summaryrefslogtreecommitdiff
path: root/release/src/router/rp-l2tp
diff options
context:
space:
mode:
Diffstat (limited to 'release/src/router/rp-l2tp')
-rw-r--r--release/src/router/rp-l2tp/Makefile97
-rw-r--r--release/src/router/rp-l2tp/Makefile.in98
-rw-r--r--release/src/router/rp-l2tp/README35
-rw-r--r--release/src/router/rp-l2tp/auth.c54
-rw-r--r--release/src/router/rp-l2tp/config.cache43
-rw-r--r--release/src/router/rp-l2tp/config.log53
-rwxr-xr-xrelease/src/router/rp-l2tp/config.status163
-rwxr-xr-xrelease/src/router/rp-l2tp/configure1888
-rw-r--r--release/src/router/rp-l2tp/configure.in59
-rw-r--r--release/src/router/rp-l2tp/debug.c238
-rw-r--r--release/src/router/rp-l2tp/dgram.c1070
-rw-r--r--release/src/router/rp-l2tp/handlers/Makefile45
-rw-r--r--release/src/router/rp-l2tp/handlers/Makefile.in45
-rw-r--r--release/src/router/rp-l2tp/handlers/cmd-control.c108
-rw-r--r--release/src/router/rp-l2tp/handlers/cmd.c458
-rw-r--r--release/src/router/rp-l2tp/handlers/dstring.c139
-rw-r--r--release/src/router/rp-l2tp/handlers/dstring.h26
-rw-r--r--release/src/router/rp-l2tp/handlers/pty.c96
-rw-r--r--release/src/router/rp-l2tp/handlers/sync-pppd.c443
-rwxr-xr-xrelease/src/router/rp-l2tp/install-sh238
-rw-r--r--release/src/router/rp-l2tp/l2tp.conf38
-rw-r--r--release/src/router/rp-l2tp/l2tp.h484
-rw-r--r--release/src/router/rp-l2tp/libevent/Doc/flow.fig32
-rw-r--r--release/src/router/rp-l2tp/libevent/Doc/libevent.pdfbin0 -> 126661 bytes
-rw-r--r--release/src/router/rp-l2tp/libevent/Doc/libevent.tex549
-rw-r--r--release/src/router/rp-l2tp/libevent/Doc/style.tex139
-rw-r--r--release/src/router/rp-l2tp/libevent/Makefile40
-rw-r--r--release/src/router/rp-l2tp/libevent/Makefile.in41
-rw-r--r--release/src/router/rp-l2tp/libevent/event.c640
-rw-r--r--release/src/router/rp-l2tp/libevent/event.h114
-rw-r--r--release/src/router/rp-l2tp/libevent/event.obin0 -> 31512 bytes
-rw-r--r--release/src/router/rp-l2tp/libevent/event_sig.c265
-rw-r--r--release/src/router/rp-l2tp/libevent/event_sig.obin0 -> 25716 bytes
-rw-r--r--release/src/router/rp-l2tp/libevent/event_tcp.c581
-rw-r--r--release/src/router/rp-l2tp/libevent/event_tcp.h87
-rw-r--r--release/src/router/rp-l2tp/libevent/event_tcp.obin0 -> 31332 bytes
-rw-r--r--release/src/router/rp-l2tp/libevent/eventpriv.h46
-rw-r--r--release/src/router/rp-l2tp/libevent/hash.c266
-rw-r--r--release/src/router/rp-l2tp/libevent/hash.h54
-rw-r--r--release/src/router/rp-l2tp/libevent/hash.obin0 -> 15076 bytes
-rw-r--r--release/src/router/rp-l2tp/libevent/libevent.abin0 -> 104588 bytes
-rw-r--r--release/src/router/rp-l2tp/main.c124
-rwxr-xr-xrelease/src/router/rp-l2tp/make-release.sh30
-rw-r--r--release/src/router/rp-l2tp/man/l2tp.conf.5163
-rw-r--r--release/src/router/rp-l2tp/man/l2tpd.865
-rw-r--r--release/src/router/rp-l2tp/md5.c248
-rw-r--r--release/src/router/rp-l2tp/md5.h28
-rw-r--r--release/src/router/rp-l2tp/network.c136
-rw-r--r--release/src/router/rp-l2tp/options.c404
-rw-r--r--release/src/router/rp-l2tp/peer.c422
-rw-r--r--release/src/router/rp-l2tp/session.c855
-rw-r--r--release/src/router/rp-l2tp/tunnel.c1920
-rw-r--r--release/src/router/rp-l2tp/utils.c219
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
new file mode 100644
index 00000000..53d46821
--- /dev/null
+++ b/release/src/router/rp-l2tp/libevent/Doc/libevent.pdf
Binary files differ
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
new file mode 100644
index 00000000..d5a6e2da
--- /dev/null
+++ b/release/src/router/rp-l2tp/libevent/event.o
Binary files differ
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
new file mode 100644
index 00000000..f54cb297
--- /dev/null
+++ b/release/src/router/rp-l2tp/libevent/event_sig.o
Binary files differ
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
new file mode 100644
index 00000000..8a130463
--- /dev/null
+++ b/release/src/router/rp-l2tp/libevent/event_tcp.o
Binary files differ
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
new file mode 100644
index 00000000..5fb830bd
--- /dev/null
+++ b/release/src/router/rp-l2tp/libevent/hash.o
Binary files differ
diff --git a/release/src/router/rp-l2tp/libevent/libevent.a b/release/src/router/rp-l2tp/libevent/libevent.a
new file mode 100644
index 00000000..81034a83
--- /dev/null
+++ b/release/src/router/rp-l2tp/libevent/libevent.a
Binary files differ
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;
+}