diff options
Diffstat (limited to 'release/src/router/rp-l2tp')
53 files changed, 13386 insertions, 0 deletions
diff --git a/release/src/router/rp-l2tp/Makefile b/release/src/router/rp-l2tp/Makefile new file mode 100644 index 00000000..25acf25c --- /dev/null +++ b/release/src/router/rp-l2tp/Makefile @@ -0,0 +1,97 @@ +#*********************************************************************** +# +# Makefile +# +# Makefile for L2TP code. +# +# Copyright (C) 2002 Roaring Penguin Software Inc. +# +# This software may be distributed under the terms of the GNU General +# Public License, Version 2, or (at your option) any later version. +# +# LIC: GPL +# +# $Id: Makefile,v 1.3.18.1 2005/08/08 12:05:25 honor Exp $ +#*********************************************************************** + +VERSION=0.4 + +DEFINES= +prefix=/usr +exec_prefix=${prefix} +mandir=${prefix}/man +docdir=/usr/doc/l2tp-$(VERSION) +install=/usr/bin/install -c +install_dir=/usr/bin/install -c -d +sbindir=${exec_prefix}/sbin + +OBJS=auth.o debug.o dgram.o main.o md5.o network.o options.o peer.o session.o tunnel.o utils.o +EXTRA_LIBS=libevent/*.o -ldl + +SRCS=$(OBJS:.o=.c) +CFLAGS=-g -I.. -Ilibevent -Wall -Wstrict-prototypes -pedantic -D_GNU_SOURCE -DVERSION=\"$(VERSION)\" -DPREFIX=\"$(prefix)\" -I$(SRCBASE)/include + +TARGETS=l2tpd libl2tp.a handlers + +all: $(TARGETS) + +libl2tp.a: $(OBJS) + rm -f $@ + ar -rc $@ $^ + ranlib $@ || true + +l2tpd: libl2tp.a libevent/libevent.a + $(MAKE) -C handlers + $(CC) -o l2tpd -rdynamic $(OBJS) $(EXTRA_LIBS) + +libevent/libevent.a: + test -d libevent || ln -s ../libevent . + $(MAKE) -C libevent + +%.o: %.c + $(CC) -c -o $@ $(CFLAGS) $< + +depend: .depend + +.depend: $(SRCS) + $(CC) -M $(CFLAGS) $^ > .depend + +handlers: + $(MAKE) -C handlers + +# Release requires GNU tar!! +release: FORCE + rm -f rp-l2tp-$(VERSION).tar.gz + make -C libevent/Doc + ./make-release.sh $(VERSION) + tar cvhf rp-l2tp-$(VERSION).tar rp-l2tp-$(VERSION) + gzip -v -9 rp-l2tp-$(VERSION).tar + +clean: + rm -f *.o *.a *~ + rm -f l2tpd + $(MAKE) -C handlers clean + +distclean: clean + rm -f Makefile config.cache + $(MAKE) -C handlers distclean + +-include .depend + +install: all + -mkdir -p $(RPM_INSTALL_ROOT)$(DESTDIR)$(sbindir) + $(install) -m 755 -s l2tpd $(RPM_INSTALL_ROOT)$(DESTDIR)$(sbindir) + -mkdir -p $(RPM_INSTALL_ROOT)$(DESTDIR)/etc/l2tp + $(install) -m 644 l2tp.conf $(RPM_INSTALL_ROOT)$(DESTDIR)/etc/l2tp/l2tp.conf.example + -mkdir -p $(RPM_INSTALL_ROOT)$(DESTDIR)$(mandir)/man8 + -mkdir -p $(RPM_INSTALL_ROOT)$(DESTDIR)$(mandir)/man5 + $(install) -m 644 man/l2tpd.8 $(RPM_INSTALL_ROOT)$(DESTDIR)$(mandir)/man8 + $(install) -m 644 man/l2tp.conf.5 $(RPM_INSTALL_ROOT)$(DESTDIR)$(mandir)/man5 + + $(MAKE) -C handlers install + +FORCE: + +.PHONY: handlers + +.PHONY: FORCE diff --git a/release/src/router/rp-l2tp/Makefile.in b/release/src/router/rp-l2tp/Makefile.in new file mode 100644 index 00000000..c636cf4d --- /dev/null +++ b/release/src/router/rp-l2tp/Makefile.in @@ -0,0 +1,98 @@ +# @configure_input@ +#*********************************************************************** +# +# Makefile +# +# Makefile for L2TP code. +# +# Copyright (C) 2002 Roaring Penguin Software Inc. +# +# This software may be distributed under the terms of the GNU General +# Public License, Version 2, or (at your option) any later version. +# +# LIC: GPL +# +# $Id: Makefile.in,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $ +#*********************************************************************** + +VERSION=0.4 + +DEFINES= +prefix=@prefix@ +exec_prefix=@exec_prefix@ +mandir=@mandir@ +docdir=@prefix@/doc/l2tp-$(VERSION) +install=@INSTALL@ +install_dir=@INSTALL@ -d +sbindir=@sbindir@ + +OBJS=auth.o debug.o dgram.o main.o md5.o network.o options.o peer.o session.o tunnel.o utils.o +EXTRA_LIBS=@LIBEVENT@/*.o -ldl + +SRCS=$(OBJS:.o=.c) +CFLAGS=-g -I.. -I@LIBEVENT@ -Wall -Wstrict-prototypes -ansi -pedantic -D_GNU_SOURCE -DVERSION=\"$(VERSION)\" -DPREFIX=\"$(prefix)\" + +TARGETS=l2tpd libl2tp.a handlers + +all: $(TARGETS) + +libl2tp.a: $(OBJS) + rm -f $@ + ar -rc $@ $^ + ranlib $@ || true + +l2tpd: libl2tp.a libevent/libevent.a + $(MAKE) -C handlers + @CC@ -o l2tpd -rdynamic $(OBJS) $(EXTRA_LIBS) + +libevent/libevent.a: + test -d libevent || ln -s ../libevent . + $(MAKE) -C libevent + +%.o: %.c + @CC@ -c -o $@ $(CFLAGS) $< + +depend: .depend + +.depend: $(SRCS) + @CC@ -M $(CFLAGS) $^ > .depend + +handlers: + $(MAKE) -C handlers + +# Release requires GNU tar!! +release: FORCE + rm -f rp-l2tp-$(VERSION).tar.gz + make -C libevent/Doc + ./make-release.sh $(VERSION) + tar cvhf rp-l2tp-$(VERSION).tar rp-l2tp-$(VERSION) + gzip -v -9 rp-l2tp-$(VERSION).tar + +clean: + rm -f *.o *.a *~ + rm -f l2tpd + $(MAKE) -C handlers clean + +distclean: clean + rm -f Makefile config.cache + $(MAKE) -C handlers distclean + +-include .depend + +install: all + -mkdir -p $(RPM_INSTALL_ROOT)$(DESTDIR)$(sbindir) + $(install) -m 755 -s l2tpd $(RPM_INSTALL_ROOT)$(DESTDIR)$(sbindir) + -mkdir -p $(RPM_INSTALL_ROOT)$(DESTDIR)/etc/l2tp + $(install) -m 644 l2tp.conf $(RPM_INSTALL_ROOT)$(DESTDIR)/etc/l2tp/l2tp.conf.example + -mkdir -p $(RPM_INSTALL_ROOT)$(DESTDIR)$(mandir)/man8 + -mkdir -p $(RPM_INSTALL_ROOT)$(DESTDIR)$(mandir)/man5 + $(install) -m 644 man/l2tpd.8 $(RPM_INSTALL_ROOT)$(DESTDIR)$(mandir)/man8 + $(install) -m 644 man/l2tp.conf.5 $(RPM_INSTALL_ROOT)$(DESTDIR)$(mandir)/man5 + + $(MAKE) -C handlers install + +FORCE: + +.PHONY: handlers + +.PHONY: FORCE diff --git a/release/src/router/rp-l2tp/README b/release/src/router/rp-l2tp/README new file mode 100644 index 00000000..c5cbb2aa --- /dev/null +++ b/release/src/router/rp-l2tp/README @@ -0,0 +1,35 @@ +# LIC: GPL + +This is a user-space implementation of L2TP (RFC 2661) for Linux. + +Copying +------- + +All software included in this package is Copyright 2002 Roaring +Penguin Software Inc. You may distribute it under the terms of the +GNU General Public License (the "GPL"), Version 2, or (at your option) +any later version. + +Handlers +-------- + +Part of the l2tp code is implemented in the l2tpd program, and part of +it is implemented as "handlers". A handler is a shared-object library +which is dynamically linked into l2tpd at run-time using the +"load-handler" configuration directive. + +Although the handlers included with this package (sync-pppd and cmd) +are licensed under the GPL, as a special exception, you may write your +own handlers which link to code in this package and not release them +under the GPL. There may be other reasons why your handlers must +be released under the GPL (for example, they may link with other GPL'd +code), but in the absence of other reasons, you may keep your handlers +proprietary. + +Installation +------------ + +./configure && make +make install + +Now read l2tpd(8) and l2tp.conf(5) diff --git a/release/src/router/rp-l2tp/auth.c b/release/src/router/rp-l2tp/auth.c new file mode 100644 index 00000000..a51a911d --- /dev/null +++ b/release/src/router/rp-l2tp/auth.c @@ -0,0 +1,54 @@ +/*********************************************************************** +* +* auth.c +* +* Code for doing CHAP-style authentication +* +* Copyright (C) 2002 by Roaring Penguin Software Inc. +* +* This software may be distributed under the terms of the GNU General +* Public License, Version 2, or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id: auth.c,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $"; + +#include "l2tp.h" +#include "md5.h" +#include <string.h> + +/********************************************************************** +* %FUNCTION: auth_gen_response +* %ARGUMENTS: +* msg_type -- message type +* secret -- secret to use +* challenge -- challenge received from peer +* chal_len -- length of challenge +* buf -- buffer in which to place 16-byte response +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Computes a response for the challenge using "secret" +***********************************************************************/ +void +l2tp_auth_gen_response(uint16_t msg_type, + char const *secret, + unsigned char const *challenge, + size_t chal_len, + unsigned char buf[16]) +{ + struct MD5Context ctx; + unsigned char id = (unsigned char) msg_type; + + MD5Init(&ctx); + MD5Update(&ctx, &id, 1); + MD5Update(&ctx, (unsigned char *) secret, strlen(secret)); + MD5Update(&ctx, challenge, chal_len); + MD5Final(buf, &ctx); + DBG(l2tp_db(DBG_AUTH, "auth_gen_response(secret=%s) -> %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", secret, + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], + buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15])); +} diff --git a/release/src/router/rp-l2tp/config.cache b/release/src/router/rp-l2tp/config.cache new file mode 100644 index 00000000..bf447a89 --- /dev/null +++ b/release/src/router/rp-l2tp/config.cache @@ -0,0 +1,43 @@ +# 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. +# +ac_cv_c_const=${ac_cv_c_const=yes} +ac_cv_func_gethostname=${ac_cv_func_gethostname=yes} +ac_cv_func_gettimeofday=${ac_cv_func_gettimeofday=yes} +ac_cv_func_memcmp_clean=${ac_cv_func_memcmp_clean=no} +ac_cv_func_socket=${ac_cv_func_socket=yes} +ac_cv_func_strdup=${ac_cv_func_strdup=yes} +ac_cv_func_strerror=${ac_cv_func_strerror=yes} +ac_cv_func_strtol=${ac_cv_func_strtol=yes} +ac_cv_func_vprintf=${ac_cv_func_vprintf=yes} +ac_cv_header_fcntl_h=${ac_cv_header_fcntl_h=yes} +ac_cv_header_stdc=${ac_cv_header_stdc=yes} +ac_cv_header_sys_ioctl_h=${ac_cv_header_sys_ioctl_h=yes} +ac_cv_header_sys_time_h=${ac_cv_header_sys_time_h=yes} +ac_cv_header_syslog_h=${ac_cv_header_syslog_h=yes} +ac_cv_header_time=${ac_cv_header_time=yes} +ac_cv_header_unistd_h=${ac_cv_header_unistd_h=yes} +ac_cv_lib_dl_dlopen=${ac_cv_lib_dl_dlopen=yes} +ac_cv_path_install=${ac_cv_path_install='/usr/bin/install -c'} +ac_cv_prog_CC=${ac_cv_prog_CC=mipsel-uclibc-gcc} +ac_cv_prog_CPP=${ac_cv_prog_CPP='mipsel-uclibc-gcc -E'} +ac_cv_prog_RANLIB=${ac_cv_prog_RANLIB=mipsel-uclibc-ranlib} +ac_cv_prog_cc_cross=${ac_cv_prog_cc_cross=yes} +ac_cv_prog_cc_g=${ac_cv_prog_cc_g=yes} +ac_cv_prog_cc_works=${ac_cv_prog_cc_works=yes} +ac_cv_prog_gcc=${ac_cv_prog_gcc=yes} +ac_cv_prog_gcc_traditional=${ac_cv_prog_gcc_traditional=no} +ac_cv_type_pid_t=${ac_cv_type_pid_t=yes} +ac_cv_type_signal=${ac_cv_type_signal=void} +ac_cv_type_size_t=${ac_cv_type_size_t=yes} diff --git a/release/src/router/rp-l2tp/config.log b/release/src/router/rp-l2tp/config.log new file mode 100644 index 00000000..09253700 --- /dev/null +++ b/release/src/router/rp-l2tp/config.log @@ -0,0 +1,53 @@ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +configure:529: checking for gcc +configure:642: checking whether the C compiler (mipsel-uclibc-gcc ) works +configure:658: mipsel-uclibc-gcc -o conftest conftest.c 1>&5 +configure:684: checking whether the C compiler (mipsel-uclibc-gcc ) is a cross-compiler +configure:689: checking whether we are using GNU C +configure:698: mipsel-uclibc-gcc -E conftest.c +configure:717: checking whether mipsel-uclibc-gcc accepts -g +configure:779: checking for a BSD compatible install +configure:834: checking for ranlib +configure:863: checking for dlopen in -ldl +configure:882: mipsel-uclibc-gcc -o conftest -g -O2 conftest.c -ldl 1>&5 +configure:911: checking how to run the C preprocessor +configure:932: mipsel-uclibc-gcc -E conftest.c >/dev/null 2>conftest.out +configure:991: checking for ANSI C header files +configure:1004: mipsel-uclibc-gcc -E conftest.c >/dev/null 2>conftest.out +configure:1098: checking for fcntl.h +configure:1108: mipsel-uclibc-gcc -E conftest.c >/dev/null 2>conftest.out +configure:1098: checking for sys/ioctl.h +configure:1108: mipsel-uclibc-gcc -E conftest.c >/dev/null 2>conftest.out +configure:1098: checking for sys/time.h +configure:1108: mipsel-uclibc-gcc -E conftest.c >/dev/null 2>conftest.out +configure:1098: checking for syslog.h +configure:1108: mipsel-uclibc-gcc -E conftest.c >/dev/null 2>conftest.out +configure:1098: checking for unistd.h +configure:1108: mipsel-uclibc-gcc -E conftest.c >/dev/null 2>conftest.out +configure:1136: checking for libevent directory +configure:1152: checking for working const +configure:1206: mipsel-uclibc-gcc -c -g -O2 conftest.c 1>&5 +configure:1227: checking for pid_t +configure:1260: checking for size_t +configure:1293: checking whether time.h and sys/time.h may both be included +configure:1307: mipsel-uclibc-gcc -c -g -O2 conftest.c 1>&5 +configure:1330: checking whether mipsel-uclibc-gcc needs -traditional +configure:1376: checking for 8-bit clean memcmp +configure:1412: checking return type of signal handlers +configure:1434: mipsel-uclibc-gcc -c -g -O2 conftest.c 1>&5 +configure:1453: checking for vprintf +configure:1481: mipsel-uclibc-gcc -o conftest -g -O2 conftest.c -ldl 1>&5 +configure:1560: checking for gethostname +configure:1588: mipsel-uclibc-gcc -o conftest -g -O2 conftest.c -ldl 1>&5 +configure:1560: checking for gettimeofday +configure:1588: mipsel-uclibc-gcc -o conftest -g -O2 conftest.c -ldl 1>&5 +configure:1560: checking for socket +configure:1588: mipsel-uclibc-gcc -o conftest -g -O2 conftest.c -ldl 1>&5 +configure:1560: checking for strdup +configure:1588: mipsel-uclibc-gcc -o conftest -g -O2 conftest.c -ldl 1>&5 +configure:1560: checking for strerror +configure:1588: mipsel-uclibc-gcc -o conftest -g -O2 conftest.c -ldl 1>&5 +configure:1560: checking for strtol +configure:1588: mipsel-uclibc-gcc -o conftest -g -O2 conftest.c -ldl 1>&5 diff --git a/release/src/router/rp-l2tp/config.status b/release/src/router/rp-l2tp/config.status new file mode 100755 index 00000000..1eae8e65 --- /dev/null +++ b/release/src/router/rp-l2tp/config.status @@ -0,0 +1,163 @@ +#! /bin/sh +# Generated automatically by configure. +# Run this file to recreate the current configuration. +# This directory was configured as follows, +# on host localhost.localdomain: +# +# ./configure +# +# 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} ./configure --no-create --no-recursion" + exec ${CONFIG_SHELL-/bin/sh} ./configure --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=. +ac_given_INSTALL="/usr/bin/install -c" + +trap 'rm -fr Makefile libevent/Makefile handlers/Makefile conftest*; exit 1' 1 2 15 + +# 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 +/^[ ]*VPATH[ ]*=[^:]*$/d + +s%@SHELL@%/bin/sh%g +s%@CFLAGS@%-g -O2%g +s%@CPPFLAGS@%%g +s%@CXXFLAGS@%%g +s%@FFLAGS@%%g +s%@DEFS@% -DHAVE_LIBDL=1 -DSTDC_HEADERS=1 -DHAVE_FCNTL_H=1 -DHAVE_SYS_IOCTL_H=1 -DHAVE_SYS_TIME_H=1 -DHAVE_SYSLOG_H=1 -DHAVE_UNISTD_H=1 -DTIME_WITH_SYS_TIME=1 -DRETSIGTYPE=void -DHAVE_VPRINTF=1 -DHAVE_GETHOSTNAME=1 -DHAVE_GETTIMEOFDAY=1 -DHAVE_SOCKET=1 -DHAVE_STRDUP=1 -DHAVE_STRERROR=1 -DHAVE_STRTOL=1 %g +s%@LDFLAGS@%%g +s%@LIBS@%-ldl %g +s%@exec_prefix@%${prefix}%g +s%@prefix@%/usr%g +s%@program_transform_name@%s,x,x,%g +s%@bindir@%${exec_prefix}/bin%g +s%@sbindir@%${exec_prefix}/sbin%g +s%@libexecdir@%${exec_prefix}/libexec%g +s%@datadir@%${prefix}/share%g +s%@sysconfdir@%${prefix}/etc%g +s%@sharedstatedir@%${prefix}/com%g +s%@localstatedir@%${prefix}/var%g +s%@libdir@%${exec_prefix}/lib%g +s%@includedir@%${prefix}/include%g +s%@oldincludedir@%/usr/include%g +s%@infodir@%${prefix}/info%g +s%@mandir@%${prefix}/man%g +s%@CC@%mipsel-uclibc-gcc%g +s%@INSTALL_PROGRAM@%${INSTALL}%g +s%@INSTALL_SCRIPT@%${INSTALL_PROGRAM}%g +s%@INSTALL_DATA@%${INSTALL} -m 644%g +s%@RANLIB@%mipsel-uclibc-ranlib%g +s%@CPP@%mipsel-uclibc-gcc -E%g +s%@LIBEVENT@%libevent%g +s%@LIBOBJS@% memcmp.o%g + +CEOF + +# 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 + +CONFIG_FILES=${CONFIG_FILES-"Makefile libevent/Makefile handlers/Makefile"} +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* + + + +exit 0 diff --git a/release/src/router/rp-l2tp/configure b/release/src/router/rp-l2tp/configure new file mode 100755 index 00000000..32a85ad0 --- /dev/null +++ b/release/src/router/rp-l2tp/configure @@ -0,0 +1,1888 @@ +#! /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 +ac_default_prefix=/usr +# Any additions from configure.in: + +# 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=l2tp.h + +# 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:529: 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:559: 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:610: 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:642: 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 653 "configure" +#include "confdefs.h" + +main(){return(0);} +EOF +if { (eval echo configure:658: \"$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:684: 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:689: 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:698: \"$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:717: 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 + +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:779: 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' + +# Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:834: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # 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_RANLIB="ranlib" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":" +fi +fi +RANLIB="$ac_cv_prog_RANLIB" +if test -n "$RANLIB"; then + echo "$ac_t""$RANLIB" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + +echo $ac_n "checking for dlopen in -ldl""... $ac_c" 1>&6 +echo "configure:863: checking for dlopen in -ldl" >&5 +ac_lib_var=`echo dl'_'dlopen | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ldl $LIBS" +cat > conftest.$ac_ext <<EOF +#line 871 "configure" +#include "confdefs.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 dlopen(); + +int main() { +dlopen() +; return 0; } +EOF +if { (eval echo configure:882: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo dl | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <<EOF +#define $ac_tr_lib 1 +EOF + + LIBS="-ldl $LIBS" + +else + echo "$ac_t""no" 1>&6 +fi + + +echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 +echo "configure:911: 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 926 "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:932: \"$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 943 "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:949: \"$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 960 "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:966: \"$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:991: 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 996 "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:1004: \"$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 1021 "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 1039 "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 1060 "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:1071: \"$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 + +for ac_hdr in fcntl.h sys/ioctl.h sys/time.h syslog.h unistd.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:1098: 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 1103 "configure" +#include "confdefs.h" +#include <$ac_hdr> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1108: \"$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 libevent directory""... $ac_c" 1>&6 +echo "configure:1136: checking for libevent directory" >&5 +if test -d ../libevent ; then + LIBEVENT=../libevent +elif test -d libevent ; then + LIBEVENT=libevent +else + echo "$ac_t""not found" 1>&6 + echo "Cannot find libevent library; exiting" + exit 1 +fi + +echo "$ac_t""$LIBEVENT" 1>&6 + + + +echo $ac_n "checking for working const""... $ac_c" 1>&6 +echo "configure:1152: 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 1157 "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:1206: \"$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:1227: 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 1232 "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 for size_t""... $ac_c" 1>&6 +echo "configure:1260: checking for size_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1265 "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])size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_size_t=yes +else + rm -rf conftest* + ac_cv_type_size_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_size_t" 1>&6 +if test $ac_cv_type_size_t = no; then + cat >> confdefs.h <<\EOF +#define size_t unsigned +EOF + +fi + +echo $ac_n "checking whether time.h and sys/time.h may both be included""... $ac_c" 1>&6 +echo "configure:1293: 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 1298 "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:1307: \"$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 + + +if test $ac_cv_prog_gcc = yes; then + echo $ac_n "checking whether ${CC-cc} needs -traditional""... $ac_c" 1>&6 +echo "configure:1330: checking whether ${CC-cc} needs -traditional" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc_traditional'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_pattern="Autoconf.*'x'" + cat > conftest.$ac_ext <<EOF +#line 1336 "configure" +#include "confdefs.h" +#include <sgtty.h> +Autoconf TIOCGETP +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "$ac_pattern" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_prog_gcc_traditional=yes +else + rm -rf conftest* + ac_cv_prog_gcc_traditional=no +fi +rm -f conftest* + + + if test $ac_cv_prog_gcc_traditional = no; then + cat > conftest.$ac_ext <<EOF +#line 1354 "configure" +#include "confdefs.h" +#include <termio.h> +Autoconf TCGETA +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "$ac_pattern" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_prog_gcc_traditional=yes +fi +rm -f conftest* + + fi +fi + +echo "$ac_t""$ac_cv_prog_gcc_traditional" 1>&6 + if test $ac_cv_prog_gcc_traditional = yes; then + CC="$CC -traditional" + fi +fi + +echo $ac_n "checking for 8-bit clean memcmp""... $ac_c" 1>&6 +echo "configure:1376: 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 1384 "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:1394: \"$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 return type of signal handlers""... $ac_c" 1>&6 +echo "configure:1412: 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 1417 "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:1434: \"$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 + + +echo $ac_n "checking for vprintf""... $ac_c" 1>&6 +echo "configure:1453: checking for vprintf" >&5 +if eval "test \"`echo '$''{'ac_cv_func_vprintf'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1458 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char vprintf(); 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 vprintf(); + +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_vprintf) || defined (__stub___vprintf) +choke me +#else +vprintf(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1481: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_vprintf=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_vprintf=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'vprintf`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_VPRINTF 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi + +if test "$ac_cv_func_vprintf" != yes; then +echo $ac_n "checking for _doprnt""... $ac_c" 1>&6 +echo "configure:1505: checking for _doprnt" >&5 +if eval "test \"`echo '$''{'ac_cv_func__doprnt'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1510 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char _doprnt(); 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 _doprnt(); + +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__doprnt) || defined (__stub____doprnt) +choke me +#else +_doprnt(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1533: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func__doprnt=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func__doprnt=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'_doprnt`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_DOPRNT 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi + +fi + +for ac_func in gethostname gettimeofday socket strdup strerror strtol +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:1560: 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 1565 "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:1588: \"$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 + + + +if test -d libevent ; then + if test -L libevent ; then + OUTPUT="Makefile handlers/Makefile" + else + OUTPUT="Makefile libevent/Makefile handlers/Makefile" + fi +else + OUTPUT="Makefile handlers/Makefile" +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 + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +cat > conftest.defs <<\EOF +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%-D\1=\2%g +s%[ `~#$^&*(){}\\|;'"<>?]%\\&%g +s%\[%\\&%g +s%\]%\\&%g +s%\$%$$%g +EOF +DEFS=`sed -f conftest.defs confdefs.h | tr '\012' ' '` +rm -f conftest.defs + + +# 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 "$OUTPUT" | 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%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g +s%@INSTALL_DATA@%$INSTALL_DATA%g +s%@RANLIB@%$RANLIB%g +s%@CPP@%$CPP%g +s%@LIBEVENT@%$LIBEVENT%g +s%@LIBOBJS@%$LIBOBJS%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-"$OUTPUT"} +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* + +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 + diff --git a/release/src/router/rp-l2tp/configure.in b/release/src/router/rp-l2tp/configure.in new file mode 100644 index 00000000..0b8890cc --- /dev/null +++ b/release/src/router/rp-l2tp/configure.in @@ -0,0 +1,59 @@ +dnl Process this file with autoconf to produce a configure script. +dnl LIC: GPL +AC_INIT(l2tp.h) + +dnl Checks for programs. +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_RANLIB + +dnl Checks for libraries. +dnl Replace `main' with a function in -ldl: +AC_CHECK_LIB(dl, dlopen) + +dnl Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS(fcntl.h sys/ioctl.h sys/time.h syslog.h unistd.h) + +dnl Check for libevent +AC_MSG_CHECKING([for libevent directory]) +if test -d ../libevent ; then + LIBEVENT=../libevent +elif test -d libevent ; then + LIBEVENT=libevent +else + AC_MSG_RESULT([not found]) + echo "Cannot find libevent library; exiting" + exit 1 +fi + +AC_MSG_RESULT($LIBEVENT) + +AC_SUBST(LIBEVENT) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_HEADER_TIME + +dnl Checks for library functions. +AC_PROG_GCC_TRADITIONAL +AC_FUNC_MEMCMP +AC_TYPE_SIGNAL +AC_FUNC_VPRINTF +AC_CHECK_FUNCS(gethostname gettimeofday socket strdup strerror strtol) + +dnl If we own this copy of libevent, do its makefile; otherwise, not + +if test -d libevent ; then + if test -L libevent ; then + OUTPUT="Makefile handlers/Makefile" + else + OUTPUT="Makefile libevent/Makefile handlers/Makefile" + fi +else + OUTPUT="Makefile handlers/Makefile" +fi + +AC_OUTPUT($OUTPUT) diff --git a/release/src/router/rp-l2tp/debug.c b/release/src/router/rp-l2tp/debug.c new file mode 100644 index 00000000..dd7a45e8 --- /dev/null +++ b/release/src/router/rp-l2tp/debug.c @@ -0,0 +1,238 @@ +/*********************************************************************** +* +* debug.c +* +* Debugging routines for L2TP +* +* Copyright (C) 2002 by Roaring Penguin Software Inc. +* +* This software may be distributed under the terms of the GNU General +* Public License, Version 2, or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id: debug.c,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $"; + +#include "l2tp.h" +#include <stdio.h> +#include <stdarg.h> +#include <sys/time.h> +#include <unistd.h> + +#define AVPCASE(x) case AVP_ ## x: return #x +#define MSGCASE(x) case MESSAGE_ ## x: return #x + +static unsigned long debug_mask = 0; + +/* Big bang is when the universe started... set by first call + to db */ +static struct timeval big_bang = {0, 0}; + +/********************************************************************** +* %FUNCTION: debug_avp_type_to_str +* %ARGUMENTS: +* type -- an AVP type +* %RETURNS: +* A string representation of AVP type +***********************************************************************/ +char const * +l2tp_debug_avp_type_to_str(uint16_t type) +{ + static char buf[64]; + switch(type) { + AVPCASE(MESSAGE_TYPE); + AVPCASE(RESULT_CODE); + AVPCASE(PROTOCOL_VERSION); + AVPCASE(FRAMING_CAPABILITIES); + AVPCASE(BEARER_CAPABILITIES); + AVPCASE(TIE_BREAKER); + AVPCASE(FIRMWARE_REVISION); + AVPCASE(HOST_NAME); + AVPCASE(VENDOR_NAME); + AVPCASE(ASSIGNED_TUNNEL_ID); + AVPCASE(RECEIVE_WINDOW_SIZE); + AVPCASE(CHALLENGE); + AVPCASE(Q931_CAUSE_CODE); + AVPCASE(CHALLENGE_RESPONSE); + AVPCASE(ASSIGNED_SESSION_ID); + AVPCASE(CALL_SERIAL_NUMBER); + AVPCASE(MINIMUM_BPS); + AVPCASE(MAXIMUM_BPS); + AVPCASE(BEARER_TYPE); + AVPCASE(FRAMING_TYPE); + AVPCASE(CALLED_NUMBER); + AVPCASE(CALLING_NUMBER); + AVPCASE(SUB_ADDRESS); + AVPCASE(TX_CONNECT_SPEED); + AVPCASE(PHYSICAL_CHANNEL_ID); + AVPCASE(INITIAL_RECEIVED_CONFREQ); + AVPCASE(LAST_SENT_CONFREQ); + AVPCASE(LAST_RECEIVED_CONFREQ); + AVPCASE(PROXY_AUTHEN_TYPE); + AVPCASE(PROXY_AUTHEN_NAME); + AVPCASE(PROXY_AUTHEN_CHALLENGE); + AVPCASE(PROXY_AUTHEN_ID); + AVPCASE(PROXY_AUTHEN_RESPONSE); + AVPCASE(CALL_ERRORS); + AVPCASE(ACCM); + AVPCASE(RANDOM_VECTOR); + AVPCASE(PRIVATE_GROUP_ID); + AVPCASE(RX_CONNECT_SPEED); + AVPCASE(SEQUENCING_REQUIRED); + } + /* Unknown */ + sprintf(buf, "Unknown_AVP#%d", (int) type); + return buf; +} + +/********************************************************************** +* %FUNCTION: debug_message_type_to_str +* %ARGUMENTS: +* type -- an MESSAGE type +* %RETURNS: +* A string representation of message type +***********************************************************************/ +char const * +l2tp_debug_message_type_to_str(uint16_t type) +{ + static char buf[64]; + switch(type) { + MSGCASE(SCCRQ); + MSGCASE(SCCRP); + MSGCASE(SCCCN); + MSGCASE(StopCCN); + MSGCASE(HELLO); + MSGCASE(OCRQ); + MSGCASE(OCRP); + MSGCASE(OCCN); + MSGCASE(ICRQ); + MSGCASE(ICRP); + MSGCASE(ICCN); + MSGCASE(CDN); + MSGCASE(WEN); + MSGCASE(SLI); + MSGCASE(ZLB); + } + sprintf(buf, "Unknown_Message#%d", (int) type); + return buf; +} + +/********************************************************************** +* %FUNCTION: debug_tunnel_to_str +* %ARGUMENTS: +* tunnel +* %RETURNS: +* A string representation of tunnel (my_id/assigned_id) +***********************************************************************/ +char const * +l2tp_debug_tunnel_to_str(l2tp_tunnel *tunnel) +{ + static char buf[64]; + sprintf(buf, "%d/%d", (int) tunnel->my_id, (int) tunnel->assigned_id); + return buf; +} + +/********************************************************************** +* %FUNCTION: debug_session_to_str +* %ARGUMENTS: +* session +* %RETURNS: +* A string representation of session (my_id/assigned_id) +***********************************************************************/ +char const * +l2tp_debug_session_to_str(l2tp_session *session) +{ + static char buf[128]; + sprintf(buf, "(%d/%d, %d/%d)", + (int) session->tunnel->my_id, + (int) session->tunnel->assigned_id, + (int) session->my_id, (int) session->assigned_id); + return buf; +} + +/********************************************************************** +* %FUNCTION: db +* %ARGUMENTS: +* what -- which facet we are debugging +* fmt -- printf-style format +* args -- arguments +* %RETURNS: +* Nothing +* %DESCRIPTION: +* If bit in debug mask for "what" is set, print debugging message. +***********************************************************************/ +void +l2tp_db(int what, char const *fmt, ...) +{ + va_list ap; + struct timeval now; + long sec_diff, usec_diff; + + if (!(debug_mask & what)) return; + + gettimeofday(&now, NULL); + + if (!big_bang.tv_sec) { + big_bang = now; + } + + /* Compute difference between now and big_bang */ + sec_diff = now.tv_sec - big_bang.tv_sec; + usec_diff = now.tv_usec - big_bang.tv_usec; + if (usec_diff < 0) { + usec_diff += 1000000; + sec_diff--; + } + + /* Convert to seconds.milliseconds */ + usec_diff /= 1000; + + va_start(ap, fmt); + fprintf(stderr, "%4ld.%03ld ", sec_diff, usec_diff); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +/********************************************************************** +* %FUNCTION: debug_set_bitmask +* %ARGUMENTS: +* mask -- what to set debug bitmask to +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Sets debug bitmask +***********************************************************************/ +void +l2tp_debug_set_bitmask(unsigned long mask) +{ + debug_mask = mask; +} + +/********************************************************************** +* %FUNCTION: debug_describe_dgram +* %ARGUMENTS: +* dgram -- an L2TP datagram +* %RETURNS: +* A string describing the datagram +***********************************************************************/ +char const * +l2tp_debug_describe_dgram(l2tp_dgram const *dgram) +{ + static char buf[256]; + + if (dgram->bits & TYPE_BIT) { + /* Control datagram */ + snprintf(buf, sizeof(buf), "type=%s, tid=%d, sid=%d, Nr=%d, Ns=%d", + l2tp_debug_message_type_to_str(dgram->msg_type), + (int) dgram->tid, (int) dgram->sid, + (int) dgram->Nr, (int) dgram->Ns); + } else { + snprintf(buf, sizeof(buf), "data message tid=%d sid=%d payload_len=%d", + (int) dgram->tid, (int) dgram->sid, + (int) dgram->payload_len); + } + return buf; +} diff --git a/release/src/router/rp-l2tp/dgram.c b/release/src/router/rp-l2tp/dgram.c new file mode 100644 index 00000000..cf352eef --- /dev/null +++ b/release/src/router/rp-l2tp/dgram.c @@ -0,0 +1,1070 @@ +/*********************************************************************** +* +* dgram.c +* +* Routines for manipulating L2TP datagrams. +* +* Copyright (C) 2002 by Roaring Penguin Software Inc. +* +* This software may be distributed under the terms of the GNU General +* Public License, Version 2, or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id: dgram.c,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $"; + +#include "l2tp.h" +#include "md5.h" +#include <sys/types.h> +#include <sys/socket.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> + +#define PULL_UINT16(buf, cursor, val) \ +do { \ + val = ((uint16_t) buf[cursor]) * 256 + (uint16_t) buf[cursor+1]; \ + cursor += 2; \ +} while(0) + +#define PUSH_UINT16(buf, cursor, val) \ +do { \ + buf[cursor] = val / 256; \ + buf[cursor+1] = val & 0xFF; \ + cursor += 2; \ +} while (0) + +#define GET_AVP_LEN(x) ((((x)[0] & 3) * 256) + (x)[1]) + +static int dgram_add_random_vector_avp(l2tp_dgram *dgram); + +static void dgram_do_hide(uint16_t type, + uint16_t len, + unsigned char *value, + unsigned char const *secret, + size_t secret_len, + unsigned char const *random, + size_t random_len, + unsigned char *output); + +static unsigned char *dgram_do_unhide(uint16_t type, + uint16_t *len, + unsigned char *value, + unsigned char const *secret, + size_t secret_len, + unsigned char const *random, + size_t random_len, + unsigned char *output); + +/* Description of AVP's indexed by AVP type */ +struct avp_descrip { + char const *name; /* AVP name */ + int can_hide; /* Can AVP be hidden? */ + uint16_t min_len; /* Minimum PAYLOAD length */ + uint16_t max_len; /* Maximum PAYLOAD length (9999 = no limit) */ +}; + +static struct avp_descrip avp_table[] = { + /* Name can_hide min_len max_len */ + {"Message Type", 0, 2, 2}, /* 0 */ + {"Result Code", 0, 2, 9999}, /* 1 */ + {"Protocol Version", 0, 2, 2}, /* 2 */ + {"Framing Capabilities", 1, 4, 4}, /* 3 */ + {"Bearer Capabilities", 1, 4, 4}, /* 4 */ + {"Tie Breaker", 0, 8, 8}, /* 5 */ + {"Firmware Revision", 1, 2, 2}, /* 6 */ + {"Host Name", 0, 0, 9999}, /* 7 */ + {"Vendor Name", 1, 0, 9999}, /* 8 */ + {"Assigned Tunnel ID", 1, 2, 2}, /* 9 */ + {"Receive Window Size", 0, 2, 2}, /* 10 */ + {"Challenge", 1, 0, 9999}, /* 11 */ + {"Q.931 Cause Code", 0, 3, 9999}, /* 12 */ + {"Challenge Response", 1, 16, 16}, /* 13 */ + {"Assigned Session ID", 1, 2, 2}, /* 14 */ + {"Call Serial Number", 1, 4, 4}, /* 15 */ + {"Minimum BPS", 1, 4, 4}, /* 16 */ + {"Maximum BPS", 1, 4, 4}, /* 17 */ + {"Bearer Type", 1, 4, 4}, /* 18 */ + {"Framing Type", 1, 4, 4}, /* 19 */ + {"Unknown", 0, 0, 9999}, /* 20 */ + {"Called Number", 1, 0, 9999}, /* 21 */ + {"Calling Number", 1, 0, 9999}, /* 22 */ + {"Sub-Address", 1, 0, 9999}, /* 23 */ + {"TX Connect Speed", 1, 4, 4}, /* 24 */ + {"Physical Channel ID", 1, 4, 4}, /* 25 */ + {"Intial Received Confreq", 1, 0, 9999}, /* 26 */ + {"Last Sent Confreq", 1, 0, 9999}, /* 27 */ + {"Last Received Confreq", 1, 0, 9999}, /* 28 */ + {"Proxy Authen Type", 1, 2, 2}, /* 29 */ + {"Proxy Authen Name", 1, 0, 9999}, /* 30 */ + {"Proxy Authen Challenge", 1, 0, 9999}, /* 31 */ + {"Proxy Authen ID", 1, 2, 2}, /* 32 */ + {"Proxy Authen Response", 1, 0, 9999}, /* 33 */ + {"Call Errors", 1, 26, 26}, /* 34 */ + {"ACCM", 1, 10, 10}, /* 35 */ + {"Random Vector", 0, 0, 9999}, /* 36 */ + {"Private Group ID", 1, 0, 9999}, /* 37 */ + {"RX Connect Speed", 1, 4, 4}, /* 38 */ + {"Sequencing Required", 0, 0, 0} /* 39 */ +}; + +/* A free list of L2TP dgram strucures. Allocation of L2TP dgrams + must be fast... */ +static l2tp_dgram *dgram_free_list = NULL; + +static void +describe_pulled_avp(uint16_t vendor, + uint16_t type, + uint16_t len, + int hidden, + int mandatory, + void *val) +{ + unsigned char *buf = val; + uint16_t i; + int ascii = 1; + + fprintf(stderr, "Pulled avp: len=%d ", (int) len); + if (vendor == VENDOR_IETF) { + fprintf(stderr, "type='%s' ",avp_table[type].name); + } else { + fprintf(stderr, "type=%d/%d ", (int) vendor, (int) type); + } + fprintf(stderr, "M=%d H=%d ", mandatory ? 1 : 0, hidden ? 1 : 0); + + fprintf(stderr, "val='"); + for (i=0; i<len; i++) { + if (buf[i] < 32 || buf[i] > 126) { + ascii = 0; + break; + } + } + for (i=0; i<len; i++) { + if (ascii) { + fprintf(stderr, "%c", buf[i]); + } else { + fprintf(stderr, "%02X ", (unsigned int) buf[i]); + } + } + fprintf(stderr, "'\n"); +} + +/********************************************************************** +* %FUNCTION: dgram_validate_avp +* %ARGUMENTS: +* vendor -- vendor code +* type -- AVP type +* len -- PAYLOAD length +* mandatory -- non-zero if mandatory bit is set +* %RETURNS: +* 1 if len is valid for type, 0 otherwise. +***********************************************************************/ +int +l2tp_dgram_validate_avp(uint16_t vendor, + uint16_t type, + uint16_t len, + int mandatory) +{ + if (vendor != VENDOR_IETF) { + if (mandatory) { + l2tp_set_errmsg("Unknown mandatory AVP (vendor %d, type %d)", + (int) vendor, (int) type); + return 0; + } + /* I suppose... */ + return 1; + } + + if (type > HIGHEST_AVP) { + if (mandatory) { + l2tp_set_errmsg("Unknown mandatory AVP of type %d", + (int) type); + return 0; + } + return 1; + } + + return (len >= avp_table[type].min_len && + len <= avp_table[type].max_len); +} + +/********************************************************************** +* %FUNCTION: dgram_new +* %ARGUMENTS: +* len -- payload length to allocate (not currently used...) +* %RETURNS: +* A newly-allocated l2tp_dgram structure or NULL +* %DESCRIPTION: +* Allocates and initializes an datagram structure. +***********************************************************************/ +l2tp_dgram * +l2tp_dgram_new(size_t len) +{ + l2tp_dgram *dgram; + + if (len > MAX_PACKET_LEN) { + l2tp_set_errmsg("dgram_new: cannot allocate datagram with payload length %d", + (int) len); + return NULL; + } + + if (dgram_free_list) { + dgram = dgram_free_list; + dgram_free_list = dgram->next; + } else { + dgram = malloc(sizeof(l2tp_dgram)); + } + if (!dgram) { + l2tp_set_errmsg("dgram_new: Out of memory"); + return NULL; + } + dgram->bits = 0; + dgram->version = 2; + dgram->length = 0; + dgram->tid = 0; + dgram->sid = 0; + dgram->Ns = 0; + dgram->Nr = 0; + dgram->off_size = 0; + dgram->last_random = 0; + dgram->payload_len = 0; + dgram->cursor = 0; + + /* We maintain this in case it becomes dynamic later on */ + dgram->alloc_len = MAX_PACKET_LEN; + dgram->next = NULL; + + return dgram; +} + +/********************************************************************** +* %FUNCTION: dgram_new_control +* %ARGUMENTS: +* msg_type -- message type +* tid -- tunnel ID +* sid -- session ID +* %RETURNS: +* A newly-allocated control datagram +* %DESCRIPTION: +* Allocates datagram; sets tid and sid fields; adds message_type AVP +***********************************************************************/ +l2tp_dgram * +l2tp_dgram_new_control(uint16_t msg_type, + uint16_t tid, + uint16_t sid) +{ + l2tp_dgram *dgram = l2tp_dgram_new(MAX_PACKET_LEN); + uint16_t u16; + + if (!dgram) return NULL; + + dgram->bits = TYPE_BIT | LENGTH_BIT | SEQUENCE_BIT; + dgram->tid = tid; + dgram->sid = sid; + dgram->msg_type = msg_type; + if (msg_type != MESSAGE_ZLB) { + u16 = htons(msg_type); + + l2tp_dgram_add_avp(dgram, NULL, MANDATORY, + sizeof(u16), VENDOR_IETF, AVP_MESSAGE_TYPE, &u16); + } + return dgram; +} + +/********************************************************************** +* %FUNCTION: dgram_free +* %ARGUMENTS: +* dgram -- datagram to free +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Frees a datagram structure. +***********************************************************************/ +void +l2tp_dgram_free(l2tp_dgram *dgram) +{ + if (!dgram) return; + dgram->next = dgram_free_list; + dgram_free_list = dgram; +} + +/********************************************************************** +* %FUNCTION: dgram_take_from_wire +* %ARGUMENTS: +* from -- set to address of peer. +* %RETURNS: +* NULL on error, allocated datagram otherwise. +* %DESCRIPTION: +* Reads an L2TP datagram off the wire and puts it in dgram. Adjusts +* header fields to host byte order. Keeps reading in a loop unless +* we hit a control frame or EAGAIN. This is more efficient than +* returning to select loop each time if there's lots of traffic. +***********************************************************************/ +l2tp_dgram * +l2tp_dgram_take_from_wire(struct sockaddr_in *from) +{ + /* EXTRA_HEADER_ROOM bytes for other headers like PPPoE, etc. */ + + unsigned char inbuf[MAX_PACKET_LEN+EXTRA_HEADER_ROOM]; + unsigned char *buf; + socklen_t len = sizeof(struct sockaddr_in); + int cursor; + l2tp_dgram *dgram; + unsigned char *payload; + unsigned char *tidptr; + uint16_t tid, sid; + uint16_t cache_tid, cache_sid; + l2tp_tunnel *tunnel; + l2tp_session *ses = NULL; + int mandatory, hidden, err; + uint16_t vendor, type; + unsigned char *msg; + int r; + + /* Limit iterations before bailing back to select look. Otherwise, + we have a nice DoS possibility */ + int iters = 5; + + uint16_t off; + int framelen; + + /* Active part of buffer */ + buf = inbuf + EXTRA_HEADER_ROOM; + + while(1) { + if (--iters <= 0) return NULL; + framelen = -1; + r = recvfrom(Sock, buf, MAX_PACKET_LEN, 0, + (struct sockaddr *) from, &len); + if (r <= 0) { + return NULL; + } + + /* Check version; drop frame if not L2TP (ver = 2) */ + if ((buf[1] & VERSION_MASK) != 2) continue; + + /* Not a data frame -- break out of loop and handle control frame */ + if (buf[0] & TYPE_BIT) break; + + /* If it's a data frame, we need to be FAST, so do not allocate + a dgram structure, etc; just call into handler */ + payload = buf + 6; + tidptr = buf + 2; + if (buf[0] & LENGTH_BIT) { + framelen = (((uint16_t) buf[2]) << 8) + buf[3]; + payload += 2; + tidptr += 2; + } + if (buf[0] & SEQUENCE_BIT) { + payload += 4; + } + if (buf[0] & OFFSET_BIT) { + payload += 2; + off = (((uint16_t) *(payload-2)) << 8) + *(payload-1); + payload += off; + } + off = (payload - buf); + if (framelen == -1) { + framelen = r - off; + } else { + if (framelen < off || framelen > r) { + continue; + } + /* Only count payload length */ + framelen -= off; + } + + /* Forget the 0xFF, 0x03 HDLC markers */ + payload += 2; + framelen -= 2; + + if (framelen < 0) { + continue; + } + /* Get tunnel, session ID */ + tid = (((uint16_t) tidptr[0]) << 8) + (uint16_t) tidptr[1]; + sid = (((uint16_t) tidptr[2]) << 8) + (uint16_t) tidptr[3]; + + /* Only do hash lookup if it's not cached */ + /* TODO: Is this optimization really worthwhile? */ + if (!ses || tid != cache_tid || sid != cache_sid) { + ses = NULL; + tunnel = l2tp_tunnel_find_by_my_id(tid); + if (!tunnel) { + l2tp_set_errmsg("Unknown tunnel %d", (int) tid); + continue; + } + if (tunnel->state != TUNNEL_ESTABLISHED) { + /* Drop frame if tunnel in wrong state */ + continue; + } + /* TODO: Verify source address */ + ses = l2tp_tunnel_find_session(tunnel, sid); + if (!ses) { + l2tp_set_errmsg("Unknown session %d in tunnel %d", + (int) sid, (int) tid); + continue; + } + cache_tid = tid; + cache_sid = sid; + if (ses->state != SESSION_ESTABLISHED) { + /* Drop frame if session in wrong state */ + continue; + } + } + ses->call_ops->handle_ppp_frame(ses, payload, framelen); + /* Snoop, if necessary */ + if (ses->snooping) { + l2tp_session_lcp_snoop(ses, payload, framelen, 1); + } + } + + + /* A bit of slop on dgram size */ + dgram = l2tp_dgram_new(r); + if (!dgram) return NULL; + + dgram->bits = buf[0]; + dgram->version = buf[1]; + cursor = 2; + + if (dgram->bits & LENGTH_BIT) { + PULL_UINT16(buf, cursor, dgram->length); + if (dgram->length > r) { + l2tp_set_errmsg("Invalid length field %d greater than received datagram size %d", (int) dgram->length, r); + l2tp_dgram_free(dgram); + return NULL; + } + } else { + dgram->length = r; + } + + PULL_UINT16(buf, cursor, dgram->tid); + PULL_UINT16(buf, cursor, dgram->sid); + if (dgram->bits & SEQUENCE_BIT) { + PULL_UINT16(buf, cursor, dgram->Ns); + PULL_UINT16(buf, cursor, dgram->Nr); + } else { + dgram->Ns = 0; + dgram->Nr = 0; + } + + if (dgram->bits & OFFSET_BIT) { + PULL_UINT16(buf, cursor, dgram->off_size); + } else { + dgram->off_size = 0; + } + if (cursor > dgram->length) { + l2tp_set_errmsg("Invalid length of datagram %d", (int) dgram->length); + l2tp_dgram_free(dgram); + return NULL; + } + + dgram->payload_len = dgram->length - cursor; + memcpy(dgram->data, buf+cursor, dgram->payload_len); + + /* Pull off the message type */ + if (dgram->bits & OFFSET_BIT) { + l2tp_set_errmsg("Invalid control frame: O bit is set"); + l2tp_dgram_free(dgram); + return NULL; + } + + /* Handle ZLB */ + if (dgram->payload_len == 0) { + dgram->msg_type = MESSAGE_ZLB; + } else { + uint16_t avp_len; + msg = l2tp_dgram_pull_avp(dgram, NULL, &mandatory, &hidden, + &avp_len, &vendor, &type, &err); + if (!msg) { + l2tp_dgram_free(dgram); + return NULL; + } + if (type != AVP_MESSAGE_TYPE || vendor != VENDOR_IETF) { + l2tp_set_errmsg("Invalid control message: First AVP must be message type"); + l2tp_dgram_free(dgram); + return NULL; + } + if (avp_len != 2) { + l2tp_set_errmsg("Invalid length %d for message-type AVP", (int) avp_len+6); + l2tp_dgram_free(dgram); + return NULL; + } + if (!mandatory) { + l2tp_set_errmsg("Invalid control message: M bit not set on message-type AVP"); + l2tp_dgram_free(dgram); + return NULL; + } + if (hidden) { + l2tp_set_errmsg("Invalid control message: H bit set on message-type AVP"); + l2tp_dgram_free(dgram); + return NULL; + } + + dgram->msg_type = ((unsigned int) msg[0]) * 256 + + (unsigned int) msg[1]; + } + DBG(l2tp_db(DBG_XMIT_RCV, + "dgram_take_from_wire() -> %s\n", + l2tp_debug_describe_dgram(dgram))); + return dgram; +} + +/********************************************************************** +* %FUNCTION: dgram_send_to_wire +* %ARGUMENTS: +* dgram -- datagram to send +* to -- address to send datagram to +* %RETURNS: +* Whatever sendto returns. 0 on success, -1 on failure. +* %DESCRIPTION: +* Adjusts header fields from host byte order, then sends datagram +***********************************************************************/ +int +l2tp_dgram_send_to_wire(l2tp_dgram const *dgram, + struct sockaddr_in const *to) +{ + unsigned char buf[MAX_PACKET_LEN+128]; + socklen_t len = sizeof(struct sockaddr_in); + int cursor = 2; + size_t total_len; + unsigned char *len_ptr = NULL; + + DBG(l2tp_db(DBG_XMIT_RCV, + "dgram_send_to_wire() -> %s\n", + l2tp_debug_describe_dgram(dgram))); + buf[0] = dgram->bits; + buf[1] = dgram->version; + + if (dgram->bits & LENGTH_BIT) { + len_ptr = buf + cursor; + PUSH_UINT16(buf, cursor, dgram->length); + } + PUSH_UINT16(buf, cursor, dgram->tid); + PUSH_UINT16(buf, cursor, dgram->sid); + if (dgram->bits & SEQUENCE_BIT) { + PUSH_UINT16(buf, cursor, dgram->Ns); + PUSH_UINT16(buf, cursor, dgram->Nr); + } + if (dgram->bits & OFFSET_BIT) { + PUSH_UINT16(buf, cursor, dgram->off_size); + } + total_len = cursor + dgram->payload_len; + if (dgram->bits & LENGTH_BIT) { + *len_ptr++ = total_len / 256; + *len_ptr = total_len & 255; + } + memcpy(buf+cursor, dgram->data, dgram->payload_len); + return sendto(Sock, buf, total_len, 0, + (struct sockaddr const *) to, len); +} + +/********************************************************************** +* %FUNCTION: dgram_add_avp +* %ARGUMENTS: +* dgram -- the L2TP datagram +* tunnel -- the tunnel it will be sent on (for secret for hiding) +* mandatory -- if true, set the M bit +* len -- length of VALUE. NOT length of entire attribute! +* vendor -- vendor ID +* type -- attribute type +* val -- attribute value +* %RETURNS: +* 0 on success, -1 on failure +* %DESCRIPTION: +* Adds an AVP to the datagram. If AVP may be hidden, and we have +* a secret for it, then hide the AVP. +***********************************************************************/ +int +l2tp_dgram_add_avp(l2tp_dgram *dgram, + l2tp_tunnel *tunnel, + int mandatory, + uint16_t len, + uint16_t vendor, + uint16_t type, + void *val) +{ + static unsigned char hidden_buffer[1024]; + unsigned char const *random_data; + size_t random_len; + int hidden = 0; + + /* max len is 1023 - 6 */ + if (len > 1023 - 6) { + l2tp_set_errmsg("AVP length of %d too long", (int) len); + return -1; + } + + /* Do hiding where possible */ + if (tunnel && + tunnel->peer && + tunnel->peer->hide_avps && + vendor == VENDOR_IETF && + tunnel->peer->secret_len && + len <= 1021 - 6 && + avp_table[type].can_hide) { + if (!dgram->last_random) { + /* Add a random vector */ + if (dgram_add_random_vector_avp(dgram) < 0) return -1; + } + /* Get pointer to random data */ + random_data = dgram->data + dgram->last_random + 6; + random_len = GET_AVP_LEN(dgram->data + dgram->last_random) - 6; + + /* Hide the value into the buffer */ + dgram_do_hide(type, len, val, (unsigned char *) tunnel->peer->secret, + tunnel->peer->secret_len, + random_data, random_len, hidden_buffer); + + /* Length is increased by 2 */ + len += 2; + val = hidden_buffer; + hidden = 1; + } + + /* Adjust from payload len to actual len */ + len += 6; + + /* Does datagram have room? */ + if (dgram->cursor + len > dgram->alloc_len) { + l2tp_set_errmsg("No room for AVP of length %d", (int) len); + return -1; + } + + dgram->data[dgram->cursor] = 0; + if (mandatory) { + dgram->data[dgram->cursor] |= AVP_MANDATORY_BIT; + } + if (hidden) { + dgram->data[dgram->cursor] |= AVP_HIDDEN_BIT; + } + + dgram->data[dgram->cursor] |= (len >> 8); + dgram->data[dgram->cursor+1] = (len & 0xFF); + dgram->data[dgram->cursor+2] = (vendor >> 8); + dgram->data[dgram->cursor+3] = (vendor & 0xFF); + dgram->data[dgram->cursor+4] = (type >> 8); + dgram->data[dgram->cursor+5] = (type & 0xFF); + + if (len > 6) { + memcpy(dgram->data + dgram->cursor + 6, val, len-6); + } + if (type == AVP_RANDOM_VECTOR) { + /* Remember location of random vector */ + dgram->last_random = dgram->cursor; + } + dgram->cursor += len; + dgram->payload_len = dgram->cursor; + return 0; +} + +/********************************************************************** +* %FUNCTION: dgram_pull_avp +* %ARGUMENTS: +* dgram -- the L2TP datagram +* tunnel -- the tunnel it was received on (for secret for hiding) +* mandatory -- if set to true, the M bit was set. +* hidden -- if set to true, the value was hidden +* len -- set to length of value (after unhiding) +* vendor -- set to vendor ID +* type -- set to attribute type +* err -- set to true if an error occurs, false if not. +* %RETURNS: +* A pointer to a buffer containing the value, or NULL if an +* error occurs or there are no more AVPs. +* %DESCRIPTION: +* Pulls an AVP out of a received datagram. +***********************************************************************/ +unsigned char * +l2tp_dgram_pull_avp(l2tp_dgram *dgram, + l2tp_tunnel *tunnel, + int *mandatory, + int *hidden, + uint16_t *len, + uint16_t *vendor, + uint16_t *type, + int *err) +{ + static unsigned char val[1024]; + unsigned char *ans; + unsigned char *random_data; + size_t random_len; + int local_hidden; + int local_mandatory; + + *err = 1; + if (dgram->cursor >= dgram->payload_len) { + /* EOF */ + *err = 0; + return NULL; + } + + /* Get bits */ + if (!mandatory) { + mandatory = &local_mandatory; + } + *mandatory = dgram->data[dgram->cursor] & AVP_MANDATORY_BIT; + + if (!hidden) { + hidden = &local_hidden; + } + *hidden = dgram->data[dgram->cursor] & AVP_HIDDEN_BIT; + if (dgram->data[dgram->cursor] & AVP_RESERVED_BITS) { + l2tp_set_errmsg("AVP with reserved bits set to non-zero"); + return NULL; + } + + /* Get len */ + *len = ((unsigned int) (dgram->data[dgram->cursor] & 3)) * 256 + + dgram->data[dgram->cursor+1]; + if (*len < 6) { + l2tp_set_errmsg("Received AVP of length %d (too short)", (int) *len); + return NULL; + } + + if (dgram->cursor + *len > dgram->payload_len) { + l2tp_set_errmsg("Received AVP of length %d too long for rest of datagram", + (int) *len); + return NULL; + } + dgram->cursor += 2; + + PULL_UINT16(dgram->data, dgram->cursor, *vendor); + PULL_UINT16(dgram->data, dgram->cursor, *type); + + /* If we see a random vector, remember it */ + if (*vendor == VENDOR_IETF && *type == AVP_RANDOM_VECTOR) { + if (*hidden) { + l2tp_set_errmsg("Invalid random vector AVP has H bit set"); + return NULL; + } + dgram->last_random = dgram->cursor - 6; + } + + ans = dgram->data + dgram->cursor; + dgram->cursor += *len - 6; + if (*hidden) { + if (!tunnel) { + l2tp_set_errmsg("AVP cannot be hidden"); + return NULL; + } + if (*len < 8) { + l2tp_set_errmsg("Received hidden AVP of length %d (too short)", + (int) *len); + return NULL; + } + + if (!tunnel->peer) { + l2tp_set_errmsg("No peer??"); + return NULL; + } + if (!tunnel->peer->secret_len) { + l2tp_set_errmsg("No shared secret to unhide AVP"); + return NULL; + } + if (!dgram->last_random) { + l2tp_set_errmsg("Cannot unhide AVP unless Random Vector received first"); + return NULL; + } + + if (*type <= HIGHEST_AVP) { + if (!avp_table[*type].can_hide) { + l2tp_set_errmsg("AVP of type %s cannot be hidden, but H bit set", + avp_table[*type].name); + return NULL; + } + } + + /* Get pointer to random data */ + random_data = dgram->data + dgram->last_random + 6; + random_len = GET_AVP_LEN(dgram->data + dgram->last_random) - 6; + + /* Unhide value */ + ans = dgram_do_unhide(*type, len, ans, + (unsigned char *) tunnel->peer->secret, + tunnel->peer->secret_len, + random_data, random_len, val); + if (!ans) return NULL; + } + + /* Set len to length of value only */ + *len -= 6; + + *err = 0; + /* DBG(describe_pulled_avp(*vendor, *type, *len, *hidden, *mandatory, ans)); */ + return ans; + +} + +/********************************************************************** +* %FUNCTION: dgram_do_hide +* %ARGUMENTS: +* type -- attribute type +* len -- attribute length +* value -- attribute value +* secret -- shared tunnel secret +* secret_len -- length of secret +* random -- random data +* random_len -- length of random data +* output -- where to put result +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Hides AVP into output as described in RFC2661. Does not do +* padding (should we?) +***********************************************************************/ +static void +dgram_do_hide(uint16_t type, + uint16_t len, + unsigned char *value, + unsigned char const *secret, + size_t secret_len, + unsigned char const *random_data, + size_t random_len, + unsigned char *output) +{ + struct MD5Context ctx; + unsigned char t[2]; + unsigned char digest[16]; + size_t done; + size_t todo = len; + + /* Put type in network byte order */ + t[0] = type / 256; + t[1] = type & 255; + + /* Compute initial pad */ + MD5Init(&ctx); + MD5Update(&ctx, t, 2); + MD5Update(&ctx, secret, secret_len); + MD5Update(&ctx, random_data, random_len); + MD5Final(digest, &ctx); + + /* Insert length */ + output[0] = digest[0] ^ (len / 256); + output[1] = digest[1] ^ (len & 255); + output += 2; + done = 2; + + /* Insert value */ + while(todo) { + *output = digest[done] ^ *value; + ++output; + ++value; + --todo; + ++done; + if (done == 16 && todo) { + /* Compute new digest */ + done = 0; + MD5Init(&ctx); + MD5Update(&ctx, secret, secret_len); + MD5Update(&ctx, output-16, 16); + MD5Final(digest, &ctx); + } + } +} + +/********************************************************************** +* %FUNCTION: dgram_do_unhide +* %ARGUMENTS: +* type -- attribute type +* len -- attribute length (input = orig len; output = unhidden len) +* value -- attribute value (the hidden characters) +* secret -- shared tunnel secret +* secret_len -- length of secret +* random_data -- random data +* random_len -- length of random data +* output -- where to put result +* %RETURNS: +* pointer to "output" on success; NULL on failure. +* %DESCRIPTION: +* Un-hides AVP as in RFC2661 +***********************************************************************/ +static unsigned char * +dgram_do_unhide(uint16_t type, + uint16_t *len, + unsigned char *value, + unsigned char const *secret, + size_t secret_len, + unsigned char const *random_data, + size_t random_len, + unsigned char *output) +{ + struct MD5Context ctx; + unsigned char t[2]; + unsigned char digest[16]; + uint16_t tmplen; + unsigned char *orig_output = output; + size_t done, todo; + + /* Put type in network byte order */ + t[0] = type / 256; + t[1] = type & 255; + + /* Compute initial pad */ + MD5Init(&ctx); + MD5Update(&ctx, t, 2); + MD5Update(&ctx, secret, secret_len); + MD5Update(&ctx, random_data, random_len); + MD5Final(digest, &ctx); + + /* Get hidden length */ + tmplen = + ((uint16_t) (digest[0] ^ value[0])) * 256 + + (uint16_t) (digest[1] ^ value[1]); + + value += 2; + + if (tmplen > *len-8) { + l2tp_set_errmsg("Hidden length %d too long in AVP of length %d", + (int) tmplen, (int) *len); + return NULL; + } + + /* Adjust len. Add 6 to compensate for pseudo-header */ + *len = tmplen + 6; + + /* Decrypt remainder */ + done = 2; + todo = tmplen; + while(todo) { + *output = digest[done] ^ *value; + ++output; + ++value; + --todo; + ++done; + if (done == 16 && todo) { + /* Compute new digest */ + done = 0; + MD5Init(&ctx); + MD5Update(&ctx, secret, secret_len); + MD5Update(&ctx, value-16, 16); + MD5Final(digest, &ctx); + } + } + return orig_output; +} + +/********************************************************************** +* %FUNCTION: dgram_search_avp +* %ARGUMENTS: +* dgram -- the datagram +* tunnel -- associated tunnel +* mandatory -- set to 1 if M bit was set +* hidden -- set to 1 if H bit was set +* len -- set to length of payload +* vendor -- vendor ID we want +* type -- AVP type we want +* %RETURNS: +* A pointer to the AVP value if found, NULL if not +* %DESCRIPTION: +* Searches dgram for specific AVP type. +***********************************************************************/ +unsigned char * +l2tp_dgram_search_avp(l2tp_dgram *dgram, + l2tp_tunnel *tunnel, + int *mandatory, + int *hidden, + uint16_t *len, + uint16_t vendor, + uint16_t type) +{ + uint16_t svend, stype; + int err; + unsigned char *val; + size_t cursor = dgram->cursor; + size_t last_random = dgram->last_random; + while(1) { + val = l2tp_dgram_pull_avp(dgram, tunnel, mandatory, hidden, len, + &svend, &stype, &err); + if (!val) { + if (!err) { + l2tp_set_errmsg("AVP of vendor/type (%d, %d) not found", + (int) vendor, (int) type); + } + dgram->cursor = cursor; + dgram->last_random = last_random; + return NULL; + } + if (vendor == svend && type == stype) break; + } + dgram->cursor = cursor; + dgram->last_random = last_random; + return val; +} + +/********************************************************************** +* %FUNCTION: dgram_send_ppp_frame +* %ARGUMENTS: +* ses -- the session +* buf -- PPP frame. BUF MUST HAVE AT LEAST 16 BYTES OF SPACE AHEAD OF IT! +* len -- length of PPP frame +* %RETURNS: +* Whatever sendto returns +* %DESCRIPTION: +* Sends a PPP frame. TODO: Implement sequence numbers if required. +***********************************************************************/ +int +l2tp_dgram_send_ppp_frame(l2tp_session *ses, + unsigned char const *buf, + int len) +{ + int r; + unsigned char *real_buf = (unsigned char *) buf - 8; + l2tp_tunnel *tunnel = ses->tunnel; + + /* Drop frame if tunnel and/or session in wrong state */ + if (!tunnel || + tunnel->state != TUNNEL_ESTABLISHED || + ses->state != SESSION_ESTABLISHED) { + return 0; + } + + /* If there is a pending ack on the tunnel, cancel and ack now */ + if (tunnel->ack_handler) { + Event_DelHandler(tunnel->es, tunnel->ack_handler); + tunnel->ack_handler = NULL; + tunnel_send_ZLB(tunnel); + } + + real_buf[0] = 0; + real_buf[1] = 2; + real_buf[2] = (tunnel->assigned_id >> 8); + real_buf[3] = tunnel->assigned_id & 0xFF; + real_buf[4] = ses->assigned_id >> 8; + real_buf[5] = ses->assigned_id & 0xFF; + real_buf[6] = 0xFF; /* HDLC address */ + real_buf[7] = 0x03; /* HDLC control */ + r = sendto(Sock, real_buf, len+8, 0, + (struct sockaddr const *) &tunnel->peer_addr, + sizeof(struct sockaddr_in)); + /* Snoop, if necessary */ + if (ses->snooping) { + l2tp_session_lcp_snoop(ses, buf, len, 0); + } + return r; +} + +/********************************************************************** +* %FUNCTION: dgram_add_random_vector_avp +* %ARGUMENTS: +* dgram -- datagram +* %RETURNS: +* 0 on success; -1 on failure +* %DESCRIPTION: +* Adds a random-vector AVP to the datagram +***********************************************************************/ +static int +dgram_add_random_vector_avp(l2tp_dgram *dgram) +{ + unsigned char buf[16]; + + l2tp_random_fill(buf, sizeof(buf)); + + return l2tp_dgram_add_avp(dgram, NULL, MANDATORY, sizeof(buf), + VENDOR_IETF, AVP_RANDOM_VECTOR, buf); +} diff --git a/release/src/router/rp-l2tp/handlers/Makefile b/release/src/router/rp-l2tp/handlers/Makefile new file mode 100644 index 00000000..7c94a805 --- /dev/null +++ b/release/src/router/rp-l2tp/handlers/Makefile @@ -0,0 +1,45 @@ +# $Id: Makefile,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $ +# Makefile for LNS handlers +# LIC: GPL + +prefix=/usr +exec_prefix=${prefix} +mandir=${prefix}/man +docdir=/usr/doc/rp-pppoe-$(VERSION) +install=/usr/bin/install -c +install_dir=/usr/bin/install -c -d +sbindir=${exec_prefix}/sbin + +HANDLERS=sync-pppd.so cmd.so + +OBJS=pty.o sync-pppd.o dstring.o +SRCS=$(OBJS:.o=.c) +CFLAGS=-g -O2 -I.. -I../libevent -I../../libevent -Wall + +all: $(HANDLERS) l2tp-control + +l2tp-control: cmd-control.o + $(CC) -o l2tp-control cmd-control.o + +cmd-control.o: cmd-control.c + $(CC) -c -o $@ $^ + +%.o: %.c + $(CC) $(CFLAGS) -fPIC -c -o $@ $< + +sync-pppd.so: pty.o sync-pppd.o + $(CC) -shared -o $@ $^ + +cmd.so: cmd.o dstring.o + $(CC) -shared -o $@ $^ + +clean: + rm -f *.so *.o *~ + rm -f l2tp-control + +install: all + -mkdir -p $(RPM_INSTALL_ROOT)$(sbindir) + -mkdir -p $(RPM_INSTALL_ROOT)$(prefix)/lib/l2tp/plugins + $(install) -m 755 -s l2tp-control $(RPM_INSTALL_ROOT)$(sbindir) + $(install) -m 755 $(HANDLERS) $(RPM_INSTALL_ROOT)$(prefix)/lib/l2tp/plugins +distclean: clean diff --git a/release/src/router/rp-l2tp/handlers/Makefile.in b/release/src/router/rp-l2tp/handlers/Makefile.in new file mode 100644 index 00000000..03663866 --- /dev/null +++ b/release/src/router/rp-l2tp/handlers/Makefile.in @@ -0,0 +1,45 @@ +# $Id: Makefile.in,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $ +# Makefile for LNS handlers +# LIC: GPL + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +mandir=@mandir@ +docdir=@prefix@/doc/rp-pppoe-$(VERSION) +install=@INSTALL@ +install_dir=@INSTALL@ -d +sbindir=@sbindir@ + +HANDLERS=sync-pppd.so cmd.so + +OBJS=pty.o sync-pppd.o dstring.o +SRCS=$(OBJS:.o=.c) +CFLAGS=-g -O2 -I.. -I../libevent -I../../libevent -Wall + +all: $(HANDLERS) l2tp-control + +l2tp-control: cmd-control.o + @CC@ -o l2tp-control cmd-control.o + +cmd-control.o: cmd-control.c + @CC@ -c -o $@ $^ + +%.o: %.c + @CC@ $(CFLAGS) -fPIC -c -o $@ $< + +sync-pppd.so: pty.o sync-pppd.o + @CC@ -shared -o $@ $^ + +cmd.so: cmd.o dstring.o + @CC@ -shared -o $@ $^ + +clean: + rm -f *.so *.o *~ + rm -f l2tp-control + +install: all + -mkdir -p $(RPM_INSTALL_ROOT)$(sbindir) + -mkdir -p $(RPM_INSTALL_ROOT)$(prefix)/lib/l2tp/plugins + $(install) -m 755 -s l2tp-control $(RPM_INSTALL_ROOT)$(sbindir) + $(install) -m 755 $(HANDLERS) $(RPM_INSTALL_ROOT)$(prefix)/lib/l2tp/plugins +distclean: clean diff --git a/release/src/router/rp-l2tp/handlers/cmd-control.c b/release/src/router/rp-l2tp/handlers/cmd-control.c new file mode 100644 index 00000000..bec578ce --- /dev/null +++ b/release/src/router/rp-l2tp/handlers/cmd-control.c @@ -0,0 +1,108 @@ +/*********************************************************************** +* +* cmd-control.c +* +* Simple command-line program for sending commands to L2TP daemon +* +* Copyright (C) 2002 by Roaring Penguin Software Inc. +* +* This software may be distributed under the terms of the GNU General +* Public License, Version 2, or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id: cmd-control.c,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $"; +#include <stdio.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <syslog.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <sys/uio.h> + + +/********************************************************************** +* %FUNCTION: send_cmd +* %ARGUMENTS: +* cmd -- command to send to server +* %RETURNS: +* file descriptor for channel to server +* %DESCRIPTION: +* Sends a command to the server +***********************************************************************/ +static int +send_cmd(char const *cmd) +{ + struct sockaddr_un addr; + int fd; + struct iovec v[2]; + + /* Do not send zero-length command */ + if (!*cmd) { + errno = ESPIPE; + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_LOCAL; + strncpy(addr.sun_path, "/var/run/l2tpctrl", sizeof(addr.sun_path) - 1); + + fd = socket(AF_LOCAL, SOCK_STREAM, 0); + if (fd < 0) { + perror("socket"); + return -1; + } + if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("connect"); + close(fd); + return -1; + } + v[0].iov_base = (char *) cmd; + v[0].iov_len = strlen(cmd); + v[1].iov_base = "\n"; + v[1].iov_len = 1; + writev(fd, v, 2); + return fd; +} + +int +main(int argc, char *argv[]) +{ + int fd; + int n; + char buf[4096]; + + if (argc != 2) { + fprintf(stderr, "Usage: %s command\n", argv[0]); + return 1; + } + + fd = send_cmd(argv[1]); + if (fd < 0) { + return 1; + } + + for(;;) { + n = read(fd, buf, sizeof(buf)); + if (n < 0) { + perror("read"); + printf("\n"); + close(fd); + return 1; + } + if (n == 0) { + printf("\n"); + close(fd); + return 0; + } + write(1, buf, n); + } +} + diff --git a/release/src/router/rp-l2tp/handlers/cmd.c b/release/src/router/rp-l2tp/handlers/cmd.c new file mode 100644 index 00000000..8804a9f0 --- /dev/null +++ b/release/src/router/rp-l2tp/handlers/cmd.c @@ -0,0 +1,458 @@ +/*********************************************************************** +* +* l2tp/handlers/cmd.c +* +* Simple (VERY simple) command-processor for the L2TP daemon. +* +* Copyright (C) 2002 Roaring Penguin Software Inc. +* +* This software may be distributed under the terms of the GNU General +* Public License, Version 2, or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id: cmd.c,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $"; + +#include "l2tp.h" +#include "dstring.h" +#include "event_tcp.h" +#include <string.h> +#include <errno.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <netdb.h> +#include <signal.h> + +#define HANDLER_NAME "cmd" + +static int process_option(EventSelector *, char const *, char const *); +static void cmd_acceptor(EventSelector *es, int fd); +static void cmd_handler(EventSelector *es, + int fd, char *buf, int len, int flag, void *data); + + +static void cmd_exit(EventSelector *es, int fd); +static void cmd_start_session(EventSelector *es, int fd, char *buf); +static void cmd_stop_session(EventSelector *es, int fd, char *buf); +static void cmd_dump_sessions(EventSelector *es, int fd, char *buf); +static void cmd_reply(EventSelector *es, int fd, char const *msg); + +static option_handler my_option_handler = { + NULL, HANDLER_NAME, process_option +}; + +/* Socket name for commands */ +static char const *sockname = NULL; + +static l2tp_opt_descriptor my_opts[] = { + { "socket-path", OPT_TYPE_STRING, &sockname }, + { NULL, OPT_TYPE_BOOL, NULL} +}; + + +/********************************************************************** +* %FUNCTION: describe_session +* %ARGUMENTS: +* ses -- an L2TP session +* str -- a dynamic string to which description is appended +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Dumps session description into str +***********************************************************************/ +static void +describe_session(l2tp_session *ses, + dynstring *str) +{ + char buf[1024]; + + sprintf(buf, "Session %s MyID %d AssignedID %d", + (ses->we_are_lac ? "LAC" : "LNS"), + (int) ses->my_id, (int) ses->assigned_id); + dynstring_append(str, buf); + sprintf(buf, " State %s\n", + l2tp_session_state_name(ses)); + dynstring_append(str, buf); +} + +/********************************************************************** +* %FUNCTION: describe_tunnel +* %ARGUMENTS: +* tunnel -- an L2TP tunnel +* str -- a dynamic string to which description is appended +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Dumps tunnel description into str +***********************************************************************/ +static void +describe_tunnel(l2tp_tunnel *tunnel, + dynstring *str) +{ + char buf[1024]; + l2tp_session *ses; + void *cursor; + + sprintf(buf, "Tunnel MyID %d AssignedID %d", + (int) tunnel->my_id, (int) tunnel->assigned_id); + dynstring_append(str, buf); + sprintf(buf, " NumSessions %d", (int) hash_num_entries(&tunnel->sessions_by_my_id)); + dynstring_append(str, buf); + sprintf(buf, " PeerIP %s State %s\n", inet_ntoa(tunnel->peer_addr.sin_addr), + l2tp_tunnel_state_name(tunnel)); + + dynstring_append(str, buf); + + /* Describe each session */ + for (ses = l2tp_tunnel_first_session(tunnel, &cursor); + ses; + ses = l2tp_tunnel_next_session(tunnel, &cursor)) { + describe_session(ses, str); + } +} + +/********************************************************************** +* %FUNCTION: handler_init +* %ARGUMENTS: +* es -- event selector +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Initializes the command processor's option handler. We do not +* actually start a command processor until last option has been parsed. +***********************************************************************/ +void +handler_init(EventSelector *es) +{ + l2tp_option_register_section(&my_option_handler); +} + +/********************************************************************** +* %FUNCTION: process_option +* %ARGUMENTS: +* es -- event selector +* name, value -- name and value of option +* %RETURNS: +* 0 on success; -1 on failure. +* %DESCRIPTION: +* Processes options. When last option has been processed, begins +* command handler. +***********************************************************************/ +static int +process_option(EventSelector *es, + char const *name, + char const *value) +{ + struct sockaddr_un addr; + socklen_t len; + int fd; + + if (!strcmp(name, "*begin*")) return 0; + if (strcmp(name, "*end*")) { + return l2tp_option_set(es, name, value, my_opts); + } + + /* We have hit the end of our options. Open command socket */ + if (!sockname) { + sockname = "/var/run/l2tpctrl"; + } + + (void) remove(sockname); + fd = socket(AF_LOCAL, SOCK_STREAM, 0); + if (fd < 0) { + l2tp_set_errmsg("cmd: process_option: socket: %s", strerror(errno)); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_LOCAL; + strncpy(addr.sun_path, sockname, sizeof(addr.sun_path) - 1); + + len = sizeof(addr); + if (bind(fd, (struct sockaddr *) &addr, SUN_LEN(&addr)) < 0) { + l2tp_set_errmsg("cmd: process_option: bind: %s", strerror(errno)); + close(fd); + return -1; + } + (void) chmod(sockname, 0600); + if (listen(fd, 5) < 0) { + l2tp_set_errmsg("cmd: process_option: listen: %s", strerror(errno)); + close(fd); + return -1; + } + + /* Ignore sigpipe */ + signal(SIGPIPE, SIG_IGN); + + /* Add an accept handler */ + if (!EventTcp_CreateAcceptor(es, fd, cmd_acceptor)) { + l2tp_set_errmsg("cmd: process_option: EventTcp_CreateAcceptor: %s", strerror(errno)); + close(fd); + return -1; + } + return 0; +} + +/********************************************************************** +* %FUNCTION: cmd_acceptor +* %ARGUMENTS: +* es -- event selector +* fd -- file descriptor of accepted connection +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Accepts a control connection and sets up read event. +***********************************************************************/ +static void +cmd_acceptor(EventSelector *es, int fd) +{ + EventTcp_ReadBuf(es, fd, 512, '\n', cmd_handler, 5, NULL); +} + +/********************************************************************** +* %FUNCTION: cmd_handler +* %ARGUMENTS: +* es -- event selector +* fd -- file descriptor +* buf -- buffer which was read +* len -- length of data +* flag -- flags +* data -- not used +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Processes a command from the user +***********************************************************************/ +static void +cmd_handler(EventSelector *es, + int fd, + char *buf, + int len, + int flag, + void *data) +{ + char word[512]; + + if (flag == EVENT_TCP_FLAG_IOERROR || flag == EVENT_TCP_FLAG_TIMEOUT) { + close(fd); + return; + } + if (len < 511) { + buf[len+1] = 0; + } else { + buf[len] = 0; + } + + /* Chop off newline */ + if (len && (buf[len-1] == '\n')) { + buf[len-1] = 0; + } + + /* Get first word */ + buf = (char *) l2tp_chomp_word(buf, word); + + if (!strcmp(word, "exit")) { + cmd_exit(es, fd); + } else if (!strcmp(word, "start-session")) { + cmd_start_session(es, fd, buf); + } else if (!strcmp(word, "stop-session")) { + cmd_stop_session(es, fd, buf); + } else if (!strcmp(word, "dump-sessions")) { + cmd_dump_sessions(es, fd, buf); + } else { + cmd_reply(es, fd, "ERR Unknown command"); + } +} + +/********************************************************************** +* %FUNCTION: cmd_reply +* %ARGUMENTS: +* es -- event selector +* fd -- file descriptor +* msg -- message +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Schedules reply to be shot back to user +***********************************************************************/ +static void +cmd_reply(EventSelector *es, + int fd, + char const *msg) +{ + EventTcp_WriteBuf(es, fd, (char *) msg, strlen(msg), NULL, 10, NULL); +} + +/********************************************************************** +* %FUNCTION: cmd_exit +* %ARGUMENTS: +* es -- Event selector +* fd -- command file descriptor +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Tears down tunnels and quits +***********************************************************************/ +static void +cmd_exit(EventSelector *es, + int fd) +{ + cmd_reply(es, fd, "OK Shutting down"); + l2tp_tunnel_stop_all("Stopped by system administrator"); + l2tp_cleanup(); + exit(0); +} + +/********************************************************************** +* %FUNCTION: cmd_start_session +* %ARGUMENTS: +* es -- event selector +* fd -- command file descriptor +* buf -- rest of command from user +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Starts an L2TP session, if possible +***********************************************************************/ +static void +cmd_start_session(EventSelector *es, + int fd, + char *buf) +{ + char peer[512]; + struct hostent *he; + struct sockaddr_in haddr; + l2tp_peer *p; + l2tp_session *sess; + + buf = (char *) l2tp_chomp_word(buf, peer); + he = gethostbyname(peer); + if (!he) { + cmd_reply(es, fd, "ERR Unknown peer - gethostbyname failed"); + return; + } + memcpy(&haddr.sin_addr, he->h_addr, sizeof(haddr.sin_addr)); + p = l2tp_peer_find(&haddr, NULL); + if (!p) { + cmd_reply(es, fd, "ERR Unknown peer"); + return; + } + sess = l2tp_session_call_lns(p, "foobar", es, NULL); + if (!sess) { + cmd_reply(es, fd, l2tp_get_errmsg()); + return; + } + + /* Form reply */ + sprintf(peer, "OK %d %d", + (int) sess->tunnel->my_id, + (int) sess->my_id); + cmd_reply(es, fd, peer); +} + +/********************************************************************** +* %FUNCTION: cmd_stop_session +* %ARGUMENTS: +* es -- event selector +* fd -- command file descriptor +* buf -- rest of command from user +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Stops an L2TP session, identified by (Tunnel, Session) pair. +***********************************************************************/ +static void +cmd_stop_session(EventSelector *es, + int fd, + char *buf) +{ + char junk[512]; + l2tp_tunnel *tunnel; + l2tp_session *sess; + unsigned int x; + + buf = (char *) l2tp_chomp_word(buf, junk); + if (sscanf(junk, "%u", &x) != 1) { + cmd_reply(es, fd, "ERR Syntax error: stop-session tid sid"); + return; + } + tunnel = l2tp_tunnel_find_by_my_id((uint16_t) x); + if (!tunnel) { + cmd_reply(es, fd, "ERR No such tunnel"); + return; + } + + + buf = (char *) l2tp_chomp_word(buf, junk); + if (sscanf(junk, "%u", &x) != 1) { + cmd_reply(es, fd, "ERR Syntax error: stop-session tid sid"); + return; + } + sess = l2tp_tunnel_find_session(tunnel, (uint16_t) x); + + if (!sess) { + cmd_reply(es, fd, "ERR No such session"); + return; + } + + /* Stop the session */ + l2tp_session_send_CDN(sess, RESULT_GENERAL_REQUEST, ERROR_OK, + "Call terminated by operator"); + cmd_reply(es, fd, "OK Session stopped"); +} + +/********************************************************************** +* %FUNCTION: cmd_dump_sessions +* %ARGUMENTS: +* es -- event selector +* fd -- command file descriptor +* buf -- rest of command from user +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Dumps info about all currently-active tunnels and sessions +***********************************************************************/ +static void +cmd_dump_sessions(EventSelector *es, + int fd, + char *buf) +{ + dynstring str; + char tmp[256]; + void *cursor; + char const *ans; + l2tp_tunnel *tunnel; + + dynstring_init(&str); + + dynstring_append(&str, "OK\n"); + + /* Print info about each tunnel */ + sprintf(tmp, "NumL2TPTunnels %d\n", l2tp_num_tunnels()); + dynstring_append(&str, tmp); + + for (tunnel = l2tp_first_tunnel(&cursor); + tunnel; + tunnel = l2tp_next_tunnel(&cursor)) { + describe_tunnel(tunnel, &str); + } + + /* If something went wrong, say so... */ + ans = dynstring_data(&str); + if (!ans) { + cmd_reply(es, fd, "ERR Out of memory"); + return; + } + + cmd_reply(es, fd, ans); + dynstring_free(&str); +} + diff --git a/release/src/router/rp-l2tp/handlers/dstring.c b/release/src/router/rp-l2tp/handlers/dstring.c new file mode 100644 index 00000000..718fe509 --- /dev/null +++ b/release/src/router/rp-l2tp/handlers/dstring.c @@ -0,0 +1,139 @@ +/*********************************************************************** +* +* l2tp/handlers/dstring.c +* +* Implements simple buffer which grows to accomodate accumulated string +* data +* +* Copyright (C) 2002 by Roaring Penguin Software Inc. +* +* This software may be distributed under the terms of the GNU General +* Public License, Version 2, or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id: dstring.c,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $"; + +#define INITIAL_SIZE 128 +#define GROW_FACTOR 2 + +#include "dstring.h" +#include <stdlib.h> +#include <string.h> + +static int +expand_for_size(dynstring *str, size_t len) +{ + size_t newlen; + + if (len >= str->alloc_size * GROW_FACTOR) { + newlen = len + 1; + } else { + newlen = str->alloc_size * GROW_FACTOR; + } + + str->data = realloc(str->data, newlen); + if (!str->data) { + str->alloc_size = 0; + str->actual_size = 0; + return -1; + } + + str->alloc_size = newlen; + return 0; +} + +/********************************************************************** +* %FUNCTION: dynstring_init +* %ARGUMENTS: +* str -- a dynstring structure +* %RETURNS: +* 0 on success; -1 on failure +* %DESCRIPTION: +* Initializes dynamic string +***********************************************************************/ +int +dynstring_init(dynstring *str) +{ + str->data = malloc(INITIAL_SIZE); + if (!str->data) return -1; + + str->alloc_size = INITIAL_SIZE; + str->actual_size = 0; + str->data[0] = 0; + return 0; +} + +/********************************************************************** +* %FUNCTION: dynstring_free +* %ARGUMENTS: +* str -- a dynstring structure +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Frees resources used by dynamic string +***********************************************************************/ +void +dynstring_free(dynstring *str) +{ + if (str->data) { + free(str->data); + str->data = NULL; + } + str->alloc_size = 0; + str->actual_size = 0; +} + +/********************************************************************** +* %FUNCTION: dynstring_append +* %ARGUMENTS: +* str -- dynamic string +* s2 -- string to append +* %RETURNS: +* -1 on failure; 0 on success +* %DESCRIPTION: +* Appends s2 to str +***********************************************************************/ +int +dynstring_append(dynstring *str, char const *s2) +{ + return dynstring_append_len(str, s2, strlen(s2)); +} + +/********************************************************************** +* %FUNCTION: dynstring_append_len +* %ARGUMENTS: +* str -- dynamic string +* s2 -- string to append +* len -- length of s2 +* %RETURNS: +* -1 on failure; 0 on success +* %DESCRIPTION: +* Appends s2 to str +***********************************************************************/ +int +dynstring_append_len(dynstring *str, char const *s2, size_t len) +{ + if (!len) return 0; + if (!str->data) return -1; + + if (len + str->actual_size >= str->alloc_size) { + if (expand_for_size(str, str->actual_size + len) < 0) { + return -1; + } + } + + memcpy(str->data + str->actual_size, s2, len); + str->actual_size += len; + str->data[str->actual_size] = 0; + return 0; +} + +char const * +dynstring_data(dynstring *str) +{ + return str->data; +} diff --git a/release/src/router/rp-l2tp/handlers/dstring.h b/release/src/router/rp-l2tp/handlers/dstring.h new file mode 100644 index 00000000..f82a7ea6 --- /dev/null +++ b/release/src/router/rp-l2tp/handlers/dstring.h @@ -0,0 +1,26 @@ +/*********************************************************************** +* +* l2tp/handlers/dstring.h +* +* Declares simple buffer which grows to accomodate accumulated string +* data +* +* Copyright (C) 2002 by Roaring Penguin Software Inc. +* +* LIC: GPL +* +***********************************************************************/ + +#include <stdlib.h> + +typedef struct { + size_t alloc_size; + size_t actual_size; + char *data; +} dynstring; + +int dynstring_init(dynstring *str); +void dynstring_free(dynstring *str); +int dynstring_append(dynstring *str, char const *s2); +int dynstring_append_len(dynstring *str, char const *s2, size_t len); +char const *dynstring_data(dynstring *str); diff --git a/release/src/router/rp-l2tp/handlers/pty.c b/release/src/router/rp-l2tp/handlers/pty.c new file mode 100644 index 00000000..ed880a94 --- /dev/null +++ b/release/src/router/rp-l2tp/handlers/pty.c @@ -0,0 +1,96 @@ +/*********************************************************************** +* +* pty.c +* +* Code for dealing with pseudo-tty's for running pppd. +* +* Copyright (C) 2002 Roaring Penguin Software Inc. +* +* This software may be distributed under the terms of the GNU General +* Public License, Version 2, or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id: pty.c,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $"; + +#include "../l2tp.h" +#include <sys/ioctl.h> +#include <termios.h> +#include <fcntl.h> +#include <stdio.h> +#ifndef N_HDLC +#include <linux/termios.h> +#endif + +/********************************************************************** +* %FUNCTION: pty_get +* %ARGUMENTS: +* mfp -- pointer to master file descriptor +* sfp -- pointer to slave file descriptor +* %RETURNS: +* 0 on success, -1 on failure +* %DESCRIPTION: +* Opens a PTY and sets line discipline to N_HDLC for ppp. +* Taken almost verbatim from Linux pppd code. +***********************************************************************/ +int +pty_get(int *mfp, int *sfp) +{ + char pty_name[24]; + struct termios tios; + int mfd, sfd; + int disc = N_HDLC; + + mfd = -1; + sfd = -1; + + mfd = open("/dev/ptmx", O_RDWR); + if (mfd >= 0) { + int ptn; + if (ioctl(mfd, TIOCGPTN, &ptn) >= 0) { + snprintf(pty_name, sizeof(pty_name), "/dev/pts/%d", ptn); + ptn = 0; + if (ioctl(mfd, TIOCSPTLCK, &ptn) < 0) { + /* warn("Couldn't unlock pty slave %s: %m", pty_name); */ + } + if ((sfd = open(pty_name, O_RDWR | O_NOCTTY)) < 0) { + /* warn("Couldn't open pty slave %s: %m", pty_name); */ + } + } + } + + if (sfd < 0 || mfd < 0) { + if (sfd >= 0) close(sfd); + if (mfd >= 0) close(mfd); + return -1; + } + + *mfp = mfd; + *sfp = sfd; + if (tcgetattr(sfd, &tios) == 0) { + tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB); + tios.c_cflag |= CS8 | CREAD | CLOCAL; + tios.c_iflag = IGNPAR; + tios.c_oflag = 0; + tios.c_lflag = 0; + tcsetattr(sfd, TCSAFLUSH, &tios); + } + if (ioctl(sfd, TIOCSETD, &disc) < 0) { + l2tp_set_errmsg("Unable to set line discipline to N_HDLC"); + close(mfd); + close(sfd); + return -1; + } + disc = N_HDLC; + if (ioctl(mfd, TIOCSETD, &disc) < 0) { + l2tp_set_errmsg("Unable to set line discipline to N_HDLC"); + close(mfd); + close(sfd); + return -1; + } + return 0; +} + diff --git a/release/src/router/rp-l2tp/handlers/sync-pppd.c b/release/src/router/rp-l2tp/handlers/sync-pppd.c new file mode 100644 index 00000000..bc20ef49 --- /dev/null +++ b/release/src/router/rp-l2tp/handlers/sync-pppd.c @@ -0,0 +1,443 @@ +/*********************************************************************** +* +* sync-pppd.c +* +* An LNS handler which starts pppd attached to a PTY in +* synchronous mode. +* +* Copyright (C) 2002 by Roaring Penguin Software Inc. +* +* This software may be distributed under the terms of the GNU General +* Public License, Version 2, or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id: sync-pppd.c,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $"; + +#include "l2tp.h" +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <fcntl.h> + +#define HANDLER_NAME "sync-pppd" + +#define DEFAULT_PPPD_PATH "/usr/sbin/pppd" + +#define MAX_FDS 256 + +extern int pty_get(int *mfp, int *sfp); +static int establish_session(l2tp_session *ses); +static void close_session(l2tp_session *ses, char const *reason, int may_reestablish); +static void handle_frame(l2tp_session *ses, unsigned char *buf, size_t len); + +/* Options for invoking pppd */ +#define MAX_OPTS 64 +static char *pppd_lns_options[MAX_OPTS+1]; +static char *pppd_lac_options[MAX_OPTS+1]; +static int num_pppd_lns_options = 0; +static int num_pppd_lac_options = 0; +static int use_unit_option = 0; +static char *pppd_path = NULL; + +#define PUSH_LNS_OPT(x) pppd_lns_options[num_pppd_lns_options++] = (x) +#define PUSH_LAC_OPT(x) pppd_lac_options[num_pppd_lac_options++] = (x) + +/* Our call ops */ +static l2tp_call_ops my_ops = { + establish_session, + close_session, + handle_frame +}; + +/* The slave process */ +struct slave { + EventSelector *es; /* Event selector */ + l2tp_session *ses; /* L2TP session we're hooked to */ + pid_t pid; /* PID of child PPPD process */ + int fd; /* File descriptor for event-handler loop */ + EventHandler *event; /* Event handler */ +}; + +static int handle_lac_opts(EventSelector *es, l2tp_opt_descriptor *desc, char const *value); +static int handle_lns_opts(EventSelector *es, l2tp_opt_descriptor *desc, char const *value); + +/* Options */ +static l2tp_opt_descriptor my_opts[] = { + /* name type addr */ + { "lac-pppd-opts", OPT_TYPE_CALLFUNC, (void *) handle_lac_opts}, + { "lns-pppd-opts", OPT_TYPE_CALLFUNC, (void *) handle_lns_opts}, + { "set-ppp-if-name", OPT_TYPE_BOOL, &use_unit_option}, + { "pppd-path", OPT_TYPE_STRING, &pppd_path}, + { NULL, OPT_TYPE_BOOL, NULL } +}; + +static int +process_option(EventSelector *es, char const *name, char const *value) +{ + if (!strcmp(name, "*begin*")) return 0; + if (!strcmp(name, "*end*")) return 0; + return l2tp_option_set(es, name, value, my_opts); +} + +static option_handler my_option_handler = { + NULL, HANDLER_NAME, process_option +}; + +static int +handle_lac_opts(EventSelector *es, + l2tp_opt_descriptor *desc, char const *value) +{ + char word[512]; + while (value && *value) { + value = l2tp_chomp_word(value, word); + if (!word[0]) break; + if (num_pppd_lac_options < MAX_OPTS) { + char *x = strdup(word); + if (x) PUSH_LAC_OPT(x); + pppd_lac_options[num_pppd_lac_options] = NULL; + } else { + break; + } + } + return 0; +} + +static int +handle_lns_opts(EventSelector *es, + l2tp_opt_descriptor *desc, char const *value) +{ + char word[512]; + while (value && *value) { + value = l2tp_chomp_word(value, word); + if (!word[0]) break; + if (num_pppd_lns_options < MAX_OPTS) { + char *x = strdup(word); + if (x) PUSH_LNS_OPT(x); + pppd_lns_options[num_pppd_lns_options] = NULL; + } else { + break; + } + } + return 0; +} + +/********************************************************************** +* %FUNCTION: handle_frame +* %ARGUMENTS: +* ses -- l2tp session +* buf -- received PPP frame +* len -- length of frame +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Shoots the frame to PPP's pty +***********************************************************************/ +static void +handle_frame(l2tp_session *ses, + unsigned char *buf, + size_t len) +{ + struct slave *sl = ses->private; + int n; + + if (!sl) return; + + /* Add framing bytes */ + *--buf = 0x03; + *--buf = 0xFF; + len += 2; + + /* TODO: Add error checking */ + n = write(sl->fd, buf, len); +} + +/********************************************************************** +* %FUNCTION: close_session +* %ARGUMENTS: +* ses -- L2TP session +* reason -- reason why session is closing +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Kills pppd. +***********************************************************************/ +static void +close_session(l2tp_session *ses, char const *reason, int may_reestablish) +{ + l2tp_tunnel *tunnel = ses->tunnel; + struct slave *sl = ses->private; + if (!sl) return; + + /* Detach slave */ + ses->private = NULL; + sl->ses = NULL; + + kill(SIGTERM, sl->pid); + close(sl->fd); + sl->fd = -1; + Event_DelHandler(sl->es, sl->event); + sl->event = NULL; + + /* Re-establish session if desired */ + if (may_reestablish && tunnel->peer->persist && tunnel->peer->fail < tunnel->peer->maxfail) { + struct timeval t; + + t.tv_sec = tunnel->peer->holdoff; + t.tv_usec = 0; + Event_AddTimerHandler(tunnel->es, t, l2tp_tunnel_reestablish, tunnel->peer); + } +} + +/********************************************************************** +* %FUNCTION: slave_exited +* %ARGUMENTS: +* pid -- PID of exiting slave +* status -- exit status of slave +* data -- the slave structure +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Handles an exiting slave +***********************************************************************/ +static void +slave_exited(pid_t pid, int status, void *data) +{ + l2tp_session *ses; + struct slave *sl = (struct slave *) data; + if (!sl) return; + + ses = sl->ses; + + if (sl->fd >= 0) close(sl->fd); + if (sl->event) Event_DelHandler(sl->es, sl->event); + + if (ses) { + ses->private = NULL; + l2tp_session_send_CDN(ses, RESULT_GENERAL_REQUEST, 0, + "pppd process exited"); + } + free(sl); +} + +/********************************************************************** +* %FUNCTION: readable +* %ARGUMENTS: +* es -- event selector +* fd -- file descriptor +* flags -- we ignore +* data -- the L2TP session +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Handles readability on PTY; shoots PPP frame over tunnel +***********************************************************************/ +static void +readable(EventSelector *es, int fd, unsigned int flags, void *data) +{ + unsigned char buf[4096+EXTRA_HEADER_ROOM]; + int n; + l2tp_session *ses = (l2tp_session *) data; + int iters = 5; + + /* It seems to be better to read in a loop than to go + back to select loop. However, don't loop forever, or + we could have a DoS potential */ + while(iters--) { + /* EXTRA_HEADER_ROOM bytes extra space for l2tp header */ + n = read(fd, buf+EXTRA_HEADER_ROOM, sizeof(buf)-EXTRA_HEADER_ROOM); + + /* TODO: Check this.... */ + if (n <= 2) return; + + if (!ses) continue; + + /* Chop off framing bytes */ + l2tp_dgram_send_ppp_frame(ses, buf+EXTRA_HEADER_ROOM+2, n-2); + } +} + +/********************************************************************** +* %FUNCTION: establish_session +* %ARGUMENTS: +* ses -- the L2TP session +* %RETURNS: +* 0 if session could be established, -1 otherwise. +* %DESCRIPTION: +* Forks a pppd process and connects fd to pty +***********************************************************************/ +static int +establish_session(l2tp_session *ses) +{ + int m_pty, s_pty; + pid_t pid; + EventSelector *es = ses->tunnel->es; + struct slave *sl = malloc(sizeof(struct slave)); + int i; + char unit[32]; + + ses->private = NULL; + if (!sl) return -1; + sl->ses = ses; + sl->es = es; + + /* Get pty */ + if (pty_get(&m_pty, &s_pty) < 0) { + free(sl); + return -1; + } + + /* Fork */ + pid = fork(); + if (pid == (pid_t) -1) { + free(sl); + return -1; + } + + if (pid) { + int flags; + + /* In the parent */ + sl->pid = pid; + + /* Set up handler for when pppd exits */ + Event_HandleChildExit(es, pid, slave_exited, sl); + + /* Close the slave tty */ + close(s_pty); + + sl->fd = m_pty; + + /* Set slave FD non-blocking */ + flags = fcntl(sl->fd, F_GETFL); + if (flags >= 0) fcntl(sl->fd, F_SETFL, (long) flags | O_NONBLOCK); + + /* Handle readability on slave end */ + sl->event = Event_AddHandler(es, m_pty, EVENT_FLAG_READABLE, + readable, ses); + + ses->private = sl; + return 0; + } + + /* In the child. Exec pppd */ + /* Close all file descriptors except s_pty */ + for (i=0; i<MAX_FDS; i++) { + if (i != s_pty) close(i); + } + + /* Dup s_pty onto stdin and stdout */ + dup2(s_pty, 0); + dup2(s_pty, 1); + if (s_pty > 1) close(s_pty); + + /* Create unit */ + sprintf(unit, "%d", (int) getpid()); + + if (ses->we_are_lac) { + char **lac_opt; + + /* Push a unit option */ + if (use_unit_option && num_pppd_lac_options <= MAX_OPTS-2) { + PUSH_LAC_OPT("unit"); + PUSH_LAC_OPT(unit); + } + /* push peer specific options */ + lac_opt = ses->tunnel->peer->lac_options; + while (*lac_opt) { + if (num_pppd_lac_options <= MAX_OPTS-1) { + PUSH_LAC_OPT(*lac_opt); + ++lac_opt; + } else { + break; + } + } + if (pppd_path) { + execv(pppd_path, pppd_lac_options); + } else { + execv(DEFAULT_PPPD_PATH, pppd_lac_options); + } + } else { + char **lns_opt; + + /* Push a unit option */ + if (use_unit_option && num_pppd_lns_options <= MAX_OPTS-2) { + PUSH_LNS_OPT("unit"); + PUSH_LNS_OPT(unit); + } + /* push peer specific options */ + lns_opt = ses->tunnel->peer->lns_options; + while (*lns_opt) { + if (num_pppd_lns_options <= MAX_OPTS-1) { + PUSH_LNS_OPT(*lns_opt); + ++lns_opt; + } else { + break; + } + } + if (pppd_path) { + execv(pppd_path, pppd_lns_options); + } else { + execv(DEFAULT_PPPD_PATH, pppd_lns_options); + } + } + + /* Doh.. execl failed */ + _exit(1); +} + +static l2tp_lns_handler my_lns_handler = { + NULL, + HANDLER_NAME, + &my_ops +}; + +static l2tp_lac_handler my_lac_handler = { + NULL, + HANDLER_NAME, + &my_ops +}; + +void +handler_init(EventSelector *es) +{ + l2tp_session_register_lns_handler(&my_lns_handler); + l2tp_session_register_lac_handler(&my_lac_handler); + l2tp_option_register_section(&my_option_handler); + + PUSH_LNS_OPT("pppd"); + PUSH_LNS_OPT("sync"); + PUSH_LNS_OPT("nodetach"); + PUSH_LNS_OPT("noaccomp"); + PUSH_LNS_OPT("nobsdcomp"); + PUSH_LNS_OPT("nodeflate"); + PUSH_LNS_OPT("nopcomp"); + PUSH_LNS_OPT("novj"); + PUSH_LNS_OPT("novjccomp"); +#if 0 + PUSH_LNS_OPT("logfile"); + PUSH_LNS_OPT("/dev/null"); + PUSH_LNS_OPT("nolog"); +#endif + pppd_lns_options[num_pppd_lns_options] = NULL; + + PUSH_LAC_OPT("pppd"); + PUSH_LAC_OPT("sync"); + PUSH_LAC_OPT("nodetach"); + PUSH_LAC_OPT("noaccomp"); + PUSH_LAC_OPT("nobsdcomp"); + PUSH_LAC_OPT("nodeflate"); + PUSH_LAC_OPT("nopcomp"); + PUSH_LAC_OPT("novj"); + PUSH_LAC_OPT("novjccomp"); +#if 0 + PUSH_LAC_OPT("logfile"); + PUSH_LAC_OPT("/dev/null"); + PUSH_LAC_OPT("nolog"); +#endif + pppd_lac_options[num_pppd_lac_options] = NULL; +} + diff --git a/release/src/router/rp-l2tp/install-sh b/release/src/router/rp-l2tp/install-sh new file mode 100755 index 00000000..58719246 --- /dev/null +++ b/release/src/router/rp-l2tp/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/release/src/router/rp-l2tp/l2tp.conf b/release/src/router/rp-l2tp/l2tp.conf new file mode 100644 index 00000000..d32263da --- /dev/null +++ b/release/src/router/rp-l2tp/l2tp.conf @@ -0,0 +1,38 @@ +# comment + +# Global section (by default, we start in global mode) +global + +# Load handlers +load-handler "sync-pppd.so" +load-handler "cmd.so" + +# Bind address +listen-port 1701 + +# Configure the sync-pppd handler. You MUST have a "section sync-pppd" line +# even if you don't set any options. +section sync-pppd +lns-pppd-opts "require-pap 10.0.0.1:10.0.0.2 lcp-echo-interval 30 lcp-echo-failure 6" +lac-pppd-opts "user example name example noipdefault ipcp-accept-local ipcp-accept-remote lcp-echo-interval 30 lcp-echo-failure 6" + +# Peer section +section peer +peer 192.168.2.3 +secret s3cr3t +port 1701 +lac-handler sync-pppd +lns-handler sync-pppd +hide-avps yes + +section peer +peer 192.168.2.9 +# No secret - no authentication +port 1701 +lac-handler sync-pppd +lns-handler sync-pppd +hide-avps no + +# Configure the cmd handler. You MUST have a "section cmd" line +# even if you don't set any options. +section cmd diff --git a/release/src/router/rp-l2tp/l2tp.h b/release/src/router/rp-l2tp/l2tp.h new file mode 100644 index 00000000..0116ecaf --- /dev/null +++ b/release/src/router/rp-l2tp/l2tp.h @@ -0,0 +1,484 @@ +/*********************************************************************** +* +* lt2p.h +* +* Header file for L2TP definitions. +* +* Copyright (C) 2002 Roaring Penguin Software Inc. +* +* LIC: GPL +* +***********************************************************************/ + +#ifndef L2TP_H +#define L2TP_H + +#include <stdint.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "hash.h" +#include "event.h" + +#define ENABLE_DEBUG + +#ifdef ENABLE_DEBUG +#define DBG(x) x +#else +#define DBG(x) (void) 0 +#endif + +#define MD5LEN 16 /* Length of MD5 hash */ + +/* Debug bitmasks */ +#define DBG_TUNNEL 1 /* Tunnel-related events */ +#define DBG_XMIT_RCV 2 /* Datagram transmission/reception */ +#define DBG_AUTH 4 /* Authentication */ +#define DBG_SESSION 8 /* Session-related events */ +#define DBG_FLOW 16 /* Flow control code */ +#define DBG_AVP 32 /* Hiding/showing of AVP's */ +#define DBG_SNOOP 64 /* Snooping in on LCP */ + +/* Maximum size of L2TP datagram we accept... kludge... */ +#define MAX_PACKET_LEN 4096 + +#define MAX_SECRET_LEN 96 +#define MAX_HOSTNAME 128 +#define MAX_OPTS 64 + +#define MAX_RETRANSMISSIONS 5 + +#define EXTRA_HEADER_ROOM 32 + +/* Forward declarations */ + +/* an L2TP datagram */ +typedef struct l2tp_dgram_t { + uint16_t msg_type; /* Message type */ + uint8_t bits; /* Options bits */ + uint8_t version; /* Version */ + uint16_t length; /* Length (opt) */ + uint16_t tid; /* Tunnel ID */ + uint16_t sid; /* Session ID */ + uint16_t Ns; /* Ns (opt) */ + uint16_t Nr; /* Nr (opt) */ + uint16_t off_size; /* Offset size (opt) */ + unsigned char data[MAX_PACKET_LEN]; /* Data */ + size_t last_random; /* Offset of last random vector AVP */ + size_t payload_len; /* Payload len (not including L2TP header) */ + size_t cursor; /* Cursor for adding/stripping AVP's */ + size_t alloc_len; /* Length allocated for data */ + struct l2tp_dgram_t *next; /* Link to next packet in xmit queue */ +} l2tp_dgram; + +/* An L2TP peer */ +typedef struct l2tp_peer_t { + hash_bucket hash; /* all_peers hash (hashed by address) */ + struct sockaddr_in addr; /* Peer's address */ + int mask_bits; /* Peer's netmask in number of bits */ + char hostname[MAX_HOSTNAME]; /* My hostname as presented to this peer. */ + size_t hostname_len; /* Length of my hostname */ + char peername[MAX_HOSTNAME]; /* Peer's hostname. */ + size_t peername_len; /* Length of hostname */ + char secret[MAX_SECRET_LEN]; /* Secret for this peer */ + size_t secret_len; /* Length of secret */ + struct l2tp_call_ops_t *lac_ops; /* Call ops if we act as LAC */ + char *lac_options[MAX_OPTS+1]; /* Handler options if we act as LAC */ + int num_lac_options; /* Number of above */ + struct l2tp_call_ops_t *lns_ops; /* Call ops if we act as LNS */ + char *lns_options[MAX_OPTS+1]; /* Handler options if we act as LNS */ + int num_lns_options; /* Number of above */ + int hide_avps; /* If true, hide AVPs to this peer */ + int retain_tunnel; /* If true, keep tunnel after last session is + deleted. Otherwise, delete tunnel too. */ + int validate_peer_ip; /* If true, do not accept datagrams except + from initial peer IP address */ + int persist; /* If true, keep session established */ + int holdoff; /* If persist is true, delay after which the + session is re-established. */ + int maxfail; /* If persist is true, try to establish a + broken session at most on maxfail times. */ + int fail; /* Number of failed attempts. */ +} l2tp_peer; + +/* An L2TP tunnel */ +typedef struct l2tp_tunnel_t { + hash_bucket hash_by_my_id; /* Hash bucket for tunnel hash table */ + hash_bucket hash_by_peer; /* Hash bucket for tunnel-by-peer table */ + hash_table sessions_by_my_id; /* Sessions in this tunnel */ + uint16_t my_id; /* My tunnel ID */ + uint16_t assigned_id; /* ID assigned by peer */ + l2tp_peer *peer; /* The L2TP peer */ + struct sockaddr_in peer_addr; /* Peer's address */ + uint16_t Ns; /* Sequence of next packet to queue */ + uint16_t Ns_on_wire; /* Sequence of next packet to be sent on wire */ + uint16_t Nr; /* Expected sequence of next received packet */ + uint16_t peer_Nr; /* Last packet ack'd by peer */ + int ssthresh; /* Slow-start threshold */ + int cwnd; /* Congestion window */ + int cwnd_counter; /* Counter for incrementing cwnd in congestion-avoidance phase */ + int timeout; /* Retransmission timeout (seconds) */ + int retransmissions; /* Number of retransmissions */ + int rws; /* Our receive window size */ + int peer_rws; /* Peer receive window size */ + EventSelector *es; /* The event selector */ + EventHandler *hello_handler; /* Timer for sending HELLO */ + EventHandler *timeout_handler; /* Handler for timeout */ + EventHandler *ack_handler; /* Handler for sending Ack */ + l2tp_dgram *xmit_queue_head; /* Head of control transmit queue */ + l2tp_dgram *xmit_queue_tail; /* Tail of control transmit queue */ + l2tp_dgram *xmit_new_dgrams; /* dgrams which have not been transmitted */ + char peer_hostname[MAX_HOSTNAME]; /* Peer's host name */ + unsigned char response[MD5LEN]; /* Our response to challenge */ + unsigned char expected_response[MD5LEN]; /* Expected resp. to challenge */ + int state; /* Tunnel state */ +} l2tp_tunnel; + +/* A session within a tunnel */ +typedef struct l2tp_session_t { + hash_bucket hash_by_my_id; /* Hash bucket for session table */ + l2tp_tunnel *tunnel; /* Tunnel we belong to */ + uint16_t my_id; /* My ID */ + uint16_t assigned_id; /* Assigned ID */ + int state; /* Session state */ + + /* Some flags */ + unsigned int snooping:1; /* Are we snooping in on LCP? */ + unsigned int got_send_accm:1; /* Do we have send_accm? */ + unsigned int got_recv_accm:1; /* Do we have recv_accm? */ + unsigned int we_are_lac:1; /* Are we a LAC? */ + unsigned int sequencing_required:1; /* Sequencing required? */ + unsigned int sent_sli:1; /* Did we send SLI yet? */ + + uint32_t send_accm; /* Negotiated send accm */ + uint32_t recv_accm; /* Negotiated receive accm */ + uint16_t Nr; /* Data sequence number */ + uint16_t Ns; /* Data sequence number */ + struct l2tp_call_ops_t *call_ops; /* Call ops */ + char calling_number[MAX_HOSTNAME]; /* Calling number */ + void *private; /* Private data for call-op's use */ +} l2tp_session; + +/* Call operations */ +typedef struct l2tp_call_ops_t { + /* Called once session has been established (LAC) or when we want + to establish session (LNS) */ + int (*establish)(l2tp_session *ses); + + /* Called when session must be closed. May be called without + established() being called if session could not be established.*/ + void (*close)(l2tp_session *ses, char const *reason, int may_reestablish); + + /* Called when a PPP frame arrives over tunnel */ + void (*handle_ppp_frame)(l2tp_session *ses, unsigned char *buf, + size_t len); +} l2tp_call_ops; + +/* an LNS handler */ +typedef struct l2tp_lns_handler_t { + struct l2tp_lns_handler_t *next; + char const *handler_name; + l2tp_call_ops *call_ops; +} l2tp_lns_handler; + +/* an LAC handler */ +typedef struct l2tp_lac_handler_t { + struct l2tp_lac_handler_t *next; + char const *handler_name; + l2tp_call_ops *call_ops; +} l2tp_lac_handler; + +/* Settings */ +typedef struct l2tp_settings_t { + int listen_port; /* Port we listen on */ + struct in_addr listen_addr; /* IP to bind to */ +} l2tp_settings; + +extern l2tp_settings Settings; + +/* Bit definitions */ +#define TYPE_BIT 0x80 +#define LENGTH_BIT 0x40 +#define SEQUENCE_BIT 0x08 +#define OFFSET_BIT 0x02 +#define PRIORITY_BIT 0x01 +#define RESERVED_BITS 0x34 +#define VERSION_MASK 0x0F +#define VERSION_RESERVED 0xF0 + +#define AVP_MANDATORY_BIT 0x80 +#define AVP_HIDDEN_BIT 0x40 +#define AVP_RESERVED_BITS 0x3C + +#define MANDATORY 1 +#define NOT_MANDATORY 0 +#define HIDDEN 1 +#define NOT_HIDDEN 0 +#define VENDOR_IETF 0 + +#define AVP_MESSAGE_TYPE 0 +#define AVP_RESULT_CODE 1 +#define AVP_PROTOCOL_VERSION 2 +#define AVP_FRAMING_CAPABILITIES 3 +#define AVP_BEARER_CAPABILITIES 4 +#define AVP_TIE_BREAKER 5 +#define AVP_FIRMWARE_REVISION 6 +#define AVP_HOST_NAME 7 +#define AVP_VENDOR_NAME 8 +#define AVP_ASSIGNED_TUNNEL_ID 9 +#define AVP_RECEIVE_WINDOW_SIZE 10 +#define AVP_CHALLENGE 11 +#define AVP_Q931_CAUSE_CODE 12 +#define AVP_CHALLENGE_RESPONSE 13 +#define AVP_ASSIGNED_SESSION_ID 14 +#define AVP_CALL_SERIAL_NUMBER 15 +#define AVP_MINIMUM_BPS 16 +#define AVP_MAXIMUM_BPS 17 +#define AVP_BEARER_TYPE 18 +#define AVP_FRAMING_TYPE 19 +#define AVP_CALLED_NUMBER 21 +#define AVP_CALLING_NUMBER 22 +#define AVP_SUB_ADDRESS 23 +#define AVP_TX_CONNECT_SPEED 24 +#define AVP_PHYSICAL_CHANNEL_ID 25 +#define AVP_INITIAL_RECEIVED_CONFREQ 26 +#define AVP_LAST_SENT_CONFREQ 27 +#define AVP_LAST_RECEIVED_CONFREQ 28 +#define AVP_PROXY_AUTHEN_TYPE 29 +#define AVP_PROXY_AUTHEN_NAME 30 +#define AVP_PROXY_AUTHEN_CHALLENGE 31 +#define AVP_PROXY_AUTHEN_ID 32 +#define AVP_PROXY_AUTHEN_RESPONSE 33 +#define AVP_CALL_ERRORS 34 +#define AVP_ACCM 35 +#define AVP_RANDOM_VECTOR 36 +#define AVP_PRIVATE_GROUP_ID 37 +#define AVP_RX_CONNECT_SPEED 38 +#define AVP_SEQUENCING_REQUIRED 39 + +#define HIGHEST_AVP 39 + +#define MESSAGE_SCCRQ 1 +#define MESSAGE_SCCRP 2 +#define MESSAGE_SCCCN 3 +#define MESSAGE_StopCCN 4 +#define MESSAGE_HELLO 6 + +#define MESSAGE_OCRQ 7 +#define MESSAGE_OCRP 8 +#define MESSAGE_OCCN 9 + +#define MESSAGE_ICRQ 10 +#define MESSAGE_ICRP 11 +#define MESSAGE_ICCN 12 + +#define MESSAGE_CDN 14 +#define MESSAGE_WEN 15 +#define MESSAGE_SLI 16 + +/* A fake type for our own consumption */ +#define MESSAGE_ZLB 32767 + +/* Result and error codes */ +#define RESULT_GENERAL_REQUEST 1 +#define RESULT_GENERAL_ERROR 2 +#define RESULT_CHANNEL_EXISTS 3 +#define RESULT_NOAUTH 4 +#define RESULT_UNSUPPORTED_VERSION 5 +#define RESULT_SHUTTING_DOWN 6 +#define RESULT_FSM_ERROR 7 + +#define ERROR_OK 0 +#define ERROR_NO_CONTROL_CONNECTION 1 +#define ERROR_BAD_LENGTH 2 +#define ERROR_BAD_VALUE 3 +#define ERROR_OUT_OF_RESOURCES 4 +#define ERROR_INVALID_SESSION_ID 5 +#define ERROR_VENDOR_SPECIFIC 6 +#define ERROR_TRY_ANOTHER 7 +#define ERROR_UNKNOWN_AVP_WITH_M_BIT 8 + +/* Tunnel states */ +enum { + TUNNEL_IDLE, + TUNNEL_WAIT_CTL_REPLY, + TUNNEL_WAIT_CTL_CONN, + TUNNEL_ESTABLISHED, + TUNNEL_RECEIVED_STOP_CCN, + TUNNEL_SENT_STOP_CCN +}; + +/* Session states */ +enum { + SESSION_IDLE, + SESSION_WAIT_TUNNEL, + SESSION_WAIT_REPLY, + SESSION_WAIT_CONNECT, + SESSION_ESTABLISHED +}; + +/* Constants and structures for parsing config file */ +typedef struct l2tp_opt_descriptor_t { + char const *name; + int type; + void *addr; +} l2tp_opt_descriptor; + +/* Structures for option-handlers for different sections */ +typedef struct option_handler_t { + struct option_handler_t *next; + char const *section; + int (*process_option)(EventSelector *, char const *, char const *); +} option_handler; + +#define OPT_TYPE_BOOL 0 +#define OPT_TYPE_INT 1 +#define OPT_TYPE_IPADDR 2 +#define OPT_TYPE_STRING 3 +#define OPT_TYPE_CALLFUNC 4 +#define OPT_TYPE_PORT 5 /* 1-65535 */ + +/* tunnel.c */ +l2tp_session *l2tp_tunnel_find_session(l2tp_tunnel *tunnel, uint16_t sid); +l2tp_tunnel *l2tp_tunnel_find_by_my_id(uint16_t id); +l2tp_tunnel *l2tp_tunnel_find_for_peer(l2tp_peer *peer, EventSelector *es); +void l2tp_tunnel_add_session(l2tp_session *ses); +void l2tp_tunnel_reestablish(EventSelector *es, int fd, unsigned int flags, void *data); +void l2tp_tunnel_delete_session(l2tp_session *ses, char const *reason, int may_reestablish); +void l2tp_tunnel_handle_received_control_datagram(l2tp_dgram *dgram, + EventSelector *es, + struct sockaddr_in *from); +void l2tp_tunnel_init(EventSelector *es); +void l2tp_tunnel_xmit_control_message(l2tp_tunnel *tunnel, l2tp_dgram *dgram); +void l2tp_tunnel_stop_tunnel(l2tp_tunnel *tunnel, char const *reason); +void l2tp_tunnel_stop_all(char const *reason); + +l2tp_session *l2tp_tunnel_first_session(l2tp_tunnel *tunnel, void **cursor); +l2tp_session *l2tp_tunnel_next_session(l2tp_tunnel *tunnel, void **cursor); +void tunnel_send_ZLB(l2tp_tunnel *tunnel); + +/* Access functions */ +int l2tp_num_tunnels(void); +l2tp_tunnel *l2tp_first_tunnel(void **cursor); +l2tp_tunnel *l2tp_next_tunnel(void **cursor); +char const *l2tp_tunnel_state_name(l2tp_tunnel *tunnel); + +/* session.c */ +void l2tp_session_lcp_snoop(l2tp_session *ses, + unsigned char const *buf, + int len, + int incoming); +int l2tp_session_register_lns_handler(l2tp_lns_handler *handler); +int l2tp_session_register_lac_handler(l2tp_lac_handler *handler); +l2tp_lns_handler *l2tp_session_find_lns_handler(char const *name); +l2tp_lac_handler *l2tp_session_find_lac_handler(char const *name); + +void l2tp_session_send_CDN(l2tp_session *ses, int result_code, int error_code, + char const *fmt, ...); +void l2tp_session_hash_init(hash_table *tab); +void l2tp_session_free(l2tp_session *ses, char const *reason, int may_reestablish); +void l2tp_session_notify_tunnel_open(l2tp_session *ses); +void l2tp_session_lns_handle_incoming_call(l2tp_tunnel *tunnel, + uint16_t assigned_id, + l2tp_dgram *dgram, + char const *calling_number); +void l2tp_session_handle_CDN(l2tp_session *ses, l2tp_dgram *dgram); +void l2tp_session_handle_ICRP(l2tp_session *ses, l2tp_dgram *dgram); +void l2tp_session_handle_ICCN(l2tp_session *ses, l2tp_dgram *dgram); +char const *l2tp_session_state_name(l2tp_session *ses); + +/* Call this when a LAC wants to send an incoming-call-request to an LNS */ +l2tp_session *l2tp_session_call_lns(l2tp_peer *peer, + char const *calling_number, + EventSelector *es, + void *private); + +/* dgram.c */ +l2tp_dgram *l2tp_dgram_new(size_t len); +l2tp_dgram *l2tp_dgram_new_control(uint16_t msg_type, uint16_t tid, uint16_t sid); +void l2tp_dgram_free(l2tp_dgram *dgram); +l2tp_dgram *l2tp_dgram_take_from_wire(struct sockaddr_in *from); +int l2tp_dgram_send_to_wire(l2tp_dgram const *dgram, + struct sockaddr_in const *to); +int l2tp_dgram_send_ppp_frame(l2tp_session *ses, unsigned char const *buf, + int len); + +unsigned char *l2tp_dgram_search_avp(l2tp_dgram *dgram, + l2tp_tunnel *tunnel, + int *mandatory, + int *hidden, + uint16_t *len, + uint16_t vendor, + uint16_t type); + +unsigned char *l2tp_dgram_pull_avp(l2tp_dgram *dgram, + l2tp_tunnel *tunnel, + int *mandatory, + int *hidden, + uint16_t *len, + uint16_t *vendor, + uint16_t *type, + int *err); + +int l2tp_dgram_add_avp(l2tp_dgram *dgram, + l2tp_tunnel *tunnel, + int mandatory, + uint16_t len, + uint16_t vendor, + uint16_t type, + void *val); + +int l2tp_dgram_validate_avp(uint16_t vendor, uint16_t type, + uint16_t len, int mandatory); + +/* utils.c */ +typedef void (*l2tp_shutdown_func)(void *); + +void l2tp_random_init(void); +void l2tp_random_fill(void *ptr, size_t size); +void l2tp_set_errmsg(char const *fmt, ...); +char const *l2tp_get_errmsg(void); +void l2tp_cleanup(void); +int l2tp_register_shutdown_handler(l2tp_shutdown_func f, void *data); +void l2tp_die(void); +int l2tp_load_handler(EventSelector *es, char const *fname); + +#define L2TP_RANDOM_FILL(x) l2tp_random_fill(&(x), sizeof(x)) + +/* network.c */ +extern int Sock; +//extern char Hostname[MAX_HOSTNAME]; //2005-04-14 by kanki + +int l2tp_network_init(EventSelector *es); + +/* peer.c */ +void l2tp_peer_init(void); +l2tp_peer *l2tp_peer_find(struct sockaddr_in *addr, char const *hostname); +l2tp_peer *l2tp_peer_insert(struct sockaddr_in *addr); + +/* debug.c */ +char const *l2tp_debug_avp_type_to_str(uint16_t type); +char const *l2tp_debug_message_type_to_str(uint16_t type); +char const *l2tp_debug_tunnel_to_str(l2tp_tunnel *tunnel); +char const *l2tp_debug_session_to_str(l2tp_session *session); +char const *l2tp_debug_describe_dgram(l2tp_dgram const *dgram); +void l2tp_db(int what, char const *fmt, ...); +void l2tp_debug_set_bitmask(unsigned long mask); + +/* auth.c */ +void l2tp_auth_gen_response(uint16_t msg_type, char const *secret, + unsigned char const *challenge, size_t chal_len, + unsigned char buf[16]); + +/* options.c */ +int l2tp_parse_config_file(EventSelector *es, + char const *fname); +int l2tp_option_set(EventSelector *es, + char const *name, + char const *value, + l2tp_opt_descriptor descriptors[]); + +void l2tp_option_register_section(option_handler *h); +char const *l2tp_chomp_word(char const *line, char *word); + +#endif diff --git a/release/src/router/rp-l2tp/libevent/Doc/flow.fig b/release/src/router/rp-l2tp/libevent/Doc/flow.fig new file mode 100644 index 00000000..c2434193 --- /dev/null +++ b/release/src/router/rp-l2tp/libevent/Doc/flow.fig @@ -0,0 +1,32 @@ +#FIG 3.2 +Landscape +Center +Inches +Letter +100.00 +Single +-2 +1200 2 +6 2475 2475 4725 3525 +4 1 0 50 0 5 12 0.0000 0 180 1680 3600 2625 Event_AddHandler\001 +4 1 0 50 0 5 12 0.0000 0 180 1680 3600 2835 Event_AddHandler\001 +4 1 0 50 0 5 12 0.0000 0 180 2205 3600 3045 Event_AddTimerHandler\001 +4 1 0 50 0 5 12 4.7124 0 15 315 3600 3300 ...\001 +-6 +2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 + 2400 1200 4800 1200 4800 1800 2400 1800 2400 1200 +2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 + 2400 2400 4800 2400 4800 3600 2400 3600 2400 2400 +2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 + 2400 4200 4800 4200 4800 4800 2400 4800 2400 4200 +2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 3600 1800 3600 2400 +2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 3600 3600 3600 4200 +2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 5 + 1 1 1.00 60.00 120.00 + 3600 4800 3600 5400 5400 5400 5400 4500 4800 4500 +4 1 0 50 0 5 12 0.0000 0 180 1785 3600 4500 Event_HandleEvent\001 +4 1 0 50 0 5 12 0.0000 0 180 2100 3600 1500 Event_CreateSelector\001 diff --git a/release/src/router/rp-l2tp/libevent/Doc/libevent.pdf b/release/src/router/rp-l2tp/libevent/Doc/libevent.pdf Binary files differnew file mode 100644 index 00000000..53d46821 --- /dev/null +++ b/release/src/router/rp-l2tp/libevent/Doc/libevent.pdf diff --git a/release/src/router/rp-l2tp/libevent/Doc/libevent.tex b/release/src/router/rp-l2tp/libevent/Doc/libevent.tex new file mode 100644 index 00000000..92794cb0 --- /dev/null +++ b/release/src/router/rp-l2tp/libevent/Doc/libevent.tex @@ -0,0 +1,549 @@ +% LIC: GPL +\documentclass{article} +\usepackage{epsfig} +\usepackage[colorlinks]{hyperref} +\input{style} +\newcommand{\Le}{\textsf{LibEvent}} +\newcommand{\Es}{\type{EventSelector}} +\newcommand{\Eh}{\type{EventHandler}} + +\title{\Le{} Programmers Manual} +\author{David F. Skoll\\\textit{Roaring Penguin Software Inc.}} +\begin{document} +\maketitle + +\section{Introduction} +\label{sec:introduction} + +Many UNIX programs are event-driven. They spend most of their time +waiting for an event, such as input from a file descriptor, expiration +of a timer, or a signal, and then react to that event. + +The standard UNIX mechanisms for writing event-driven programs are +the \name{select} and \name{poll} system calls, which wait for input +on a set of file descriptors, optionally with a timeout. + +While \name{select} and \name{poll} can be used to write event-driven +programs, their calling interface is awkward and their level of +abstraction too low. \Le{} is built around \name{select}, but +provides a more pleasant interface for programmers. + +\Le{} provides the following mechanisms: +\begin{itemize} +\item \textit{Events}, which trigger under user-specified conditions, + such as readability/writability of a file descriptor or expiration of + a timer. +\item \textit{Synchronous signal-handling}, which is the ability to + defer signal-handling to a safe point in the event-handling loop. +\item \textit{Syncronous child cleanup}, which lets you defer calls + to \name{wait} or \name{waitpid} to a safe point in the event-handling + loop. +\end{itemize} + +\section{Overview} +\label{sec:overview} + +Figure~\ref{fig:flow} indicates the overall flow of programs using +\Le{}. +\begin{figure}[htbp] + \begin{center} + \epsfig{file=flow.\eps,width=3in} + \caption{\Le{} Flow} + \label{fig:flow} + \end{center} +\end{figure} + +\begin{enumerate} +\item Call \name{Event\_CreateSelector} once to create an \emph{Event + Selector}. This is an object which manages event dispatch. +\item Open file descriptors as required, and call \name{Event\_CreateHandler} + to create \emph{Event Handlers} for each descriptor of interest. You + can call \name{Event\_CreateTimerHandler} to create timers which are + not associated with file descriptors. +\item Call \name{Event\_HandleEvent} in a loop. Presumably, some event + will cause the program to exit out of the infinite loop (unless the + program is designed never to exit.) +\end{enumerate} + +To use \Le{}, you should \texttt{\#include} the file +\incfile{libevent/event.h} + +\section{Types} + +\Le{} defines the following types: +\begin{itemize} +\item \Es{} -- a container object which manages event handlers. +\item \Eh{} -- an object which triggers a callback function when an event + occurs. +\item \type{EventCallbackFunc} -- a prototype for the callback function + called by an \Eh{}. +\end{itemize} + +\section{Basic Functions} +\label{sec:basic-functions} + +This section describes the basic \Le{} functions. Each function is +described in the following format: +\function{type}{name}{(\type{type1} \param{arg1}, \type{type2} \param{arg2})} +{A brief description of the function. \type{type} is the type of the +return value and \name{name} is the name of the function.} +{What the function returns} +\begin{itemize} +\item \param{arg1} -- A description of the first argument. +\item \param{arg2} -- A description of the second argument, etc. +\end{itemize} + +\subsection{Event Selector Creation and Destruction} +\function{EventSelector *}{Event\_CreateSelector}{(\type{void})} +{Creates an \Es{} object and returns a pointer to it. +An \Es{} is an object which keeps track of event handlers. +You should treat it as an opaque type.} +{A pointer to the \Es{}, or NULL if out of memory.} +{None.} + +\function{void}{Event\_DestroySelector}{(\type{EventSelector *}\name{es})} +{Destroys an \Es{} and all associated event handlers.} +{Nothing.} +\begin{itemize} +\item \param{es} -- the \Es{} to destroy. +\end{itemize} + +\subsection{Event Handler Creation and Destruction} + +An \Eh{} is an opaque object which contains information about an +event. An event may be \emph{triggered} by one or more of three things: + +\begin{enumerate} +\item A file descriptor becomes readable. That is, \name{select} for + readability would return. +\item A file descriptor becomes writeable. +\item A timeout elapses. +\end{enumerate} + +When an event triggers, it calls an event callback function. An +event callback function looks like this: + +\function{void}{functionName}{(\=\type{EventSelector *}\param{es},\\ +\>\type{int} \param{fd},\\ +\>\type{unsigned int} \param{flags},\\ +\>\type{void *}\param{data})} +{Called when an event handler triggers.} +{Nothing} +\begin{itemize} +\item \param{es} -- the \Es{} to which the event handler belongs. +\item \param{fd} -- the file descriptor (if any) associated with the event. +\item \param{flags} -- a bitmask of one or more of the following values: + \begin{itemize} + \item \texttt{EVENT\_FLAG\_READABLE} -- the descriptor is readable. + \item \texttt{EVENT\_FLAG\_WRITEABLE} -- the descriptor is writeable. + \item \texttt{EVENT\_FLAG\_TIMEOUT} -- a timeout triggered. + \end{itemize} +\item \param{data} -- an opaque pointer which was passed into + \name{Event\_AddHandler}. +\end{itemize} + +\function{EventHandler *}{Event\_AddHandler}{(\=\type{EventSelector *}\param{es},\\ +\>\type{int} \param{fd},\\ +\>\type{unsigned int} \param{flags},\\ +\>\type{EventCallbackFunc} \param{fn},\\ +\>\type{void *}\param{data})} +{Creates an \Eh{} to handle an event.} +{An allocated \Eh{}, or NULL if out of memory.} +\begin{itemize} +\item \param{es} -- the event selector. +\item \param{fd} -- the file descriptor to watch. \param{fd} must be + a legal file descriptor for use inside \name{select}. +\item \param{flags} -- a bitmask whose value is one of + \texttt{EVENT\_FLAG\_READABLE}, \texttt{EVENT\_FLAG\_WRITEABLE} or + \texttt{EVENT\_FLAG\_READABLE~|~EVENT\_FLAG\_WRITEABLE}. \param{flags} + specifies the condition(s) under which to trigger the event. +\item \param{fn} -- the callback function to invoke when the event triggers. +\item \param{data} -- a pointer which is passed unchanged as the last + parameter of \param{fn} when the event triggers. +\end{itemize} + +\function{EventHandler *}{Event\_AddTimerHandler}{(\=\type{EventSelector *}\param{es},\\ +\>\type{struct timeval} \param{t},\\ +\>\type{EventCallbackFunc} \param{fn},\\ +\>\type{void *}\param{data})} +{Creates an \Eh{} to handle a timeout. After the timeout elapses, the + callback function is called once only, and then the \Eh{} is automatically + destroyed.} +{An allocated \Eh{}, or NULL if out of memory.} +\begin{itemize} +\item \param{es} -- the event selector. +\item \param{t} -- the time after which to trigger the event. \param{t} + specifies how long \emph{after} the current time to trigger the event. +\item \param{fn} -- the callback function to invoke when the event triggers. + A timer handler function is always called with its \param{flags} set + to \texttt{EVENT\_FLAG\_TIMER~|~EVENT\_FLAG\_TIMEOUT}. +\item \param{data} -- a pointer which is passed unchanged as the last + parameter of \param{fn} when the event triggers. +\end{itemize} + +\function{EventHandler *}{Event\_AddHandlerWithTimeout} +{(\=\type{EventSelector *}\param{es},\\ +\>\type{int} \param{fd},\\ +\>\type{unsigned int} \param{flags},\\ +\>\type{struct timeval} \param{t},\\ +\>\type{EventCallbackFunc} \param{fn},\\ +\>\type{void *}\param{data})} +{Creates an \Eh{} to handle an event. The event is called when a file + descriptor is ready or a timeout elapses. This function may be viewed + as a combination of \name{Event\_AddHandler} and \name{Event\_AddTimerHandler}.} +{An allocated \Eh{}, or NULL if out of memory.} +\begin{itemize} +\item \param{es} -- the event selector. +\item \param{fd} -- the file descriptor to watch. \param{fd} must be + a legal file descriptor for use inside \name{select}. +\item \param{flags} -- a bitmask whose value is one of + \texttt{EVENT\_FLAG\_READABLE}, \texttt{EVENT\_FLAG\_WRITEABLE} or + \texttt{EVENT\_FLAG\_READABLE~|~EVENT\_FLAG\_WRITEABLE}. \param{flags} + specifies the condition(s) under which to trigger the event. +\item \param{t} -- the time after which to trigger the event. If the event + is triggered because of a timeout, the callback function's \param{flags} + has the \texttt{EVENT\_FLAG\_TIMEOUT} bit set. +\item \param{fn} -- the callback function to invoke when the event triggers. +\item \param{data} -- a pointer which is passed unchanged as the last + parameter of \param{fn} when the event triggers. +\end{itemize} + +\function{int}{Event\_DelHandler} +{(\=\type{EventSelector *}\param{es},\\ +\>\type{EventHandler *}\param{eh})} +{Deletes an \Eh{} and frees its memory. A handler may be deleted from + inside a handler callback; \Le{} defers the actual deallocation of + resources to a safe time.} +{0 if the handler was found and deleted, non-zero otherwise. A non-zero + return value indicates a critical internal error.} +\begin{itemize} +\item \param{es} -- the event selector which contains \param{eh}. +\item \param{eh} -- the event handler to delete. +\end{itemize} + +\subsection{Event Handler Access Functions} + +The functions in this section access or modify fields in the +\Eh{} structure. You should \emph{never} access or modify fields +in an \Eh{} except with these functions. + +\function{void}{Event\_ChangeTimeout} +{(\=\type{EventHandler *}\param{eh},\\ + \>\type{struct timeval} \param{t})} +{Changes the timeout of \param{eh} to be \param{t} seconds from now. If + \param{eh} was not created with \name{Event\_AddTimerHandler} or + \name{Event\_AddHandlerWithTimeout}, then this function has no effect.} +{Nothing} +\begin{itemize} +\item \param{eh} -- the \Eh{} whose timeout is to be modified. +\item \param{t} -- new value of timeout, relative to current time. +\end{itemize} + +\function{EventCallbackFunc}{Event\_GetCallback} +{(\type{EventHandler *}\param{eh})} +{Returns the callback function associated with \param{eh}.} +{A pointer to the callback function associated with \param{eh}.} +\begin{itemize} +\item \param{eh} -- the \Eh{} whose callback pointer is desired. +\end{itemize} + +\function{void *}{Event\_GetData} +{(\type{EventHandler *}\param{eh})} +{Returns the data associated with \param{eh} (the \param{data} argument + to the \ldots{}AddHandler\ldots{} function.)} +{The data pointer associated with \param{eh}.} +\begin{itemize} +\item \param{eh} -- the \Eh{} whose data pointer is desired. +\end{itemize} + +\function{void}{Event\_SetCallbackAndData} +{(\=\type{EventHandler *}\param{eh},\\ + \>\type{EventCallbackFunc} \param{fn},\\ + \>\type{void *}\param{data})} +{Sets the callback function and data associated with \param{eh}.} +{Nothing.} +\begin{itemize} +\item \param{eh} -- the \Eh{} whose callback function and data pointer are + to be set. +\item \param{fn} -- the new value for the callback function. +\item \param{data} -- the new value for the data pointer. +\end{itemize} + +\section{Signal Handling} + +In UNIX, signals can arrive asynchronously, and a signal-handler +function may be called at an unsafe time, leading to race conditions. +\Le{} has a mechanism to call a handler function during +\name{Event\_HandleEvent} so that the handler is dispatched just like +any other event handler. In this way, the signal handler knows that +it is safe to access shared data without interference from another +thread of control. + +\Le{} implements this \emph{synchronous signal handling} by setting up +a UNIX pipe, and writing to the write-end inside the asynchronous +handler. The read end then becomes ready for reading, and triggers +a normal event. \Le{} encapsulates all the details for you in +two functions. + +\function{int}{Event\_HandleSignal} +{(\=\type{EventSelector *}\param{es},\\ + \>\type{int} \param{sig},\\ + \>\type{void (*}\param{handler}\type{)(int} \param{sig}\type{)})} +{Arranges for the function \param{handler} to be called when signal + \param{sig} is received. \param{sig} is typically a constant from + \incfile{signal.h}, such as \texttt{SIGHUP}, \texttt{SIGINT}, etc. + The \param{handler} function is not called in the context of a UNIX + signal handler; rather, it is called soon after the signal has been + received as part of the normal \name{Event\_HandleEvent} loop. + + As a side-effect of calling this function, a UNIX signal handler + is established for \param{sig}. Any existing signal disposition is + forgotten. If \param{sig} is \texttt{SIGCHLD}, then the + \texttt{SA\_NOCLDSTOP} flag is set in the \param{struct sigaction} passed + to the low-level \name{sigaction} function.} +{0 on success; -1 on failure. Failure is usually due to a UNIX system + call failing or a lack of memory.} +\begin{itemize} +\item \param{es} -- the event selector. +\item \param{sig} -- the signal we wish to handle. +\item \param{handler} -- the function to call. It is passed a single + argument---the signal which is being handled. +\end{itemize} + +\function{int}{Event\_HandleChildExit} +{(\=\type{EventSelector *}\param{es},\\ + \>\type{pid\_t} \param{pid},\\ + \>\type{void (*}\param{handler}\type{)(pid\_t} \param{pid}, \type{int} \param{status}, \type{void *}\param{data}\type{)},\\ + \>\type{void *}\param{data})} +{Arranges for \param{handler} to be called when the child process with + process-ID \param{pid} exits. \param{pid} must be the return + value of a successful call to \name{fork}. + + When the process with process-ID \param{pid} exits, \Le{} catches + the \texttt{SIGCHILD} signal and at some point in the event-handling + loop, calls \param{handler} with three arguments: \param{pid} is + the process-ID of the process which terminated. \param{status} is + the exit status as returned by the \name{waitpid} system call. And + \param{data} is passed unchanged from the call to \name{Event\_HandleChildExit}.} +{0 on success; -1 on failure. Failure is the result of lack of memory or + the failure of a UNIX system call.} +\begin{itemize} +\item \param{es} -- the event selector. +\item \param{pid} -- process-ID of the child process. +\item \param{handler} -- the function to call when the process exits. +\item \param{data} -- a pointer which is passed unchanged to \param{handler} + when the process exits. +\end{itemize} + +\section{Stream-Oriented Functions} + +The functions presented in the previous sections are appropriate for +simple events, especially those associated with datagram sockets. A +higher level of abstraction is required for stream-oriented descriptors. +It would be nice for \Le{} to invoke a callback function when a certain +number of bytes or a specific delimiter have been read from a stream, +or when an entire buffer's worth of data has been written to a stream. + +The functions in this section all (unfortunately) have the string +\texttt{Tcp} in their names, because they were originally used with TCP +sockets. However, they may be used with any stream-oriented sockets, +including UNIX-domain sockets. + +All of the stream-oriented functions are built on the simpler event +functions described previously. They simply add an extra layer of +convenience. To use the stream-oriented functions, +\texttt{\#include} the file \incfile{libevent/event\_tcp.h}. + +\section{Stream-Oriented Data Types} + +The stream-oriented functions use the following publicly-accessible type: +\begin{itemize} +\item \type{EventTcpState} -- an opaque object which records the state + of stream-oriented event handlers. +\end{itemize} + +\section{Stream-Oriented Functions} +\label{sec:basic-stream-oriented-functions} + +The stream-oriented functions may be broken into two main groups: +Connection establishment, and data transfer. + +\subsection{Connection Establishment} + +\function{EventHandler *}{EventTcp\_CreateAcceptor} +{(\=\type{EventSelector *}\param{es},\\ + \>\type{int} \param{fd},\\ + \>\type{EventTcpAcceptFunc} \param{f})} +{Creates an event handler to accept incoming connections on the listening + descriptor \param{fd}. Each time an incomming connection is accepted, + the function \param{f} is called.} +{An \Eh{} on success; NULL on failure.} +\begin{itemize} +\item \param{es} -- the event selector. +\item \param{fd} -- a listening socket (i.e., one for which the + \name{listen}(2) system call has been called.) +\item \param{f} -- a function which is called each time an incoming + connection is accepted. The function \param{f} should look like this: + + \type{void} \name{f}{(\type{EventSelector *}\param{es}, \type{int} \param{fd})} + + In this case, \param{es} is the \Es{}, and \param{fd} is the new file + descriptor returned by \name{accept}(2). +\end{itemize} + +\function{void}{EventTcp\_Connect} +{(\=\type{EventSelector *}\param{es},\\ + \>\type{int} \param{fd},\\ + \>\type{struct sockaddr const *}\param{addr},\\ + \>\type{socklen\_t} \param{addrlen},\\ + \>\type{EventTcpConnectFunc} \param{f},\\ + \>\type{int} \param{timeout},\\ + \>\type{void *}\param{data})} +{Attempts to connect the socket \param{fd} to \param{addr} using the + \name{connect}(2) system call.} +{Nothing. See below for error-handling notes.} +\begin{itemize} +\item \param{es} -- the event selector. +\item \param{fd} -- a socket which is suitable for passing to + \name{connect}(2). +\item \param{addr} -- the server address to connect to. +\item \param{addrlen} -- the length of the server address. The + three parameters \param{fd}, \param{addr} and \param{addrlen} are passed + directly to \name{connect}(2). +\item \param{f} -- A function which is called when the connection succeeds + (or if an error occurs.) The function \param{f} looks like this: + + \type{void} \name{f}{(\type{EventSelector *}\param{es}, \type{int} \param{fd}, \type{int} \param{flag}, \type{void *}\param{data})} + + The parameters of \param{f} have the following meaning: + \begin{itemize} + \item \param{es} -- the event selector. + \item \param{fd} -- the descriptor. + \item \param{flag} -- a flag indicating what happened. It may contain + one of the following values: + \begin{itemize} + \item \param{EVENT\_TCP\_FLAG\_IOERROR} -- the \name{connect} system call + failed. + \item \param{EVENT\_TCP\_FLAG\_COMPLETE} -- the \name{connect} system + call succeeded and the descriptor is now connected. + \item \param{EVENT\_TCP\_FLAG\_TIMEOUT} -- the \name{connect} system call + did not complete within the specified timeout. + \end{itemize} + \item \param{data} -- a copy of the \param{data} given to + \name{EventTcp\_Connect}. + \end{itemize} +\item \param{timeout} -- a timeout value in seconds. If \name{connect} does + not complete withing \param{timeout} seconds, the \param{f} is called + with a flag of \param{EVENT\_TCP\_FLAG\_TIMEOUT}. +\item \param{data} -- an opaque pointer passed unchanged to \param{f}. +\end{itemize} + +\subsection{Data Transfer} + +There are two stream-oriented functions for data transfer: One for +reading and one for writing. + +\function{EventTcpState *}{EventTcp\_ReadBuf} +{(\=\type{EventSelector *}\param{es},\\ + \>\type{int} \param{fd},\\ + \>\type{int} \param{len},\\ + \>\type{int} \param{delim},\\ + \>\type{EventTcpIOFinishedFunc} \param{f},\\ + \>\type{int} \param{timeout},\\ + \>\type{void *}\param{data})} +{Arranges events to read up to \param{len} characters from the file + descriptor \param{fd}. If \param{delim} is non-negative, reading stops + when the characters \param{delim} is encountered. After \param{len} + characters have been read (or \param{delim} has been encountered), or + after \param{timeout} seconds have elapsed, the function \param{f} is + called.} +{An \type{EventTcpState} object on success; NULL on failure. Failure + is usually due to failure of a UNIX system call or lack of memory.} +\begin{itemize} +\item \param{es} -- the event selector. +\item \param{fd} -- the descriptor to read from. +\item \param{len} -- the maximum number of bytes to read. +\item \param{delim} -- if negative, reading continues until exactly + \param{len} bytes have been read or the operation times out. If + non-negative, reading stops when \param{len} bytes have been read + or the characters \param{delim} is encountered, whichever comes first. + Note that supplying a non-negative \param{delim} causes \Le{} to + invoke the \name{read}(2) system call for \emph{each character}; if + you are expecting large amounts of data before the delimiter, this + could be inefficient. +\item \param{f} -- a function which is called when reading has finished, + an error occurs, or the operation times out. The function \param{f} + looks like this: + + \type{void}~\name{f}{(\type{EventSelector~*}\param{es},~\type{int}~\param{fd},~\type{char~*}\param{int~buf},~\type{int}~\param{len},~\type{int}~\param{flag},~\type{void~*}\param{data})} + + The arguments passed to \param{f} are: + \begin{itemize} + \item \param{es} -- the event selector. + \item \param{fd} -- the file descriptor that was passed to + \name{EventTcp\_ReadBuf}. If no more activity on \param{fd} is + required, then you should \name{close} it inside \param{f}. + \item \param{buf} -- a dynamically-allocated buffer holding the data + which were read from \param{fd}. \emph{Do not} free this buffer; + \Le{} will take care of it. \emph{Do not} store the pointer value; + if you need a copy of the data, you must copy the whole buffer. + \item \param{len} -- the number of bytes actually read from \param{fd}. + \item \param{flag} -- a flag indicating what happened. It can have + one of four values: + \begin{itemize} + \item \param{EVENT\_TCP\_FLAG\_COMPLETE} -- the operation completed + successfully. + \item \param{EVENT\_TCP\_FLAG\_IOERROR} -- an error occurred during + a \name{read}(2) or some other system call. + \item \param{EVENT\_TCP\_FLAG\_EOF} -- EOF was detected before all + bytes were read. Nevertheless, \param{len} and \param{buf} have + valid contents. + \item \param{EVENT\_TCP\_FLAG\_TIMEOUT} -- the operation timed out + before all bytes were read. Nevertheless, \param{len} and + \param{buf} have valid contents. + \end{itemize} + \item \param{data} -- a copy of the \param{data} pointer passed to + \name{EventTcp\_ReadBuf}. + \end{itemize} +\item \param{timeout} -- if positive, \Le{} times the operation out + after \param{timeout} seconds. +\item \param{data} -- an opaque pointer which is passed as-is to + \param{f}. +\end{itemize} + +\function{EventTcpState *}{EventTcp\_WriteBuf} +{(\=\type{EventSelector *}\param{es},\\ + \>\type{int} \param{fd},\\ + \>\type{char *}\param{buf},\\ + \>\type{int} \param{len},\\ + \>\type{EventTcpIOFinishedFunc} \param{f},\\ + \>\type{int} \param{timeout},\\ + \>\type{void *}\param{data})} +{Arranges events to write \param{len} characters from the buffer + \param{buf} to the file + descriptor \param{fd}. After \param{len} + characters have been written, an error occurs, or + \param{timeout} seconds have elapsed, the function \param{f} is + called.} +{An \type{EventTcpState} object on success; NULL on failure. Failure + is usually due to failure of a UNIX system call or lack of memory.} +\begin{itemize} +\item \param{es} -- the event selector. +\item \param{fd} -- the descriptor to write to. +\item \param{buf} -- buffer containing characters to write. + \name{EventTcp\_WriteBuf} allocates its own private copy of the buffer; + you may free or reuse the buffer once \name{EventTcp\_WriteBuf} returns. +\item \param{len} -- the number of bytes to write. +\item \param{f} -- a function which is called when reading has finished, + an error occurs, or the operation times out. The function \param{f} + is as described in \name{EventTcp\_ReadBuf}. As a special case, + you may supply NULL as the value for \param{f}. In this case, + \name{EventTcp\_WriteBuf} calls \name{close}(2) on the descriptor + \param{fd} once writing has finished or timed out, or if an error + occurs. +\item \param{timeout} -- if positive, \Le{} times the operation out + after \param{timeout} seconds. +\item \param{data} -- an opaque pointer which is passed as-is to + \param{f}. +\end{itemize} +\end{document} diff --git a/release/src/router/rp-l2tp/libevent/Doc/style.tex b/release/src/router/rp-l2tp/libevent/Doc/style.tex new file mode 100644 index 00000000..79ade27d --- /dev/null +++ b/release/src/router/rp-l2tp/libevent/Doc/style.tex @@ -0,0 +1,139 @@ +% Style file for Roaring Penguin software maintenance documents +% Please use these macros in your LaTeX documentation: +% \tclok -- expands to TCL_OK in fixed-width font +% \tclerr -- expands to TCL_ERROR in fixed-width font +% \squiggle -- Outputs "~" (tilde) +% \type{foo} -- Use for C++ types. e.g.: \type{unsigned char} +% \param{bar} -- Use for function parameters +% \incfile{foo/b.h} -- Use for C++ header files +% \name{func} -- Use for function and method names. +\newcommand{\tclok}{\texttt{TCL\_OK}} +\newcommand{\tclerr}{\texttt{TCL\_ERROR}} +\newcommand{\squiggle}{\symbol{"7E}}% \tilde was already taken... +\newcommand{\type}[1]{\textit{#1}} +\newcommand{\param}[1]{\texttt{#1}} +\newcommand{\incfile}[1]{\texttt{#1}} +\newcommand{\name}[1]{\textbf{#1}} + +% \function{type}{name}{(arglist)}{description}{returns} +% +% Example: +% \function{int}{abs}{(\type{int} \param{j})} +% {Computes the absolute value of \param{j}.} % Description +% {The absolute value of \param{j}.} % Returns +% \begin{itemize} % Arguments +% \item \param{j} -- The number whose absolute value is needed +% \end{itemize} +% +% Note that ``arglist'' is typeset in a tabbing environment; you +% can do this to align arguments: +% \function{int}{frob}{(\=\type{int} \param{arg1},\\ +% \>\type{char *} \param{arg2}, \\ +% \>\type{double} \param{arg3})} ... + +\newcommand{\function}[5]{% + \begin{tabbing}{\rule{\linewidth}{0.5pt}}\\% + \type{#1} \name{#2}#3\end{tabbing}% + \begin{description}% + \item[Description:] {#4}% + \item[Returns:] {#5}% + \item[Arguments:]% + \end{description}% +} +% \method{type}{class}{name}{(arglist)}{description}{returns} +% +% Example: +% \method{void}{Box}{engulf}{(\type{Point} \param{p})} +% {Enlarges \param{self} to include \param{p}} % Description +% {Nothing.} % Returns +% \begin{itemize} % Arguments +% \item \param{p} -- A point to be included in the box. +% \end{itemize} +% +% Note that ``arglist'' is typeset in a tabbing environment as with +% \function + +\newcommand{\method}[6]{% + \begin{tabbing}\rule{\linewidth}{0.5pt}\\% + \type{#1} \name{#2}::\name{#3}#4\end{tabbing}% + \begin{description}% + \item[Description:] {#5}% + \item[Returns:] {#6}% + \item[Arguments:] % + \end{description}% +} + +% \simplemethod{type}{class}{name}{(arglist)}{description} +% +% Simpler version when \method is overkill. Just includes description. +% Example: +% \simplemethod{void}{Box}{engulf}{(\type{Point} \param{p})} +% {Enlarges \param{self} to include \param{p}} % Description +% Note that ``arglist'' is typeset in a tabbing environment as with +% \function + +\newcommand{\simplemethod}[5]{% + \begin{tabbing}\rule{\linewidth}{0.5pt}\\% + \type{#1} \name{#2}::\name{#3}#4\end{tabbing}% + \begin{description}% + \item[Description:] {#5}% + \end{description}% +} + +% \simplefunction{type}{name}{(arglist)}{description} +% +% Simpler version when \function is overkill. Just includes description. +% Example: +% \simplefunction{void}{Box}{engulf}{(\type{Point} \param{p})} +% {Enlarges \param{self} to include \param{p}} % Description +% Note that ``arglist'' is typeset in a tabbing environment as with +% \function + +\newcommand{\simplefunction}[4]{% + \begin{tabbing}\rule{\linewidth}{0.5pt}\\% + \type{#1} \name{#2}#3\end{tabbing}% + \begin{description}% + \item[Description:] {#4}% + \end{description}% +} + +% \tclmethod -- like \method, but for Tcl methods. +\newcommand{\tclmethod}[6]{% + \begin{tabbing}\rule{\linewidth}{0.5pt}\\% + \type{#1} \name{#2}.\name{#3} #4\end{tabbing}% + \begin{description}% + \item[Description:] {#5}% + \item[Returns:] {#6}% + \item[Arguments:] + \end{description}% +} +% \variable{type}{name}{description} +% Use this for global variables. +% +% Example: +% \variable{int}{Timeout} +% {The timeout value of frobnosticate in seconds.} + +\newcommand{\variable}[3]{\rule{\linewidth}{0.5pt}\\% + \type{#1} \name{#2}% + \begin{description}% + \item[Description:] {#3}% + \end{description}% +} + +\newcommand{\synopsis}[3]{\rule{\linewidth}{0.5pt}% +\\% +\begin{tabular}{l@@{ }l@@{}p{4in}} +\name{#1} & \texttt{#2} & \texttt{#3}% +\end{tabular} +\\% +\raisebox{5pt}{\rule{\linewidth}{0.5pt}}} + +\setlength{\parindent}{0pt} +\setlength{\parskip}{2pt} + +\ifx\pdfoutput\undefined +\newcommand{\eps}{eps} +\else +\newcommand{\eps}{pdf} +\fi diff --git a/release/src/router/rp-l2tp/libevent/Makefile b/release/src/router/rp-l2tp/libevent/Makefile new file mode 100644 index 00000000..185bb480 --- /dev/null +++ b/release/src/router/rp-l2tp/libevent/Makefile @@ -0,0 +1,40 @@ +# $Id: Makefile,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $ +# +# Makefile for event-handling library +# +# Copyright 2002 Roaring Penguin Software Inc. +# +# This software may be distributed according to the terms of the GNU +# General Public License, version 2 or (at your option) any later version. +# LIC: GPL + +OBJS=event.o event_tcp.o hash.o event_sig.o +SRCS=$(OBJS:.o=.c) +HDRS=event.h event_tcp.h eventpriv.h hash.h +CFLAGS=-g -O2 -I.. + +all: libevent.a + +libevent.a: $(OBJS) + rm -f libevent.a + ar -cq libevent.a $(OBJS) + mipsel-uclibc-ranlib libevent.a + +event.o: event.c $(HDRS) + $(CC) $(CFLAGS) -c -o event.o event.c + +hash.o: hash.c $(HDRS) + $(CC) $(CFLAGS) -c -o hash.o hash.c + +event_sig.o: event_sig.c $(HDRS) + $(CC) $(CFLAGS) -c -o event_sig.o event_sig.c + +event_tcp.o: event_tcp.c $(HDRS) + $(CC) $(CFLAGS) -c -o event_tcp.o event_tcp.c + +clean: FORCE + rm -f *.a *.o *~ + +FORCE: + +.phony: FORCE diff --git a/release/src/router/rp-l2tp/libevent/Makefile.in b/release/src/router/rp-l2tp/libevent/Makefile.in new file mode 100644 index 00000000..5f314812 --- /dev/null +++ b/release/src/router/rp-l2tp/libevent/Makefile.in @@ -0,0 +1,41 @@ +# Generated automatically from Makefile.in by configure. +# $Id: Makefile.in,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $ +# +# Makefile for event-handling library +# +# Copyright 2002 Roaring Penguin Software Inc. +# +# This software may be distributed according to the terms of the GNU +# General Public License, version 2 or (at your option) any later version. +# LIC: GPL + +OBJS=event.o event_tcp.o hash.o event_sig.o +SRCS=$(OBJS:.o=.c) +HDRS=event.h event_tcp.h eventpriv.h hash.h +CFLAGS=@CFLAGS@ -I.. + +all: libevent.a + +libevent.a: $(OBJS) + rm -f libevent.a + ar -cq libevent.a $(OBJS) + @RANLIB@ libevent.a + +event.o: event.c $(HDRS) + @CC@ $(CFLAGS) -c -o event.o event.c + +hash.o: hash.c $(HDRS) + @CC@ $(CFLAGS) -c -o hash.o hash.c + +event_sig.o: event_sig.c $(HDRS) + @CC@ $(CFLAGS) -c -o event_sig.o event_sig.c + +event_tcp.o: event_tcp.c $(HDRS) + @CC@ $(CFLAGS) -c -o event_tcp.o event_tcp.c + +clean: FORCE + rm -f *.a *.o *~ + +FORCE: + +.phony: FORCE diff --git a/release/src/router/rp-l2tp/libevent/event.c b/release/src/router/rp-l2tp/libevent/event.c new file mode 100644 index 00000000..398ac392 --- /dev/null +++ b/release/src/router/rp-l2tp/libevent/event.c @@ -0,0 +1,640 @@ +/*********************************************************************** +* +* event.c +* +* Abstraction of select call into "event-handling" to make programming +* easier. +* +* Copyright (C) 2001 Roaring Penguin Software Inc. +* +* This program may be distributed according to the terms of the GNU +* General Public License, version 2 or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id: event.c,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $"; + +#include "event.h" +#include <stdlib.h> +#include <errno.h> + +static void DestroySelector(EventSelector *es); +static void DestroyHandler(EventHandler *eh); +static void DoPendingChanges(EventSelector *es); + +/********************************************************************** +* %FUNCTION: Event_CreateSelector +* %ARGUMENTS: +* None +* %RETURNS: +* A newly-allocated EventSelector, or NULL if out of memory. +* %DESCRIPTION: +* Creates a new EventSelector. +***********************************************************************/ +EventSelector * +Event_CreateSelector(void) +{ + EventSelector *es = malloc(sizeof(EventSelector)); + if (!es) return NULL; + es->handlers = NULL; + es->nestLevel = 0; + es->destroyPending = 0; + es->opsPending = 0; + EVENT_DEBUG(("CreateSelector() -> %p\n", (void *) es)); + return es; +} + +/********************************************************************** +* %FUNCTION: Event_DestroySelector +* %ARGUMENTS: +* es -- EventSelector to destroy +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Destroys an EventSelector. Destruction may be delayed if we +* are in the HandleEvent function. +***********************************************************************/ +void +Event_DestroySelector(EventSelector *es) +{ + if (es->nestLevel) { + es->destroyPending = 1; + es->opsPending = 1; + return; + } + DestroySelector(es); +} + +/********************************************************************** +* %FUNCTION: Event_HandleEvent +* %ARGUMENTS: +* es -- EventSelector +* %RETURNS: +* 0 if OK, non-zero on error. errno is set appropriately. +* %DESCRIPTION: +* Handles a single event (uses select() to wait for an event.) +***********************************************************************/ +int +Event_HandleEvent(EventSelector *es) +{ + fd_set readfds, writefds; + fd_set *rd, *wr; + unsigned int flags; + + struct timeval abs_timeout, now; + struct timeval timeout; + struct timeval *tm; + EventHandler *eh; + + int r = 0; + int errno_save = 0; + int foundTimeoutEvent = 0; + int foundReadEvent = 0; + int foundWriteEvent = 0; + int maxfd = -1; + int pastDue; + + EVENT_DEBUG(("Enter Event_HandleEvent(es=%p)\n", (void *) es)); + + /* Build the select sets */ + FD_ZERO(&readfds); + FD_ZERO(&writefds); + + eh = es->handlers; + for (eh=es->handlers; eh; eh=eh->next) { + if (eh->flags & EVENT_FLAG_DELETED) continue; + if (eh->flags & EVENT_FLAG_READABLE) { + foundReadEvent = 1; + FD_SET(eh->fd, &readfds); + if (eh->fd > maxfd) maxfd = eh->fd; + } + if (eh->flags & EVENT_FLAG_WRITEABLE) { + foundWriteEvent = 1; + FD_SET(eh->fd, &writefds); + if (eh->fd > maxfd) maxfd = eh->fd; + } + if (eh->flags & EVENT_TIMER_BITS) { + if (!foundTimeoutEvent) { + abs_timeout = eh->tmout; + foundTimeoutEvent = 1; + } else { + if (eh->tmout.tv_sec < abs_timeout.tv_sec || + (eh->tmout.tv_sec == abs_timeout.tv_sec && + eh->tmout.tv_usec < abs_timeout.tv_usec)) { + abs_timeout = eh->tmout; + } + } + } + } + if (foundReadEvent) { + rd = &readfds; + } else { + rd = NULL; + } + if (foundWriteEvent) { + wr = &writefds; + } else { + wr = NULL; + } + + if (foundTimeoutEvent) { + gettimeofday(&now, NULL); + /* Convert absolute timeout to relative timeout for select */ + timeout.tv_usec = abs_timeout.tv_usec - now.tv_usec; + timeout.tv_sec = abs_timeout.tv_sec - now.tv_sec; + if (timeout.tv_usec < 0) { + timeout.tv_usec += 1000000; + timeout.tv_sec--; + } + if (timeout.tv_sec < 0 || + (timeout.tv_sec == 0 && timeout.tv_usec < 0)) { + timeout.tv_sec = 0; + timeout.tv_usec = 0; + } + tm = &timeout; + } else { + tm = NULL; + } + + if (foundReadEvent || foundWriteEvent || foundTimeoutEvent) { + for(;;) { + r = select(maxfd+1, rd, wr, NULL, tm); + if (r < 0) { + if (errno == EINTR) continue; + } + break; + } + } + + if (foundTimeoutEvent) gettimeofday(&now, NULL); + errno_save = errno; + es->nestLevel++; + + if (r >= 0) { + /* Call handlers */ + for (eh=es->handlers; eh; eh=eh->next) { + + /* Pending delete for this handler? Ignore it */ + if (eh->flags & EVENT_FLAG_DELETED) continue; + + flags = 0; + if ((eh->flags & EVENT_FLAG_READABLE) && + FD_ISSET(eh->fd, &readfds)) { + flags |= EVENT_FLAG_READABLE; + } + if ((eh->flags & EVENT_FLAG_WRITEABLE) && + FD_ISSET(eh->fd, &writefds)) { + flags |= EVENT_FLAG_WRITEABLE; + } + if (eh->flags & EVENT_TIMER_BITS) { + pastDue = (eh->tmout.tv_sec < now.tv_sec || + (eh->tmout.tv_sec == now.tv_sec && + eh->tmout.tv_usec <= now.tv_usec)); + if (pastDue) { + flags |= EVENT_TIMER_BITS; + if (eh->flags & EVENT_FLAG_TIMER) { + /* Timer events are only called once */ + es->opsPending = 1; + eh->flags |= EVENT_FLAG_DELETED; + } + } + } + /* Do callback */ + if (flags) { + EVENT_DEBUG(("Enter callback: eh=%p flags=%u\n", eh, flags)); + eh->fn(es, eh->fd, flags, eh->data); + EVENT_DEBUG(("Leave callback: eh=%p flags=%u\n", eh, flags)); + } + } + } + + es->nestLevel--; + + if (!es->nestLevel && es->opsPending) { + DoPendingChanges(es); + } + errno = errno_save; + return r; +} + +/********************************************************************** +* %FUNCTION: Event_AddHandler +* %ARGUMENTS: +* es -- event selector +* fd -- file descriptor to watch +* flags -- combination of EVENT_FLAG_READABLE and EVENT_FLAG_WRITEABLE +* fn -- callback function to call when event is triggered +* data -- extra data to pass to callback function +* %RETURNS: +* A newly-allocated EventHandler, or NULL. +***********************************************************************/ +EventHandler * +Event_AddHandler(EventSelector *es, + int fd, + unsigned int flags, + EventCallbackFunc fn, + void *data) +{ + EventHandler *eh; + + /* Specifically disable timer and deleted flags */ + flags &= (~(EVENT_TIMER_BITS | EVENT_FLAG_DELETED)); + + /* Bad file descriptor */ + if (fd < 0) { + errno = EBADF; + return NULL; + } + + eh = malloc(sizeof(EventHandler)); + if (!eh) return NULL; + eh->fd = fd; + eh->flags = flags; + eh->tmout.tv_usec = 0; + eh->tmout.tv_sec = 0; + eh->fn = fn; + eh->data = data; + + /* Add immediately. This is safe even if we are in a handler. */ + eh->next = es->handlers; + es->handlers = eh; + + EVENT_DEBUG(("Event_AddHandler(es=%p, fd=%d, flags=%u) -> %p\n", es, fd, flags, eh)); + return eh; +} + +/********************************************************************** +* %FUNCTION: Event_AddHandlerWithTimeout +* %ARGUMENTS: +* es -- event selector +* fd -- file descriptor to watch +* flags -- combination of EVENT_FLAG_READABLE and EVENT_FLAG_WRITEABLE +* t -- Timeout after which to call handler, even if not readable/writable. +* If t.tv_sec < 0, calls normal Event_AddHandler with no timeout. +* fn -- callback function to call when event is triggered +* data -- extra data to pass to callback function +* %RETURNS: +* A newly-allocated EventHandler, or NULL. +***********************************************************************/ +EventHandler * +Event_AddHandlerWithTimeout(EventSelector *es, + int fd, + unsigned int flags, + struct timeval t, + EventCallbackFunc fn, + void *data) +{ + EventHandler *eh; + struct timeval now; + + /* If timeout is negative, just do normal non-timing-out event */ + if (t.tv_sec < 0 || t.tv_usec < 0) { + return Event_AddHandler(es, fd, flags, fn, data); + } + + /* Specifically disable timer and deleted flags */ + flags &= (~(EVENT_FLAG_TIMER | EVENT_FLAG_DELETED)); + flags |= EVENT_FLAG_TIMEOUT; + + /* Bad file descriptor? */ + if (fd < 0) { + errno = EBADF; + return NULL; + } + + /* Bad timeout? */ + if (t.tv_usec >= 1000000) { + errno = EINVAL; + return NULL; + } + + eh = malloc(sizeof(EventHandler)); + if (!eh) return NULL; + + /* Convert time interval to absolute time */ + gettimeofday(&now, NULL); + + t.tv_sec += now.tv_sec; + t.tv_usec += now.tv_usec; + if (t.tv_usec >= 1000000) { + t.tv_usec -= 1000000; + t.tv_sec++; + } + + eh->fd = fd; + eh->flags = flags; + eh->tmout = t; + eh->fn = fn; + eh->data = data; + + /* Add immediately. This is safe even if we are in a handler. */ + eh->next = es->handlers; + es->handlers = eh; + + EVENT_DEBUG(("Event_AddHandlerWithTimeout(es=%p, fd=%d, flags=%u, t=%d/%d) -> %p\n", es, fd, flags, t.tv_sec, t.tv_usec, eh)); + return eh; +} + + +/********************************************************************** +* %FUNCTION: Event_AddTimerHandler +* %ARGUMENTS: +* es -- event selector +* t -- time interval after which to trigger event +* fn -- callback function to call when event is triggered +* data -- extra data to pass to callback function +* %RETURNS: +* A newly-allocated EventHandler, or NULL. +***********************************************************************/ +EventHandler * +Event_AddTimerHandler(EventSelector *es, + struct timeval t, + EventCallbackFunc fn, + void *data) +{ + EventHandler *eh; + struct timeval now; + + /* Check time interval for validity */ + if (t.tv_sec < 0 || t.tv_usec < 0 || t.tv_usec >= 1000000) { + errno = EINVAL; + return NULL; + } + + eh = malloc(sizeof(EventHandler)); + if (!eh) return NULL; + + /* Convert time interval to absolute time */ + gettimeofday(&now, NULL); + + t.tv_sec += now.tv_sec; + t.tv_usec += now.tv_usec; + if (t.tv_usec >= 1000000) { + t.tv_usec -= 1000000; + t.tv_sec++; + } + + eh->fd = -1; + eh->flags = EVENT_FLAG_TIMER; + eh->tmout = t; + eh->fn = fn; + eh->data = data; + + /* Add immediately. This is safe even if we are in a handler. */ + eh->next = es->handlers; + es->handlers = eh; + + EVENT_DEBUG(("Event_AddTimerHandler(es=%p, t=%d/%d) -> %p\n", es, t.tv_sec,t.tv_usec, eh)); + return eh; +} + +/********************************************************************** +* %FUNCTION: Event_DelHandler +* %ARGUMENTS: +* es -- event selector +* eh -- event handler +* %RETURNS: +* 0 if OK, non-zero if there is an error +* %DESCRIPTION: +* Deletes the event handler eh +***********************************************************************/ +int +Event_DelHandler(EventSelector *es, + EventHandler *eh) +{ + /* Scan the handlers list */ + EventHandler *cur, *prev; + EVENT_DEBUG(("Event_DelHandler(es=%p, eh=%p)\n", es, eh)); + for (cur=es->handlers, prev=NULL; cur; prev=cur, cur=cur->next) { + if (cur == eh) { + if (es->nestLevel) { + eh->flags |= EVENT_FLAG_DELETED; + es->opsPending = 1; + return 0; + } else { + if (prev) prev->next = cur->next; + else es->handlers = cur->next; + + DestroyHandler(cur); + return 0; + } + } + } + + /* Handler not found */ + return 1; +} + +/********************************************************************** +* %FUNCTION: DestroySelector +* %ARGUMENTS: +* es -- an event selector +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Destroys selector and all associated handles. +***********************************************************************/ +void +DestroySelector(EventSelector *es) +{ + EventHandler *cur, *next; + for (cur=es->handlers; cur; cur=next) { + next = cur->next; + DestroyHandler(cur); + } + + free(es); +} + +/********************************************************************** +* %FUNCTION: DestroyHandler +* %ARGUMENTS: +* eh -- an event handler +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Destroys handler +***********************************************************************/ +void +DestroyHandler(EventHandler *eh) +{ + EVENT_DEBUG(("DestroyHandler(eh=%p)\n", eh)); + free(eh); +} + +/********************************************************************** +* %FUNCTION: DoPendingChanges +* %ARGUMENTS: +* es -- an event selector +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Makes all pending insertions and deletions happen. +***********************************************************************/ +void +DoPendingChanges(EventSelector *es) +{ + EventHandler *cur, *prev, *next; + + es->opsPending = 0; + + /* If selector is to be deleted, do it and skip everything else */ + if (es->destroyPending) { + DestroySelector(es); + return; + } + + /* Do deletions */ + cur = es->handlers; + prev = NULL; + while(cur) { + if (!(cur->flags & EVENT_FLAG_DELETED)) { + prev = cur; + cur = cur->next; + continue; + } + + /* Unlink from list */ + if (prev) { + prev->next = cur->next; + } else { + es->handlers = cur->next; + } + next = cur->next; + DestroyHandler(cur); + cur = next; + } +} + +/********************************************************************** +* %FUNCTION: Event_GetCallback +* %ARGUMENTS: +* eh -- the event handler +* %RETURNS: +* The callback function +***********************************************************************/ +EventCallbackFunc +Event_GetCallback(EventHandler *eh) +{ + return eh->fn; +} + +/********************************************************************** +* %FUNCTION: Event_GetData +* %ARGUMENTS: +* eh -- the event handler +* %RETURNS: +* The "data" field. +***********************************************************************/ +void * +Event_GetData(EventHandler *eh) +{ + return eh->data; +} + +/********************************************************************** +* %FUNCTION: Event_SetCallbackAndData +* %ARGUMENTS: +* eh -- the event handler +* fn -- new callback function +* data -- new data value +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Sets the callback function and data fields. +***********************************************************************/ +void +Event_SetCallbackAndData(EventHandler *eh, + EventCallbackFunc fn, + void *data) +{ + eh->fn = fn; + eh->data = data; +} + +#ifdef DEBUG_EVENT +#include <stdarg.h> +#include <stdio.h> +FILE *Event_DebugFP = NULL; +/********************************************************************** +* %FUNCTION: Event_DebugMsg +* %ARGUMENTS: +* fmt, ... -- format string +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Writes a debug message to the debug file. +***********************************************************************/ +void +Event_DebugMsg(char const *fmt, ...) +{ + va_list ap; + struct timeval now; + + if (!Event_DebugFP) return; + + gettimeofday(&now, NULL); + + fprintf(Event_DebugFP, "%03d.%03d ", (int) now.tv_sec % 1000, + (int) now.tv_usec / 1000); + + va_start(ap, fmt); + vfprintf(Event_DebugFP, fmt, ap); + va_end(ap); + fflush(Event_DebugFP); +} + +#endif + +/********************************************************************** +* %FUNCTION: Event_EnableDebugging +* %ARGUMENTS: +* fname -- name of file to log debug messages to +* %RETURNS: +* 1 if debugging was enabled; 0 otherwise. +***********************************************************************/ +int +Event_EnableDebugging(char const *fname) +{ +#ifndef DEBUG_EVENT + return 0; +#else + Event_DebugFP = fopen(fname, "w"); + return (Event_DebugFP != NULL); +#endif +} + +/********************************************************************** +* %FUNCTION: Event_ChangeTimeout +* %ARGUMENTS: +* h -- event handler +* t -- new timeout +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Changes timeout of event handler to be "t" seconds in the future. +***********************************************************************/ +void +Event_ChangeTimeout(EventHandler *h, struct timeval t) +{ + struct timeval now; + + /* Check time interval for validity */ + if (t.tv_sec < 0 || t.tv_usec < 0 || t.tv_usec >= 1000000) { + return; + } + /* Convert time interval to absolute time */ + gettimeofday(&now, NULL); + + t.tv_sec += now.tv_sec; + t.tv_usec += now.tv_usec; + if (t.tv_usec >= 1000000) { + t.tv_usec -= 1000000; + t.tv_sec++; + } + + h->tmout = t; +} diff --git a/release/src/router/rp-l2tp/libevent/event.h b/release/src/router/rp-l2tp/libevent/event.h new file mode 100644 index 00000000..c6850c2e --- /dev/null +++ b/release/src/router/rp-l2tp/libevent/event.h @@ -0,0 +1,114 @@ +/*********************************************************************** +* +* event.h +* +* Abstraction of select call into "event-handling" to make programming +* easier. +* +* Copyright (C) 2001 Roaring Penguin Software Inc. +* +* This program may be distributed according to the terms of the GNU +* General Public License, version 2 or (at your option) any later version. +* +* $Id: event.h,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $ +* +* LIC: GPL +* +***********************************************************************/ + +#define DEBUG_EVENT + +#ifndef INCLUDE_EVENT_H +#define INCLUDE_EVENT_H 1 + +/* Solaris moans if we don't do this... */ +#ifdef __sun +#define __EXTENSIONS__ 1 +#endif + +struct EventSelector_t; + +/* Callback function */ +typedef void (*EventCallbackFunc)(struct EventSelector_t *es, + int fd, unsigned int flags, + void *data); + +#include "eventpriv.h" + +/* Create an event selector */ +extern EventSelector *Event_CreateSelector(void); + +/* Destroy the event selector */ +extern void Event_DestroySelector(EventSelector *es); + +/* Handle one event */ +extern int Event_HandleEvent(EventSelector *es); + +/* Add a handler for a ready file descriptor */ +extern EventHandler *Event_AddHandler(EventSelector *es, + int fd, + unsigned int flags, + EventCallbackFunc fn, void *data); + +/* Add a handler for a ready file descriptor with associated timeout*/ +extern EventHandler *Event_AddHandlerWithTimeout(EventSelector *es, + int fd, + unsigned int flags, + struct timeval t, + EventCallbackFunc fn, + void *data); + + +/* Add a timer handler */ +extern EventHandler *Event_AddTimerHandler(EventSelector *es, + struct timeval t, + EventCallbackFunc fn, + void *data); + +/* Change the timeout of a timer handler */ +void Event_ChangeTimeout(EventHandler *handler, struct timeval t); + +/* Delete a handler */ +extern int Event_DelHandler(EventSelector *es, + EventHandler *eh); + +/* Retrieve callback function from a handler */ +extern EventCallbackFunc Event_GetCallback(EventHandler *eh); + +/* Retrieve data field from a handler */ +extern void *Event_GetData(EventHandler *eh); + +/* Set callback and data to new values */ +extern void Event_SetCallbackAndData(EventHandler *eh, + EventCallbackFunc fn, + void *data); + +/* Handle a signal synchronously in event loop */ +int Event_HandleSignal(EventSelector *es, int sig, void (*handler)(int sig)); + +/* Reap children synchronously in event loop */ +int Event_HandleChildExit(EventSelector *es, pid_t pid, + void (*handler)(pid_t, int, void *), void *data); + +extern int Event_EnableDebugging(char const *fname); + +#ifdef DEBUG_EVENT +extern void Event_DebugMsg(char const *fmt, ...); +#define EVENT_DEBUG(x) Event_DebugMsg x +#else +#define EVENT_DEBUG(x) ((void) 0) +#endif + +/* Flags */ +#define EVENT_FLAG_READABLE 1 +#define EVENT_FLAG_WRITEABLE 2 +#define EVENT_FLAG_WRITABLE EVENT_FLAG_WRITEABLE + +/* This is strictly a timer event */ +#define EVENT_FLAG_TIMER 4 + +/* This is a read or write event with an associated timeout */ +#define EVENT_FLAG_TIMEOUT 8 + +#define EVENT_TIMER_BITS (EVENT_FLAG_TIMER | EVENT_FLAG_TIMEOUT) +#endif diff --git a/release/src/router/rp-l2tp/libevent/event.o b/release/src/router/rp-l2tp/libevent/event.o Binary files differnew file mode 100644 index 00000000..d5a6e2da --- /dev/null +++ b/release/src/router/rp-l2tp/libevent/event.o diff --git a/release/src/router/rp-l2tp/libevent/event_sig.c b/release/src/router/rp-l2tp/libevent/event_sig.c new file mode 100644 index 00000000..0d46f76b --- /dev/null +++ b/release/src/router/rp-l2tp/libevent/event_sig.c @@ -0,0 +1,265 @@ +/*********************************************************************** +* +* event_sig.c +* +* Code for handling signals nicely (synchronously) and for dealing +* with reaping child processes. +* +* Copyright (C) 2002 by Roaring Penguin Software Inc. +* +* This software may be distributed under the terms of the GNU General +* Public License, Version 2, or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id: event_sig.c,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $"; + +#define _POSIX_SOURCE 1 /* For sigaction defines */ +#define _BSD_SOURCE 1 /* For SA_RESTART */ + +#include <signal.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <errno.h> +#include <stddef.h> + +#include "event.h" +#include "hash.h" + +/* Kludge for figuring out NSIG */ +#ifdef NSIG +#define MAX_SIGNALS NSIG +#elif defined(_NSIG) +#define MAX_SIGNALS _NSIG +#else +#define MAX_SIGNALS 256 /* Should be safe... */ +#endif + +/* A structure for a "synchronous" signal handler */ +struct SynchronousSignalHandler { + int fired; /* Have we received this signal? */ + void (*handler)(int sig); /* Handler function */ +}; + +/* A structure for calling back when a child dies */ +struct ChildEntry { + hash_bucket hash; + void (*handler)(pid_t pid, int status, void *data); + pid_t pid; + void *data; +}; + +static struct SynchronousSignalHandler SignalHandlers[MAX_SIGNALS]; +static int Pipe[2] = {-1, -1}; +static EventHandler *PipeHandler = NULL; +static sig_atomic_t PipeFull = 0; +static hash_table child_process_table; + +static unsigned int child_hash(void *data) +{ + return (unsigned int) ((struct ChildEntry *) data)->pid; +} + +static int child_compare(void *d1, void *d2) +{ + return ((struct ChildEntry *)d1)->pid != ((struct ChildEntry *)d2)->pid; +} + +/********************************************************************** +* %FUNCTION: DoPipe +* %ARGUMENTS: +* es -- event selector +* fd -- readable file descriptor +* flags -- flags from event system +* data -- ignored +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Called when an async signal handler wants attention. This function +* fires all "synchronous" signal handlers. +***********************************************************************/ +static void +DoPipe(EventSelector *es, + int fd, + unsigned int flags, + void *data) +{ + char buf[64]; + int i; + + /* Clear buffer */ + read(fd, buf, 64); + PipeFull = 0; + + /* Fire handlers */ + for (i=0; i<MAX_SIGNALS; i++) { + if (SignalHandlers[i].fired && + SignalHandlers[i].handler) { + SignalHandlers[i].handler(i); + } + SignalHandlers[i].fired = 0; + } +} + +/********************************************************************** +* %FUNCTION: sig_handler +* %ARGUMENTS: +* sig -- signal number +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Marks a signal as having "fired"; fills IPC pipe. +***********************************************************************/ +static void +sig_handler(int sig) +{ + if (sig <0 || sig > MAX_SIGNALS) { + /* Ooops... */ + return; + } + SignalHandlers[sig].fired = 1; + if (!PipeFull) { + write(Pipe[1], &sig, 1); + PipeFull = 1; + } +} + +/********************************************************************** +* %FUNCTION: child_handler +* %ARGUMENTS: +* sig -- signal number (whoop-dee-doo) +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Called *SYNCHRONOUSLY* to reap dead children. +***********************************************************************/ +static void +child_handler(int sig) +{ + int status; + int pid; + struct ChildEntry *ce; + struct ChildEntry candidate; + + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { + candidate.pid = (pid_t) pid; + ce = hash_find(&child_process_table, &candidate); + if (ce) { + if (ce->handler) { + ce->handler(pid, status, ce->data); + } + hash_remove(&child_process_table, ce); + free(ce); + } + } +} + +/********************************************************************** +* %FUNCTION: SetupPipes (static) +* %ARGUMENTS: +* es -- event selector +* %RETURNS: +* 0 on success; -1 on failure +* %DESCRIPTION: +* Sets up pipes with an event handler to handle IPC from a signal handler +***********************************************************************/ +static int +SetupPipes(EventSelector *es) +{ + /* If already done, do nothing */ + if (PipeHandler) return 0; + + /* Initialize the child-process hash table */ + hash_init(&child_process_table, + offsetof(struct ChildEntry, hash), + child_hash, + child_compare); + + /* Open pipe to self */ + if (pipe(Pipe) < 0) { + return -1; + } + + PipeHandler = Event_AddHandler(es, Pipe[0], + EVENT_FLAG_READABLE, DoPipe, NULL); + if (!PipeHandler) { + int old_errno = errno; + close(Pipe[0]); + close(Pipe[1]); + errno = old_errno; + return -1; + } + return 0; +} + +/********************************************************************** +* %FUNCTION: Event_HandleSignal +* %ARGUMENTS: +* es -- event selector +* sig -- signal number +* handler -- handler to call when signal is raised. Handler is called +* "synchronously" as events are processed by event loop. +* %RETURNS: +* 0 on success, -1 on error. +* %DESCRIPTION: +* Sets up a "synchronous" signal handler. +***********************************************************************/ +int +Event_HandleSignal(EventSelector *es, + int sig, + void (*handler)(int sig)) +{ + struct sigaction act; + + if (SetupPipes(es) < 0) return -1; + + act.sa_handler = sig_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; +#ifdef SA_RESTART + act.sa_flags |= SA_RESTART; +#endif + if (sig == SIGCHLD) { + act.sa_flags |= SA_NOCLDSTOP; + } + if (sigaction(sig, &act, NULL) < 0) return -1; + + SignalHandlers[sig].handler = handler; + + return 0; +} + +/********************************************************************** +* %FUNCTION: Event_HandleChildExit +* %ARGUMENTS: +* es -- event selector +* pid -- process-ID of child to wait for +* handler -- function to call when child exits +* data -- data to pass to handler when child exits +* %RETURNS: +* 0 on success, -1 on failure. +* %DESCRIPTION: +* Sets things up so that when a child exits, handler() will be called +* with the pid of the child and "data" as arguments. The call will +* be synchronous (part of the normal event loop on es). +***********************************************************************/ +int +Event_HandleChildExit(EventSelector *es, + pid_t pid, + void (*handler)(pid_t, int, void *), + void *data) +{ + struct ChildEntry *ce; + + if (Event_HandleSignal(es, SIGCHLD, child_handler) < 0) return -1; + ce = malloc(sizeof(struct ChildEntry)); + if (!ce) return -1; + ce->pid = pid; + ce->data = data; + ce->handler = handler; + hash_insert(&child_process_table, ce); + return 0; +} diff --git a/release/src/router/rp-l2tp/libevent/event_sig.o b/release/src/router/rp-l2tp/libevent/event_sig.o Binary files differnew file mode 100644 index 00000000..f54cb297 --- /dev/null +++ b/release/src/router/rp-l2tp/libevent/event_sig.o diff --git a/release/src/router/rp-l2tp/libevent/event_tcp.c b/release/src/router/rp-l2tp/libevent/event_tcp.c new file mode 100644 index 00000000..14259989 --- /dev/null +++ b/release/src/router/rp-l2tp/libevent/event_tcp.c @@ -0,0 +1,581 @@ +/*********************************************************************** +* +* event_tcp.c -- implementation of event-driven socket I/O. +* +* Copyright (C) 2001 Roaring Penguin Software Inc. +* +* This program may be distributed according to the terms of the GNU +* General Public License, version 2 or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id: event_tcp.c,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $"; + +#include "event_tcp.h" +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> + +static void free_state(EventTcpState *state); + +typedef struct EventTcpConnectState_t { + int fd; + EventHandler *conn; + EventTcpConnectFunc f; + void *data; +} EventTcpConnectState; + +/********************************************************************** +* %FUNCTION: handle_accept +* %ARGUMENTS: +* es -- event selector +* fd -- socket +* flags -- ignored +* data -- the accept callback function +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Calls accept; if a connection arrives, calls the accept callback +* function with connected descriptor +***********************************************************************/ +static void +handle_accept(EventSelector *es, + int fd, + unsigned int flags, + void *data) +{ + int conn; + EventTcpAcceptFunc f; + + EVENT_DEBUG(("tcp_handle_accept(es=%p, fd=%d, flags=%u, data=%p)\n", es, fd, flags, data)); + conn = accept(fd, NULL, NULL); + if (conn < 0) return; + f = (EventTcpAcceptFunc) data; + + f(es, conn); +} + +/********************************************************************** +* %FUNCTION: handle_connect +* %ARGUMENTS: +* es -- event selector +* fd -- socket +* flags -- ignored +* data -- the accept callback function +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Calls accept; if a connection arrives, calls the accept callback +* function with connected descriptor +***********************************************************************/ +static void +handle_connect(EventSelector *es, + int fd, + unsigned int flags, + void *data) +{ + int error = 0; + socklen_t len = sizeof(error); + EventTcpConnectState *state = (EventTcpConnectState *) data; + + EVENT_DEBUG(("tcp_handle_connect(es=%p, fd=%d, flags=%u, data=%p)\n", es, fd, flags, data)); + + /* Cancel writable event */ + Event_DelHandler(es, state->conn); + state->conn = NULL; + + /* Timeout? */ + if (flags & EVENT_FLAG_TIMEOUT) { + errno = ETIMEDOUT; + state->f(es, fd, EVENT_TCP_FLAG_TIMEOUT, state->data); + free(state); + return; + } + + /* Check for pending error */ + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + state->f(es, fd, EVENT_TCP_FLAG_IOERROR, state->data); + free(state); + return; + } + if (error) { + errno = error; + state->f(es, fd, EVENT_TCP_FLAG_IOERROR, state->data); + free(state); + return; + } + + /* It looks cool! */ + state->f(es, fd, EVENT_TCP_FLAG_COMPLETE, state->data); + free(state); +} + +/********************************************************************** +* %FUNCTION: EventTcp_CreateAcceptor +* %ARGUMENTS: +* es -- event selector +* socket -- listening socket +* f -- function to call when a connection is accepted +* data -- extra data to pass to f. +* %RETURNS: +* An event handler on success, NULL on failure. +* %DESCRIPTION: +* Sets up an accepting socket and calls "f" whenever a new +* connection arrives. +***********************************************************************/ +EventHandler * +EventTcp_CreateAcceptor(EventSelector *es, + int socket, + EventTcpAcceptFunc f) +{ + int flags; + + EVENT_DEBUG(("EventTcp_CreateAcceptor(es=%p, socket=%d)\n", es, socket)); + /* Make sure socket is non-blocking */ + flags = fcntl(socket, F_GETFL, 0); + if (flags == -1) { + return NULL; + } + if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) { + return NULL; + } + + return Event_AddHandler(es, socket, EVENT_FLAG_READABLE, + handle_accept, (void *) f); + +} + +/********************************************************************** +* %FUNCTION: free_state +* %ARGUMENTS: +* state -- EventTcpState to free +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Frees all state associated with the TcpEvent. +***********************************************************************/ +static void +free_state(EventTcpState *state) +{ + if (!state) return; + EVENT_DEBUG(("tcp_free_state(state=%p)\n", state)); + if (state->buf) free(state->buf); + if (state->eh) Event_DelHandler(state->es, state->eh); + free(state); +} + +/********************************************************************** +* %FUNCTION: handle_readable +* %ARGUMENTS: +* es -- event selector +* fd -- the readable socket +* flags -- ignored +* data -- the EventTcpState object +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Continues to fill buffer. Calls callback when done. +***********************************************************************/ +static void +handle_readable(EventSelector *es, + int fd, + unsigned int flags, + void *data) +{ + EventTcpState *state = (EventTcpState *) data; + int done = state->cur - state->buf; + int togo = state->len - done; + int nread = 0; + int flag; + + EVENT_DEBUG(("tcp_handle_readable(es=%p, fd=%d, flags=%u, data=%p)\n", es, fd, flags, data)); + + /* Timed out? */ + if (flags & EVENT_FLAG_TIMEOUT) { + errno = ETIMEDOUT; + (state->f)(es, state->socket, state->buf, done, EVENT_TCP_FLAG_TIMEOUT, + state->data); + free_state(state); + return; + } + if (state->delim < 0) { + /* Not looking for a delimiter */ + /* togo had better not be zero here! */ + nread = read(fd, state->cur, togo); + if (nread <= 0) { + /* Change connection reset to EOF if we have read at least + one char */ + if (nread < 0 && errno == ECONNRESET && done > 0) { + nread = 0; + } + flag = (nread) ? EVENT_TCP_FLAG_IOERROR : EVENT_TCP_FLAG_EOF; + /* error or EOF */ + (state->f)(es, state->socket, state->buf, done, flag, state->data); + free_state(state); + return; + } + state->cur += nread; + done += nread; + if (done >= state->len) { + /* Read enough! */ + (state->f)(es, state->socket, state->buf, done, + EVENT_TCP_FLAG_COMPLETE, state->data); + free_state(state); + return; + } + } else { + /* Looking for a delimiter */ + while ( (togo > 0) && (nread = read(fd, state->cur, 1)) == 1) { + togo--; + done++; + state->cur++; + if (*(state->cur - 1) == state->delim) break; + } + + if (nread <= 0) { + /* Error or EOF -- check for EAGAIN */ + if (nread < 0 && errno == EAGAIN) return; + } + + /* Some other error, or EOF, or delimiter, or read enough */ + if (nread < 0) { + flag = EVENT_TCP_FLAG_IOERROR; + } else if (nread == 0) { + flag = EVENT_TCP_FLAG_EOF; + } else { + flag = EVENT_TCP_FLAG_COMPLETE; + } + (state->f)(es, state->socket, state->buf, done, flag, state->data); + free_state(state); + return; + } +} + +/********************************************************************** +* %FUNCTION: handle_writeable +* %ARGUMENTS: +* es -- event selector +* fd -- the writeable socket +* flags -- ignored +* data -- the EventTcpState object +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Continues to fill buffer. Calls callback when done. +***********************************************************************/ +static void +handle_writeable(EventSelector *es, + int fd, + unsigned int flags, + void *data) +{ + EventTcpState *state = (EventTcpState *) data; + int done = state->cur - state->buf; + int togo = state->len - done; + int n; + + /* Timed out? */ + if (flags & EVENT_FLAG_TIMEOUT) { + errno = ETIMEDOUT; + if (state->f) { + (state->f)(es, state->socket, state->buf, done, EVENT_TCP_FLAG_TIMEOUT, + state->data); + } else { + close(fd); + } + free_state(state); + return; + } + + /* togo had better not be zero here! */ + n = write(fd, state->cur, togo); + + EVENT_DEBUG(("tcp_handle_writeable(es=%p, fd=%d, flags=%u, data=%p)\n", es, fd, flags, data)); + if (n <= 0) { + /* error */ + if (state->f) { + (state->f)(es, state->socket, state->buf, done, + EVENT_TCP_FLAG_IOERROR, + state->data); + } else { + close(fd); + } + free_state(state); + return; + } + state->cur += n; + done += n; + if (done >= state->len) { + /* Written enough! */ + if (state->f) { + (state->f)(es, state->socket, state->buf, done, + EVENT_TCP_FLAG_COMPLETE, state->data); + } else { + close(fd); + } + free_state(state); + return; + } + +} + +/********************************************************************** +* %FUNCTION: EventTcp_ReadBuf +* %ARGUMENTS: +* es -- event selector +* socket -- socket to read from +* len -- maximum number of bytes to read +* delim -- delimiter at which to stop reading, or -1 if we should +* read exactly len bytes +* f -- function to call on EOF or when all bytes have been read +* timeout -- if non-zero, timeout in seconds after which we cancel +* operation. +* data -- extra data to pass to function f. +* %RETURNS: +* A new EventTcpState token or NULL on error +* %DESCRIPTION: +* Sets up a handler to fill a buffer from a socket. +***********************************************************************/ +EventTcpState * +EventTcp_ReadBuf(EventSelector *es, + int socket, + int len, + int delim, + EventTcpIOFinishedFunc f, + int timeout, + void *data) +{ + EventTcpState *state; + int flags; + struct timeval t; + + EVENT_DEBUG(("EventTcp_ReadBuf(es=%p, socket=%d, len=%d, delim=%d, timeout=%d)\n", es, socket, len, delim, timeout)); + if (len <= 0) return NULL; + if (socket < 0) return NULL; + + /* Make sure socket is non-blocking */ + flags = fcntl(socket, F_GETFL, 0); + if (flags == -1) { + return NULL; + } + if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) { + return NULL; + } + + state = malloc(sizeof(EventTcpState)); + if (!state) return NULL; + + memset(state, 0, sizeof(EventTcpState)); + + state->socket = socket; + + state->buf = malloc(len); + if (!state->buf) { + free_state(state); + return NULL; + } + + state->cur = state->buf; + state->len = len; + state->f = f; + state->es = es; + + if (timeout <= 0) { + t.tv_sec = -1; + t.tv_usec = -1; + } else { + t.tv_sec = timeout; + t.tv_usec = 0; + } + + state->eh = Event_AddHandlerWithTimeout(es, socket, EVENT_FLAG_READABLE, + t, handle_readable, + (void *) state); + if (!state->eh) { + free_state(state); + return NULL; + } + state->data = data; + state->delim = delim; + EVENT_DEBUG(("EventTcp_ReadBuf() -> %p\n", state)); + + return state; +} + +/********************************************************************** +* %FUNCTION: EventTcp_WriteBuf +* %ARGUMENTS: +* es -- event selector +* socket -- socket to read from +* buf -- buffer to write +* len -- number of bytes to write +* f -- function to call on EOF or when all bytes have been read +* timeout -- timeout after which to cancel operation +* data -- extra data to pass to function f. +* %RETURNS: +* A new EventTcpState token or NULL on error +* %DESCRIPTION: +* Sets up a handler to fill a buffer from a socket. +***********************************************************************/ +EventTcpState * +EventTcp_WriteBuf(EventSelector *es, + int socket, + char *buf, + int len, + EventTcpIOFinishedFunc f, + int timeout, + void *data) +{ + EventTcpState *state; + int flags; + struct timeval t; + + EVENT_DEBUG(("EventTcp_WriteBuf(es=%p, socket=%d, len=%d, timeout=%d)\n", es, socket, len, timeout)); + if (len <= 0) return NULL; + if (socket < 0) return NULL; + + /* Make sure socket is non-blocking */ + flags = fcntl(socket, F_GETFL, 0); + if (flags == -1) { + return NULL; + } + if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) { + return NULL; + } + + state = malloc(sizeof(EventTcpState)); + if (!state) return NULL; + + memset(state, 0, sizeof(EventTcpState)); + + state->socket = socket; + + state->buf = malloc(len); + if (!state->buf) { + free_state(state); + return NULL; + } + memcpy(state->buf, buf, len); + + state->cur = state->buf; + state->len = len; + state->f = f; + state->es = es; + + if (timeout <= 0) { + t.tv_sec = -1; + t.tv_usec = -1; + } else { + t.tv_sec = timeout; + t.tv_usec = 0; + } + + state->eh = Event_AddHandlerWithTimeout(es, socket, EVENT_FLAG_WRITEABLE, + t, handle_writeable, + (void *) state); + if (!state->eh) { + free_state(state); + return NULL; + } + + state->data = data; + state->delim = -1; + EVENT_DEBUG(("EventTcp_WriteBuf() -> %p\n", state)); + return state; +} + +/********************************************************************** +* %FUNCTION: EventTcp_Connect +* %ARGUMENTS: +* es -- event selector +* fd -- descriptor to connect +* addr -- address to connect to +* addrlen -- length of address +* f -- function to call with connected socket +* data -- extra data to pass to f +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Does a non-blocking connect on fd +***********************************************************************/ +void +EventTcp_Connect(EventSelector *es, + int fd, + struct sockaddr const *addr, + socklen_t addrlen, + EventTcpConnectFunc f, + int timeout, + void *data) +{ + int flags; + int n; + EventTcpConnectState *state; + struct timeval t; + + /* Make sure socket is non-blocking */ + flags = fcntl(fd, F_GETFL, 0); + if (flags == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { + f(es, fd, EVENT_TCP_FLAG_IOERROR, data); + return; + } + + n = connect(fd, addr, addrlen); + if (n < 0) { + if (errno != EINPROGRESS) { + f(es, fd, EVENT_TCP_FLAG_IOERROR, data); + return; + } + } + + if (n == 0) { /* Connect succeeded immediately */ + f(es, fd, EVENT_TCP_FLAG_COMPLETE, data); + return; + } + + state = malloc(sizeof(*state)); + if (!state) { + f(es, fd, EVENT_TCP_FLAG_IOERROR, data); + return; + } + state->f = f; + state->fd = fd; + state->data = data; + + if (timeout <= 0) { + t.tv_sec = -1; + t.tv_usec = -1; + } else { + t.tv_sec = timeout; + t.tv_usec = 0; + } + + state->conn = Event_AddHandlerWithTimeout(es, fd, EVENT_FLAG_WRITEABLE, + t, handle_connect, + (void *) state); + if (!state->conn) { + free(state); + f(es, fd, EVENT_TCP_FLAG_IOERROR, data); + return; + } +} + +/********************************************************************** +* %FUNCTION: EventTcp_CancelPending +* %ARGUMENTS: +* s -- an EventTcpState +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Cancels the pending event handler +***********************************************************************/ +void +EventTcp_CancelPending(EventTcpState *s) +{ + free_state(s); +} diff --git a/release/src/router/rp-l2tp/libevent/event_tcp.h b/release/src/router/rp-l2tp/libevent/event_tcp.h new file mode 100644 index 00000000..ae9ac0b1 --- /dev/null +++ b/release/src/router/rp-l2tp/libevent/event_tcp.h @@ -0,0 +1,87 @@ +/*********************************************************************** +* +* event-tcp.h +* +* Event-driven TCP functions to allow for single-threaded "concurrent" +* server. +* +* Copyright (C) 2001 Roaring Penguin Software Inc. +* +* $Id: event_tcp.h,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $ +* +* This program may be distributed according to the terms of the GNU +* General Public License, version 2 or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +#ifndef INCLUDE_EVENT_TCP_H +#define INCLUDE_EVENT_TCP_H 1 + +#include "event.h" +#include <sys/socket.h> + +typedef void (*EventTcpAcceptFunc)(EventSelector *es, + int fd); + +typedef void (*EventTcpConnectFunc)(EventSelector *es, + int fd, + int flag, + void *data); + +typedef void (*EventTcpIOFinishedFunc)(EventSelector *es, + int fd, + char *buf, + int len, + int flag, + void *data); + +#define EVENT_TCP_FLAG_COMPLETE 0 +#define EVENT_TCP_FLAG_IOERROR 1 +#define EVENT_TCP_FLAG_EOF 2 +#define EVENT_TCP_FLAG_TIMEOUT 3 + +typedef struct EventTcpState_t { + int socket; + char *buf; + char *cur; + int len; + int delim; + EventTcpIOFinishedFunc f; + EventSelector *es; + EventHandler *eh; + void *data; +} EventTcpState; + +extern EventHandler *EventTcp_CreateAcceptor(EventSelector *es, + int socket, + EventTcpAcceptFunc f); + +extern void EventTcp_Connect(EventSelector *es, + int fd, + struct sockaddr const *addr, + socklen_t addrlen, + EventTcpConnectFunc f, + int timeout, + void *data); + +extern EventTcpState *EventTcp_ReadBuf(EventSelector *es, + int socket, + int len, + int delim, + EventTcpIOFinishedFunc f, + int timeout, + void *data); + +extern EventTcpState *EventTcp_WriteBuf(EventSelector *es, + int socket, + char *buf, + int len, + EventTcpIOFinishedFunc f, + int timeout, + void *data); + +extern void EventTcp_CancelPending(EventTcpState *s); + +#endif diff --git a/release/src/router/rp-l2tp/libevent/event_tcp.o b/release/src/router/rp-l2tp/libevent/event_tcp.o Binary files differnew file mode 100644 index 00000000..8a130463 --- /dev/null +++ b/release/src/router/rp-l2tp/libevent/event_tcp.o diff --git a/release/src/router/rp-l2tp/libevent/eventpriv.h b/release/src/router/rp-l2tp/libevent/eventpriv.h new file mode 100644 index 00000000..193e1b33 --- /dev/null +++ b/release/src/router/rp-l2tp/libevent/eventpriv.h @@ -0,0 +1,46 @@ +/*********************************************************************** +* +* eventpriv.h +* +* Abstraction of select call into "event-handling" to make programming +* easier. This header includes "private" definitions which users +* of the event-handling code should not care about. +* +* Copyright (C) 2001 Roaring Penguin Software Inc. +* +* This program may be distributed according to the terms of the GNU +* General Public License, version 2 or (at your option) any later version. +* +* $Id: eventpriv.h,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $ +* +* LIC: GPL +* +***********************************************************************/ + +#ifndef INCLUDE_EVENTPRIV_H +#define INCLUDE_EVENTPRIV_H 1 +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +/* Handler structure */ +typedef struct EventHandler_t { + struct EventHandler_t *next; /* Link in list */ + int fd; /* File descriptor for select */ + unsigned int flags; /* Select on read or write; enable timeout */ + struct timeval tmout; /* Absolute time for timeout */ + EventCallbackFunc fn; /* Callback function */ + void *data; /* Extra data to pass to callback */ +} EventHandler; + +/* Selector structure */ +typedef struct EventSelector_t { + EventHandler *handlers; /* Linked list of EventHandlers */ + int nestLevel; /* Event-handling nesting level */ + int opsPending; /* True if operations are pending */ + int destroyPending; /* If true, a destroy is pending */ +} EventSelector; + +/* Private flags */ +#define EVENT_FLAG_DELETED 256 +#endif diff --git a/release/src/router/rp-l2tp/libevent/hash.c b/release/src/router/rp-l2tp/libevent/hash.c new file mode 100644 index 00000000..00e08032 --- /dev/null +++ b/release/src/router/rp-l2tp/libevent/hash.c @@ -0,0 +1,266 @@ +/*********************************************************************** +* +* hash.c +* +* Implementation of hash tables. Each item inserted must include +* a hash_bucket member. +* +* Copyright (C) 2002 Roaring Penguin Software Inc. +* +* This software may be distributed under the terms of the GNU General +* Public License, Version 2 or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id: hash.c,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $"; + +#include "hash.h" + +#include <limits.h> +#define BITS_IN_int ( sizeof(int) * CHAR_BIT ) +#define THREE_QUARTERS ((int) ((BITS_IN_int * 3) / 4)) +#define ONE_EIGHTH ((int) (BITS_IN_int / 8)) +#define HIGH_BITS ( ~((unsigned int)(~0) >> ONE_EIGHTH )) + +#define GET_BUCKET(tab, data) ((hash_bucket *) ((char *) (data) + (tab)->hash_offset)) + +#define GET_ITEM(tab, bucket) ((void *) (((char *) (void *) bucket) - (tab)->hash_offset)) + +static void *hash_next_cursor(hash_table *tab, hash_bucket *b); + +/********************************************************************** +* %FUNCTION: hash_init +* %ARGUMENTS: +* tab -- hash table +* hash_offset -- offset of hash_bucket data member in inserted items +* compute -- pointer to function to compute hash value +* compare -- pointer to comparison function. Returns 0 if items are equal, +* non-zero otherwise. +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Initializes a hash table. +***********************************************************************/ +void +hash_init(hash_table *tab, + size_t hash_offset, + unsigned int (*compute)(void *data), + int (*compare)(void *item1, void *item2)) +{ + size_t i; + + tab->hash_offset = hash_offset; + tab->compute_hash = compute; + tab->compare = compare; + for (i=0; i<HASHTAB_SIZE; i++) { + tab->buckets[i] = NULL; + } + tab->num_entries = 0; +} + +/********************************************************************** +* %FUNCTION: hash_insert +* %ARGUMENTS: +* tab -- hash table to insert into +* item -- the item we're inserting +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Inserts an item into the hash table. It must not currently be in any +* hash table. +***********************************************************************/ +void +hash_insert(hash_table *tab, + void *item) +{ + hash_bucket *b = GET_BUCKET(tab, item); + unsigned int val = tab->compute_hash(item); + b->hashval = val; + val %= HASHTAB_SIZE; + b->prev = NULL; + b->next = tab->buckets[val]; + if (b->next) { + b->next->prev = b; + } + tab->buckets[val] = b; + tab->num_entries++; +} + +/********************************************************************** +* %FUNCTION: hash_remove +* %ARGUMENTS: +* tab -- hash table +* item -- item in hash table +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Removes item from hash table +***********************************************************************/ +void +hash_remove(hash_table *tab, + void *item) +{ + hash_bucket *b = GET_BUCKET(tab, item); + unsigned int val = b->hashval % HASHTAB_SIZE; + + if (b->prev) { + b->prev->next = b->next; + } else { + tab->buckets[val] = b->next; + } + if (b->next) { + b->next->prev = b->prev; + } + tab->num_entries--; +} + +/********************************************************************** +* %FUNCTION: hash_find +* %ARGUMENTS: +* tab -- hash table +* item -- item equal to one we're seeking (in the compare-function sense) +* %RETURNS: +* A pointer to the item in the hash table, or NULL if no such item +* %DESCRIPTION: +* Searches hash table for item. +***********************************************************************/ +void * +hash_find(hash_table *tab, + void *item) +{ + unsigned int val = tab->compute_hash(item) % HASHTAB_SIZE; + hash_bucket *b; + for (b = tab->buckets[val]; b; b = b->next) { + void *item2 = GET_ITEM(tab, b); + if (!tab->compare(item, item2)) return item2; + } + return NULL; +} + +/********************************************************************** +* %FUNCTION: hash_find_next +* %ARGUMENTS: +* tab -- hash table +* item -- an item returned by hash_find or hash_find_next +* %RETURNS: +* A pointer to the next equal item in the hash table, or NULL if no such item +* %DESCRIPTION: +* Searches hash table for anoter item equivalent to this one. Search +* starts from item. +***********************************************************************/ +void * +hash_find_next(hash_table *tab, + void *item) +{ + hash_bucket *b = GET_BUCKET(tab, item); + for (b = b->next; b; b = b->next) { + void *item2 = GET_ITEM(tab, b); + if (!tab->compare(item, item2)) return item2; + } + return NULL; +} +/********************************************************************** +* %FUNCTION: hash_start +* %ARGUMENTS: +* tab -- hash table +* cursor -- a void pointer to keep track of location +* %RETURNS: +* "first" entry in hash table, or NULL if table is empty +* %DESCRIPTION: +* Starts an iterator -- sets cursor so hash_next will return next entry. +***********************************************************************/ +void * +hash_start(hash_table *tab, void **cursor) +{ + int i; + for (i=0; i<HASHTAB_SIZE; i++) { + if (tab->buckets[i]) { + /* Point cursor to NEXT item so it is valid + even if current item is free'd */ + *cursor = hash_next_cursor(tab, tab->buckets[i]); + return GET_ITEM(tab, tab->buckets[i]); + } + } + *cursor = NULL; + return NULL; +} + +/********************************************************************** +* %FUNCTION: hash_next +* %ARGUMENTS: +* tab -- hash table +* cursor -- cursor into hash table +* %RETURNS: +* Next item in table, or NULL. +* %DESCRIPTION: +* Steps cursor to next item in table. +***********************************************************************/ +void * +hash_next(hash_table *tab, void **cursor) +{ + hash_bucket *b; + + if (!*cursor) return NULL; + + b = (hash_bucket *) *cursor; + *cursor = hash_next_cursor(tab, b); + return GET_ITEM(tab, b); +} + +/********************************************************************** +* %FUNCTION: hash_next_cursor +* %ARGUMENTS: +* tab -- a hash table +* b -- a hash bucket +* %RETURNS: +* Cursor value for bucket following b in hash table. +***********************************************************************/ +static void * +hash_next_cursor(hash_table *tab, hash_bucket *b) +{ + unsigned int i; + if (!b) return NULL; + if (b->next) return b->next; + + i = b->hashval % HASHTAB_SIZE; + for (++i; i<HASHTAB_SIZE; ++i) { + if (tab->buckets[i]) return tab->buckets[i]; + } + return NULL; +} + +size_t +hash_num_entries(hash_table *tab) +{ + return tab->num_entries; +} + +/********************************************************************** +* %FUNCTION: hash_pjw +* %ARGUMENTS: +* str -- a zero-terminated string +* %RETURNS: +* A hash value using the hashpjw algorithm +* %DESCRIPTION: +* An adaptation of Peter Weinberger's (PJW) generic hashing +* algorithm based on Allen Holub's version. Accepts a pointer +* to a datum to be hashed and returns an unsigned integer. +***********************************************************************/ +unsigned int +hash_pjw(char const * str) +{ + unsigned int hash_value, i; + + for (hash_value = 0; *str; ++str) { + hash_value = ( hash_value << ONE_EIGHTH ) + *str; + if (( i = hash_value & HIGH_BITS ) != 0 ) { + hash_value = + ( hash_value ^ ( i >> THREE_QUARTERS )) & + ~HIGH_BITS; + } + } + return hash_value; +} diff --git a/release/src/router/rp-l2tp/libevent/hash.h b/release/src/router/rp-l2tp/libevent/hash.h new file mode 100644 index 00000000..6169c6ee --- /dev/null +++ b/release/src/router/rp-l2tp/libevent/hash.h @@ -0,0 +1,54 @@ +/*********************************************************************** +* +* hash.h +* +* Hash table utilities +* +* Copyright (C) 2002 Roaring Penguin Software Inc. +* +* LIC: GPL +* +***********************************************************************/ + +#ifndef HASH_H +#define HASH_H + +#include <stdlib.h> +/* Fixed-size hash tables for now */ +#define HASHTAB_SIZE 67 + +/* A hash bucket */ +typedef struct hash_bucket_t { + struct hash_bucket_t *next; + struct hash_bucket_t *prev; + unsigned int hashval; +} hash_bucket; + +/* A hash table */ +typedef struct hash_table_t { + hash_bucket *buckets[HASHTAB_SIZE]; + size_t hash_offset; + unsigned int (*compute_hash)(void *data); + int (*compare)(void *item1, void *item2); + size_t num_entries; +} hash_table; + +/* Functions */ +void hash_init(hash_table *tab, + size_t hash_offset, + unsigned int (*compute)(void *data), + int (*compare)(void *item1, void *item2)); +void hash_insert(hash_table *tab, void *item); +void hash_remove(hash_table *tab, void *item); +void *hash_find(hash_table *tab, void *item); +void *hash_find_next(hash_table *tab, void *item); +size_t hash_num_entries(hash_table *tab); + +/* Iteration functions */ +void *hash_start(hash_table *tab, void **cursor); +void *hash_next(hash_table *tab, void **cursor); + +/* Utility function: hashpjw for strings */ +unsigned int hash_pjw(char const *str); + +#endif diff --git a/release/src/router/rp-l2tp/libevent/hash.o b/release/src/router/rp-l2tp/libevent/hash.o Binary files differnew file mode 100644 index 00000000..5fb830bd --- /dev/null +++ b/release/src/router/rp-l2tp/libevent/hash.o diff --git a/release/src/router/rp-l2tp/libevent/libevent.a b/release/src/router/rp-l2tp/libevent/libevent.a Binary files differnew file mode 100644 index 00000000..81034a83 --- /dev/null +++ b/release/src/router/rp-l2tp/libevent/libevent.a diff --git a/release/src/router/rp-l2tp/main.c b/release/src/router/rp-l2tp/main.c new file mode 100644 index 00000000..01e5d687 --- /dev/null +++ b/release/src/router/rp-l2tp/main.c @@ -0,0 +1,124 @@ +/*********************************************************************** +* +* main.c +* +* Main program of l2tp +* +* Copyright (C) 2002 by Roaring Penguin Software Inc. +* +* This software may be distributed under the terms of the GNU General +* Public License, Version 2, or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id: main.c,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $"; + +#include "l2tp.h" +#include <stdio.h> +#include <string.h> +#include <getopt.h> +#include <signal.h> +#include <fcntl.h> +#include <stdlib.h> + +static void +usage(int argc, char *argv[], int exitcode) +{ + fprintf(stderr, "\nl2tpd Version %s Copyright 2002 Roaring Penguin Software Inc.\n", VERSION); + fprintf(stderr, "http://www.roaringpenguin.com/\n\n"); + fprintf(stderr, "Usage: %s [options]\n", argv[0]); + fprintf(stderr, "Options:\n"); + fprintf(stderr, "-d level -- Set debugging to 'level'\n"); + fprintf(stderr, "-f -- Do not fork\n"); + fprintf(stderr, "-h -- Print usage\n"); + fprintf(stderr, "\nThis program is licensed under the terms of\nthe GNU General Public License, Version 2.\n"); + exit(exitcode); +} + +int +main(int argc, char *argv[]) +{ + EventSelector *es = Event_CreateSelector(); + int i; + int opt; + int do_fork = 1; + int debugmask = 0; + + while((opt = getopt(argc, argv, "d:fh")) != -1) { + switch(opt) { + case 'h': + usage(argc, argv, EXIT_SUCCESS); + break; + case 'f': + do_fork = 0; + break; + case 'd': + sscanf(optarg, "%d", &debugmask); + break; + default: + usage(argc, argv, EXIT_FAILURE); + } + } + + l2tp_random_init(); + l2tp_tunnel_init(es); + l2tp_peer_init(); + l2tp_debug_set_bitmask(debugmask); + + if (l2tp_parse_config_file(es, "/tmp/l2tp.conf") < 0) { //2005-04-14 by kanki + l2tp_die(); + } + + if (!l2tp_network_init(es)) { + l2tp_die(); + } + + /* Daemonize */ + if (do_fork) { + i = fork(); + if (i < 0) { + perror("fork"); + exit(EXIT_FAILURE); + } else if (i != 0) { + /* Parent */ + exit(EXIT_SUCCESS); + } + + setsid(); + signal(SIGHUP, SIG_IGN); + i = fork(); + if (i < 0) { + perror("fork"); + exit(EXIT_FAILURE); + } else if (i != 0) { + exit(EXIT_SUCCESS); + } + + chdir("/"); + + /* Point stdin/stdout/stderr to /dev/null */ + for (i=0; i<3; i++) { + close(i); + } + i = open("/dev/console", O_RDWR); //2005-04-14 by kanki for debugging + if (i >= 0) { + dup2(i, 0); + dup2(i, 1); + dup2(i, 2); + if (i > 2) close(i); + } + } + + while(1) { + i = Event_HandleEvent(es); + if (i < 0) { + fprintf(stderr, "Event_HandleEvent returned %d\n", i); + l2tp_cleanup(); + exit(EXIT_FAILURE); + } + } + return 0; +} diff --git a/release/src/router/rp-l2tp/make-release.sh b/release/src/router/rp-l2tp/make-release.sh new file mode 100755 index 00000000..39bb7fcb --- /dev/null +++ b/release/src/router/rp-l2tp/make-release.sh @@ -0,0 +1,30 @@ +#!/bin/sh +# +# Tar up a releasable archive +# $Id: make-release.sh,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $ + +VERSION=`grep '^VERSION=' Makefile.in | sed -e 's/VERSION=//'` + +if test "$VERSION" = "" ; then + echo "Doh! Could not figure out version from Makefile.in" + exit 1 +fi + +# In DFS's tree, libevent is in parent directory. Create symlink +# if needed + +test -d libevent || ln -s ../libevent . || exit 1 + +MANIFEST="README Makefile.in install-sh auth.c configure configure.in debug.c dgram.c l2tp.conf l2tp.h main.c make-release.sh md5.c md5.h network.c options.c peer.c session.c tunnel.c utils.c handlers/Makefile.in handlers/cmd-control.c handlers/cmd.c handlers/dstring.c handlers/dstring.h handlers/pty.c handlers/sync-pppd.c man/l2tpd.8 man/l2tp.conf.5 libevent/Makefile.in libevent/event.c libevent/event.h libevent/event_sig.c libevent/event_tcp.c libevent/event_tcp.h libevent/eventpriv.h libevent/hash.c libevent/hash.h libevent/Doc/flow.fig libevent/Doc/libevent.tex libevent/Doc/style.tex libevent/Doc/libevent.pdf" + +DIR=rp-l2tp-$VERSION +PWD=`pwd` +test -d $DIR && rm -rf $DIR +mkdir $DIR || exit 1 +for i in $MANIFEST ; do + echo "Doing $i..." + d=`dirname $i` + test -d $DIR/$d || mkdir -p $DIR/$d || exit 1 + ln -s $PWD/$i $DIR/$d || exit 1 +done +exit 0 diff --git a/release/src/router/rp-l2tp/man/l2tp.conf.5 b/release/src/router/rp-l2tp/man/l2tp.conf.5 new file mode 100644 index 00000000..a83ed440 --- /dev/null +++ b/release/src/router/rp-l2tp/man/l2tp.conf.5 @@ -0,0 +1,163 @@ +.\" $Id: l2tp.conf.5,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $ +.\" LIC: GPL +.TH L2TP.CONF 5 "11 March 2002" +.\"" +.UC 4 +.SH NAME +l2tp.conf \- L2TP configuration file +.SH DESCRIPTION +The \fBl2tp.conf\fP file contains the configuration for +the L2TP daemon \fBl2tpd\fP(8). +Each line in the file takes one of the following forms: +.\" +.IP "# \fIcomment\fR" +A line beginning with a pound sign is ignored. +.\" +.IP "\fBglobal\fP" +The keyword \fBglobal\fR marks the start of the global configuration section. +.\" +.IP "\fBsection\fP \fIsec_name\fR" +The keyword \fBsection\fR marks the start of configuration options for a +particular section. The section \fIpeer\fR is built-in and allows you +to specify options relating to a particular L2TP peer. Other sections +may be present, depending on which handlers have been dynamically-loaded. +,\" +.IP "\fIoption\fR \fIvalue\fR" +All other lines contain two space-separated words: An option name +and a corresponding value. If the \fIvalue\fR contains spaces, +it should be surrounded by double-quotes. Double-quotes within the +value may be escaped with a backslash, as in C. +.\" +.SS "The GLOBAL Section" +The global section, introduced by the \fBglobal\fR keyword, defines +options applicable to the entire daemon. The available options are: +.\" +.IP "\fBlisten-port\fP" +The UDP port on which the daemon listens. By default, the standard UDP +port 1701 is used. +.\" +.IP "\fBlisten-addr\fP" +The IP address of the interface on which the daemon listens. By default, +it listens on INADDR_ANY, meaning it listens on all interfaces. +.\" +.IP "\fBload-handler\fP \fIhandler_name\fR" +Causes the daemon to dynamically-load a module which extends its functionality. +The available modules are \fIsync-pppd.so\fR, an interface to the Linux +pppd daemon, and \fIcmd.so\fR, a simple command-processor for controlling +the daemon. These handlers will be described in later sections. +.\" +.SS "The PEER Section" +The peer section, introduced by the \fBsection peer\fR line, has +the following options. Note that each set of peer options \fImust\fR +be preceded by its own \fBsection peer\fR line. +.\" +.IP "\fBpeer\fP \fIaddr\fR" +Specifies the IP address of the peer. It may be an actual IP address +in dotted-quad notation, or a host name. +.\" +.IP "\fBmask\fP \fInumber_of_bits\fR" +Specifies the number of bits to use as the netmask. The address specified +in the \fBpeer\fR command is then treated as the subnet IP. +.\" +.IP "\fBhostname\fP \fIname\fR" +Specifies the local \fIname\fP to be sent to the peer. If this option is +not specified, the local host name is sent. +.\" +.IP "\fBpeername\fP \fIname\fP" +Only accept the connection if the peer announces this \fIname\fP as +its local name. Not specifying this option puts no restrictions on the +peer name. +.\" +.IP "\fBsecret\fP \fIshared_secret\fR" +Specifies the shared secret to use for tunnel authentication with the peer. +If you omit this option, tunnel authentication is not performed. +.\" +.IP "\fBport\fP \fIport\fR" +Specifies the UDP port for contacting the peer. If the peer contacts +us first, we use whichever port the peer used. +.\" +.IP "\fBlac-handler\fP \fImodule_name\fR" +Specifies the name of the module which implements LAC functionality. +In most cases, this should be \fBsync-pppd\fR. If you omit this option, +then the daemon will not act as a LAC for the peer. +.\" +.IP "\fBlac-opts\fP \fIoptions\fP" +Specifies additional pppd options for the LAC handler. +.\" +.IP "\fBlns-handler\fP \fImodule_name\fR" +Specifies the name of the module which implements LNS functionality. +In most cases, this should be \fBsync-pppd\fR. If you omit this option, +then the daemon will not act as a LNS for the peer. +.\" +.IP "\fBlns-opts\fP \fIoptions\fP" +Specifies additional pppd options for the LNS handler. +.\" +.IP "\fBhide-avps\fP \fIbool\fR" +If \fIbool\fR is 0, we will not hide any AVP's. If it is 1, we will +hide all the AVP's for which hiding is permitted. If there is no +shared secret, no AVP's are hidden. +.\" +.IP "\fBretain-tunnel\fP \fIbool\fR" +If \fIbool\fR is 0, then the tunnel will be torn down when the last +session terminates. If it is 1, the tunnel will be maintained even +if there are no sessions. +.\" +.IP "\fBstrict-ip-check\fP \fIbool\fR" +By default, the l2tp code will discard datagrams for a given tunnel +if the source address does not match the specified peer's IP address. +If you set strict-ip-check to 0, then the l2tp code will accept +datagrams for a tunnel regardless of the source IP address. +.\" +.IP "\fBpersist\fP" +Persistent sessions will be reestablished if they terminate due to +connection or tunnel errors. +.\" +.IP "\fBmaxfail\fP \fInumber\fP" +If a session could not be reestablished after \fInumber\fP times, it will +stay down until started manually. +.\" +.IP "\fBholdoff\fP \fIdelay\fP" +Wait \fIdelay\fP seconds before trying to reestablish a session. +.\" +.SS "The SYNC-PPPD Section" +NOTE: The sync-pppd handler only works on Linux. You must have pppd version +2.4.1 or later, and your kernel must have these modules loaded (or compiled +in): ppp_synctty, ppp_generic, slhc and n_hdlc. +.PP +This section is available only if you have used \fBload-handler sync-pppd.so\fR +in the global section. Options available are: +.\" +.IP "\fBlns-pppd-opts\fP \fIoptions\fR" +Options to supply to pppd when we are acting as an LNS. +.\" +.IP "\fBlac-pppd-opts\fP \fIoptions\fR" +Options to supply to pppd when we are acting as an LAC. +.\" +.IP "\fBset-ppp-if-name\fP \fIbool\dR" +If this is set to 1, then each PPP interface is named with the process-ID +of the pppd process. For example, if pppd has process-ID 3211, then the +corresponding interface is called ppp3211. If you set this to 0 (the default), +then the kernel picks the interface name starting at ppp0. +.\" +.IP "\fBpppd-path \fIpath\fR\fP +Sets the path to the pppd program. If this option is not supplied, the +default path of \fI/usr/sbin/pppd\fR is used. +.PP +Note that the \fBsync-pppd\fR module does not directly support maintaining +a pool of IP addresses for IP address assignment. We recommend that you +assign IP addresses using a RADIUS server and the latest PPP implementation +from CVS. +.PP +Even if you do not supply any options to sync-pppd, you \fImust\fR have +a \fBsection sync-pppd\fR line to activate the handler. +.SH "The CMD Section" +This section is available only if you have used \fBload-handler cmd.so\fR +in the global section. The only option available is: +.IP "\fBsocket-path\fP \fIpathname\fR" +Specifies the path name of the UNIX-domain socket for controlling the +daemon. Defaults to \fI/var/run/l2tpctrl\fR. +.PP +Even if you do not supply any options to cmd, you \fImust\fR have +a \fBsection cmd\fR line to activate the handler. +.SH AUTHORS +\fBl2tpd\fR was written by David F. Skoll <dfs@roaringpenguin.com>. diff --git a/release/src/router/rp-l2tp/man/l2tpd.8 b/release/src/router/rp-l2tp/man/l2tpd.8 new file mode 100644 index 00000000..08b0d6eb --- /dev/null +++ b/release/src/router/rp-l2tp/man/l2tpd.8 @@ -0,0 +1,65 @@ +.\" $Id: l2tpd.8,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $ +.\" LIC: GPL +.TH L2TPD 8 "11 March 2002" +.UC 4 +.SH NAME +l2tpd \- user-space L2TP daemon. +.SH SYNOPSIS +.B l2tpd \fR[\fIoptions\fR] + +.SH DESCRIPTION +\fBl2tpd\fR is a user-space L2TP daemon. L2TP is the Layer Two +Tunneling Protocol described in RFC 2661. It allows you to tunnel +PPP sessions over a network or transport protocol (in this case, UDP.) + +.SH OPTIONS +.TP +.B \-d \fInum\fR +Sets the debug level to \fInum\fR. Of interest mostly to +\fBl2tpd\fR developers. \fInum\fR is a bitmask; set it to +65535 for maximum debugging information. + +.TP +.B \-f +Do not fork. Normally, \fBl2tpd\fR forks and puts itself in the +background. This option causes it to stay running in the foreground. + +.SH L2TPD ARCHITECTURE + +The \fBl2tpd\fR program itself contains \fIonly\fR code necessary +for processing L2TP datagrams and maintaining L2TP tunnels and +sessions. It does \fInot\fR contain code for interacting with +\fBpppd\fR or for users to control session establishment and teardown. +These functions are performed by \fIhandlers\fR, which are +shared-object libraries dynamically loaded at run-time. +.PP +The handlers currently distributed with \fBl2tpd\fR include: +.TP +.B sync-pppd.so +A shared-library which interfaces with \fBpppd\fR and allows \fBl2tpd\fR +to act as both an LAC and an LNS. +.TP +.B cmd.so +A simple command-interpreter which listens on a UNIX-domain socket +and allows you to start and stop L2TP sessions. +.PP +\fBl2tpd\fR is a single-threaded, event-driven program. It should be +fairly lightweight and efficient. + +.SH L2TPD CONFIGURATION +On startup, \fBl2tpd\fR reads the configuration file \fB/etc/l2tp/l2tp.conf\fR. +This configuration file sets operating parameters, describes peers and loads +and configures handlers. See \fBl2tp.conf\fR(5) for configuration +file details. + +.SH FILES +.TP +.B /etc/l2tp/l2tp.conf +Main configuration file. +.TP +.B /var/run/l2tpctrl +Control socket for \fBcmd.so\fR handler. + +.SH AUTHORS +\fBl2tpd\fR was written by David F. Skoll <dfs@roaringpenguin.com>. + diff --git a/release/src/router/rp-l2tp/md5.c b/release/src/router/rp-l2tp/md5.c new file mode 100644 index 00000000..1047e75f --- /dev/null +++ b/release/src/router/rp-l2tp/md5.c @@ -0,0 +1,248 @@ +/* + * 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. + * LIC: GPL + * + */ +#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/release/src/router/rp-l2tp/md5.h b/release/src/router/rp-l2tp/md5.h new file mode 100644 index 00000000..bb5a0417 --- /dev/null +++ b/release/src/router/rp-l2tp/md5.h @@ -0,0 +1,28 @@ +#ifndef MD5_H +#define MD5_H +/* LIC: GPL */ + +#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/release/src/router/rp-l2tp/network.c b/release/src/router/rp-l2tp/network.c new file mode 100644 index 00000000..2adc0fb0 --- /dev/null +++ b/release/src/router/rp-l2tp/network.c @@ -0,0 +1,136 @@ +/*********************************************************************** +* +* network.c +* +* Code for handling the UDP socket we send/receive on. All of our +* tunnels use a single UDP socket which stays open for the life of +* the application. +* +* Copyright (C) 2002 by Roaring Penguin Software Inc. +* +* This software may be distributed under the terms of the GNU General +* Public License, Version 2, or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id: network.c,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $"; + +#include "l2tp.h" +#include "event.h" +#include <sys/socket.h> +#include <netinet/in.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <signal.h> +#include <stdio.h> + +/* Our socket */ +int Sock = -1; + +static EventHandler *NetworkReadHandler = NULL; +static void network_readable(EventSelector *es, + int fd, + unsigned int flags, + void *data); +//char Hostname[MAX_HOSTNAME]; //2005-04-14 by kanki + +static void +sigint_handler(int sig) +{ + static int count = 0; + + count++; + fprintf(stderr, "In sigint handler: %d\n", count); + if (count < 5) { + l2tp_cleanup(); + } + exit(1); +} + +/********************************************************************** +* %FUNCTION: network_init +* %ARGUMENTS: +* es -- an event selector +* %RETURNS: +* >= 0 if all is OK, <0 if not +* %DESCRIPTION: +* Initializes network; opens socket on UDP port 1701; sets up +* event handler for incoming packets. +***********************************************************************/ +int +l2tp_network_init(EventSelector *es) +{ + struct sockaddr_in me; + int flags; + + //gethostname(Hostname, sizeof(Hostname)); //2005-04-14 by kanki + //Hostname[sizeof(Hostname)-1] = 0; + + Event_HandleSignal(es, SIGINT, sigint_handler); + if (Sock >= 0) { + if (NetworkReadHandler) { + Event_DelHandler(es, NetworkReadHandler); + NetworkReadHandler = NULL; + } + close(Sock); + Sock = -1; + } + Sock = socket(PF_INET, SOCK_DGRAM, 0); + if (Sock < 0) { + l2tp_set_errmsg("network_init: socket: %s", strerror(errno)); + return -1; + } + + me.sin_family = AF_INET; + me.sin_addr = Settings.listen_addr; + me.sin_port = htons((uint16_t) Settings.listen_port); + if (bind(Sock, (struct sockaddr *) &me, sizeof(me)) < 0) { + l2tp_set_errmsg("network_init: bind: %s", strerror(errno)); + close(Sock); + Sock = -1; + return -1; + } + + /* Set socket non-blocking */ + flags = fcntl(Sock, F_GETFL); + flags |= O_NONBLOCK; + fcntl(Sock, F_SETFL, flags); + + /* Set up the network read handler */ + Event_AddHandler(es, Sock, EVENT_FLAG_READABLE, + network_readable, NULL); + return Sock; +} + +/********************************************************************** +* %FUNCTION: network_readable +* %ARGUMENTS: +* es -- event selector +* fd -- socket +* flags -- event-handling flags telling what happened +* data -- not used +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Called when a packet arrives on the UDP socket. +***********************************************************************/ +static void +network_readable(EventSelector *es, + int fd, + unsigned int flags, + void *data) +{ + l2tp_dgram *dgram; + + struct sockaddr_in from; + dgram = l2tp_dgram_take_from_wire(&from); + if (!dgram) return; + + /* It's a control packet if we get here */ + l2tp_tunnel_handle_received_control_datagram(dgram, es, &from); + l2tp_dgram_free(dgram); +} diff --git a/release/src/router/rp-l2tp/options.c b/release/src/router/rp-l2tp/options.c new file mode 100644 index 00000000..44a77839 --- /dev/null +++ b/release/src/router/rp-l2tp/options.c @@ -0,0 +1,404 @@ +/*********************************************************************** +* +* options.c +* +* Code for parsing options out of configuration file. +* +* Copyright (C) 2002 by Roaring Penguin Software Inc. +* +* This software may be distributed under the terms of the GNU General +* Public License, Version 2, or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id: options.c,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $"; + +#include "l2tp.h" +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <netdb.h> +#include <stdio.h> +#include <ctype.h> + +l2tp_settings Settings; + +static option_handler *option_handlers = NULL; + +/* Function for currently-active option context */ +static int (*option_context_fn)(EventSelector *es, + char const *name, char const *value); +static int do_load_handler(EventSelector *es, + l2tp_opt_descriptor *desc, char const *value); +static int set_option(EventSelector *es, + l2tp_opt_descriptor *desc, char const *value); + +/* Global options */ +static l2tp_opt_descriptor global_opts[] = { + /* name type addr */ + { "load-handler", OPT_TYPE_CALLFUNC, (void *) do_load_handler }, + { "listen-port", OPT_TYPE_PORT, &Settings.listen_port }, + { "listen-addr", OPT_TYPE_IPADDR, &Settings.listen_addr }, + { NULL, OPT_TYPE_BOOL, NULL } +}; + +/********************************************************************** +* %FUNCTION: do_load_handler +* %ARGUMENTS: +* es -- event selector +* desc -- option descriptor +* value -- name of handler to load +* %RETURNS: +* 0 on success, -1 on failure +* %DESCRIPTION: +* Loads a DLL as a handler +***********************************************************************/ +static int +do_load_handler(EventSelector *es, + l2tp_opt_descriptor *desc, + char const *value) +{ + return l2tp_load_handler(es, value); +} + +/********************************************************************** +* %FUNCTION: set_option +* %ARGUMENTS: +* es -- event selector +* desc -- option descriptor +* value -- value string parsed from config file +* %RETURNS: +* -1 on error, 0 if all is OK +* %DESCRIPTION: +* Sets an option value. +***********************************************************************/ +static int +set_option(EventSelector *es, + l2tp_opt_descriptor *desc, + char const *value) +{ + long x; + char *end; + struct hostent *he; + int (*fn)(EventSelector *, l2tp_opt_descriptor *, char const *); + + switch(desc->type) { + case OPT_TYPE_BOOL: + if (!strcasecmp(value, "true") || + !strcasecmp(value, "yes") || + !strcasecmp(value, "on") || + !strcasecmp(value, "1")) { + * (int *) (desc->addr) = 1; + return 0; + } + if (!strcasecmp(value, "false") || + !strcasecmp(value, "no") || + !strcasecmp(value, "off") || + !strcasecmp(value, "0")) { + * (int *) (desc->addr) = 0; + return 0; + } + l2tp_set_errmsg("Expecting boolean value, found '%s'", value); + return -1; + + case OPT_TYPE_INT: + case OPT_TYPE_PORT: + x = strtol(value, &end, 0); + if (*end) { + l2tp_set_errmsg("Expecting integer value, found '%s'", value); + return -1; + } + if (desc->type == OPT_TYPE_PORT) { + if (x < 1 || x > 65535) { + l2tp_set_errmsg("Port values must range from 1 to 65535"); + return -1; + } + } + + * (int *) desc->addr = (int) x; + return 0; + + case OPT_TYPE_IPADDR: + he = gethostbyname(value); + if (!he) { + l2tp_set_errmsg("Could not resolve %s as IP address: %s", + value, strerror(errno)); + return -1; + } + + memcpy(desc->addr, he->h_addr, sizeof(he->h_addr)); + return 0; + + case OPT_TYPE_STRING: + if (* (char **) desc->addr) { + free(* (char **) desc->addr); + } + * (char **) desc->addr = strdup(value); + if (! * (char *) desc->addr) { + l2tp_set_errmsg("Out of memory"); + return -1; + } + return 0; + + case OPT_TYPE_CALLFUNC: + fn = (int (*)(EventSelector *, l2tp_opt_descriptor *, char const *)) desc->addr; + return fn(es, desc, value); + } + l2tp_set_errmsg("Unknown value type %d", desc->type); + return -1; +} + +/********************************************************************** +* %FUNCTION: chomp_word +* %ARGUMENTS: +* line -- the input line +* word -- buffer for storing word +* %RETURNS: +* Updated value of line +* %DESCRIPTION: +* Chomps a word from line +***********************************************************************/ +char const * +l2tp_chomp_word(char const *line, char *word) +{ + *word = 0; + + /* Chew up whitespace */ + while(*line && isspace(*line)) line++; + + if (*line != '"') { + /* Not quoted string */ + while (*line && !isspace(*line)) { + *word++ = *line++; + } + *word = 0; + return line; + } + + /* Quoted string */ + line++; + while(*line) { + if (*line != '\\') { + if (*line == '"') { + line++; + *word = 0; + return line; + } + *word++ = *line++; + continue; + } + line++; + if (*line) *word++ = *line++; + } + *word = 0; + return line; +} + +/********************************************************************** +* %FUNCTION: split_line_into_words +* %ARGUMENTS: +* line -- the input line +* name, value -- buffers which are large enough to contain all chars in line +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Splits line into two words. A word is: +* - Non-whitespace chars: foobarbazblech_3 +* - Quoted text: "Here is text \"with embedded quotes\"" +***********************************************************************/ +static void +split_line_into_words(char const *line, char *name, char *value) +{ + line = l2tp_chomp_word(line, name); + line = l2tp_chomp_word(line, value); +} + +/********************************************************************** +* %FUNCTION: parser_switch_context +* %ARGUMENTS: +* name, value -- words read from line. Either "global ..ignored.." +* or "section context" +* %RETURNS: +* 0 if context-switch proceeded OK, -1 if not. +* %DESCRIPTION: +* Switches configuration contexts +***********************************************************************/ +static int +parser_switch_context(EventSelector *es, + char const *name, + char const *value) +{ + int r; + option_handler *handler; + + /* Switch out of old context */ + if (option_context_fn) { + r = option_context_fn(es, "*end*", "*end*"); + option_context_fn = NULL; + if (r < 0) return r; + } + + if (!strcasecmp(name, "global")) { + return 0; + } + + /* Must be "section foo" */ + handler = option_handlers; + while(handler) { + if (!strcasecmp(value, handler->section)) { + option_context_fn = handler->process_option; + option_context_fn(es, "*begin*", "*begin*"); + return 0; + } + handler = handler->next; + } + l2tp_set_errmsg("No handler for section %s", value); + return -1; +} + +/********************************************************************** +* %FUNCTION: option_set +* %ARGUMENTS: +* es -- event selector +* name -- name of option +* value -- value of option +* descriptors -- array of option descriptors for this context +* %RETURNS: +* 0 on success, -1 on failure +* %DESCRIPTION: +* Sets an option +***********************************************************************/ +int +l2tp_option_set(EventSelector *es, + char const *name, + char const *value, + l2tp_opt_descriptor descriptors[]) +{ + int i; + + for (i=0; descriptors[i].name; i++) { + if (!strcasecmp(descriptors[i].name, name)) { + return set_option(es, &descriptors[i], value); + } + } + l2tp_set_errmsg("Option %s is not known in this context", + name); + return -1; +} + +/********************************************************************** +* %FUNCTION: option_register_section +* %ARGUMENTS: +* handler -- an option-handler +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Adds handler to linked-list of sections. +***********************************************************************/ +void +l2tp_option_register_section(option_handler *h) +{ + h->next = option_handlers; + option_handlers = h; +} + +/********************************************************************** +* %FUNCTION: handle_option +* %ARGUMENTS: +* es -- event selector +* name -- name of option +* value -- option's value +* %RETURNS: +* 0 on success, -1 on failure +* %DESCRIPTION: +* Handles an option +***********************************************************************/ +static int +handle_option(EventSelector *es, + char const *name, + char const *value) +{ + if (option_context_fn) { + return option_context_fn(es, name, value); + } + + return l2tp_option_set(es, name, value, global_opts); +} + +/********************************************************************** +* %FUNCTION: parse_config_file +* %ARGUMENTS: +* es -- event selector +* fname -- filename to parse +* %RETURNS: +* -1 on error, 0 if all is OK +* %DESCRIPTION: +* Parses configuration file. +***********************************************************************/ +int +l2tp_parse_config_file(EventSelector *es, + char const *fname) +{ + char buf[512]; + char name[512]; + char value[512]; + int r = 0; + size_t l; + char *line; + FILE *fp; + + /* Defaults */ + Settings.listen_port = 1701; + Settings.listen_addr.s_addr = htonl(INADDR_ANY); + + fp = fopen(fname, "r"); + if (!fp) { + l2tp_set_errmsg("Could not open '%s' for reading: %s", + fname, strerror(errno)); + return -1; + } + + /* Start in global context */ + option_context_fn = NULL; + while (fgets(buf, sizeof(buf), fp) != NULL) { + l = strlen(buf); + if (l && (buf[l] == '\n')) { + buf[l--] = 0; + } + + /* Skip leading whitespace */ + line = buf; + while(*line && isspace(*line)) line++; + + /* Ignore blank lines and comments */ + if (!*line || *line == '#') { + continue; + } + + /* Split line into two words */ + split_line_into_words(line, name, value); + + /* Check for context switch */ + if (!strcasecmp(name, "global") || + !strcasecmp(name, "section")) { + r = parser_switch_context(es, name, value); + if (r < 0) break; + continue; + } + + r = handle_option(es, name, value); + if (r < 0) break; + } + fclose(fp); + if (r >= 0) { + if (option_context_fn) { + r = option_context_fn(es, "*end*", "*end*"); + option_context_fn = NULL; + } + } + + return r; +} diff --git a/release/src/router/rp-l2tp/peer.c b/release/src/router/rp-l2tp/peer.c new file mode 100644 index 00000000..ecbce5cf --- /dev/null +++ b/release/src/router/rp-l2tp/peer.c @@ -0,0 +1,422 @@ +/*********************************************************************** +* +* peer.c +* +* Manage lists of peers for L2TP +* +* Copyright (C) 2002 by Roaring Penguin Software Inc. +* +* This software may be distributed under the terms of the GNU General +* Public License, Version 2, or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id: peer.c,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $"; + +#include "l2tp.h" +#include <stddef.h> +#include <string.h> + +static hash_table all_peers; +static int peer_process_option(EventSelector *es, + char const *name, + char const *value); + +static l2tp_peer prototype; + +static option_handler peer_option_handler = { + NULL, "peer", peer_process_option +}; + +static int port; + +static int handle_secret_option(EventSelector *es, l2tp_opt_descriptor *desc, char const *value); +static int handle_hostname_option(EventSelector *es, l2tp_opt_descriptor *desc, char const *value); +static int handle_peername_option(EventSelector *es, l2tp_opt_descriptor *desc, char const *value); +static int set_lac_handler(EventSelector *es, l2tp_opt_descriptor *desc, char const *value); +static int handle_lac_option(EventSelector *es, l2tp_opt_descriptor *desc, char const *value); +static int set_lns_handler(EventSelector *es, l2tp_opt_descriptor *desc, char const *value); +static int handle_lns_option(EventSelector *es, l2tp_opt_descriptor *desc, char const *value); + +/* Peer options */ +static l2tp_opt_descriptor peer_opts[] = { + /* name type addr */ + { "peer", OPT_TYPE_IPADDR, &prototype.addr.sin_addr.s_addr}, + { "mask", OPT_TYPE_INT, &prototype.mask_bits}, + { "secret", OPT_TYPE_CALLFUNC, (void *) handle_secret_option}, + { "hostname", OPT_TYPE_CALLFUNC, (void *) handle_hostname_option}, + { "peername", OPT_TYPE_CALLFUNC, (void *) handle_peername_option}, + { "port", OPT_TYPE_PORT, &port }, + { "lac-handler", OPT_TYPE_CALLFUNC, (void *) set_lac_handler}, + { "lac-opts", OPT_TYPE_CALLFUNC, (void *) handle_lac_option}, + { "lns-handler", OPT_TYPE_CALLFUNC, (void *) set_lns_handler}, + { "lns-opts", OPT_TYPE_CALLFUNC, (void *) handle_lns_option}, + { "hide-avps", OPT_TYPE_BOOL, &prototype.hide_avps}, + { "retain-tunnel", OPT_TYPE_BOOL, &prototype.retain_tunnel}, + { "persist", OPT_TYPE_BOOL, &prototype.persist}, + { "holdoff", OPT_TYPE_INT, &prototype.holdoff}, + { "maxfail", OPT_TYPE_INT, &prototype.maxfail}, + { "strict-ip-check", OPT_TYPE_BOOL, &prototype.validate_peer_ip}, + { NULL, OPT_TYPE_BOOL, NULL } +}; + +static int +set_lac_handler(EventSelector *es, + l2tp_opt_descriptor *desc, + char const *value) +{ + l2tp_lac_handler *handler = l2tp_session_find_lac_handler(value); + if (!handler) { + l2tp_set_errmsg("No LAC handler named '%s'", value); + return -1; + } + prototype.lac_ops = handler->call_ops; + return 0; +} + +static int +set_lns_handler(EventSelector *es, + l2tp_opt_descriptor *desc, + char const *value) +{ + l2tp_lns_handler *handler = l2tp_session_find_lns_handler(value); + if (!handler) { + l2tp_set_errmsg("No LNS handler named '%s'", value); + return -1; + } + prototype.lns_ops = handler->call_ops; + return 0; +} + +/********************************************************************** +* %FUNCTION: handle_secret_option +* %ARGUMENTS: +* es -- event selector +* desc -- descriptor +* value -- the secret +* %RETURNS: +* 0 +* %DESCRIPTION: +* Copies secret to prototype +***********************************************************************/ +static int +handle_secret_option(EventSelector *es, + l2tp_opt_descriptor *desc, + char const *value) +{ + strncpy(prototype.secret, value, MAX_SECRET_LEN); + prototype.secret[MAX_SECRET_LEN-1] = 0; + prototype.secret_len = strlen(prototype.secret); + return 0; +} + +/********************************************************************** +* %FUNCTION: handle_hostname_option +* %ARGUMENTS: +* es -- event selector +* desc -- descriptor +* value -- the hostname +* %RETURNS: +* 0 +* %DESCRIPTION: +* Copies hostname to prototype +***********************************************************************/ +static int +handle_hostname_option(EventSelector *es, + l2tp_opt_descriptor *desc, + char const *value) +{ + strncpy(prototype.hostname, value, MAX_HOSTNAME); + prototype.hostname[MAX_HOSTNAME-1] = 0; + prototype.hostname_len = strlen(prototype.hostname); + return 0; +} + +/********************************************************************** +* %FUNCTION: handle_peername_option +* %ARGUMENTS: +* es -- event selector +* desc -- descriptor +* value -- the hostname +* %RETURNS: +* 0 +* %DESCRIPTION: +* Copies peername to prototype +***********************************************************************/ +static int +handle_peername_option(EventSelector *es, + l2tp_opt_descriptor *desc, + char const *value) +{ + strncpy(prototype.peername, value, MAX_HOSTNAME); + prototype.peername[MAX_HOSTNAME-1] = 0; + prototype.peername_len = strlen(prototype.peername); + return 0; +} + +/********************************************************************** +* %FUNCTION: handle_lac_option +* %ARGUMENTS: +* es -- event selector +* desc -- descriptor +* value -- the hostname +* %RETURNS: +* 0 +* %DESCRIPTION: +* Copies LAC options to prototype +***********************************************************************/ +static int +handle_lac_option(EventSelector *es, + l2tp_opt_descriptor *desc, + char const *value) +{ + char word[512]; + while (value && *value) { + value = l2tp_chomp_word(value, word); + if (!word[0]) break; + if (prototype.num_lac_options < MAX_OPTS) { + char *x = strdup(word); + if (x) prototype.lac_options[prototype.num_lac_options++] = x; + prototype.lac_options[prototype.num_lac_options] = NULL; + } else { + break; + } + } + return 0; +} + +/********************************************************************** +* %FUNCTION: handle_lns_option +* %ARGUMENTS: +* es -- event selector +* desc -- descriptor +* value -- the hostname +* %RETURNS: +* 0 +* %DESCRIPTION: +* Copies LNS options to prototype +***********************************************************************/ +static int +handle_lns_option(EventSelector *es, + l2tp_opt_descriptor *desc, + char const *value) +{ + char word[512]; + while (value && *value) { + value = l2tp_chomp_word(value, word); + if (!word[0]) break; + if (prototype.num_lns_options < MAX_OPTS) { + char *x = strdup(word); + if (x) prototype.lns_options[prototype.num_lns_options++] = x; + prototype.lns_options[prototype.num_lns_options] = NULL; + } else { + break; + } + } + return 0; +} + +/********************************************************************** +* %FUNCTION: peer_process_option +* %ARGUMENTS: +* es -- event selector +* name, value -- name and value of option +* %RETURNS: +* 0 on success, -1 on failure +* %DESCRIPTION: +* Processes an option in the "peer" section +***********************************************************************/ +static int +peer_process_option(EventSelector *es, + char const *name, + char const *value) +{ + l2tp_peer *peer; + + /* Special cases: begin and end */ + if (!strcmp(name, "*begin*")) { + /* Switching in to peer context */ + memset(&prototype, 0, sizeof(prototype)); + prototype.mask_bits = 32; + prototype.validate_peer_ip = 1; + port = 1701; + return 0; + } + + if (!strcmp(name, "*end*")) { + /* Validate settings */ + uint16_t u16 = (uint16_t) port; + prototype.addr.sin_port = htons(u16); + prototype.addr.sin_family = AF_INET; + + /* Allow non-authenticated tunnels + if (!prototype.secret_len) { + l2tp_set_errmsg("No secret supplied for peer"); + return -1; + } + */ + if (!prototype.lns_ops && !prototype.lac_ops) { + l2tp_set_errmsg("You must enable at least one of lns-handler or lac-handler"); + return -1; + } + + /* Add the peer */ + peer = l2tp_peer_insert(&prototype.addr); + if (!peer) return -1; + + peer->mask_bits = prototype.mask_bits; + memcpy(&peer->hostname,&prototype.hostname, sizeof(prototype.hostname)); + peer->hostname_len = prototype.hostname_len; + memcpy(&peer->peername,&prototype.peername, sizeof(prototype.peername)); + peer->peername_len = prototype.peername_len; + memcpy(&peer->secret, &prototype.secret, MAX_SECRET_LEN); + peer->secret_len = prototype.secret_len; + peer->lns_ops = prototype.lns_ops; + memcpy(&peer->lns_options,&prototype.lns_options,sizeof(prototype.lns_options)); + peer->lac_ops = prototype.lac_ops; + memcpy(&peer->lac_options,&prototype.lac_options,sizeof(prototype.lac_options)); + peer->hide_avps = prototype.hide_avps; + peer->retain_tunnel = prototype.retain_tunnel; + peer->persist = prototype.persist; + peer->holdoff = prototype.holdoff; + peer->maxfail = prototype.maxfail; + peer->fail = 0; + peer->validate_peer_ip = prototype.validate_peer_ip; + return 0; + } + + /* Process option */ + return l2tp_option_set(es, name, value, peer_opts); +} + +/********************************************************************** +* %FUNCTION: peer_compute_hash +* %ARGUMENTS: +* data -- a void pointer which is really a peer +* %RETURNS: +* Inet address +***********************************************************************/ +static unsigned int +peer_compute_hash(void *data) +{ + unsigned int hash = (unsigned int) (((l2tp_peer *) data)->addr.sin_addr.s_addr); + return hash; +} + +/********************************************************************** +* %FUNCTION: peer_compare +* %ARGUMENTS: +* item1 -- first peer +* item2 -- second peer +* %RETURNS: +* 0 if both peers have same ID, non-zero otherwise +***********************************************************************/ +static int +peer_compare(void *item1, void *item2) +{ + return ((l2tp_peer *) item1)->addr.sin_addr.s_addr != + ((l2tp_peer *) item2)->addr.sin_addr.s_addr; +} + +/********************************************************************** +* %FUNCTION: peer_init +* %ARGUMENTS: +* None +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Initializes peer hash table +***********************************************************************/ +void +l2tp_peer_init(void) +{ + hash_init(&all_peers, + offsetof(l2tp_peer, hash), + peer_compute_hash, + peer_compare); + l2tp_option_register_section(&peer_option_handler); +} + +/********************************************************************** +* %FUNCTION: peer_find +* %ARGUMENTS: +* addr -- IP address of peer +* hostname -- AVP peer hostname +* %RETURNS: +* A pointer to the peer with given IP address, or NULL if not found. +* %DESCRIPTION: +* Searches peer hash table for specified peer. +***********************************************************************/ +l2tp_peer * +l2tp_peer_find(struct sockaddr_in *addr, char const *peername) +{ + void *cursor; + l2tp_peer *peer = NULL; + l2tp_peer *candidate = NULL; + char addr1_str[16], addr2_str[16]; + + for (candidate = hash_start(&all_peers, &cursor); + candidate ; + candidate = hash_next(&all_peers, &cursor)) { + + unsigned long mask = candidate->mask_bits ? + htonl(0xFFFFFFFFUL << (32 - candidate->mask_bits)) : 0; + + strcpy(addr1_str, inet_ntoa(addr->sin_addr)); + strcpy(addr2_str, inet_ntoa(candidate->addr.sin_addr)); + DBG(l2tp_db(DBG_TUNNEL, "l2tp_peer_find(%s) examining peer %s/%d\n", + addr1_str, addr2_str, + candidate->mask_bits)); + + if ((candidate->addr.sin_addr.s_addr & mask) == + (addr->sin_addr.s_addr & mask) + && (!peername || + !(candidate->peername[0]) || + !strcmp(peername,candidate->peername))) { + + if (peer == NULL) { + peer = candidate; + } else { + if (peer->mask_bits < candidate->mask_bits) + peer = candidate; + } + } + } + + strcpy(addr1_str, inet_ntoa(addr->sin_addr)); + if (peer != NULL) + strcpy(addr2_str, inet_ntoa(peer->addr.sin_addr)); + DBG(l2tp_db(DBG_TUNNEL, "l2tp_peer_find(%s) found %s/%d\n", + addr1_str, + peer == NULL ? "NULL" : addr2_str, + peer == NULL ? -1 : peer->mask_bits)); + + return peer; +} + +/********************************************************************** +* %FUNCTION: peer_insert +* %ARGUMENTS: +* addr -- IP address of peer +* %RETURNS: +* NULL if insert failed, pointer to new peer structure otherwise +* %DESCRIPTION: +* Inserts a new peer in the all_peers table +***********************************************************************/ +l2tp_peer * +l2tp_peer_insert(struct sockaddr_in *addr) +{ + l2tp_peer *peer = malloc(sizeof(l2tp_peer)); + if (!peer) { + l2tp_set_errmsg("peer_insert: Out of memory"); + return NULL; + } + memset(peer, 0, sizeof(*peer)); + + peer->addr = *addr; + hash_insert(&all_peers, peer); + return peer; +} diff --git a/release/src/router/rp-l2tp/session.c b/release/src/router/rp-l2tp/session.c new file mode 100644 index 00000000..3b613c72 --- /dev/null +++ b/release/src/router/rp-l2tp/session.c @@ -0,0 +1,855 @@ +/*********************************************************************** +* +* session.c +* +* Code for managing L2TP sessions +* +* Copyright (C) 2001 by Roaring Penguin Software Inc. +* +* This software may be distributed under the terms of the GNU General +* Public License, Version 2, or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id: session.c,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $"; + +#include "l2tp.h" +#include <stddef.h> +#include <string.h> +#include <stdarg.h> +#include <stdio.h> + +static uint16_t session_make_sid(l2tp_tunnel *tunnel); +static void session_set_state(l2tp_session *session, int state); +static void session_send_sli(l2tp_session *session); + +/* Registered LNS incoming-call handlers */ +static l2tp_lns_handler *lns_handlers = NULL; + +/* Registered LAC handlers */ +static l2tp_lac_handler *lac_handlers = NULL; + +static uint32_t call_serial_number = 0; + +static char *state_names[] = { + "idle", "wait-tunnel", "wait-reply", "wait-connect", "established" +}; + +/********************************************************************** +* %FUNCTION: session_compute_hash +* %ARGUMENTS: +* data -- a session +* %RETURNS: +* The session ID +* %DESCRIPTION: +* Returns a hash value for a session +***********************************************************************/ +static unsigned int +session_compute_hash(void *data) +{ + return (unsigned int) ((l2tp_session *) data)->my_id; +} + + +/********************************************************************** +* %FUNCTION: session_compare +* %ARGUMENTS: +* d1, d2 -- two sessions +* %RETURNS: +* 0 if sids are equal, non-zero otherwise +* %DESCRIPTION: +* Compares two sessions +***********************************************************************/ +static int +session_compare(void *d1, void *d2) +{ + return ((l2tp_session *) d1)->my_id != ((l2tp_session *) d2)->my_id; +} + +/********************************************************************** +* %FUNCTION: session_hash_init +* %ARGUMENTS: +* tab -- hash table +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Initializes hash table of sessions +***********************************************************************/ +void +l2tp_session_hash_init(hash_table *tab) +{ + hash_init(tab, offsetof(l2tp_session, hash_by_my_id), + session_compute_hash, session_compare); +} + +/********************************************************************** +* %FUNCTION: session_free +* %ARGUMENTS: +* ses -- a session to free +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Frees a session, closing down all resources associated with it. +***********************************************************************/ +void +l2tp_session_free(l2tp_session *ses, char const *reason, int may_reestablish) +{ + session_set_state(ses, SESSION_IDLE); + DBG(l2tp_db(DBG_SESSION, "session_free(%s) %s\n", + l2tp_debug_session_to_str(ses), reason)); + if (ses->call_ops && ses->call_ops->close) { + ses->call_ops->close(ses, reason, may_reestablish); + } + memset(ses, 0, sizeof(l2tp_session)); + free(ses); +} + +/********************************************************************** +* %FUNCTION: session_call_lns +* %ARGUMENTS: +* peer -- L2TP peer +* calling_number -- calling phone number (or MAC address or whatever...) +* private -- private data to be stored in session structure +* %RETURNS: +* A newly-allocated session, or NULL if session could not be created +* %DESCRIPTION: +* Initiates session setup. Once call is active, established() will be +* called. +***********************************************************************/ +l2tp_session * +l2tp_session_call_lns(l2tp_peer *peer, + char const *calling_number, + EventSelector *es, + void *private) +{ + l2tp_session *ses; + l2tp_tunnel *tunnel; + + /* Find a tunnel to the peer */ + tunnel = l2tp_tunnel_find_for_peer(peer, es); + if (!tunnel) return NULL; + + /* Do we have call ops? */ + if (!peer->lac_ops) { + l2tp_set_errmsg("Cannot act as LAC for peer"); + return NULL; + } + + /* Allocate session */ + ses = malloc(sizeof(l2tp_session)); + if (!ses) { + l2tp_set_errmsg("session_call_lns: out of memory"); + return NULL; + } + + /* Init fields */ + memset(ses, 0, sizeof(l2tp_session)); + ses->we_are_lac = 1; + ses->tunnel = tunnel; + ses->my_id = session_make_sid(tunnel); + ses->call_ops = peer->lac_ops; + ses->state = SESSION_WAIT_TUNNEL; + strncpy(ses->calling_number, calling_number, MAX_HOSTNAME); + ses->calling_number[MAX_HOSTNAME-1] = 0; + ses->private = private; + ses->snooping = 1; + ses->send_accm = 0xFFFFFFFF; + ses->recv_accm = 0xFFFFFFFF; + + /* Add it to the tunnel */ + l2tp_tunnel_add_session(ses); + + return ses; +} + +/********************************************************************** +* %FUNCTION: session_make_sid +* %ARGUMENTS: +* tunnel -- an L2TP tunnel +* %RETURNS: +* An unused random session ID +***********************************************************************/ +static uint16_t +session_make_sid(l2tp_tunnel *tunnel) +{ + uint16_t sid; + while(1) { + L2TP_RANDOM_FILL(sid); + if (!sid) continue; + if (!l2tp_tunnel_find_session(tunnel, sid)) return sid; + } +} + +/********************************************************************** +* %FUNCTION: session_notify_open +* %ARGUMENTS: +* ses -- an L2TP session +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Called when tunnel has been established +***********************************************************************/ +void +l2tp_session_notify_tunnel_open(l2tp_session *ses) +{ + uint16_t u16; + uint32_t u32; + l2tp_dgram *dgram; + l2tp_tunnel *tunnel = ses->tunnel; + + if (ses->state != SESSION_WAIT_TUNNEL) return; + + /* Send ICRQ */ + DBG(l2tp_db(DBG_SESSION, "Session %s tunnel open\n", + l2tp_debug_session_to_str(ses))); + + dgram = l2tp_dgram_new_control(MESSAGE_ICRQ, ses->tunnel->assigned_id, + 0); + if (!dgram) { + l2tp_set_errmsg("Could not establish session - out of memory"); + l2tp_tunnel_delete_session(ses, "Out of memory", 0); + return; + } + + /* assigned session ID */ + u16 = htons(ses->my_id); + l2tp_dgram_add_avp(dgram, tunnel, MANDATORY, + sizeof(u16), VENDOR_IETF, AVP_ASSIGNED_SESSION_ID, &u16); + + /* Call serial number */ + u32 = htonl(call_serial_number); + call_serial_number++; + l2tp_dgram_add_avp(dgram, tunnel, MANDATORY, + sizeof(u32), VENDOR_IETF, AVP_CALL_SERIAL_NUMBER, &u32); + + /* Ship it out */ + l2tp_tunnel_xmit_control_message(tunnel, dgram); + + session_set_state(ses, SESSION_WAIT_REPLY); +} + +/********************************************************************** +* %FUNCTION: session_set_state +* %ARGUMENTS: +* session -- the session +* state -- new state +* %RETURNS: +* Nothing +***********************************************************************/ +static void +session_set_state(l2tp_session *session, int state) +{ + if (state == session->state) return; + DBG(l2tp_db(DBG_SESSION, "session(%s) state %s -> %s\n", + l2tp_debug_session_to_str(session), + state_names[session->state], + state_names[state])); + session->state = state; +} + +/********************************************************************** +* %FUNCTION: session_register_lns_handler +* %ARGUMENTS: +* handler -- LNS handler +* %RETURNS: +* -1 on error, 0 if all is OK +* %DESCRIPTION: +* Registers an LNS handler for incoming call requests +***********************************************************************/ +int +l2tp_session_register_lns_handler(l2tp_lns_handler *handler) +{ + l2tp_lns_handler *prev = lns_handlers; + + if (l2tp_session_find_lns_handler(handler->handler_name)) { + l2tp_set_errmsg("LNS Handler named %s already exists", + handler->handler_name); + return -1; + } + /* Add to end of handler list */ + handler->next = NULL; + if (!prev) { + lns_handlers = handler; + return 0; + } + while (prev->next) { + prev = prev->next; + } + prev->next = handler; + return 0; +} + +/********************************************************************** +* %FUNCTION: session_register_lac_handler +* %ARGUMENTS: +* handler -- LAC handler +* %RETURNS: +* -1 on error, 0 if all is OK +* %DESCRIPTION: +* Registers an LAC handler for incoming call requests +***********************************************************************/ +int +l2tp_session_register_lac_handler(l2tp_lac_handler *handler) +{ + l2tp_lac_handler *prev = lac_handlers; + + if (l2tp_session_find_lac_handler(handler->handler_name)) { + l2tp_set_errmsg("LAC Handler named %s already exists", + handler->handler_name); + return -1; + } + /* Add to end of handler list */ + handler->next = NULL; + if (!prev) { + lac_handlers = handler; + return 0; + } + while (prev->next) { + prev = prev->next; + } + prev->next = handler; + return 0; +} + +/********************************************************************** +* %FUNCTION: session_send_CDN +* %ARGUMENTS: +* ses -- which session to terminate +* result_code -- result code +* error_code -- error code +* fmt -- printf-style format string for error message +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Sends CDN with specified result code and message. +***********************************************************************/ +void +l2tp_session_send_CDN(l2tp_session *ses, + int result_code, + int error_code, + char const *fmt, ...) +{ + char buf[256]; + va_list ap; + l2tp_tunnel *tunnel = ses->tunnel; + uint16_t len; + l2tp_dgram *dgram; + uint16_t u16; + + /* Build the buffer for the result-code AVP */ + buf[0] = result_code / 256; + buf[1] = result_code & 255; + buf[2] = error_code / 256; + buf[3] = error_code & 255; + + va_start(ap, fmt); + vsnprintf(buf+4, 256-4, fmt, ap); + buf[255] = 0; + va_end(ap); + + DBG(l2tp_db(DBG_SESSION, "session_send_CDN(%s): %s\n", + l2tp_debug_session_to_str(ses), buf+4)); + + len = 4 + strlen(buf+4); + /* Build the datagram */ + dgram = l2tp_dgram_new_control(MESSAGE_CDN, tunnel->assigned_id, + ses->assigned_id); + if (!dgram) return; + + /* Add assigned session ID */ + u16 = htons(ses->my_id); + l2tp_dgram_add_avp(dgram, tunnel, MANDATORY, + sizeof(u16), VENDOR_IETF, AVP_ASSIGNED_SESSION_ID, &u16); + + /* Add result code */ + l2tp_dgram_add_avp(dgram, tunnel, MANDATORY, + len, VENDOR_IETF, AVP_RESULT_CODE, buf); + + /* TODO: Clean up */ + session_set_state(ses, SESSION_IDLE); + + /* Ship it out */ + l2tp_tunnel_xmit_control_message(tunnel, dgram); + + /* Free session */ + l2tp_tunnel_delete_session(ses, buf+4, 0); +} + +/********************************************************************** +* %FUNCTION: session_lns_handle_incoming_call +* %ARGUMENTS: +* tunnel -- tunnel on which ICRQ arrived +* sid -- assigned session ID +* dgram -- the ICRQ datagram +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Handles ICRQ. If we find an LNS handler willing to take the call, +* send ICRP. Otherwise, send CDN. +***********************************************************************/ +void +l2tp_session_lns_handle_incoming_call(l2tp_tunnel *tunnel, + uint16_t sid, + l2tp_dgram *dgram, + char const *calling_number) +{ + l2tp_call_ops *ops = tunnel->peer->lns_ops; + l2tp_session *ses; + uint16_t u16; + + /* Allocate a session */ + ses = malloc(sizeof(l2tp_session)); + if (!ses) { + l2tp_set_errmsg("session_lns_handle_incoming_call: out of memory"); + return; + } + /* Init fields */ + memset(ses, 0, sizeof(l2tp_session)); + ses->we_are_lac = 0; + ses->tunnel = tunnel; + ses->my_id = session_make_sid(tunnel); + ses->assigned_id = sid; + ses->state = SESSION_IDLE; + strncpy(ses->calling_number, calling_number, MAX_HOSTNAME); + ses->calling_number[MAX_HOSTNAME-1] = 0; + ses->private = NULL; + ses->snooping = 1; + ses->send_accm = 0xFFFFFFFF; + ses->recv_accm = 0xFFFFFFFF; + + l2tp_tunnel_add_session(ses); + + if (!ops || !ops->establish) { + l2tp_session_send_CDN(ses, RESULT_GENERAL_ERROR, + ERROR_OUT_OF_RESOURCES, + "No LNS handler willing to accept call"); + return; + } + + ses->call_ops = ops; + + /* Send ICRP */ + dgram = l2tp_dgram_new_control(MESSAGE_ICRP, ses->tunnel->assigned_id, + ses->assigned_id); + if (!dgram) { + /* Ugh... not much chance of this working... */ + l2tp_session_send_CDN(ses, RESULT_GENERAL_ERROR, ERROR_OUT_OF_RESOURCES, + "Out of memory"); + return; + } + + /* Add assigned session ID */ + u16 = htons(ses->my_id); + l2tp_dgram_add_avp(dgram, tunnel, MANDATORY, + sizeof(u16), VENDOR_IETF, AVP_ASSIGNED_SESSION_ID, &u16); + + /* Set session state */ + session_set_state(ses, SESSION_WAIT_CONNECT); + + /* Ship ICRP */ + l2tp_tunnel_xmit_control_message(tunnel, dgram); +} + +/********************************************************************** +* %FUNCTION: session_handle_CDN +* %ARGUMENTS: +* ses -- the session +* dgram -- the datagram +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Handles a CDN by destroying session +***********************************************************************/ +void +l2tp_session_handle_CDN(l2tp_session *ses, + l2tp_dgram *dgram) +{ + char buf[1024]; + unsigned char *val; + uint16_t len; + val = l2tp_dgram_search_avp(dgram, ses->tunnel, NULL, NULL, &len, + VENDOR_IETF, AVP_RESULT_CODE); + if (!val || len < 4) { + l2tp_tunnel_delete_session(ses, "Received CDN", 0); + } else { + uint16_t result_code, error_code; + char *msg; + result_code = ((uint16_t) val[0]) * 256 + (uint16_t) val[1]; + error_code = ((uint16_t) val[2]) * 256 + (uint16_t) val[3]; + if (len > 4) { + msg = (char *) &val[4]; + } else { + msg = ""; + } + snprintf(buf, sizeof(buf), "Received CDN: result-code = %d, error-code = %d, message = '%.*s'", result_code, error_code, (int) len-4, msg); + buf[1023] = 0; + l2tp_tunnel_delete_session(ses, buf, 0); + } +} + +/********************************************************************** +* %FUNCTION: session_handle_ICRP +* %ARGUMENTS: +* ses -- the session +* dgram -- the datagram +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Handles an ICRP +***********************************************************************/ +void +l2tp_session_handle_ICRP(l2tp_session *ses, + l2tp_dgram *dgram) +{ + uint16_t u16; + unsigned char *val; + uint16_t len; + uint32_t u32; + + int mandatory, hidden; + l2tp_tunnel *tunnel = ses->tunnel; + + /* Get assigned session ID */ + val = l2tp_dgram_search_avp(dgram, tunnel, &mandatory, &hidden, &len, + VENDOR_IETF, AVP_ASSIGNED_SESSION_ID); + if (!val) { + l2tp_set_errmsg("No assigned session-ID in ICRP"); + return; + } + if (!l2tp_dgram_validate_avp(VENDOR_IETF, AVP_ASSIGNED_SESSION_ID, + len, mandatory)) { + l2tp_set_errmsg("Invalid assigned session-ID in ICRP"); + return; + } + + /* Set assigned session ID */ + u16 = ((uint16_t) val[0]) * 256 + (uint16_t) val[1]; + + if (!u16) { + l2tp_set_errmsg("Invalid assigned session-ID in ICRP"); + return; + } + + ses->assigned_id = u16; + + /* If state is not WAIT_REPLY, fubar */ + if (ses->state != SESSION_WAIT_REPLY) { + l2tp_session_send_CDN(ses, RESULT_FSM_ERROR, 0, "Received ICRP for session in state %s", state_names[ses->state]); + return; + } + + /* Tell PPP code that call has been established */ + if (ses->call_ops && ses->call_ops->establish) { + if (ses->call_ops->establish(ses) < 0) { + l2tp_session_send_CDN(ses, RESULT_GENERAL_ERROR, ERROR_VENDOR_SPECIFIC, + "%s", l2tp_get_errmsg()); + return; + } + } + + /* Send ICCN */ + dgram = l2tp_dgram_new_control(MESSAGE_ICCN, tunnel->assigned_id, + ses->assigned_id); + if (!dgram) { + /* Ugh... not much chance of this working... */ + l2tp_session_send_CDN(ses, RESULT_GENERAL_ERROR, ERROR_OUT_OF_RESOURCES, + "Out of memory"); + return; + } + + /* TODO: Speed, etc. are faked for now. */ + + /* Connect speed */ + u32 = htonl(57600); + l2tp_dgram_add_avp(dgram, tunnel, MANDATORY, + sizeof(u32), VENDOR_IETF, AVP_TX_CONNECT_SPEED, &u32); + + /* Framing Type */ + u32 = htonl(1); + l2tp_dgram_add_avp(dgram, tunnel, MANDATORY, + sizeof(u32), VENDOR_IETF, AVP_FRAMING_TYPE, &u32); + + /* Ship it out */ + l2tp_tunnel_xmit_control_message(tunnel, dgram); + + /* Set session state */ + session_set_state(ses, SESSION_ESTABLISHED); + ses->tunnel->peer->fail = 0; + +} + +/********************************************************************** +* %FUNCTION: session_handle_ICCN +* %ARGUMENTS: +* ses -- the session +* dgram -- the datagram +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Handles an ICCN +***********************************************************************/ +void +l2tp_session_handle_ICCN(l2tp_session *ses, + l2tp_dgram *dgram) +{ + unsigned char *val; + int mandatory, hidden; + uint16_t len, vendor, type; + int err = 0; + + l2tp_tunnel *tunnel = ses->tunnel; + + /* If state is not WAIT_CONNECT, fubar */ + if (ses->state != SESSION_WAIT_CONNECT) { + l2tp_session_send_CDN(ses, RESULT_FSM_ERROR, 0, + "Received ICCN for session in state %s", + state_names[ses->state]); + return; + } + + /* Set session state */ + session_set_state(ses, SESSION_ESTABLISHED); + ses->tunnel->peer->fail = 0; + + /* Pull out and examine AVP's */ + while(1) { + val = l2tp_dgram_pull_avp(dgram, tunnel, &mandatory, &hidden, + &len, &vendor, &type, &err); + if (!val) { + if (err) { + l2tp_session_send_CDN(ses, RESULT_GENERAL_ERROR, + ERROR_BAD_VALUE, "%s", l2tp_get_errmsg()); + return; + } + break; + } + if (vendor != VENDOR_IETF) { + if (!mandatory) continue; + l2tp_session_send_CDN(ses, RESULT_GENERAL_ERROR, + ERROR_UNKNOWN_AVP_WITH_M_BIT, + "Unknown mandatory attribute (vendor=%d, type=%d) in %s", + (int) vendor, (int) type, + l2tp_debug_avp_type_to_str(dgram->msg_type)); + return; + } + switch(type) { + case AVP_SEQUENCING_REQUIRED: + ses->sequencing_required = 1; + break; + } + + } + + /* Start the call */ + if (ses->call_ops->establish(ses) < 0) { + l2tp_session_send_CDN(ses, RESULT_GENERAL_ERROR, + ERROR_OUT_OF_RESOURCES, + "No LNS handler willing to accept call"); + return; + } + +} + +/********************************************************************** +* %FUNCTION: session_find_lns_handler +* %ARGUMENTS: +* name -- name of handler +* %RETURNS: +* Pointer to the handler if found, NULL if not +* %DESCRIPTION: +* Searches for an LNS handler by name +***********************************************************************/ +l2tp_lns_handler * +l2tp_session_find_lns_handler(char const *name) +{ + l2tp_lns_handler *cur = lns_handlers; + while(cur) { + if (!strcmp(name, cur->handler_name)) return cur; + cur = cur->next; + } + return NULL; +} + +/********************************************************************** +* %FUNCTION: session_find_lac_handler +* %ARGUMENTS: +* name -- name of handler +* %RETURNS: +* Pointer to the handler if found, NULL if not +* %DESCRIPTION: +* Searches for an LAC handler by name +***********************************************************************/ +l2tp_lac_handler * +l2tp_session_find_lac_handler(char const *name) +{ + l2tp_lac_handler *cur = lac_handlers; + while(cur) { + if (!strcmp(name, cur->handler_name)) return cur; + cur = cur->next; + } + return NULL; +} + +/********************************************************************** +* %FUNCTION: l2tp_session_state_name +* %ARGUMENTS: +* ses -- the session +* %RETURNS: +* The name of the session's state +***********************************************************************/ +char const * +l2tp_session_state_name(l2tp_session *ses) +{ + return state_names[ses->state]; +} + +/********************************************************************** +* %FUNCTION: l2tp_session_lcp_snoop +* %ARGUMENTS: +* ses -- L2TP session structure +* buf -- PPP frame +* len -- length of PPP frame +* incoming -- if 1, frame is coming from L2TP tunnel. If 0, frame is +* going to L2TP tunnel. +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Snoops on LCP negotiation. Used to send SLI to LAC if we're an LNS. +***********************************************************************/ +void +l2tp_session_lcp_snoop(l2tp_session *ses, + unsigned char const *buf, + int len, + int incoming) +{ + unsigned int protocol; + int stated_len; + int opt, opt_len; + int reject; + unsigned char const *opt_data; + uint32_t accm; + + /* If we are LAC, do not snoop */ + if (ses->we_are_lac) { + DBG(l2tp_db(DBG_SNOOP, "Turning off snooping: We are LAC.\n")); + ses->snooping = 0; + return; + } + + /* Get protocol */ + if (buf[0] & 0x01) { + /* Compressed protcol field */ + protocol = buf[0]; + } else { + protocol = ((unsigned int) buf[0]) * 256 + buf[1]; + } + + /* If it's a network protocol, stop snooping */ + if (protocol <= 0x3fff) { + DBG(l2tp_db(DBG_SNOOP, "Turning off snooping: Network protocol %04x found.\n", protocol)); + ses->snooping = 0; + return; + } + + /* If it's not LCP, do not snoop */ + if (protocol != 0xc021) { + return; + } + + /* Skip protocol; go to packet data */ + buf += 2; + len -= 2; + + /* Unreasonably short frame?? */ + if (len <= 0) return; + + /* Look for Configure-Ack or Configure-Reject code */ + if (buf[0] != 2 && buf[0] != 4) return; + + reject = (buf[0] == 4); + + stated_len = ((unsigned int) buf[2]) * 256 + buf[3]; + + /* Something fishy with length field? */ + if (stated_len > len) return; + + /* Skip to options */ + len = stated_len - 4; + buf += 4; + + while (len > 0) { + /* Pull off an option */ + opt = buf[0]; + opt_len = buf[1]; + opt_data = &buf[2]; + if (opt_len > len || opt_len < 2) break; + len -= opt_len; + buf += opt_len; + DBG(l2tp_db(DBG_SNOOP, "Found option type %02x; len %d\n", + opt, opt_len)); + /* We are specifically interested in ACCM */ + if (opt == 0x02 && opt_len == 0x06) { + if (reject) { + /* ACCM negotiation REJECTED; use default */ + accm = 0xFFFFFFFF; + DBG(l2tp_db(DBG_SNOOP, "Rejected ACCM negotiation; defaulting (%s)\n", incoming ? "incoming" : "outgoing")); + /* ??? Is this right? */ + ses->recv_accm = accm; + ses->send_accm = accm; + ses->got_recv_accm = 1; + ses->got_send_accm = 1; + } else { + memcpy(&accm, opt_data, sizeof(accm)); + DBG(l2tp_db(DBG_SNOOP, "Found ACCM of %08x (%s)\n", accm, incoming ? "incoming" : "outgoing")); + if (incoming) { + ses->recv_accm = accm; + ses->got_recv_accm = 1; + } else { + ses->send_accm = accm; + ses->got_send_accm = 1; + } + } + + if (ses->got_recv_accm && ses->got_send_accm) { + DBG(l2tp_db(DBG_SNOOP, "Sending SLI: Send ACCM = %08x; Receive ACCM = %08x\n", ses->send_accm, ses->recv_accm)); + session_send_sli(ses); + ses->got_recv_accm = 0; + ses->got_send_accm = 0; + } + } + } +} +/********************************************************************** +* %FUNCTION: session_send_sli +* %ARGUMENTS: +* ses -- the session +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Sends an SLI message with send/receive ACCM's. +***********************************************************************/ +void +session_send_sli(l2tp_session *ses) +{ + l2tp_dgram *dgram; + + unsigned char buf[10]; + memset(buf, 0, 2); + memcpy(buf+2, &ses->send_accm, 4); + memcpy(buf+6, &ses->recv_accm, 4); + + dgram = l2tp_dgram_new_control(MESSAGE_SLI, ses->tunnel->assigned_id, + ses->assigned_id); + if (!dgram) return; + + /* Add ACCM */ + l2tp_dgram_add_avp(dgram, ses->tunnel, MANDATORY, + sizeof(buf), VENDOR_IETF, AVP_ACCM, buf); + + /* Ship it out */ + l2tp_tunnel_xmit_control_message(ses->tunnel, dgram); + ses->sent_sli = 1; +} diff --git a/release/src/router/rp-l2tp/tunnel.c b/release/src/router/rp-l2tp/tunnel.c new file mode 100644 index 00000000..004974ce --- /dev/null +++ b/release/src/router/rp-l2tp/tunnel.c @@ -0,0 +1,1920 @@ +/*********************************************************************** +* +* tunnel.c +* +* Functions for manipulating L2TP tunnel objects. +* +* Copyright (C) 2002 Roaring Penguin Software Inc. +* +* This software may be distributed under the terms of the GNU General +* Public License, Version 2, or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id: tunnel.c,v 1.2.40.1 2005/08/08 12:05:25 honor Exp $"; + +#include "l2tp.h" +#include <stddef.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <cyutils.h> + +/* Hash tables of all tunnels */ +static hash_table tunnels_by_my_id; +static hash_table tunnels_by_peer_address; + +static uint16_t tunnel_make_tid(void); +static l2tp_tunnel *tunnel_new(EventSelector *es); +static void tunnel_free(l2tp_tunnel *tunnel); +static int tunnel_send_SCCRQ(l2tp_tunnel *tunnel); +static void tunnel_handle_SCCRQ(l2tp_dgram *dgram, + EventSelector *es, + struct sockaddr_in *from); +static void tunnel_handle_SCCRP(l2tp_tunnel *tunnel, + l2tp_dgram *dgram); +static void tunnel_handle_SCCCN(l2tp_tunnel *tunnel, + l2tp_dgram *dgram); +static void tunnel_handle_ICRQ(l2tp_tunnel *tunnel, + l2tp_dgram *dgram); +static void tunnel_process_received_datagram(l2tp_tunnel *tunnel, + l2tp_dgram *dgram); +static void tunnel_schedule_ack(l2tp_tunnel *tunnel); +static void tunnel_do_ack(EventSelector *es, int fd, unsigned int flags, + void *data); +static void tunnel_handle_timeout(EventSelector *es, int fd, + unsigned int flags, void *data); +static void tunnel_finally_cleanup(EventSelector *es, int fd, + unsigned int flags, void *data); + +static void tunnel_do_hello(EventSelector *es, int fd, + unsigned int flags, void *data); + +static void tunnel_dequeue_acked_packets(l2tp_tunnel *tunnel); + +static void tunnel_send_StopCCN(l2tp_tunnel *tunnel, + int result_code, + int error_code, + char const *fmt, ...); +static void tunnel_schedule_destruction(l2tp_tunnel *tunnel); +static int tunnel_set_params(l2tp_tunnel *tunnel, l2tp_dgram *dgram); + +static int tunnel_outstanding_frames(l2tp_tunnel *tunnel); +static void tunnel_setup_hello(l2tp_tunnel *tunnel); +static void tunnel_tell_sessions_tunnel_open(l2tp_tunnel *tunnel); +static l2tp_tunnel *tunnel_establish(l2tp_peer *peer, EventSelector *es); +static l2tp_tunnel *tunnel_find_bypeer(struct sockaddr_in addr); + +static char *state_names[] = { + "idle", "wait-ctl-reply", "wait-ctl-conn", "established", + "received-stop-ccn", "sent-stop-ccn" +}; + +//#define VENDOR_STR "Roaring Penguin Software Inc." +#define VENDOR_STR L2TP_VENDOR +#define HOSTNAME_STR L2TP_HOSTNAME //2005-04-14 by kanki + +/* Comparison of serial numbers according to RFC 1982 */ +#define SERIAL_GT(a, b) \ +(((a) > (b) && (a) - (b) < 32768) || ((a) < (b) && (b) - (a) > 32768)) + +#define SERIAL_LT(a, b) \ +(((a) < (b) && (b) - (a) < 32768) || ((a) > (b) && (a) - (b) > 32768)) + +/********************************************************************** +* %FUNCTION: tunnel_set_state +* %ARGUMENTS: +* tunnel -- the tunnel +* state -- new state +* %RETURNS: +* Nothing +***********************************************************************/ +static void +tunnel_set_state(l2tp_tunnel *tunnel, int state) +{ + if (state == tunnel->state) return; + DBG(l2tp_db(DBG_TUNNEL, "tunnel(%s) state %s -> %s\n", + l2tp_debug_tunnel_to_str(tunnel), + state_names[tunnel->state], + state_names[state])); + tunnel->state = state; +} + +/********************************************************************** +* %FUNCTION: tunnel_find_by_my_id +* %ARGUMENTS: +* id -- tunnel ID +* %RETURNS: +* A tunnel whose my_id field equals id, or NULL if no such tunnel +***********************************************************************/ +l2tp_tunnel * +l2tp_tunnel_find_by_my_id(uint16_t id) +{ + l2tp_tunnel candidate; + candidate.my_id = id; + return hash_find(&tunnels_by_my_id, &candidate); +} + +/********************************************************************** +* %FUNCTION: tunnel_find_bypeer +* %ARGUMENTS: +* addr -- peer address +* %RETURNS: +* A tunnel whose peer_addr field equals addr, or NULL if no such tunnel +***********************************************************************/ +static l2tp_tunnel * +tunnel_find_bypeer(struct sockaddr_in addr) +{ + l2tp_tunnel candidate; + candidate.peer_addr = addr; + return hash_find(&tunnels_by_peer_address, &candidate); +} + +/********************************************************************** +* %FUNCTION: tunnel_flow_control_stats +* %ARGUMENTS: +* tunnel -- a tunnel +* %RETURNS: +* A string representing flow-control info (used for debugging) +***********************************************************************/ +static char const * +tunnel_flow_control_stats(l2tp_tunnel *tunnel) +{ + static char buf[256]; + + snprintf(buf, sizeof(buf), "rws=%d cwnd=%d ssthresh=%d outstanding=%d", + (int) tunnel->peer_rws, + tunnel->cwnd, + tunnel->ssthresh, + tunnel_outstanding_frames(tunnel)); + return buf; +} + +/********************************************************************** +* %FUNCTION: tunnel_outstanding_frames +* %ARGUMENTS: +* tunnel -- a tunnel +* %RETURNS: +* The number of outstanding (transmitted, but not ack'd) frames. +***********************************************************************/ +static int +tunnel_outstanding_frames(l2tp_tunnel *tunnel) +{ + int Ns = (int) tunnel->Ns_on_wire; + int Nr = (int) tunnel->peer_Nr; + if (Ns >= Nr) return Ns - Nr; + return 65536 - Nr + Ns; +} + +/********************************************************************** +* %FUNCTION: tunnel_make_tid +* %ARGUMENTS: +* None +* %RETURNS: +* An unused, random tunnel ID. +***********************************************************************/ +static uint16_t tunnel_make_tid(void) +{ + uint16_t id; + for(;;) { + L2TP_RANDOM_FILL(id); + if (!id) continue; + if (!l2tp_tunnel_find_by_my_id(id)) return id; + } +} + +/********************************************************************** +* %FUNCTION: tunnel_enqueue_dgram +* %ARGUMENTS: +* tunnel -- the tunnel +* dgram -- an L2TP datagram to place at the tail of the transmit queue +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Adds datagram to end of transmit queue. +***********************************************************************/ +static void +tunnel_enqueue_dgram(l2tp_tunnel *tunnel, + l2tp_dgram *dgram) +{ + dgram->next = NULL; + if (tunnel->xmit_queue_tail) { + tunnel->xmit_queue_tail->next = dgram; + tunnel->xmit_queue_tail = dgram; + } else { + tunnel->xmit_queue_head = dgram; + tunnel->xmit_queue_tail = dgram; + } + if (!tunnel->xmit_new_dgrams) { + tunnel->xmit_new_dgrams = dgram; + } + + dgram->Ns = tunnel->Ns; + tunnel->Ns++; + DBG(l2tp_db(DBG_FLOW, "tunnel_enqueue_dgram(%s, %s) %s\n", + l2tp_debug_tunnel_to_str(tunnel), + l2tp_debug_message_type_to_str(dgram->msg_type), + tunnel_flow_control_stats(tunnel))); + +} + +/********************************************************************** +* %FUNCTION: tunnel_dequeue_head +* %ARGUMENTS: +* tunnel -- the tunnel +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Dequeues datagram from head of transmit queue and frees it +***********************************************************************/ +static void +tunnel_dequeue_head(l2tp_tunnel *tunnel) +{ + l2tp_dgram *dgram = tunnel->xmit_queue_head; + if (dgram) { + tunnel->xmit_queue_head = dgram->next; + if (tunnel->xmit_new_dgrams == dgram) { + tunnel->xmit_new_dgrams = dgram->next; + } + if (tunnel->xmit_queue_tail == dgram) { + tunnel->xmit_queue_tail = NULL; + } + l2tp_dgram_free(dgram); + } +} + +/********************************************************************** +* %FUNCTION: tunnel_xmit_queued_messages +* %ARGUMENTS: +* tunnel -- the tunnel +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Transmits as many control messages as possible from the queue. +***********************************************************************/ +static void +tunnel_xmit_queued_messages(l2tp_tunnel *tunnel) +{ + l2tp_dgram *dgram; + struct timeval t; + + dgram = tunnel->xmit_new_dgrams; + if (!dgram) return; + + DBG(l2tp_db(DBG_FLOW, "xmit_queued(%s): %s\n", + l2tp_debug_tunnel_to_str(tunnel), + tunnel_flow_control_stats(tunnel))); + while (dgram) { + /* If window is closed, we can't transmit anything */ + if (tunnel_outstanding_frames(tunnel) >= tunnel->cwnd) { + break; + } + + /* Update Nr */ + dgram->Nr = tunnel->Nr; + + /* Tid might have changed if call was initiated before + tunnel establishment was complete */ + dgram->tid = tunnel->assigned_id; + + if (l2tp_dgram_send_to_wire(dgram, &tunnel->peer_addr) < 0) { + break; + } + + /* Cancel any pending ack */ + if (tunnel->ack_handler) { + Event_DelHandler(tunnel->es, tunnel->ack_handler); + tunnel->ack_handler = NULL; + } + + tunnel->xmit_new_dgrams = dgram->next; + tunnel->Ns_on_wire = dgram->Ns + 1; + DBG(l2tp_db(DBG_FLOW, "loop in xmit_queued(%s): %s\n", + l2tp_debug_tunnel_to_str(tunnel), + tunnel_flow_control_stats(tunnel))); + dgram = dgram->next; + } + + t.tv_sec = tunnel->timeout; + t.tv_usec = 0; + + /* Set retransmission timer */ + if (tunnel->timeout_handler) { + Event_ChangeTimeout(tunnel->timeout_handler, t); + } else { + tunnel->timeout_handler = + Event_AddTimerHandler(tunnel->es, t, + tunnel_handle_timeout, tunnel); + } +} + +/********************************************************************** +* %FUNCTION: tunnel_xmit_control_message +* %ARGUMENTS: +* tunnel -- the tunnel +* dgram -- the datagram to transmit. After return from this +* function, the tunnel "owns" the dgram and the caller should +* no longer use it. +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Transmits a control message. If there is no room in the transmit +* window, queues the message. +***********************************************************************/ +void +l2tp_tunnel_xmit_control_message(l2tp_tunnel *tunnel, + l2tp_dgram *dgram) +{ + /* Queue the datagram in case we need to retransmit it */ + tunnel_enqueue_dgram(tunnel, dgram); + + /* Run the queue */ + tunnel_xmit_queued_messages(tunnel); +} + +static unsigned int +tunnel_compute_peerhash(void *data) +{ + return (unsigned int) (((l2tp_tunnel *)data)->peer_addr.sin_addr.s_addr); +} + +static int +tunnel_compare_peer(void *d1, void *d2) +{ + return (((l2tp_tunnel *)d1)->peer_addr.sin_addr.s_addr) != + (((l2tp_tunnel *)d2)->peer_addr.sin_addr.s_addr); +} + +/********************************************************************** +* %FUNCTION: tunnel_compute_hash_my_id +* %ARGUMENTS: +* data -- a void pointer which is really a tunnel +* %RETURNS: +* My tunnel ID +***********************************************************************/ +static unsigned int +tunnel_compute_hash_my_id(void *data) +{ + return (unsigned int) (((l2tp_tunnel *) data)->my_id); +} + +/********************************************************************** +* %FUNCTION: tunnel_compare_my_id +* %ARGUMENTS: +* item1 -- first tunnel +* item2 -- second tunnel +* %RETURNS: +* 0 if both tunnels have same ID, non-zero otherwise +***********************************************************************/ +static int +tunnel_compare_my_id(void *item1, void *item2) +{ + return ((l2tp_tunnel *) item1)->my_id != ((l2tp_tunnel *) item2)->my_id; +} + +/********************************************************************** +* %FUNCTION: tunnel_cleanup +* %ARGUMENTS: +* data -- event selector +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Shuts down all tunnels and waits for them to exit +***********************************************************************/ +static void +tunnel_cleanup(void *data) +{ + EventSelector *es = (EventSelector *) data; + int i; + + /* Stop all tunnels */ + l2tp_tunnel_stop_all("Shutting down"); + + while (hash_num_entries(&tunnels_by_my_id)) { + i = Event_HandleEvent(es); + if (i < 0) { + exit(EXIT_FAILURE); + } + } +} + +/********************************************************************** +* %FUNCTION: tunnel_init +* %ARGUMENTS: +* es -- event selector +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Initializes static tunnel data structures. +***********************************************************************/ +void +l2tp_tunnel_init(EventSelector *es) +{ + hash_init(&tunnels_by_my_id, + offsetof(l2tp_tunnel, hash_by_my_id), + tunnel_compute_hash_my_id, + tunnel_compare_my_id); + hash_init(&tunnels_by_peer_address, + offsetof(l2tp_tunnel, hash_by_peer), + tunnel_compute_peerhash, + tunnel_compare_peer); + + l2tp_register_shutdown_handler(tunnel_cleanup, es); +} + +/********************************************************************** +* %FUNCTION: tunnel_new +* %ARGUMENTS: +* es -- event selector +* %RETURNS: +* A newly-allocated and initialized l2tp_tunnel object +***********************************************************************/ +static l2tp_tunnel * +tunnel_new(EventSelector *es) +{ + l2tp_tunnel *tunnel = malloc(sizeof(l2tp_tunnel)); + if (!tunnel) return NULL; + + memset(tunnel, 0, sizeof(l2tp_tunnel)); + l2tp_session_hash_init(&tunnel->sessions_by_my_id); + tunnel->rws = 4; + tunnel->peer_rws = 1; + tunnel->es = es; + tunnel->timeout = 1; + tunnel->my_id = tunnel_make_tid(); + tunnel->ssthresh = 1; + tunnel->cwnd = 1; + + hash_insert(&tunnels_by_my_id, tunnel); + DBG(l2tp_db(DBG_TUNNEL, "tunnel_new() -> %s\n", l2tp_debug_tunnel_to_str(tunnel))); + return tunnel; +} + +/********************************************************************** +* %FUNCTION: tunnel_free +* %ARGUMENTS: +* tunnel -- tunnel to free +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Frees all memory used by tunnel +***********************************************************************/ +static void +tunnel_free(l2tp_tunnel *tunnel) +{ + void *cursor; + l2tp_session *ses; + EventSelector *es = tunnel->es; + + DBG(l2tp_db(DBG_TUNNEL, "tunnel_free(%s)\n", l2tp_debug_tunnel_to_str(tunnel))); + + hash_remove(&tunnels_by_my_id, tunnel); + hash_remove(&tunnels_by_peer_address, tunnel); + + for (ses = hash_start(&tunnel->sessions_by_my_id, &cursor); + ses ; + ses = hash_next(&tunnel->sessions_by_my_id, &cursor)) { + l2tp_session_free(ses, "Tunnel closing down", 1); + } + if (tunnel->hello_handler) Event_DelHandler(es, tunnel->hello_handler); + if (tunnel->timeout_handler) Event_DelHandler(es, tunnel->timeout_handler); + if (tunnel->ack_handler) Event_DelHandler(es, tunnel->ack_handler); + + while(tunnel->xmit_queue_head) { + tunnel_dequeue_head(tunnel); + } + memset(tunnel, 0, sizeof(l2tp_tunnel)); + free(tunnel); +} + +/********************************************************************** +* %FUNCTION: tunnel_establish +* %ARGUMENTS: +* peer -- peer with which to establish tunnel +* es -- event selector for event loop +* %RETURNS: +* A newly-allocated tunnel, or NULL on error. +* %DESCRIPTION: +* Begins tunnel establishment to peer. +***********************************************************************/ +static l2tp_tunnel * +tunnel_establish(l2tp_peer *peer, EventSelector *es) +{ + l2tp_tunnel *tunnel; + + tunnel = tunnel_new(es); + if (!tunnel) return NULL; + + tunnel->peer = peer; + tunnel->peer_addr = peer->addr; + + hash_insert(&tunnels_by_peer_address, tunnel); + tunnel_send_SCCRQ(tunnel); + tunnel_set_state(tunnel, TUNNEL_WAIT_CTL_REPLY); + return tunnel; +} + +/********************************************************************** +* %FUNCTION: tunnel_send_SCCRQ +* %ARGUMENTS: +* tunnel -- the tunnel we wish to establish +* %RETURNS: +* 0 if we handed the datagram off to control transport, -1 otherwise. +* %DESCRIPTION: +* Sends SCCRQ to establish a tunnel. +***********************************************************************/ +static int +tunnel_send_SCCRQ(l2tp_tunnel *tunnel) +{ + uint16_t u16; + uint32_t u32; + unsigned char tie_breaker[8]; + unsigned char challenge[16]; + int old_hide; + char *hostname; + + l2tp_dgram *dgram = l2tp_dgram_new_control(MESSAGE_SCCRQ, 0, 0); + if (!dgram) return -1; + + /* Add the AVP's */ + /* HACK! Cisco equipment cannot handle hidden AVP's in SCCRQ. + So we temporarily disable AVP hiding */ + old_hide = tunnel->peer->hide_avps; + tunnel->peer->hide_avps = 0; + + /* Protocol version */ + u16 = htons(0x0100); + l2tp_dgram_add_avp(dgram, tunnel, MANDATORY, + sizeof(u16), VENDOR_IETF, AVP_PROTOCOL_VERSION, &u16); + + /* Framing capabilities -- hard-coded as sync and async */ + u32 = htonl(3); + l2tp_dgram_add_avp(dgram, tunnel, MANDATORY, + sizeof(u32), VENDOR_IETF, AVP_FRAMING_CAPABILITIES, &u32); + + //hostname = tunnel->peer->hostname ? tunnel->peer->hostname : Hostname; //2005-04-14 by kanki + + /* Host name */ + l2tp_dgram_add_avp(dgram, tunnel, MANDATORY, + //strlen(hostname), VENDOR_IETF, AVP_HOST_NAME, //2005-04-14 by kanki + //hostname); + strlen(HOSTNAME_STR), VENDOR_IETF, AVP_HOST_NAME, + HOSTNAME_STR); + + /* Assigned ID */ + u16 = htons(tunnel->my_id); + l2tp_dgram_add_avp(dgram, tunnel, MANDATORY, + sizeof(u16), VENDOR_IETF, AVP_ASSIGNED_TUNNEL_ID, &u16); + + /* Receive window size */ + u16 = htons(tunnel->rws); + l2tp_dgram_add_avp(dgram, tunnel, MANDATORY, + sizeof(u16), VENDOR_IETF, AVP_RECEIVE_WINDOW_SIZE, &u16); + + /* Tie-breaker */ + l2tp_random_fill(tie_breaker, sizeof(tie_breaker)); + l2tp_dgram_add_avp(dgram, tunnel, NOT_MANDATORY, + sizeof(tie_breaker), VENDOR_IETF, AVP_TIE_BREAKER, + tie_breaker); + + /* Vendor name */ + l2tp_dgram_add_avp(dgram, tunnel, NOT_MANDATORY, + strlen(VENDOR_STR), VENDOR_IETF, + AVP_VENDOR_NAME, VENDOR_STR); + + /* Challenge */ + if (tunnel->peer->secret_len) { + l2tp_random_fill(challenge, sizeof(challenge)); + l2tp_dgram_add_avp(dgram, tunnel, MANDATORY, + sizeof(challenge), VENDOR_IETF, + AVP_CHALLENGE, challenge); + + /* Compute and save expected response */ + l2tp_auth_gen_response(MESSAGE_SCCRP, tunnel->peer->secret, + challenge, sizeof(challenge), tunnel->expected_response); + } + + tunnel->peer->hide_avps = old_hide; + /* And ship it out */ + l2tp_tunnel_xmit_control_message(tunnel, dgram); + return 1; +} + +/********************************************************************** +* %FUNCTION: tunnel_handle_received_control_datagram +* %ARGUMENTS: +* dgram -- received datagram +* es -- event selector +* from -- address of sender +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Handles a received control datagram +***********************************************************************/ +void +l2tp_tunnel_handle_received_control_datagram(l2tp_dgram *dgram, + EventSelector *es, + struct sockaddr_in *from) +{ + l2tp_tunnel *tunnel; + + /* If it's SCCRQ, then handle it */ + if (dgram->msg_type == MESSAGE_SCCRQ) { + tunnel_handle_SCCRQ(dgram, es, from); + return; + } + + /* Find the tunnel to which it refers */ + if (dgram->tid == 0) { + l2tp_set_errmsg("Invalid control message - tunnel ID = 0"); + return; + } + tunnel = l2tp_tunnel_find_by_my_id(dgram->tid); + + if (!tunnel) { + /* TODO: Send error message back? */ + l2tp_set_errmsg("Invalid control message - unknown tunnel ID %d", + (int) dgram->tid); + return; + } + + /* Verify that source address is the tunnel's peer */ + if (tunnel->peer && tunnel->peer->validate_peer_ip) { + if (from->sin_addr.s_addr != tunnel->peer_addr.sin_addr.s_addr) { + l2tp_set_errmsg("Invalid control message for tunnel %s - not sent from peer", + l2tp_debug_tunnel_to_str(tunnel)); + return; + } + } + + /* Set port for replies */ + tunnel->peer_addr.sin_port = from->sin_port; + + /* Schedule an ACK for 100ms from now, but do not ack ZLB's */ + if (dgram->msg_type != MESSAGE_ZLB) { + tunnel_schedule_ack(tunnel); + } + + /* If it's an old datagram, ignore it */ + if (dgram->Ns != tunnel->Nr) { + if (SERIAL_LT(dgram->Ns, tunnel->Nr)) { + /* Old packet: Drop it */ + /* Throw away ack'd packets in our xmit queue */ + tunnel_dequeue_acked_packets(tunnel); + return; + } + /* Out-of-order packet or intermediate dropped packets. + TODO: Look into handling this better */ + return; + } + + /* Do not increment if we got ZLB */ + if (dgram->msg_type != MESSAGE_ZLB) { + tunnel->Nr++; + } + + /* Update peer_Nr */ + if (SERIAL_GT(dgram->Nr, tunnel->peer_Nr)) { + tunnel->peer_Nr = dgram->Nr; + } + + /* Reschedule HELLO handler for 60 seconds in future */ + if (tunnel->state != TUNNEL_RECEIVED_STOP_CCN && + tunnel->state != TUNNEL_SENT_STOP_CCN && + tunnel->hello_handler != NULL) { + struct timeval t; + t.tv_sec = 60; + t.tv_usec = 0; + Event_ChangeTimeout(tunnel->hello_handler, t); + } + + /* Reset retransmission stuff */ + tunnel->retransmissions = 0; + tunnel->timeout = 1; + + /* Throw away ack'd packets in our xmit queue */ + tunnel_dequeue_acked_packets(tunnel); + + /* Let the specific tunnel handle it */ + tunnel_process_received_datagram(tunnel, dgram); + + /* Run the xmit queue -- window may have opened */ + tunnel_xmit_queued_messages(tunnel); + + /* Destroy tunnel if required and if xmit queue empty */ + if (!tunnel->xmit_queue_head) { + if (tunnel->timeout_handler) { + Event_DelHandler(tunnel->es, tunnel->timeout_handler); + tunnel->timeout_handler = NULL; + } + if (tunnel->state == TUNNEL_RECEIVED_STOP_CCN) { + tunnel_schedule_destruction(tunnel); + } else if (tunnel->state == TUNNEL_SENT_STOP_CCN) { + /* Our stop-CCN has been ack'd, destroy NOW */ + tunnel_free(tunnel); + } + } +} + +/********************************************************************** +* %FUNCTION: tunnel_handle_SCCRQ +* %ARGUMENTS: +* dgram -- the received datagram +* es -- event selector +* from -- address of sender +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Handles an incoming SCCRQ +***********************************************************************/ +static void +tunnel_handle_SCCRQ(l2tp_dgram *dgram, + EventSelector *es, + struct sockaddr_in *from) +{ + l2tp_tunnel *tunnel = NULL; + + uint16_t u16; + uint32_t u32; + unsigned char challenge[16]; + char *hostname; + + /* TODO: Check if this is a retransmitted SCCRQ */ + /* Allocate a tunnel */ + tunnel = tunnel_new(es); + if (!tunnel) { + l2tp_set_errmsg("Unable to allocate new tunnel"); + return; + } + + tunnel->peer_addr = *from; + + hash_insert(&tunnels_by_peer_address, tunnel); + + /* We've received our first control datagram (SCCRQ) */ + tunnel->Nr = 1; + + if (tunnel_set_params(tunnel, dgram) < 0) return; + + /* Hunky-dory. Send SCCRP */ + dgram = l2tp_dgram_new_control(MESSAGE_SCCRP, tunnel->assigned_id, 0); + if (!dgram) { + /* Doh! Out of resources. Not much chance of StopCCN working... */ + tunnel_send_StopCCN(tunnel, + RESULT_GENERAL_ERROR, ERROR_OUT_OF_RESOURCES, + "Out of memory"); + return; + } + + /* Protocol version */ + u16 = htons(0x0100); + l2tp_dgram_add_avp(dgram, tunnel, MANDATORY, + sizeof(u16), VENDOR_IETF, AVP_PROTOCOL_VERSION, &u16); + + /* Framing capabilities -- hard-coded as sync and async */ + u32 = htonl(3); + l2tp_dgram_add_avp(dgram, tunnel, MANDATORY, + sizeof(u32), VENDOR_IETF, AVP_FRAMING_CAPABILITIES, &u32); + + //hostname = tunnel->peer->hostname ? tunnel->peer->hostname : Hostname; //2005-04-14 by kanki + + /* Host name */ + l2tp_dgram_add_avp(dgram, tunnel, MANDATORY, + //strlen(hostname), VENDOR_IETF, AVP_HOST_NAME, //2005-04-14 by kanki + //hostname); + strlen(HOSTNAME_STR), VENDOR_IETF, AVP_HOST_NAME, + HOSTNAME_STR); + + /* Assigned ID */ + u16 = htons(tunnel->my_id); + l2tp_dgram_add_avp(dgram, tunnel, MANDATORY, + sizeof(u16), VENDOR_IETF, AVP_ASSIGNED_TUNNEL_ID, &u16); + + /* Receive window size */ + u16 = htons(tunnel->rws); + l2tp_dgram_add_avp(dgram, tunnel, MANDATORY, + sizeof(u16), VENDOR_IETF, AVP_RECEIVE_WINDOW_SIZE, &u16); + + /* Vendor name */ + l2tp_dgram_add_avp(dgram, tunnel, NOT_MANDATORY, + strlen(VENDOR_STR), VENDOR_IETF, + AVP_VENDOR_NAME, VENDOR_STR); + + if (tunnel->peer->secret_len) { + /* Response */ + l2tp_dgram_add_avp(dgram, tunnel, MANDATORY, + MD5LEN, VENDOR_IETF, AVP_CHALLENGE_RESPONSE, + tunnel->response); + + /* Challenge */ + l2tp_random_fill(challenge, sizeof(challenge)); + l2tp_dgram_add_avp(dgram, tunnel, MANDATORY, + sizeof(challenge), VENDOR_IETF, + AVP_CHALLENGE, challenge); + + /* Compute and save expected response */ + l2tp_auth_gen_response(MESSAGE_SCCCN, tunnel->peer->secret, + challenge, sizeof(challenge), tunnel->expected_response); + } + + tunnel_set_state(tunnel, TUNNEL_WAIT_CTL_CONN); + + /* And ship it out */ + l2tp_tunnel_xmit_control_message(tunnel, dgram); +} + +/********************************************************************** +* %FUNCTION: tunnel_schedule_ack +* %ARGUMENTS: +* tunnel -- the tunnel +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Schedules an ack for 100ms from now. The hope is we'll be able to +* piggy-back the ack on a packet in the queue; if not, we'll send a ZLB. +***********************************************************************/ +static void +tunnel_schedule_ack(l2tp_tunnel *tunnel) +{ + struct timeval t; + + DBG(l2tp_db(DBG_FLOW, "tunnel_schedule_ack(%s)\n", + l2tp_debug_tunnel_to_str(tunnel))); + /* Already scheduled? Do nothing */ + if (tunnel->ack_handler) return; + + t.tv_sec = 0; + t.tv_usec = 100000; + tunnel->ack_handler = Event_AddTimerHandler(tunnel->es, + t, tunnel_do_ack, tunnel); +} + +/********************************************************************** +* %FUNCTION: tunnel_do_ack +* %ARGUMENTS: +* es -- event selector +* fd, flags -- not used +* data -- pointer to tunnel which needs ack +* %RETURNS: +* Nothing +* %DESCRIPTION: +* If there is a frame on our queue, update it's Nr and run queue; if not, +* send a ZLB immediately. +***********************************************************************/ +static void +tunnel_do_ack(EventSelector *es, + int fd, + unsigned int flags, + void *data) +{ + l2tp_tunnel *tunnel = (l2tp_tunnel *) data; + + /* Ack handler has fired */ + tunnel->ack_handler = NULL; + + DBG(l2tp_db(DBG_FLOW, "tunnel_do_ack(%s)\n", + l2tp_debug_tunnel_to_str(tunnel))); + + /* If nothing is on the queue, add a ZLB */ + /* Or, if we cannot transmit because of flow-control, send ZLB */ + if (!tunnel->xmit_new_dgrams || + tunnel_outstanding_frames(tunnel) >= tunnel->cwnd) { + tunnel_send_ZLB(tunnel); + return; + } + + /* Run the queue */ + tunnel_xmit_queued_messages(tunnel); +} + +/********************************************************************** +* %FUNCTION: tunnel_dequeue_acked_packets +* %ARGUMENTS: +* tunnel -- the tunnel +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Discards all acknowledged packets from our xmit queue. +***********************************************************************/ +static void +tunnel_dequeue_acked_packets(l2tp_tunnel *tunnel) +{ + l2tp_dgram *dgram = tunnel->xmit_queue_head; + DBG(l2tp_db(DBG_FLOW, "tunnel_dequeue_acked_packets(%s) %s\n", + l2tp_debug_tunnel_to_str(tunnel), tunnel_flow_control_stats(tunnel))); + while (dgram) { + if (SERIAL_LT(dgram->Ns, tunnel->peer_Nr)) { + tunnel_dequeue_head(tunnel); + if (tunnel->cwnd < tunnel->ssthresh) { + /* Slow start: increment CWND for each packet dequeued */ + tunnel->cwnd++; + if (tunnel->cwnd > tunnel->peer_rws) { + tunnel->cwnd = tunnel->peer_rws; + } + } else { + /* Congestion avoidance: increment CWND for each CWND + packets dequeued */ + tunnel->cwnd_counter++; + if (tunnel->cwnd_counter >= tunnel->cwnd) { + tunnel->cwnd_counter = 0; + tunnel->cwnd++; + if (tunnel->cwnd > tunnel->peer_rws) { + tunnel->cwnd = tunnel->peer_rws; + } + } + } + } else { + break; + } + dgram = tunnel->xmit_queue_head; + } +} + +/********************************************************************** +* %FUNCTION: tunnel_handle_timeout +* %ARGUMENTS: +* es -- event selector +* fd, flags -- not used +* data -- pointer to tunnel which needs ack +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Called when retransmission timer fires. +***********************************************************************/ +static void +tunnel_handle_timeout(EventSelector *es, + int fd, + unsigned int flags, + void *data) +{ + l2tp_tunnel *tunnel = (l2tp_tunnel *) data; + + /* Timeout handler has fired */ + tunnel->timeout_handler = NULL; + + /* Reset xmit_new_dgrams */ + tunnel->xmit_new_dgrams = tunnel->xmit_queue_head; + + /* Set Ns on wire to Ns of first frame in queue */ + if (tunnel->xmit_queue_head) { + tunnel->Ns_on_wire = tunnel->xmit_queue_head->Ns; + } + + /* Go back to slow-start phase */ + tunnel->ssthresh = tunnel->cwnd / 2; + if (!tunnel->ssthresh) tunnel->ssthresh = 1; + tunnel->cwnd = 1; + tunnel->cwnd_counter = 0; + + tunnel->retransmissions++; + if (tunnel->retransmissions >= MAX_RETRANSMISSIONS) { + l2tp_set_errmsg("Too many retransmissions on tunnel (%s); closing down", + l2tp_debug_tunnel_to_str(tunnel)); + /* Close tunnel... */ + tunnel_free(tunnel); + return; + } + + /* Double timeout, capping at 8 seconds */ + if (tunnel->timeout < 8) { + tunnel->timeout *= 2; + } + + /* Run the queue */ + tunnel_xmit_queued_messages(tunnel); +} + +/********************************************************************** +* %FUNCTION: tunnel_send_StopCCN +* %ARGUMENTS: +* tunnel -- the tunnel +* result_code -- what to put in result-code AVP +* error_code -- what to put in error-code field +* fmt -- format string for error message +* ... -- args for formatting error message +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Sends a StopCCN control message. +***********************************************************************/ +static void +tunnel_send_StopCCN(l2tp_tunnel *tunnel, + int result_code, + int error_code, + char const *fmt, ...) +{ + char buf[256]; + va_list ap; + uint16_t u16; + uint16_t len; + l2tp_dgram *dgram; + + /* Build the buffer for the result-code AVP */ + buf[0] = result_code / 256; + buf[1] = result_code & 255; + buf[2] = error_code / 256; + buf[3] = error_code & 255; + + va_start(ap, fmt); + vsnprintf(buf+4, 256-4, fmt, ap); + buf[255] = 0; + va_end(ap); + + DBG(l2tp_db(DBG_TUNNEL, "tunnel_send_StopCCN(%s, %d, %d, %s)\n", + l2tp_debug_tunnel_to_str(tunnel), result_code, error_code, buf+4)); + + len = 4 + strlen(buf+4); + /* Build the datagram */ + dgram = l2tp_dgram_new_control(MESSAGE_StopCCN, tunnel->assigned_id, 0); + if (!dgram) return; + + /* Add assigned tunnel ID */ + u16 = htons(tunnel->my_id); + l2tp_dgram_add_avp(dgram, tunnel, MANDATORY, + sizeof(u16), VENDOR_IETF, AVP_ASSIGNED_TUNNEL_ID, &u16); + + /* Add result code */ + l2tp_dgram_add_avp(dgram, tunnel, MANDATORY, + len, VENDOR_IETF, AVP_RESULT_CODE, buf); + + /* TODO: Clean up */ + tunnel_set_state(tunnel, TUNNEL_SENT_STOP_CCN); + + /* Ship it out */ + l2tp_tunnel_xmit_control_message(tunnel, dgram); +} + +/********************************************************************** +* %FUNCTION: tunnel_handle_StopCCN +* %ARGUMENTS: +* tunnel -- the tunnel +* dgram -- received datagram +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Processes a received StopCCN frame (shuts tunnel down) +***********************************************************************/ +static void +tunnel_handle_StopCCN(l2tp_tunnel *tunnel, + l2tp_dgram *dgram) +{ + unsigned char *val; + int mandatory, hidden; + uint16_t len, vendor, type; + int err; + l2tp_session *ses; + void *cursor; + + /* Shut down all the sessions */ + for (ses = hash_start(&tunnel->sessions_by_my_id, &cursor); + ses ; + ses = hash_next(&tunnel->sessions_by_my_id, &cursor)) { + hash_remove(&tunnel->sessions_by_my_id, ses); + l2tp_session_free(ses, "Tunnel closing down", 1); + } + + tunnel_set_state(tunnel, TUNNEL_RECEIVED_STOP_CCN); + + /* If there are any queued datagrams waiting for transmission, + nuke them and adjust tunnel's Ns to whatever peer has ack'd */ + /* TODO: Is this correct? */ + if (tunnel->xmit_queue_head) { + tunnel->Ns = tunnel->peer_Nr; + while(tunnel->xmit_queue_head) { + tunnel_dequeue_head(tunnel); + } + } + + /* Parse the AVP's */ + while(1) { + val = l2tp_dgram_pull_avp(dgram, tunnel, &mandatory, &hidden, + &len, &vendor, &type, &err); + if (!val) { + break; + } + + /* Only care about assigned tunnel ID. TODO: Fix this! */ + if (vendor != VENDOR_IETF || type != AVP_ASSIGNED_TUNNEL_ID) { + continue; + } + + if (len == 2) { + tunnel->assigned_id = ((unsigned int) val[0]) * 256 + + (unsigned int) val[1]; + } + } + + /* No point in delaying ack; there will never be a datagram for + piggy-back. So cancel ack timer and shoot out a ZLB now */ + if (tunnel->ack_handler) { + Event_DelHandler(tunnel->es, tunnel->ack_handler); + tunnel->ack_handler = NULL; + } + tunnel_send_ZLB(tunnel); + /* We'll be scheduled for destruction after this function returns */ +} + +/********************************************************************** +* %FUNCTION: tunnel_process_received_datagram +* %ARGUMENTS: +* tunnel -- the tunnel +* dgram -- the received datagram +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Handles a received control message for this tunnel +***********************************************************************/ +static void +tunnel_process_received_datagram(l2tp_tunnel *tunnel, + l2tp_dgram *dgram) +{ + l2tp_session *ses = NULL; + + /* If message is associated with existing session, find session */ + + DBG(l2tp_db(DBG_TUNNEL, "tunnel_process_received_datagram(%s, %s)\n", + l2tp_debug_tunnel_to_str(tunnel), + l2tp_debug_message_type_to_str(dgram->msg_type))); + switch (dgram->msg_type) { + case MESSAGE_OCRP: + case MESSAGE_OCCN: + case MESSAGE_ICRP: + case MESSAGE_ICCN: + case MESSAGE_CDN: + ses = l2tp_tunnel_find_session(tunnel, dgram->sid); + if (!ses) { + l2tp_set_errmsg("Session-related message for unknown session %d", + (int) dgram->sid); + return; + } + } + + switch (dgram->msg_type) { + case MESSAGE_StopCCN: + tunnel_handle_StopCCN(tunnel, dgram); + return; + case MESSAGE_SCCRP: + tunnel_handle_SCCRP(tunnel, dgram); + return; + case MESSAGE_SCCCN: + tunnel_handle_SCCCN(tunnel, dgram); + return; + case MESSAGE_ICRQ: + tunnel_handle_ICRQ(tunnel, dgram); + return; + case MESSAGE_CDN: + l2tp_session_handle_CDN(ses, dgram); + return; + case MESSAGE_ICRP: + l2tp_session_handle_ICRP(ses, dgram); + return; + case MESSAGE_ICCN: + l2tp_session_handle_ICCN(ses, dgram); + return; + } +} + +/********************************************************************** +* %FUNCTION: tunnel_finally_cleanup +* %ARGUMENTS: +* es -- event selector +* fd, flags -- ignored +* data -- the tunnel +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Deallocates all tunnel state +***********************************************************************/ +static void +tunnel_finally_cleanup(EventSelector *es, + int fd, + unsigned int flags, + void *data) +{ + l2tp_tunnel *tunnel = (l2tp_tunnel *) data; + + /* Hello handler has fired */ + tunnel->hello_handler = NULL; + tunnel_free(tunnel); +} + +/********************************************************************** +* %FUNCTION: tunnel_schedule_destruction +* %ARGUMENTS: +* tunnel -- tunnel which is to be destroyed +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Schedules tunnel for destruction 31 seconds hence. +***********************************************************************/ +static void +tunnel_schedule_destruction(l2tp_tunnel *tunnel) +{ + struct timeval t; + void *cursor; + l2tp_session *ses; + + t.tv_sec = 31; + t.tv_usec = 0; + + DBG(l2tp_db(DBG_TUNNEL, "tunnel_schedule_destruction(%s)\n", + l2tp_debug_tunnel_to_str(tunnel))); + /* Shut down the tunnel in 31 seconds - (ab)use the hello handler */ + if (tunnel->hello_handler) { + Event_DelHandler(tunnel->es, tunnel->hello_handler); + } + tunnel->hello_handler = + Event_AddTimerHandler(tunnel->es, t, + tunnel_finally_cleanup, tunnel); + + /* Kill the sessions now */ + for (ses = hash_start(&tunnel->sessions_by_my_id, &cursor); + ses ; + ses = hash_next(&tunnel->sessions_by_my_id, &cursor)) { + l2tp_session_free(ses, "Tunnel closing down", 1); + } + + /* Clear hash table */ + l2tp_session_hash_init(&tunnel->sessions_by_my_id); +} + +/********************************************************************** +* %FUNCTION: tunnel_send_ZLB +* %ARGUMENTS: +* tunnel -- the tunnel +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Sends a ZLB ack packet. +***********************************************************************/ +void +tunnel_send_ZLB(l2tp_tunnel *tunnel) +{ + l2tp_dgram *dgram = + l2tp_dgram_new_control(MESSAGE_ZLB, tunnel->assigned_id, 0); + if (!dgram) { + l2tp_set_errmsg("tunnel_send_ZLB: Out of memory"); + return; + } + dgram->Nr = tunnel->Nr; + dgram->Ns = tunnel->Ns; + l2tp_dgram_send_to_wire(dgram, &tunnel->peer_addr); + l2tp_dgram_free(dgram); +} + +/********************************************************************** +* %FUNCTION: tunnel_handle_SCCRP +* %ARGUMENTS: +* tunnel -- the tunnel +* dgram -- the incoming datagram +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Handles an incoming SCCRP +***********************************************************************/ +static void +tunnel_handle_SCCRP(l2tp_tunnel *tunnel, + l2tp_dgram *dgram) +{ + + /* TODO: If we don't get challenge response at all, barf */ + /* Are we expecing SCCRP? */ + if (tunnel->state != TUNNEL_WAIT_CTL_REPLY) { + tunnel_send_StopCCN(tunnel, RESULT_FSM_ERROR, 0, "Not expecting SCCRP"); + return; + } + + /* Extract tunnel params */ + if (tunnel_set_params(tunnel, dgram) < 0) return; + + tunnel_set_state(tunnel, TUNNEL_ESTABLISHED); + tunnel_setup_hello(tunnel); + + /* Hunky-dory. Send SCCCN */ + dgram = l2tp_dgram_new_control(MESSAGE_SCCCN, tunnel->assigned_id, 0); + if (!dgram) { + /* Doh! Out of resources. Not much chance of StopCCN working... */ + tunnel_send_StopCCN(tunnel, + RESULT_GENERAL_ERROR, ERROR_OUT_OF_RESOURCES, + "Out of memory"); + return; + } + + /* Add response */ + if (tunnel->peer->secret_len) { + l2tp_dgram_add_avp(dgram, tunnel, MANDATORY, + MD5LEN, VENDOR_IETF, AVP_CHALLENGE_RESPONSE, + tunnel->response); + } + + /* Ship it out */ + l2tp_tunnel_xmit_control_message(tunnel, dgram); + + /* Tell sessions tunnel has been established */ + tunnel_tell_sessions_tunnel_open(tunnel); +} + +/********************************************************************** +* %FUNCTION: tunnel_set_params +* %ARGUMENTS: +* tunnel -- the tunnel +* dgram -- incoming SCCRQ or SCCRP datagram +* %RETURNS: +* 0 if OK, non-zero on error +* %DESCRIPTION: +* Sets up initial tunnel parameters (assigned ID, etc.) +***********************************************************************/ +static int +tunnel_set_params(l2tp_tunnel *tunnel, + l2tp_dgram *dgram) +{ + unsigned char *val; + int mandatory, hidden; + uint16_t len, vendor, type; + int err = 0; + int found_response = 0; + + uint16_t u16; + + /* Get host name */ + val = l2tp_dgram_search_avp(dgram, tunnel, &mandatory, &hidden, &len, + VENDOR_IETF, AVP_HOST_NAME); + if (!val) { + l2tp_set_errmsg("No host name AVP in SCCRQ"); + tunnel_free(tunnel); + return -1; + } + + if (len >= MAX_HOSTNAME) len = MAX_HOSTNAME-1; + memcpy(tunnel->peer_hostname, val, len); + tunnel->peer_hostname[len+1] = 0; + DBG(l2tp_db(DBG_TUNNEL, "%s: Peer host name is '%s'\n", + l2tp_debug_tunnel_to_str(tunnel), tunnel->peer_hostname)); + + /* Find peer */ + tunnel->peer = l2tp_peer_find(&tunnel->peer_addr, tunnel->peer_hostname); + + /* Get assigned tunnel ID */ + val = l2tp_dgram_search_avp(dgram, tunnel, &mandatory, &hidden, &len, + VENDOR_IETF, AVP_ASSIGNED_TUNNEL_ID); + if (!val) { + l2tp_set_errmsg("No assigned tunnel ID AVP in SCCRQ"); + tunnel_free(tunnel); + return -1; + } + + if (!l2tp_dgram_validate_avp(VENDOR_IETF, AVP_ASSIGNED_TUNNEL_ID, + len, mandatory)) { + tunnel_free(tunnel); + return -1; + } + + /* Set tid */ + tunnel->assigned_id = ((unsigned int) val[0]) * 256 + (unsigned int) val[1]; + if (!tunnel->assigned_id) { + l2tp_set_errmsg("Invalid assigned-tunnel-ID of zero"); + tunnel_free(tunnel); + return -1; + } + + /* Validate peer */ + if (!tunnel->peer) { + l2tp_set_errmsg("Peer %s is not authorized to create a tunnel", + inet_ntoa(tunnel->peer_addr.sin_addr)); + tunnel_send_StopCCN(tunnel, RESULT_NOAUTH, ERROR_OK, "%s", l2tp_get_errmsg()); + return -1; + } + + /* Pull out and examine AVP's */ + while(1) { + val = l2tp_dgram_pull_avp(dgram, tunnel, &mandatory, &hidden, + &len, &vendor, &type, &err); + if (!val) { + if (err) { + tunnel_send_StopCCN(tunnel, RESULT_GENERAL_ERROR, + ERROR_BAD_VALUE, "%s", l2tp_get_errmsg()); + return -1; + } + break; + } + + /* Unknown vendor? Ignore, unless mandatory */ + if (vendor != VENDOR_IETF) { + if (!mandatory) continue; + tunnel_send_StopCCN(tunnel, RESULT_GENERAL_ERROR, + ERROR_UNKNOWN_AVP_WITH_M_BIT, + "Unknown mandatory attribute (vendor=%d, type=%d) in %s", + (int) vendor, (int) type, + l2tp_debug_avp_type_to_str(dgram->msg_type)); + return -1; + } + + /* Validate AVP, ignore invalid AVP's without M bit set */ + if (!l2tp_dgram_validate_avp(vendor, type, len, mandatory)) { + if (!mandatory) continue; + tunnel_send_StopCCN(tunnel, RESULT_GENERAL_ERROR, + ERROR_BAD_LENGTH, "%s", l2tp_get_errmsg()); + return -1; + } + + switch(type) { + case AVP_PROTOCOL_VERSION: + u16 = ((uint16_t) val[0]) * 256 + val[1]; + if (u16 != 0x100) { + tunnel_send_StopCCN(tunnel, RESULT_UNSUPPORTED_VERSION, + 0x100, "Unsupported protocol version"); + return -1; + } + break; + + case AVP_HOST_NAME: + /* Already been handled */ + break; + + case AVP_FRAMING_CAPABILITIES: + /* TODO: Do we care about framing capabilities? */ + break; + + case AVP_ASSIGNED_TUNNEL_ID: + /* Already been handled */ + break; + + case AVP_BEARER_CAPABILITIES: + /* TODO: Do we care? */ + break; + + case AVP_RECEIVE_WINDOW_SIZE: + u16 = ((uint16_t) val[0]) * 256 + val[1]; + /* Silently correct bogus rwin */ + if (u16 < 1) u16 = 1; + tunnel->peer_rws = u16; + tunnel->ssthresh = u16; + break; + + case AVP_CHALLENGE: + if (tunnel->peer->secret_len) { + l2tp_auth_gen_response( + ((dgram->msg_type == MESSAGE_SCCRQ) ? MESSAGE_SCCRP : MESSAGE_SCCCN), + tunnel->peer->secret, + val, len, tunnel->response); + } + break; + + case AVP_CHALLENGE_RESPONSE: + /* Length has been validated by l2tp_dgram_validate_avp */ + if (tunnel->peer->secret_len) { + if (memcmp(val, tunnel->expected_response, MD5LEN)) { + tunnel_send_StopCCN(tunnel, RESULT_NOAUTH, ERROR_BAD_VALUE, + "Incorrect challenge response"); + return -1; + } + } + found_response = 1; + break; + + case AVP_TIE_BREAKER: + /* TODO: Handle tie-breaker */ + break; + + case AVP_FIRMWARE_REVISION: + /* TODO: Do we care? */ + break; + + case AVP_VENDOR_NAME: + /* TODO: Do we care? */ + break; + + default: + /* TODO: Maybe print an error? */ + break; + } + } + + if (tunnel->peer->secret_len && + !found_response && + dgram->msg_type != MESSAGE_SCCRQ) { + tunnel_send_StopCCN(tunnel, RESULT_NOAUTH, 0, + "Missing challenge-response"); + return -1; + } + return 0; +} + +/********************************************************************** +* %FUNCTION: tunnel_setup_hello +* %ARGUMENTS: +* tunnel -- the tunnel +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Sets up timer for sending HELLO messages +***********************************************************************/ +static void +tunnel_setup_hello(l2tp_tunnel *tunnel) +{ + struct timeval t; + + t.tv_sec = 60; + t.tv_usec = 0; + + if (tunnel->hello_handler) { + Event_DelHandler(tunnel->es, tunnel->hello_handler); + } + tunnel->hello_handler = Event_AddTimerHandler(tunnel->es, t, + tunnel_do_hello, tunnel); +} + +/********************************************************************** +* %FUNCTION: tunnel_do_hello +* %ARGUMENTS: +* es -- event selector +* fd, flags -- ignored +* data -- the tunnel +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Deallocates all tunnel state +***********************************************************************/ +static void +tunnel_do_hello(EventSelector *es, + int fd, + unsigned int flags, + void *data) +{ + l2tp_tunnel *tunnel = (l2tp_tunnel *) data; + l2tp_dgram *dgram; + + /* Hello handler has fired */ + tunnel->hello_handler = NULL; + + /* Reschedule HELLO timer */ + tunnel_setup_hello(tunnel); + + /* Send a HELLO message */ + dgram = l2tp_dgram_new_control(MESSAGE_HELLO, tunnel->assigned_id, 0); + if (dgram) l2tp_tunnel_xmit_control_message(tunnel, dgram); +} + +/********************************************************************** +* %FUNCTION: tunnel_handle_SCCCN +* %ARGUMENTS: +* tunnel -- the tunnel +* dgram -- the incoming datagram +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Handles an incoming SCCCN +***********************************************************************/ +static void +tunnel_handle_SCCCN(l2tp_tunnel *tunnel, + l2tp_dgram *dgram) +{ + unsigned char *val; + uint16_t len; + int hidden, mandatory; + + /* Are we expecing SCCCN? */ + if (tunnel->state != TUNNEL_WAIT_CTL_CONN) { + tunnel_send_StopCCN(tunnel, RESULT_FSM_ERROR, 0, "Not expecting SCCCN"); + return; + } + + /* Check challenge response */ + if (tunnel->peer->secret_len) { + val = l2tp_dgram_search_avp(dgram, tunnel, &mandatory, &hidden, &len, + VENDOR_IETF, AVP_CHALLENGE_RESPONSE); + if (!val) { + tunnel_send_StopCCN(tunnel, RESULT_NOAUTH, 0, + "Missing challenge-response"); + return; + } + + if (!l2tp_dgram_validate_avp(VENDOR_IETF, AVP_CHALLENGE_RESPONSE, + len, mandatory)) { + tunnel_send_StopCCN(tunnel, RESULT_GENERAL_ERROR, ERROR_BAD_LENGTH, + "Invalid challenge-response"); + return; + } + if (memcmp(val, tunnel->expected_response, MD5LEN)) { + tunnel_send_StopCCN(tunnel, RESULT_NOAUTH, ERROR_BAD_VALUE, + "Incorrect challenge response"); + return; + } + } + + tunnel_set_state(tunnel, TUNNEL_ESTABLISHED); + tunnel_setup_hello(tunnel); + + /* Tell sessions tunnel has been established */ + tunnel_tell_sessions_tunnel_open(tunnel); +} + +/********************************************************************** +* %FUNCTION: tunnel_find_for_peer +* %ARGUMENTS: +* peer -- an L2TP peer +* es -- an event selector +* %RETURNS: +* An existing tunnel to peer (if one exists) or a new one (if one does not), +* or NULL if no tunnel could be established. If the existing tunnel +* is in the state RECEIVED_STOP_CCN or SENT_STOP_CCN, make a new one. +***********************************************************************/ +l2tp_tunnel * +l2tp_tunnel_find_for_peer(l2tp_peer *peer, + EventSelector *es) +{ + l2tp_tunnel *tunnel = tunnel_find_bypeer(peer->addr); + if (tunnel) { + if (tunnel->state == TUNNEL_WAIT_CTL_REPLY || + tunnel->state == TUNNEL_WAIT_CTL_CONN || + tunnel->state == TUNNEL_ESTABLISHED) { + return tunnel; + } + } + + /* No tunnel, or tunnel in wrong state */ + return tunnel_establish(peer, es); +} + +/********************************************************************** +* %FUNCTION: tunnel_find_session +* %ARGUMENTS: +* sid -- session ID +* %RETURNS: +* The session with specified ID, or NULL if no such session +***********************************************************************/ +l2tp_session * +l2tp_tunnel_find_session(l2tp_tunnel *tunnel, + uint16_t sid) +{ + l2tp_session candidate; + candidate.my_id = sid; + return hash_find(&tunnel->sessions_by_my_id, &candidate); +} + +/********************************************************************** +* %FUNCTION: tunnel_tell_sessions_tunnel_open +* %ARGUMENTS: +* tunnel -- the tunnel +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Informs all waiting sessions that tunnel has moved into ESTABLISHED state. +***********************************************************************/ +static void +tunnel_tell_sessions_tunnel_open(l2tp_tunnel *tunnel) +{ + l2tp_session *ses; + void *cursor; + + for (ses = hash_start(&tunnel->sessions_by_my_id, &cursor); + ses ; + ses = hash_next(&tunnel->sessions_by_my_id, &cursor)) { + l2tp_session_notify_tunnel_open(ses); + } +} + +/********************************************************************** +* %FUNCTION: tunnel_add_session +* %ARGUMENTS: +* ses -- session to add +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Adds session to tunnel's hash table. If tunnel is up, calls +* l2tp_session_notify_tunnel_open +***********************************************************************/ +void +l2tp_tunnel_add_session(l2tp_session *ses) +{ + l2tp_tunnel *tunnel = ses->tunnel; + + hash_insert(&tunnel->sessions_by_my_id, ses); + if (tunnel->state == TUNNEL_ESTABLISHED) { + l2tp_session_notify_tunnel_open(ses); + } +} + +void +l2tp_tunnel_reestablish(EventSelector *es, + int fd, + unsigned int flags, + void *data) +{ + l2tp_peer *peer = (l2tp_peer*) data; + l2tp_session *ses; + + ses = l2tp_session_call_lns(peer, "foobar", es, NULL); + if (!ses) { + DBG(l2tp_db(DBG_TUNNEL, "l2tp_tunnel_reestablish() failed\n")); + return; + } +} + +/********************************************************************** +* %FUNCTION: tunnel_delete_session +* %ARGUMENTS: +* ses -- session to delete +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Deletes session from tunnel's hash table and frees it. +***********************************************************************/ +void +l2tp_tunnel_delete_session(l2tp_session *ses, char const *reason, int may_reestablish) +{ + l2tp_tunnel *tunnel = ses->tunnel; + + hash_remove(&tunnel->sessions_by_my_id, ses); + l2tp_session_free(ses, reason, may_reestablish); + + /* Tear down tunnel if so indicated */ + if (!hash_num_entries(&tunnel->sessions_by_my_id)) { + if (!tunnel->peer->retain_tunnel) { + tunnel_send_StopCCN(tunnel, + RESULT_GENERAL_REQUEST, 0, + "Last session has closed"); + } + } +} + +/********************************************************************** +* %FUNCTION: tunnel_handle_ICRQ +* %ARGUMENTS: +* tunnel -- the tunnel +* dgram -- the datagram +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Handles ICRQ (Incoming Call ReQuest) +***********************************************************************/ +static void +tunnel_handle_ICRQ(l2tp_tunnel *tunnel, + l2tp_dgram *dgram) +{ + uint16_t u16; + unsigned char *val; + uint16_t len; + int mandatory, hidden; + + /* Get assigned session ID */ + val = l2tp_dgram_search_avp(dgram, tunnel, &mandatory, &hidden, &len, + VENDOR_IETF, AVP_ASSIGNED_SESSION_ID); + if (!val) { + l2tp_set_errmsg("No assigned tunnel ID AVP in ICRQ"); + return; + } + if (!l2tp_dgram_validate_avp(VENDOR_IETF, AVP_ASSIGNED_SESSION_ID, + len, mandatory)) { + /* TODO: send CDN */ + return; + } + + /* Set assigned session ID */ + u16 = ((uint16_t) val[0]) * 256 + (uint16_t) val[1]; + + if (!u16) { + /* TODO: send CDN */ + return; + } + + /* Tunnel in wrong state? */ + if (tunnel->state != TUNNEL_ESTABLISHED) { + /* TODO: Send CDN */ + return; + } + + /* Set up new incoming call */ + /* TODO: Include calling number */ + l2tp_session_lns_handle_incoming_call(tunnel, u16, dgram, ""); +} + +/********************************************************************** +* %FUNCTION: l2tp_tunnel_stop_all +* %ARGUMENTS: +* reason -- reason for stopping tunnels +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Stops all tunnels +***********************************************************************/ +void +l2tp_tunnel_stop_all(char const *reason) +{ + l2tp_tunnel *tunnel; + void *cursor; + + /* Send StopCCN on all tunnels except those which are scheduled for + destruction */ + for (tunnel = hash_start(&tunnels_by_my_id, &cursor); + tunnel; + tunnel = hash_next(&tunnels_by_my_id, &cursor)) { + l2tp_tunnel_stop_tunnel(tunnel, reason); + } + +} + +/********************************************************************** +* %FUNCTION: l2tp_tunnel_stop_tunnel +* %ARGUMENTS: +* tunnel -- tunnel to stop +* reason -- reason for stopping tunnel +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Stops a tunnels (sends StopCCN) +***********************************************************************/ +void +l2tp_tunnel_stop_tunnel(l2tp_tunnel *tunnel, + char const *reason) +{ + /* Do not send StopCCN if we've received one already */ + if (tunnel->state != TUNNEL_RECEIVED_STOP_CCN && + tunnel->state != TUNNEL_SENT_STOP_CCN) { + tunnel_send_StopCCN(tunnel, RESULT_SHUTTING_DOWN, 0, reason); + } +} + +/********************************************************************** +* %FUNCTION: l2tp_num_tunnels +* %ARGUMENTS: +* None +* %RETURNS: +* The number of L2TP tunnels +***********************************************************************/ +int +l2tp_num_tunnels(void) +{ + return hash_num_entries(&tunnels_by_my_id); +} + +/********************************************************************** +* %FUNCTION: l2tp_first_tunnel +* %ARGUMENTS: +* cursor -- cursor for keeping track of where we are in interation +* %RETURNS: +* First L2TP tunnel +***********************************************************************/ +l2tp_tunnel * +l2tp_first_tunnel(void **cursor) +{ + return hash_start(&tunnels_by_my_id, cursor); +} + + +/********************************************************************** +* %FUNCTION: l2tp_next_tunnel +* %ARGUMENTS: +* cursor -- cursor for keeping track of where we are in interation +* %RETURNS: +* Next L2TP tunnel +***********************************************************************/ +l2tp_tunnel * +l2tp_next_tunnel(void **cursor) +{ + return hash_next(&tunnels_by_my_id, cursor); +} + +/********************************************************************** +* %FUNCTION: l2tp_tunnel_state_name +* %ARGUMENTS: +* tunnel -- the tunnel +* %RETURNS: +* The name of the tunnel's state +***********************************************************************/ +char const * +l2tp_tunnel_state_name(l2tp_tunnel *tunnel) +{ + return state_names[tunnel->state]; +} + +/********************************************************************** +* %FUNCTION: l2tp_tunnel_first_session +* %ARGUMENTS: +* tunnel -- the tunnel +* cursor -- cursor for hash table iteration +* %RETURNS: +* First session in tunnel +***********************************************************************/ +l2tp_session * +l2tp_tunnel_first_session(l2tp_tunnel *tunnel, void **cursor) +{ + return hash_start(&tunnel->sessions_by_my_id, cursor); +} + +/********************************************************************** +* %FUNCTION: l2tp_tunnel_next_session +* %ARGUMENTS: +* tunnel -- the tunnel +* cursor -- cursor for hash table iteration +* %RETURNS: +* Next session in tunnel +***********************************************************************/ +l2tp_session * +l2tp_tunnel_next_session(l2tp_tunnel *tunnel, void **cursor) +{ + return hash_next(&tunnel->sessions_by_my_id, cursor); +} diff --git a/release/src/router/rp-l2tp/utils.c b/release/src/router/rp-l2tp/utils.c new file mode 100644 index 00000000..c05bd425 --- /dev/null +++ b/release/src/router/rp-l2tp/utils.c @@ -0,0 +1,219 @@ +/*********************************************************************** +* +* utils.c +* +* Utility functions for l2tp +* +* Copyright (C) 2002 Roaring Penguin Software Inc. +* +* This software may be distributed under the terms of the GNU General +* Public License, Version 2, or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id: utils.c,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $"; + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <stdarg.h> +#include <stdio.h> +#include <dlfcn.h> +#include <string.h> +#include <errno.h> + +#include "l2tp.h" + +#define MAX_ERRMSG_LEN 512 + +static int random_fd = -1; +static char errmsg[MAX_ERRMSG_LEN]; + +struct sd_handler { + l2tp_shutdown_func f; + void *data; +}; + +static struct sd_handler shutdown_handlers[16]; + +static int n_shutdown_handlers = 0; + +int +l2tp_register_shutdown_handler(l2tp_shutdown_func f, void *data) +{ + if (n_shutdown_handlers == 16) return -1; + shutdown_handlers[n_shutdown_handlers].f = f; + shutdown_handlers[n_shutdown_handlers].data = data; + n_shutdown_handlers++; + return n_shutdown_handlers; +} + +void +l2tp_cleanup(void) +{ + int i; + for (i=0; i<n_shutdown_handlers; i++) { + shutdown_handlers[i].f(shutdown_handlers[i].data); + } +} + +char const * +l2tp_get_errmsg(void) +{ + return errmsg; +} + +/********************************************************************** +* %FUNCTION: set_errmsg +* %ARGUMENTS: +* fmt -- printf format +* ... -- format args +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Sets static errmsg string +***********************************************************************/ +void +l2tp_set_errmsg(char const *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vsnprintf(errmsg, MAX_ERRMSG_LEN, fmt, ap); + va_end(ap); + errmsg[MAX_ERRMSG_LEN-1] = 0; + fprintf(stderr, "Error: %s\n", errmsg); +} + +/********************************************************************** +* %FUNCTION: random_init +* %ARGUMENTS: +* None +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Sets up random-number generator +***********************************************************************/ +void +l2tp_random_init(void) +{ + /* Prefer /dev/urandom; fall back on rand() */ + random_fd = open("/dev/urandom", O_RDONLY); + if (random_fd < 0) { + srand(time(NULL) + getpid()*getppid()); + } + +} + +/********************************************************************** +* %FUNCTION: bad random_fill +* %ARGUMENTS: +* ptr -- pointer to a buffer +* size -- size of buffer +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Fills buffer with "size" random bytes. This function is not +* cryptographically strong; it's used as a fallback for systems +* without /dev/urandom. +***********************************************************************/ +static void +bad_random_fill(void *ptr, size_t size) +{ + unsigned char *buf = ptr; + while(size--) { + *buf++ = rand() & 0xFF; + } +} + +/********************************************************************** +* %FUNCTION: random_fill +* %ARGUMENTS: +* ptr -- pointer to a buffer +* size -- size of buffer +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Fills buffer with "size" random bytes. +***********************************************************************/ +void +l2tp_random_fill(void *ptr, size_t size) +{ + int n; + int ndone = 0; + int nleft = size; + unsigned char *buf = ptr; + + if (random_fd < 0) { + bad_random_fill(ptr, size); + return; + } + + while(nleft) { + n = read(random_fd, buf+ndone, nleft); + if (n <= 0) { + close(random_fd); + random_fd = -1; + bad_random_fill(buf+ndone, nleft); + return; + } + nleft -= n; + ndone += n; + } +} + +void l2tp_die(void) +{ + fprintf(stderr, "FATAL: %s\n", errmsg); + l2tp_cleanup(); + exit(1); +} + +/********************************************************************** +* %FUNCTION: load_handler +* %ARGUMENTS: +* fname -- filename to load +* %RETURNS: +* -1 on error, 0 if OK +* %DESCRIPTION: +* Dynamically-loads a handler and initializes it. If fname is not +* an absolute path name, we load the handler from /usr/lib/l2tp/plugins +***********************************************************************/ +int +l2tp_load_handler(EventSelector *es, + char const *fname) +{ + char buf[1024]; + void *handle; + void *init; + void (*init_fn)(EventSelector *); + + if (*fname == '/') { + handle = dlopen(fname, RTLD_NOW); + } else { + snprintf(buf, sizeof(buf), "%s/lib/l2tp/%s", PREFIX, fname); //2005-04-14 by kanki + buf[sizeof(buf)-1] = 0; + handle = dlopen(buf, RTLD_NOW); + } + + if (!handle) { + l2tp_set_errmsg("Could not dload %s: %s", + fname, dlerror()); + return -1; + } + + init = dlsym(handle, "handler_init"); + if (!init) { + dlclose(handle); + l2tp_set_errmsg("No handler_init found in %s", fname); + return -1; + } + init_fn = (void (*)(EventSelector *)) init; + init_fn(es); + return 0; +} |