[vpnc-devel] svn commit: vpnc r286 - /branches/vpnc-nortel/

vpnc at unix-ag.uni-kl.de vpnc at unix-ag.uni-kl.de
Wed Jun 11 01:19:11 CEST 2008


Author: Joerg Mayer
Date: Wed Jun 11 01:19:11 2008
New Revision: 286

Log:
Zingo Andersen <spamfilter at zingo.org>:

I have merged in latest from trunk to my vpnc-nortel setup and manage to get it usable
I tried to re-struct it back as much as possible to follow the trunk code base.
I tried to put all important difference behind a build flag ( NORTELVPN ) so that
the code for nortel and non nortel could coexist e.g. the branch could be removed.
( Well maybe after some more code cleanup. )

The latest fix on vpnc-nortel branch did breake my setup so I have put it
under another build flag ( NORTELVPN_XAUTHTYPE_AS_REQUEST ) until someone know
how to fix it.

I don't know anything about VPN stuff so my remaining difference might be done better
please let me know.

Next step would be better cleanup and if possible a runtime check instead of this
"#ifdef NORTELVPN" madness. The Idea was to bring it up to par with trunk so that
someone that knows the code could look at it merge it in easier or give more
instruction on what needs to be fixed.

Me:

Copy various missing files from trunk


Added:
    branches/vpnc-nortel/cisco-decrypt.c   (with props)
    branches/vpnc-nortel/decrypt-utils.c   (with props)
    branches/vpnc-nortel/decrypt-utils.h   (with props)
    branches/vpnc-nortel/enum2debug.pl   (with props)
    branches/vpnc-nortel/makeman.pl   (with props)
    branches/vpnc-nortel/mk-version   (with props)
    branches/vpnc-nortel/supp.c   (with props)
    branches/vpnc-nortel/supp.h   (with props)
    branches/vpnc-nortel/sysdep.c   (with props)
    branches/vpnc-nortel/tunip.h   (with props)
    branches/vpnc-nortel/vpnc-debug.c   (with props)
    branches/vpnc-nortel/vpnc-debug.h   (with props)
    branches/vpnc-nortel/vpnc-script.in   (with props)
    branches/vpnc-nortel/vpnc.8.template   (with props)
Removed:
    branches/vpnc-nortel/sysdep-bsd.c
    branches/vpnc-nortel/sysdep-linux.c
    branches/vpnc-nortel/sysdep-svr4.c
    branches/vpnc-nortel/vpnc-connect
    branches/vpnc-nortel/vpnc.8
Modified:
    branches/vpnc-nortel/   (props changed)
    branches/vpnc-nortel/COPYING
    branches/vpnc-nortel/Makefile
    branches/vpnc-nortel/README
    branches/vpnc-nortel/TODO
    branches/vpnc-nortel/VERSION
    branches/vpnc-nortel/config.c
    branches/vpnc-nortel/config.h
    branches/vpnc-nortel/isakmp-pkt.c
    branches/vpnc-nortel/isakmp-pkt.h
    branches/vpnc-nortel/isakmp.h
    branches/vpnc-nortel/math_group.c
    branches/vpnc-nortel/math_group.h
    branches/vpnc-nortel/nortel.txt
    branches/vpnc-nortel/pcf2vpnc
    branches/vpnc-nortel/sysdep.h
    branches/vpnc-nortel/tunip.c
    branches/vpnc-nortel/vpnc-disconnect
    branches/vpnc-nortel/vpnc.c
    branches/vpnc-nortel/vpnc.conf
    branches/vpnc-nortel/vpnc.h

Modified: branches/vpnc-nortel/COPYING
==============================================================================
--- branches/vpnc-nortel/COPYING (original)
+++ branches/vpnc-nortel/COPYING Wed Jun 11 01:19:11 2008
@@ -1,3 +1,9 @@
+This file contains two licenses:
+
+1) The GNU GENERAL PUBLIC LICENSE Version 2
+2) dh.[hc] and math_group.[hc] license
+
+-------------------------------------------------------------
 		    GNU GENERAL PUBLIC LICENSE
 		       Version 2, June 1991
 
@@ -338,3 +344,31 @@
 consider it more useful to permit linking proprietary applications with the
 library.  If this is what you want to do, use the GNU Library General
 Public License instead of this License.
+
+-------------------------------------------------------------
+
+Copyright (c) 1998 Niels Provos.  All rights reserved.
+Copyright (c) 1999 Niklas Hallqvist.  All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-------------------------------------------------------------
+

Modified: branches/vpnc-nortel/Makefile
==============================================================================
--- branches/vpnc-nortel/Makefile (original)
+++ branches/vpnc-nortel/Makefile Wed Jun 11 01:19:11 2008
@@ -1,6 +1,7 @@
 # Makefile for an IPSec VPN client compatible with Cisco equipment.
 # Copyright (C) 2002  Geoffrey Keating
 # Copyright (C) 2003-2004  Maurice Massar
+# Copyright (C) 2006-2007 Dan Villiom Podlaski Christiansen
 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -15,48 +16,75 @@
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+# $Id$
 
 DESTDIR=
 PREFIX=/usr/local
+ETCDIR=/etc/vpnc
+BINDIR=$(PREFIX)/bin
 SBINDIR=$(PREFIX)/sbin
 MANDIR=$(PREFIX)/share/man
+DOCDIR=$(PREFIX)/share/doc/vpnc
+
+SRCS = sysdep.c vpnc-debug.c isakmp-pkt.c tunip.c config.c dh.c math_group.c supp.c decrypt-utils.c
+BINS = vpnc cisco-decrypt
+OBJS = $(addsuffix .o,$(basename $(SRCS)))
+BINOBJS = $(addsuffix .o,$(BINS))
+BINSRCS = $(addsuffix .c,$(BINS))
+VERSION := $(shell sh mk-version)
+RELEASE_VERSION := $(shell cat VERSION)
+
+# The license of vpnc (Gpl >= 2) is quite likely incompatible with the
+# openssl license. Openssl is currently used to provide certificate
+# support for vpnc (hybrid only).
+# While it is OK for users to build their own binaries linking in openssl
+# with vpnc and even providing dynamically linked binaries it is probably
+# not OK to provide the binaries inside a distribution.
+# See http://www.gnome.org/~markmc/openssl-and-the-gpl.html for further
+# details.
+# Some distributions like Suse and Fedora seem to think otherwise.
+
+# Comment this in to obtain a binary with certificate support which is
+# GPL incompliant though.
+#OPENSSL_GPL_VIOLATION = -DOPENSSL_GPL_VIOLATION
+#OPENSSLLIBS = -lcrypto
 
 CC=gcc
-CFLAGS=-W -Wall -O -g '-DVERSION="$(shell cat VERSION)"' $(shell libgcrypt-config --cflags)
-LDFLAGS=-g $(shell libgcrypt-config --libs)
+CFLAGS ?= -O3 -g -DNORTELVPN
+CFLAGS += -W -Wall -Wmissing-declarations -Wwrite-strings
+CFLAGS +=  $(shell libgcrypt-config --cflags)
+CPPFLAGS += -DVERSION=\"$(VERSION)\" $(OPENSSL_GPL_VIOLATION)
+LDFLAGS ?= -g
+LDFLAGS += $(shell libgcrypt-config --libs) $(OPENSSLLIBS)
 
-ifeq ($(shell uname -s), Linux)
-SYSDEP=sysdep-linux.o
+ifeq ($(shell uname -s), SunOS)
+LDFLAGS += -lnsl -lresolv -lsocket
 endif
-ifeq ($(shell uname -s), FreeBSD)
-CFLAGS += -DSOCKADDR_IN_SIN_LEN -DHAVE_SA_LEN
-SYSDEP=sysdep-bsd.o
-endif
-ifeq ($(shell uname -s), NetBSD)
-CFLAGS += -DSOCKADDR_IN_SIN_LEN -DHAVE_SA_LEN
-SYSDEP=sysdep-bsd.o
-endif
-ifeq ($(shell uname -s), OpenBSD)
-CFLAGS += -DSOCKADDR_IN_SIN_LEN -DHAVE_SA_LEN -DNEED_IPLEN_FIX -DNEW_TUN
-SYSDEP=sysdep-bsd.o
-endif
-ifeq ($(shell uname -s), SunOS)
-CFLAGS += -DNEED_IPLEN_FIX
-LDFLAGS += -lnsl -lresolv -lsocket
-SYSDEP=sysdep-svr4.o
+ifneq (,$(findstring Apple,$(shell $(CC) --version)))
+# enabled in FSF GCC, disabled by default in Apple GCC
+CFLAGS += -fstrict-aliasing -freorder-blocks -fsched-interblock
 endif
 
-FILELIST := $(shell echo *.c *.h vpnc-*) Makefile README ChangeLog COPYING TODO VERSION vpnc.conf vpnc.8 pcf2vpnc
+all : $(BINS) vpnc.8 vpnc-script
 
-vpnc : vpnc.o isakmp-pkt.o tunip.o config.o $(SYSDEP) dh.o math_group.o
+vpnc : $(OBJS) vpnc.o
 	$(CC) -o $@ $^ $(LDFLAGS)
 
-vpnc.o : isakmp.h isakmp-pkt.h dh.h sysdep.h math_group.h config.h VERSION
-isakmp-pkt.o : isakmp.h isakmp-pkt.h config.h
-tunip.o : sysdep.h vpnc.h config.h
-config.o : vpnc.h config.h VERSION
-dh.o : dh.h math_group.h
-math_group.o : math_group.h
+vpnc.8 : vpnc.8.template makeman.pl vpnc
+	./makeman.pl
+
+vpnc-script : vpnc-script.in
+	sed -e 's,@''PREFIX''@,$(PREFIX),g' $< > $@ && chmod 755 $@
+
+cisco-decrypt : cisco-decrypt.o decrypt-utils.o
+	$(CC) -o $@ $^ $(LDFLAGS)
+
+.depend: $(SRCS) $(BINSRCS)
+	$(CC) -MM $(SRCS) $(BINSRCS) $(CFLAGS) $(CPPFLAGS) > $@
+
+vpnc-debug.c vpnc-debug.h : isakmp.h enum2debug.pl
+	LC_ALL=C perl -w ./enum2debug.pl isakmp.h >vpnc-debug.c 2>vpnc-debug.h
 
 vpnc.ps : vpnc.c
 	enscript -E -G -T 4 --word-wrap -o- $^ | psnup -2 /dev/stdin $@
@@ -68,35 +96,55 @@
 ctags :
 	ctags *.[ch]
 
-vpnc-%.tar.gz : $(FILELIST)
+vpnc-%.tar.gz :
 	mkdir vpnc-$*
-	cp -al $(FILELIST) vpnc-$*/
-	tar zcf ../$@ vpnc-$*
+	LC_ALL=C svn info -R | awk -v RS='' -v FS='\n' '/Node Kind: file/ {print substr($$1,7)}' | \
+		tar -cf - -T - | tar -xf - -C vpnc-$*/
+	tar -czf ../$@ vpnc-$*
 	rm -rf vpnc-$*
 
-dist : VERSION vpnc-$(shell cat VERSION).tar.gz
+dist : VERSION vpnc.8 vpnc-$(RELEASE_VERSION).tar.gz
 
 clean :
-	-rm -f vpnc *.o tags
+	-rm -f $(OBJS) $(BINOBJS) $(BINS) tags
 
-all : vpnc
+distclean : clean
+	-rm -f vpnc-debug.c vpnc-debug.h vpnc.ps vpnc.8 .depend
 
-install :
-	install -d $(DESTDIR)$(SBINDIR) $(DESTDIR)$(MANDIR)/man8
-	install vpnc vpnc-connect vpnc-disconnect $(DESTDIR)$(SBINDIR)
-	install vpnc.8 $(DESTDIR)$(MANDIR)/man8
+install-common: all
+	install -d $(DESTDIR)$(ETCDIR) $(DESTDIR)$(BINDIR) $(DESTDIR)$(SBINDIR) $(DESTDIR)$(MANDIR)/man1 $(DESTDIR)$(MANDIR)/man8 $(DESTDIR)$(DOCDIR)
+	if [ "`uname -s | cut -c-6`" = "CYGWIN" ]; then \
+		install vpnc-script-win $(DESTDIR)$(ETCDIR)/vpnc-script; \
+		install vpnc-script-win.js $(DESTDIR)$(ETCDIR); \
+	else \
+		install vpnc-script $(DESTDIR)$(ETCDIR); \
+	fi
+	install -m600 vpnc.conf $(DESTDIR)$(ETCDIR)/default.conf
+	install -m755 vpnc-disconnect $(DESTDIR)$(SBINDIR)
+	install -m755 pcf2vpnc $(DESTDIR)$(BINDIR)
+	install -m644 vpnc.8 $(DESTDIR)$(MANDIR)/man8
+	install -m644 pcf2vpnc.1 $(DESTDIR)$(MANDIR)/man1
+	install -m644 cisco-decrypt.1 $(DESTDIR)$(MANDIR)/man1
+	install -m644 COPYING $(DESTDIR)$(DOCDIR)
 
-install-strip :
-	install -d $(DESTDIR)$(SBINDIR) $(DESTDIR)$(MANDIR)/man8
-	install -s vpnc $(DESTDIR)$(SBINDIR)
-	install vpnc-connect vpnc-disconnect $(DESTDIR)$(SBINDIR)
-	install vpnc.8 $(DESTDIR)$(MANDIR)/man8
+install : install-common
+	install -m755 vpnc $(DESTDIR)$(SBINDIR)
+	install -m755 cisco-decrypt $(DESTDIR)$(BINDIR)
+
+install-strip : install-common
+	install -s -m755 vpnc $(DESTDIR)$(SBINDIR)
+	install -s -m755 cisco-decrypt $(DESTDIR)$(BINDIR)
 
 uninstall :
-	rm -f $(DESTDIR)$(SBINDIR)/vpnc $(DESTDIR)$(SBINDIR)/vpnc-connect \
+	rm -f $(DESTDIR)$(SBINDIR)/vpnc \
 		$(DESTDIR)$(SBINDIR)/vpnc-disconnect \
+		$(DESTDIR)$(BINDIR)/pcf2vpnc \
+		$(DESTDIR)$(BINDIR)/cisco-decrypt \
+		$(DESTDIR)$(MANDIR)/man1/cisco-decrypt.1 \
+		$(DESTDIR)$(MANDIR)/man1/pcf2vpnc \
 		$(DESTDIR)$(MANDIR)/man8/vpnc.8
+	@echo NOTE: remove $(DESTDIR)$(ETCDIR) manually
 
-.PHONY : clean dist all install install-strip uninstall
+.PHONY : clean distclean dist all install install-strip uninstall
 
 #

Modified: branches/vpnc-nortel/README
==============================================================================
--- branches/vpnc-nortel/README (original)
+++ branches/vpnc-nortel/README Wed Jun 11 01:19:11 2008
@@ -8,14 +8,31 @@
 It runs entirely in userspace. Only "Universal TUN/TAP device
 driver support" is needed in kernel.
 
+Project home page: http://www.unix-ag.uni-kl.de/~massar/vpnc/
+
+
+========= Contents of this file ============================================
+
+
+- Gereral configuration of vpnc 
+- Using a modified script
+- Additional steps to configure hybrid authentication
+- Setting up vpnc on Vista 64bit
+- Known problems
+
+
+========= General configuration of vpnc ====================================
+
+
 Required Libraries: libgcrypt (version 1.1.90 for 0.2-rm+zomb-pre7 or later)
+                    libopenssl (optional, to provide hybrid support)
 
 It reads configuration data from the following places:
 
 - From command-line options
 - From config file(s) specified on the command line
-- From /etc/vpnc/default.conf
-- From /etc/vpnc.conf
+- From /etc/vpnc/default.conf  only if no configfile was given on the command line
+- From /etc/vpnc.conf          same as default.conf, ie: both are used, or none
 - If a setting is not given in any of those places, it prompts the user.
 
 The configuration information it currently needs is:
@@ -37,7 +54,7 @@
 
 Note that all strings start exactly one space after the keyword
 string, and run to the end of the line.  This lets you put any kind of
-weird character (except EOL and NUL) in your strings, but it does mean
+weird character (except CR, LF and NUL) in your strings, but it does mean
 you can't add comments after a string, or spaces before them.
 
 It may be easier to use the --print-config option to generate the
@@ -48,26 +65,169 @@
 administrator. If (s)he declines and refers to the 
 configuration files provided for the vpnclient program, tell 
 him/her that the contents of that files is (though scrambled) 
-not really protected and can be extracted using tools shipped 
-with any Linux distribution.
-
-Please note that vpnc does NOT setup routing. You need to do this
-yourself, or --script resp. "Config Script" in the config file.
-The default script depends on the platform. For Linux it is currently:
-
-ifconfig $TUNDEV inet $INTERNAL_IP4_ADDRESS \
-                 pointopoint $INTERNAL_IP4_ADDRESS \
-                 netmask 255.255.255.255 mtu 1412 up
+not really protected. If you have a working configuration file
+(.pcf file) for the Cisco client then you can use the pcf2vpnc
+utility instead, which will extract most/all of the required
+information and convert it into a vpnc configuration file.
+
+
+========= Using a modified script ==========================================
+
+
+Please note that vpnc itself does NOT setup routing. You need to do this
+yourself, or use --script "Script" in the config file.
+The default script is /etc/vpnc/vpnc-script which sets a default route
+to the remote network, or if the Concentrator provided split-network
+settings, these are used to setup routes.
 
 This option is passed to system(), so you can use any shell-specials you
-like. Information is passed from vpnc via enviroment variables:
-#* VPNGATEWAY             -- vpn gateway address (always present)
-#* TUNDEV                 -- tunnel device (always present)
-#* INTERNAL_IP4_ADDRESS   -- address (always present)
-#* INTERNAL_IP4_NETMASK   -- netmask (often unset)
-#* INTERNAL_IP4_DNS       -- list of dns serverss
-#* INTERNAL_IP4_NBNS      -- list of wins servers
-#* CISCO_DEF_DOMAIN       -- default domain name
-#* CISCO_BANNER           -- banner from server
-
-An example for a such a script is vpnc-connect.
+like. This script gets called three times:
+$reason == pre-init: this is befor vpnc opens the tun device
+   so you can do what is neccessary to ensure that it is available.
+   Note that none of the variables mentioned below is available
+$reason == connect: this is what used to be "Config Script".
+   The connection is established, but vpnc will not begin forwarding
+   pakets until the script finishs.
+$reason == disconnect: This is called just after vpnc received a signal.
+   Note that vpnc will not forward packets anymore while the script is
+   running or therafter.
+
+Information is passed from vpnc via enviroment variables:
+
+#* reason                       -- why this script was called, one of: pre-init connect disconnect
+#* VPNGATEWAY                   -- vpn gateway address (always present)
+#* TUNDEV                       -- tunnel device (always present)
+#* INTERNAL_IP4_ADDRESS         -- address (always present)
+#* INTERNAL_IP4_NETMASK         -- netmask (often unset)
+#* INTERNAL_IP4_DNS             -- list of dns serverss
+#* INTERNAL_IP4_NBNS            -- list of wins servers
+#* CISCO_DEF_DOMAIN             -- default domain name
+#* CISCO_BANNER                 -- banner from server
+#* CISCO_SPLIT_INC              -- number of networks in split-network-list
+#* CISCO_SPLIT_INC_%d_ADDR      -- network address
+#* CISCO_SPLIT_INC_%d_MASK      -- subnet mask (for example: 255.255.255.0)
+#* CISCO_SPLIT_INC_%d_MASKLEN   -- subnet masklen (for example: 24)
+#* CISCO_SPLIT_INC_%d_PROTOCOL  -- protocol (often just 0)
+#* CISCO_SPLIT_INC_%d_SPORT     -- source port (often just 0)
+#* CISCO_SPLIT_INC_%d_DPORT     -- destination port (often just 0)
+
+Currently vpnc-script is not directly configurable from configfiles.
+However, a workaround is to use a "wrapper-script" like this, to
+disable /etc/resolv.conf rewriting and setup a custom split-routing:
+
+------------------------------
+#!/bin/sh
+
+# this effectively disables changes to /etc/resolv.conf
+INTERNAL_IP4_DNS=
+
+# This sets up split networking regardless
+# of the concentrators specifications.
+# You can add as many routes as you want,
+# but you must set the counter $CISCO_SPLIT_INC
+# accordingly
+CISCO_SPLIT_INC=1
+CISCO_SPLIT_INC_0_ADDR=131.246.89.7
+CISCO_SPLIT_INC_0_MASK=255.255.255.255
+CISCO_SPLIT_INC_0_MASKLEN=32
+CISCO_SPLIT_INC_0_PROTOCOL=0
+CISCO_SPLIT_INC_0_SPORT=0
+CISCO_SPLIT_INC_0_DPORT=0
+
+. /etc/vpnc/vpnc-script
+------------------------------
+
+Store this example script, for example in /etc/vpnc/custom-script,
+do a "chmod +x /etc/vpnc/custom-script" and add
+"Script /etc/vpnc/custom-script" to your configuration.
+
+
+========= Additional steps to configure hybrid authentication ==============
+
+
+To use the hybrid extension add
+	Use Hybrid Auth
+to your .conf file or add
+	--hybrid
+when starting vpnc.
+
+The trusted root certificate may be passed by adding
+	CA-File <root_certificate.pem>
+to your .conf file or adding
+	--ca-file <root_certificate.pem>
+when starting vpnc.
+
+The trusted root certificate may be contained in a directory by adding
+	CA-Dir <trusted_certificate_directory>
+to your .conf file or adding
+	--ca-dir <trusted_certificate_directory>
+when starting vpnc.
+The default is 
+	/etc/ssl
+
+As the trusted certificate is referenced by the hash of the subject name, 
+the directory has to contain the certificate named like this hash_value. 
+A link can also be used like in /etc/ssl/Certs.
+The hash value can be calculated by e.g.
+	openssl x509 -in <ca_certfile.pem> -noout -hash
+
+
+========= Setting up vpnc on Vista 64bit ===================================
+
+
+1. Install cygwin onto vista.  Details here: http://www.cygwin.com/
+2. Make sure you install the development options for cygwin to give you
+   access to make and gcc etc
+3. Make sure you install libcrypt for cygwin as it is needed in the make
+4. Modify the bash.exe to run as administrator or you will have
+   privilege issues later, this is done on the properties tab of the
+   executable in c:/cygwin/bin
+4. Download the latest vpnc tarball from here
+   http://www.unix-ag.uni-kl.de/~massar/vpnc/
+5. Unzip and explode the tarball
+6. modify tap-win32.h to change #define TAP_COMPONENT_ID "tap0801" to
+   "tap0901" (No sure if this is necesary but I did it and it is working
+   for me)
+7. make
+8. You should have a shinny new vpnc.exe
+9. Download openvpn from http://openvpn.net/download.html.  I used
+   openvpn-2.1_rc4-install.exe as all other version I tried had errors
+   during install
+10. Run the exe but only install the TAP-Win32 Adapter V9
+11. Go to control Panel | Network Connections and rename the TAP device
+    to my-tap
+12. create a /etc/vpnc/default.conf file something like this
+------------- begin -------------
+IPSec gateway YOURGATEWAY
+IPSec ID YOURID
+IPSec obfuscated secret YOURREALYLONGHEXVALUE (you can use your clear
+text password here if you remove obfuscated)
+Xauth username YOURUSERNAME
+Xauth password YOURPASSWORD
+Interface name my-tap
+Interface mode tap
+Local Port 0
+------------- end ---------------
+    See the general config section above and the manpage for details.
+
+
+========= Known problems ===================================================
+
+
+Known problems:
+
+Problem:
+In some environments it may happen that stuff works for a while and then
+stops working.
+
+Reason:
+The dhcp leases are very short intervals and on each renew the dhcp
+client overwrites things like /etc/resolv.conf and maybe the default route.
+
+Solution:
+Fix your dhcpclient. On Debian that problem can be fixed by installing
+and using resolvconf to modify that file instead of modifying it directly.
+
+
+============================================================================
+

Modified: branches/vpnc-nortel/TODO
==============================================================================
--- branches/vpnc-nortel/TODO (original)
+++ branches/vpnc-nortel/TODO Wed Jun 11 01:19:11 2008
@@ -1,85 +1,59 @@
 TODO list
 
-* DONE: implement ISAKMP and IPSEC SA negotiate support
+* clean up scripts
+  - config-support for vpnc-script
+  - customizable handling of routing
+  - switch to disable resolv.conf rewriting
+  - do $something with split_dns
 
-* DONE: fix delete message
+* beautify paket dump output
 
-* DONE --non-interactive
+* large code cleanup
+  - at least one function per packet (instead of one function per phase)
+  - factor out a central select-loop, send / receive code, nat-t handling
+  - maybe even add some sort of state machine
+  - get a rid of remaining (non-const) global variables
 
-* DONE --pid-file
+* implement phase1 rekeying (with or without xauth-reauthentication)
+* implement compression
+* try a list of gateways (backup server)
+* Generate the manpage command line part directly from vpnc
 
-* DONE VERSION
+* optionally use in-kernel-ipsec with pf-key
+  - merge patch
 
-* DONE don't ignore all notifies at ipsec-sa-negotation
+* add support for pcap and dump decrypted traffic
 
-* DONE hide user/pass from --debug output
+* research/bugs:
+  - usernames containing "@" unable to login
+  - ipsec over tcp
+  - nortel support?
+  - segfault if > 100 routes/acls (to large paket? read size?)
+    (probably "fixed" by increasing the size in r_packet in vpnc.c,
+    but why did it crash?)
+  - amd64 somehow broken? maybe gcc bugs??
+  - some debug prints get the endianess wrong
+  - In case the psk in hybrid isn't correct, the server sends annother AM_2
+    packet - to port 500 of course, even if we are using nat-t and talked on
+    4500 already. We currently don't handle that.
 
-* DONE --verbose
+* optional drop root (rekey? reconnect? vpnc-script calls?)
+  - Don't drop privileges, ever, but allow to be run suid.
+  - If euid != ruid, clear out env on program start.
+  - Sanitize variables for vpnc-script (snarf code from
+    callscript.c from dhcpclient).
+  - If euid != ruid, disable command line options (but not the profile
+    parameter).
+  - If euid != ruid, treat profiles as filenames only. They must not
+    be paths, i.e. contain PATHSEP. Read them relative to /etc/vpnc.
+  - Make sure vpnc-disconnect only kills processes owned by same user.
 
-* DONE fix vpnc-disconnect
-
-* DONE NetBSD supported
-
-* DONE FreeBSD supported
-
-* DONE ignore attr 32136! (Cisco extension: XAUTH Vendor)
-
-* DONE ignore "metric10 xx"
-
-* DONE make /var/run/vpnc as needed
-
-* DONE support for new libgcrypt versions
-
-* DONE passcode == password
-
-* DONE post link to http://www.liebchen-online.de/vpn-zaurus.html
-
-* DONE post rfcs and drafts
-
-* DONE include man-page
-
-* DONE fix link at alioth
-
-* DONE memleak fix from Sebastian Biallas
-
-* DONE include OpenBSD support from Nikolay Sturm
-
-* DONE load balancing support (fixes INVALID_EXCHANGE_TYPE in S4.5)
-
-* DONE accept (== ignore) lifetime update in phase1
-
-* DONE send lifetime in phase1 and phase2
-
-* DONE send version string
-
-* DONE automatic handling of pfs
-
-* DONE ask for dns/wins servers, default domain, pfs setting, netmask
-
-* DONE spawn post-connect script
-
-* DONE check /dev/net/tun, reject /dev/tun* on linux
-
-***
-
-* DONE XAUTH Domain: (empty)
-
-* DONE svn-Repository
-
-* DONE implement udp encap via port 10.000
-  - apply patch
-
-* DONE fix Makefile (install, DESTDIR, CFLAGS, ...)
-
-* DONE implement udp transport NAT-T
-  - apply patch
-
-* links to packages, howtos, etc.
-  - http://localhost.ruhr.de/~stefan/uni-duisburg.ai/vpnc.shtml
-  - link to kvpnc?
-
-* add macosx support
-  - add pointer to http://chrisp.de/en/projects/tunnel.html
+* implement certificate support
+* implement dsa certificates in hybrid mode
+* Adapt lifetime (when given as time) to certificate lifetime etc
+  (rfc2401, 4.4.3)
+* implement main mode for phase 1 (needed to *use* certificates in
+  many cases)
 
 * factor out crypto stuff (cipher, hmac, dh)
   - http://libtomcrypt.org/features.html
@@ -87,36 +61,56 @@
   - libgcrypt (old too?)
   - autodetect?
   - openssl??
-* relicense to gpl+ssl?
+  - relicense to gpl+ssl?
 
-* --local-address
+* links to packages, howtos, etc.
+  - kvpnc http://home.gna.org/kvpnc/
+  - vpnc+Zaurus http://users.ox.ac.uk/~oliver/vpnc.html
+  - linux-mipsel (WRT54G) http://openwrt.alphacore.net/vpnc_0.3.2_mipsel.ipk
+  - howto-de http://localhost.ruhr.de/~stefan/uni-duisburg.ai/vpnc.shtml
 
-* clean up scripts
+----
 
-* update "check pfs setting" error message
-
-* better handling of routing
-
-* better handling of resolv.conf?
-
-* use in-kernel-ipsec with pf-key
-  - apply patch
-
-* make doing xauth optional
-
-* add support for pcap for packet capture
-
---
-
-* implement compression
-
-* implement rekeying / DPD / frozen connections?
-
-* implement certificate support
-
-* implement hybrid-auth
-
-* optional drop root (rekey? reconnect?)
-
-* nortel support?
-
+* DONE implement hybrid-auth
+* DONE implement DPD, RFC 3706 Dead Peer Detection
+* DONE --local-address
+* DONE implement phase2 rekeying
+* DONE support rsa-SecurID token which sometimes needs 2 IDs
+* DONE add macosx support
+* DONE update "check pfs setting" error message
+* DONE make doing xauth optional
+* DONE implement udp transport NAT-T
+* DONE fix Makefile (install, DESTDIR, CFLAGS, ...)
+* DONE implement udp encap via port 10.000
+* DONE svn-Repository
+* DONE XAUTH Domain: (empty)
+* DONE check /dev/net/tun, reject /dev/tun* on linux
+* DONE spawn post-connect script
+* DONE ask for dns/wins servers, default domain, pfs setting, netmask
+* DONE automatic handling of pfs
+* DONE send version string
+* DONE send lifetime in phase1 and phase2
+* DONE accept (== ignore) lifetime update in phase1
+* DONE load balancing support (fixes INVALID_EXCHANGE_TYPE in S4.5)
+* DONE include OpenBSD support from Nikolay Sturm
+* DONE memleak fix from Sebastian Biallas
+* DONE fix link at alioth
+* DONE include man-page
+* DONE post rfcs and drafts
+* DONE post link to http://www.liebchen-online.de/vpn-zaurus.html
+* DONE passcode == password
+* DONE support for new libgcrypt versions
+* DONE make /var/run/vpnc as needed
+* DONE ignore "metric10 xx"
+* DONE ignore attr 32136! (Cisco extension: XAUTH Vendor)
+* DONE FreeBSD supported
+* DONE NetBSD supported
+* DONE fix vpnc-disconnect
+* DONE --verbose
+* DONE hide user/pass from --debug output
+* DONE don't ignore all notifies at ipsec-sa-negotation
+* DONE VERSION
+* DONE --pid-file
+* DONE --non-interactive
+* DONE fix delete message
+* DONE implement ISAKMP and IPSEC SA negotiate support

Modified: branches/vpnc-nortel/VERSION
==============================================================================
--- branches/vpnc-nortel/VERSION (original)
+++ branches/vpnc-nortel/VERSION Wed Jun 11 01:19:11 2008
@@ -1,1 +1,1 @@
-0.3.2
+0.5.1

Added: branches/vpnc-nortel/cisco-decrypt.c
==============================================================================
--- branches/vpnc-nortel/cisco-decrypt.c (added)
+++ branches/vpnc-nortel/cisco-decrypt.c Wed Jun 11 01:19:11 2008
@@ -1,0 +1,64 @@
+/* Decoder for password encoding of Cisco VPN client.
+   Copyright (C) 2005 Maurice Massar
+   
+   Thanks to HAL-9000 at evilscientists.de for decoding and posting the algorithm!
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "decrypt-utils.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <gcrypt.h>
+#include <errno.h>
+
+int main(int argc, char *argv[])
+{
+	int i, len, ret = 0;
+	char *bin, *pw = NULL;
+	
+	gcry_check_version(NULL);
+
+	if (argc == 1 || *argv[1] == '-') {
+		fprintf(stderr,
+			"\nUsage: %s DEADBEEF...012345678 424242...7261\n"
+			"    Print decoded result to stdout\n\n",
+			argv[0]);
+		exit(1);
+	}
+	/* Hack for use in pcf2vpnc */
+	if (*argv[1] == 'q') {
+		exit(1);
+	}
+	
+	for (i = 1; i < argc; i++) {
+		ret = hex2bin(argv[i], &bin, &len);
+		if (ret != 0) {
+			perror("decoding input");
+			continue;
+		}
+		ret = deobfuscate(bin, len, (const char **)&pw, NULL);
+		free(bin);
+		if (ret != 0) {
+			perror("decrypting input");
+			continue;
+		}
+		printf("%s\n", pw);
+		free(pw);
+	}
+	
+	exit(ret != 0);
+}

Modified: branches/vpnc-nortel/config.c
==============================================================================
--- branches/vpnc-nortel/config.c (original)
+++ branches/vpnc-nortel/config.c Wed Jun 11 01:19:11 2008
@@ -1,5 +1,5 @@
 /* IPSec VPN client compatible with Cisco equipment.
-   Copyright (C) 2004 Maurice Massar
+   Copyright (C) 2004-2005 Maurice Massar
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -14,6 +14,8 @@
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   $Id$
 */
 
 #define _GNU_SOURCE
@@ -26,61 +28,53 @@
 #include <errno.h>
 #include <sys/utsname.h>
 
+#include <gcrypt.h>
+
 #include "sysdep.h"
 #include "config.h"
 #include "vpnc.h"
-
-/*
-#include <assert.h>
-#include <sys/fcntl.h>
-#include <time.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netdb.h>
-#include <arpa/inet.h>
-#include <poll.h>
-#include <net/if.h>
-#include <sys/ioctl.h>
-
-#include <gcrypt.h>
-
-#include "isakmp-pkt.h"
-#include "math_group.h"
-#include "dh.h"
-*/
+#include "supp.h"
+#include "decrypt-utils.h"
 
 const char *config[LAST_CONFIG];
 
 int opt_debug = 0;
 int opt_nd;
-int opt_1des;
-int opt_udpencap;
+int opt_1des, opt_no_encryption, opt_auth_mode;
+enum natt_mode_enum opt_natt_mode;
+enum vendor_enum opt_vendor;
+enum if_mode_enum opt_if_mode;
 uint16_t opt_udpencapport;
 
-void hex_dump(const char *str, const void *data, ssize_t len)
+void hex_dump(const char *str, const void *data, ssize_t len, const struct debug_strings *decode)
 {
 	size_t i;
 	const uint8_t *p = data;
+	const char *decodedval;
 
 	if (opt_debug < 3)
 		return;
 
+	printf("   ");
 	switch (len) {
-	case UINT8:
-		printf("%s: %02x\n", str, *(uint8_t *)p);
+	case DUMP_UINT8:
+		decodedval = val_to_string(*(uint8_t *)p, decode);
+		printf("%s: %02x%s\n", str, *(uint8_t *)p, decodedval);
 		return;
-	case UINT16:
-		printf("%s: %04x\n", str, *(uint16_t *)p);
+	case DUMP_UINT16:
+		decodedval = val_to_string(*(uint16_t *)p, decode);
+		printf("%s: %04x%s\n", str, *(uint16_t *)p, decodedval);
 		return;
-	case UINT32:
-		printf("%s: %08x\n", str, *(uint32_t *)p);
+	case DUMP_UINT32:
+		decodedval = val_to_string(*(uint32_t *)p, decode);
+		printf("%s: %08x%s\n", str, *(uint32_t *)p, decodedval);
 		return;
 	}
 
-	printf("%s:%c", str, (len <= 32) ? ' ' : '\n');
+	printf("%s:%s", str, (len <= 16) ? " " : "\n   ");
 	for (i = 0; i < (size_t)len; i++) {
 		if (i && !(i % 32))
-			printf("\n");
+			printf("\n   ");
 		else if (i && !(i % 4))
 			printf(" ");
 		printf("%02x", p[i]);
@@ -88,9 +82,33 @@
 	printf("\n");
 }
 
-static const char *config_def_description(void)
-{
-	return "default value for this option";
+static void config_deobfuscate(int obfuscated, int clear)
+{
+	int ret, len = 0;
+	char *bin = NULL;
+	
+	if (config[obfuscated] == NULL)
+		return;
+	
+	if (config[clear] != NULL) {
+		config[obfuscated] = NULL;
+		error(0, 0, "warning: ignoring obfuscated password because cleartext password set");
+		return;
+	}
+	
+	ret = hex2bin(config[obfuscated], &bin, &len);
+	if (ret != 0) {
+		error(1, 0, "error: deobfuscating of password failed (input not a hex string)");
+	}
+	
+	ret = deobfuscate(bin, len, config+clear, NULL);
+	free(bin);
+	if (ret != 0) {
+		error(1, 0, "error: deobfuscating of password failed");
+	}
+	
+	config[obfuscated] = NULL;
+	return;
 }
 
 static const char *config_def_ike_dh(void)
@@ -103,14 +121,44 @@
 	return "server";
 }
 
+static const char *config_def_local_addr(void)
+{
+	return "0.0.0.0";
+}
+
 static const char *config_def_local_port(void)
 {
 	return "500";
 }
 
+static const char *config_def_if_mode(void)
+{
+	return "tun";
+}
+
+static const char *config_def_natt_mode(void)
+{
+	return "natt";
+}
+
 static const char *config_def_udp_port(void)
 {
 	return "10000";
+}
+
+static const char *config_def_dpd_idle(void)
+{
+	return "300";
+}
+
+static const char *config_ca_dir(void)
+{
+	return "/etc/ssl/certs";
+}
+
+static const char *config_def_auth_mode(void)
+{
+	return "psk";
 }
 
 static const char *config_def_app_version(void)
@@ -123,10 +171,30 @@
 	return version;
 }
 
+static const char *config_def_script(void)
+{
+	return "/etc/vpnc/vpnc-script";
+}
+
+static const char *config_def_pid_file(void)
+{
+	return "/var/run/vpnc/pid";
+}
+
+static const char *config_def_vendor(void)
+{
+	return "cisco";
+}
+
+static const char *config_def_target_network(void)
+{
+	return "0.0.0.0/0.0.0.0";
+}
+
 static const struct config_names_s {
 	enum config_enum nm;
 	const int needsArgument;
-	const int lvl;
+	const int long_only;
 	const char *option;
 	const char *name;
 	const char *type;
@@ -138,13 +206,6 @@
 	 * fix the parser to care about ' ' or '\t' after the wanted
 	 * option... */
 	{
-		CONFIG_NONE, 0, 0,
-		"commandline option,",
-		"configfile variable, ",
-		"argument type",
-		"description",
-		config_def_description
-	}, {
 		CONFIG_IPSEC_GATEWAY, 1, 0,
 		"--gateway",
 		"IPSec gateway ",
@@ -163,7 +224,14 @@
 		NULL,
 		"IPSec secret ",
 		"<ASCII string>",
-		"your group password (cleartext, no support for obfuscated strings)",
+		"your group password (cleartext)",
+		NULL
+	}, {
+		CONFIG_IPSEC_SECRET_OBF, 1, 1,
+		NULL,
+		"IPSec obfuscated secret ",
+		"<hex string>",
+		"your group password (obfuscated)",
 		NULL
 	}, {
 		CONFIG_XAUTH_USERNAME, 1, 0,
@@ -177,14 +245,14 @@
 		NULL,
 		"Xauth password ",
 		"<ASCII string>",
-		"your password (cleartext, no support for obfuscated strings)",
-		NULL
-	}, {
-		CONFIG_UDP_ENCAP, 0, 0,
-		"--udp",
-		"UDP Encapsulate",
+		"your password (cleartext)",
+		NULL
+	}, {
+		CONFIG_XAUTH_PASSWORD_OBF, 1, 1,
 		NULL,
-		"Use Cisco-UDP encapsulation of IPSEC traffic",
+		"Xauth obfuscated password ",
+		"<hex string>",
+		"your password (obfuscated)",
 		NULL
 	}, {
 		CONFIG_DOMAIN, 1, 1,
@@ -198,18 +266,40 @@
 		"--xauth-inter",
 		"Xauth interactive",
 		NULL,
-		"enable interactive extended authentication (for challange response auth)",
-		NULL
-	}, {
-		CONFIG_CONFIG_SCRIPT, 1, 1,
+		"enable interactive extended authentication (for challenge response auth)",
+		NULL
+	}, {
+		CONFIG_VENDOR, 1, 1,
+		"--vendor",
+		"Vendor ",
+		"<cisco/netscreen>",
+		"vendor of your IPSec gateway",
+		config_def_vendor
+	}, {
+		CONFIG_NATT_MODE, 1, 1,
+		"--natt-mode",
+		"NAT Traversal Mode ",
+		"<natt/none/force-natt/cisco-udp>",
+		"Which NAT-Traversal Method to use:\n"
+		" * natt -- NAT-T as defined in RFC3947\n"
+		" * none -- disable use of any NAT-T method\n"
+		" * force-natt -- always use NAT-T encapsulation even\n"
+		"                 without presence of a NAT device\n"
+		"                 (useful if the OS captures all ESP traffic)\n"
+		" * cisco-udp -- Cisco proprietary UDP encapsulation, commonly over Port 10000\n"
+		"Note: cisco-tcp encapsulation is not yet supported\n",
+		config_def_natt_mode
+	}, {
+		CONFIG_SCRIPT, 1, 1,
 		"--script",
-		"Config Script ",
+		"Script ",
 		"<command>",
 		"command is executed using system() to configure the interface,\n"
 		"routing and so on. Device name, IP, etc. are passed using enviroment\n"
 		"variables, see README. This script is executed right after ISAKMP is\n"
-		"done, but befor tunneling is enabled.\n",
-		sysdep_config_script
+		"done, but before tunneling is enabled. It is called when vpnc\n"
+		"terminates, too\n",
+		config_def_script
 	}, {
 		CONFIG_IKE_DH, 1, 1,
 		"--dh",
@@ -232,25 +322,46 @@
 		"enables weak single DES encryption",
 		NULL
 	}, {
+		CONFIG_ENABLE_NO_ENCRYPTION, 0, 1,
+		"--enable-no-encryption",
+		"Enable no encryption",
+		NULL,
+		"enables using no encryption for data traffic (key exchanged must be encrypted)",
+		NULL
+	}, {
 		CONFIG_VERSION, 1, 1,
 		"--application-version",
 		"Application version ",
 		"<ASCII string>",
-		"Application Version to report",
+		"Application Version to report. Note: Default string is generated at runtime.",
 		config_def_app_version
 	}, {
 		CONFIG_IF_NAME, 1, 1,
 		"--ifname",
 		"Interface name ",
 		"<ASCII string>",
-		"visible name of the TUN interface",
-		NULL
+		"visible name of the TUN/TAP interface",
+		NULL
+	}, {
+		CONFIG_IF_MODE, 1, 1,
+		"--ifmode",
+		"Interface mode ",
+		"<tun/tap>",
+		"mode of TUN/TAP interface:\n"
+		" * tun: virtual point to point interface (default)\n"
+		" * tap: virtual ethernet interface\n",
+		config_def_if_mode
 	}, {
 		CONFIG_DEBUG, 1, 1,
 		"--debug",
 		"Debug ",
 		"<0/1/2/3/99>",
-		"Show verbose debug messages",
+		"Show verbose debug messages\n"
+		" *  0: Do not print debug information.\n"
+		" *  1: Print minimal debug information.\n"
+		" *  2: Show statemachine and packet/payload type information.\n"
+		" *  3: Dump everything exluding authentication data.\n"
+		" * 99: Dump everything including authentication data (e.g. passwords).\n",
 		NULL
 	}, {
 		CONFIG_ND, 0, 1,
@@ -265,7 +376,14 @@
 		"Pidfile ",
 		"<filename>",
 		"store the pid of background process in <filename>",
-		NULL
+		config_def_pid_file
+	}, {
+		CONFIG_LOCAL_ADDR, 1, 1,
+		"--local-addr",
+		"Local Addr ",
+		"<ip/hostname>",
+		"local IP to use for ISAKMP / ESP / ... (0.0.0.0 == automatically assign)",
+		config_def_local_addr
 	}, {
 		CONFIG_LOCAL_PORT, 1, 1,
 		"--local-port",
@@ -276,17 +394,21 @@
 	}, {
 		CONFIG_UDP_ENCAP_PORT, 1, 1,
 		"--udp-port",
-		"UDP Encapsulation Port ",
+		"Cisco UDP Encapsulation Port ",
 		"<0-65535>",
-		"local UDP port number to use (0 == use random port)",
+		"Local UDP port number to use (0 == use random port).\n"
+		"This is only relevant if cisco-udp nat-traversal is used.\n"
+		"This is the _local_ port, the remote udp port is discovered automatically.\n"
+		"It is especially not the cisco-tcp port.\n",
 		config_def_udp_port
 	}, {
-		CONFIG_DISABLE_NATT, 0, 1,
-		"--disable-natt",
-		"Disable NAT Traversal",
-		NULL,
-		"disable use of NAT-T",
-		NULL
+		CONFIG_DPD_IDLE, 1, 1,
+		"--dpd-idle",
+		"DPD idle timeout (our side) ",
+		"<0,10-86400>",
+		"Send DPD packet after not receiving anything for <idle> seconds.\n"
+		"Use 0 to disable DPD completely (both ways).\n",
+		config_def_dpd_idle
 	}, {
 		CONFIG_NON_INTERACTIVE, 0, 1,
 		"--non-inter",
@@ -295,22 +417,75 @@
 		"Don't ask anything, exit on missing options",
 		NULL
 	}, {
+ 		CONFIG_AUTH_MODE, 1, 1,
+		"--auth-mode",
+		"IKE Authmode ",
+		"<psk/cert/hybrid>",
+		"Authentication mode:\n"
+		" * psk:    pre-shared key (default)\n"
+		" * cert:   server + client certificate (not implemented yet)\n"
+		" * hybrid: server certificate + xauth (if built with openssl support)\n",
+		config_def_auth_mode
+	}, {
+		CONFIG_CA_FILE, 1, 1,
+		"--ca-file",
+		"CA-File ",
+		"<filename>",
+		"filename and path to the CA-PEM-File",
+		NULL
+	}, {
+		CONFIG_CA_DIR, 1, 1,
+		"--ca-dir",
+		"CA-Dir ",
+		"<directory>",
+		"path of the trusted CA-Directory",
+		config_ca_dir
+	}, {
+		CONFIG_IPSEC_TARGET_NETWORK, 1, 1,
+		"--target-network",
+		"IPSEC target network ",
+		"<target network/netmask>",
+		"Target network in dotted decimal or CIDR notation\n",
+		config_def_target_network
+	}, {
 		0, 0, 0, NULL, NULL, NULL, NULL, NULL
 	}
 };
 
-static void read_config_file(char *name, const char **configs, int missingok)
+static char *get_config_filename(const char *name, int add_dot_conf)
+{
+	char *realname;
+	
+	asprintf(&realname, "%s%s%s", index(name, '/') ? "" : "/etc/vpnc/", name, add_dot_conf ? ".conf" : "");
+	return realname;
+}
+
+static void read_config_file(const char *name, const char **configs, int missingok)
 {
 	FILE *f;
 	char *line = NULL;
-	ssize_t line_length = 0;
+	size_t line_length = 0;
 	int linenum = 0;
-
-	f = fopen(name, "r");
-	if (missingok && f == NULL && errno == ENOENT)
-		return;
-	if (f == NULL)
-		error(1, errno, "couldn't open `%s'", name);
+	char *realname;
+
+	if (!strcmp(name, "-")) {
+		f = stdin;
+		realname = strdup("stdin");
+	} else {
+		realname = get_config_filename(name, 0);
+		f = fopen(realname, "r");
+		if (f == NULL && errno == ENOENT) {
+			free(realname);
+			realname = get_config_filename(name, 1);
+			f = fopen(realname, "r");
+		}
+		if (missingok && f == NULL && errno == ENOENT) {
+			free(realname);
+			return;
+		}
+		if (f == NULL)
+			error(1, errno, "couldn't open `%s'", realname);
+	}
 	for (;;) {
 		ssize_t llen;
 		int i;
@@ -319,16 +494,16 @@
 		if (llen == -1 && feof(f))
 			break;
 		if (llen == -1)
-			error(1, errno, "reading `%s'", name);
-		if (line[llen - 1] == '\n')
-			line[llen - 1] = 0;
+			error(1, errno, "reading `%s'", realname);
+		if (llen > 0 && line[llen - 1] == '\n')
+			line[--llen] = 0;
+		if (llen > 0 && line[llen - 1] == '\r')
+			line[--llen] = 0;
 		linenum++;
 		for (i = 0; config_names[i].name != NULL; i++) {
-			if (config_names[i].nm == CONFIG_NONE)
-				continue;
 			if (strncasecmp(config_names[i].name, line,
 					strlen(config_names[i].name)) == 0) {
-				// boolean implementation, using harmles pointer targets as true
+				/* boolean implementation, using harmless pointer targets as true */
 				if (!config_names[i].needsArgument) {
 					configs[config_names[i].nm] = config_names[i].name;
 					break;
@@ -343,8 +518,12 @@
 		}
 		if (config_names[i].name == NULL && line[0] != '#' && line[0] != 0)
 			error(0, 0, "warning: unknown configuration directive in %s at line %d",
-				name, linenum);
-	}
+				realname, linenum);
+	}
+	free(line);
+	free(realname);
+	if (strcmp(name, "-"))
+		fclose(f);
 }
 
 static void print_desc(const char *pre, const char *text)
@@ -352,38 +531,42 @@
 	const char *p, *q;
 
 	for (p = text, q = strchr(p, '\n'); q; p = q+1, q = strchr(p, '\n'))
-		printf("%s%.*s\n", pre, q-p, p);
+		printf("%s%.*s\n", pre, (int)(q-p), p);
 
 	if (*p != '\0')
 		printf("%s%s\n", pre, p);
 }
 
-static void print_usage(char *argv0, int long_help)
+static void print_usage(char *argv0, int print_level)
 {
 	int c;
 
-	printf("Usage: %s [--version] [--print-config] [--help] [--long-help] [options] [config file]\n\n",
+	printf("Usage: %s [--version] [--print-config] [--help] [--long-help] [options] [config files]\n\n",
 		argv0);
-	printf("Legend:\n");
+	printf("Options:\n");
 	for (c = 0; config_names[c].name != NULL; c++) {
-		if (config_names[c].lvl > long_help)
+		if (config_names[c].long_only > print_level)
 			continue;
 
-		printf("  %s %s\n"
-			"  %s%s\n",
-			(config_names[c].option == NULL ? "(configfile only option)" :
-				config_names[c].option),
+		printf("  %s %s\n", (config_names[c].option == NULL ?
+				"(configfile only option)" : config_names[c].option),
 			((config_names[c].type == NULL || config_names[c].option == NULL) ?
-				"" : config_names[c].type),
-			config_names[c].name,
-			(config_names[c].type == NULL ? "" : config_names[c].type));
+				"" : config_names[c].type));
+
 		print_desc("      ", config_names[c].desc);
 
 		if (config_names[c].get_def != NULL)
 			printf("    Default: %s\n", config_names[c].get_def());
 
+		printf("  conf-variable: %s%s\n", config_names[c].name,
+			(config_names[c].type == NULL ? "" : config_names[c].type));
+
 		printf("\n");
 	}
+	
+	if (!print_level)
+		printf("Use --long-help to see all options\n\n");
+	
 	printf("Report bugs to vpnc at unix-ag.uni-kl.de\n");
 }
 
@@ -392,11 +575,17 @@
 	unsigned int i;
 
 	printf("vpnc version " VERSION "\n");
-	printf("Copyright (C) 2002-2004 Geoffrey Keating, Maurice Massar\n");
+	printf("Copyright (C) 2002-2006 Geoffrey Keating, Maurice Massar, others\n");
 	printf("vpnc comes with NO WARRANTY, to the extent permitted by law.\n"
 		"You may redistribute copies of vpnc under the terms of the GNU General\n"
 		"Public License.  For more information about these matters, see the files\n"
 		"named COPYING.\n");
+#ifdef OPENSSL_GPL_VIOLATION
+	printf("Built with openssl (certificate) support. Be aware of the\n"
+		"license implications.\n");
+#else /* OPENSSL_GPL_VIOLATION */
+	printf("Built without openssl (certificate) support.\n");
+#endif /* OPENSSL_GPL_VIOLATION */
 	printf("\n");
 
 	printf("Supported DH-Groups:");
@@ -423,12 +612,14 @@
 void do_config(int argc, char **argv)
 {
 	char *s;
-	int i, c, known, s_len;
-	int print_config = 0;
+	int i, c, known;
+	int got_conffile = 0, print_config = 0;
+	size_t s_len;
 
 	for (i = 1; i < argc; i++) {
-		if (argv[i][0] != '-') {
+		if (argv[i][0] && (argv[i][0] != '-' || argv[i][1] == '\0')) {
 			read_config_file(argv[i], config, 0);
+			got_conffile = 1;
 			continue;
 		}
 
@@ -436,7 +627,6 @@
 
 		for (c = 0; config_names[c].name != NULL && !known; c++) {
 			if (config_names[c].option == NULL
-				|| config_names[c].nm == CONFIG_NONE
 				|| strncmp(argv[i], config_names[c].option,
 					strlen(config_names[c].option)) != 0)
 				continue;
@@ -483,29 +673,83 @@
 			exit(1);
 		}
 	}
-
-	read_config_file("/etc/vpnc/default.conf", config, 1);
-	read_config_file("/etc/vpnc.conf", config, 1);
-
+	
+	if (!got_conffile) {
+		read_config_file("/etc/vpnc/default.conf", config, 1);
+		read_config_file("/etc/vpnc.conf", config, 1);
+	}
+	
 	if (!print_config) {
 		for (i = 0; config_names[i].name != NULL; i++)
-			if (!config[config_names[i].nm] && i != CONFIG_NONE
+			if (!config[config_names[i].nm]
 				&& config_names[i].get_def != NULL)
 				config[config_names[i].nm] = config_names[i].get_def();
-
+		
 		opt_debug = (config[CONFIG_DEBUG]) ? atoi(config[CONFIG_DEBUG]) : 0;
 		opt_nd = (config[CONFIG_ND]) ? 1 : 0;
 		opt_1des = (config[CONFIG_ENABLE_1DES]) ? 1 : 0;
-		opt_udpencap=(config[CONFIG_UDP_ENCAP]) ? 1 : 0;
+
+		if (!strcmp(config[CONFIG_AUTH_MODE], "psk")) {
+			opt_auth_mode = AUTH_MODE_PSK;
+		} else if (!strcmp(config[CONFIG_AUTH_MODE], "cert")) {
+			opt_auth_mode = AUTH_MODE_CERT;
+		} else if (!strcmp(config[CONFIG_AUTH_MODE], "hybrid")) {
+			opt_auth_mode = AUTH_MODE_HYBRID;
+		} else {
+			printf("%s: unknown authentication mode %s\nknown modes: psk cert hybrid\n", argv[0], config[CONFIG_AUTH_MODE]);
+			exit(1);
+		}
+#ifndef OPENSSL_GPL_VIOLATION
+		if (opt_auth_mode == AUTH_MODE_HYBRID ||
+			opt_auth_mode == AUTH_MODE_CERT) {
+			printf("%s was built without openssl: Can't do hybrid or cert mode.\n", argv[0]);
+			exit(1);
+		}
+#endif
+		opt_no_encryption = (config[CONFIG_ENABLE_NO_ENCRYPTION]) ? 1 : 0;
 		opt_udpencapport=atoi(config[CONFIG_UDP_ENCAP_PORT]);
-	}
-
+		
+		if (!strcmp(config[CONFIG_NATT_MODE], "natt")) {
+			opt_natt_mode = NATT_NORMAL;
+		} else if (!strcmp(config[CONFIG_NATT_MODE], "none")) {
+			opt_natt_mode = NATT_NONE;
+		} else if (!strcmp(config[CONFIG_NATT_MODE], "force-natt")) {
+			opt_natt_mode = NATT_FORCE;
+		} else if (!strcmp(config[CONFIG_NATT_MODE], "cisco-udp")) {
+			opt_natt_mode = NATT_CISCO_UDP;
+		} else {
+			printf("%s: unknown nat traversal mode %s\nknown modes: natt none force-natt cisco-udp\n", argv[0], config[CONFIG_NATT_MODE]);
+			exit(1);
+		}
+		
+		if (!strcmp(config[CONFIG_IF_MODE], "tun")) {
+			opt_if_mode = IF_MODE_TUN;
+		} else if (!strcmp(config[CONFIG_IF_MODE], "tap")) {
+			opt_if_mode = IF_MODE_TAP;
+		} else {
+			printf("%s: unknown interface mode %s\nknown modes: tun tap\n", argv[0], config[CONFIG_IF_MODE]);
+			exit(1);
+		}
+		
+		if (!strcmp(config[CONFIG_VENDOR], "cisco")) {
+			opt_vendor = VENDOR_CISCO;
+		} else if (!strcmp(config[CONFIG_VENDOR], "netscreen")) {
+			opt_vendor = VENDOR_NETSCREEN;
+		} else {
+			printf("%s: unknown vendor %s\nknown vendors: cisco netscreen\n", argv[0], config[CONFIG_VENDOR]);
+			exit(1);
+		}
+	}
+	
 	if (opt_debug >= 99) {
 		printf("WARNING! active debug level is >= 99, output includes username and password (hex encoded)\n");
 		fprintf(stderr,
 			"WARNING! active debug level is >= 99, output includes username and password (hex encoded)\n");
 	}
-
+	
+	config_deobfuscate(CONFIG_IPSEC_SECRET_OBF, CONFIG_IPSEC_SECRET);
+	config_deobfuscate(CONFIG_XAUTH_PASSWORD_OBF, CONFIG_XAUTH_PASSWORD);
+	
 	for (i = 0; i < LAST_CONFIG; i++) {
 		if (config[i] != NULL || config[CONFIG_NON_INTERACTIVE] != NULL)
 			continue;
@@ -546,7 +790,7 @@
 		case CONFIG_XAUTH_USERNAME:
 			getline(&s, &s_len, stdin);
 		}
-		if (s != NULL && s[strlen(s) - 1] == '\n')
+		if (s != NULL && strlen(s) > 0 && s[strlen(s) - 1] == '\n')
 			s[strlen(s) - 1] = 0;
 		config[i] = s;
 	}

Modified: branches/vpnc-nortel/config.h
==============================================================================
--- branches/vpnc-nortel/config.h (original)
+++ branches/vpnc-nortel/config.h Wed Jun 11 01:19:11 2008
@@ -1,5 +1,5 @@
 /* IPSec VPN client compatible with Cisco equipment.
-   Copyright (C) 2004 Maurice Massar
+   Copyright (C) 2004-2005 Maurice Massar
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -14,56 +14,108 @@
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   $Id$
 */
 
 #ifndef __CONFIG_H__
 #define __CONFIG_H__
 
 #include <unistd.h>
+#include <inttypes.h>
+
+#include "vpnc-debug.h"
 
 enum config_enum {
-	CONFIG_NONE,
-	CONFIG_CONFIG_SCRIPT,
+	CONFIG_SCRIPT,
 	CONFIG_DEBUG,
 	CONFIG_DOMAIN,
 	CONFIG_ENABLE_1DES,
+	CONFIG_ENABLE_NO_ENCRYPTION,
 	CONFIG_ND,
 	CONFIG_NON_INTERACTIVE,
 	CONFIG_PID_FILE,
+	CONFIG_LOCAL_ADDR,
 	CONFIG_LOCAL_PORT,
 	CONFIG_VERSION,
 	CONFIG_IF_NAME,
+	CONFIG_IF_MODE,
 	CONFIG_IKE_DH,
 	CONFIG_IPSEC_PFS,
 	CONFIG_IPSEC_GATEWAY,
+	CONFIG_IPSEC_TARGET_NETWORK,
 	CONFIG_IPSEC_ID,
 	CONFIG_IPSEC_SECRET,
+	CONFIG_IPSEC_SECRET_OBF,
 	CONFIG_XAUTH_USERNAME,
 	CONFIG_XAUTH_PASSWORD,
+	CONFIG_XAUTH_PASSWORD_OBF,
 	CONFIG_XAUTH_INTERACTIVE,
-	CONFIG_UDP_ENCAP,
+	CONFIG_VENDOR,
+	CONFIG_NATT_MODE,
 	CONFIG_UDP_ENCAP_PORT,
-	CONFIG_DISABLE_NATT,
+	CONFIG_DPD_IDLE,
+	CONFIG_AUTH_MODE,
+	CONFIG_CA_FILE,
+	CONFIG_CA_DIR,
 	LAST_CONFIG
 };
 
 enum hex_dump_enum {
-	UINT8 = -1,
-	UINT16 = -2,
-	UINT32 = -4
+	DUMP_UINT8 = -1,
+	DUMP_UINT16 = -2,
+	DUMP_UINT32 = -4
+};
+
+enum vendor_enum {
+	VENDOR_CISCO,
+	VENDOR_NETSCREEN
+};
+
+enum natt_mode_enum {
+	NATT_NONE,
+	NATT_NORMAL,
+	NATT_FORCE,
+	NATT_CISCO_UDP
+};
+
+enum if_mode_enum {
+	IF_MODE_TUN,
+	IF_MODE_TAP
+};
+
+enum auth_mode_enum {
+	AUTH_MODE_PSK,
+	AUTH_MODE_RSA1,
+	AUTH_MODE_RSA2,
+	AUTH_MODE_CERT,
+	AUTH_MODE_HYBRID
 };
 
 extern const char *config[LAST_CONFIG];
 
+extern enum vendor_enum opt_vendor;
 extern int opt_debug;
 extern int opt_nd;
-extern int opt_1des;
-extern int opt_udpencap;
+extern int opt_1des, opt_no_encryption, opt_auth_mode;
+extern enum natt_mode_enum opt_natt_mode;
+extern enum if_mode_enum opt_if_mode;
 extern uint16_t opt_udpencapport;
 
-#define DEBUG(lvl, a) do {if (opt_debug >= (lvl)) {a;}} while (0)
+#define TIMESTAMP() ({				\
+	char st[20];				\
+	time_t t;				\
+	struct tm *tm;				\
+	t = time(NULL);				\
+	tm = localtime(&t);			\
+	strftime(st, sizeof(st), "%F %T", tm);	\
+	st;					\
+	})
 
-extern void hex_dump(const char *str, const void *data, ssize_t len);
+#define DEBUGTOP(lvl, a) do {if(opt_debug >= (lvl)){ printf("\n");(a);printf(" [%s]\n", TIMESTAMP());}} while (0)
+#define DEBUG(lvl, a) do {if (opt_debug >= (lvl)) {if(opt_debug>1)printf("   "); a;}} while (0)
+
+extern void hex_dump(const char *str, const void *data, ssize_t len, const struct debug_strings *decode);
 extern void do_config(int argc, char **argv);
 
 #endif

Added: branches/vpnc-nortel/decrypt-utils.c
==============================================================================
--- branches/vpnc-nortel/decrypt-utils.c (added)
+++ branches/vpnc-nortel/decrypt-utils.c Wed Jun 11 01:19:11 2008
@@ -1,0 +1,128 @@
+/* IPSec VPN client compatible with Cisco equipment.
+   Copyright (C) 2004-2007 Maurice Massar
+   A bit reorganized in 2007 by Wolfram Sang
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   $Id$
+*/
+
+#define _GNU_SOURCE
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <gcrypt.h>
+
+#include "decrypt-utils.h"
+
+
+static int hex2bin_c(unsigned int c)
+{
+	if ((c >= '0')&&(c <= '9'))
+		return c - '0';
+	if ((c >= 'A')&&(c <= 'F'))
+		return c - 'A' + 10;
+	if ((c >= 'a')&&(c <= 'f'))
+		return c - 'a' + 10;
+	return -1;
+}
+
+int hex2bin(const char *str, char **bin, int *len)
+{
+	char *p;
+	int i, l;
+	
+	if (!bin)
+		return EINVAL;
+	
+	for (i = 0; str[i] != '\0'; i++)
+		if (hex2bin_c(str[i]) == -1)
+			return EINVAL;
+	
+	l = i;
+	if ((l & 1) != 0)
+		return EINVAL;
+	l /= 2;
+	
+	p = malloc(l);
+	if (p == NULL)
+		return ENOMEM;
+	
+	for (i = 0; i < l; i++)
+		p[i] = hex2bin_c(str[i*2]) << 4 | hex2bin_c(str[i*2+1]);
+	
+	*bin = p;
+	if (len)
+		*len = l;
+	
+	return 0;
+}
+
+int deobfuscate(char *ct, int len, const char **resp, char *reslenp)
+{
+	const char *h1  = ct;
+	const char *h4  = ct + 20;
+	const char *enc = ct + 40;
+	
+	char ht[20], h2[20], h3[20], key[24];
+	const char *iv = h1;
+	char *res;
+	gcry_cipher_hd_t ctx;
+	int reslen;
+	
+	if (len < 48)
+		return -1;
+	len -= 40;
+	
+	memcpy(ht, h1, 20);
+	
+	ht[19]++;
+	gcry_md_hash_buffer(GCRY_MD_SHA1, h2, ht, 20);
+	
+	ht[19] += 2;
+	gcry_md_hash_buffer(GCRY_MD_SHA1, h3, ht, 20);
+	
+	memcpy(key, h2, 20);
+	memcpy(key+20, h3, 4);
+	/* who cares about parity anyway? */
+	
+	gcry_md_hash_buffer(GCRY_MD_SHA1, ht, enc, len);
+	
+	if (memcmp(h4, ht, 20) != 0)
+		return -1;
+	
+	res = malloc(len);
+	if (res == NULL)
+		return -1;
+	
+	gcry_cipher_open(&ctx, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC, 0);
+	gcry_cipher_setkey(ctx, key, 24);
+	gcry_cipher_setiv(ctx, iv, 8);
+	gcry_cipher_decrypt(ctx, (unsigned char *)res, len, (unsigned char *)enc, len);
+	gcry_cipher_close(ctx);
+	
+	reslen = len - res[len-1];
+	res[reslen] = '\0';
+	
+	if (resp)
+		*resp = res;
+	if (reslenp)
+		*reslenp = reslen;
+	return 0;
+}

Added: branches/vpnc-nortel/decrypt-utils.h
==============================================================================
--- branches/vpnc-nortel/decrypt-utils.h (added)
+++ branches/vpnc-nortel/decrypt-utils.h Wed Jun 11 01:19:11 2008
@@ -1,0 +1,28 @@
+/* IPSec VPN client compatible with Cisco equipment.
+   Copyright (C) 2004-2007 Maurice Massar
+   A bit reorganized in 2007 by Wolfram Sang
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   $Id$
+*/
+
+#ifndef __DECRYPT_UTILS_H__
+#define __DECRYPT_UTILS_H__
+
+extern int hex2bin(const char *str, char **bin, int *len);
+extern int deobfuscate(char *ct, int len, const char **resp, char *reslenp);
+
+#endif

Added: branches/vpnc-nortel/enum2debug.pl
==============================================================================
--- branches/vpnc-nortel/enum2debug.pl (added)
+++ branches/vpnc-nortel/enum2debug.pl Wed Jun 11 01:19:11 2008
@@ -1,0 +1,63 @@
+#!/usr/bin/perl -w
+
+# Usage: ./enum2debug.pl isakmp.h  >vpnc-debug.c 2>vpnc-debug.h
+
+use strict;
+
+my $in_enum = 0;
+my $element;
+my $arrayname;
+
+print STDERR << 'EOF';
+/* Automatically generated with enum2debug.pl: Don't edit! */
+
+struct debug_strings {
+	unsigned int id;
+	const char *string;
+};
+
+extern const char *val_to_string(unsigned int, const struct debug_strings *);
+
+EOF
+
+print << 'EOF';
+/* Automatically generated with enum2debug.pl: Don't edit! */
+
+#include <stdio.h>
+
+#include "vpnc-debug.h"
+#include "isakmp.h"
+
+const char *val_to_string(unsigned int val, const struct debug_strings *dstrings)
+{
+	static const char *unknown = " (unknown)";
+	static const char *na = "";
+	unsigned int i;
+	
+	if (dstrings == NULL)
+		return na;
+	
+	for (i = 0; dstrings[i].id != 0 || dstrings[i].string != NULL; i++)
+		if (dstrings[i].id == val)
+			return dstrings[i].string;
+	return unknown;
+}
+
+EOF
+
+while (<>) {
+	if (/^enum\W+(\w+)\W*/) {
+		print STDERR "extern const struct debug_strings $1_array[];\n";
+		print "const struct debug_strings $1_array[] = {\n";
+		$in_enum = 1;
+	} elsif ($in_enum && /^}/) {
+		print "\t{ 0,\t(const char *) 0 }\n};\n\n";
+		$in_enum = 0;
+	} elsif ($in_enum && /^\W*(\w+)\W*/) {
+		print "\t{ $1,\t\" ($1)\" },\n";
+	}
+}
+
+exit 0;
+
+__END__

Modified: branches/vpnc-nortel/isakmp-pkt.c
==============================================================================
--- branches/vpnc-nortel/isakmp-pkt.c (original)
+++ branches/vpnc-nortel/isakmp-pkt.c Wed Jun 11 01:19:11 2008
@@ -1,5 +1,6 @@
 /* ISAKMP packing and unpacking routines.
    Copyright (C) 2002  Geoffrey Keating
+   Copyright (C) 2003-2005 Maurice Massar
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -14,6 +15,8 @@
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   $Id$
 */
 
 #include <assert.h>
@@ -25,6 +28,8 @@
 #include "sysdep.h"
 #include "config.h"
 #include "isakmp-pkt.h"
+#include "math_group.h"
+#include "vpnc.h"
 
 void *xallocc(size_t x)
 {
@@ -271,7 +276,6 @@
 	 /*DUMP*/ if (opt_debug >= 3) {
 		printf("\n sending: ========================>\n");
 		free_isakmp_packet(parse_isakmp_packet(f.base, f.end - f.base, NULL));
-		printf("\n ==================================\n");
 	}
 }
 
@@ -321,6 +325,8 @@
 		nextatt = att->next;
 		if (att->af == isakmp_attr_lots)
 			free(att->u.lots.data);
+		if (att->af == isakmp_attr_acl)
+			free(att->u.acl.acl_ent);
 		free(att);
 	}
 }
@@ -339,7 +345,7 @@
 
 struct isakmp_payload *new_isakmp_data_payload(uint8_t type, const void *data, size_t data_length)
 {
-	struct isakmp_payload *result = xallocc(sizeof(struct isakmp_packet));
+	struct isakmp_payload *result = xallocc(sizeof(struct isakmp_payload));
 
 	if (type != ISAKMP_PAYLOAD_KE && type != ISAKMP_PAYLOAD_HASH
 		&& type != ISAKMP_PAYLOAD_SIG && type != ISAKMP_PAYLOAD_NONCE
@@ -358,7 +364,7 @@
 
 struct isakmp_payload *dup_isakmp_payload(struct isakmp_payload *p)
 {
-	struct isakmp_payload *np = xallocc(sizeof(struct isakmp_packet));
+	struct isakmp_payload *np = xallocc(sizeof(struct isakmp_payload));
 	*np = *p;
 
 	switch (np->type) {
@@ -412,7 +418,7 @@
 	return np;
 }
 
-void free_isakmp_payload(struct isakmp_payload *p)
+static void free_isakmp_payload(struct isakmp_payload *p)
 {
 	struct isakmp_payload *nxt;
 
@@ -424,6 +430,7 @@
 		free_isakmp_payload(p->u.sa.proposals);
 		break;
 	case ISAKMP_PAYLOAD_P:
+		free(p->u.p.spi);
 		free_isakmp_payload(p->u.p.transforms);
 		break;
 	case ISAKMP_PAYLOAD_T:
@@ -458,7 +465,7 @@
 		}
 		break;
 	case ISAKMP_PAYLOAD_MODECFG_ATTR:
-		free_isakmp_attributes(p->u.t.attributes);
+		free_isakmp_attributes(p->u.modecfg.attributes);		
 		break;
 	default:
 		abort();
@@ -475,6 +482,63 @@
 		return;
 	free_isakmp_payload(p->payload);
 	free(p);
+}
+
+static const struct debug_strings *transform_id_to_debug_strings(enum isakmp_ipsec_proto_enum decode_proto)
+{
+	switch (decode_proto) {
+	case ISAKMP_IPSEC_PROTO_ISAKMP:
+		return isakmp_ipsec_key_enum_array;
+	case ISAKMP_IPSEC_PROTO_IPSEC_AH:
+		return isakmp_ipsec_ah_enum_array;
+	case ISAKMP_IPSEC_PROTO_IPSEC_ESP:
+		return isakmp_ipsec_esp_enum_array;
+	case ISAKMP_IPSEC_PROTO_IPCOMP:
+		return isakmp_ipsec_ipcomp_enum_array;
+	default:
+		return NULL;
+	}
+}
+
+static const struct debug_strings *attr_type_to_debug_strings(enum isakmp_ipsec_proto_enum decode_proto)
+{
+	switch (decode_proto) {
+	case ISAKMP_IPSEC_PROTO_ISAKMP:
+		return ike_attr_enum_array;
+	case ISAKMP_IPSEC_PROTO_IPSEC_AH:
+	case ISAKMP_IPSEC_PROTO_IPSEC_ESP:
+		return isakmp_ipsec_attr_enum_array;
+	case ISAKMP_IPSEC_PROTO_MODECFG:
+		return isakmp_modecfg_attrib_enum_array;
+	default:
+		return NULL;
+	}
+}
+
+static const struct debug_strings *attr_val_to_debug_strings(enum isakmp_ipsec_proto_enum decode_proto, uint16_t type)
+{
+	switch (decode_proto) {
+	case ISAKMP_IPSEC_PROTO_ISAKMP:
+		switch (type) {
+		case IKE_ATTRIB_ENC:         return ike_enc_enum_array;
+		case IKE_ATTRIB_HASH:        return ike_hash_enum_array;
+		case IKE_ATTRIB_AUTH_METHOD: return ike_auth_enum_array;
+		case IKE_ATTRIB_GROUP_DESC:  return ike_group_enum_array;
+		case IKE_ATTRIB_GROUP_TYPE:  return ike_group_type_enum_array;
+		case IKE_ATTRIB_LIFE_TYPE:   return ike_life_enum_array;
+		default:  return NULL;
+		}
+	case ISAKMP_IPSEC_PROTO_IPSEC_AH:
+	case ISAKMP_IPSEC_PROTO_IPSEC_ESP:
+		switch (type) {
+		case ISAKMP_IPSEC_ATTRIB_SA_LIFE_TYPE: return ipsec_life_enum_array;
+		case ISAKMP_IPSEC_ATTRIB_ENCAP_MODE:   return ipsec_encap_enum_array;
+		case ISAKMP_IPSEC_ATTRIB_AUTH_ALG:     return ipsec_auth_enum_array;
+		default:  return NULL;
+		}
+	default:
+		return NULL;
+	}
 }
 
 #define fetch4()  					\
@@ -489,11 +553,12 @@
   (memcpy ((d), data, (n)), data += (n), data_len -= (n))
 
 static struct isakmp_attribute *parse_isakmp_attributes(const uint8_t ** data_p,
-	size_t data_len, int * reject)
+	size_t data_len, int * reject, enum isakmp_ipsec_proto_enum decode_proto)
 {
 	const uint8_t *data = *data_p;
 	struct isakmp_attribute *r;
 	uint16_t type, length;
+	int i;
 
 	if (data_len < 4)
 		return NULL;
@@ -503,46 +568,78 @@
 	length = fetch2();
 	if (type & 0x8000) {
 		r->type = type & ~0x8000;
-		hex_dump("t.attributes.type", &r->type, UINT16);
+		hex_dump("t.attributes.type", &r->type, DUMP_UINT16, attr_type_to_debug_strings(decode_proto));
 		r->af = isakmp_attr_16;
 		r->u.attr_16 = length;
 		if ((ISAKMP_XAUTH_ATTRIB_TYPE <= r->type)
 			&& (r->type <= ISAKMP_XAUTH_ATTRIB_ANSWER)
+			&& (r->type != ISAKMP_XAUTH_ATTRIB_STATUS)
+			&& (length > 0)
 			&& (opt_debug < 99))
 			DEBUG(3, printf("(not dumping xauth data)\n"));
 		else
-			hex_dump("t.attributes.u.attr_16", &r->u.attr_16, UINT16);
+			hex_dump("t.attributes.u.attr_16", &r->u.attr_16, DUMP_UINT16,
+				attr_val_to_debug_strings(decode_proto, r->type));
 	} else {
 		r->type = type;
-		hex_dump("t.attributes.type", &r->type, UINT16);
+		hex_dump("t.attributes.type", &r->type, DUMP_UINT16, attr_type_to_debug_strings(decode_proto));
 		r->af = isakmp_attr_lots;
 		r->u.lots.length = length;
-		if ((ISAKMP_XAUTH_ATTRIB_TYPE <= r->type) && (r->type <= ISAKMP_XAUTH_ATTRIB_ANSWER)
+		if ((ISAKMP_XAUTH_ATTRIB_TYPE <= r->type)
+			&& (r->type <= ISAKMP_XAUTH_ATTRIB_ANSWER)
+			&& (r->type != ISAKMP_XAUTH_ATTRIB_STATUS)
+			&& (length > 0)
 			&& (opt_debug < 99))
 			DEBUG(3, printf("(not dumping xauth data length)\n"));
 		else
-			hex_dump("t.attributes.u.lots.length", &r->u.lots.length, UINT16);
+			hex_dump("t.attributes.u.lots.length", &r->u.lots.length, DUMP_UINT16, NULL);
 		if (data_len < length) {
 			*reject = ISAKMP_N_PAYLOAD_MALFORMED;
 			return r;
 		}
-		r->u.lots.data = xallocc(length);
-		fetchn(r->u.lots.data, length);
-		if ((ISAKMP_XAUTH_ATTRIB_TYPE <= type) && (type <= ISAKMP_XAUTH_ATTRIB_ANSWER)
-			&& (opt_debug < 99))
-			DEBUG(3, printf("(not dumping xauth data)\n"));
-		else
-			hex_dump("t.attributes.u.lots.data", r->u.lots.data, r->u.lots.length);
-	}
-	r->next = parse_isakmp_attributes(&data, data_len, reject);
+		if (r->type == ISAKMP_MODECFG_ATTRIB_CISCO_SPLIT_INC) {
+			r->af = isakmp_attr_acl;
+			r->u.acl.count = length / (4+4+2+2+2);
+			if (r->u.acl.count * (4+4+2+2+2) != length) {
+				*reject = ISAKMP_N_PAYLOAD_MALFORMED;
+				return r;
+			}
+			r->u.acl.acl_ent = xallocc(r->u.acl.count * sizeof(struct acl_ent_s));
+			
+			for (i = 0; i < r->u.acl.count; i++) {
+				fetchn(&r->u.acl.acl_ent[i].addr.s_addr, 4);
+				fetchn(&r->u.acl.acl_ent[i].mask.s_addr, 4);
+				r->u.acl.acl_ent[i].protocol = fetch2();
+				r->u.acl.acl_ent[i].sport = fetch2();
+				r->u.acl.acl_ent[i].dport = fetch2();
+				hex_dump("t.attributes.u.acl.addr", &r->u.acl.acl_ent[i].addr.s_addr, 4, NULL);
+				hex_dump("t.attributes.u.acl.mask", &r->u.acl.acl_ent[i].mask.s_addr, 4, NULL);
+				hex_dump("t.attributes.u.acl.protocol", &r->u.acl.acl_ent[i].protocol, DUMP_UINT16, NULL);
+				hex_dump("t.attributes.u.acl.sport", &r->u.acl.acl_ent[i].sport, DUMP_UINT16, NULL);
+				hex_dump("t.attributes.u.acl.dport", &r->u.acl.acl_ent[i].dport, DUMP_UINT16, NULL);
+			}
+		} else {
+			r->u.lots.data = xallocc(length);
+			fetchn(r->u.lots.data, length);
+			if ((ISAKMP_XAUTH_ATTRIB_TYPE <= type)
+				&& (type <= ISAKMP_XAUTH_ATTRIB_ANSWER)
+				&& (r->type != ISAKMP_XAUTH_ATTRIB_STATUS)
+				&& (length > 0)
+				&& (opt_debug < 99))
+				DEBUG(3, printf("(not dumping xauth data)\n"));
+			else
+				hex_dump("t.attributes.u.lots.data", r->u.lots.data, r->u.lots.length, NULL);
+		}
+	}
+	r->next = parse_isakmp_attributes(&data, data_len, reject, decode_proto);
 	*data_p = data;
 	return r;
 }
 
 static struct isakmp_payload *parse_isakmp_payload(uint8_t type,
-	const uint8_t ** data_p, size_t * data_len_p, int * reject)
-{
-	const uint8_t *data = *data_p;
+	const uint8_t ** data_p, size_t * data_len_p, int * reject, enum isakmp_ipsec_proto_enum decode_proto)
+{
+	const uint8_t *data = *data_p, *tmpdata;
 	size_t data_len = *data_len_p;
 	struct isakmp_payload *r;
 	uint8_t next_type;
@@ -552,7 +649,8 @@
 		4, 12, 8, 8, 4, 8, 5, 5, 4, 4, 4, 12, 12, 4, 8
 	};
 
-	hex_dump("PARSING PAYLOAD type", &type, UINT8);
+	DEBUG(3, printf("\n"));
+	hex_dump("PARSING PAYLOAD type", &type, DUMP_UINT8, isakmp_payload_enum_array);
 	if (type == 0)
 		return NULL;
 	if (type <= ISAKMP_PAYLOAD_MODECFG_ATTR) {
@@ -567,13 +665,13 @@
 
 	r = new_isakmp_payload(type);
 	next_type = fetch1();
-	hex_dump("next_type", &next_type, UINT8);
+	hex_dump("next_type", &next_type, DUMP_UINT8, isakmp_payload_enum_array);
 	if (fetch1() != 0) {
 		*reject = ISAKMP_N_PAYLOAD_MALFORMED;
 		return r;
 	}
 	length = fetch2();
-	hex_dump("length", &length, UINT16);
+	hex_dump("length", &length, DUMP_UINT16, NULL);
 	if (length > data_len + 4
 		|| ((type <= ISAKMP_PAYLOAD_MODECFG_ATTR)&&(length < min_payload_len[type]))
 		|| (length < 4)) {
@@ -584,20 +682,20 @@
 	switch (type) {
 	case ISAKMP_PAYLOAD_SA:
 		r->u.sa.doi = fetch4();
-		hex_dump("sa.doi", &r->u.sa.doi, UINT32);
+		hex_dump("sa.doi", &r->u.sa.doi, DUMP_UINT32, isakmp_doi_enum_array);
 		if (r->u.sa.doi != ISAKMP_DOI_IPSEC) {
 			*reject = ISAKMP_N_DOI_NOT_SUPPORTED;
 			return r;
 		}
 		r->u.sa.situation = fetch4();
-		hex_dump("sa.situation", &r->u.sa.situation, UINT32);
+		hex_dump("sa.situation", &r->u.sa.situation, DUMP_UINT32, isakmp_ipsec_sit_enum_array);
 		if (r->u.sa.situation != ISAKMP_IPSEC_SIT_IDENTITY_ONLY) {
 			*reject = ISAKMP_N_SITUATION_NOT_SUPPORTED;
 			return r;
 		}
 		*reject = 0;
 		length -= 12;
-		r->u.sa.proposals = parse_isakmp_payload(ISAKMP_PAYLOAD_P, &data, &length, reject);
+		r->u.sa.proposals = parse_isakmp_payload(ISAKMP_PAYLOAD_P, &data, &length, reject, decode_proto);
 		if (*reject != 0)
 			return r;
 		/* Allow trailing garbage at end of payload.  */
@@ -614,13 +712,13 @@
 			struct isakmp_payload *xform;
 
 			r->u.p.number = fetch1();
-			hex_dump("p.number", &r->u.p.number, UINT8);
+			hex_dump("p.number", &r->u.p.number, DUMP_UINT8, NULL);
 			r->u.p.prot_id = fetch1();
-			hex_dump("p.prot_id", &r->u.p.prot_id, UINT8);
+			hex_dump("p.prot_id", &r->u.p.prot_id, DUMP_UINT8, isakmp_ipsec_proto_enum_array);
 			r->u.p.spi_size = fetch1();
-			hex_dump("p.spi_size", &r->u.p.spi_size, UINT8);
+			hex_dump("p.spi_size", &r->u.p.spi_size, DUMP_UINT8, NULL);
 			num_xform = fetch1();
-			hex_dump("length", &num_xform, UINT8);
+			hex_dump("length", &num_xform, DUMP_UINT8, NULL);
 
 			if (data_len < r->u.p.spi_size) {
 				*reject = ISAKMP_N_PAYLOAD_MALFORMED;
@@ -628,10 +726,10 @@
 			}
 			r->u.p.spi = xallocc(r->u.p.spi_size);
 			fetchn(r->u.p.spi, r->u.p.spi_size);
-			hex_dump("p.spi", r->u.p.spi, r->u.p.spi_size);
+			hex_dump("p.spi", r->u.p.spi, r->u.p.spi_size, NULL);
 			length -= 8 + r->u.p.spi_size;
 			r->u.p.transforms = parse_isakmp_payload(ISAKMP_PAYLOAD_T,
-				&data, &length, reject);
+				&data, &length, reject, r->u.p.prot_id);
 			for (xform = r->u.p.transforms; xform; xform = xform->next)
 				if (num_xform-- == 0)
 					break;
@@ -651,15 +749,15 @@
 			return r;
 		}
 		r->u.t.number = fetch1();
-		hex_dump("t.number", &r->u.t.number, UINT8);
+		hex_dump("t.number", &r->u.t.number, DUMP_UINT8, NULL);
 		r->u.t.id = fetch1();
-		hex_dump("t.id", &r->u.t.id, UINT8);
+		hex_dump("t.id", &r->u.t.id, DUMP_UINT8, transform_id_to_debug_strings(decode_proto));
 		if (fetch2() != 0) {
 			*reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
 			return r;
 		}
 		length -= 8;
-		r->u.t.attributes = parse_isakmp_attributes(&data, length, reject);
+		r->u.t.attributes = parse_isakmp_attributes(&data, length, reject, decode_proto);
 		data_len -= olength - 8;
 		break;
 
@@ -673,59 +771,67 @@
 		r->u.ke.length = length - 4;
 		r->u.ke.data = xallocc(r->u.ke.length);
 		fetchn(r->u.ke.data, r->u.ke.length);
-		hex_dump("ke.data", r->u.ke.data, r->u.ke.length);
+		hex_dump("ke.data", r->u.ke.data, r->u.ke.length, NULL);
+		if (type == ISAKMP_PAYLOAD_VID)
+			print_vid(r->u.ke.data, r->u.ke.length);
 		break;
 	case ISAKMP_PAYLOAD_ID:
 		r->u.id.type = fetch1();
-		hex_dump("id.type", &r->u.id.type, UINT8);
+		hex_dump("id.type", &r->u.id.type, DUMP_UINT8, isakmp_ipsec_id_enum_array);
 		r->u.id.protocol = fetch1();
-		hex_dump("id.protocol", &r->u.id.protocol, UINT8);
+		hex_dump("id.protocol", &r->u.id.protocol, DUMP_UINT8, NULL); /* IP protocol nr */
 		r->u.id.port = fetch2();
-		hex_dump("id.port", &r->u.id.port, sizeof(r->u.id.port));
+		hex_dump("id.port", &r->u.id.port, DUMP_UINT16, NULL);
 		r->u.id.length = length - 8;
 		r->u.id.data = xallocc(r->u.id.length);
 		fetchn(r->u.id.data, r->u.id.length);
-		hex_dump("id.data", r->u.id.data, r->u.id.length);
+		hex_dump("id.data", r->u.id.data, r->u.id.length, NULL);
 		break;
 	case ISAKMP_PAYLOAD_CERT:
 	case ISAKMP_PAYLOAD_CR:
 		r->u.cert.encoding = fetch1();
-		hex_dump("cert.encoding", &r->u.cert.encoding, UINT8);
+		hex_dump("cert.encoding", &r->u.cert.encoding, DUMP_UINT8, NULL);
 		r->u.cert.length = length - 5;
+		r->u.cert.data = xallocc(r->u.cert.length);
 		fetchn(r->u.cert.data, r->u.cert.length);
-		hex_dump("cert.data", r->u.cert.data, r->u.cert.length);
+		hex_dump("cert.data", r->u.cert.data, r->u.cert.length, NULL);
 		break;
 	case ISAKMP_PAYLOAD_N:
 		r->u.n.doi = fetch4();
-		hex_dump("n.doi", &r->u.n.doi, UINT32);
+		hex_dump("n.doi", &r->u.n.doi, DUMP_UINT32, isakmp_doi_enum_array);
 		r->u.n.protocol = fetch1();
-		hex_dump("n.protocol", &r->u.n.protocol, UINT8);
+		hex_dump("n.protocol", &r->u.n.protocol, DUMP_UINT8, isakmp_ipsec_proto_enum_array);
 		r->u.n.spi_length = fetch1();
-		hex_dump("n.spi_length", &r->u.n.spi_length, UINT8);
+		hex_dump("n.spi_length", &r->u.n.spi_length, DUMP_UINT8, NULL);
 		r->u.n.type = fetch2();
-		hex_dump("n.type", &r->u.n.type, UINT16);
+		hex_dump("n.type", &r->u.n.type, DUMP_UINT16, isakmp_notify_enum_array);
 		if (r->u.n.spi_length + 12u > length) {
 			*reject = ISAKMP_N_PAYLOAD_MALFORMED;
 			return r;
 		}
 		r->u.n.spi = xallocc(r->u.n.spi_length);
 		fetchn(r->u.n.spi, r->u.n.spi_length);
-		hex_dump("n.spi", r->u.n.spi, r->u.n.spi_length);
+		hex_dump("n.spi", r->u.n.spi, r->u.n.spi_length, NULL);
 		r->u.n.data_length = length - 12 - r->u.n.spi_length;
 		r->u.n.data = xallocc(r->u.n.data_length);
 		fetchn(r->u.n.data, r->u.n.data_length);
-		hex_dump("n.data", r->u.n.data, r->u.n.data_length);
+		hex_dump("n.data", r->u.n.data, r->u.n.data_length, NULL);
+		if ((r->u.n.doi == ISAKMP_DOI_IPSEC)&&(r->u.n.type == ISAKMP_N_IPSEC_RESPONDER_LIFETIME)) {
+			tmpdata = r->u.n.data;
+			r->u.n.attributes = parse_isakmp_attributes(&tmpdata, r->u.n.data_length, reject,
+				r->u.n.protocol);
+		}
 		break;
 	case ISAKMP_PAYLOAD_D:
-		r->u.n.doi = fetch4(); /*FIXME: huuuh? */
-		hex_dump("n.doi", &r->u.n.doi, UINT32);
-		r->u.n.protocol = fetch1();
-		hex_dump("n.protocol", &r->u.n.protocol, UINT8);
-		r->u.n.spi_length = fetch1();
-		hex_dump("n.spi_length", &r->u.n.spi_length, UINT8);
+		r->u.d.doi = fetch4();
+		hex_dump("d.doi", &r->u.d.doi, DUMP_UINT32, isakmp_doi_enum_array);
+		r->u.d.protocol = fetch1();
+		hex_dump("d.protocol", &r->u.d.protocol, DUMP_UINT8, isakmp_ipsec_proto_enum_array);
+		r->u.d.spi_length = fetch1();
+		hex_dump("d.spi_length", &r->u.d.spi_length, DUMP_UINT8, NULL);
 		r->u.d.num_spi = fetch2();
-		hex_dump("d.num_spi", &r->u.d.num_spi, UINT16);
-		if (r->u.d.num_spi * r->u.n.spi_length + 12u != length) {
+		hex_dump("d.num_spi", &r->u.d.num_spi, DUMP_UINT16, NULL);
+		if (r->u.d.num_spi * r->u.d.spi_length + 12u != length) {
 			*reject = ISAKMP_N_PAYLOAD_MALFORMED;
 			return r;
 		}
@@ -735,21 +841,22 @@
 			for (i = 0; i < r->u.d.num_spi; i++) {
 				r->u.d.spi[i] = xallocc(r->u.d.spi_length);
 				fetchn(r->u.d.spi[i], r->u.d.spi_length);
-				hex_dump("d.spi", r->u.d.spi[i], r->u.d.spi_length);
+				hex_dump("d.spi", r->u.d.spi[i], r->u.d.spi_length, NULL);
 			}
 		}
 		break;
 	case ISAKMP_PAYLOAD_MODECFG_ATTR:
 		r->u.modecfg.type = fetch1();
-		hex_dump("modecfg.type", &r->u.modecfg.type, UINT8);
+		hex_dump("modecfg.type", &r->u.modecfg.type, DUMP_UINT8, isakmp_modecfg_cfg_enum_array);
 		if (fetch1() != 0) {
 			*reject = ISAKMP_N_PAYLOAD_MALFORMED;
 			return r;
 		}
 		r->u.modecfg.id = fetch2();
-		hex_dump("modecfg.id", &r->u.modecfg.id, UINT16);
+		hex_dump("modecfg.id", &r->u.modecfg.id, DUMP_UINT16, NULL);
 		length -= 8;
-		r->u.modecfg.attributes = parse_isakmp_attributes(&data, length, reject);
+		r->u.modecfg.attributes = parse_isakmp_attributes(&data, length, reject,
+			ISAKMP_IPSEC_PROTO_MODECFG); /* this "proto" is a hack for simplicity */
 		data_len -= olength - 8;
 		break;
 
@@ -757,13 +864,13 @@
 		r->u.ke.length = length - 4;
 		r->u.ke.data = xallocc(r->u.ke.length);
 		fetchn(r->u.ke.data, r->u.ke.length);
-		hex_dump("UNKNOWN.data", r->u.ke.data, r->u.ke.length);
+		hex_dump("UNKNOWN.data", r->u.ke.data, r->u.ke.length, NULL);
 		break;
 	}
 	*data_p = data;
 	*data_len_p = data_len;
-	hex_dump("DONE PARSING PAYLOAD type", &type, UINT8);
-	r->next = parse_isakmp_payload(next_type, data_p, data_len_p, reject);
+	hex_dump("DONE PARSING PAYLOAD type", &type, DUMP_UINT8, isakmp_payload_enum_array);
+	r->next = parse_isakmp_payload(next_type, data_p, data_len_p, reject, decode_proto);
 	return r;
 }
 
@@ -776,21 +883,22 @@
 	size_t isakmp_data_len;
 
 	if (data_len < ISAKMP_PAYLOAD_O) {
-		DEBUG(2, printf("packet to short: len = %d < min = %d\n", data_len, ISAKMP_PAYLOAD_O));
+		DEBUG(2, printf("packet to short: len = %lld < min = %lld\n", (long long) data_len, (long long)ISAKMP_PAYLOAD_O));
 		reason = ISAKMP_N_UNEQUAL_PAYLOAD_LENGTHS;
 		goto error;
 	}
 
-	DEBUG(3, printf("\nBEGIN_PARSE\n"));
+	DEBUG(3, printf("BEGIN_PARSE\n"));
+	DEBUG(3, printf("Recieved Packet Len: %d\n", data_len));
 	fetchn(r->i_cookie, ISAKMP_COOKIE_LENGTH);
-	hex_dump("i_cookie", r->i_cookie, ISAKMP_COOKIE_LENGTH);
+	hex_dump("i_cookie", r->i_cookie, ISAKMP_COOKIE_LENGTH, NULL);
 	fetchn(r->r_cookie, ISAKMP_COOKIE_LENGTH);
-	hex_dump("r_cookie", r->r_cookie, ISAKMP_COOKIE_LENGTH);
+	hex_dump("r_cookie", r->r_cookie, ISAKMP_COOKIE_LENGTH, NULL);
 	payload = fetch1();
-	hex_dump("payload", &payload, UINT8);
+	hex_dump("payload", &payload, DUMP_UINT8, isakmp_payload_enum_array);
 
 	r->isakmp_version = fetch1();
-	hex_dump("isakmp_version", &r->isakmp_version, UINT8);
+	hex_dump("isakmp_version", &r->isakmp_version, DUMP_UINT8, NULL);
 	if (r->isakmp_version > ISAKMP_VERSION) {
 		if ((r->isakmp_version & 0xF0) >= (ISAKMP_VERSION & 0xF0))
 			reason = ISAKMP_N_INVALID_MAJOR_VERSION;
@@ -800,26 +908,26 @@
 	}
 
 	r->exchange_type = fetch1();
-	hex_dump("exchange_type", &r->exchange_type, UINT8);
+	hex_dump("exchange_type", &r->exchange_type, DUMP_UINT8, isakmp_exchange_enum_array);
 	r->flags = fetch1();
-	hex_dump("flags", &r->flags, UINT8);
+	hex_dump("flags", &r->flags, DUMP_UINT8, NULL);
 	r->message_id = fetch4();
-	hex_dump("message_id", &r->message_id, sizeof(r->message_id));
+	hex_dump("message_id", &r->message_id, sizeof(r->message_id), NULL);
 
 	isakmp_data_len = fetch4();
-	hex_dump("len", &isakmp_data_len, UINT32);
+	hex_dump("len", &isakmp_data_len, DUMP_UINT32, NULL);
 	if (o_data_len != isakmp_data_len) {
-		DEBUG(2, printf("isakmp length does not match packet length: isakmp = %d != datalen = %d\n",
-			isakmp_data_len, o_data_len));
+		DEBUG(2, printf("isakmp length does not match packet length: isakmp = %lld != datalen = %lld\n",
+			(long long)isakmp_data_len, (long long)o_data_len));
 		reason = ISAKMP_N_UNEQUAL_PAYLOAD_LENGTHS;
 		goto error;
 	}
 
-	r->payload = parse_isakmp_payload(payload, &data, &data_len, &reason);
+	r->payload = parse_isakmp_payload(payload, &data, &data_len, &reason, 0);
 	if (reason != 0)
 		goto error;
 
-	DEBUG(3, printf("PARSE_OK\n\n"));
+	DEBUG(3, printf("PARSE_OK\n"));
 	return r;
 
       error:
@@ -827,53 +935,6 @@
 	if (reject)
 		*reject = reason;
 	return NULL;
-}
-
-const char *isakmp_notify_to_error(uint16_t notify)
-{
-	static const struct {
-		uint16_t id;
-		const char *name;
-	} data[] = {
-		{ ISAKMP_N_INVALID_PAYLOAD_TYPE, "INVALID_PAYLOAD_TYPE"},
-		{ ISAKMP_N_DOI_NOT_SUPPORTED, "DOI_NOT_SUPPORTED"},
-		{ ISAKMP_N_SITUATION_NOT_SUPPORTED, "SITUATION_NOT_SUPPORTED"},
-		{ ISAKMP_N_INVALID_COOKIE, "INVALID_COOKIE"},
-		{ ISAKMP_N_INVALID_MAJOR_VERSION, "INVALID_MAJOR_VERSION"},
-		{ ISAKMP_N_INVALID_MINOR_VERSION, "INVALID_MINOR_VERSION"},
-		{ ISAKMP_N_INVALID_EXCHANGE_TYPE, "INVALID_EXCHANGE_TYPE"},
-		{ ISAKMP_N_INVALID_FLAGS, "INVALID_FLAGS"},
-		{ ISAKMP_N_INVALID_MESSAGE_ID, "INVALID_MESSAGE_ID"},
-		{ ISAKMP_N_INVALID_PROTOCOL_ID, "INVALID_PROTOCOL_ID"},
-		{ ISAKMP_N_INVALID_SPI, "INVALID_SPI"},
-		{ ISAKMP_N_INVALID_TRANSFORM_ID, "INVALID_TRANSFORM_ID"},
-		{ ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED, "ATTRIBUTES_NOT_SUPPORTED"},
-		{ ISAKMP_N_NO_PROPOSAL_CHOSEN, "NO_PROPOSAL_CHOSEN"},
-		{ ISAKMP_N_BAD_PROPOSAL_SYNTAX, "BAD_PROPOSAL_SYNTAX"},
-		{ ISAKMP_N_PAYLOAD_MALFORMED, "PAYLOAD_MALFORMED"},
-		{ ISAKMP_N_INVALID_KEY_INFORMATION, "INVALID_KEY_INFORMATION"},
-		{ ISAKMP_N_INVALID_ID_INFORMATION, "INVALID_ID_INFORMATION"},
-		{ ISAKMP_N_INVALID_CERT_ENCODING, "INVALID_CERT_ENCODING"},
-		{ ISAKMP_N_INVALID_CERTIFICATE, "INVALID_CERTIFICATE"},
-		{ ISAKMP_N_CERT_TYPE_UNSUPPORTED, "CERT_TYPE_UNSUPPORTED"},
-		{ ISAKMP_N_INVALID_CERT_AUTHORITY, "INVALID_CERT_AUTHORITY"},
-		{ ISAKMP_N_INVALID_HASH_INFORMATION, "INVALID_HASH_INFORMATION"},
-		{ ISAKMP_N_AUTHENTICATION_FAILED, "AUTHENTICATION_FAILED"},
-		{ ISAKMP_N_INVALID_SIGNATURE, "INVALID_SIGNATURE"},
-		{ ISAKMP_N_ADDRESS_NOTIFICATION, "ADDRESS_NOTIFICATION"},
-		{ ISAKMP_N_NOTIFY_SA_LIFETIME, "NOTIFY_SA_LIFETIME"},
-		{ ISAKMP_N_CERTIFICATE_UNAVAILABLE, "CERTIFICATE_UNAVAILABLE"},
-		{ ISAKMP_N_UNSUPPORTED_EXCHANGE_TYPE, "UNSUPPORTED_EXCHANGE_TYPE"},
-		{ ISAKMP_N_UNEQUAL_PAYLOAD_LENGTHS, "UNEQUAL_PAYLOAD_LENGTHS"}
-	};
-	size_t i;
-	static char number[10];
-
-	for (i = 0; i < sizeof(data) / sizeof(data[0]); i++)
-		if (data[i].id == notify)
-			return data[i].name;
-	sprintf(number, "%d", notify);
-	return number;
 }
 
 void test_pack_unpack(void)

Modified: branches/vpnc-nortel/isakmp-pkt.h
==============================================================================
--- branches/vpnc-nortel/isakmp-pkt.h (original)
+++ branches/vpnc-nortel/isakmp-pkt.h Wed Jun 11 01:19:11 2008
@@ -1,5 +1,6 @@
 /* ISAKMP packing and unpacking routines.
    Copyright (C) 2002  Geoffrey Keating
+   Copyright (C) 2003-2005 Maurice Massar
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -14,6 +15,8 @@
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   $Id$
 */
 
 #ifndef __ISAKMP_PKT_H__
@@ -31,7 +34,8 @@
 	enum {
 		isakmp_attr_lots,
 		isakmp_attr_16,
-		isakmp_attr_2x8
+		isakmp_attr_2x8,
+		isakmp_attr_acl
 	} af;
 	union {
 		uint16_t attr_16;
@@ -40,6 +44,13 @@
 			uint16_t length;
 			uint8_t *data;
 		} lots;
+		struct {
+			uint16_t count;
+			struct acl_ent_s {
+				struct in_addr addr, mask;
+				uint16_t protocol, sport, dport;
+			} *acl_ent;
+		} acl;
 	} u;
 };
 
@@ -88,6 +99,7 @@
 			uint16_t type;
 			uint16_t data_length;
 			uint8_t *data;
+			struct isakmp_attribute *attributes; /* sometimes, data is an attributes array */
 		} n;
 		struct {
 			uint32_t doi;
@@ -98,7 +110,9 @@
 		} d;
 		struct {
 			uint8_t type;
+#ifdef NORTELVPN
 			uint8_t pad;
+#endif
 			uint16_t id;
 			struct isakmp_attribute *attributes;
 		} modecfg;
@@ -122,7 +136,7 @@
 extern struct isakmp_payload *new_isakmp_data_payload(uint8_t type, const void *data,
 	size_t data_length);
 extern struct isakmp_payload *dup_isakmp_payload(struct isakmp_payload *p);
-extern void free_isakmp_payload(struct isakmp_payload *p);
+//extern void free_isakmp_payload(struct isakmp_payload *p);
 extern struct isakmp_attribute *new_isakmp_attribute(uint16_t, struct isakmp_attribute *);
 extern struct isakmp_attribute *new_isakmp_attribute_16(uint16_t type, uint16_t data,
 	struct isakmp_attribute *next);
@@ -134,7 +148,6 @@
 	uint8_t ** result, size_t * size, size_t blksz);
 extern struct isakmp_packet *parse_isakmp_packet(const uint8_t * data,
 	size_t data_len, int * reject);
-extern const char *isakmp_notify_to_error(uint16_t notify);
 extern void test_pack_unpack(void);
 
 #endif

Modified: branches/vpnc-nortel/isakmp.h
==============================================================================
--- branches/vpnc-nortel/isakmp.h (original)
+++ branches/vpnc-nortel/isakmp.h Wed Jun 11 01:19:11 2008
@@ -14,6 +14,8 @@
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   $Id$
 */
 
 #ifndef __ISAKMP_H__
@@ -26,24 +28,30 @@
 
 /* Payload types */
 enum isakmp_payload_enum {
-	ISAKMP_PAYLOAD_NONE = 0,
-	ISAKMP_PAYLOAD_SA,
-	ISAKMP_PAYLOAD_P,
-	ISAKMP_PAYLOAD_T,
-	ISAKMP_PAYLOAD_KE,
-	ISAKMP_PAYLOAD_ID,
-	ISAKMP_PAYLOAD_CERT,
-	ISAKMP_PAYLOAD_CR,
-	ISAKMP_PAYLOAD_HASH,
-	ISAKMP_PAYLOAD_SIG,
-	ISAKMP_PAYLOAD_NONCE,
-	ISAKMP_PAYLOAD_N,
-	ISAKMP_PAYLOAD_D,
-	ISAKMP_PAYLOAD_VID,
+	ISAKMP_PAYLOAD_NONE = 0,	/* RFC 2408 */
+	ISAKMP_PAYLOAD_SA,		/* RFC 2408 */
+	ISAKMP_PAYLOAD_P,		/* RFC 2408 */
+	ISAKMP_PAYLOAD_T,		/* RFC 2408 */
+	ISAKMP_PAYLOAD_KE,		/* RFC 2408 */
+	ISAKMP_PAYLOAD_ID,		/* RFC 2408 */
+	ISAKMP_PAYLOAD_CERT,		/* RFC 2408 */
+	ISAKMP_PAYLOAD_CR,		/* RFC 2408 */
+	ISAKMP_PAYLOAD_HASH,		/* RFC 2408 */
+	ISAKMP_PAYLOAD_SIG,		/* RFC 2408 */
+	ISAKMP_PAYLOAD_NONCE,		/* RFC 2408 */
+	ISAKMP_PAYLOAD_N,		/* RFC 2408 */
+	ISAKMP_PAYLOAD_D,		/* RFC 2408 */
+	ISAKMP_PAYLOAD_VID,		/* RFC 2408 */
 	ISAKMP_PAYLOAD_MODECFG_ATTR,
-	ISAKMP_PAYLOAD_NAT_D,
-	ISAKMP_PAYLOAD_NAT_OA,
+	ISAKMP_PAYLOAD_SAK,		/* RFC 3547 */
+	ISAKMP_PAYLOAD_SAT,		/* RFC 3547 */
+	ISAKMP_PAYLOAD_KD,		/* RFC 3547 */
+	ISAKMP_PAYLOAD_SEQNO,		/* RFC 3547 */
+	ISAKMP_PAYLOAD_POP,		/* RFC 3547 */
+	ISAKMP_PAYLOAD_NAT_D,		/* RFC 3947 */
+	ISAKMP_PAYLOAD_NAT_OA,		/* RFC 3947 */
 	ISAKMP_PAYLOAD_NAT_D_OLD = 0x82,
+	ISAKMP_PAYLOAD_FRAG = 0x84
 };
 
 /* Exchange types.  */
@@ -60,10 +68,12 @@
 };
 
 /* DOI types.  */
-#define ISAKMP_DOI_GENERIC		0
-#define ISAKMP_DOI_IPSEC		1
-
-/* Notify message types.  */
+enum isakmp_doi_enum {
+	ISAKMP_DOI_GENERIC = 0,
+	ISAKMP_DOI_IPSEC
+};
+
+/* Notify message types (error: 1-16383; status: 16384-65535).  */
 enum isakmp_notify_enum {
 	ISAKMP_N_INVALID_PAYLOAD_TYPE = 1,
 	ISAKMP_N_DOI_NOT_SUPPORTED,
@@ -99,11 +109,34 @@
 	ISAKMP_N_IPSEC_RESPONDER_LIFETIME = 24576,
 	ISAKMP_N_IPSEC_REPLAY_STATUS,
 	ISAKMP_N_IPSEC_INITIAL_CONTACT,
-	ISAKMP_N_CISCO_LOAD_BALANCE = 40501
+	ISAKMP_N_CISCO_HELLO = 30000,
+	ISAKMP_N_CISCO_WWTEBR,
+	ISAKMP_N_CISCO_SHUT_UP,
+	ISAKMP_N_IOS_KEEP_ALIVE_REQ = 32768,
+	ISAKMP_N_IOS_KEEP_ALIVE_ACK,
+	ISAKMP_N_R_U_THERE = 36136,
+	ISAKMP_N_R_U_THERE_ACK,
+	ISAKMP_N_CISCO_LOAD_BALANCE = 40501,
+	ISAKMP_N_CISCO_PRESHARED_KEY_HASH = 40503
+};
+
+/* Certificate types.  */
+enum isakmp_certificate_enum {
+	ISAKMP_CERT_NONE = 0,
+	ISAKMP_CERT_PKCS7_X509,
+	ISAKMP_CERT_PGP,
+	ISAKMP_CERT_DNS_SIG_KEY,
+	ISAKMP_CERT_X509_SIG,
+	ISAKMP_CERT_X509_KEX_EXCHANGE,
+	ISAKMP_CERT_KERBEROS_TOKENS,
+	ISAKMP_CERT_CRL,
+	ISAKMP_CERT_ARL,
+	ISAKMP_CERT_SPKI,
+	ISAKMP_CERT_X509_ATTRIBUTE
 };
 
 /* IKE attribute types.  */
-enum {
+enum ike_attr_enum {
 	IKE_ATTRIB_ENC = 1,
 	IKE_ATTRIB_HASH,
 	IKE_ATTRIB_AUTH_METHOD,
@@ -124,8 +157,9 @@
 };
 
 /* IKE encryption algorithm IDs.  */
-enum {
-	IKE_ENC_DES_CBC = 1,
+enum ike_enc_enum {
+	IKE_ENC_NO_CBC = 0,
+	IKE_ENC_DES_CBC,
 	IKE_ENC_IDEA_CBC,
 	IKE_ENC_BLOWFISH_CBC,
 	IKE_ENC_RC5_R16_B16_CBC,
@@ -135,7 +169,7 @@
 };
 
 /* IKE hash algorithm IDs.  */
-enum {
+enum ike_hash_enum {
 	IKE_HASH_MD5 = 1,
 	IKE_HASH_SHA,
 	IKE_HASH_TIGER,
@@ -145,7 +179,7 @@
 };
 
 /* IKE authentication method IDs.  */
-enum {
+enum ike_auth_enum {
 	IKE_AUTH_PRESHARED = 1,
 	IKE_AUTH_DSS,
 	IKE_AUTH_RSA_SIG,
@@ -154,6 +188,10 @@
 	IKE_AUTH_EL_GAMAL_ENC,
 	IKE_AUTH_EL_GAMAL_ENC_REV,
 	IKE_AUTH_ECDSA_SIG,
+	IKE_AUTH_HybridInitRSA = 64221,
+	IKE_AUTH_HybridRespRSA,
+	IKE_AUTH_HybridInitDSS,
+	IKE_AUTH_HybridRespDSS,
 	IKE_AUTH_XAUTHInitPreShared = 65001,
 	IKE_AUTH_XAUTHRespPreShared,
 	IKE_AUTH_XAUTHInitDSS,
@@ -167,7 +205,7 @@
 };
 
 /* IKE group IDs.  */
-enum {
+enum ike_group_enum {
 	IKE_GROUP_MODP_768 = 1,
 	IKE_GROUP_MODP_1024,
 	IKE_GROUP_EC2N_155,
@@ -180,29 +218,31 @@
 	IKE_GROUP_EC2N_409sect,
 	IKE_GROUP_EC2N_409K,
 	IKE_GROUP_EC2N_571sect,
-	IKE_GROUP_EC2N_571K,
+	IKE_GROUP_EC2N_571K
 };
 
 /* IKE group type IDs.  */
-enum {
+enum ike_group_type_enum {
 	IKE_GROUP_TYPE_MODP = 1,
 	IKE_GROUP_TYPE_ECP,
 	IKE_GROUP_TYPE_EC2N
 };
 
 /* IKE life type IDs.  */
-enum {
+enum ike_life_enum {
 	IKE_LIFE_TYPE_SECONDS = 1,
 	IKE_LIFE_TYPE_K
 };
 
 /* IPSEC situation masks.  */
-#define ISAKMP_IPSEC_SIT_IDENTITY_ONLY	0x01
-#define ISAKMP_IPSEC_SIT_SECRECY	0x02
-#define ISAKMP_IPSEC_SIT_INTEGRITY	0x04
+enum isakmp_ipsec_sit_enum {
+	ISAKMP_IPSEC_SIT_IDENTITY_ONLY = 0x1,
+	ISAKMP_IPSEC_SIT_SECRECY       = 0x2,
+	ISAKMP_IPSEC_SIT_INTEGRITY     = 0x4
+};
 
 /* IPSEC Identification types.  */
-enum {
+enum isakmp_ipsec_id_enum {
 	ISAKMP_IPSEC_ID_RESERVED = 0,
 	ISAKMP_IPSEC_ID_IPV4_ADDR,
 	ISAKMP_IPSEC_ID_FQDN,
@@ -218,22 +258,23 @@
 };
 
 /* IPSEC protocol IDs.  */
-enum {
+enum isakmp_ipsec_proto_enum {
 	ISAKMP_IPSEC_PROTO_RESERVED = 0,
 	ISAKMP_IPSEC_PROTO_ISAKMP,
 	ISAKMP_IPSEC_PROTO_IPSEC_AH,
 	ISAKMP_IPSEC_PROTO_IPSEC_ESP,
-	ISAKMP_IPSEC_PROTO_IPCOMP
+	ISAKMP_IPSEC_PROTO_IPCOMP,
+	ISAKMP_IPSEC_PROTO_MODECFG = 512 /* hack for simplicity in debug code */
 };
 
 /* IPSEC transform IDs.  */
-enum {
+enum isakmp_ipsec_key_enum {
 	ISAKMP_IPSEC_KEY_RESERVED = 0,
 	ISAKMP_IPSEC_KEY_IKE
 };
 
 /* IPSEC AH IDs.  */
-enum {
+enum isakmp_ipsec_ah_enum {
 	ISAKMP_IPSEC_AH_RESERVED = 0,
 	ISAKMP_IPSEC_AH_MD5 = 2,
 	ISAKMP_IPSEC_AH_SHA,
@@ -245,7 +286,7 @@
 };
 
 /* IPSEC ESP IDs.  */
-enum {
+enum isakmp_ipsec_esp_enum {
 	ISAKMP_IPSEC_ESP_RESERVED = 0,
 	ISAKMP_IPSEC_ESP_DES_IV64,
 	ISAKMP_IPSEC_ESP_DES,
@@ -264,11 +305,11 @@
 	ISAKMP_IPSEC_ESP_AES_RC6,
 	ISAKMP_IPSEC_ESP_AES_RIJNDAEL,
 	ISAKMP_IPSEC_ESP_AES_SERPENT,
-	ISAKMP_IPSEC_ESP_AES_TWOFISH,
+	ISAKMP_IPSEC_ESP_AES_TWOFISH
 };
 
 /* IPSEC attribute types.  */
-enum {
+enum isakmp_ipsec_attr_enum {
 	ISAKMP_IPSEC_ATTRIB_SA_LIFE_TYPE = 1,
 	ISAKMP_IPSEC_ATTRIB_SA_LIFE_DURATION,
 	ISAKMP_IPSEC_ATTRIB_GROUP_DESC,
@@ -282,7 +323,7 @@
 };
 
 /* IPSEC compression IDs.  */
-enum {
+enum isakmp_ipsec_ipcomp_enum {
 	ISAKMP_IPSEC_IPCOMP_RESERVED = 0,
 	ISAKMP_IPSEC_IPCOMP_OUI,
 	ISAKMP_IPSEC_IPCOMP_DEFLATE,
@@ -291,13 +332,13 @@
 };
 
 /* IPSEC lifetime attribute values.  */
-enum {
+enum ipsec_life_enum {
 	IPSEC_LIFE_SECONDS = 1,
 	IPSEC_LIFE_K
 };
 
 /* IPSEC encapsulation attribute numbers.  */
-enum {
+enum ipsec_encap_enum {
 	IPSEC_ENCAP_TUNNEL = 1,
 	IPSEC_ENCAP_TRANSPORT,
 	IPSEC_ENCAP_UDP_TUNNEL,
@@ -307,7 +348,7 @@
 };
 
 /* IPSEC authentication attribute numbers.  */
-enum {
+enum ipsec_auth_enum {
 	IPSEC_AUTH_HMAC_MD5 = 1,
 	IPSEC_AUTH_HMAC_SHA,
 	IPSEC_AUTH_DES_MAC,
@@ -317,54 +358,40 @@
 /* Other numbers.  */
 #define ISAKMP_COOKIE_LENGTH		8
 #define ISAKMP_VERSION			0x10
+/* offsets */
 #define ISAKMP_EXCHANGE_TYPE_O		18
 #define ISAKMP_I_COOKIE_O		0
 #define ISAKMP_R_COOKIE_O		8
 #define ISAKMP_MESSAGE_ID_O		20
 #define ISAKMP_PAYLOAD_O		28
 
-/* Support for draft-ietf-ipsec-isakmp-xauth-06.txt (yuk).  */
-#define XAUTH_VENDOR_ID { 0x09, 0x00, 0x26, 0x89, 0xDF, 0xD6, 0xB7, 0x12 }
-/* From dead-peer-detection RFC 3706 */
-#define DPD_VENDOR_ID { 0xAF, 0xCA, 0xD7, 0x13, 0x68, 0xA1, 0xF1, 0xC9, \
-	0x6B, 0x86, 0x96, 0xFC, 0x77, 0x57, 0x01, 0x00}
-#define UNITY_VENDOR_ID { 0x12, 0xF5, 0xF2, 0x8C, 0x45, 0x71, 0x68, 0xA9, \
-	0x70, 0x2D, 0x9F, 0xE2, 0x74, 0xCC, 0x01, 0x00 }
-#define UNKNOWN_VENDOR_ID { 0x12, 0x6E, 0x1F, 0x57, 0x72, 0x91, 0x15, 0x3B, \
-	0x20, 0x48, 0x5F, 0x7F, 0x15, 0x5B, 0x4B, 0xC8 }
-/* Support for draft-ietf-ipsec-nat-t-ike-02 */
-#define NATT_VENDOR_ID { 0x90, 0xCB, 0x80, 0x91, 0x3E, 0xBB, 0x69, 0x6E, \
-	0x08, 0x63, 0x81, 0xB5, 0xEC, 0x42, 0x7B, 0x1F }
-#define NATSI_VENDOR_ID { 0x4E, 0x61, 0x54, 0x2D, 0x53, 0x49, 0x01, 0x87, \
-	0x77, 0xC1, 0x7D, 0x76, 0xEE, 0xDA, 0xFE, 0xE5, 0xF9, 0x39, 0xF8, \
-	0xE9, 0xE9, 0xA0, 0x35, 0x1B, 0x51, 0x14 }
-#define BNEC_VENDOR_ID { 0x42, 0x4E, 0x45, 0x43, 0x00, 0x00, 0x00, 0x04 }
-
-enum {
-	ISAKMP_XAUTH_ATTRIB_TYPE = 0xd /* 16520 */,
-	ISAKMP_XAUTH_ATTRIB_USER_NAME,
-	ISAKMP_XAUTH_ATTRIB_USER_PASSWORD,
-	ISAKMP_XAUTH_ATTRIB_PASSCODE,
-	ISAKMP_XAUTH_ATTRIB_MESSAGE,
-	ISAKMP_XAUTH_ATTRIB_CHALLENGE,
-	ISAKMP_XAUTH_ATTRIB_DOMAIN,
-	ISAKMP_XAUTH_ATTRIB_STATUS,
-	ISAKMP_XAUTH_ATTRIB_NEXT_PIN,
-	ISAKMP_XAUTH_ATTRIB_ANSWER, /* TYPE .. ANSWER is excluded from dump */
-	/* strange cisco things ... need docs! */
-	ISAKMP_XAUTH_ATTRIB_CISCOEXT_VENDOR = 32136,
-};
+/* defined in vpnc.c */
+extern const unsigned char VID_XAUTH[];
+extern const unsigned char VID_DPD[];
+extern const unsigned char VID_UNITY[];
+extern const unsigned char VID_UNKNOWN[];
+extern const unsigned char VID_NATT_00[];
+extern const unsigned char VID_NATT_01[];
+extern const unsigned char VID_NATT_02[];
+extern const unsigned char VID_NATT_02N[];
+extern const unsigned char VID_NATT_RFC[];
 
 /* Support for draft-ietf-ipsec-isakmp-mode-cfg-05.txt (yuk).  */
-
-enum {
+enum isakmp_modecfg_cfg_enum {
 	ISAKMP_MODECFG_CFG_REQUEST = 1,
 	ISAKMP_MODECFG_CFG_REPLY,
 	ISAKMP_MODECFG_CFG_SET,
 	ISAKMP_MODECFG_CFG_ACK
 };
 
-enum {
+#ifndef NORTELVPN
+   #define ISAKMP_XAUTH_ATTRIB_TYPE_VALUE 0x4088
+#else
+   #define ISAKMP_XAUTH_ATTRIB_TYPE_VALUE 0x0d
+#endif
+
+
+enum isakmp_modecfg_attrib_enum {
 	ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_ADDRESS = 1,
 	ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_NETMASK,
 	ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_DNS,
@@ -380,16 +407,28 @@
 	ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_SUBNET,
 	ISAKMP_MODECFG_ATTRIB_SUPPORTED_ATTRIBUTES,
 	ISAKMP_MODECFG_ATTRIB_INTERNAL_IP6_SUBNET,
-	ISAKMP_MODECFG_ATTRIB_CISCO_BANNER = 28672,
+	ISAKMP_XAUTH_ATTRIB_TYPE = ISAKMP_XAUTH_ATTRIB_TYPE_VALUE,
+	ISAKMP_XAUTH_ATTRIB_USER_NAME,
+	ISAKMP_XAUTH_ATTRIB_USER_PASSWORD,
+	ISAKMP_XAUTH_ATTRIB_PASSCODE,
+	ISAKMP_XAUTH_ATTRIB_MESSAGE,
+	ISAKMP_XAUTH_ATTRIB_CHALLENGE,
+	ISAKMP_XAUTH_ATTRIB_DOMAIN,
+	ISAKMP_XAUTH_ATTRIB_STATUS,
+	ISAKMP_XAUTH_ATTRIB_NEXT_PIN,
+	ISAKMP_XAUTH_ATTRIB_ANSWER, /* TYPE .. ANSWER is excluded from dump */
+	ISAKMP_MODECFG_ATTRIB_CISCO_BANNER = 0x7000,
 	ISAKMP_MODECFG_ATTRIB_CISCO_SAVE_PW,
 	ISAKMP_MODECFG_ATTRIB_CISCO_DEF_DOMAIN,
 	ISAKMP_MODECFG_ATTRIB_CISCO_SPLIT_DNS,
 	ISAKMP_MODECFG_ATTRIB_CISCO_SPLIT_INC,
 	ISAKMP_MODECFG_ATTRIB_CISCO_UDP_ENCAP_PORT,
-	ISAKMP_MODECFG_ATTRIB_CISCO_DO_PFS = 28679,
+	ISAKMP_MODECFG_ATTRIB_CISCO_UNKNOWN, /* whatever 0x7006 is... */
+	ISAKMP_MODECFG_ATTRIB_CISCO_DO_PFS,
 	ISAKMP_MODECFG_ATTRIB_CISCO_FW_TYPE,
 	ISAKMP_MODECFG_ATTRIB_CISCO_BACKUP_SERVER,
 	ISAKMP_MODECFG_ATTRIB_CISCO_DDNS_HOSTNAME,
+	ISAKMP_XAUTH_ATTRIB_CISCOEXT_VENDOR = 0x7d88 /* strange cisco things ... need docs! */
 };
 
 #endif

Added: branches/vpnc-nortel/makeman.pl
==============================================================================
--- branches/vpnc-nortel/makeman.pl (added)
+++ branches/vpnc-nortel/makeman.pl Wed Jun 11 01:19:11 2008
@@ -1,0 +1,132 @@
+#! /usr/bin/perl -w
+
+# $Id$
+
+# Written by Wolfram Sang (wolfram at the-dreams.de) in 2007,
+# some inspiration from help2man by Brendan O'Dea and from Perl::Critic
+
+# Generate the vpnc-manpage from a template and the --long-help-output.
+# Version 0.2
+
+# Command-line options: none
+# Files needed        : ./vpnc ./vpnc.8.template ./VERSION
+# Files created       : ./vpnc.8
+# Exit status         : errno-values or 255 (Magic string not found)
+
+# Distributed under the same licence as vpnc.
+
+use strict;
+use Fatal    qw(open close);
+use filetest qw(access);	# to always get errno-values on filetests
+use POSIX    qw(strftime setlocale LC_ALL);
+
+my $vpnc = './vpnc';
+-e $vpnc or die "$0: Can't find $vpnc. Did you compile it?\n";
+-x $vpnc or die "$0: Can't execute $vpnc. Please check permissions.\n";
+
+# The code converting the help-output to manpage format is lots of
+# regex-fiddling, sorry. It got a bit more complicated by additionally
+# indenting lists (those originally starting with an asterisk). I hope
+# this pays off when converting the manpage to HTML or such.
+
+open my $LONGHELP, '-|', "$vpnc --long-help";
+my $vpnc_options    = '';
+my $relative_indent = 0;
+my $indent_needed   = 0;
+
+while (<$LONGHELP>) {
+    if (/^  /) {
+
+	# Check if additional indent needs to be finished by comparing the
+	# amount of spaces at the beginning. A bit ugly, but I don't see a
+	# better way to do it.
+	if ($relative_indent) {
+	    /^( *)/;
+	    if (length($1) < $relative_indent) {
+		$vpnc_options .= ".RE\n";
+		$relative_indent = 0;
+		$indent_needed = 1;
+	    }
+	}
+	
+	# Highlight the option and make an optional argument italic.
+	if (s/^ *(--[\w-]+)/\n.TP\n.BI "$1"/) {
+	    s/(<.+>)/ " $1"/;
+	}
+	
+	# Highlight conffile-only options.
+	s/^ *(\(configfile only option\))/\n.TP\n.B $1/;
+
+	# Position the Default-string
+	s/^ *(Default:)/.IP\n$1/;
+
+	# Highlight the conf-variable and make an optional argument italic.
+	if (s/^ *(conf-variable:) (.+?) ?([<\n])/.P\n$1\n.BI "$2"$3/) {
+	    s/(<.+>)/ " $1"/;
+	}
+
+	# Replace asterisk with bulletin; indent if needed.
+	if (s/^( +)\* /.IP \\(bu\n/) {
+	    if (not $relative_indent) {
+		$vpnc_options .= ".RS\n";
+	        $relative_indent = length $1;
+	    }
+	}
+
+	# Do we need to add an .IP-command after .RE or is there already one?
+	if ($indent_needed and not /^\n?\.[TI]?P/) {
+	    $vpnc_options .= ".IP\n";
+	    $indent_needed = 0;
+	}
+	
+	# Finalize string and add it to buffer
+        s/^ *//;
+	s/ *$//;
+	s/-/\\-/g;
+        $vpnc_options .= $_;
+    }
+}
+close $LONGHELP;
+
+# Hopefully the code speaks for itself from now on...
+
+setlocale( LC_ALL, 'C' );
+my $date = strftime( '%B %Y', localtime );
+
+open my $VERSION, '<', './VERSION';
+my $vpnc_version = <$VERSION>;
+close $VERSION;
+chomp $vpnc_version;
+
+open my $TEMPLATE, '<', './vpnc.8.template';
+open my $MANPAGE , '>', './vpnc.8';
+my $magic_found;
+my $MAGIC_FOR_HEADER  = qq(.\\" ###makeman.pl: Replace header here!\n);
+my $MAGIC_FOR_OPTIONS = qq(.\\" ###makeman.pl: Insert options from help-output here!\n);
+
+# Skip the template-header
+while (<$TEMPLATE>) {
+    last if ($magic_found = ($_ eq $MAGIC_FOR_HEADER));
+}
+die "$0: Missing magic: $MAGIC_FOR_HEADER" if not $magic_found;
+
+print {$MANPAGE} <<"END_MANPAGE_HEADER";
+.\\" This manpage is generated!
+.\\" Please edit the template-file in the source-distribution only.
+.TH VPNC "8" "$date" "vpnc version $vpnc_version" "System Administration Utilities"
+END_MANPAGE_HEADER
+
+$magic_found = 0;
+
+while (<$TEMPLATE>) {
+    if ($_ ne $MAGIC_FOR_OPTIONS) {
+	print {$MANPAGE} $_;
+    } else {
+	print {$MANPAGE} $vpnc_options;
+	$magic_found = 1;
+    }
+}
+die "$0: Missing magic: $MAGIC_FOR_OPTIONS" if not $magic_found;
+
+close $TEMPLATE;
+close $MANPAGE;

Modified: branches/vpnc-nortel/math_group.c
==============================================================================
--- branches/vpnc-nortel/math_group.c (original)
+++ branches/vpnc-nortel/math_group.c Wed Jun 11 01:19:11 2008
@@ -43,17 +43,15 @@
 #include "math_group.h"
 
 /* We do not want to export these definitions.  */
-int modp_getlen(struct group *);
-void modp_getraw(struct group *, gcry_mpi_t, unsigned char *);
-int modp_setraw(struct group *, gcry_mpi_t, unsigned char *, int);
-int modp_setrandom(struct group *, gcry_mpi_t);
-int modp_operation(struct group *, gcry_mpi_t, gcry_mpi_t, gcry_mpi_t);
-
-struct modp_group {
-	gcry_mpi_t gen; /* Generator */
-	gcry_mpi_t p; /* Prime */
-	gcry_mpi_t a, b, c, d;
-};
+static void modp_free(struct group *);
+static struct group *modp_clone(struct group *, struct group *);
+static void modp_init(struct group *);
+
+static int modp_getlen(struct group *);
+static void modp_getraw(struct group *, gcry_mpi_t, unsigned char *);
+static int modp_setraw(struct group *, gcry_mpi_t, unsigned char *, int);
+static int modp_setrandom(struct group *, gcry_mpi_t);
+static int modp_operation(struct group *, gcry_mpi_t, gcry_mpi_t, gcry_mpi_t);
 
 /*
  * This module provides access to the operations on the specified group
@@ -68,7 +66,7 @@
  * group order, e.g. q = 2**768.
  */
 
-struct modp_dscr oakley_modp[] = {
+static const struct modp_dscr oakley_modp[] = {
 	{
 		OAKLEY_GRP_1, 72, /* This group is insecure, only sufficient for DES */
 		"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
@@ -102,9 +100,9 @@
 };
 
 /* XXX I want to get rid of the casting here.  */
-struct group groups[] = {
-	{
-		MODP, OAKLEY_GRP_1, 0, &oakley_modp[0], 0, 0, 0, 0, 0,
+static struct group groups[] = {
+	{
+		MODP, OAKLEY_GRP_1, 0, NULL, &oakley_modp[0], NULL, NULL, NULL, NULL, NULL,
 		(int (*)(struct group *))modp_getlen,
 		(void (*)(struct group *, void *, unsigned char *))modp_getraw,
 		(int (*)(struct group *, void *, unsigned char *, int))modp_setraw,
@@ -112,7 +110,7 @@
 		(int (*)(struct group *, void *, void *, void *))modp_operation
 	},
 	{
-		MODP, OAKLEY_GRP_2, 0, &oakley_modp[1], 0, 0, 0, 0, 0,
+		MODP, OAKLEY_GRP_2, 0, NULL, &oakley_modp[1], NULL, NULL, NULL, NULL, NULL,
 		(int (*)(struct group *))modp_getlen,
 		(void (*)(struct group *, void *, unsigned char *))modp_getraw,
 		(int (*)(struct group *, void *, unsigned char *, int))modp_setraw,
@@ -120,7 +118,7 @@
 		(int (*)(struct group *, void *, void *, void *))modp_operation
 	},
 	{
-		MODP, OAKLEY_GRP_5, 0, &oakley_modp[2], 0, 0, 0, 0, 0,
+		MODP, OAKLEY_GRP_5, 0, NULL, &oakley_modp[2], NULL, NULL, NULL, NULL, NULL,
 		(int (*)(struct group *))modp_getlen,
 		(void (*)(struct group *, void *, unsigned char *))modp_getraw,
 		(int (*)(struct group *, void *, unsigned char *, int))modp_setraw,
@@ -168,7 +166,7 @@
 	free(grp);
 }
 
-struct group *modp_clone(struct group *new, struct group *clone)
+static struct group *modp_clone(struct group *new, struct group *clone)
 {
 	struct modp_group *new_grp, *clone_grp = clone->group;
 
@@ -193,7 +191,7 @@
 	return new;
 }
 
-void modp_free(struct group *old)
+static void modp_free(struct group *old)
 {
 	struct modp_group *grp = old->group;
 
@@ -206,9 +204,9 @@
 	free(grp);
 }
 
-void modp_init(struct group *group)
-{
-	struct modp_dscr *dscr = (struct modp_dscr *)group->group;
+static void modp_init(struct group *group)
+{
+	const struct modp_dscr *dscr = group->group_dscr;
 	struct modp_group *grp;
 
 	grp = malloc(sizeof *grp);
@@ -216,8 +214,8 @@
 
 	group->bits = dscr->bits;
 
-	gcry_mpi_scan(&grp->p, GCRYMPI_FMT_HEX, dscr->prime, 0, NULL);
-	gcry_mpi_scan(&grp->gen, GCRYMPI_FMT_HEX, dscr->gen, 0, NULL);
+	gcry_mpi_scan(&grp->p, GCRYMPI_FMT_HEX, (const unsigned char*)dscr->prime, 0, NULL);
+	gcry_mpi_scan(&grp->gen, GCRYMPI_FMT_HEX, (const unsigned char *)dscr->gen, 0, NULL);
 
 	grp->a = gcry_mpi_new(group->bits);
 	grp->b = gcry_mpi_new(group->bits);
@@ -231,14 +229,14 @@
 	group->group = grp;
 }
 
-int modp_getlen(struct group *group)
+static int modp_getlen(struct group *group)
 {
 	struct modp_group *grp = (struct modp_group *)group->group;
 
 	return (gcry_mpi_get_nbits(grp->p) + 7) / 8;
 }
 
-void modp_getraw(struct group *grp, gcry_mpi_t v, unsigned char *d)
+static void modp_getraw(struct group *grp, gcry_mpi_t v, unsigned char *d)
 {
 	size_t l, l2;
 	unsigned char *tmp;
@@ -258,11 +256,11 @@
 #endif
 }
 
-int modp_setraw(struct group *grp, gcry_mpi_t d, unsigned char *s, int l)
+static int modp_setraw(struct group *grp, gcry_mpi_t d, unsigned char *s, int l)
 {
 	int i;
 
-	grp = 0; /* unused */
+	grp = NULL; /* unused */
 
 	gcry_mpi_set_ui(d, 0);
 	for (i = 0; i < l; i++) {
@@ -280,7 +278,7 @@
 	return 0;
 }
 
-int modp_setrandom(struct group *grp, gcry_mpi_t d)
+static int modp_setrandom(struct group *grp, gcry_mpi_t d)
 {
 	int i, l = grp->getlen(grp);
 	uint32_t tmp = 0;
@@ -289,7 +287,7 @@
 
 	for (i = 0; i < l; i++) {
 		if (i % 4)
-			gcry_randomize((unsigned char *)&tmp, sizeof(tmp), GCRY_WEAK_RANDOM);
+			gcry_randomize((unsigned char *)&tmp, sizeof(tmp), GCRY_STRONG_RANDOM);
 
 		gcry_mpi_mul_2exp(d, d, 8);
 		gcry_mpi_add_ui(d, d, tmp & 0xFF);
@@ -298,7 +296,7 @@
 	return 0;
 }
 
-int modp_operation(struct group *group, gcry_mpi_t d, gcry_mpi_t a, gcry_mpi_t e)
+static int modp_operation(struct group *group, gcry_mpi_t d, gcry_mpi_t a, gcry_mpi_t e)
 {
 	struct modp_group *grp = (struct modp_group *)group->group;
 

Modified: branches/vpnc-nortel/math_group.h
==============================================================================
--- branches/vpnc-nortel/math_group.h (original)
+++ branches/vpnc-nortel/math_group.h Wed Jun 11 01:19:11 2008
@@ -35,8 +35,10 @@
 #ifndef __MATH_GROUP_H__
 #define __MATH_GROUP_H__
 
+#include <gcrypt.h>
+
 enum groups {
-	MODP, /* F_p, Z modulo a prime */
+	MODP  /* F_p, Z modulo a prime */
 };
 
 #define OAKLEY_GRP_1	1
@@ -47,11 +49,27 @@
  * The group on which diffie hellmann calculations are done.
  */
 
+/* Description of F_p for Boot-Strapping */
+
+struct modp_dscr {
+	int id;
+	int bits; /* Key Bits provided by this group */
+	const char *prime; /* Prime */
+	const char *gen; /* Generator */
+};
+
+struct modp_group {
+	gcry_mpi_t gen; /* Generator */
+	gcry_mpi_t p; /* Prime */
+	gcry_mpi_t a, b, c, d;
+};
+
 struct group {
 	enum groups type;
 	int id; /* Group ID */
 	int bits; /* Number of key bits provided by this group */
-	void *group;
+	struct modp_group *group;
+	const struct modp_dscr *group_dscr;
 	void *a, *b, *c, *d;
 	void *gen; /* Group Generator */
 	int (*getlen) (struct group *);
@@ -61,23 +79,10 @@
 	int (*operation) (struct group *, void *, void *, void *);
 };
 
-/* Description of F_p for Boot-Strapping */
-
-struct modp_dscr {
-	int id;
-	int bits; /* Key Bits provided by this group */
-	char *prime; /* Prime */
-	char *gen; /* Generator */
-};
-
 /* Prototypes */
 
 void group_init(void);
 void group_free(struct group *);
 struct group *group_get(int);
 
-void modp_free(struct group *);
-struct group *modp_clone(struct group *, struct group *);
-void modp_init(struct group *);
-
 #endif /* _MATH_GROUP_H_ */

Added: branches/vpnc-nortel/mk-version
==============================================================================
--- branches/vpnc-nortel/mk-version (added)
+++ branches/vpnc-nortel/mk-version Wed Jun 11 01:19:11 2008
@@ -1,0 +1,18 @@
+#!/bin/sh
+
+# print vpnc version from file VERSION, appending the string printed
+# by svnversion(1) if appropriate
+
+_version="`cat VERSION`"
+
+if [ -d .svn ]; then
+		if command -v svnversion >/dev/null; then
+				_version="$_version-`svnversion`"
+		else
+				_version="$_version-`sed -n '/^dir$/{n;p;q;}' .svn/entries`"
+		fi
+fi
+
+echo "$_version"
+
+exit 0

Modified: branches/vpnc-nortel/nortel.txt
==============================================================================
--- branches/vpnc-nortel/nortel.txt (original)
+++ branches/vpnc-nortel/nortel.txt Wed Jun 11 01:19:11 2008
@@ -42,3 +42,48 @@
 and send patches.
 
 Matt
+----------------
+I've tried to connect to my corporate VPN, but the only message I got was:
+
+vpnc: xauth packet unsupported: ATTRIBUTES_NOT_SUPPORTED
+
+Problem was found in xauth_type. In my case request attribute "xauth_type" consisted 5 as value. There is quick 
+fix for this issue below. But...
+
+In the chapter 6.2 of the draft-beaulieu-ike-xauth-02.txt we can read:
+
+"XAUTH-TYPE - The type of extended authentication requested whose
+   values are described in the next section.  This is an optional
+   attribute for the ISAKMP_CFG_REQUEST and ISAKMP_CFG_REPLY messages.
+   If the XAUTH-TYPE is not present, then it is assumed to be Generic.
+   The XAUTH-TYPE in a REPLY MUST be identical to the XAUTH-TYPE in the
+   REQUEST."
+
+So I think the better way is to send reply with the same xauth_type value as in request. But I can't test it 
+because our server always use 5 and never 0.
+
+Thanks.
+----
+Volodymyr Buell
+
+------------ This ended up in the folowin patch
+        if (ap->af != isakmp_attr_16 || !(ap->u.attr_16 == 0 || ap->u.attr_16 == 5))
+          reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
+                xauth_type_requested = ap->u.attr_16;
+----------------------
+
+This patch didn't work in my NortelVPN setup I mad a buildflag of until a proper fix can be made
+so please set NORTELVPN_XAUTHTYPE_AS_REQUEST flag to vpnc.c to get this
+
+#ifdef NORTELVPN_XAUTHTYPE_AS_REQUEST
+        if (ap->af != isakmp_attr_16 || !(ap->u.attr_16 == 0 || ap->u.attr_16 == 5))
+          reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
+                xauth_type_requested = ap->u.attr_16;
+#else
+
+        if (ap->af != isakmp_attr_16 || ap->u.attr_16 != 0)
+          reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
+#endif
+
+
+/Zingo Andersen

Modified: branches/vpnc-nortel/pcf2vpnc
==============================================================================
--- branches/vpnc-nortel/pcf2vpnc (original)
+++ branches/vpnc-nortel/pcf2vpnc Wed Jun 11 01:19:11 2008
@@ -1,7 +1,8 @@
 #!/usr/bin/perl
 # Stefan Tomanek <stefan at pico.ruhr.de>
+# updated by Wolfram Sang <ninja at the-dreams.de> on 21.10.06 and on 26.06.07
 ##
-# pcf2vpnc.pl <pcf file> <vpnc config>
+# pcf2vpnc <pcf file> [vpnc file]
 ##
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -16,48 +17,28 @@
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+# $Id$
 
 use IO::File;
-use LWP::Simple;
-use CGI;
 use strict;
 use warnings;
 
-sub decodePW($$) {
-    my ($encPW, $type) = @_;
-    
-    my $confirm;
-    
-    print STDERR "To decrypt the $type password, it will be sent to the website\n";
-    print STDERR "'http://www.unix-ag.uni-kl.de/~massar/bin/cisco-decode'. If\n";
-    print STDERR "you are absolutely sure that you want to do this, enter 'yes',\n";
-    print STDERR "otherwise just enter anything else.\n";
-    print STDERR " > ";
-    read STDIN, $confirm, 3;
-    
-    unless ($confirm eq "yes") {
-	print STDERR "Password decryption has been omitted!";
-	return "";
-    }
-    
-    print STDERR "Decrypting password...";
-    
-    my $q = new CGI;
-    my $result = get("http://www.unix-ag.uni-kl.de/~massar/bin/cisco-decode?enc=".$encPW);
-    
-    if ($result =~ /clear: (.*?)\n/) {
-	print STDERR "done.\n";
-	return $q->unescapeHTML($1);
-    } else {
-	print STDERR "Error!\n";
-	return ""
-    }
+my %authmode = ( 1 => 'psk', 3 => 'cert', 5 => 'hybrid' );
+my $needs_cert = 0;
+my $no_decrypt = 0;
+if (system("cisco-decrypt", "q") == -1) {
+    $no_decrypt = 1;
+    print STDERR "cisco-decrypt not in search path,\n";
+    print STDERR "  adding passwords in obfuscated form\n";
 }
 
 sub readPCF($) {
     my ($file) = @_;
     my %config;
     while (<$file>) {
+	# Filter unnecessary chars at beginning & end of line
+	s/^!*//; s/[\r ]*$//;
 	if (/^(.*?)=(.*?)$/) {
 	    # We don't need emtpy config strings
 	    next if ($2 eq "");
@@ -65,14 +46,31 @@
 		$config{IPSec}{gateway} = $2;
 	    } elsif ($1 eq "GroupName") {
 		$config{IPSec}{ID} = $2;
+	    } elsif ($1 eq "GroupPwd") {
+		$config{IPSec}{secret} = $2;
 	    } elsif ($1 eq "enc_GroupPwd") {
-		$config{IPSec}{secret} = decodePW($2, "group");
+		if ($no_decrypt) {
+		    $config{IPSec}{obfuscated} = "secret $2";
+		} else {
+		    $config{IPSec}{secret} = `cisco-decrypt $2`;
+		}
+	    } elsif ($1 eq "AuthType") {
+		$config{IKE}{Authmode} = $authmode{$2};
+		if ($2 == 3 || $2 == 5) {
+		  $needs_cert = 1;
+		}
+	    } elsif ($1 eq "DHGroup") {
+		$config{IKE}{DH} = "Group dh$2";
 	    } elsif ($1 eq "Username") {
 		$config{Xauth}{username} = $2;
 	    } elsif ($1 eq "UserPassword") {
 		$config{Xauth}{password} = $2;
 	    } elsif ($1 eq "enc_UserPassword") {
-		$config{Xauth}{password} = decodePW($2, "user");
+		if ($no_decrypt) {
+		    $config{Xauth}{obfuscated} = "password $2";
+		} else {
+		    $config{Xauth}{password} = `cisco-decrypt $2`;
+		}
 	    } elsif ($1 eq "NTDomain") {
 		$config{Domain}{""} = $2;
 	    }
@@ -83,7 +81,7 @@
 
 sub writeVPNC($) {
     my ($config) = @_;
-    my $text = "## generated by pcf2vpnc.pl\n## Stefan Tomanek <stefan\@pico.ruhr.de>\n";
+    my $text = "## generated by pcf2vpnc\n";
     foreach my $section (keys %$config) {
 	foreach my $item (keys %{ $config->{$section} }) {
 	    $text .= "$section $item ".$config->{$section}{$item}."\n";
@@ -102,13 +100,18 @@
     my $src = new IO::File($ARGV[0]) || die "Unable to open file ".$ARGV[0]."\n";
     if (defined $ARGV[1]) {
 	my $dst = new IO::File($ARGV[1], "w") || die "Unable to open file ".$ARGV[1]."\n";
-	$dst->write( writeVPNC(readPCF($src)) );
-	$dst->close();
-	print STDERR "Vpnc config written to ".$ARGV[1]."\n";
+	$dst->write( writeVPNC(readPCF($src)) ) || die "Unable to write to file ".$ARGV[1]."\n";
+	$dst->close() || die "Unable to close file ".$ARGV[1]."\n";
+	printf STDERR "vpnc config written to '%s' with permissions '%04o'.\n", $ARGV[1], (stat($ARGV[1]))[2];
+	print  STDERR "Please take care of permissions.\n";
+    } else {
+	print writeVPNC(readPCF($src));
     }
-    print writeVPNC(readPCF($src));
-    $src->close();
+    $src->close() || die "Unable to close file ".$ARGV[0]."\n";
+    if ($needs_cert) {
+	print STDERR "\nDon't forget to copy the needed certificate(s).\n\n";
+    }
 } else {
-    print STDERR "Usage: pcf2vpnc.pl <pcf file>\n";
+    print STDERR "$0 converts VPN-config files from pcf to vpnc-format.\n";
+    print STDERR "Usage: $0 <pcf file> [vpnc file]\n";
 }
-

Added: branches/vpnc-nortel/supp.c
==============================================================================
--- branches/vpnc-nortel/supp.c (added)
+++ branches/vpnc-nortel/supp.c Wed Jun 11 01:19:11 2008
@@ -1,0 +1,132 @@
+/* Algorithm support checks
+   Copyright (C) 2005 Maurice Massar
+   Reorganised 2006 by Dan Villiom Podlaski Christiansen
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   $Id$
+*/
+
+#include "supp.h"
+#include "math_group.h"
+#include "config.h"
+#include "isakmp.h"
+
+#include <gcrypt.h>
+#include <stdlib.h>
+
+const supported_algo_t supp_dh_group[] = {
+	{"nopfs", 0, 0, 0, 0},
+	{"dh1", OAKLEY_GRP_1, IKE_GROUP_MODP_768,  IKE_GROUP_MODP_768,  0},
+	{"dh2", OAKLEY_GRP_2, IKE_GROUP_MODP_1024, IKE_GROUP_MODP_1024, 0},
+	{"dh5", OAKLEY_GRP_5, IKE_GROUP_MODP_1536, IKE_GROUP_MODP_1536, 0},
+	/*{ "dh7", OAKLEY_GRP_7, IKE_GROUP_EC2N_163K, IKE_GROUP_EC2N_163K, 0 } note: code missing */
+	{NULL, 0, 0, 0, 0}
+};
+
+const supported_algo_t supp_hash[] = {
+	{"md5", GCRY_MD_MD5, IKE_HASH_MD5, IPSEC_AUTH_HMAC_MD5, 0},
+	{"sha1", GCRY_MD_SHA1, IKE_HASH_SHA, IPSEC_AUTH_HMAC_SHA, 0},
+	{NULL, 0, 0, 0, 0}
+};
+
+const supported_algo_t supp_crypt[] = {
+	{"null", GCRY_CIPHER_NONE, IKE_ENC_NO_CBC, ISAKMP_IPSEC_ESP_NULL, 0},
+	{"des", GCRY_CIPHER_DES, IKE_ENC_DES_CBC, ISAKMP_IPSEC_ESP_DES, 0},
+	{"3des", GCRY_CIPHER_3DES, IKE_ENC_3DES_CBC, ISAKMP_IPSEC_ESP_3DES, 0},
+	{"aes128", GCRY_CIPHER_AES128, IKE_ENC_AES_CBC, ISAKMP_IPSEC_ESP_AES, 128},
+	{"aes192", GCRY_CIPHER_AES192, IKE_ENC_AES_CBC, ISAKMP_IPSEC_ESP_AES, 192},
+	{"aes256", GCRY_CIPHER_AES256, IKE_ENC_AES_CBC, ISAKMP_IPSEC_ESP_AES, 256},
+	{NULL, 0, 0, 0, 0}
+};
+
+const supported_algo_t supp_auth[] = {
+	{"psk", 0, IKE_AUTH_PRESHARED, 0, 0},
+	{"psk+xauth", 0, IKE_AUTH_XAUTHInitPreShared, 0, 0},
+#ifdef OPENSSL_GPL_VIOLATION
+#if 0
+	{"cert(dsa)", 0, IKE_AUTH_RSA_SIG, 0, 0},
+	{"cert(rsasig)", 0, IKE_AUTH_DSS, 0, 0},
+	{"hybrid(dsa)", 0, IKE_AUTH_DSS, 0, 0},
+#endif /* 0 */
+	{"hybrid(rsa)", 0, IKE_AUTH_HybridInitRSA, 0, 0},
+#endif /* OPENSSL_GPL_VIOLATION */
+	{NULL, 0, 0, 0, 0}
+};
+
+const supported_algo_t *get_algo(enum algo_group what, enum supp_algo_key key, int id,
+	const char *name, int keylen)
+{
+	const supported_algo_t *sa = NULL;
+	int i = 0, val = 0;
+	const char *valname = NULL;
+
+	switch (what) {
+	case SUPP_ALGO_DH_GROUP:
+		sa = supp_dh_group;
+		break;
+	case SUPP_ALGO_HASH:
+		sa = supp_hash;
+		break;
+	case SUPP_ALGO_CRYPT:
+		sa = supp_crypt;
+		break;
+	case SUPP_ALGO_AUTH:
+		sa = supp_auth;
+		break;
+	default:
+		abort();
+	}
+
+	for (i = 0; sa[i].name != NULL; i++) {
+		switch (key) {
+		case SUPP_ALGO_NAME:
+			valname = sa[i].name;
+			break;
+		case SUPP_ALGO_MY_ID:
+			val = sa[i].my_id;
+			break;
+		case SUPP_ALGO_IKE_SA:
+			val = sa[i].ike_sa_id;
+			break;
+		case SUPP_ALGO_IPSEC_SA:
+			val = sa[i].ipsec_sa_id;
+			break;
+		default:
+			abort();
+		}
+		if ((key == SUPP_ALGO_NAME) ? !strcasecmp(name, valname) : (val == id))
+			if (keylen == sa[i].keylen)
+				return sa + i;
+	}
+
+	return NULL;
+}
+
+const supported_algo_t *get_dh_group_ike(void)
+{
+	return get_algo(SUPP_ALGO_DH_GROUP, SUPP_ALGO_NAME, 0, config[CONFIG_IKE_DH], 0);
+}
+const supported_algo_t *get_dh_group_ipsec(int server_setting)
+{
+	const char *pfs_setting = config[CONFIG_IPSEC_PFS];
+
+	if (!strcmp(config[CONFIG_IPSEC_PFS], "server")) {
+		/* treat server_setting == -1 (unknown) as 0 */
+		pfs_setting = (server_setting == 1) ? "dh2" : "nopfs";
+	}
+
+	return get_algo(SUPP_ALGO_DH_GROUP, SUPP_ALGO_NAME, 0, pfs_setting, 0);
+}

Added: branches/vpnc-nortel/supp.h
==============================================================================
--- branches/vpnc-nortel/supp.h (added)
+++ branches/vpnc-nortel/supp.h Wed Jun 11 01:19:11 2008
@@ -1,0 +1,54 @@
+/* Algorithm support checks
+   Copyright (C) 2005 Maurice Massar
+   Reorganised 2006 by Dan Villiom Podlaski Christiansen
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   $Id$
+*/
+
+#ifndef __SUPP_H__
+#define __SUPP_H__
+
+enum supp_algo_key {
+	SUPP_ALGO_NAME,
+	SUPP_ALGO_MY_ID,
+	SUPP_ALGO_IKE_SA,
+	SUPP_ALGO_IPSEC_SA
+};
+
+enum algo_group {
+	SUPP_ALGO_DH_GROUP,
+	SUPP_ALGO_HASH,
+	SUPP_ALGO_CRYPT,
+	SUPP_ALGO_AUTH
+};
+
+typedef struct {
+	const char *name;
+	int my_id, ike_sa_id, ipsec_sa_id;
+	int keylen;
+} supported_algo_t;
+
+extern const supported_algo_t supp_dh_group[];
+extern const supported_algo_t supp_hash[];
+extern const supported_algo_t supp_crypt[];
+extern const supported_algo_t supp_auth[];
+
+extern const supported_algo_t *get_algo(enum algo_group what, enum supp_algo_key key, int id, const char *name, int keylen);
+extern const supported_algo_t *get_dh_group_ike(void);
+extern const supported_algo_t *get_dh_group_ipsec(int server_setting);
+
+#endif

Added: branches/vpnc-nortel/sysdep.c
==============================================================================
--- branches/vpnc-nortel/sysdep.c (added)
+++ branches/vpnc-nortel/sysdep.c Wed Jun 11 01:19:11 2008
@@ -1,0 +1,819 @@
+/* IPSec VPN client compatible with Cisco equipment.
+    Copyright (C) 2007      Maurice Massar
+    Copyright (C) 2007      Paolo Zarpellon <paolo.zarpellon at gmail.com> (Cygwin support)
+
+    based on VTun - Virtual Tunnel over TCP/IP network.
+    Copyright (C) 1998-2000  Maxim Krasnyansky <max_mk at yahoo.com>
+    VTun has been derived from VPPP package by Maxim Krasnyansky. 
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#include <sys/socket.h>
+#include <net/if.h>
+
+#ifdef __sun__
+#include <ctype.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <signal.h>
+#include <stropts.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#endif
+
+#if defined(__CYGWIN__)
+#include <io.h>
+#include <w32api/windef.h>
+#include <w32api/winbase.h>
+#include <w32api/winnt.h>
+#include <w32api/winioctl.h>
+#include <w32api/iphlpapi.h>
+#include <w32api/iptypes.h>
+#include <w32api/winreg.h>
+#include <sys/cygwin.h>
+#endif
+
+#if defined(__DragonFly__)
+#include <net/tun/if_tun.h>
+#elif defined(__linux__)
+#include <linux/if_tun.h>
+#elif defined(__APPLE__)
+/* no header for tun */
+#elif defined(__CYGWIN__)
+#include "tap-win32.h"
+#else
+#include <net/if_tun.h>
+#endif
+
+#include "sysdep.h"
+
+#if !defined(HAVE_VASPRINTF) || !defined(HAVE_ASPRINTF) || !defined(HAVE_ERROR)
+#include <stdarg.h>
+#endif
+
+#if defined(__sun__)
+extern char **environ;
+static int ip_fd = -1, muxid;
+#endif
+
+#if defined(__CYGWIN__)
+/*
+ * Overlapped structures for asynchronous read and write
+ */
+static OVERLAPPED overlap_read, overlap_write;
+
+typedef enum {
+	SEARCH_IF_GUID_FROM_NAME,
+	SEARCH_IF_NAME_FROM_GUID
+} search_if_en;
+#endif
+
+/* 
+ * Allocate TUN/TAP device, returns opened fd. 
+ * Stores dev name in the first arg(must be large enough).
+ */
+#if defined(__sun__)
+int tun_open(char *dev, enum if_mode_enum mode)
+{
+	int tun_fd, if_fd, ppa = -1;
+	struct ifreq ifr;
+	char *ptr;
+
+	if (*dev) {
+		ptr = dev;
+		while (*ptr && !isdigit((int)*ptr))
+			ptr++;
+		ppa = atoi(ptr);
+	}
+
+	if ((ip_fd = open("/dev/ip", O_RDWR, 0)) < 0) {
+		syslog(LOG_ERR, "Can't open /dev/ip");
+		return -1;
+	}
+
+	if ((tun_fd = open(((mode == IF_MODE_TUN) ? "/dev/tun" : "/dev/tap"), O_RDWR, 0)) < 0) {
+		syslog(LOG_ERR, "Can't open /dev/tun");
+		return -1;
+	}
+
+	/* Assign a new PPA and get its unit number. */
+	if ((ppa = ioctl(tun_fd, TUNNEWPPA, ppa)) < 0) {
+		syslog(LOG_ERR, "Can't assign new interface");
+		return -1;
+	}
+
+	if ((if_fd = open(((mode == IF_MODE_TUN) ? "/dev/tun" : "/dev/tap"), O_RDWR, 0)) < 0) {
+		syslog(LOG_ERR, "Can't open /dev/tun (2)");
+		return -1;
+	}
+	if (ioctl(if_fd, I_PUSH, "ip") < 0) {
+		syslog(LOG_ERR, "Can't push IP module");
+		return -1;
+	}
+
+	/* Assign ppa according to the unit number returned by tun device */
+	if (ioctl(if_fd, IF_UNITSEL, (char *)&ppa) < 0 && errno != EEXIST) {
+		syslog(LOG_ERR, "Can't set PPA %d", ppa);
+		return -1;
+	}
+	if ((muxid = ioctl(ip_fd, I_PLINK, if_fd)) < 0) {
+		syslog(LOG_ERR, "Can't link TUN device to IP");
+		return -1;
+	}
+	close(if_fd);
+
+	snprintf(dev, IFNAMSIZ, "%s%d", ((mode == IF_MODE_TUN) ? "tun" : "tap"), ppa);
+
+	memset(&ifr, 0, sizeof(ifr));
+	strcpy(ifr.ifr_name, dev);
+	ifr.ifr_ip_muxid = muxid;
+
+	if (ioctl(ip_fd, SIOCSIFMUXID, &ifr) < 0) {
+		ioctl(ip_fd, I_PUNLINK, muxid);
+		syslog(LOG_ERR, "Can't set multiplexor id");
+		return -1;
+	}
+
+	return tun_fd;
+}
+#elif defined(__CYGWIN__)
+/*
+ * Get interface guid/name from registry
+ */
+static char *search_if(char *value, char *key, search_if_en type)
+{
+	int i = 0;
+	LONG status;
+	DWORD len;
+	HKEY net_conn_key;
+	BOOL found = FALSE;
+	char guid[256];
+	char ifname[256];
+	char conn_string[512];
+	HKEY conn_key;
+	DWORD value_type;
+	
+	if (!value || !key) {
+		return NULL;
+	}
+	
+	status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+		NETWORK_CONNECTIONS_KEY,
+		0,
+		KEY_READ,
+		&net_conn_key);
+	
+	if (status != ERROR_SUCCESS) {
+		printf("Error opening registry key: %s\n", NETWORK_CONNECTIONS_KEY);
+		return NULL;
+	}
+	
+	while (!found) {
+		len = sizeof(guid);
+		status = RegEnumKeyEx(net_conn_key, i++, guid, &len,
+			NULL, NULL, NULL, NULL);
+		if (status == ERROR_NO_MORE_ITEMS) {
+			break;
+		} else if (status != ERROR_SUCCESS) {
+			continue;
+		}
+		snprintf(conn_string, sizeof(conn_string),
+			"%s\\%s\\Connection",
+			NETWORK_CONNECTIONS_KEY, guid);
+		status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+			conn_string,
+			0,
+			KEY_READ,
+			&conn_key);
+		if (status != ERROR_SUCCESS) {
+			continue;
+		}
+		len = sizeof(ifname);
+		status = RegQueryValueEx(conn_key, "Name", NULL,
+			&value_type, ifname, &len);
+		if (status != ERROR_SUCCESS || value_type != REG_SZ) {
+			RegCloseKey(conn_key);
+			continue;
+		}
+		
+		switch (type) {
+		case SEARCH_IF_GUID_FROM_NAME:
+			if (!strcmp(key, ifname)) {
+				strcpy(value, guid);
+				found = TRUE;
+			}
+			break;
+		case SEARCH_IF_NAME_FROM_GUID:
+			if (!strcmp(key, guid)) {
+				strcpy(value, ifname);
+				found = TRUE;
+			}
+			break;
+		default:
+			break;
+		}
+		RegCloseKey(conn_key);
+	}
+	RegCloseKey(net_conn_key);
+	
+	if (found) {
+		return value;
+	}
+	
+	return NULL;
+}
+
+/*
+ * Open the TUN/TAP device with the provided guid
+ */
+static int open_tun_device (char *guid, char *dev, enum if_mode_enum mode)
+{
+	HANDLE handle;
+	ULONG len, status, info[3];
+	char device_path[512];
+	
+	printf("Device: %s\n", dev);
+	
+	if (mode == IF_MODE_TUN) {
+		printf("TUN mode is not supported\n");
+		return -1;
+	}
+	
+	/*
+	 * Let's try to open Windows TAP-Win32 adapter
+	 */
+	snprintf(device_path, sizeof(device_path), "%s%s%s",
+		USERMODEDEVICEDIR, guid, TAPSUFFIX);
+	
+	handle = CreateFile(device_path,
+		GENERIC_READ | GENERIC_WRITE,
+		0, /* Don't let other processes share or open
+		the resource until the handle's been closed */
+		0,
+		OPEN_EXISTING,
+		FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
+		0);
+	
+	if (handle == INVALID_HANDLE_VALUE) {
+		return -1;
+	}
+	
+	/*
+	 * get driver version info
+	 */
+	memset(info, 0, sizeof(info));
+	if (DeviceIoControl(handle, TAP_IOCTL_GET_VERSION,
+		&info, sizeof(info),
+		&info, sizeof(info), &len, NULL)) {
+		printf("TAP-Win32 Driver Version %d.%d %s\n",
+		(int) info[0],
+		(int) info[1],
+		(info[2] ? "(DEBUG)" : ""));
+	}
+	
+	/*
+	 * Set driver media status to 'connected'
+	 */
+	status = TRUE;
+	if (!DeviceIoControl(handle, TAP_IOCTL_SET_MEDIA_STATUS,
+		&status, sizeof(status),
+		&status, sizeof(status), &len, NULL)) {
+		printf("WARNING: The TAP-Win32 driver rejected a "
+		"TAP_IOCTL_SET_MEDIA_STATUS DeviceIoControl call.\n");
+	}
+	
+	/*
+	 * Initialize overlapped structures
+	 */
+	overlap_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+	overlap_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+	if (!overlap_read.hEvent || !overlap_write.hEvent) {
+		return -1;
+	}
+	
+	/*
+	 * Return fd
+	 */
+	return cygwin_attach_handle_to_fd(NULL, -1, handle, 1, GENERIC_READ | GENERIC_WRITE);
+}
+
+/*
+ * Allocate TUN device, returns opened fd.
+ * Stores dev name in the first arg (must be large enough).
+ */
+int tun_open (char *dev, enum if_mode_enum mode)
+{
+	int fd = -1;
+	HKEY unit_key;
+	char guid[256];
+	char comp_id[256];
+	char enum_name[256];
+	char unit_string[512];
+	BOOL found = FALSE;
+	HKEY adapter_key;
+	DWORD value_type;
+	LONG status;
+	DWORD len;
+	
+	if (!dev) {
+		return -1;
+	}
+	
+	/*
+	 * Device name has been provided. Open such device.
+	 */
+	if (*dev != '\0') {
+		if (!search_if(guid, dev, SEARCH_IF_GUID_FROM_NAME)) {
+			return -1;
+		}
+		return open_tun_device(guid, dev, mode);
+	}
+	
+	/*
+	 * Device name has non been specified. Look for one available!
+	 */
+	int i = 0;
+	status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+		ADAPTER_KEY,
+		0,
+		KEY_READ,
+		&adapter_key);
+	if (status != ERROR_SUCCESS) {
+		printf("Error opening registry key: %s", ADAPTER_KEY);
+		return -1;
+	}
+	
+	while (!found) {
+		len = sizeof(enum_name);
+		status = RegEnumKeyEx(adapter_key, i++,
+			enum_name, &len,
+			NULL, NULL, NULL, NULL);
+		if (status == ERROR_NO_MORE_ITEMS) {
+			break;
+		} else if (status != ERROR_SUCCESS) {
+			continue;
+		}
+		snprintf(unit_string, sizeof(unit_string), "%s\\%s",
+			ADAPTER_KEY, enum_name);
+		status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+			unit_string,
+			0,
+			KEY_READ,
+			&unit_key);
+		if (status != ERROR_SUCCESS) {
+			continue;
+		}
+		len = sizeof(comp_id);
+		status = RegQueryValueEx(unit_key,
+			"ComponentId", NULL,
+			&value_type, comp_id, &len);
+		if (status != ERROR_SUCCESS || value_type != REG_SZ) {
+			RegCloseKey(unit_key);
+			continue;
+		}
+		len = sizeof(guid);
+		status = RegQueryValueEx(unit_key,
+			"NetCfgInstanceId", NULL,
+			&value_type, guid, &len);
+		if (status != ERROR_SUCCESS || value_type != REG_SZ) {
+			RegCloseKey(unit_key);
+			continue;
+		}
+		if (strcmp(comp_id, TAP_COMPONENT_ID) != 0) {
+			RegCloseKey(unit_key);
+			continue;
+		}
+		
+		/*
+		 * Let's try to open this device
+		 */
+		search_if(dev, guid, SEARCH_IF_NAME_FROM_GUID);
+		fd = open_tun_device(guid, dev, mode);
+		if (fd != -1) {
+			found = TRUE;
+		}
+		
+		RegCloseKey(unit_key);
+	}
+	RegCloseKey(adapter_key);
+	
+	return fd;
+}
+#elif defined(IFF_TUN)
+int tun_open(char *dev, enum if_mode_enum mode)
+{
+	struct ifreq ifr;
+	int fd, err;
+
+	if ((fd = open("/dev/net/tun", O_RDWR)) < 0) {
+		error(0, errno,
+			"can't open /dev/net/tun, check that it is either device char 10 200 or (with DevFS) a symlink to ../misc/net/tun (not misc/net/tun)");
+		return -1;
+	}
+
+	memset(&ifr, 0, sizeof(ifr));
+	ifr.ifr_flags = ((mode == IF_MODE_TUN) ? IFF_TUN : IFF_TAP) | IFF_NO_PI;
+	if (*dev)
+		strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+
+	if ((err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0) {
+		close(fd);
+		return err;
+	}
+	strcpy(dev, ifr.ifr_name);
+	return fd;
+}
+#else
+int tun_open(char *dev, enum if_mode_enum mode)
+{
+	char tunname[14];
+	int i, fd;
+
+	if (*dev) {
+		if (strncmp(dev, ((mode == IF_MODE_TUN) ? "tun" : "tap"), 3))
+			error(1, 0,
+				"error: arbitrary naming tunnel interface is not supported in this version\n");
+		snprintf(tunname, sizeof(tunname), "/dev/%s", dev);
+		return open(tunname, O_RDWR);
+	}
+
+	for (i = 0; i < 255; i++) {
+		snprintf(tunname, sizeof(tunname), "/dev/%s%d",
+			((mode == IF_MODE_TUN) ? "tun" : "tap"), i);
+		/* Open device */
+		if ((fd = open(tunname, O_RDWR)) > 0) {
+			snprintf(dev, IFNAMSIZ, "%s%d",
+				((mode == IF_MODE_TUN) ? "tun" : "tap"), i);
+			return fd;
+		}
+	}
+	return -1;
+}
+#endif /* New driver support */
+
+/* 
+ * Close TUN device. 
+ */
+#if defined(__sun__)
+int tun_close(int fd, char *dev)
+{
+	struct ifreq ifr;
+
+	memset(&ifr, 0, sizeof(ifr));
+	strcpy(ifr.ifr_name, dev);
+	if (ioctl(ip_fd, SIOCGIFFLAGS, &ifr) < 0) {
+		syslog(LOG_ERR, "Can't get iface flags");
+		return 0;
+	}
+
+	if (ioctl(ip_fd, I_PUNLINK, muxid) < 0) {
+		syslog(LOG_ERR, "Can't unlink interface");
+		return 0;
+	}
+
+	close(ip_fd);
+	close(fd);
+	return 0;
+}
+#elif defined(__CYGWIN__)
+int tun_close(int fd, char *dev)
+{
+	dev = NULL; /* unused */
+	return CloseHandle((HANDLE) get_osfhandle(fd));
+}
+#else
+int tun_close(int fd, char *dev)
+{
+	dev = NULL; /*unused */
+	return close(fd);
+}
+#endif
+
+
+#if defined(__sun__)
+int tun_write(int fd, unsigned char *buf, int len)
+{
+	struct strbuf sbuf;
+	sbuf.len = len;
+	sbuf.buf = buf;
+	return putmsg(fd, NULL, &sbuf, 0) >= 0 ? sbuf.len : -1;
+}
+
+int tun_read(int fd, unsigned char *buf, int len)
+{
+	struct strbuf sbuf;
+	int f = 0;
+
+	sbuf.maxlen = len;
+	sbuf.buf = buf;
+	return getmsg(fd, NULL, &sbuf, &f) >= 0 ? sbuf.len : -1;
+}
+#elif defined(__CYGWIN__)
+int tun_read(int fd, unsigned char *buf, int len)
+{
+	DWORD read_size;
+	
+	ResetEvent(overlap_read.hEvent);
+	if (ReadFile((HANDLE) get_osfhandle(fd), buf, len, &read_size, &overlap_read)) {
+		return read_size;
+	}
+	switch (GetLastError()) {
+	case ERROR_IO_PENDING:
+		WaitForSingleObject(overlap_read.hEvent, INFINITE);
+		GetOverlappedResult((HANDLE) get_osfhandle(fd), &overlap_read, &read_size, FALSE);
+		return read_size;
+		break;
+	default:
+		break;
+	}
+	
+	return -1;
+}
+
+int tun_write(int fd, unsigned char *buf, int len)
+{
+	DWORD write_size;
+	
+	ResetEvent(overlap_write.hEvent);
+	if (WriteFile((HANDLE) get_osfhandle(fd),
+		buf,
+		len,
+		&write_size,
+		&overlap_write)) {
+		return write_size;
+	}
+	switch (GetLastError()) {
+	case ERROR_IO_PENDING:
+		WaitForSingleObject(overlap_write.hEvent, INFINITE);
+		GetOverlappedResult((HANDLE) get_osfhandle(fd), &overlap_write,
+			&write_size, FALSE);
+		return write_size;
+		break;
+	default:
+		break;
+	}
+	
+	return -1;
+}
+#elif defined(NEW_TUN)
+#define MAX_MRU 2048
+struct tun_data {
+	union {
+		uint32_t family;
+		uint32_t timeout;
+	} header;
+	u_char data[MAX_MRU];
+};
+
+/* Read/write frames from TUN device */
+int tun_write(int fd, unsigned char *buf, int len)
+{
+	char *data;
+	struct tun_data tun;
+
+	if (len > (int)sizeof(tun.data))
+		return -1;
+
+	memcpy(tun.data, buf, len);
+	tun.header.family = htonl(AF_INET);
+	len += (sizeof(tun) - sizeof(tun.data));
+	data = (char *)&tun;
+
+	return write(fd, data, len) - (sizeof(tun) - sizeof(tun.data));
+}
+
+int tun_read(int fd, unsigned char *buf, int len)
+{
+	struct tun_data tun;
+	char *data;
+	size_t sz;
+	int pack;
+
+	data = (char *)&tun;
+	sz = sizeof(tun);
+	pack = read(fd, data, sz);
+	if (pack == -1)
+		return -1;
+
+	pack -= sz - sizeof(tun.data);
+	if (pack > len)
+		pack = len; /* truncate paket */
+
+	memcpy(buf, tun.data, pack);
+
+	return pack;
+}
+
+#else
+
+int tun_write(int fd, unsigned char *buf, int len)
+{
+	return write(fd, buf, len);
+}
+
+int tun_read(int fd, unsigned char *buf, int len)
+{
+	return read(fd, buf, len);
+}
+
+#endif
+
+/*
+ * Get HW addr
+ */
+int tun_get_hwaddr(int fd, char *dev, uint8_t *hwaddr)
+{
+#if defined(__CYGWIN__)
+	ULONG len;
+	
+	dev = NULL; /* unused */
+	if (!DeviceIoControl((HANDLE) get_osfhandle(fd), TAP_IOCTL_GET_MAC,
+		hwaddr, ETH_ALEN, hwaddr, ETH_ALEN, &len, NULL)) {
+		printf("Cannot get HW address\n");
+		return -1;
+	}
+	
+	return 0;
+#elif defined(SIOCGIFHWADDR)
+	struct ifreq ifr;
+	
+	/* Use a new socket fd! */
+	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+		return -1;
+	}
+	
+	memset(&ifr, 0, sizeof(struct ifreq));
+	strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+	
+	if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
+		return -1;
+	}
+	
+	memcpy(hwaddr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+	
+	return 0;
+#else
+	/* todo: implement using SIOCGLIFADDR */
+	fd = 0;
+	dev = 0;
+	hwaddr = 0;
+	errno = ENOSYS;
+	return -1;
+#endif
+}
+
+/***********************************************************************/
+/* other support functions */
+
+#ifndef HAVE_VASPRINTF
+int vasprintf(char **strp, const char *fmt, va_list ap)
+{
+	int ret;
+	char *strbuf;
+
+	ret = vsnprintf(NULL, 0, fmt, ap);
+	strbuf = (char *)malloc(ret + 1);
+	if (strbuf == NULL) {
+		errno = ENOMEM;
+		ret = -1;
+	}
+	vsnprintf(strbuf, ret + 1, fmt, ap);
+	*strp = strbuf;
+	return ret;
+}
+#endif
+
+#ifndef HAVE_ASPRINTF
+int asprintf(char **strp, const char *fmt, ...)
+{
+	int ret;
+	va_list ap;
+
+	va_start(ap, fmt);
+	ret = vasprintf(strp, fmt, ap);
+	va_end(ap);
+
+	return ret;
+}
+#endif
+
+#ifndef HAVE_ERROR
+void error(int status, int errornum, const char *fmt, ...)
+{
+	char *buf2;
+	va_list ap;
+
+	va_start(ap, fmt);
+	vasprintf(&buf2, fmt, ap);
+	va_end(ap);
+	fprintf(stderr, "%s", buf2);
+	if (errornum)
+		fprintf(stderr, ": %s\n", strerror(errornum));
+	else
+		fprintf(stderr, "\n");
+	free(buf2);
+
+	if (status)
+		exit(status);
+}
+#endif
+
+#ifndef HAVE_GETLINE
+int getline(char **line, size_t * length, FILE * stream)
+{
+	size_t len;
+#ifdef HAVE_FGETLN
+	char *tmpline;
+
+	tmpline = fgetln(stream, &len);
+#else
+	char tmpline[512];
+
+	fgets(tmpline, sizeof(tmpline), stream);
+	len = strlen(tmpline);
+#endif
+	if (feof(stream))
+		return -1;
+	if (*line == NULL) {
+		*line = malloc(len + 1);
+		*length = len + 1;
+	}
+	if (*length < len + 1) {
+		*line = realloc(*line, len + 1);
+		*length = len + 1;
+	}
+	if (*line == NULL)
+		return -1;
+	memcpy(*line, tmpline, len);
+	(*line)[len] = '\0';
+	return len;
+}
+#endif
+
+#ifndef HAVE_UNSETENV
+int unsetenv(const char *name)
+{
+	int i, len;
+
+	len = strlen(name);
+	for (i = 0; environ[i]; i++)
+		if (!strncmp(name, environ[i], len))
+			if (environ[i][len] == '=')
+				break;
+
+	for (; environ[i] && environ[i + 1]; i++)
+		environ[i] = environ[i + 1];
+	
+	return 0;
+}
+#endif
+
+#ifndef HAVE_SETENV
+int setenv(const char *name, const char *value, int overwrite)
+{
+	int ret;
+	char *newenv;
+
+	if (overwrite == 0)
+		if (getenv(name) != NULL)
+			return 0;
+
+	newenv = malloc(strlen(name) + 1 + strlen(value) + 1);
+	if (newenv == NULL)
+		return -1;
+
+	*newenv = '\0';
+	strcat(newenv, name);
+	strcat(newenv, "=");
+	strcat(newenv, value);
+
+	ret = putenv(newenv);
+	if (ret == -1)
+		free(newenv);
+
+	return ret;
+}
+#endif

Modified: branches/vpnc-nortel/sysdep.h
==============================================================================
--- branches/vpnc-nortel/sysdep.h (original)
+++ branches/vpnc-nortel/sysdep.h Wed Jun 11 01:19:11 2008
@@ -1,39 +1,214 @@
 #ifndef __SYSDEP_H__
 #define __SYSDEP_H__
 
+/*
+ * Different systems define different macros.
+ * For vpnc, this list should be used as
+ * reference:
+ *
+ * __linux__ 
+ * __NetBSD__
+ * __OpenBSD__
+ * __FreeBSD__
+ * __DragonFly__
+ * __APPLE__      Darwin / MacOS X
+ * __sun__        SunOS / Solaris
+ * __CYGWIN__
+ * __SKYOS__
+ *
+ */
+
 #include <sys/types.h>
+#include <sys/socket.h>
 #include <netinet/in.h>
 
-int tun_open(char *dev);
+#if !defined(__CYGWIN__)
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/if_ether.h>
+#endif
+
+#include "config.h"
+
+int tun_open(char *dev, enum if_mode_enum mode);
 int tun_close(int fd, char *dev);
-int tun_write(int fd, char *buf, int len);
-int tun_read(int fd, char *buf, int len);
-
-const char *sysdep_config_script(void);
-
-#if defined(__linux__)
+int tun_write(int fd, unsigned char *buf, int len);
+int tun_read(int fd, unsigned char *buf, int len);
+int tun_get_hwaddr(int fd, char *dev, uint8_t *hwaddr);
+
+/***************************************************************************/
+#if defined(__linux__) || defined(__GLIBC__)
 #include <error.h>
-#else
-extern void error(int fd, int errorno, const char *fmt, ...);
-extern int getline(char **line, size_t * length, FILE * stream);
-#endif
-
+
+#define HAVE_VASPRINTF 1
+#define HAVE_ASPRINTF  1
+#define HAVE_ERROR     1
+#define HAVE_GETLINE   1
+#define HAVE_UNSETENV  1
+#define HAVE_SETENV    1
+#endif
+
+/***************************************************************************/
+#if defined(__NetBSD__)
+#define HAVE_SA_LEN 1
+
+#define HAVE_VASPRINTF 1
+#define HAVE_ASPRINTF  1
+#define HAVE_FGETLN    1
+#define HAVE_UNSETENV  1
+#define HAVE_SETENV    1
+#endif
+
+/***************************************************************************/
+#if defined(__OpenBSD__)
+#define HAVE_SA_LEN 1
+#define NEED_IPLEN_FIX 1
+#define NEW_TUN 1
+
+#define HAVE_VASPRINTF 1
+#define HAVE_ASPRINTF  1
+#define HAVE_FGETLN    1
+#define HAVE_UNSETENV  1
+#define HAVE_SETENV    1
+#endif
+
+/***************************************************************************/
+#if defined(__FreeBSD_kernel__)
+#define HAVE_SA_LEN 1
+#endif
+
+/***************************************************************************/
+#if defined(__FreeBSD__)
+#define HAVE_SA_LEN 1
+
+#define HAVE_VASPRINTF 1
+#define HAVE_ASPRINTF  1
+#define HAVE_FGETLN    1
+#define HAVE_UNSETENV  1
+#define HAVE_SETENV    1
+#endif
+
+/***************************************************************************/
+#if defined(__DragonFly__)
+#define HAVE_SA_LEN 1
+
+#define HAVE_VASPRINTF 1
+#define HAVE_ASPRINTF  1
+#define HAVE_FGETLN    1
+#define HAVE_UNSETENV  1
+#define HAVE_SETENV    1
+#endif
+
+/***************************************************************************/
+#if defined(__APPLE__)
+#define HAVE_SA_LEN 1
+#define NEED_IPLEN_FIX 1
+
+#define HAVE_VASPRINTF 1
+#define HAVE_ASPRINTF  1
+#define HAVE_FGETLN    1
+#define HAVE_UNSETENV  1
+#define HAVE_SETENV    1
+#endif
+
+/***************************************************************************/
 #if defined(__sun__)
-#include <stdarg.h>
+#define NEED_IPLEN_FIX 1
 
 #ifndef IPPROTO_ESP
 #define IPPROTO_ESP 50
 #endif
 
-extern int vasprintf(char **strp, const char *fmt, va_list ap);
-extern int asprintf(char **strp, const char *fmt, ...);
-extern int setenv(const char *name, const char *value, int overwrite);
-extern void unsetenv(const char *name);
+#define getpass(prompt) getpassphrase(prompt)
 
 /* where is this defined? */
 #include <sys/socket.h>
-const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);
-#endif
+const char *inet_ntop(int af, const void *src, char *dst, size_t cnt);
+#endif
+/***************************************************************************/
+#if defined (__SKYOS__)
+#define HAVE_UNSETENV  1
+
+#ifndef IPPROTO_ENCAP
+#define IPPROTO_ENCAP 4
+#endif
+
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP 50
+#endif
+#endif
+/***************************************************************************/
+#if defined (__CYGWIN__)
+#define HAVE_VASPRINTF 1
+#define HAVE_ASPRINTF  1
+#define HAVE_GETLINE   1
+#define HAVE_FGETLN    1
+#define HAVE_UNSETENV  1
+#define HAVE_SETENV    1
+
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP 50
+#endif
+
+#ifndef IPPROTO_ENCAP
+#define IPPROTO_ENCAP 4
+#endif
+
+#ifdef IFNAMSIZ
+#undef IFNAMSIZ
+#endif
+#define IFNAMSIZ 256
+
+/*
+ * At the moment the Cygwin environment does not have header files
+ * for raw ethernet access, hence we need to define here what
+ * is usually found in net/ethernet.h and netinet/if_ether.h
+ */
+
+#define ETH_ALEN 6
+
+/* Ethernet header */
+struct ether_header
+{
+	unsigned char ether_dhost[ETH_ALEN]; /* destination eth addr */
+	unsigned char ether_shost[ETH_ALEN]; /* source ether addr    */
+	unsigned short ether_type;           /* packet type ID field */
+} __attribute__ ((__packed__));
+
+#define ETHERTYPE_IP  0x0800 /* IP  */
+#define ETHERTYPE_ARP 0x0806 /* ARP */
+
+/* Common ARP header */
+struct arphdr {
+	unsigned short ar_hrd; /* format of hardware address   */
+	unsigned short ar_pro; /* format of protocol address   */
+	unsigned char  ar_hln; /* length of hardware address   */
+	unsigned char  ar_pln; /* length of protocol address   */
+	unsigned short ar_op;  /* ARP opcode (command)         */
+};
+
+/* Ethernet ARP header */
+struct ether_arp {
+	struct arphdr ea_hdr;            /* fixed-size header */
+	unsigned char arp_sha[ETH_ALEN]; /* sender hardware address */
+	unsigned char arp_spa[4];        /* sender protocol address */
+	unsigned char arp_tha[ETH_ALEN]; /* target hardware address */
+	unsigned char arp_tpa[4];        /* target protocol address */
+};
+#define arp_hrd ea_hdr.ar_hrd
+#define arp_pro ea_hdr.ar_pro
+#define arp_hln ea_hdr.ar_hln
+#define arp_pln ea_hdr.ar_pln
+#define arp_op  ea_hdr.ar_op
+
+#define ARPHRD_ETHER 1 /* Ethernet */
+
+#define ARPOP_REQUEST 1 /* ARP request */
+#define ARPOP_REPLY   2 /* ARP reply   */
+
+#endif
+/***************************************************************************/
+
 
 #ifndef IPDEFTTL
 #define IPDEFTTL 64 /* default ttl, from RFC 1340 */
@@ -43,4 +218,33 @@
 #define IPPROTO_IPIP IPPROTO_ENCAP
 #endif
 
-#endif
+#ifndef ETH_HLEN
+#define ETH_HLEN (sizeof(struct ether_header))
+#endif
+
+#ifndef ETH_ALEN
+#define ETH_ALEN (sizeof(struct ether_addr))
+#endif
+
+#ifndef HAVE_ERROR
+extern void error(int fd, int errorno, const char *fmt, ...);
+#endif
+#ifndef HAVE_GETLINE
+extern int getline(char **line, size_t * length, FILE * stream);
+#endif
+#ifndef HAVE_VASPRINTF
+#include <stdarg.h>
+extern int vasprintf(char **strp, const char *fmt, va_list ap);
+#endif
+#ifndef HAVE_ASPRINTF
+extern int asprintf(char **strp, const char *fmt, ...);
+#endif
+#ifndef HAVE_SETENV
+extern int setenv(const char *name, const char *value, int overwrite);
+#endif
+#ifndef HAVE_UNSETENV
+extern int unsetenv(const char *name);
+#endif
+
+
+#endif

Modified: branches/vpnc-nortel/tunip.c
==============================================================================
--- branches/vpnc-nortel/tunip.c (original)
+++ branches/vpnc-nortel/tunip.c Wed Jun 11 01:19:11 2008
@@ -1,8 +1,11 @@
 /* IPSec ESP and AH support.
    Copyright (c) 1999      Pierre Beyssac
    Copyright (C) 2002      Geoffrey Keating
-   Copyright (C) 2003-2004 Maurice Massar
+   Copyright (C) 2003-2007 Maurice Massar
    Copyright (C) 2004      Tomas Mraz
+   Copyright (C) 2005      Michael Tilstra
+   Copyright (C) 2006      Daniel Roethlisberger
+   Copyright (C) 2007      Paolo Zarpellon (tap+Cygwin support)
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -17,6 +20,8 @@
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   $Id$
 */
 
 /* borrowed from pipsecd (-; */
@@ -58,16 +63,23 @@
 #include <netinet/in_systm.h>
 #include <netinet/in.h>
 #include <netinet/ip.h>
+#ifndef __SKYOS__
 #include <netinet/ip_icmp.h>
+#endif
 #include <arpa/inet.h>
 #include <stdlib.h>
 #include <string.h>
+#include <strings.h>
 #include <syslog.h>
 #include <time.h>
-#include <poll.h>
+#include <sys/select.h>
 #include <signal.h>
 
-#ifndef __sun__
+#ifdef __CYGWIN__
+#include <pthread.h>
+#endif
+
+#if !defined(__sun__) && !defined(__SKYOS__)
 #include <err.h>
 #endif
 
@@ -76,115 +88,46 @@
 #include "config.h"
 #include "vpnc.h"
 
-#define max(a,b)	((a)>(b)?(a):(b))
-
-struct sa_desc {
-	struct sa_desc *next;
-
-	struct sockaddr_in init; /* initial and fallback remote address */
-	struct sockaddr_in dest; /* current remote address */
-	struct sockaddr_in source; /* local socket address we send packets from */
-	unsigned char use_fallback; /* use initial address as fallback? */
-	unsigned char use_dest; /* is dest address known yet? */
-
-	unsigned long spi; /* security parameters index */
-	unsigned long seq_id; /* for replay protection (not implemented) */
-
-	/* Encryption key */
-	const unsigned char *enc_secret;
-	size_t enc_secret_size;
-	size_t ivlen;
-	/* Preprocessed encryption key */
-	gcry_cipher_hd_t cry_ctx;
-	int cry_algo;
-
-	/* Authentication secret */
-	const unsigned char *auth_secret;
-	unsigned int auth_secret_size;
-	/* Authentication method to use, or NULL */
-	int md_algo;
-
-	/* Encapsulation method to use to send packets */
-	struct encap_method *em;
-
-	/* timeout counters */
-	time_t last_packet_sent, last_packet_recv, last_checkifaddr;
-};
-
-struct peer_desc {
-	struct sa_desc *local_sa, *remote_sa;
-	int tun_fd; /* file descriptor for associated tunnel device */
-};
+#include "tunip.h"
+
+#ifndef MAX
+#define MAX(a,b)	((a)>(b)?(a):(b))
+#endif
+
+#ifndef FD_COPY
+#define FD_COPY(f, t)	((void)memcpy((t), (f), sizeof(*(f))))
+#endif
 
 /* A real ESP header (RFC 2406) */
 typedef struct esp_encap_header {
-	unsigned long spi; /* security parameters index */
-	unsigned long seq_id; /* sequence id (unimplemented) */
+	uint32_t spi; /* security parameters index */
+	uint32_t seq_id; /* sequence id (unimplemented) */
 	/* variable-length payload data + padding */
 	/* unsigned char next_header */
 	/* optional auth data */
-} esp_encap_header_t;
+} __attribute__((packed)) esp_encap_header_t;
 
 struct encap_method {
-	int fd; /* file descriptor for relevant socket */
-	unsigned char *name;
-
 	int fixed_header_size;
-
-	/* Description of the packet being processed */
-	unsigned char *buf;
-	unsigned int bufsize, bufpayload, var_header_size;
-	int buflen;
-	struct sockaddr_in from;
-	int fromlen;
-
-	int (*recv) (struct encap_method * encap,
-		unsigned char *buf, unsigned int bufsize, struct sockaddr_in * from);
-	struct peer_desc *(*peer_find) (struct encap_method * encap);
-	void (*send_peer) (struct encap_method * encap,
-		struct peer_desc * peer, unsigned char *buf, unsigned int bufsize);
-	int (*recv_peer) (struct encap_method * encap, struct peer_desc * peer);
+	
+	int  (*recv)      (struct sa_block *s, unsigned char *buf, unsigned int bufsize);
+	void (*send_peer) (struct sa_block *s, unsigned char *buf, unsigned int bufsize);
+	int  (*recv_peer) (struct sa_block *s);
 };
 
-/* Forward decl */
-void encap_esp_send_peer(struct encap_method *encap,
-	struct peer_desc *peer, unsigned char *buf, unsigned int bufsize);
-void encap_udp_send_peer(struct encap_method *encap,
-	struct peer_desc *peer, unsigned char *buf, unsigned int bufsize);
-struct peer_desc *peer_find(unsigned long spi, struct encap_method *encap);
-int encap_esp_recv_peer(struct encap_method *encap, struct peer_desc *peer);
-
 /* Yuck! Global variables... */
 
-#define MAX_HEADER 64
+#define MAX_HEADER 72
 #define MAX_PACKET 4096
-unsigned char buf[MAX_HEADER + MAX_PACKET];
-
-struct peer_desc vpnpeer;
-
-unsigned short ip_id;
-
-/* Security associations lists */
-struct sa_desc *local_sa_list = NULL;
-struct sa_desc *remote_sa_list = NULL;
-
-#define encap_get_fd(e)	((e)->fd)
-#define encap_recv(e,b,bs,f) \
-	((e)->recv((e),(b),(bs),(f)))
-#define encap_peer_find(e) \
-	((e)->peer_find((e)))
-#define encap_send_peer(e,p,b,bs) \
-	((e)->send_peer((e),(p),(b),(bs)))
-#define encap_recv_peer(e,p) \
-	((e)->recv_peer((e),(p)))
+int volatile do_kill;
+static uint8_t global_buffer_rx[MAX_HEADER + MAX_PACKET + ETH_HLEN];
+static uint8_t global_buffer_tx[MAX_HEADER + MAX_PACKET + ETH_HLEN];
 
 /*
  * in_cksum --
  *	Checksum routine for Internet Protocol family headers (C Version)
  */
-u_short in_cksum(addr, len)
-	u_short *addr;
-	int len;
+static u_short in_cksum(u_short *addr, int len)
 {
 	register int nleft = len;
 	register u_short *w = addr;
@@ -217,105 +160,77 @@
 /*
  * Decapsulate from a raw IP packet
  */
-int encap_rawip_recv(struct encap_method *encap,
-	unsigned char *buf, unsigned int bufsize, struct sockaddr_in *from)
-{
-	int r;
+static int encap_rawip_recv(struct sa_block *s, unsigned char *buf, unsigned int bufsize)
+{
+	ssize_t r;
 	struct ip *p = (struct ip *)buf;
-
-	encap->fromlen = sizeof(encap->from);
-
-	r = recvfrom(encap->fd, buf, bufsize, 0, (struct sockaddr *)&encap->from, &encap->fromlen);
+	struct sockaddr_in from;
+	socklen_t fromlen = sizeof(from);
+	
+	r = recvfrom(s->esp_fd, buf, bufsize, 0, (struct sockaddr *)&from, &fromlen);
 	if (r == -1) {
 		syslog(LOG_ERR, "recvfrom: %m");
 		return -1;
 	}
-	if (r < (p->ip_hl << 2) + encap->fixed_header_size) {
-		syslog(LOG_ALERT, "packet too short from %s", inet_ntoa(encap->from.sin_addr));
+	if (from.sin_addr.s_addr != s->dst.s_addr) {
+		syslog(LOG_ALERT, "packet from unknown host %s", inet_ntoa(from.sin_addr));
 		return -1;
 	}
-#if 0
-	printf("raw got %d bytes\n", r);
-	for (i = 0; i < r; i++) {
-		printf(" %02x", buf[i]);
-		if ((i & 15) == 15)
-			printf("\n");
-	}
-	printf("\n");
-#endif
-
-#ifdef NEED_IPID_SWAP
-	p->ip_id = htons(p->ip_id);
-#endif
+	if (r < (p->ip_hl << 2) + s->ipsec.em->fixed_header_size) {
+		syslog(LOG_ALERT, "packet too short");
+		return -1;
+	}
+
 #ifdef NEED_IPLEN_FIX
 	p->ip_len = r;
 #else
 	p->ip_len = ntohs(r);
 #endif
 
-	encap->buf = buf;
-	encap->buflen = r;
-	encap->bufpayload = (p->ip_hl << 2);
-	encap->bufsize = bufsize;
-	*from = encap->from;
+	s->ipsec.rx.buf = buf;
+	s->ipsec.rx.buflen = r;
+	s->ipsec.rx.bufpayload = (p->ip_hl << 2);
+	s->ipsec.rx.bufsize = bufsize;
 	return r;
 }
 
 /*
  * Decapsulate from an UDP packet
  */
-int encap_udp_recv(struct encap_method *encap,
-	unsigned char *buf, unsigned int bufsize,
-	struct sockaddr_in *from)
-{
-	int r;
-
-	encap->fromlen = sizeof(encap->from);
-
-	r = recvfrom(encap->fd, buf, bufsize, 0,
-		(struct sockaddr *)&encap->from, &encap->fromlen);
+static int encap_udp_recv(struct sa_block *s, unsigned char *buf, unsigned int bufsize)
+{
+	ssize_t r;
+
+	r = recv(s->esp_fd, buf, bufsize, 0);
 	if (r == -1) {
 		syslog(LOG_ERR, "recvfrom: %m");
 		return -1;
 	}
-	if (r < encap->fixed_header_size) {
+	if (s->ipsec.natt_active_mode == NATT_ACTIVE_DRAFT_OLD && r > 8) {
+		r -= 8;
+		memmove(buf, buf + 8, r);
+	}
+	if (r < s->ipsec.em->fixed_header_size) {
 		syslog(LOG_ALERT, "packet too short from %s",
-			inet_ntoa(encap->from.sin_addr));
+			inet_ntoa(s->dst));
 		return -1;
 	}
 
-#if 0
-	printf("udp got %d bytes\n", r);
-	for (i = 0; i < r; i++) {
-		printf(" %02x", buf[i]);
-		if ((i & 15) == 15) printf("\n");
-	}
-	printf("\n");
-#endif
-
-	encap->buf = buf;
-	encap->buflen = r;
-	encap->bufpayload = 0;
-	encap->bufsize = bufsize;
-	*from = encap->from;
+	s->ipsec.rx.buf = buf;
+	s->ipsec.rx.buflen = r;
+	s->ipsec.rx.bufpayload = 0;
+	s->ipsec.rx.bufsize = bufsize;
 	return r;
-}
-
-struct peer_desc *encap_esp_peer_find(struct encap_method *encap)
-{
-	esp_encap_header_t *eh;
-	eh = (esp_encap_header_t *) (encap->buf + encap->bufpayload);
-	return peer_find(ntohl(eh->spi), encap);
 }
 
 /*
  * Decapsulate packet
  */
-int encap_any_decap(struct encap_method *encap)
-{
-	encap->buflen -= encap->bufpayload + encap->fixed_header_size + encap->var_header_size;
-	encap->buf += encap->bufpayload + encap->fixed_header_size + encap->var_header_size;
-	if (encap->buflen == 0)
+static int encap_any_decap(struct sa_block *s)
+{
+	s->ipsec.rx.buflen -= s->ipsec.rx.bufpayload + s->ipsec.em->fixed_header_size + s->ipsec.rx.var_header_size;
+	s->ipsec.rx.buf    += s->ipsec.rx.bufpayload + s->ipsec.em->fixed_header_size + s->ipsec.rx.var_header_size;
+	if (s->ipsec.rx.buflen == 0)
 		return 0;
 	return 1;
 }
@@ -323,142 +238,45 @@
 /*
  * Send decapsulated packet to tunnel device
  */
-int tun_send_ip(struct encap_method *encap, int fd)
-{
-	int sent;
-
-	sent = tun_write(fd, encap->buf, encap->buflen);
-	if (sent != encap->buflen)
-		syslog(LOG_ERR, "truncated in: %d -> %d\n", encap->buflen, sent);
+static int tun_send_ip(struct sa_block *s)
+{
+	int sent, len;
+	uint8_t *start;
+	
+	start = s->ipsec.rx.buf;
+	len   = s->ipsec.rx.buflen;
+	
+	if (opt_if_mode == IF_MODE_TAP) {
+#ifndef __sun__
+		/*
+		 * Add ethernet header before s->ipsec.rx.buf where
+		 * at least ETH_HLEN bytes should be available.
+		 */
+		struct ether_header *eth_hdr = (struct ether_header *) (s->ipsec.rx.buf - ETH_HLEN);
+		
+		memcpy(eth_hdr->ether_dhost, s->tun_hwaddr, ETH_ALEN);
+		memcpy(eth_hdr->ether_shost, s->tun_hwaddr, ETH_ALEN);
+		
+		/* Use a different MAC as source */
+		eth_hdr->ether_shost[0] ^= 0x80; /* toggle some visible bit */
+		eth_hdr->ether_type = htons(ETHERTYPE_IP);
+		
+		start = (uint8_t *) eth_hdr;
+		len += ETH_HLEN;
+#endif
+	}
+	
+	sent = tun_write(s->tun_fd, start, len);
+	if (sent != len)
+		syslog(LOG_ERR, "truncated in: %d -> %d\n", len, sent);
+	hex_dump("Tx pkt", start, len, NULL);
 	return 1;
-}
-
-int encap_esp_new(struct encap_method *encap, unsigned char proto)
-{
-#ifdef IP_HDRINCL
-	int hincl = 1;
-#endif
-
-	encap->fd = socket(PF_INET, SOCK_RAW, proto);
-
-	if (encap->fd == -1) {
-		perror("socket(SOCK_RAW)");
-		return -1;
-	}
-#ifdef IP_HDRINCL
-	if (setsockopt(encap->fd, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof(hincl))
-		== -1) {
-		perror("setsockopt(IP_HDRINCL)");
-		close(encap->fd);
-		return -1;
-	}
-#endif
-	encap->name = "ipesp";
-	encap->recv = encap_rawip_recv;
-	encap->peer_find = encap_esp_peer_find;
-	encap->send_peer = encap_esp_send_peer;
-	encap->recv_peer = encap_esp_recv_peer;
-	encap->fixed_header_size = sizeof(esp_encap_header_t);
-	encap->var_header_size = 0;
-	return 0;
-}
-
-int encap_udp_new(struct encap_method *encap, int udp_fd)
-{
-	encap->fd = udp_fd;
-
-	encap->name = "udpesp";
-	encap->recv = encap_udp_recv;
-	encap->peer_find = encap_esp_peer_find;
-	encap->send_peer = encap_udp_send_peer;
-	encap->recv_peer = encap_esp_recv_peer;
-	encap->fixed_header_size = sizeof(esp_encap_header_t);
-	encap->var_header_size = 0;
-	return 0;
-}
-
-/*
- * This is a hack to retrieve which local IP address the system would use
- * as a source when sending packets to a given destination.
- */
-int find_local_addr(struct sockaddr_in *dest, struct sockaddr_in *source)
-{
-	int addrlen;
-	struct sockaddr_in dest_socket;
-	int fd;
-
-	fd = socket(PF_INET, SOCK_DGRAM, 0);
-	if (fd == -1) {
-		syslog(LOG_ERR, "socket: %m");
-		return -1;
-	}
-
-	memset(&dest_socket, 0, sizeof(dest_socket));
-
-	dest_socket.sin_family = AF_INET;
-#ifdef HAVE_SA_LEN
-	dest_socket.sin_len = sizeof(dest_socket);
-#endif
-	dest_socket.sin_addr = dest->sin_addr;
-	dest_socket.sin_port = htons(4444);
-
-	if (connect(fd, (struct sockaddr *)&dest_socket, sizeof(dest_socket)) == -1) {
-		syslog(LOG_ERR, "connect: %m");
-		close(fd);
-		return -1;
-	}
-
-	addrlen = sizeof(*source);
-
-	if (getsockname(fd, (struct sockaddr *)source, &addrlen) == -1) {
-		syslog(LOG_ERR, "getsockname: %m");
-		close(fd);
-		return -1;
-	}
-	close(fd);
-	return 0;
-}
-
-/*
- * Retrieve and possibly update our local address to a given remote SA.
- * Return 1 if changed, 0 if not, -1 if error.
- */
-int update_sa_addr(struct sa_desc *p)
-{
-	struct sockaddr_in new_addr;
-
-	if (find_local_addr(&p->dest, &new_addr) == -1) {
-		syslog(LOG_ALERT,
-			"can't find a local address for packets to %s",
-			inet_ntoa(p->dest.sin_addr));
-		return -1;
-	}
-	if (new_addr.sin_addr.s_addr != p->source.sin_addr.s_addr) {
-		char addr1[16];
-		p->source.sin_addr = new_addr.sin_addr;
-		strcpy(addr1, inet_ntoa(p->dest.sin_addr));
-		syslog(LOG_NOTICE,
-			"local address for %s is %s", addr1, inet_ntoa(p->source.sin_addr));
-		return 1;
-	}
-	return 0;
-}
-
-/*
- * Find the peer record associated with a given local SPI.
- */
-struct peer_desc *peer_find(unsigned long spi, struct encap_method *encap)
-{
-	if (vpnpeer.local_sa->spi == spi && vpnpeer.local_sa->em == encap)
-		return &vpnpeer;
-	syslog(LOG_ALERT, "unknown spi %ld", spi);
-	return NULL;
 }
 
 /*
  * Compute HMAC for an arbitrary stream of bytes
  */
-int hmac_compute(int md_algo,
+static int hmac_compute(int md_algo,
 	const unsigned char *data, unsigned int data_size,
 	unsigned char *digest, unsigned char do_store,
 	const unsigned char *secret, unsigned short secret_size)
@@ -470,7 +288,7 @@
 
 	/* See RFC 2104 */
 	gcry_md_open(&md_ctx, md_algo, GCRY_MD_FLAG_HMAC);
-	assert(md_ctx != 0);
+	assert(md_ctx != NULL);
 	ret = gcry_md_setkey(md_ctx, secret, secret_size);
 	assert(ret == 0);
 	gcry_md_write(md_ctx, data, data_size);
@@ -491,8 +309,7 @@
 /*
  * Encapsulate a packet in ESP
  */
-void encap_esp_encapsulate(struct encap_method *encap,
-	struct peer_desc *peer)
+static void encap_esp_encapsulate(struct sa_block *s)
 {
 	esp_encap_header_t *eh;
 	unsigned char *iv, *cleartext;
@@ -506,61 +323,54 @@
 	 *      obscure on that point.
 	 * seems fine
 	 */
-	gcry_cipher_algo_info(peer->remote_sa->cry_algo, GCRYCTL_GET_BLKLEN, NULL, &pad_blksz);
+	pad_blksz = s->ipsec.blk_len;
 	while (pad_blksz & 3) /* must be multiple of 4 */
 		pad_blksz <<= 1;
-	padding = pad_blksz - ((encap->buflen + 2 - encap->var_header_size - encap->bufpayload) % pad_blksz);
-	DEBUG(2, printf("sending packet: len = %d, padding = %lu\n", encap->buflen, (unsigned long)padding));
+	padding = pad_blksz - ((s->ipsec.tx.buflen + 2 - s->ipsec.tx.var_header_size - s->ipsec.tx.bufpayload) % pad_blksz);
+	DEBUG(3, printf("sending packet: len = %d, padding = %lu\n", s->ipsec.tx.buflen, (unsigned long)padding));
 	if (padding == pad_blksz)
 		padding = 0;
 
 	for (i = 1; i <= padding; i++) {
-		encap->buf[encap->buflen] = i;
-		encap->buflen++;
+		s->ipsec.tx.buf[s->ipsec.tx.buflen] = i;
+		s->ipsec.tx.buflen++;
 	}
 
 	/* Add trailing padlen and next_header */
-	encap->buf[encap->buflen++] = padding;
-	encap->buf[encap->buflen++] = IPPROTO_IPIP;
-
-	cleartext = encap->buf + encap->var_header_size + encap->bufpayload;
-	cleartextlen = encap->buflen - encap->var_header_size - encap->bufpayload;
-
-	eh = (esp_encap_header_t *) (encap->buf + encap->bufpayload);
-	eh->spi = htonl(peer->remote_sa->spi);
-	eh->seq_id = htonl(++peer->remote_sa->seq_id);
+	s->ipsec.tx.buf[s->ipsec.tx.buflen++] = padding;
+	s->ipsec.tx.buf[s->ipsec.tx.buflen++] = IPPROTO_IPIP;
+
+	cleartext = s->ipsec.tx.buf + s->ipsec.tx.var_header_size + s->ipsec.tx.bufpayload;
+	cleartextlen = s->ipsec.tx.buflen - s->ipsec.tx.var_header_size - s->ipsec.tx.bufpayload;
+
+	eh = (esp_encap_header_t *) (s->ipsec.tx.buf + s->ipsec.tx.bufpayload);
+	eh->spi = s->ipsec.tx.spi;
+	eh->seq_id = htonl(s->ipsec.tx.seq_id++);
 
 	/* Copy initialization vector in packet */
 	iv = (unsigned char *)(eh + 1);
-	gcry_randomize(iv, peer->remote_sa->ivlen, GCRY_WEAK_RANDOM);
-	hex_dump("iv", iv, peer->remote_sa->ivlen);
-	hex_dump("auth_secret", peer->remote_sa->auth_secret, peer->remote_sa->auth_secret_size);
-
-#if 1
-	hex_dump("sending ESP packet (before crypt)", encap->buf, encap->buflen);
-#endif
-
-	{
-		gcry_cipher_setiv(peer->remote_sa->cry_ctx, iv, peer->remote_sa->ivlen);
-		gcry_cipher_encrypt(peer->remote_sa->cry_ctx, cleartext, cleartextlen, NULL, 0);
-	}
-
-#if 1
-	hex_dump("sending ESP packet (after crypt)", encap->buf, encap->buflen);
-#endif
+	gcry_create_nonce(iv, s->ipsec.iv_len);
+	hex_dump("iv", iv, s->ipsec.iv_len, NULL);
+
+	hex_dump("sending ESP packet (before crypt)", s->ipsec.tx.buf, s->ipsec.tx.buflen, NULL);
+
+	if (s->ipsec.cry_algo) {
+		gcry_cipher_setiv(s->ipsec.tx.cry_ctx, iv, s->ipsec.iv_len);
+		gcry_cipher_encrypt(s->ipsec.tx.cry_ctx, cleartext, cleartextlen, NULL, 0);
+	}
+
+	hex_dump("sending ESP packet (after crypt)", s->ipsec.tx.buf, s->ipsec.tx.buflen, NULL);
 
 	/* Handle optional authentication field */
-	if (peer->remote_sa->md_algo) {
-		hmac_compute(peer->remote_sa->md_algo,
-			encap->buf + encap->bufpayload,
-			encap->var_header_size + cleartextlen,
-			encap->buf + encap->bufpayload
-			+ encap->var_header_size + cleartextlen,
-			1, peer->remote_sa->auth_secret, peer->remote_sa->auth_secret_size);
-		encap->buflen += 12; /*gcry_md_get_algo_dlen(md_algo); see RFC .. only use 96 bit */
-#if 1
-		hex_dump("sending ESP packet (after ah)", encap->buf, encap->buflen);
-#endif
+	if (s->ipsec.md_algo) {
+		hmac_compute(s->ipsec.md_algo,
+			s->ipsec.tx.buf + s->ipsec.tx.bufpayload,
+			s->ipsec.tx.var_header_size + cleartextlen,
+			s->ipsec.tx.buf + s->ipsec.tx.bufpayload
+			+ s->ipsec.tx.var_header_size + cleartextlen,
+			1, s->ipsec.tx.key_md, s->ipsec.md_len);
+		s->ipsec.tx.buflen += 12; /*gcry_md_get_algo_dlen(md_algo); see RFC .. only use 96 bit */
+		hex_dump("sending ESP packet (after ah)", s->ipsec.tx.buf, s->ipsec.tx.buflen, NULL);
 	}
 }
 
@@ -569,61 +379,63 @@
  * "buf" should have exactly MAX_HEADER free bytes at its beginning
  * to account for encapsulation data (not counted in "size").
  */
-void encap_esp_send_peer(struct encap_method *encap,
-	struct peer_desc *peer,
-	unsigned char *buf, unsigned int bufsize)
+static void encap_esp_send_peer(struct sa_block *s, unsigned char *buf, unsigned int bufsize)
 {
 	ssize_t sent;
-	struct ip *tip, *ip;
+	struct ip *tip, ip;
+	struct sockaddr_in dstaddr;
 
 	buf += MAX_HEADER;
 
 	/* Keep a pointer to the old IP header */
 	tip = (struct ip *)buf;
 
-	encap->buf = buf;
-	encap->buflen = bufsize;
+	s->ipsec.tx.buf = buf;
+	s->ipsec.tx.buflen = bufsize;
 
 	/* Prepend our encapsulation header and new IP header */
-	encap->var_header_size = (encap->fixed_header_size + peer->remote_sa->ivlen);
-
-	encap->buf -= sizeof(struct ip) + encap->var_header_size;
-	encap->buflen += sizeof(struct ip) + encap->var_header_size;
-
-	encap->bufpayload = sizeof(struct ip);
-
-	ip = (struct ip *)(encap->buf);
+	s->ipsec.tx.var_header_size = (s->ipsec.em->fixed_header_size + s->ipsec.iv_len);
+
+	s->ipsec.tx.buf -= sizeof(struct ip) + s->ipsec.tx.var_header_size;
+	s->ipsec.tx.buflen += sizeof(struct ip) + s->ipsec.tx.var_header_size;
+
+	s->ipsec.tx.bufpayload = sizeof(struct ip);
+
 	/* Fill non-mutable fields */
-	ip->ip_v = IPVERSION;
-	ip->ip_hl = 5;
+	ip.ip_v = IPVERSION;
+	ip.ip_hl = 5;
 	/*gcry_md_get_algo_dlen(md_algo); see RFC .. only use 96 bit */
-	ip->ip_id = htons(ip_id++);
-	ip->ip_p = IPPROTO_ESP;
-	ip->ip_src = peer->remote_sa->source.sin_addr;
-	ip->ip_dst = peer->remote_sa->dest.sin_addr;
+	ip.ip_id = htons(s->ipsec.ip_id++);
+	ip.ip_p = IPPROTO_ESP;
+	ip.ip_src = s->src;
+	ip.ip_dst = s->dst;
 
 	/* Fill mutable fields */
-	ip->ip_tos = (bufsize < sizeof(struct ip)) ? 0 : tip->ip_tos;
-	ip->ip_off = 0;
-	ip->ip_ttl = IPDEFTTL;
-	ip->ip_sum = 0;
-
-	encap_esp_encapsulate(encap, peer);
-
-	ip->ip_len = encap->buflen;
+	ip.ip_tos = (bufsize < sizeof(struct ip)) ? 0 : tip->ip_tos;
+	ip.ip_off = 0;
+	ip.ip_ttl = IPDEFTTL;
+	ip.ip_sum = 0;
+
+	encap_esp_encapsulate(s);
+
+	ip.ip_len = s->ipsec.tx.buflen;
 #ifdef NEED_IPLEN_FIX
-	ip->ip_len = htons(ip->ip_len);
-#endif
-	ip->ip_sum = in_cksum((u_short *) encap->buf, sizeof(struct ip));
-
-	sent = sendto(encap->fd, encap->buf, encap->buflen, 0,
-		(struct sockaddr *)&peer->remote_sa->dest, sizeof(peer->remote_sa->dest));
+	ip.ip_len = htons(ip.ip_len);
+#endif
+	ip.ip_sum = in_cksum((u_short *) s->ipsec.tx.buf, sizeof(struct ip));
+
+	memcpy(s->ipsec.tx.buf, &ip, sizeof ip);
+
+	dstaddr.sin_family = AF_INET;
+	dstaddr.sin_addr = s->dst;
+	dstaddr.sin_port = 0;
+	sent = sendto(s->esp_fd, s->ipsec.tx.buf, s->ipsec.tx.buflen, 0, (struct sockaddr *)&dstaddr, sizeof(struct sockaddr_in));
 	if (sent == -1) {
 		syslog(LOG_ERR, "sendto: %m");
 		return;
 	}
-	if (sent != encap->buflen)
-		syslog(LOG_ALERT, "truncated out (%d out of %d)", sent, encap->buflen);
+	if (sent != s->ipsec.tx.buflen)
+		syslog(LOG_ALERT, "truncated out (%lld out of %d)", (long long)sent, s->ipsec.tx.buflen);
 }
 
 /*
@@ -631,39 +443,42 @@
  * "buf" should have exactly MAX_HEADER free bytes at its beginning
  * to account for encapsulation data (not counted in "size").
  */
-void encap_udp_send_peer(struct encap_method *encap,
-	struct peer_desc *peer,
-	unsigned char *buf, unsigned int bufsize)
+static void encap_udp_send_peer(struct sa_block *s, unsigned char *buf, unsigned int bufsize)
 {
 	ssize_t sent;
-
+	
 	buf += MAX_HEADER;
-
-	encap->buf = buf;
-	encap->buflen = bufsize;
-
+	
+	s->ipsec.tx.buf = buf;
+	s->ipsec.tx.buflen = bufsize;
+	
 	/* Prepend our encapsulation header and new IP header */
-	encap->var_header_size = (encap->fixed_header_size + peer->remote_sa->ivlen);
-
-	encap->buf -= encap->var_header_size;
-	encap->buflen += encap->var_header_size;
-
-	encap->bufpayload = 0;
-
-	encap_esp_encapsulate(encap, peer);
-
-	sent = sendto(encap->fd, encap->buf, encap->buflen, 0,
-		(struct sockaddr *)&peer->remote_sa->dest, sizeof(peer->remote_sa->dest));
+	s->ipsec.tx.var_header_size = (s->ipsec.em->fixed_header_size + s->ipsec.iv_len);
+	
+	s->ipsec.tx.buf -= s->ipsec.tx.var_header_size;
+	s->ipsec.tx.buflen += s->ipsec.tx.var_header_size;
+	
+	s->ipsec.tx.bufpayload = 0;
+	
+	encap_esp_encapsulate(s);
+	
+	if (s->ipsec.natt_active_mode == NATT_ACTIVE_DRAFT_OLD) {
+		s->ipsec.tx.buf -= 8;
+		s->ipsec.tx.buflen += 8;
+		memset(s->ipsec.tx.buf, 0, 8);
+	}
+	
+	sent = send(s->esp_fd, s->ipsec.tx.buf, s->ipsec.tx.buflen, 0);
 	if (sent == -1) {
 		syslog(LOG_ERR, "sendto: %m");
 		return;
 	}
-	if (sent != encap->buflen)
-		syslog(LOG_ALERT, "truncated out (%Zd out of %Zd)",
-			sent, encap->buflen);
-}
-
-int encap_esp_recv_peer(struct encap_method *encap, struct peer_desc *peer)
+	if (sent != s->ipsec.tx.buflen)
+		syslog(LOG_ALERT, "truncated out (%lld out of %d)",
+			(long long)sent, s->ipsec.tx.buflen);
+}
+
+static int encap_esp_recv_peer(struct sa_block *s)
 {
 	int len, i;
 	size_t blksz;
@@ -672,11 +487,11 @@
 	unsigned char *iv;
 	struct esp_encap_header *eh;
 
-	eh = (struct esp_encap_header *)(encap->buf + encap->bufpayload);
-	encap->var_header_size = peer->local_sa->ivlen;
-	iv = encap->buf + encap->bufpayload + encap->fixed_header_size;
-
-	len = encap->buflen - encap->bufpayload - encap->fixed_header_size - encap->var_header_size;
+	eh = (struct esp_encap_header *)(s->ipsec.rx.buf + s->ipsec.rx.bufpayload);
+	s->ipsec.rx.var_header_size = s->ipsec.iv_len;
+	iv = s->ipsec.rx.buf + s->ipsec.rx.bufpayload + s->ipsec.em->fixed_header_size;
+
+	len = s->ipsec.rx.buflen - s->ipsec.rx.bufpayload - s->ipsec.em->fixed_header_size - s->ipsec.rx.var_header_size;
 
 	if (len < 0) {
 		syslog(LOG_ALERT, "Packet too short");
@@ -684,57 +499,51 @@
 	}
 
 	/* Handle optional authentication field */
-	if (peer->local_sa->md_algo) {
+	if (s->ipsec.md_algo) {
 		len -= 12; /*gcry_md_get_algo_dlen(peer->local_sa->md_algo); */
-		if (hmac_compute(peer->local_sa->md_algo,
-				encap->buf + encap->bufpayload,
-				encap->fixed_header_size + encap->var_header_size + len,
-				encap->buf + encap->bufpayload
-				+ encap->fixed_header_size + encap->var_header_size + len,
+		s->ipsec.rx.buflen -= 12;
+		if (hmac_compute(s->ipsec.md_algo,
+				s->ipsec.rx.buf + s->ipsec.rx.bufpayload,
+				s->ipsec.em->fixed_header_size + s->ipsec.rx.var_header_size + len,
+				s->ipsec.rx.buf + s->ipsec.rx.bufpayload
+				+ s->ipsec.em->fixed_header_size + s->ipsec.rx.var_header_size + len,
 				0,
-				peer->local_sa->auth_secret,
-				peer->local_sa->auth_secret_size) != 0) {
+				s->ipsec.rx.key_md,
+				s->ipsec.md_len) != 0) {
 			syslog(LOG_ALERT, "HMAC mismatch in ESP mode");
 			return -1;
 		}
 	}
 
-	gcry_cipher_algo_info(peer->local_sa->cry_algo, GCRYCTL_GET_BLKLEN, NULL, &blksz);
+	blksz = s->ipsec.blk_len;
 	if ((len % blksz) != 0) {
 		syslog(LOG_ALERT,
 			"payload len %d not a multiple of algorithm block size %lu", len,
 			(unsigned long)blksz);
 		return -1;
 	}
-#if 0
-	printf("receiving ESP packet (before decrypt):\n");
-	for (i = 0; i < len; i++)
-		printf(" %02x", encap->buf[encap->bufpayload
-				+ encap->fixed_header_size + encap->var_header_size + i]);
-	printf("\n");
-#endif
-
-	{
+	
+	hex_dump("receiving ESP packet (before decrypt)",
+		&s->ipsec.rx.buf[s->ipsec.rx.bufpayload + s->ipsec.em->fixed_header_size +
+			 s->ipsec.rx.var_header_size], len, NULL);
+
+	if (s->ipsec.cry_algo) {
 		unsigned char *data;
 
-		data = (encap->buf + encap->bufpayload
-			+ encap->fixed_header_size + encap->var_header_size);
-		gcry_cipher_setiv(peer->local_sa->cry_ctx, iv, peer->local_sa->ivlen);
-		gcry_cipher_decrypt(peer->local_sa->cry_ctx, data, len, NULL, 0);
-	}
-
-#if 0
-	printf("receiving ESP packet (after decrypt %d):\n", len);
-	for (i = 0; i < len; i++)
-		printf(" %02x", encap->buf[encap->bufpayload
-				+ encap->fixed_header_size + encap->var_header_size + i]);
-	printf("\n");
-#endif
-
-	padlen = encap->buf[encap->bufpayload
-		+ encap->fixed_header_size + encap->var_header_size + len - 2];
-	next_header = encap->buf[encap->bufpayload
-		+ encap->fixed_header_size + encap->var_header_size + len - 1];
+		data = (s->ipsec.rx.buf + s->ipsec.rx.bufpayload
+			+ s->ipsec.em->fixed_header_size + s->ipsec.rx.var_header_size);
+		gcry_cipher_setiv(s->ipsec.rx.cry_ctx, iv, s->ipsec.iv_len);
+		gcry_cipher_decrypt(s->ipsec.rx.cry_ctx, data, len, NULL, 0);
+	}
+
+	hex_dump("receiving ESP packet (after decrypt)",
+		&s->ipsec.rx.buf[s->ipsec.rx.bufpayload + s->ipsec.em->fixed_header_size +
+			s->ipsec.rx.var_header_size], len, NULL);
+	
+	padlen = s->ipsec.rx.buf[s->ipsec.rx.bufpayload
+		+ s->ipsec.em->fixed_header_size + s->ipsec.rx.var_header_size + len - 2];
+	next_header = s->ipsec.rx.buf[s->ipsec.rx.bufpayload
+		+ s->ipsec.em->fixed_header_size + s->ipsec.rx.var_header_size + len - 1];
 
 	if (padlen + 2 > len) {
 		syslog(LOG_ALERT, "Inconsistent padlen");
@@ -744,14 +553,14 @@
 		syslog(LOG_ALERT, "Inconsistent next_header %d", next_header);
 		return -1;
 	}
-#if 0
-	printf("pad len: %d, next_header: %d\n", padlen, next_header);
-#endif
+	DEBUG(3, printf("pad len: %d, next_header: %d\n", padlen, next_header));
+
 	len -= padlen + 2;
+	s->ipsec.rx.buflen -= padlen + 2;
 
 	/* Check padding */
-	pad = encap->buf + encap->bufpayload
-		+ encap->fixed_header_size + encap->var_header_size + len;
+	pad = s->ipsec.rx.buf + s->ipsec.rx.bufpayload
+		+ s->ipsec.em->fixed_header_size + s->ipsec.rx.var_header_size + len;
 	for (i = 1; i <= padlen; i++) {
 		if (*pad != i) {
 			syslog(LOG_ALERT, "Bad padding");
@@ -763,254 +572,493 @@
 	return 0;
 }
 
-static void vpnc_main_loop(struct peer_desc *peer, struct encap_method *meth, int tun_fd)
-{
-	struct pollfd pollfds[2];
-
-	pollfds[0].fd = tun_fd;
-	pollfds[0].events = POLLIN;
-	pollfds[1].fd = encap_get_fd(meth);
-	pollfds[1].events = POLLIN;
-
-	for (;;) {
+static void encap_esp_new(struct encap_method *encap)
+{
+	encap->recv = encap_rawip_recv;
+	encap->send_peer = encap_esp_send_peer;
+	encap->recv_peer = encap_esp_recv_peer;
+	encap->fixed_header_size = sizeof(esp_encap_header_t);
+}
+
+static void encap_udp_new(struct encap_method *encap)
+{
+	encap->recv = encap_udp_recv;
+	encap->send_peer = encap_udp_send_peer;
+	encap->recv_peer = encap_esp_recv_peer;
+	encap->fixed_header_size = sizeof(esp_encap_header_t);
+}
+
+/*
+ * Process ARP 
+ * Return 1 if packet has been processed, 0 otherwise
+ */
+static int process_arp(struct sa_block *s, uint8_t *frame)
+{
+#ifndef __sun__
+	int frame_size;
+	uint8_t tmp[4];
+	struct ether_header *eth = (struct ether_header *) frame;
+	struct ether_arp *arp = (struct ether_arp *) (frame + ETH_HLEN);
+	
+	if (ntohs(eth->ether_type) != ETHERTYPE_ARP) {
+		return 0;
+	}
+	
+	if (ntohs(arp->arp_hrd) != ARPHRD_ETHER ||
+		ntohs(arp->arp_pro) != 0x800 ||
+		arp->arp_hln != ETH_ALEN ||
+		arp->arp_pln != 4 ||
+		ntohs(arp->arp_op) != ARPOP_REQUEST ||
+		!memcmp(arp->arp_spa, arp->arp_tpa, 4) ||
+		memcmp(eth->ether_shost, s->tun_hwaddr, ETH_ALEN) ||
+		!memcmp(arp->arp_tpa, s->our_address, 4)) {
+		/* whatever .. just drop it */
+		return 1;
+	}
+	
+	/* send arp reply */
+	
+	memcpy(eth->ether_dhost, s->tun_hwaddr, ETH_ALEN);
+	eth->ether_shost[0] ^= 0x80; /* Use a different MAC as source */
+	
+	memcpy(tmp, arp->arp_spa, 4);
+	memcpy(arp->arp_spa, arp->arp_tpa, 4);
+	memcpy(arp->arp_tpa, tmp, 4);
+	
+	memcpy(arp->arp_tha, s->tun_hwaddr, ETH_ALEN);
+	arp->arp_sha[0] ^= 0x80; /* Use a different MAC as source */
+	
+	arp->arp_op = htons(ARPOP_REPLY);
+	
+	frame_size = ETH_HLEN + sizeof(struct ether_arp);
+	tun_write(s->tun_fd, frame, frame_size);
+	hex_dump("ARP reply", frame, frame_size, NULL);
+	
+	return 1;
+#else
+	s = 0;
+	frame = 0;
+	return 0;
+#endif
+}
+
+/*
+ * Process non-IP packets
+ * Return 1 if packet has been processed, 0 otherwise
+ */
+static int process_non_ip(struct sa_block *s, uint8_t *frame)
+{
+	struct ether_header *eth = (struct ether_header *) frame;
+	
+	s = NULL; /* unused */
+	
+	if (ntohs(eth->ether_type) != ETHERTYPE_IP) {
+		/* drop non-ip traffic */
+		return 1;
+	}
+	
+	return 0;
+}
+
+static void process_tun(struct sa_block *s)
+{
+	int pack;
+	int size = MAX_PACKET;
+	uint8_t *start = global_buffer_rx + MAX_HEADER;
+	
+	if (opt_if_mode == IF_MODE_TAP) {
+		/* Make sure IP packet starts at buf + MAX_HEADER */
+		start -= ETH_HLEN;
+		size += ETH_HLEN;
+	}
+	
+	/* Receive a packet from the tunnel interface */
+	pack = tun_read(s->tun_fd, start, size);
+	
+	hex_dump("Rx pkt", start, pack, NULL);
+	
+	if (opt_if_mode == IF_MODE_TAP) {
+		if (process_arp(s, start)) {
+			return;
+		}
+		if (process_non_ip(s, start)) {
+			return;
+		}
+		pack -= ETH_HLEN;
+	}
+	
+	if (pack == -1) {
+		syslog(LOG_ERR, "read: %m");
+		return;
+	}
+	
+	/* Don't access the contents of the buffer other than byte aligned.
+	 * 12: Offset of ip source address in ip header,
+	 *  4: Length of IP address */
+	if (!memcmp(global_buffer_rx + MAX_HEADER + 12, &s->dst.s_addr, 4)) {
+		syslog(LOG_ALERT, "routing loop to %s",
+			inet_ntoa(s->dst));
+		return;
+	}
+	
+	/* Encapsulate and send to the other end of the tunnel */
+	s->ipsec.life.tx += pack;
+	s->ipsec.em->send_peer(s, global_buffer_rx, pack);
+}
+
+static void process_socket(struct sa_block *s)
+{
+	/* Receive a packet from a socket */
+	int pack;
+	uint8_t *start = global_buffer_tx;
+	esp_encap_header_t *eh;
+
+	if (opt_if_mode == IF_MODE_TAP) {
+		start += ETH_HLEN;
+	}
+	
+	pack = s->ipsec.em->recv(s, start, MAX_HEADER + MAX_PACKET);
+	if (pack == -1)
+		return;
+	
+	eh = (esp_encap_header_t *) (s->ipsec.rx.buf + s->ipsec.rx.bufpayload);
+	if (eh->spi == 0) {
+		process_late_ike(s, s->ipsec.rx.buf + s->ipsec.rx.bufpayload + 4 /* SPI-size */,
+			s->ipsec.rx.buflen - s->ipsec.rx.bufpayload - 4);
+		return;
+	} else if (eh->spi != s->ipsec.rx.spi) {
+		syslog(LOG_NOTICE, "unknown spi %#08x from peer", ntohl(eh->spi));
+		return;
+	}
+	
+	/* Check auth digest and/or decrypt */
+	if (s->ipsec.em->recv_peer(s) != 0)
+		return;
+	
+	if (encap_any_decap(s) == 0) {
+		syslog(LOG_DEBUG, "received update probe from peer");
+	} else {
+		/* Send the decapsulated packet to the tunnel interface */
+		s->ipsec.life.rx += s->ipsec.rx.buflen;
+		tun_send_ip(s);
+	}
+}
+
+#if defined(__CYGWIN__)
+static void *tun_thread (void *arg)
+{
+	struct sa_block *s = (struct sa_block *) arg;
+	
+	while (!do_kill) {
+		process_tun(s);
+	}
+	return NULL;
+}
+#endif
+
+static void vpnc_main_loop(struct sa_block *s)
+{
+	fd_set rfds, refds;
+	int nfds=0;
+	int enable_keepalives;
+	int timed_mode;
+	ssize_t len;
+	struct timeval select_timeout;
+	struct timeval normal_timeout;
+	time_t next_ike_keepalive=0;
+	time_t next_ike_dpd=0;
+#if defined(__CYGWIN__)
+	pthread_t tid;
+#endif
+	
+	/* non-esp marker, nat keepalive payload (0xFF) */
+	uint8_t keepalive_v2[5] = { 0x00, 0x00, 0x00, 0x00, 0xFF };
+	uint8_t keepalive_v1[1] = { 0xFF };
+	uint8_t *keepalive;
+	size_t keepalive_size;
+	
+	if (s->ipsec.natt_active_mode == NATT_ACTIVE_DRAFT_OLD) {
+		keepalive = keepalive_v1;
+		keepalive_size = sizeof(keepalive_v1);
+	} else { /* active_mode is either RFC or CISCO_UDP */
+		keepalive = keepalive_v2;
+		keepalive_size = sizeof(keepalive_v2);
+	}
+	
+	/* send keepalives if UDP encapsulation is enabled */
+	enable_keepalives = (s->ipsec.encap_mode != IPSEC_ENCAP_TUNNEL);
+	
+	/* regular wakeups if keepalives on ike or dpd active */
+	timed_mode = ((enable_keepalives && s->ike_fd != s->esp_fd) || s->ike.do_dpd);
+	
+	FD_ZERO(&rfds);
+	
+#if !defined(__CYGWIN__)
+	FD_SET(s->tun_fd, &rfds);
+	nfds = MAX(nfds, s->tun_fd +1);
+#endif
+	
+	FD_SET(s->esp_fd, &rfds);
+	nfds = MAX(nfds, s->esp_fd +1);
+	
+	if (s->ike_fd != s->esp_fd) {
+		FD_SET(s->ike_fd, &rfds);
+		nfds = MAX(nfds, s->ike_fd +1);
+	}
+	
+#if defined(__CYGWIN__)
+	if (pthread_create(&tid, NULL, tun_thread, s)) {
+	        syslog(LOG_ERR, "Cannot create tun thread!\n");
+		return;
+	}
+#endif
+	
+	normal_timeout.tv_sec = 86400;
+	normal_timeout.tv_usec = 0;
+	
+	if (s->ike.do_dpd) {
+		/* send initial dpd request */
+		next_ike_dpd = time(NULL) + s->ike.dpd_idle;
+		dpd_ike(s);
+		normal_timeout.tv_sec = s->ike.dpd_idle;
+		normal_timeout.tv_usec = 0;
+	}
+	
+	if (enable_keepalives) {
+		normal_timeout.tv_sec = 9;
+		normal_timeout.tv_usec = 500000;
+
+		if (s->ike_fd != s->esp_fd) {
+			/* send initial nat ike keepalive packet */
+			next_ike_keepalive = time(NULL) + 9;
+			keepalive_ike(s);
+		}
+	}
+	
+	select_timeout = normal_timeout;
+	
+	while (!do_kill) {
 		int presult;
-
+		
 		do {
-			presult = poll(pollfds, sizeof(pollfds) / sizeof(pollfds[0]), -1);
-		} while (presult == -1 && errno == EINTR);
+			struct timeval *tvp = NULL;
+			FD_COPY(&rfds, &refds);
+			if (s->ike.do_dpd || enable_keepalives)
+				tvp = &select_timeout;
+			presult = select(nfds, &refds, NULL, NULL, tvp);
+			if (presult == 0 && (s->ike.do_dpd || enable_keepalives)) {
+				/* reset to max timeout */
+				select_timeout = normal_timeout;
+				if (enable_keepalives) {
+					if (s->ike_fd != s->esp_fd) {
+						/* send nat ike keepalive packet */
+						next_ike_keepalive = time(NULL) + 9;
+						keepalive_ike(s);
+					}
+					/* send nat keepalive packet */
+					if (send(s->esp_fd, keepalive, keepalive_size, 0) == -1) {
+						syslog(LOG_ERR, "sendto: %m");
+					}
+				}
+				if (s->ike.do_dpd) {
+					time_t now = time(NULL);
+					if (s->ike.dpd_seqno != s->ike.dpd_seqno_ack) {
+						/* Wake up more often for dpd attempts */
+						select_timeout.tv_sec = 5;
+						select_timeout.tv_usec = 0;
+						dpd_ike(s);
+						next_ike_dpd = now + s->ike.dpd_idle;
+					}
+					else if (now >= next_ike_dpd) {
+						dpd_ike(s);
+						next_ike_dpd = now + s->ike.dpd_idle;
+					}
+				}
+			}
+			DEBUG(2,printf("lifetime status: %ld of %u seconds used, %u|%u of %u kbytes used\n",
+				time(NULL) - s->ipsec.life.start,
+				s->ipsec.life.seconds,
+				s->ipsec.life.rx/1024,
+				s->ipsec.life.tx/1024,
+				s->ipsec.life.kbytes));
+		} while ((presult == 0 || (presult == -1 && errno == EINTR)) && !do_kill);
 		if (presult == -1) {
-			syslog(LOG_ERR, "poll: %m");
+			syslog(LOG_ERR, "select: %m");
 			continue;
 		}
-
-		if (pollfds[0].revents & POLLIN) {
-			int pack;
-
-			/* Receive a packet from the tunnel interface */
-			pack = tun_read(peer->tun_fd, buf + MAX_HEADER, MAX_PACKET);
-			if (pack == -1) {
-				syslog(LOG_ERR, "read: %m");
-				continue;
+		
+#if !defined(__CYGWIN__)
+		if (FD_ISSET(s->tun_fd, &refds)) {
+			process_tun(s);
+		}
+#endif
+		
+		if (FD_ISSET(s->esp_fd, &refds) ) {
+			process_socket(s);
+		}
+		
+		if (s->ike_fd != s->esp_fd && FD_ISSET(s->ike_fd, &refds) ) {
+			DEBUG(3,printf("received something on ike fd..\n"));
+			len = recv(s->ike_fd, global_buffer_tx, MAX_HEADER + MAX_PACKET, 0);
+			process_late_ike(s, global_buffer_tx, len);
+		}
+
+		if (timed_mode) {
+			time_t now = time(NULL);
+			time_t next_up = now + 86400;
+			if (enable_keepalives) {
+				/* never wait more than 9 seconds for a UDP keepalive */
+				next_up = now + 9;
+				if (s->ike_fd != s->esp_fd) {
+					if (now >= next_ike_keepalive) {
+						/* send nat ike keepalive packet now */
+						next_ike_keepalive = now + 9;
+						keepalive_ike(s);
+						select_timeout = normal_timeout;
+					}
+					if (next_ike_keepalive < next_up)
+						next_up = next_ike_keepalive;
+				}
 			}
-
-			if (peer->remote_sa->use_dest == 0) {
-				syslog(LOG_NOTICE, "peer hasn't a known address yet");
-				continue;
+			if (s->ike.do_dpd) {
+				if (s->ike.dpd_seqno != s->ike.dpd_seqno_ack) {
+					dpd_ike(s);
+					next_ike_dpd = now + s->ike.dpd_idle;
+					if (now + 5 < next_up)
+						next_up = now + 5;
+				}
+				else if (now >= next_ike_dpd) {
+					dpd_ike(s);
+					next_ike_dpd = now + s->ike.dpd_idle;
+				}
+				if (next_ike_dpd < next_up)
+					next_up = next_ike_dpd;
 			}
-
-			if (((struct ip *)(buf + MAX_HEADER))->ip_dst.s_addr
-				== peer->remote_sa->dest.sin_addr.s_addr) {
-				syslog(LOG_ALERT, "routing loop to %s",
-					inet_ntoa(peer->remote_sa->dest.sin_addr));
-				continue;
-			}
-
-			/* Encapsulate and send to the other end of the tunnel */
-			encap_send_peer(peer->remote_sa->em, peer, buf, pack);
-
-			/* Update sent packet timeout */
-			peer->remote_sa->last_packet_sent = time(NULL);
+			/* Reduce timeout so next activity happens on schedule */
+			select_timeout.tv_sec = next_up - now;
+			select_timeout.tv_usec = 0;
 		}
-		if (pollfds[1].revents & POLLIN) {
-			/* Receive a packet from a socket */
-			struct peer_desc *peer;
-			int pack;
-			struct sockaddr_in from;
-
-			pack = encap_recv(meth, buf, MAX_HEADER + MAX_PACKET, &from);
-			if (pack == -1)
-				continue;
-
-			peer = encap_peer_find(meth);
-			if (peer == NULL) {
-				syslog(LOG_NOTICE, "unknown spi from %s", inet_ntoa(from.sin_addr));
-				continue;
-			}
-
-			/* Check auth digest and/or decrypt */
-			if (encap_recv_peer(meth, peer) != 0)
-				continue;
-
-			/* Check origin IP; update our copy if need be */
-			if (peer->remote_sa->use_dest == 0
-				|| from.sin_addr.s_addr != peer->remote_sa->dest.sin_addr.s_addr) {
-				/* remote end changed address */
-				char addr1[16];
-				strcpy(addr1, inet_ntoa(peer->remote_sa->dest.sin_addr));
-				syslog(LOG_NOTICE,
-					"spi %ld: remote address changed from %s to %s",
-					peer->remote_sa->spi, addr1, inet_ntoa(from.sin_addr));
-				peer->remote_sa->dest.sin_addr.s_addr = from.sin_addr.s_addr;
-				peer->remote_sa->use_dest = 1;
-				update_sa_addr(peer->remote_sa);
-			}
-			/* Update received packet timeout */
-			peer->remote_sa->last_packet_recv = time(NULL);
-
-			if (encap_any_decap(meth) == 0)
-				syslog(LOG_DEBUG, "received update probe from peer");
-			else
-				/* Send the decapsulated packet to the tunnel interface */
-				tun_send_ip(meth, peer->tun_fd);
-		}
-	}
-}
-
-static uint8_t *volatile kill_packet;
-static size_t volatile kill_packet_size;
-static struct sockaddr *volatile kill_dest;
-
-void killit(int signum)
-{
-	int sock = signum; /* unused */
-	sock = socket(AF_INET, SOCK_DGRAM, 0);
-	if (sock >= 0) {
-		sendto(sock, kill_packet, kill_packet_size, 0,
-			kill_dest, sizeof(struct sockaddr_in));
-		close(sock);
-	}
-	tun_close(oursa->tun_fd, oursa->tun_name);
-	syslog(LOG_NOTICE, "terminated");
-	_exit(0);
-}
-
-void write_pidfile(const char *pidfile)
+
+	}
+	
+	tun_close(s->tun_fd, s->tun_name);
+	switch (do_kill) {
+		case -2:
+			syslog(LOG_NOTICE, "connection terminated by dead peer detection");
+			break;
+		case -1:
+			syslog(LOG_NOTICE, "connection terminated by peer");
+			break;
+		default:
+			syslog(LOG_NOTICE, "terminated by signal: %d", do_kill);
+			break;
+	}
+}
+
+static void killit(int signum)
+{
+	do_kill = signum;
+}
+
+static void write_pidfile(const char *pidfile)
 {
 	FILE *pf;
-
-	if (pidfile == NULL)
+	
+	if (pidfile == NULL || pidfile[0] == '\0')
 		return;
-
+	
 	pf = fopen(pidfile, "w");
 	if (pf == NULL) {
 		syslog(LOG_WARNING, "can't open pidfile %s for writing", pidfile);
 		return;
 	}
-
+	
 	fprintf(pf, "%d\n", (int)getpid());
 	fclose(pf);
 }
 
-void
-vpnc_doit(unsigned long tous_spi,
-	const unsigned char *tous_key,
-	struct sockaddr_in *tous_dest,
-	unsigned long tothem_spi,
-	const unsigned char *tothem_key,
-	struct sockaddr_in *tothem_dest,
-	int tun_fd, int md_algo, int cry_algo,
-	uint8_t * kill_packet_p, size_t kill_packet_size_p,
-	struct sockaddr *kill_dest_p,
-	uint16_t encap_mode, int udp_fd,
-	const char *pidfile)
-{
+void vpnc_doit(struct sa_block *s)
+{
+	struct sigaction act;
 	struct encap_method meth;
-
-	static struct sa_desc tous_sa, tothem_sa;
-	time_t t = time(NULL);
-
-	switch (encap_mode) {
+	
+	const char *pidfile = config[CONFIG_PID_FILE];
+	
+	switch (s->ipsec.encap_mode) {
 		case IPSEC_ENCAP_TUNNEL:
-			if (encap_esp_new(&meth, IPPROTO_ESP) == -1)
-				exit(1);
+			encap_esp_new(&meth);
+			gcry_create_nonce(&s->ipsec.ip_id, sizeof(uint16_t));
 			break;
 		case IPSEC_ENCAP_UDP_TUNNEL:
 		case IPSEC_ENCAP_UDP_TUNNEL_OLD:
-			if (encap_udp_new(&meth, udp_fd) == -1)
-				exit(1);
+			encap_udp_new(&meth);
 			break;
 		default:
 			abort();
 	}
-
-	tous_sa.next = remote_sa_list;
-	remote_sa_list = &tous_sa;
-	tous_sa.em = &meth;
-	tous_sa.last_packet_recv = t;
-	tous_sa.last_packet_sent = t;
-	tous_sa.last_checkifaddr = t;
-	tous_sa.md_algo = md_algo;
-	tous_sa.spi = htonl(tous_spi);
-	tous_sa.enc_secret = tous_key;
-	gcry_cipher_algo_info(cry_algo, GCRYCTL_GET_KEYLEN, NULL, &(tous_sa.enc_secret_size));
-	hex_dump("tous.enc_secret", tous_sa.enc_secret, tous_sa.enc_secret_size);
-	tous_sa.auth_secret = tous_key + tous_sa.enc_secret_size;
-	tous_sa.auth_secret_size = gcry_md_get_algo_dlen(md_algo);
-	hex_dump("tous.auth_secret", tous_sa.auth_secret, tous_sa.auth_secret_size);
-	memcpy(&tous_sa.init, tous_dest, sizeof(struct sockaddr_in));
-	memcpy(&tous_sa.dest, tous_dest, sizeof(struct sockaddr_in));
-	if (update_sa_addr(&tous_sa) != -1) {
-		tous_sa.use_fallback = 1;
-		tous_sa.use_dest = 1;
-	}
-	tous_sa.cry_algo = cry_algo;
-	gcry_cipher_open(&tous_sa.cry_ctx, tous_sa.cry_algo, GCRY_CIPHER_MODE_CBC, 0);
-	gcry_cipher_setkey(tous_sa.cry_ctx, tous_sa.enc_secret, tous_sa.enc_secret_size);
-	gcry_cipher_algo_info(tous_sa.cry_algo, GCRYCTL_GET_BLKLEN, NULL, &(tous_sa.ivlen));
-
-	tothem_sa.next = local_sa_list;
-	local_sa_list = &tothem_sa;
-	tothem_sa.em = &meth;
-	tothem_sa.last_packet_recv = t;
-	tothem_sa.last_packet_sent = t;
-	tothem_sa.last_checkifaddr = t;
-	tothem_sa.md_algo = md_algo;
-	tothem_sa.spi = htonl(tothem_spi);
-	tothem_sa.enc_secret = tothem_key;
-	gcry_cipher_algo_info(cry_algo, GCRYCTL_GET_KEYLEN, NULL, &(tothem_sa.enc_secret_size));
-	hex_dump("tothem.enc_secret", tothem_sa.enc_secret, tothem_sa.enc_secret_size);
-	tothem_sa.auth_secret = tothem_key + tothem_sa.enc_secret_size;
-	tothem_sa.auth_secret_size = gcry_md_get_algo_dlen(md_algo);
-	hex_dump("tothem.auth_secret", tothem_sa.auth_secret, tothem_sa.auth_secret_size);
-	memcpy(&tothem_sa.init, tothem_dest, sizeof(struct sockaddr_in));
-	memcpy(&tothem_sa.dest, tothem_dest, sizeof(struct sockaddr_in));
-	if (update_sa_addr(&tothem_sa) != -1) {
-		tothem_sa.use_fallback = 1;
-		tothem_sa.use_dest = 1;
-	}
-	tothem_sa.cry_algo = cry_algo;
-	gcry_cipher_open(&tothem_sa.cry_ctx, tothem_sa.cry_algo, GCRY_CIPHER_MODE_CBC, 0);
-	gcry_cipher_setkey(tothem_sa.cry_ctx, tothem_sa.enc_secret, tothem_sa.enc_secret_size);
-	gcry_cipher_algo_info(tothem_sa.cry_algo, GCRYCTL_GET_BLKLEN, NULL, &(tothem_sa.ivlen));
-
-	vpnpeer.tun_fd = tun_fd;
-	vpnpeer.local_sa = &tous_sa;
-	vpnpeer.remote_sa = &tothem_sa;
-
-	kill_packet = kill_packet_p;
-	kill_packet_size = kill_packet_size_p;
-	kill_dest = kill_dest_p;
-
-	signal(SIGHUP, killit);
+	s->ipsec.em = &meth;
+	
+	s->ipsec.rx.key_cry = s->ipsec.rx.key;
+	hex_dump("rx.key_cry", s->ipsec.rx.key_cry, s->ipsec.key_len, NULL);
+	
+	s->ipsec.rx.key_md = s->ipsec.rx.key + s->ipsec.key_len;
+	hex_dump("rx.key_md", s->ipsec.rx.key_md, s->ipsec.md_len, NULL);
+	
+	if (s->ipsec.cry_algo) {
+		gcry_cipher_open(&s->ipsec.rx.cry_ctx, s->ipsec.cry_algo, GCRY_CIPHER_MODE_CBC, 0);
+		gcry_cipher_setkey(s->ipsec.rx.cry_ctx, s->ipsec.rx.key_cry, s->ipsec.key_len);
+	} else {
+		s->ipsec.rx.cry_ctx = NULL;
+	}
+	
+	s->ipsec.tx.key_cry = s->ipsec.tx.key;
+	hex_dump("tx.key_cry", s->ipsec.tx.key_cry, s->ipsec.key_len, NULL);
+	
+	s->ipsec.tx.key_md = s->ipsec.tx.key + s->ipsec.key_len;
+	hex_dump("tx.key_md", s->ipsec.tx.key_md, s->ipsec.md_len, NULL);
+	
+	if (s->ipsec.cry_algo) {
+		gcry_cipher_open(&s->ipsec.tx.cry_ctx, s->ipsec.cry_algo, GCRY_CIPHER_MODE_CBC, 0);
+		gcry_cipher_setkey(s->ipsec.tx.cry_ctx, s->ipsec.tx.key_cry, s->ipsec.key_len);
+	} else {
+		s->ipsec.tx.cry_ctx = NULL;
+	}
+	
+	DEBUG(2, printf("remote -> local spi: %#08x\n", ntohl(s->ipsec.rx.spi)));
+	DEBUG(2, printf("local -> remote spi: %#08x\n", ntohl(s->ipsec.tx.spi)));
+	
+	do_kill = 0;
+	
+	sigaction(SIGHUP, NULL, &act);
+	if (act.sa_handler == SIG_DFL)
+		signal(SIGHUP, killit);
+	
 	signal(SIGINT, killit);
 	signal(SIGTERM, killit);
-	signal(SIGXCPU, killit);
-#if defined(SIGPWR)
-	signal(SIGPWR, killit);
-#endif
-
+	
 	chdir("/");
-
-	setsid();
+	
 	if (!opt_nd) {
 		pid_t pid;
 		if ((pid = fork()) < 0) {
 			fprintf(stderr, "Warning, could not fork the child process!\n");
 		} else if (pid == 0) {
-			close(0);
-			close(1);
-			close(2);
-			openlog("vpnc", LOG_PID, LOG_DAEMON);
-			write_pidfile(pidfile);
+			close(0); open("/dev/null", O_RDONLY, 0666);
+			close(1); open("/dev/null", O_WRONLY, 0666);
+			close(2); open("/dev/null", O_WRONLY, 0666);
+			setsid();
 		} else {
 			printf("VPNC started in background (pid: %d)...\n", (int)pid);
 			exit(0);
 		}
 	} else {
 		printf("VPNC started in foreground...\n");
-		openlog("vpnc", LOG_PID, LOG_DAEMON);
-	}
-
-	vpnc_main_loop(&vpnpeer, &meth, tun_fd); /* never returns */
-	exit(0);
-}
+	}
+	openlog("vpnc", LOG_PID, LOG_DAEMON);
+	write_pidfile(pidfile);
+	
+	vpnc_main_loop(s);
+	
+	if (!opt_nd)
+		unlink(pidfile); /* ignore errors */
+}

Added: branches/vpnc-nortel/tunip.h
==============================================================================
--- branches/vpnc-nortel/tunip.h (added)
+++ branches/vpnc-nortel/tunip.h Wed Jun 11 01:19:11 2008
@@ -1,0 +1,120 @@
+/* IPSec ESP and AH support.
+   Copyright (C) 2005 Maurice Massar
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   $Id$
+*/
+
+#ifndef __TUNIP_H__
+#define __TUNIP_H__
+
+#include "isakmp.h"
+
+#include <time.h>
+#include <net/if.h>
+
+struct lifetime {
+	time_t   start;
+	uint32_t seconds;
+	uint32_t kbytes;
+	uint32_t rx;
+	uint32_t tx;
+};
+
+struct ike_sa {
+	uint32_t spi;
+	uint32_t seq_id; /* for replay protection (not implemented) */
+	
+	uint8_t *key;
+	uint8_t *key_cry;
+	gcry_cipher_hd_t cry_ctx;
+	uint8_t *key_md;
+	
+	/* Description of the packet being processed */
+	unsigned char *buf;
+	unsigned int bufsize, bufpayload, var_header_size;
+	int buflen;
+};
+
+struct encap_method; /* private to tunip.c */
+
+enum natt_active_mode_enum{
+	NATT_ACTIVE_NONE,
+	NATT_ACTIVE_CISCO_UDP, /* isakmp and esp on different ports => never encap */
+	NATT_ACTIVE_DRAFT_OLD, /* as in natt-draft 0 and 1 */
+	NATT_ACTIVE_RFC        /* draft 2 and RFC3947 / RFC3948 */
+};
+
+struct sa_block {
+	const char *pidfile;
+	
+	int tun_fd; /* fd to host via tun/tap */
+	char tun_name[IFNAMSIZ];
+	uint8_t tun_hwaddr[ETH_ALEN];
+	
+	struct in_addr dst; /* ip of concentrator, must be set */
+	struct in_addr src; /* local ip, from getsockname() */
+	
+	struct in_addr opt_src_ip; /* configured local ip, can be 0.0.0.0 */
+	
+	/* these sockets are connect()ed */
+	int ike_fd; /* fd over isakmp traffic, and in case of NAT-T esp too */
+	int esp_fd; /* raw socket for ip-esp or Cisco-UDP or ike_fd (NAT-T) */
+	
+	struct {
+		int timeout;
+		uint8_t *resend_hash;
+		uint16_t src_port, dst_port;
+		uint8_t i_cookie[ISAKMP_COOKIE_LENGTH];
+		uint8_t r_cookie[ISAKMP_COOKIE_LENGTH];
+		uint8_t *key; /* ike encryption key */
+		size_t keylen;
+		uint8_t *initial_iv;
+		uint8_t *skeyid_a;
+		uint8_t *skeyid_d;
+		int auth_algo; /* PSK, PSK+Xauth, Hybrid ToDo: Cert/... */
+		int cry_algo, md_algo;
+		size_t ivlen, md_len;
+		uint8_t current_iv_msgid[4];
+		uint8_t *current_iv;
+		struct lifetime life;
+		int do_dpd;
+		int dpd_idle;
+		uint32_t dpd_seqno;
+		uint32_t dpd_seqno_ack;
+		time_t dpd_sent;
+		unsigned int dpd_attempts;
+	} ike;
+	uint8_t our_address[4], our_netmask[4];
+	struct {
+		int do_pfs;
+		int cry_algo, md_algo;
+		size_t key_len, md_len;
+		size_t blk_len, iv_len;
+		uint16_t encap_mode;
+		uint16_t peer_udpencap_port;
+		enum natt_active_mode_enum natt_active_mode;
+		struct lifetime life;
+		struct ike_sa rx, tx;
+		struct encap_method *em;
+		uint16_t ip_id;
+	} ipsec;
+};
+
+extern int volatile do_kill;
+extern void vpnc_doit(struct sa_block *s);
+
+#endif

Added: branches/vpnc-nortel/vpnc-debug.c
==============================================================================
--- branches/vpnc-nortel/vpnc-debug.c (added)
+++ branches/vpnc-nortel/vpnc-debug.c Wed Jun 11 01:19:11 2008
@@ -1,0 +1,400 @@
+/* Automatically generated with enum2debug.pl: Don't edit! */
+
+#include <stdio.h>
+
+#include "vpnc-debug.h"
+#include "isakmp.h"
+
+const char *val_to_string(unsigned int val, const struct debug_strings *dstrings)
+{
+	static const char *unknown = " (unknown)";
+	static const char *na = "";
+	unsigned int i;
+	
+	if (dstrings == NULL)
+		return na;
+	
+	for (i = 0; dstrings[i].id != 0 || dstrings[i].string != NULL; i++)
+		if (dstrings[i].id == val)
+			return dstrings[i].string;
+	return unknown;
+}
+
+const struct debug_strings isakmp_payload_enum_array[] = {
+	{ ISAKMP_PAYLOAD_NONE,	" (ISAKMP_PAYLOAD_NONE)" },
+	{ ISAKMP_PAYLOAD_SA,	" (ISAKMP_PAYLOAD_SA)" },
+	{ ISAKMP_PAYLOAD_P,	" (ISAKMP_PAYLOAD_P)" },
+	{ ISAKMP_PAYLOAD_T,	" (ISAKMP_PAYLOAD_T)" },
+	{ ISAKMP_PAYLOAD_KE,	" (ISAKMP_PAYLOAD_KE)" },
+	{ ISAKMP_PAYLOAD_ID,	" (ISAKMP_PAYLOAD_ID)" },
+	{ ISAKMP_PAYLOAD_CERT,	" (ISAKMP_PAYLOAD_CERT)" },
+	{ ISAKMP_PAYLOAD_CR,	" (ISAKMP_PAYLOAD_CR)" },
+	{ ISAKMP_PAYLOAD_HASH,	" (ISAKMP_PAYLOAD_HASH)" },
+	{ ISAKMP_PAYLOAD_SIG,	" (ISAKMP_PAYLOAD_SIG)" },
+	{ ISAKMP_PAYLOAD_NONCE,	" (ISAKMP_PAYLOAD_NONCE)" },
+	{ ISAKMP_PAYLOAD_N,	" (ISAKMP_PAYLOAD_N)" },
+	{ ISAKMP_PAYLOAD_D,	" (ISAKMP_PAYLOAD_D)" },
+	{ ISAKMP_PAYLOAD_VID,	" (ISAKMP_PAYLOAD_VID)" },
+	{ ISAKMP_PAYLOAD_MODECFG_ATTR,	" (ISAKMP_PAYLOAD_MODECFG_ATTR)" },
+	{ ISAKMP_PAYLOAD_SAK,	" (ISAKMP_PAYLOAD_SAK)" },
+	{ ISAKMP_PAYLOAD_SAT,	" (ISAKMP_PAYLOAD_SAT)" },
+	{ ISAKMP_PAYLOAD_KD,	" (ISAKMP_PAYLOAD_KD)" },
+	{ ISAKMP_PAYLOAD_SEQNO,	" (ISAKMP_PAYLOAD_SEQNO)" },
+	{ ISAKMP_PAYLOAD_POP,	" (ISAKMP_PAYLOAD_POP)" },
+	{ ISAKMP_PAYLOAD_NAT_D,	" (ISAKMP_PAYLOAD_NAT_D)" },
+	{ ISAKMP_PAYLOAD_NAT_OA,	" (ISAKMP_PAYLOAD_NAT_OA)" },
+	{ ISAKMP_PAYLOAD_NAT_D_OLD,	" (ISAKMP_PAYLOAD_NAT_D_OLD)" },
+	{ ISAKMP_PAYLOAD_FRAG,	" (ISAKMP_PAYLOAD_FRAG)" },
+	{ 0,	(const char *) 0 }
+};
+
+const struct debug_strings isakmp_exchange_enum_array[] = {
+	{ ISAKMP_EXCHANGE_NONE,	" (ISAKMP_EXCHANGE_NONE)" },
+	{ ISAKMP_EXCHANGE_BASE,	" (ISAKMP_EXCHANGE_BASE)" },
+	{ ISAKMP_EXCHANGE_IDENTITY,	" (ISAKMP_EXCHANGE_IDENTITY)" },
+	{ ISAKMP_EXCHANGE_AUTH_ONLY,	" (ISAKMP_EXCHANGE_AUTH_ONLY)" },
+	{ ISAKMP_EXCHANGE_AGGRESSIVE,	" (ISAKMP_EXCHANGE_AGGRESSIVE)" },
+	{ ISAKMP_EXCHANGE_INFORMATIONAL,	" (ISAKMP_EXCHANGE_INFORMATIONAL)" },
+	{ ISAKMP_EXCHANGE_MODECFG_TRANSACTION,	" (ISAKMP_EXCHANGE_MODECFG_TRANSACTION)" },
+	{ ISAKMP_EXCHANGE_IKE_QUICK,	" (ISAKMP_EXCHANGE_IKE_QUICK)" },
+	{ ISAKMP_EXCHANGE_IKE_NEW_GROUP,	" (ISAKMP_EXCHANGE_IKE_NEW_GROUP)" },
+	{ 0,	(const char *) 0 }
+};
+
+const struct debug_strings isakmp_doi_enum_array[] = {
+	{ ISAKMP_DOI_GENERIC,	" (ISAKMP_DOI_GENERIC)" },
+	{ ISAKMP_DOI_IPSEC,	" (ISAKMP_DOI_IPSEC)" },
+	{ 0,	(const char *) 0 }
+};
+
+const struct debug_strings isakmp_notify_enum_array[] = {
+	{ ISAKMP_N_INVALID_PAYLOAD_TYPE,	" (ISAKMP_N_INVALID_PAYLOAD_TYPE)" },
+	{ ISAKMP_N_DOI_NOT_SUPPORTED,	" (ISAKMP_N_DOI_NOT_SUPPORTED)" },
+	{ ISAKMP_N_SITUATION_NOT_SUPPORTED,	" (ISAKMP_N_SITUATION_NOT_SUPPORTED)" },
+	{ ISAKMP_N_INVALID_COOKIE,	" (ISAKMP_N_INVALID_COOKIE)" },
+	{ ISAKMP_N_INVALID_MAJOR_VERSION,	" (ISAKMP_N_INVALID_MAJOR_VERSION)" },
+	{ ISAKMP_N_INVALID_MINOR_VERSION,	" (ISAKMP_N_INVALID_MINOR_VERSION)" },
+	{ ISAKMP_N_INVALID_EXCHANGE_TYPE,	" (ISAKMP_N_INVALID_EXCHANGE_TYPE)" },
+	{ ISAKMP_N_INVALID_FLAGS,	" (ISAKMP_N_INVALID_FLAGS)" },
+	{ ISAKMP_N_INVALID_MESSAGE_ID,	" (ISAKMP_N_INVALID_MESSAGE_ID)" },
+	{ ISAKMP_N_INVALID_PROTOCOL_ID,	" (ISAKMP_N_INVALID_PROTOCOL_ID)" },
+	{ ISAKMP_N_INVALID_SPI,	" (ISAKMP_N_INVALID_SPI)" },
+	{ ISAKMP_N_INVALID_TRANSFORM_ID,	" (ISAKMP_N_INVALID_TRANSFORM_ID)" },
+	{ ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED,	" (ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED)" },
+	{ ISAKMP_N_NO_PROPOSAL_CHOSEN,	" (ISAKMP_N_NO_PROPOSAL_CHOSEN)" },
+	{ ISAKMP_N_BAD_PROPOSAL_SYNTAX,	" (ISAKMP_N_BAD_PROPOSAL_SYNTAX)" },
+	{ ISAKMP_N_PAYLOAD_MALFORMED,	" (ISAKMP_N_PAYLOAD_MALFORMED)" },
+	{ ISAKMP_N_INVALID_KEY_INFORMATION,	" (ISAKMP_N_INVALID_KEY_INFORMATION)" },
+	{ ISAKMP_N_INVALID_ID_INFORMATION,	" (ISAKMP_N_INVALID_ID_INFORMATION)" },
+	{ ISAKMP_N_INVALID_CERT_ENCODING,	" (ISAKMP_N_INVALID_CERT_ENCODING)" },
+	{ ISAKMP_N_INVALID_CERTIFICATE,	" (ISAKMP_N_INVALID_CERTIFICATE)" },
+	{ ISAKMP_N_CERT_TYPE_UNSUPPORTED,	" (ISAKMP_N_CERT_TYPE_UNSUPPORTED)" },
+	{ ISAKMP_N_INVALID_CERT_AUTHORITY,	" (ISAKMP_N_INVALID_CERT_AUTHORITY)" },
+	{ ISAKMP_N_INVALID_HASH_INFORMATION,	" (ISAKMP_N_INVALID_HASH_INFORMATION)" },
+	{ ISAKMP_N_AUTHENTICATION_FAILED,	" (ISAKMP_N_AUTHENTICATION_FAILED)" },
+	{ ISAKMP_N_INVALID_SIGNATURE,	" (ISAKMP_N_INVALID_SIGNATURE)" },
+	{ ISAKMP_N_ADDRESS_NOTIFICATION,	" (ISAKMP_N_ADDRESS_NOTIFICATION)" },
+	{ ISAKMP_N_NOTIFY_SA_LIFETIME,	" (ISAKMP_N_NOTIFY_SA_LIFETIME)" },
+	{ ISAKMP_N_CERTIFICATE_UNAVAILABLE,	" (ISAKMP_N_CERTIFICATE_UNAVAILABLE)" },
+	{ ISAKMP_N_UNSUPPORTED_EXCHANGE_TYPE,	" (ISAKMP_N_UNSUPPORTED_EXCHANGE_TYPE)" },
+	{ ISAKMP_N_UNEQUAL_PAYLOAD_LENGTHS,	" (ISAKMP_N_UNEQUAL_PAYLOAD_LENGTHS)" },
+	{ ISAKMP_N_CONNECTED,	" (ISAKMP_N_CONNECTED)" },
+	{ ISAKMP_N_IPSEC_RESPONDER_LIFETIME,	" (ISAKMP_N_IPSEC_RESPONDER_LIFETIME)" },
+	{ ISAKMP_N_IPSEC_REPLAY_STATUS,	" (ISAKMP_N_IPSEC_REPLAY_STATUS)" },
+	{ ISAKMP_N_IPSEC_INITIAL_CONTACT,	" (ISAKMP_N_IPSEC_INITIAL_CONTACT)" },
+	{ ISAKMP_N_CISCO_HELLO,	" (ISAKMP_N_CISCO_HELLO)" },
+	{ ISAKMP_N_CISCO_WWTEBR,	" (ISAKMP_N_CISCO_WWTEBR)" },
+	{ ISAKMP_N_CISCO_SHUT_UP,	" (ISAKMP_N_CISCO_SHUT_UP)" },
+	{ ISAKMP_N_IOS_KEEP_ALIVE_REQ,	" (ISAKMP_N_IOS_KEEP_ALIVE_REQ)" },
+	{ ISAKMP_N_IOS_KEEP_ALIVE_ACK,	" (ISAKMP_N_IOS_KEEP_ALIVE_ACK)" },
+	{ ISAKMP_N_R_U_THERE,	" (ISAKMP_N_R_U_THERE)" },
+	{ ISAKMP_N_R_U_THERE_ACK,	" (ISAKMP_N_R_U_THERE_ACK)" },
+	{ ISAKMP_N_CISCO_LOAD_BALANCE,	" (ISAKMP_N_CISCO_LOAD_BALANCE)" },
+	{ ISAKMP_N_CISCO_PRESHARED_KEY_HASH,	" (ISAKMP_N_CISCO_PRESHARED_KEY_HASH)" },
+	{ 0,	(const char *) 0 }
+};
+
+const struct debug_strings isakmp_certificate_enum_array[] = {
+	{ ISAKMP_CERT_NONE,	" (ISAKMP_CERT_NONE)" },
+	{ ISAKMP_CERT_PKCS7_X509,	" (ISAKMP_CERT_PKCS7_X509)" },
+	{ ISAKMP_CERT_PGP,	" (ISAKMP_CERT_PGP)" },
+	{ ISAKMP_CERT_DNS_SIG_KEY,	" (ISAKMP_CERT_DNS_SIG_KEY)" },
+	{ ISAKMP_CERT_X509_SIG,	" (ISAKMP_CERT_X509_SIG)" },
+	{ ISAKMP_CERT_X509_KEX_EXCHANGE,	" (ISAKMP_CERT_X509_KEX_EXCHANGE)" },
+	{ ISAKMP_CERT_KERBEROS_TOKENS,	" (ISAKMP_CERT_KERBEROS_TOKENS)" },
+	{ ISAKMP_CERT_CRL,	" (ISAKMP_CERT_CRL)" },
+	{ ISAKMP_CERT_ARL,	" (ISAKMP_CERT_ARL)" },
+	{ ISAKMP_CERT_SPKI,	" (ISAKMP_CERT_SPKI)" },
+	{ ISAKMP_CERT_X509_ATTRIBUTE,	" (ISAKMP_CERT_X509_ATTRIBUTE)" },
+	{ 0,	(const char *) 0 }
+};
+
+const struct debug_strings ike_attr_enum_array[] = {
+	{ IKE_ATTRIB_ENC,	" (IKE_ATTRIB_ENC)" },
+	{ IKE_ATTRIB_HASH,	" (IKE_ATTRIB_HASH)" },
+	{ IKE_ATTRIB_AUTH_METHOD,	" (IKE_ATTRIB_AUTH_METHOD)" },
+	{ IKE_ATTRIB_GROUP_DESC,	" (IKE_ATTRIB_GROUP_DESC)" },
+	{ IKE_ATTRIB_GROUP_TYPE,	" (IKE_ATTRIB_GROUP_TYPE)" },
+	{ IKE_ATTRIB_GROUP_PRIME,	" (IKE_ATTRIB_GROUP_PRIME)" },
+	{ IKE_ATTRIB_GROUP_GEN_1,	" (IKE_ATTRIB_GROUP_GEN_1)" },
+	{ IKE_ATTRIB_GROUP_GEN_2,	" (IKE_ATTRIB_GROUP_GEN_2)" },
+	{ IKE_ATTRIB_GROUP_CURVE_A,	" (IKE_ATTRIB_GROUP_CURVE_A)" },
+	{ IKE_ATTRIB_GROUP_CURVE_B,	" (IKE_ATTRIB_GROUP_CURVE_B)" },
+	{ IKE_ATTRIB_LIFE_TYPE,	" (IKE_ATTRIB_LIFE_TYPE)" },
+	{ IKE_ATTRIB_LIFE_DURATION,	" (IKE_ATTRIB_LIFE_DURATION)" },
+	{ IKE_ATTRIB_PRF,	" (IKE_ATTRIB_PRF)" },
+	{ IKE_ATTRIB_KEY_LENGTH,	" (IKE_ATTRIB_KEY_LENGTH)" },
+	{ IKE_ATTRIB_FIELD_SIZE,	" (IKE_ATTRIB_FIELD_SIZE)" },
+	{ IKE_ATTRIB_GROUP_ORDER,	" (IKE_ATTRIB_GROUP_ORDER)" },
+	{ IKE_ATTRIB_BLOCK_SIZE,	" (IKE_ATTRIB_BLOCK_SIZE)" },
+	{ 0,	(const char *) 0 }
+};
+
+const struct debug_strings ike_enc_enum_array[] = {
+	{ IKE_ENC_NO_CBC,	" (IKE_ENC_NO_CBC)" },
+	{ IKE_ENC_DES_CBC,	" (IKE_ENC_DES_CBC)" },
+	{ IKE_ENC_IDEA_CBC,	" (IKE_ENC_IDEA_CBC)" },
+	{ IKE_ENC_BLOWFISH_CBC,	" (IKE_ENC_BLOWFISH_CBC)" },
+	{ IKE_ENC_RC5_R16_B16_CBC,	" (IKE_ENC_RC5_R16_B16_CBC)" },
+	{ IKE_ENC_3DES_CBC,	" (IKE_ENC_3DES_CBC)" },
+	{ IKE_ENC_CAST_CBC,	" (IKE_ENC_CAST_CBC)" },
+	{ IKE_ENC_AES_CBC,	" (IKE_ENC_AES_CBC)" },
+	{ 0,	(const char *) 0 }
+};
+
+const struct debug_strings ike_hash_enum_array[] = {
+	{ IKE_HASH_MD5,	" (IKE_HASH_MD5)" },
+	{ IKE_HASH_SHA,	" (IKE_HASH_SHA)" },
+	{ IKE_HASH_TIGER,	" (IKE_HASH_TIGER)" },
+	{ IKE_HASH_SHA2_256,	" (IKE_HASH_SHA2_256)" },
+	{ IKE_HASH_SHA2_384,	" (IKE_HASH_SHA2_384)" },
+	{ IKE_HASH_SHA2_512,	" (IKE_HASH_SHA2_512)" },
+	{ 0,	(const char *) 0 }
+};
+
+const struct debug_strings ike_auth_enum_array[] = {
+	{ IKE_AUTH_PRESHARED,	" (IKE_AUTH_PRESHARED)" },
+	{ IKE_AUTH_DSS,	" (IKE_AUTH_DSS)" },
+	{ IKE_AUTH_RSA_SIG,	" (IKE_AUTH_RSA_SIG)" },
+	{ IKE_AUTH_RSA_ENC,	" (IKE_AUTH_RSA_ENC)" },
+	{ IKE_AUTH_RSA_ENC_2,	" (IKE_AUTH_RSA_ENC_2)" },
+	{ IKE_AUTH_EL_GAMAL_ENC,	" (IKE_AUTH_EL_GAMAL_ENC)" },
+	{ IKE_AUTH_EL_GAMAL_ENC_REV,	" (IKE_AUTH_EL_GAMAL_ENC_REV)" },
+	{ IKE_AUTH_ECDSA_SIG,	" (IKE_AUTH_ECDSA_SIG)" },
+	{ IKE_AUTH_HybridInitRSA,	" (IKE_AUTH_HybridInitRSA)" },
+	{ IKE_AUTH_HybridRespRSA,	" (IKE_AUTH_HybridRespRSA)" },
+	{ IKE_AUTH_HybridInitDSS,	" (IKE_AUTH_HybridInitDSS)" },
+	{ IKE_AUTH_HybridRespDSS,	" (IKE_AUTH_HybridRespDSS)" },
+	{ IKE_AUTH_XAUTHInitPreShared,	" (IKE_AUTH_XAUTHInitPreShared)" },
+	{ IKE_AUTH_XAUTHRespPreShared,	" (IKE_AUTH_XAUTHRespPreShared)" },
+	{ IKE_AUTH_XAUTHInitDSS,	" (IKE_AUTH_XAUTHInitDSS)" },
+	{ IKE_AUTH_XAUTHRespDSS,	" (IKE_AUTH_XAUTHRespDSS)" },
+	{ IKE_AUTH_XAUTHInitRSA,	" (IKE_AUTH_XAUTHInitRSA)" },
+	{ IKE_AUTH_XAUTHRespRSA,	" (IKE_AUTH_XAUTHRespRSA)" },
+	{ IKE_AUTH_XAUTHInitRSAEncryption,	" (IKE_AUTH_XAUTHInitRSAEncryption)" },
+	{ IKE_AUTH_XAUTHRespRSAEncryption,	" (IKE_AUTH_XAUTHRespRSAEncryption)" },
+	{ IKE_AUTH_XAUTHInitRSARevisedEncryption,	" (IKE_AUTH_XAUTHInitRSARevisedEncryption)" },
+	{ IKE_AUTH_XAUTHRespRSARevisedEncryption,	" (IKE_AUTH_XAUTHRespRSARevisedEncryption)" },
+	{ 0,	(const char *) 0 }
+};
+
+const struct debug_strings ike_group_enum_array[] = {
+	{ IKE_GROUP_MODP_768,	" (IKE_GROUP_MODP_768)" },
+	{ IKE_GROUP_MODP_1024,	" (IKE_GROUP_MODP_1024)" },
+	{ IKE_GROUP_EC2N_155,	" (IKE_GROUP_EC2N_155)" },
+	{ IKE_GROUP_EC2N_185,	" (IKE_GROUP_EC2N_185)" },
+	{ IKE_GROUP_MODP_1536,	" (IKE_GROUP_MODP_1536)" },
+	{ IKE_GROUP_EC2N_163sect,	" (IKE_GROUP_EC2N_163sect)" },
+	{ IKE_GROUP_EC2N_163K,	" (IKE_GROUP_EC2N_163K)" },
+	{ IKE_GROUP_EC2N_283sect,	" (IKE_GROUP_EC2N_283sect)" },
+	{ IKE_GROUP_EC2N_283K,	" (IKE_GROUP_EC2N_283K)" },
+	{ IKE_GROUP_EC2N_409sect,	" (IKE_GROUP_EC2N_409sect)" },
+	{ IKE_GROUP_EC2N_409K,	" (IKE_GROUP_EC2N_409K)" },
+	{ IKE_GROUP_EC2N_571sect,	" (IKE_GROUP_EC2N_571sect)" },
+	{ IKE_GROUP_EC2N_571K,	" (IKE_GROUP_EC2N_571K)" },
+	{ 0,	(const char *) 0 }
+};
+
+const struct debug_strings ike_group_type_enum_array[] = {
+	{ IKE_GROUP_TYPE_MODP,	" (IKE_GROUP_TYPE_MODP)" },
+	{ IKE_GROUP_TYPE_ECP,	" (IKE_GROUP_TYPE_ECP)" },
+	{ IKE_GROUP_TYPE_EC2N,	" (IKE_GROUP_TYPE_EC2N)" },
+	{ 0,	(const char *) 0 }
+};
+
+const struct debug_strings ike_life_enum_array[] = {
+	{ IKE_LIFE_TYPE_SECONDS,	" (IKE_LIFE_TYPE_SECONDS)" },
+	{ IKE_LIFE_TYPE_K,	" (IKE_LIFE_TYPE_K)" },
+	{ 0,	(const char *) 0 }
+};
+
+const struct debug_strings isakmp_ipsec_sit_enum_array[] = {
+	{ ISAKMP_IPSEC_SIT_IDENTITY_ONLY,	" (ISAKMP_IPSEC_SIT_IDENTITY_ONLY)" },
+	{ ISAKMP_IPSEC_SIT_SECRECY,	" (ISAKMP_IPSEC_SIT_SECRECY)" },
+	{ ISAKMP_IPSEC_SIT_INTEGRITY,	" (ISAKMP_IPSEC_SIT_INTEGRITY)" },
+	{ 0,	(const char *) 0 }
+};
+
+const struct debug_strings isakmp_ipsec_id_enum_array[] = {
+	{ ISAKMP_IPSEC_ID_RESERVED,	" (ISAKMP_IPSEC_ID_RESERVED)" },
+	{ ISAKMP_IPSEC_ID_IPV4_ADDR,	" (ISAKMP_IPSEC_ID_IPV4_ADDR)" },
+	{ ISAKMP_IPSEC_ID_FQDN,	" (ISAKMP_IPSEC_ID_FQDN)" },
+	{ ISAKMP_IPSEC_ID_USER_FQDN,	" (ISAKMP_IPSEC_ID_USER_FQDN)" },
+	{ ISAKMP_IPSEC_ID_IPV4_ADDR_SUBNET,	" (ISAKMP_IPSEC_ID_IPV4_ADDR_SUBNET)" },
+	{ ISAKMP_IPSEC_ID_IPV6_ADDR,	" (ISAKMP_IPSEC_ID_IPV6_ADDR)" },
+	{ ISAKMP_IPSEC_ID_IPV6_ADDR_SUBNET,	" (ISAKMP_IPSEC_ID_IPV6_ADDR_SUBNET)" },
+	{ ISAKMP_IPSEC_ID_IPV4_ADDR_RANGE,	" (ISAKMP_IPSEC_ID_IPV4_ADDR_RANGE)" },
+	{ ISAKMP_IPSEC_ID_IPV6_ADDR_RANGE,	" (ISAKMP_IPSEC_ID_IPV6_ADDR_RANGE)" },
+	{ ISAKMP_IPSEC_ID_DER_ASN1_DN,	" (ISAKMP_IPSEC_ID_DER_ASN1_DN)" },
+	{ ISAKMP_IPSEC_ID_DER_ASN1_GN,	" (ISAKMP_IPSEC_ID_DER_ASN1_GN)" },
+	{ ISAKMP_IPSEC_ID_KEY_ID,	" (ISAKMP_IPSEC_ID_KEY_ID)" },
+	{ 0,	(const char *) 0 }
+};
+
+const struct debug_strings isakmp_ipsec_proto_enum_array[] = {
+	{ ISAKMP_IPSEC_PROTO_RESERVED,	" (ISAKMP_IPSEC_PROTO_RESERVED)" },
+	{ ISAKMP_IPSEC_PROTO_ISAKMP,	" (ISAKMP_IPSEC_PROTO_ISAKMP)" },
+	{ ISAKMP_IPSEC_PROTO_IPSEC_AH,	" (ISAKMP_IPSEC_PROTO_IPSEC_AH)" },
+	{ ISAKMP_IPSEC_PROTO_IPSEC_ESP,	" (ISAKMP_IPSEC_PROTO_IPSEC_ESP)" },
+	{ ISAKMP_IPSEC_PROTO_IPCOMP,	" (ISAKMP_IPSEC_PROTO_IPCOMP)" },
+	{ ISAKMP_IPSEC_PROTO_MODECFG,	" (ISAKMP_IPSEC_PROTO_MODECFG)" },
+	{ 0,	(const char *) 0 }
+};
+
+const struct debug_strings isakmp_ipsec_key_enum_array[] = {
+	{ ISAKMP_IPSEC_KEY_RESERVED,	" (ISAKMP_IPSEC_KEY_RESERVED)" },
+	{ ISAKMP_IPSEC_KEY_IKE,	" (ISAKMP_IPSEC_KEY_IKE)" },
+	{ 0,	(const char *) 0 }
+};
+
+const struct debug_strings isakmp_ipsec_ah_enum_array[] = {
+	{ ISAKMP_IPSEC_AH_RESERVED,	" (ISAKMP_IPSEC_AH_RESERVED)" },
+	{ ISAKMP_IPSEC_AH_MD5,	" (ISAKMP_IPSEC_AH_MD5)" },
+	{ ISAKMP_IPSEC_AH_SHA,	" (ISAKMP_IPSEC_AH_SHA)" },
+	{ ISAKMP_IPSEC_AH_DES,	" (ISAKMP_IPSEC_AH_DES)" },
+	{ ISAKMP_IPSEC_AH_SHA2_256,	" (ISAKMP_IPSEC_AH_SHA2_256)" },
+	{ ISAKMP_IPSEC_AH_SHA2_384,	" (ISAKMP_IPSEC_AH_SHA2_384)" },
+	{ ISAKMP_IPSEC_AH_SHA2_512,	" (ISAKMP_IPSEC_AH_SHA2_512)" },
+	{ ISAKMP_IPSEC_AH_RIPEMD,	" (ISAKMP_IPSEC_AH_RIPEMD)" },
+	{ 0,	(const char *) 0 }
+};
+
+const struct debug_strings isakmp_ipsec_esp_enum_array[] = {
+	{ ISAKMP_IPSEC_ESP_RESERVED,	" (ISAKMP_IPSEC_ESP_RESERVED)" },
+	{ ISAKMP_IPSEC_ESP_DES_IV64,	" (ISAKMP_IPSEC_ESP_DES_IV64)" },
+	{ ISAKMP_IPSEC_ESP_DES,	" (ISAKMP_IPSEC_ESP_DES)" },
+	{ ISAKMP_IPSEC_ESP_3DES,	" (ISAKMP_IPSEC_ESP_3DES)" },
+	{ ISAKMP_IPSEC_ESP_RC5,	" (ISAKMP_IPSEC_ESP_RC5)" },
+	{ ISAKMP_IPSEC_ESP_IDEA,	" (ISAKMP_IPSEC_ESP_IDEA)" },
+	{ ISAKMP_IPSEC_ESP_CAST,	" (ISAKMP_IPSEC_ESP_CAST)" },
+	{ ISAKMP_IPSEC_ESP_BLOWFISH,	" (ISAKMP_IPSEC_ESP_BLOWFISH)" },
+	{ ISAKMP_IPSEC_ESP_3IDEA,	" (ISAKMP_IPSEC_ESP_3IDEA)" },
+	{ ISAKMP_IPSEC_ESP_DES_IV32,	" (ISAKMP_IPSEC_ESP_DES_IV32)" },
+	{ ISAKMP_IPSEC_ESP_RC4,	" (ISAKMP_IPSEC_ESP_RC4)" },
+	{ ISAKMP_IPSEC_ESP_NULL,	" (ISAKMP_IPSEC_ESP_NULL)" },
+	{ ISAKMP_IPSEC_ESP_AES,	" (ISAKMP_IPSEC_ESP_AES)" },
+	{ ISAKMP_IPSEC_ESP_AES_128_CTR,	" (ISAKMP_IPSEC_ESP_AES_128_CTR)" },
+	{ ISAKMP_IPSEC_ESP_AES_MARS,	" (ISAKMP_IPSEC_ESP_AES_MARS)" },
+	{ ISAKMP_IPSEC_ESP_AES_RC6,	" (ISAKMP_IPSEC_ESP_AES_RC6)" },
+	{ ISAKMP_IPSEC_ESP_AES_RIJNDAEL,	" (ISAKMP_IPSEC_ESP_AES_RIJNDAEL)" },
+	{ ISAKMP_IPSEC_ESP_AES_SERPENT,	" (ISAKMP_IPSEC_ESP_AES_SERPENT)" },
+	{ ISAKMP_IPSEC_ESP_AES_TWOFISH,	" (ISAKMP_IPSEC_ESP_AES_TWOFISH)" },
+	{ 0,	(const char *) 0 }
+};
+
+const struct debug_strings isakmp_ipsec_attr_enum_array[] = {
+	{ ISAKMP_IPSEC_ATTRIB_SA_LIFE_TYPE,	" (ISAKMP_IPSEC_ATTRIB_SA_LIFE_TYPE)" },
+	{ ISAKMP_IPSEC_ATTRIB_SA_LIFE_DURATION,	" (ISAKMP_IPSEC_ATTRIB_SA_LIFE_DURATION)" },
+	{ ISAKMP_IPSEC_ATTRIB_GROUP_DESC,	" (ISAKMP_IPSEC_ATTRIB_GROUP_DESC)" },
+	{ ISAKMP_IPSEC_ATTRIB_ENCAP_MODE,	" (ISAKMP_IPSEC_ATTRIB_ENCAP_MODE)" },
+	{ ISAKMP_IPSEC_ATTRIB_AUTH_ALG,	" (ISAKMP_IPSEC_ATTRIB_AUTH_ALG)" },
+	{ ISAKMP_IPSEC_ATTRIB_KEY_LENGTH,	" (ISAKMP_IPSEC_ATTRIB_KEY_LENGTH)" },
+	{ ISAKMP_IPSEC_ATTRIB_KEY_ROUNDS,	" (ISAKMP_IPSEC_ATTRIB_KEY_ROUNDS)" },
+	{ ISAKMP_IPSEC_ATTRIB_COMP_DICT_SIZE,	" (ISAKMP_IPSEC_ATTRIB_COMP_DICT_SIZE)" },
+	{ ISAKMP_IPSEC_ATTRIB_COMP_PRIVATE_ALG,	" (ISAKMP_IPSEC_ATTRIB_COMP_PRIVATE_ALG)" },
+	{ ISAKMP_IPSEC_ATTRIB_ECN_TUNNEL,	" (ISAKMP_IPSEC_ATTRIB_ECN_TUNNEL)" },
+	{ 0,	(const char *) 0 }
+};
+
+const struct debug_strings isakmp_ipsec_ipcomp_enum_array[] = {
+	{ ISAKMP_IPSEC_IPCOMP_RESERVED,	" (ISAKMP_IPSEC_IPCOMP_RESERVED)" },
+	{ ISAKMP_IPSEC_IPCOMP_OUI,	" (ISAKMP_IPSEC_IPCOMP_OUI)" },
+	{ ISAKMP_IPSEC_IPCOMP_DEFLATE,	" (ISAKMP_IPSEC_IPCOMP_DEFLATE)" },
+	{ ISAKMP_IPSEC_IPCOMP_LZS,	" (ISAKMP_IPSEC_IPCOMP_LZS)" },
+	{ ISAKMP_IPSEC_IPCOMP_V42BIS,	" (ISAKMP_IPSEC_IPCOMP_V42BIS)" },
+	{ 0,	(const char *) 0 }
+};
+
+const struct debug_strings ipsec_life_enum_array[] = {
+	{ IPSEC_LIFE_SECONDS,	" (IPSEC_LIFE_SECONDS)" },
+	{ IPSEC_LIFE_K,	" (IPSEC_LIFE_K)" },
+	{ 0,	(const char *) 0 }
+};
+
+const struct debug_strings ipsec_encap_enum_array[] = {
+	{ IPSEC_ENCAP_TUNNEL,	" (IPSEC_ENCAP_TUNNEL)" },
+	{ IPSEC_ENCAP_TRANSPORT,	" (IPSEC_ENCAP_TRANSPORT)" },
+	{ IPSEC_ENCAP_UDP_TUNNEL,	" (IPSEC_ENCAP_UDP_TUNNEL)" },
+	{ IPSEC_ENCAP_UDP_TRANSPORT,	" (IPSEC_ENCAP_UDP_TRANSPORT)" },
+	{ IPSEC_ENCAP_UDP_TUNNEL_OLD,	" (IPSEC_ENCAP_UDP_TUNNEL_OLD)" },
+	{ IPSEC_ENCAP_UDP_TRANSPORT_OLD,	" (IPSEC_ENCAP_UDP_TRANSPORT_OLD)" },
+	{ 0,	(const char *) 0 }
+};
+
+const struct debug_strings ipsec_auth_enum_array[] = {
+	{ IPSEC_AUTH_HMAC_MD5,	" (IPSEC_AUTH_HMAC_MD5)" },
+	{ IPSEC_AUTH_HMAC_SHA,	" (IPSEC_AUTH_HMAC_SHA)" },
+	{ IPSEC_AUTH_DES_MAC,	" (IPSEC_AUTH_DES_MAC)" },
+	{ IPSEC_AUTH_KPDK,	" (IPSEC_AUTH_KPDK)" },
+	{ 0,	(const char *) 0 }
+};
+
+const struct debug_strings isakmp_modecfg_cfg_enum_array[] = {
+	{ ISAKMP_MODECFG_CFG_REQUEST,	" (ISAKMP_MODECFG_CFG_REQUEST)" },
+	{ ISAKMP_MODECFG_CFG_REPLY,	" (ISAKMP_MODECFG_CFG_REPLY)" },
+	{ ISAKMP_MODECFG_CFG_SET,	" (ISAKMP_MODECFG_CFG_SET)" },
+	{ ISAKMP_MODECFG_CFG_ACK,	" (ISAKMP_MODECFG_CFG_ACK)" },
+	{ 0,	(const char *) 0 }
+};
+
+const struct debug_strings isakmp_modecfg_attrib_enum_array[] = {
+	{ ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_ADDRESS,	" (ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_ADDRESS)" },
+	{ ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_NETMASK,	" (ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_NETMASK)" },
+	{ ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_DNS,	" (ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_DNS)" },
+	{ ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_NBNS,	" (ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_NBNS)" },
+	{ ISAKMP_MODECFG_ATTRIB_INTERNAL_ADDRESS_EXPIRY,	" (ISAKMP_MODECFG_ATTRIB_INTERNAL_ADDRESS_EXPIRY)" },
+	{ ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_DHCP,	" (ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_DHCP)" },
+	{ ISAKMP_MODECFG_ATTRIB_APPLICATION_VERSION,	" (ISAKMP_MODECFG_ATTRIB_APPLICATION_VERSION)" },
+	{ ISAKMP_MODECFG_ATTRIB_INTERNAL_IP6_ADDRESS,	" (ISAKMP_MODECFG_ATTRIB_INTERNAL_IP6_ADDRESS)" },
+	{ ISAKMP_MODECFG_ATTRIB_INTERNAL_IP6_NETMASK,	" (ISAKMP_MODECFG_ATTRIB_INTERNAL_IP6_NETMASK)" },
+	{ ISAKMP_MODECFG_ATTRIB_INTERNAL_IP6_DNS,	" (ISAKMP_MODECFG_ATTRIB_INTERNAL_IP6_DNS)" },
+	{ ISAKMP_MODECFG_ATTRIB_INTERNAL_IP6_NBNS,	" (ISAKMP_MODECFG_ATTRIB_INTERNAL_IP6_NBNS)" },
+	{ ISAKMP_MODECFG_ATTRIB_INTERNAL_IP6_DHCP,	" (ISAKMP_MODECFG_ATTRIB_INTERNAL_IP6_DHCP)" },
+	{ ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_SUBNET,	" (ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_SUBNET)" },
+	{ ISAKMP_MODECFG_ATTRIB_SUPPORTED_ATTRIBUTES,	" (ISAKMP_MODECFG_ATTRIB_SUPPORTED_ATTRIBUTES)" },
+	{ ISAKMP_MODECFG_ATTRIB_INTERNAL_IP6_SUBNET,	" (ISAKMP_MODECFG_ATTRIB_INTERNAL_IP6_SUBNET)" },
+	{ ISAKMP_XAUTH_ATTRIB_TYPE,	" (ISAKMP_XAUTH_ATTRIB_TYPE)" },
+	{ ISAKMP_XAUTH_ATTRIB_USER_NAME,	" (ISAKMP_XAUTH_ATTRIB_USER_NAME)" },
+	{ ISAKMP_XAUTH_ATTRIB_USER_PASSWORD,	" (ISAKMP_XAUTH_ATTRIB_USER_PASSWORD)" },
+	{ ISAKMP_XAUTH_ATTRIB_PASSCODE,	" (ISAKMP_XAUTH_ATTRIB_PASSCODE)" },
+	{ ISAKMP_XAUTH_ATTRIB_MESSAGE,	" (ISAKMP_XAUTH_ATTRIB_MESSAGE)" },
+	{ ISAKMP_XAUTH_ATTRIB_CHALLENGE,	" (ISAKMP_XAUTH_ATTRIB_CHALLENGE)" },
+	{ ISAKMP_XAUTH_ATTRIB_DOMAIN,	" (ISAKMP_XAUTH_ATTRIB_DOMAIN)" },
+	{ ISAKMP_XAUTH_ATTRIB_STATUS,	" (ISAKMP_XAUTH_ATTRIB_STATUS)" },
+	{ ISAKMP_XAUTH_ATTRIB_NEXT_PIN,	" (ISAKMP_XAUTH_ATTRIB_NEXT_PIN)" },
+	{ ISAKMP_XAUTH_ATTRIB_ANSWER,	" (ISAKMP_XAUTH_ATTRIB_ANSWER)" },
+	{ ISAKMP_MODECFG_ATTRIB_CISCO_BANNER,	" (ISAKMP_MODECFG_ATTRIB_CISCO_BANNER)" },
+	{ ISAKMP_MODECFG_ATTRIB_CISCO_SAVE_PW,	" (ISAKMP_MODECFG_ATTRIB_CISCO_SAVE_PW)" },
+	{ ISAKMP_MODECFG_ATTRIB_CISCO_DEF_DOMAIN,	" (ISAKMP_MODECFG_ATTRIB_CISCO_DEF_DOMAIN)" },
+	{ ISAKMP_MODECFG_ATTRIB_CISCO_SPLIT_DNS,	" (ISAKMP_MODECFG_ATTRIB_CISCO_SPLIT_DNS)" },
+	{ ISAKMP_MODECFG_ATTRIB_CISCO_SPLIT_INC,	" (ISAKMP_MODECFG_ATTRIB_CISCO_SPLIT_INC)" },
+	{ ISAKMP_MODECFG_ATTRIB_CISCO_UDP_ENCAP_PORT,	" (ISAKMP_MODECFG_ATTRIB_CISCO_UDP_ENCAP_PORT)" },
+	{ ISAKMP_MODECFG_ATTRIB_CISCO_UNKNOWN,	" (ISAKMP_MODECFG_ATTRIB_CISCO_UNKNOWN)" },
+	{ ISAKMP_MODECFG_ATTRIB_CISCO_DO_PFS,	" (ISAKMP_MODECFG_ATTRIB_CISCO_DO_PFS)" },
+	{ ISAKMP_MODECFG_ATTRIB_CISCO_FW_TYPE,	" (ISAKMP_MODECFG_ATTRIB_CISCO_FW_TYPE)" },
+	{ ISAKMP_MODECFG_ATTRIB_CISCO_BACKUP_SERVER,	" (ISAKMP_MODECFG_ATTRIB_CISCO_BACKUP_SERVER)" },
+	{ ISAKMP_MODECFG_ATTRIB_CISCO_DDNS_HOSTNAME,	" (ISAKMP_MODECFG_ATTRIB_CISCO_DDNS_HOSTNAME)" },
+	{ ISAKMP_XAUTH_ATTRIB_CISCOEXT_VENDOR,	" (ISAKMP_XAUTH_ATTRIB_CISCOEXT_VENDOR)" },
+	{ 0,	(const char *) 0 }
+};
+

Added: branches/vpnc-nortel/vpnc-debug.h
==============================================================================
--- branches/vpnc-nortel/vpnc-debug.h (added)
+++ branches/vpnc-nortel/vpnc-debug.h Wed Jun 11 01:19:11 2008
@@ -1,0 +1,34 @@
+/* Automatically generated with enum2debug.pl: Don't edit! */
+
+struct debug_strings {
+	unsigned int id;
+	const char *string;
+};
+
+extern const char *val_to_string(unsigned int, const struct debug_strings *);
+
+extern const struct debug_strings isakmp_payload_enum_array[];
+extern const struct debug_strings isakmp_exchange_enum_array[];
+extern const struct debug_strings isakmp_doi_enum_array[];
+extern const struct debug_strings isakmp_notify_enum_array[];
+extern const struct debug_strings isakmp_certificate_enum_array[];
+extern const struct debug_strings ike_attr_enum_array[];
+extern const struct debug_strings ike_enc_enum_array[];
+extern const struct debug_strings ike_hash_enum_array[];
+extern const struct debug_strings ike_auth_enum_array[];
+extern const struct debug_strings ike_group_enum_array[];
+extern const struct debug_strings ike_group_type_enum_array[];
+extern const struct debug_strings ike_life_enum_array[];
+extern const struct debug_strings isakmp_ipsec_sit_enum_array[];
+extern const struct debug_strings isakmp_ipsec_id_enum_array[];
+extern const struct debug_strings isakmp_ipsec_proto_enum_array[];
+extern const struct debug_strings isakmp_ipsec_key_enum_array[];
+extern const struct debug_strings isakmp_ipsec_ah_enum_array[];
+extern const struct debug_strings isakmp_ipsec_esp_enum_array[];
+extern const struct debug_strings isakmp_ipsec_attr_enum_array[];
+extern const struct debug_strings isakmp_ipsec_ipcomp_enum_array[];
+extern const struct debug_strings ipsec_life_enum_array[];
+extern const struct debug_strings ipsec_encap_enum_array[];
+extern const struct debug_strings ipsec_auth_enum_array[];
+extern const struct debug_strings isakmp_modecfg_cfg_enum_array[];
+extern const struct debug_strings isakmp_modecfg_attrib_enum_array[];

Modified: branches/vpnc-nortel/vpnc-disconnect
==============================================================================
--- branches/vpnc-nortel/vpnc-disconnect (original)
+++ branches/vpnc-nortel/vpnc-disconnect Wed Jun 11 01:19:11 2008
@@ -1,7 +1,5 @@
 #!/bin/sh
 
-defr=/var/run/vpnc/defaultroute
-gateway=/var/run/vpnc/gateway
 pid=/var/run/vpnc/pid
 
 if [ $# -ne 0 ]; then
@@ -22,18 +20,4 @@
 fi
 
 echo "Terminating vpnc daemon (pid: $PID)"
-kill $PID
-
-# this still sucks. Invent something to sync route removal/addition with
-# vpnc-connect
-
-if [ -s "$defr" ]; then
-	ip route del default > /dev/null 2>&1
-	ip route add $(cat "$defr")
-	test -r "$gateway" && ip route del $(cat "$gateway")
-	ip route flush cache
-fi
-
-rm -f -- "$defr" "$pid" "$gateway"
-
-exit 0
+exec kill $PID

Added: branches/vpnc-nortel/vpnc-script.in
==============================================================================
--- branches/vpnc-nortel/vpnc-script.in (added)
+++ branches/vpnc-nortel/vpnc-script.in Wed Jun 11 01:19:11 2008
@@ -1,0 +1,511 @@
+#!/bin/sh
+#* reason                       -- why this script was called, one of: pre-init connect disconnect
+#* VPNGATEWAY                   -- vpn gateway address (always present)
+#* TUNDEV                       -- tunnel device (always present)
+#* INTERNAL_IP4_ADDRESS         -- address (always present)
+#* INTERNAL_IP4_NETMASK         -- netmask (often unset)
+#* INTERNAL_IP4_DNS             -- list of dns serverss
+#* INTERNAL_IP4_NBNS            -- list of wins servers
+#* CISCO_DEF_DOMAIN             -- default domain name
+#* CISCO_BANNER                 -- banner from server
+#* CISCO_SPLIT_INC              -- number of networks in split-network-list
+#* CISCO_SPLIT_INC_%d_ADDR      -- network address
+#* CISCO_SPLIT_INC_%d_MASK      -- subnet mask (for example: 255.255.255.0)
+#* CISCO_SPLIT_INC_%d_MASKLEN   -- subnet masklen (for example: 24)
+#* CISCO_SPLIT_INC_%d_PROTOCOL  -- protocol (often just 0)
+#* CISCO_SPLIT_INC_%d_SPORT     -- source port (often just 0)
+#* CISCO_SPLIT_INC_%d_DPORT     -- destination port (often just 0)
+
+# FIXMEs:
+
+# Section A: route handling
+
+# 1) The 3 values CISCO_SPLIT_INC_%d_PROTOCOL/SPORT/DPORT are currently being ignored
+#   In order to use them, we'll probably need os specific solutions
+#   * Linux: iptables -t mangle -I PREROUTING <conditions> -j ROUTE --oif $TUNDEV
+#       This would be an *alternative* to changing the routes (and thus 2) and 3)
+#       shouldn't be relevant at all)
+# 2) There are two different functions to set routes: generic routes and the
+#   default route. Why isn't the defaultroute handled via the generic route case?
+# 3) In the split tunnel case, all routes but the default route might get replaced
+#   without getting restored later. We should explicitely check and save them just
+#   like the defaultroute
+# 4) Replies to a dhcp-server should never be sent into the tunnel
+
+# Section B: Split DNS handling
+
+# 1) Maybe dnsmasq can do something like that
+# 2) Parse dns packets going out via tunnel and redirect them to original dns-server
+
+#set -x
+
+# =========== script (variable) setup ====================================
+
+PATH=/sbin:/usr/sbin:$PATH
+
+OS="`uname -s`"
+
+DEFAULT_ROUTE_FILE=/var/run/vpnc/defaultroute
+RESOLV_CONF_BACKUP=/var/run/vpnc/resolv.conf-backup
+FULL_SCRIPTNAME=@PREFIX@/sbin/vpnc
+SCRIPTNAME=`basename $FULL_SCRIPTNAME`
+
+# some systems, eg. Darwin & FreeBSD, prune /var/run on boot
+if [ ! -d "/var/run/vpnc" ]; then
+	mkdir -p /var/run/vpnc
+fi
+
+# stupid SunOS: no blubber in /usr/local/bin ... (on stdout)
+IPROUTE="`which ip | grep '^/' 2> /dev/null`"
+
+if [ "$OS" = "Linux" ]; then
+	ifconfig_syntax_ptp="pointopoint"
+	route_syntax_gw="gw"
+	route_syntax_del="del"
+	route_syntax_netmask="netmask"
+else
+	ifconfig_syntax_ptp=""
+	route_syntax_gw=""
+	route_syntax_del="delete"
+	route_syntax_netmask="-netmask"
+fi
+
+if [ -x /sbin/resolvconf ]; then # Optional tool on Debian, Ubuntu, Gentoo
+	MODIFYRESOLVCONF=modify_resolvconf_manager
+	RESTORERESOLVCONF=restore_resolvconf_manager
+elif [ -x /sbin/modify_resolvconf ]; then # Mandatory tool on Suse
+	MODIFYRESOLVCONF=modify_resolvconf_suse
+	RESTORERESOLVCONF=restore_resolvconf_suse
+else # Generic for any OS
+	MODIFYRESOLVCONF=modify_resolvconf_generic
+	RESTORERESOLVCONF=restore_resolvconf_generic
+fi
+
+# =========== tunnel interface handling ====================================
+
+do_ifconfig() {
+	ifconfig "$TUNDEV" inet "$INTERNAL_IP4_ADDRESS" $ifconfig_syntax_ptp "$INTERNAL_IP4_ADDRESS" netmask ${INTERNAL_IP4_NETMASK:-255.255.255.255} mtu 1412 up
+}
+
+destroy_tun_device() {
+	case "$OS" in
+	NetBSD) # and probably others...
+		ifconfig "$TUNDEV" destroy
+		;;
+	esac
+}
+
+# =========== route handling ====================================
+
+if [ -n "$IPROUTE" ]; then
+	fix_ip_get_output () {
+		sed 's/cache//;s/metric \?[0-9]\+ [0-9]\+//g;s/hoplimit [0-9]\+//g'
+	}
+
+	set_vpngateway_route() {
+		$IPROUTE route add `$IPROUTE route get "$VPNGATEWAY" | fix_ip_get_output`
+		$IPROUTE route flush cache
+	}
+	
+	del_vpngateway_route() {
+		$IPROUTE route $route_syntax_del "$VPNGATEWAY"
+		$IPROUTE route flush cache
+	}
+	
+	set_default_route() {
+		$IPROUTE route | grep '^default' | fix_ip_get_output > "$DEFAULT_ROUTE_FILE"
+		$IPROUTE route replace default dev "$TUNDEV"
+		$IPROUTE route flush cache
+	}
+	
+	set_network_route() {
+		NETWORK="$1"
+		NETMASK="$2"
+		NETMASKLEN="$3"
+		$IPROUTE route replace "$NETWORK/$NETMASKLEN" dev "$TUNDEV"
+		$IPROUTE route flush cache
+	}
+	
+	reset_default_route() {
+		if [ -s "$DEFAULT_ROUTE_FILE" ]; then
+			$IPROUTE route replace `cat "$DEFAULT_ROUTE_FILE"`
+			$IPROUTE route flush cache
+			rm -f -- "$DEFAULT_ROUTE_FILE"
+		fi
+	}
+	
+	del_network_route() {
+		NETWORK="$1"
+		NETMASK="$2"
+		NETMASKLEN="$3"
+		$IPROUTE route $route_syntax_del "$NETWORK/$NETMASKLEN" dev "$TUNDEV" 
+		$IPROUTE route flush cache
+	}
+else # use route command
+	get_default_gw() {
+		# isn't -n supposed to give --numeric output?
+		# apperently not...
+		# Get rid of lines containing IPv6 addresses (':')
+		netstat -r -n | awk '/:/ { next; } /^(default|0\.0\.0\.0)/ { print $2; }'
+	}
+	
+	set_vpngateway_route() {
+		route add -host "$VPNGATEWAY" $route_syntax_gw "`get_default_gw`"
+	}
+
+	del_vpngateway_route() {
+		route $route_syntax_del -host "$VPNGATEWAY"
+	}
+	
+	set_default_route() {
+		DEFAULTGW="`get_default_gw`"
+		echo "$DEFAULTGW" > "$DEFAULT_ROUTE_FILE"
+		route $route_syntax_del default
+		route add default $route_syntax_gw "$INTERNAL_IP4_ADDRESS"
+	}
+	
+	set_network_route() {
+		NETWORK="$1"
+		NETMASK="$2"
+		NETMASKLEN="$3"
+		del_network_route "$NETWORK" "$NETMASK" "$NETMASKLEN"
+		route add -net "$NETWORK" $route_syntax_netmask "$NETMASK" $route_syntax_gw "$INTERNAL_IP4_ADDRESS"
+	}
+	
+	reset_default_route() {
+		if [ -s "$DEFAULT_ROUTE_FILE" ]; then
+			route $route_syntax_del default
+			route add default $route_syntax_gw `cat "$DEFAULT_ROUTE_FILE"`
+			rm -f -- "$DEFAULT_ROUTE_FILE"
+		fi
+	}
+	
+	del_network_route() {
+		case "$OS" in
+		Linux|NetBSD) # and probably others...
+			# routes are deleted automatically on device shutdown
+			return
+			;;
+		esac
+		NETWORK="$1"
+		NETMASK="$2"
+		NETMASKLEN="$3"
+		route $route_syntax_del -net "$NETWORK" $route_syntax_netmask "$NETMASK" $route_syntax_gw "$INTERNAL_IP4_ADDRESS"
+	}
+fi
+
+# =========== resolv.conf handling ====================================
+
+# =========== resolv.conf handling for any OS =========================
+
+modify_resolvconf_generic() {
+	grep '^#@VPNC_GENERATED@' /etc/resolv.conf > /dev/null 2>&1 || cp -- /etc/resolv.conf "$RESOLV_CONF_BACKUP"
+	NEW_RESOLVCONF="#@VPNC_GENERATED@ -- this file is generated by vpnc
+# and will be overwritten by vpnc
+# as long as the above mark is intact"
+
+	# Remember the original value of CISCO_DEF_DOMAIN we need it later
+	CISCO_DEF_DOMAIN_ORIG="$CISCO_DEF_DOMAIN"
+	# Don't step on INTERNAL_IP4_DNS value, use a temporary variable
+	INTERNAL_IP4_DNS_TEMP="$INTERNAL_IP4_DNS"
+	exec 6< "$RESOLV_CONF_BACKUP"
+	while read LINE <&6 ; do
+		case "$LINE" in
+			nameserver*)
+				if [ -n "$INTERNAL_IP4_DNS_TEMP" ]; then
+					read ONE_NAMESERVER INTERNAL_IP4_DNS_TEMP <<-EOF
+	$INTERNAL_IP4_DNS_TEMP
+EOF
+					LINE="nameserver $ONE_NAMESERVER"
+				else
+					LINE=""
+				fi
+				;;
+			search*)
+				if [ -n "$CISCO_DEF_DOMAIN" ]; then
+					LINE="$LINE $CISCO_DEF_DOMAIN"
+					CISCO_DEF_DOMAIN=""
+				fi
+				;;
+			domain*)
+				if [ -n "$CISCO_DEF_DOMAIN" ]; then
+					LINE="domain $CISCO_DEF_DOMAIN"
+					CISCO_DEF_DOMAIN=""
+				fi
+				;;
+		esac
+		NEW_RESOLVCONF="$NEW_RESOLVCONF
+$LINE"
+	done
+	exec 6<&-
+	
+	for i in $INTERNAL_IP4_DNS_TEMP ; do
+		NEW_RESOLVCONF="$NEW_RESOLVCONF
+nameserver $i"
+	done
+	if [ -n "$CISCO_DEF_DOMAIN" ]; then
+		NEW_RESOLVCONF="$NEW_RESOLVCONF
+search $CISCO_DEF_DOMAIN"
+	fi
+	echo "$NEW_RESOLVCONF" > /etc/resolv.conf
+
+	if [ "$OS" = "Darwin" ]; then
+		case "`uname -r`" in
+			# Skip for pre-10.4 systems
+			4.*|5.*|6.*|7.*)
+				;;
+			# 10.4 and later require use of scutil for DNS to work properly
+			*)
+				OVERRIDE_PRIMARY=""
+				if [ -n "$CISCO_SPLIT_INC" ]; then
+					if [ $CISCO_SPLIT_INC -lt 1 ]; then
+						# Must override for correct default route
+						# Cannot use multiple DNS matching in this case
+						OVERRIDE_PRIMARY='d.add OverridePrimary # 1'
+					fi
+				fi
+				# Uncomment the following if/fi pair to use multiple
+				# DNS matching when available.  When multiple DNS matching
+				# is present, anything reading the /etc/resolv.conf file
+				# directly will probably not work as intended.
+				#if [ -z "$CISCO_DEF_DOMAIN_ORIG" ]; then
+					# Cannot use multiple DNS matching without a domain
+					OVERRIDE_PRIMARY='d.add OverridePrimary # 1'
+				#fi
+				scutil >/dev/null 2>&1 <<-EOF
+					open
+					d.init
+					d.add ServerAddresses * $INTERNAL_IP4_DNS
+					set State:/Network/Service/$TUNDEV/DNS
+					d.init
+					d.add Router $INTERNAL_IP4_ADDRESS
+					d.add Addresses * $INTERNAL_IP4_ADDRESS
+					d.add SubnetMasks * 255.255.255.255
+					d.add InterfaceName $TUNDEV
+					$OVERRIDE_PRIMARY
+					set State:/Network/Service/$TUNDEV/IPv4
+					close
+				EOF
+				if [ -n "$CISCO_DEF_DOMAIN_ORIG" ]; then
+					scutil >/dev/null 2>&1 <<-EOF
+						open
+						get State:/Network/Service/$TUNDEV/DNS
+						d.add DomainName $CISCO_DEF_DOMAIN_ORIG
+						d.add SearchDomains * $CISCO_DEF_DOMAIN_ORIG
+						d.add SupplementalMatchDomains * $CISCO_DEF_DOMAIN_ORIG
+						set State:/Network/Service/$TUNDEV/DNS
+						close
+					EOF
+				fi
+				;;
+		esac
+	fi
+}
+
+restore_resolvconf_generic() {
+	if [ ! -e "$RESOLV_CONF_BACKUP" ]; then
+		return
+	fi
+	grep '^#@VPNC_GENERATED@' /etc/resolv.conf > /dev/null 2>&1 && cat "$RESOLV_CONF_BACKUP" > /etc/resolv.conf
+	rm -f -- "$RESOLV_CONF_BACKUP"
+
+	if [ "$OS" = "Darwin" ]; then
+		case "`uname -r`" in
+			# Skip for pre-10.4 systems
+			4.*|5.*|6.*|7.*)
+				;;
+			# 10.4 and later require use of scutil for DNS to work properly
+			*)
+				scutil >/dev/null 2>&1 <<-EOF
+					open
+					remove State:/Network/Service/$TUNDEV/IPv4
+					remove State:/Network/Service/$TUNDEV/DNS
+					close
+				EOF
+				;;
+		esac
+	fi
+}
+# === resolv.conf handling via /sbin/modify_resolvconf (Suse) =====================
+
+# Suse provides a script that modifies resolv.conf. Use it because it will
+# restart/reload all other services that care about it (e.g. lwresd).
+
+modify_resolvconf_suse()
+{
+	RESOLV_OPTS=''
+	test -n "$INTERNAL_IP4_DNS" && RESOLV_OPTS="-n \"$INTERNAL_IP4_DNS\""
+	test -n "$CISCO_DEF_DOMAIN" && RESOLV_OPTS="$RESOLV_OPTS -d $CISCO_DEF_DOMAIN"
+	test -n "$RESOLV_OPTS" && eval /sbin/modify_resolvconf modify -s $SCRIPTNAME -p $SCRIPTNAME -f $FULL_SCRIPTNAME -e $TUNDEV $RESOLV_OPTS -t \"This file was created by $SCRIPTNAME\"
+}
+
+# Restore resolv.conf to old contents on Suse
+restore_resolvconf_suse()
+{
+	/sbin/modify_resolvconf restore -s vpnc -p $SCRIPTNAME -f $FULL_SCRIPTNAME -e $TUNDEV
+}
+
+# === resolv.conf handling via /sbin/resolvconf (Debian, Ubuntu, Gentoo)) =========
+
+modify_resolvconf_manager() {
+	NEW_RESOLVCONF=""
+	for i in $INTERNAL_IP4_DNS; do
+		NEW_RESOLVCONF="$NEW_RESOLVCONF
+nameserver $i"
+	done
+	if [ -n "$CISCO_DEF_DOMAIN" ]; then
+		NEW_RESOLVCONF="$NEW_RESOLVCONF
+domain $CISCO_DEF_DOMAIN"
+	fi
+	echo "$NEW_RESOLVCONF" | /sbin/resolvconf -a $TUNDEV
+}
+
+restore_resolvconf_manager() {
+	/sbin/resolvconf -d $TUNDEV
+}
+
+# ========= Toplevel state handling  =======================================
+
+kernel_is_2_6_or_above() {
+	case `uname -r` in
+		1.*|2.[012345]*)
+			return 1
+			;;
+		*)
+			return 0
+			;;
+	esac
+}
+
+do_pre_init() {
+	if [ "$OS" = "Linux" ]; then
+		if (exec 6<> /dev/net/tun) > /dev/null 2>&1 ; then
+			:
+		else # can't open /dev/net/tun
+			test -e /proc/sys/kernel/modprobe && `cat /proc/sys/kernel/modprobe` tun 2>/dev/null
+			# fix for broken devfs in kernel 2.6.x
+			if [ "`readlink /dev/net/tun`" = misc/net/tun \
+				-a ! -e /dev/net/misc/net/tun -a -e /dev/misc/net/tun ] ; then
+				ln -sf /dev/misc/net/tun /dev/net/tun
+			fi
+			# make sure tun device exists
+			if [ ! -e /dev/net/tun ]; then
+				mkdir -p /dev/net
+				mknod -m 0640 /dev/net/tun c 10 200
+			fi
+			# workaround for a possible latency caused by udev, sleep max. 10s
+			if kernel_is_2_6_or_above ; then
+				for x in `seq 100` ; do
+					(exec 6<> /dev/net/tun) > /dev/null 2>&1 && break;
+					sleep 0.1
+				done
+			fi
+		fi
+	elif [ "$OS" = "FreeBSD" ]; then
+		if [ ! -e /dev/tun ]; then
+			kldload if_tun
+		fi
+	elif [ "$OS" = "GNU/kFreeBSD" ]; then
+		if [ ! -e /dev/tun ]; then
+			kldload if_tun
+		fi
+	elif [ "$OS" = "NetBSD" ]; then
+		:
+	elif [ "$OS" = "OpenBSD" ]; then
+		:
+	elif [ "$OS" = "SunOS" ]; then
+		:
+	elif [ "$OS" = "Darwin" ]; then
+		:
+	fi
+}
+
+do_connect() {
+	if [ -n "$CISCO_BANNER" ]; then
+		echo "Connect Banner:"
+		echo "$CISCO_BANNER" | while read LINE ; do echo "|" "$LINE" ; done
+		echo
+	fi
+	
+	do_ifconfig
+	set_vpngateway_route
+	if [ -n "$CISCO_SPLIT_INC" ]; then
+		i=0
+		while [ $i -lt $CISCO_SPLIT_INC ] ; do
+			eval NETWORK="\${CISCO_SPLIT_INC_${i}_ADDR}"
+			eval NETMASK="\${CISCO_SPLIT_INC_${i}_MASK}"
+			eval NETMASKLEN="\${CISCO_SPLIT_INC_${i}_MASKLEN}"
+			if [ $NETWORK != "0.0.0.0" ]; then
+				set_network_route "$NETWORK" "$NETMASK" "$NETMASKLEN"
+			else
+				set_default_route
+			fi
+			i=`expr $i + 1`
+		done
+		for i in $INTERNAL_IP4_DNS ; do
+			set_network_route "$i" "255.255.255.255" "32"
+		done
+	else
+		set_default_route
+	fi
+	
+	if [ -n "$INTERNAL_IP4_DNS" ]; then
+		$MODIFYRESOLVCONF
+	fi
+}
+
+do_disconnect() {
+	destroy_tun_device
+	if [ -n "$CISCO_SPLIT_INC" ]; then
+		i=0
+		while [ $i -lt $CISCO_SPLIT_INC ] ; do
+			eval NETWORK="\${CISCO_SPLIT_INC_${i}_ADDR}"
+			eval NETMASK="\${CISCO_SPLIT_INC_${i}_MASK}"
+			eval NETMASKLEN="\${CISCO_SPLIT_INC_${i}_MASKLEN}"
+			if [ $NETWORK != "0.0.0.0" ]; then
+				# FIXME: This doesn't restore previously overwritten
+				#        routes.
+				del_network_route "$NETWORK" "$NETMASK" "$NETMASKLEN"
+			else
+				reset_default_route
+			fi
+			i=`expr $i + 1`
+		done
+		for i in $INTERNAL_IP4_DNS ; do
+			del_network_route "$i" "255.255.255.255" "32"
+		done
+	else
+		reset_default_route
+	fi
+	
+	del_vpngateway_route
+	
+	if [ -n "$INTERNAL_IP4_DNS" ]; then
+		$RESTORERESOLVCONF
+	fi
+}
+
+#### Main
+
+if [ -z "$reason" ]; then
+	echo "this script must be called from vpnc" 1>&2
+	exit 1
+fi
+
+case "$reason" in
+	pre-init)
+		do_pre_init
+		;;
+	connect)
+		do_connect
+		;;
+	disconnect)
+		do_disconnect
+		;;
+	*)
+		echo "unknown reason '$reason'. Maybe vpnc-script is out of date" 1>&2
+		exit 1
+		;;
+esac
+
+exit 0

Added: branches/vpnc-nortel/vpnc.8.template
==============================================================================
--- branches/vpnc-nortel/vpnc.8.template (added)
+++ branches/vpnc-nortel/vpnc.8.template Wed Jun 11 01:19:11 2008
@@ -1,0 +1,214 @@
+.\" Template to generate the vpnc-manpage
+.\" $Id$
+.\"
+.TH VPNC "8" "Warning: Just a template!" "vpnc man-template" "Warning: Just a template!"
+.\" Fake header just to make this file viewable with man.
+.\" ###makeman.pl: Replace header here!
+.SH NAME
+vpnc \- client for Cisco VPN3000 Concentrator, IOS and PIX
+.SH SYNOPSIS
+.B vpnc
+[\fI--version\fR] [\fI--print-config\fR] [\fI--help\fR] [\fI--long-help\fR] [\fIoptions\fR] [\fIconfig files\fR]
+.SH "DESCRIPTION"
+.PP
+This manual page documents briefly the
+\fBvpnc\fR and
+\fBvpnc\-disconnect\fR commands.
+.PP
+\fBvpnc\fR is a 
+VPN client for the Cisco 3000 VPN  Concentrator,  creating  a IPSec-like
+connection as a tunneling network device for the local system. It uses
+the TUN/TAP driver in  Linux  kernel  2.4  and  above  and device tun(4)
+on BSD. The created connection is presented as a tunneling network
+device to the local system.
+.PP
+OBLIGATORY WARNING: the most used configuration (XAUTH authentication
+with pre-shared keys and password authentication) is insecure by design,
+be aware of this fact when you use vpnc to exchange sensitive data like
+passwords!
+.PP
+The vpnc daemon by itself does not set any routes, but it calls
+\fBvpnc\-script\fR to do this job. \fBvpnc\-script\fR displays
+a connect banner. If the concentrator supplies a network list
+for split-tunneling these networks are added to the routing table.
+Otherwise the default-route will be modified to point to the tunnel.
+Further a host route to the concentrator is added in the later case.
+If the client host needs DHCP, care must be taken to add another
+host route to the DHCP-Server around the tunnel.
+.PP
+The \fBvpnc\-disconnect\fR command is used to terminate
+the connection previously created by \fBvpnc\fR
+and restore the previous routing configuration.
+
+.SH CONFIGURATION
+The daemon reads configuration data from the following places:
+.PD 0
+.IP \(bu
+command line options
+.IP \(bu
+config file(s) specified on the command line
+.IP \(bu
+/etc/vpnc/default.conf
+.IP \(bu
+/etc/vpnc.conf
+.IP \(bu
+prompting the user if not found above
+
+.PP
+
+vpnc can parse options and
+.B configuration files
+in any order. However the first
+place to set an option wins.
+configuration filenames
+which do not contain a /
+will be searched at
+.B /etc/vpnc/<filename>
+and
+.B /etc/vpnc/<filename>.conf.
+Otherwise
+.B <filename>
+and
+.B <filename>.conf
+will be used.
+If no configuration file
+is specified on the command-line
+at all, both
+.B /etc/vpnc/default.conf
+and
+.B /etc/vpnc.conf
+will be loaded.
+
+.SH OPTIONS
+The program options can be either given as arguments (but not all of them
+for security reasons) or be stored in a configuration file.
+.PD 0
+.\" ###makeman.pl: Insert options from help-output here!
+
+.HP 
+\fB\-\-print\-config\fR
+.IP
+Prints your configuration; output can be used as vpnc.conf
+
+.SH FILES
+.I /etc/vpnc.conf
+.I /etc/vpnc/default.conf
+.RS
+The default configuration file. You can specify the same config
+directives as with command line options and additionaly
+.B IPSec secret
+and
+.B Xauth password
+both supplying a cleartext password. Scrambled passwords from the Cisco
+configuration profiles can be used with
+.B IPSec obfuscated secret
+and
+.B Xauth obfuscated password.
+
+See
+.BR EXAMPLES
+for further details.
+.RE
+
+.I /etc/vpnc/*.conf
+.RS
+vpnc will read configuration files in this directory when
+the config filename (with or without .conf) is specified on the command line.
+.RE
+
+
+.SH EXAMPLES
+This is an example vpnc.conf with pre-shared keys:
+
+.RS
+.PD 0
+IPSec gateway vpn.example.com
+.P
+IPSec ID ExampleVpnPSK
+.P
+IKE Authmode psk
+.P
+IPSec secret PskS3cret!
+.P
+Xauth username user at example.com
+.P
+Xauth password USecr3t
+.PD
+.RE
+
+And another one with hybrid authentication (requires that vpnc was
+built with openssl support):
+
+.RS
+.PD 0
+IPSec gateway vpn.example.com
+.P
+IPSec ID ExampleVpnHybrid
+.P
+IKE Authmode hybrid
+.P
+
+.P
+CA-Dir /etc/vpnc
+.P
+\fBor\fR
+.P
+CA-File /etc/vpnc/vpn-example-com.pem
+.P
+
+.P
+IPSec secret HybS3cret?
+.P
+Xauth username user at example.com
+.P
+Xauth password 123456
+.PD
+.RE
+
+The lines begin with a keyword (no leading spaces!).
+The values start exactly one space after the keywords, and run to the end of
+line. This lets you put any kind of weird character (except CR, LF and NUL) in
+your strings, but it does mean you can't add comments after a string, or spaces
+before them.
+
+In case the the \fBCA-Dir\fR option is used, your certificate needs to be
+named something like 722d15bd.X, where X is a manually assigned number to
+make sure that files with colliding hashes have different names. The number
+can be derived from the certificate file itself:
+.P
+openssl x509 -subject_hash -noout -in /etc/vpnc/vpn-example-com.pem
+
+See also the
+.B \-\-print\-config
+option to generate a config file, and the example file in the package
+documentation directory where more advanced usage is demonstrated.
+
+Advanced features like manual setting of multiple target routes and
+disabling /etc/resolv.conf rewriting is documented in the README of the
+vpnc package.
+
+.SH TODO
+.PD 0
+Certificate support (Pre-Shared-Key + XAUTH is known to be insecure).
+.P
+Further points can be found in the TODO file.
+.PD
+
+.SH AUTHOR
+This man-page has been written by Eduard Bloch <blade(at)debian.org> and
+Christian Lackas <delta(at)lackas.net>, based on vpnc README by
+Maurice Massar <vpnc(at)unix\-ag.uni\-kl.de>.
+Permission is
+granted to copy, distribute and/or modify this document under
+the terms of the GNU General Public License, Version 2 any 
+later version published by the Free Software Foundation.
+.PP
+On Debian systems, the complete text of the GNU General Public
+License can be found in /usr/share/common\-licenses/GPL.
+.SH "SEE ALSO"
+.BR pcf2vpnc (1),
+.BR cisco-decrypt (1),
+.BR ip (8),
+.BR ifconfig (8),
+.BR route (1),
+.BR http://www.unix\-ag.uni\-kl.de/~massar/vpnc/

Modified: branches/vpnc-nortel/vpnc.c
==============================================================================
--- branches/vpnc-nortel/vpnc.c (original)
+++ branches/vpnc-nortel/vpnc.c Wed Jun 11 01:19:11 2008
@@ -1,6 +1,6 @@
 /* IPSec VPN client compatible with Cisco equipment.
    Copyright (C) 2002      Geoffrey Keating
-   Copyright (C) 2003-2004 Maurice Massar
+   Copyright (C) 2003-2005 Maurice Massar
    Copyright (C) 2004      Tomas Mraz
    Copyright (C) 2004      Martin von Gagern
 
@@ -17,6 +17,8 @@
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   $Id$
 */
 
 #define _GNU_SOURCE
@@ -33,11 +35,16 @@
 #include <netdb.h>
 #include <arpa/inet.h>
 #include <poll.h>
-#include <net/if.h>
 #include <sys/ioctl.h>
 #include <sys/utsname.h>
 
 #include <gcrypt.h>
+
+#ifdef OPENSSL_GPL_VIOLATION
+/* OpenSSL */
+#include <openssl/x509.h>
+#include <openssl/err.h>
+#endif /* OPENSSL_GPL_VIOLATION */
 
 #include "sysdep.h"
 #include "config.h"
@@ -45,146 +52,115 @@
 #include "math_group.h"
 #include "dh.h"
 #include "vpnc.h"
+#include "tunip.h"
+#include "supp.h"
+
+#if defined(__CYGWIN__)
+	GCRY_THREAD_OPTION_PTHREAD_IMPL;
+#endif
 
 #define ISAKMP_PORT (500)
-
-struct sa_block oursa[1];
-
-static int sockfd = -1;
-static struct sockaddr *dest_addr;
-static uint16_t local_port; /* in network byte order */
+#define ISAKMP_PORT_NATT (4500)
+
+const unsigned char VID_XAUTH[] = { /* "draft-ietf-ipsra-isakmp-xauth-06.txt"/8 */
+	0x09, 0x00, 0x26, 0x89, 0xDF, 0xD6, 0xB7, 0x12
+};
+const unsigned char VID_DPD[] = { /* Dead Peer Detection, RFC 3706 */
+	0xAF, 0xCA, 0xD7, 0x13, 0x68, 0xA1, 0xF1, 0xC9,
+	0x6B, 0x86, 0x96, 0xFC, 0x77, 0x57, 0x01, 0x00
+};
+const unsigned char VID_UNITY[] = { /* "CISCO-UNITY"/14 + major + minor */
+	0x12, 0xF5, 0xF2, 0x8C, 0x45, 0x71, 0x68, 0xA9,
+	0x70, 0x2D, 0x9F, 0xE2, 0x74, 0xCC, 0x01, 0x00
+};
+const unsigned char VID_UNKNOWN[] = {
+	0x12, 0x6E, 0x1F, 0x57, 0x72, 0x91, 0x15, 0x3B,
+	0x20, 0x48, 0x5F, 0x7F, 0x15, 0x5B, 0x4B, 0xC8
+};
+const unsigned char VID_NATT_00[] = { /* "draft-ietf-ipsec-nat-t-ike-00" */
+	0x44, 0x85, 0x15, 0x2d, 0x18, 0xb6, 0xbb, 0xcd,
+	0x0b, 0xe8, 0xa8, 0x46, 0x95, 0x79, 0xdd, 0xcc
+};
+const unsigned char VID_NATT_01[] = { /* "draft-ietf-ipsec-nat-t-ike-01" */
+	0x16, 0xf6, 0xca, 0x16, 0xe4, 0xa4, 0x06, 0x6d,
+	0x83, 0x82, 0x1a, 0x0f, 0x0a, 0xea, 0xa8, 0x62
+};
+const unsigned char VID_NATT_02[] = { /* "draft-ietf-ipsec-nat-t-ike-02" */
+	0xcd, 0x60, 0x46, 0x43, 0x35, 0xdf, 0x21, 0xf8,
+	0x7c, 0xfd, 0xb2, 0xfc, 0x68, 0xb6, 0xa4, 0x48
+};
+const unsigned char VID_NATT_02N[] = { /* "draft-ietf-ipsec-nat-t-ike-02\n" */
+	0x90, 0xCB, 0x80, 0x91, 0x3E, 0xBB, 0x69, 0x6E,
+	0x08, 0x63, 0x81, 0xB5, 0xEC, 0x42, 0x7B, 0x1F
+};
+
+const unsigned char VID_NATT_RFC[] = { /* "RFC 3947" */
+	0x4A, 0x13, 0x1C, 0x81, 0x07, 0x03, 0x58, 0x45,
+	0x5C, 0x57, 0x28, 0xF2, 0x0E, 0x95, 0x45, 0x2F
+};
+
+const unsigned char VID_CISCO_FRAG[] = { /* "FRAGMENTATION" */
+	0x40, 0x48, 0xB7, 0xD5, 0x6E, 0xBC, 0xE8, 0x85,
+	0x25, 0xE7, 0xDE, 0x7F, 0x00, 0xD6, 0xC2, 0xD3,
+	0x80, 0x00, 0x00, 0x00
+};
+
+const unsigned char VID_NETSCREEN_15[] = { /* netscreen 15 */
+	0x16, 0x6f, 0x93, 0x2d, 0x55, 0xeb, 0x64, 0xd8,
+	0xe4, 0xdf, 0x4f, 0xd3, 0x7e, 0x23, 0x13, 0xf0,
+	0xd0, 0xfd, 0x84, 0x51, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00
+};
+
+struct vid_element {
+	const unsigned char* valueptr;
+	const uint16_t length;
+	const char* descr;
+};
+
+const struct vid_element vid_list[] = {
+	{ VID_XAUTH,		sizeof(VID_XAUTH),	"Xauth" },	
+	{ VID_DPD,		sizeof(VID_DPD),	"DPD" },	
+	{ VID_UNITY,		sizeof(VID_UNITY),	"Cisco Unity" },	
+	{ VID_NATT_00,		sizeof(VID_NATT_00),	"Nat-T 00" },	
+	{ VID_NATT_01,		sizeof(VID_NATT_01),	"Nat-T 01" },	
+	{ VID_NATT_02,		sizeof(VID_NATT_02),	"Nat-T 02" },	
+	{ VID_NATT_02N,		sizeof(VID_NATT_02N),	"Nat-T 02N" },	
+	{ VID_NATT_RFC,		sizeof(VID_NATT_RFC),	"Nat-T RFC" },	
+	{ VID_CISCO_FRAG,	sizeof(VID_CISCO_FRAG),	"Cisco Fragmentation" },
+	{ VID_NETSCREEN_15,	sizeof(VID_NETSCREEN_15),	"Netscreen 15" },
+
+	{ NULL, 0, NULL }
+};
+
+#ifdef NORTELVPN
 static int ipsec_cry_algo, ipsec_hash_algo, dh_group;
 static uint16_t encap_mode = IPSEC_ENCAP_TUNNEL;
-static int timeout = 5000; /* 5 seconds */
-static uint8_t *resend_hash = NULL;
 uint8_t *tous_keys, *tothem_keys;
-
-static uint8_t r_packet[2048];
+static void phase2_fatal(struct sa_block *s, const char *msg, int id);
+#endif
+
+/* What are DWR-Code and DWR-Text ? */
+
+static uint8_t r_packet[8192];
 static ssize_t r_length;
 
-extern void vpnc_doit(unsigned long tous_spi,
-	const unsigned char *tous_key,
-	struct sockaddr_in *tous_dest,
-	unsigned long tothem_spi,
-	const unsigned char *tothem_key,
-	struct sockaddr_in *tothem_dest,
-	int tun_fd, int md_algo, int cry_algo,
-	uint8_t * kill_packet_p, size_t kill_packet_size_p,
-	struct sockaddr *kill_dest_p,
-	uint16_t encap_mode, int isakmp_fd,
-	const char *pidfile);
-
-extern int find_local_addr(struct sockaddr_in *dest,
-	struct sockaddr_in *source);
-
-enum supp_algo_key {
-	SUPP_ALGO_NAME,
-	SUPP_ALGO_MY_ID,
-	SUPP_ALGO_IKE_SA,
-	SUPP_ALGO_IPSEC_SA
-};
-
-enum algo_group {
-	SUPP_ALGO_DH_GROUP,
-	SUPP_ALGO_HASH,
-	SUPP_ALGO_CRYPT,
-	SUPP_ALGO_AUTH
-};
-
-supported_algo_t supp_dh_group[] = {
-	{"nopfs", 0, 0, 0, 0},
-	{"dh1", OAKLEY_GRP_1, IKE_GROUP_MODP_768, IKE_GROUP_MODP_768, 0},
-	{"dh2", OAKLEY_GRP_2, IKE_GROUP_MODP_1024, IKE_GROUP_MODP_1024, 0},
-	{"dh5", OAKLEY_GRP_5, IKE_GROUP_MODP_1536, IKE_GROUP_MODP_1536, 0},
-	/*{ "dh7", OAKLEY_GRP_7, IKE_GROUP_EC2N_163K, IKE_GROUP_EC2N_163K, 0 } note: code missing */
-	{NULL, 0, 0, 0, 0}
-};
-
-supported_algo_t supp_hash[] = {
-	{"md5", GCRY_MD_MD5, IKE_HASH_MD5, IPSEC_AUTH_HMAC_MD5, 0},
-	{"sha1", GCRY_MD_SHA1, IKE_HASH_SHA, IPSEC_AUTH_HMAC_SHA, 0},
-	{NULL, 0, 0, 0, 0}
-};
-
-supported_algo_t supp_crypt[] = {
-	/*note: working, but not recommended */
-	{"des", GCRY_CIPHER_DES, IKE_ENC_DES_CBC, ISAKMP_IPSEC_ESP_DES, 0},
-	{"3des", GCRY_CIPHER_3DES, IKE_ENC_3DES_CBC, ISAKMP_IPSEC_ESP_3DES, 0},
-	{"aes128", GCRY_CIPHER_AES128, IKE_ENC_AES_CBC, ISAKMP_IPSEC_ESP_AES, 128},
-	{"aes192", GCRY_CIPHER_AES192, IKE_ENC_AES_CBC, ISAKMP_IPSEC_ESP_AES, 192},
-	{"aes256", GCRY_CIPHER_AES256, IKE_ENC_AES_CBC, ISAKMP_IPSEC_ESP_AES, 256},
-	{NULL, 0, 0, 0, 0}
-};
-
-supported_algo_t supp_auth[] = {
-	{"psk", 0, IKE_AUTH_PRESHARED, 0, 0},
-	{"psk+xauth", 0, IKE_AUTH_XAUTHInitPreShared, 0, 0},
-	{NULL, 0, 0, 0, 0}
-};
-
-const supported_algo_t *get_algo(enum algo_group what, enum supp_algo_key key, int id,
-	const char *name, int keylen)
-{
-	supported_algo_t *sa = NULL;
-	int i = 0, val = 0;
-	const char *valname = NULL;
-
-	switch (what) {
-	case SUPP_ALGO_DH_GROUP:
-		sa = supp_dh_group;
-		break;
-	case SUPP_ALGO_HASH:
-		sa = supp_hash;
-		break;
-	case SUPP_ALGO_CRYPT:
-		sa = supp_crypt;
-		break;
-	case SUPP_ALGO_AUTH:
-		sa = supp_auth;
-		break;
-	default:
-		abort();
-	}
-
-	for (i = 0; sa[i].name != NULL; i++) {
-		switch (key) {
-		case SUPP_ALGO_NAME:
-			valname = sa[i].name;
-			break;
-		case SUPP_ALGO_MY_ID:
-			val = sa[i].my_id;
-			break;
-		case SUPP_ALGO_IKE_SA:
-			val = sa[i].ike_sa_id;
-			break;
-		case SUPP_ALGO_IPSEC_SA:
-			val = sa[i].ipsec_sa_id;
-			break;
-		default:
-			abort();
+void print_vid(const unsigned char *vid, uint16_t len) {
+	
+	int vid_index = 0;
+
+	if (opt_debug < 3)
+		return;
+
+	while (vid_list[vid_index].length) {
+		if (len == vid_list[vid_index].length &&
+			memcmp(vid_list[vid_index].valueptr, vid, len) == 0) {
+			printf("   (%s)\n", vid_list[vid_index].descr);
+			return;
 		}
-		if ((key == SUPP_ALGO_NAME) ? !strcasecmp(name, valname) : (val == id))
-			if (keylen == sa[i].keylen)
-				return sa + i;
-	}
-
-	return NULL;
-}
-
-const supported_algo_t *get_dh_group_ike(void)
-{
-	return get_algo(SUPP_ALGO_DH_GROUP, SUPP_ALGO_NAME, 0, config[CONFIG_IKE_DH], 0);
-}
-const supported_algo_t *get_dh_group_ipsec(int server_setting)
-{
-	const char *pfs_setting = config[CONFIG_IPSEC_PFS];
-
-	if (!strcmp(config[CONFIG_IPSEC_PFS], "server")) {
-		/* treat server_setting == -1 (unknown) as 0 */
-		pfs_setting = (server_setting == 1) ? "dh2" : "nopfs";
-	}
-
-	return get_algo(SUPP_ALGO_DH_GROUP, SUPP_ALGO_NAME, 0, pfs_setting, 0);
+		vid_index++;
+	}
+	printf("   (unknown)\n");
 }
 
 static __inline__ int min(int a, int b)
@@ -215,105 +191,148 @@
 	addenv(name, inet_ntoa(*((struct in_addr *)data)));
 }
 
-static int make_socket(uint16_t port)
+static int make_socket(struct sa_block *s, uint16_t src_port, uint16_t dst_port)
 {
 	int sock;
 	struct sockaddr_in name;
-
-	/* Create the socket. */
+	socklen_t len = sizeof(name);
+
+	/* create the socket */
 	sock = socket(PF_INET, SOCK_DGRAM, 0);
 	if (sock < 0)
 		error(1, errno, "making socket");
 
-	/* Give the socket a name. */
+#ifdef FD_CLOEXEC
+	/* do not pass socket to vpnc-script, etc. */
+	fcntl(sock, F_SETFD, FD_CLOEXEC);
+#endif
+
+	/* give the socket a name */
 	name.sin_family = AF_INET;
-	name.sin_port = port;
-	name.sin_addr.s_addr = htonl(INADDR_ANY);
+	name.sin_addr = s->opt_src_ip;
+	name.sin_port = htons(src_port);
 	if (bind(sock, (struct sockaddr *)&name, sizeof(name)) < 0)
-		error(1, errno, "binding to port %d", ntohs(port));
-
+		error(1, errno, "Error binding to source port. Try '--local-port 0'\nFailed to bind to %s:%d", inet_ntoa(s->opt_src_ip), src_port);
+
+	/* connect the socket */
+	name.sin_family = AF_INET;
+	name.sin_addr = s->dst;
+	name.sin_port = htons(dst_port);
+	if (connect(sock, (struct sockaddr *)&name, sizeof(name)) < 0)
+		error(1, errno, "connecting to port %d", ntohs(dst_port));
+
+	/* who am I */
+	if (getsockname(sock, (struct sockaddr *)&name, &len) < 0)
+		error(1, errno, "reading local address from socket %d", sock);
+	s->src = name.sin_addr;
+	
 	return sock;
 }
 
-static struct sockaddr *init_sockaddr(const char *hostname, uint16_t port)
+static void init_sockaddr(struct in_addr *dst, const char *hostname)
 {
 	struct hostent *hostinfo;
-	struct sockaddr_in *result;
-
-	result = malloc(sizeof(struct sockaddr_in));
-	if (result == NULL)
-		error(1, errno, "out of memory");
-
-	result->sin_family = AF_INET;
-	result->sin_port = htons(port);
-	if (inet_aton(hostname, &result->sin_addr) == 0) {
+
+	if (inet_aton(hostname, dst) == 0) {
 		hostinfo = gethostbyname(hostname);
 		if (hostinfo == NULL)
 			error(1, 0, "unknown host `%s'\n", hostname);
-		result->sin_addr = *(struct in_addr *)hostinfo->h_addr;
-	}
-	return (struct sockaddr *)result;
+		*dst = *(struct in_addr *)hostinfo->h_addr;
+	}
+}
+
+static void init_netaddr(struct in_addr *net, const char *string)
+{
+	char *p;
+
+	if ((p = strchr(string, '/')) != NULL) {
+		char *host = xallocc(p - string + 1);
+		memcpy(host, string, p - string + 1);
+		host[p - string] = '\0';
+		init_sockaddr((struct in_addr *)net, host);
+		free(host);
+		if (strchr(p + 1, '.') != NULL)
+			init_sockaddr(net + 1, p + 1);
+		else {
+			int bits = atoi(p + 1);
+			unsigned long mask = (1 << bits) - 1;
+			memcpy((char *)(net + 1), (char *)&mask, 4);
+		}
+	} else {
+		memset((char *)net, 0, 8);
+	}
 }
 
 static void setup_tunnel(struct sa_block *s)
 {
+	setenv("reason", "pre-init", 1);
+	system(config[CONFIG_SCRIPT]);
+	
 	if (config[CONFIG_IF_NAME])
 		memcpy(s->tun_name, config[CONFIG_IF_NAME], strlen(config[CONFIG_IF_NAME]));
 
-	s->tun_fd = tun_open(s->tun_name);
+	s->tun_fd = tun_open(s->tun_name, opt_if_mode);
 	DEBUG(2, printf("using interface %s\n", s->tun_name));
+	setenv("TUNDEV", s->tun_name, 1);
 
 	if (s->tun_fd == -1)
 		error(1, errno, "can't initialise tunnel interface");
-}
-
-void config_tunnel(struct sa_block *s)
-{
-	setenv("TUNDEV", s->tun_name, 1);
-	setenv("VPNGATEWAY", inet_ntoa(((struct sockaddr_in *)dest_addr)->sin_addr), 1);
-
-	system(config[CONFIG_CONFIG_SCRIPT]);
-}
-
-static int recv_ignore_dup(void *recvbuf, size_t recvbufsize)
+#ifdef FD_CLOEXEC
+	/* do not pass socket to vpnc-script, etc. */
+	fcntl(s->tun_fd, F_SETFD, FD_CLOEXEC);
+#endif
+	
+	if (opt_if_mode == IF_MODE_TAP) {
+		if (tun_get_hwaddr(s->tun_fd, s->tun_name, s->tun_hwaddr) < 0) {
+			error(1, errno, "can't get tunnel HW address");
+		}
+		hex_dump("interface HW addr", s->tun_hwaddr, ETH_ALEN, NULL);
+	}
+}
+
+static void config_tunnel(struct sa_block *s)
+{
+	setenv("VPNGATEWAY", inet_ntoa(s->dst), 1);
+	setenv("reason", "connect", 1);
+	system(config[CONFIG_SCRIPT]);
+}
+
+static void close_tunnel()
+{
+	setenv("reason", "disconnect", 1);
+	system(config[CONFIG_SCRIPT]);
+}
+
+static int recv_ignore_dup(struct sa_block *s, void *recvbuf, size_t recvbufsize)
 {
 	uint8_t *resend_check_hash;
 	int recvsize, hash_len;
-	struct sockaddr_in recvaddr;
-	socklen_t recvaddr_size = sizeof(recvaddr);
-	char ntop_buf[32];
-
-	recvsize = recvfrom(sockfd, recvbuf, recvbufsize, 0,
-		(struct sockaddr *)&recvaddr, &recvaddr_size);
-	if (recvsize == -1)
+
+	recvsize = recv(s->ike_fd, recvbuf, recvbufsize, 0);
+	if (recvsize < 0)
 		error(1, errno, "receiving packet");
-	if (recvsize > 0) {
-		if (recvaddr_size != sizeof(recvaddr)
-			|| recvaddr.sin_family != dest_addr->sa_family
-			|| recvaddr.sin_port != ((struct sockaddr_in *)dest_addr)->sin_port
-			|| memcmp(&recvaddr.sin_addr,
-				&((struct sockaddr_in *)dest_addr)->sin_addr,
-				sizeof(struct in_addr)) != 0) {
-			error(0, 0, "got response from unknown host %s:%d",
-				inet_ntop(recvaddr.sin_family, &recvaddr.sin_addr,
-					ntop_buf, sizeof(ntop_buf)), ntohs(recvaddr.sin_port));
-			return -1;
-		}
-
-		hash_len = gcry_md_get_algo_dlen(GCRY_MD_SHA1);
-		resend_check_hash = malloc(hash_len);
-		gcry_md_hash_buffer(GCRY_MD_SHA1, resend_check_hash, recvbuf, recvsize);
-		if (resend_hash && memcmp(resend_hash, resend_check_hash, hash_len) == 0) {
-			free(resend_check_hash);
-			return -1;
-		}
-		if (!resend_hash) {
-			resend_hash = resend_check_hash;
-		} else {
-			memcpy(resend_hash, resend_check_hash, hash_len);
-			free(resend_check_hash);
-		}
-	}
+	if ((unsigned int)recvsize > recvbufsize)
+		error(1, errno, "received packet too large for buffer");
+	
+	/* skip NAT-T draft-0 keepalives */
+	if ((s->ipsec.natt_active_mode == NATT_ACTIVE_DRAFT_OLD) &&
+		(recvsize == 1) && (*((u_char *)(recvbuf)) == 0xff))
+		return -1;
+	
+	hash_len = gcry_md_get_algo_dlen(GCRY_MD_SHA1);
+	resend_check_hash = malloc(hash_len);
+	gcry_md_hash_buffer(GCRY_MD_SHA1, resend_check_hash, recvbuf, recvsize);
+	if (s->ike.resend_hash && memcmp(s->ike.resend_hash, resend_check_hash, hash_len) == 0) {
+		free(resend_check_hash);
+		return -1;
+	}
+	if (!s->ike.resend_hash) {
+		s->ike.resend_hash = resend_check_hash;
+	} else {
+		memcpy(s->ike.resend_hash, resend_check_hash, hash_len);
+		free(resend_check_hash);
+	}
+	
 	return recvsize;
 }
 
@@ -322,8 +341,7 @@
    new packet is put in RECVBUF of size RECVBUFSIZE and the actual size
    of the new packet is returned.  */
 
-static ssize_t
-sendrecv(void *recvbuf, size_t recvbufsize, void *tosend, size_t sendsize, int sendonly)
+static ssize_t sendrecv(struct sa_block *s, void *recvbuf, size_t recvbufsize, void *tosend, size_t sendsize, int sendonly)
 {
 	struct pollfd pfd;
 	int tries = 0;
@@ -332,14 +350,14 @@
 	time_t end = 0;
 	void *realtosend;
 
-	pfd.fd = sockfd;
+	pfd.fd = s->ike_fd;
 	pfd.events = POLLIN;
 	tries = 0;
 
-	if ((tosend != NULL)&&(encap_mode != IPSEC_ENCAP_TUNNEL)) {
+	if ((s->ipsec.natt_active_mode == NATT_ACTIVE_RFC) && (tosend != NULL)) {
 		DEBUG(2, printf("NAT-T mode, adding non-esp marker\n"));
 		realtosend = xallocc(sendsize+4);
-		memcpy(realtosend+4, tosend, sendsize);
+		memmove((char*)realtosend+4, tosend, sendsize);
 		sendsize += 4;
 	} else {
 		realtosend = tosend;
@@ -349,82 +367,83 @@
 		int pollresult;
 
 		if (realtosend != NULL)
-			if (sendto(sockfd, realtosend, sendsize, 0,
-					dest_addr, sizeof(struct sockaddr_in)) != (int)sendsize)
+			if (write(s->ike_fd, realtosend, sendsize) != (int)sendsize)
 				error(1, errno, "can't send packet");
 		if (sendonly)
 			break;
 		
 		do {
-			pollresult = poll(&pfd, 1, timeout << tries);
+			pollresult = poll(&pfd, 1, s->ike.timeout << tries);
 		} while (pollresult == -1 && errno == EINTR);
 		
 		if (pollresult == -1)
 			error(1, errno, "can't poll socket");
 		if (pollresult != 0) {
-			recvsize = recv_ignore_dup(recvbuf, recvbufsize);
+			recvsize = recv_ignore_dup(s, recvbuf, recvbufsize);
 			end = time(NULL);
 			if (recvsize != -1)
 				break;
 			continue;
 		}
 		
-		if (tries > 5)
+		if (tries > 2)
 			error(1, 0, "no response from target");
 		tries++;
 	}
 
-	if ((tosend != NULL)&&(encap_mode != IPSEC_ENCAP_TUNNEL))
+	if (realtosend != tosend)
 		free(realtosend);
 
 	if (sendonly)
 		return 0;
 
-	if (encap_mode != IPSEC_ENCAP_TUNNEL) {
+	if ((s->ipsec.natt_active_mode == NATT_ACTIVE_RFC)&&(recvsize > 4)) {
 		recvsize -= 4; /* 4 bytes non-esp marker */
-		memcpy(recvbuf, recvbuf+4, recvsize);
-	}
+		memmove(recvbuf, (char *)recvbuf+4, recvsize);
+	}
+
+	DEBUGTOP(3, printf("\n receiving: <========================\n"));
 
 	/* Wait at least 2s for a response or 4 times the time it took
 	 * last time.  */
-	if (start == end)
-		timeout = 2000;
+	if (start >= end)
+		s->ike.timeout = 2000;
 	else
-		timeout = 4000 * (end - start);
+		s->ike.timeout = 4000 * (end - start);
 
 	return recvsize;
 }
 
-int isakmp_crypt(struct sa_block *s, uint8_t * block, size_t blocklen, int enc)
+static int isakmp_crypt(struct sa_block *s, uint8_t * block, size_t blocklen, int enc)
 {
 	unsigned char *new_iv, *iv = NULL;
 	int info_ex;
 	gcry_cipher_hd_t cry_ctx;
 
-	if (blocklen < ISAKMP_PAYLOAD_O || ((blocklen - ISAKMP_PAYLOAD_O) % s->ivlen != 0))
+	if (blocklen < ISAKMP_PAYLOAD_O || ((blocklen - ISAKMP_PAYLOAD_O) % s->ike.ivlen != 0))
 		abort();
 
-	if (!enc && (memcmp(block + ISAKMP_I_COOKIE_O, s->i_cookie, ISAKMP_COOKIE_LENGTH) != 0
-		|| memcmp(block + ISAKMP_R_COOKIE_O, s->r_cookie, ISAKMP_COOKIE_LENGTH) != 0)) {
+	if (!enc && (memcmp(block + ISAKMP_I_COOKIE_O, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH) != 0
+		|| memcmp(block + ISAKMP_R_COOKIE_O, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH) != 0)) {
 		DEBUG(2, printf("got paket with wrong cookies\n"));
 		return ISAKMP_N_INVALID_COOKIE;
 	}
 	
 	info_ex = block[ISAKMP_EXCHANGE_TYPE_O] == ISAKMP_EXCHANGE_INFORMATIONAL;
 	
-	if (memcmp(block + ISAKMP_MESSAGE_ID_O, s->current_iv_msgid, 4) != 0) {
+	if (memcmp(block + ISAKMP_MESSAGE_ID_O, s->ike.current_iv_msgid, 4) != 0) {
 		gcry_md_hd_t md_ctx;
 
-		gcry_md_open(&md_ctx, s->md_algo, 0);
-		gcry_md_write(md_ctx, s->initial_iv, s->ivlen);
+		gcry_md_open(&md_ctx, s->ike.md_algo, 0);
+		gcry_md_write(md_ctx, s->ike.initial_iv, s->ike.ivlen);
 		gcry_md_write(md_ctx, block + ISAKMP_MESSAGE_ID_O, 4);
 		gcry_md_final(md_ctx);
 		if (info_ex) {
-			iv = xallocc(s->ivlen);
-			memcpy(iv, gcry_md_read(md_ctx, 0), s->ivlen);
+			iv = xallocc(s->ike.ivlen);
+			memcpy(iv, gcry_md_read(md_ctx, 0), s->ike.ivlen);
 		} else {
-			memcpy(s->current_iv, gcry_md_read(md_ctx, 0), s->ivlen);
-			memcpy(s->current_iv_msgid, block + ISAKMP_MESSAGE_ID_O, 4);
+			memcpy(s->ike.current_iv, gcry_md_read(md_ctx, 0), s->ike.ivlen);
+			memcpy(s->ike.current_iv_msgid, block + ISAKMP_MESSAGE_ID_O, 4);
 		}
 		gcry_md_close(md_ctx);
 	} else if (info_ex) {
@@ -432,24 +451,24 @@
 	}
 	
 	if (!info_ex) {
-		iv = s->current_iv;
-	}
-
-	new_iv = xallocc(s->ivlen);
-	gcry_cipher_open(&cry_ctx, s->cry_algo, GCRY_CIPHER_MODE_CBC, 0);
-	gcry_cipher_setkey(cry_ctx, s->key, s->keylen);
-	gcry_cipher_setiv(cry_ctx, iv, s->ivlen);
+		iv = s->ike.current_iv;
+	}
+
+	new_iv = xallocc(s->ike.ivlen);
+	gcry_cipher_open(&cry_ctx, s->ike.cry_algo, GCRY_CIPHER_MODE_CBC, 0);
+	gcry_cipher_setkey(cry_ctx, s->ike.key, s->ike.keylen);
+	gcry_cipher_setiv(cry_ctx, iv, s->ike.ivlen);
 	if (!enc) {
-		memcpy(new_iv, block + blocklen - s->ivlen, s->ivlen);
+		memcpy(new_iv, block + blocklen - s->ike.ivlen, s->ike.ivlen);
 		gcry_cipher_decrypt(cry_ctx, block + ISAKMP_PAYLOAD_O, blocklen - ISAKMP_PAYLOAD_O,
 			NULL, 0);
 		if (!info_ex)
-			memcpy(s->current_iv, new_iv, s->ivlen);
+			memcpy(s->ike.current_iv, new_iv, s->ike.ivlen);
 	} else {
 		gcry_cipher_encrypt(cry_ctx, block + ISAKMP_PAYLOAD_O, blocklen - ISAKMP_PAYLOAD_O,
 			NULL, 0);
 		if (!info_ex)
-			memcpy(s->current_iv, block + blocklen - s->ivlen, s->ivlen);
+			memcpy(s->ike.current_iv, block + blocklen - s->ike.ivlen, s->ike.ivlen);
 	}
 	gcry_cipher_close(cry_ctx);
 	
@@ -460,18 +479,18 @@
 	return 0;
 }
 
-static uint16_t unpack_verify_phase2(struct sa_block *s,
-	uint8_t * r_packet,
+static uint16_t unpack_verify_phase2(struct sa_block *s, uint8_t * r_packet,
 	size_t r_length, struct isakmp_packet **r_p, const uint8_t * nonce, size_t nonce_size)
 {
 	struct isakmp_packet *r;
 	int reject = 0;
-
+	
 	*r_p = NULL;
 
-	if (r_length < ISAKMP_PAYLOAD_O || ((r_length - ISAKMP_PAYLOAD_O) % s->ivlen != 0)) {
-		DEBUG(2, printf("payload to short or not padded: len=%d, min=%d (ivlen=%d)\n",
-			r_length, ISAKMP_PAYLOAD_O, s->ivlen));
+	if (r_length < ISAKMP_PAYLOAD_O || ((r_length - ISAKMP_PAYLOAD_O) % s->ike.ivlen != 0)) {
+		DEBUG(2, printf("payload too short or not padded: len=%lld, min=%d (ivlen=%lld)\n",
+			(long long)r_length, ISAKMP_PAYLOAD_O, (long long)s->ike.ivlen));
+		hex_dump("Payload", r_packet, r_length, NULL);
 		return ISAKMP_N_UNEQUAL_PAYLOAD_LENGTHS;
 	}
 
@@ -479,6 +498,8 @@
 	if (reject != 0)
 		return reject;
 
+	s->ike.life.rx += r_length;
+	
 	{
 		r = parse_isakmp_packet(r_packet, r_length, &reject);
 		if (reject != 0)
@@ -495,7 +516,7 @@
 		unsigned char *expected_hash;
 		struct isakmp_payload *h = r->payload;
 
-		if (h == NULL || h->type != ISAKMP_PAYLOAD_HASH || h->u.hash.length != s->md_len)
+		if (h == NULL || h->type != ISAKMP_PAYLOAD_HASH || h->u.hash.length != s->ike.md_len)
 			return ISAKMP_N_INVALID_HASH_INFORMATION;
 
 		spos = (ISAKMP_PAYLOAD_O + (r_packet[ISAKMP_PAYLOAD_O + 2] << 8)
@@ -505,8 +526,8 @@
 		for (sz = spos; r_packet[sz] != 0; sz += r_packet[sz + 2] << 8 | r_packet[sz + 3]) ;
 		sz += r_packet[sz + 2] << 8 | r_packet[sz + 3];
 
-		gcry_md_open(&hm, s->md_algo, GCRY_MD_FLAG_HMAC);
-		gcry_md_setkey(hm, s->skeyid_a, s->md_len);
+		gcry_md_open(&hm, s->ike.md_algo, GCRY_MD_FLAG_HMAC);
+		gcry_md_setkey(hm, s->ike.skeyid_a, s->ike.md_len);
 		gcry_md_write(hm, r_packet + ISAKMP_MESSAGE_ID_O, 4);
 		if (nonce)
 			gcry_md_write(hm, nonce, nonce_size);
@@ -514,15 +535,13 @@
 		gcry_md_final(hm);
 		expected_hash = gcry_md_read(hm, 0);
 
-		if (opt_debug >= 3) {
-			printf("hashlen: %lu\n", (unsigned long)s->md_len);
-			printf("u.hash.length: %d\n", h->u.hash.length);
-			hex_dump("expected_hash", expected_hash, s->md_len);
-			hex_dump("h->u.hash.data", h->u.hash.data, s->md_len);
-		}
+		DEBUG(3,printf("hashlen: %lu\n", (unsigned long)s->ike.md_len));
+		DEBUG(3,printf("u.hash.length: %d\n", h->u.hash.length));
+		hex_dump("expected_hash", expected_hash, s->ike.md_len, NULL);
+		hex_dump("h->u.hash.data", h->u.hash.data, s->ike.md_len, NULL);
 
 		reject = 0;
-		if (memcmp(h->u.hash.data, expected_hash, s->md_len) != 0)
+		if (memcmp(h->u.hash.data, expected_hash, s->ike.md_len) != 0)
 			reject = ISAKMP_N_AUTHENTICATION_FAILED;
 		gcry_md_close(hm);
 #if 0
@@ -548,20 +567,20 @@
 
 	/* Build up the packet.  */
 	p = new_isakmp_packet();
-	memcpy(p->i_cookie, s->i_cookie, ISAKMP_COOKIE_LENGTH);
-	memcpy(p->r_cookie, s->r_cookie, ISAKMP_COOKIE_LENGTH);
+	memcpy(p->i_cookie, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH);
+	memcpy(p->r_cookie, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH);
 	p->flags = ISAKMP_FLAG_E;
 	p->isakmp_version = ISAKMP_VERSION;
 	p->exchange_type = exchange_type;
 	p->message_id = msgid;
 	p->payload = new_isakmp_payload(ISAKMP_PAYLOAD_HASH);
 	p->payload->next = pl;
-	p->payload->u.hash.length = s->md_len;
-	p->payload->u.hash.data = xallocc(s->md_len);
+	p->payload->u.hash.length = s->ike.md_len;
+	p->payload->u.hash.data = xallocc(s->ike.md_len);
 
 	/* Set the MAC.  */
-	gcry_md_open(&hm, s->md_algo, GCRY_MD_FLAG_HMAC);
-	gcry_md_setkey(hm, s->skeyid_a, s->md_len);
+	gcry_md_open(&hm, s->ike.md_algo, GCRY_MD_FLAG_HMAC);
+	gcry_md_setkey(hm, s->ike.skeyid_a, s->ike.md_len);
 
 	if (pl == NULL) {
 		DEBUG(3, printf("authing NULL package!\n"));
@@ -588,10 +607,10 @@
 	}
 
 	gcry_md_final(hm);
-	memcpy(p->payload->u.hash.data, gcry_md_read(hm, 0), s->md_len);
+	memcpy(p->payload->u.hash.data, gcry_md_read(hm, 0), s->ike.md_len);
 	gcry_md_close(hm);
 
-	flatten_isakmp_packet(p, p_flat, p_size, s->ivlen);
+	flatten_isakmp_packet(p, p_flat, p_size, s->ike.ivlen);
 	free_isakmp_packet(p);
 }
 
@@ -613,7 +632,9 @@
 		p_size = *save_p_size;
 	}
 
-	recvlen = sendrecv(r_packet, sizeof(r_packet), p_flat, p_size, sendonly);
+	s->ike.life.tx += p_size;
+
+	recvlen = sendrecv(s, r_packet, sizeof(r_packet), p_flat, p_size, sendonly);
 	if (sendonly == 0)
 		r_length = recvlen;
 	
@@ -625,20 +646,111 @@
 	}
 }
 
-static void phase2_fatal(struct sa_block *s, const char *msg, int id)
+static void send_phase2_late(struct sa_block *s, struct isakmp_payload *pl,
+	uint8_t exchange_type, uint32_t msgid)
+{
+	struct isakmp_packet *p;
+	uint8_t *p_flat;
+	size_t p_size;
+	ssize_t recvlen;
+
+	/* Build up the packet.  */
+	p = new_isakmp_packet();
+	memcpy(p->i_cookie, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH);
+	memcpy(p->r_cookie, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH);
+	p->flags = ISAKMP_FLAG_E;
+	p->isakmp_version = ISAKMP_VERSION;
+	p->exchange_type = exchange_type;
+	p->message_id = msgid;
+	p->payload = pl;
+
+	flatten_isakmp_packet(p, &p_flat, &p_size, s->ike.ivlen);
+	free_isakmp_packet(p);
+	isakmp_crypt(s, p_flat, p_size, 1);
+
+	s->ike.life.tx += p_size;
+
+	recvlen = sendrecv(s, NULL, 0, p_flat, p_size, 1);
+	free(p_flat);
+}
+
+void keepalive_ike(struct sa_block *s)
+{
+	uint32_t msgid;
+
+	gcry_create_nonce((uint8_t *) & msgid, sizeof(msgid));
+	send_phase2_late(s, NULL, ISAKMP_EXCHANGE_INFORMATIONAL, msgid);
+}
+
+static void send_dpd(struct sa_block *s, int isack, uint32_t seqno)
 {
 	struct isakmp_payload *pl;
 	uint32_t msgid;
+	
+	pl = new_isakmp_payload(ISAKMP_PAYLOAD_N);
+	pl->u.n.doi = ISAKMP_DOI_IPSEC;
+	pl->u.n.protocol = ISAKMP_IPSEC_PROTO_ISAKMP;
+	pl->u.n.type = isack ? ISAKMP_N_R_U_THERE_ACK : ISAKMP_N_R_U_THERE;
+	pl->u.n.spi_length = 2 * ISAKMP_COOKIE_LENGTH;
+	pl->u.n.spi = xallocc(2 * ISAKMP_COOKIE_LENGTH);
+	memcpy(pl->u.n.spi + ISAKMP_COOKIE_LENGTH * 0, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH);
+	memcpy(pl->u.n.spi + ISAKMP_COOKIE_LENGTH * 1, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH);
+	pl->u.n.data_length = 4;
+	pl->u.n.data = xallocc(4);
+	memcpy(pl->u.n.data, &seqno, 4);
+	gcry_create_nonce((uint8_t *) & msgid, sizeof(msgid));
+	/* 2007-09-06 JKU/ZID: Sonicwall drops non hashed r_u_there-requests */
+	sendrecv_phase2(s, pl, ISAKMP_EXCHANGE_INFORMATIONAL, msgid,
+		1 , NULL, NULL, NULL, 0, NULL, 0);
+}
+
+void dpd_ike(struct sa_block *s)
+{
+	if (!s->ike.do_dpd)
+		return;
+	
+	if (s->ike.dpd_seqno == s->ike.dpd_seqno_ack) {
+		/* Increase the sequence number, reset the attempts to 6, record
+		** the current time and send a dpd request
+		*/
+		s->ike.dpd_attempts = 6;
+		s->ike.dpd_sent = time(NULL);
+		++s->ike.dpd_seqno;
+		send_dpd(s, 0, s->ike.dpd_seqno);
+	} else {
+		/* Our last dpd request has not yet been acked.  If it's been
+		** less than 5 seconds since we sent it do nothing.  Otherwise
+		** decrement dpd_attempts.  If dpd_attempts is 0 dpd fails and we
+		** terminate otherwise we send it again with the same sequence
+		** number and record current time.
+		*/
+		time_t now = time(NULL);
+		if (now < s->ike.dpd_sent + 5)
+			return;
+		if (--s->ike.dpd_attempts == 0) {
+			DEBUG(2, printf("dead peer detected, terminating\n"));
+			do_kill = -2;
+			return;
+		}
+		s->ike.dpd_sent = now;
+		send_dpd(s, 0, s->ike.dpd_seqno);
+	}
+}
+
+static void phase2_fatal(struct sa_block *s, const char *msg, int id)
+{
+	struct isakmp_payload *pl;
+	uint32_t msgid;
 
 	DEBUG(1, printf("\n\n---!!!!!!!!! entering phase2_fatal !!!!!!!!!---\n\n\n"));
-	gcry_randomize((uint8_t *) & msgid, sizeof(msgid), GCRY_WEAK_RANDOM);
+	gcry_create_nonce((uint8_t *) & msgid, sizeof(msgid));
 	pl = new_isakmp_payload(ISAKMP_PAYLOAD_N);
 	pl->u.n.doi = ISAKMP_DOI_IPSEC;
 	pl->u.n.protocol = ISAKMP_IPSEC_PROTO_ISAKMP;
 	pl->u.n.type = id;
 	sendrecv_phase2(s, pl, ISAKMP_EXCHANGE_INFORMATIONAL, msgid, 1, 0, 0, 0, 0, 0, 0);
 
-	gcry_randomize((uint8_t *) & msgid, sizeof(msgid), GCRY_WEAK_RANDOM);
+	gcry_create_nonce((uint8_t *) & msgid, sizeof(msgid));
 	pl = new_isakmp_payload(ISAKMP_PAYLOAD_D);
 	pl->u.d.doi = ISAKMP_DOI_IPSEC;
 	pl->u.d.protocol = ISAKMP_IPSEC_PROTO_ISAKMP;
@@ -646,16 +758,15 @@
 	pl->u.d.num_spi = 1;
 	pl->u.d.spi = xallocc(1 * sizeof(uint8_t *));
 	pl->u.d.spi[0] = xallocc(2 * ISAKMP_COOKIE_LENGTH);
-	memcpy(pl->u.d.spi[0] + ISAKMP_COOKIE_LENGTH * 0, s->i_cookie, ISAKMP_COOKIE_LENGTH);
-	memcpy(pl->u.d.spi[0] + ISAKMP_COOKIE_LENGTH * 1, s->r_cookie, ISAKMP_COOKIE_LENGTH);
+	memcpy(pl->u.d.spi[0] + ISAKMP_COOKIE_LENGTH * 0, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH);
+	memcpy(pl->u.d.spi[0] + ISAKMP_COOKIE_LENGTH * 1, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH);
 	sendrecv_phase2(s, pl, ISAKMP_EXCHANGE_INFORMATIONAL, msgid, 1, 0, 0, 0, 0, 0, 0);
 
-	error(1, 0, msg, isakmp_notify_to_error(id));
+	error(1, 0, msg, val_to_string(id, isakmp_notify_enum_array), id);
 }
 
 static uint8_t *gen_keymat(struct sa_block *s,
 	uint8_t protocol, uint32_t spi,
-	int md_algo, int crypt_algo,
 	const uint8_t * dh_shared, size_t dh_size,
 	const uint8_t * ni_data, size_t ni_size, const uint8_t * nr_data, size_t nr_size)
 {
@@ -665,22 +776,18 @@
 	int blksz;
 	int cnt;
 
-	int md_len = gcry_md_get_algo_dlen(md_algo);
-	size_t cry_len;
-
-	gcry_cipher_algo_info(crypt_algo, GCRYCTL_GET_KEYLEN, NULL, &cry_len);
-	blksz = md_len + cry_len;
-	cnt = (blksz + s->md_len - 1) / s->md_len;
-	block = xallocc(cnt * s->md_len);
+	blksz = s->ipsec.md_len + s->ipsec.key_len;
+	cnt = (blksz + s->ike.md_len - 1) / s->ike.md_len;
+	block = xallocc(cnt * s->ike.md_len);
 	DEBUG(3, printf("generating %d bytes keymat (cnt=%d)\n", blksz, cnt));
 	if (cnt < 1)
 		abort();
 
 	for (i = 0; i < cnt; i++) {
-		gcry_md_open(&hm, s->md_algo, GCRY_MD_FLAG_HMAC);
-		gcry_md_setkey(hm, s->skeyid_d, s->md_len);
+		gcry_md_open(&hm, s->ike.md_algo, GCRY_MD_FLAG_HMAC);
+		gcry_md_setkey(hm, s->ike.skeyid_d, s->ike.md_len);
 		if (i != 0)
-			gcry_md_write(hm, block + (i - 1) * s->md_len, s->md_len);
+			gcry_md_write(hm, block + (i - 1) * s->ike.md_len, s->ike.md_len);
 		if (dh_shared != NULL)
 			gcry_md_write(hm, dh_shared, dh_size);
 		gcry_md_write(hm, &protocol, 1);
@@ -688,19 +795,198 @@
 		gcry_md_write(hm, ni_data, ni_size);
 		gcry_md_write(hm, nr_data, nr_size);
 		gcry_md_final(hm);
-		memcpy(block + i * s->md_len, gcry_md_read(hm, 0), s->md_len);
+		memcpy(block + i * s->ike.md_len, gcry_md_read(hm, 0), s->ike.md_len);
 		gcry_md_close(hm);
 	}
 	return block;
 }
 
+static int do_config_to_env(struct sa_block *s, struct isakmp_attribute *a)
+{
+	int i;
+	int reject = 0;
+	int seen_address = 0;
+	char *strbuf, *strbuf2;
+	
+	unsetenv("CISCO_BANNER");
+	unsetenv("CISCO_DEF_DOMAIN");
+	unsetenv("CISCO_SPLIT_INC");
+	unsetenv("INTERNAL_IP4_NBNS");
+	unsetenv("INTERNAL_IP4_DNS");
+	unsetenv("INTERNAL_IP4_NETMASK");
+	unsetenv("INTERNAL_IP4_ADDRESS");
+
+	for (; a && reject == 0; a = a->next)
+		switch (a->type) {
+		case ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_ADDRESS:
+			if (a->af != isakmp_attr_lots || a->u.lots.length != 4)
+				reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
+			else {
+				addenv_ipv4("INTERNAL_IP4_ADDRESS", a->u.lots.data);
+				memcpy(s->our_address, a->u.lots.data, 4);
+			}
+			seen_address = 1;
+			break;
+
+		case ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_NETMASK:
+			if (a->af == isakmp_attr_lots && a->u.lots.length == 0) {
+				DEBUG(2, printf("ignoring zero length netmask\n"));
+				continue;
+			}
+			if (a->af != isakmp_attr_lots || a->u.lots.length != 4)
+				reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
+			else
+				addenv_ipv4("INTERNAL_IP4_NETMASK", a->u.lots.data);
+			break;
+
+		case ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_DNS:
+			if (a->af != isakmp_attr_lots || a->u.lots.length != 4)
+				reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
+			else
+				addenv_ipv4("INTERNAL_IP4_DNS", a->u.lots.data);
+			break;
+
+		case ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_NBNS:
+			if (a->af != isakmp_attr_lots || a->u.lots.length != 4)
+				reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
+			else
+				addenv_ipv4("INTERNAL_IP4_NBNS", a->u.lots.data);
+			break;
+
+		case ISAKMP_MODECFG_ATTRIB_CISCO_DEF_DOMAIN:
+			if (a->af != isakmp_attr_lots) {
+				reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
+				break;
+			}
+			strbuf = xallocc(a->u.lots.length + 1);
+			memcpy(strbuf, a->u.lots.data, a->u.lots.length);
+			addenv("CISCO_DEF_DOMAIN", strbuf);
+			free(strbuf);
+			break;
+
+		case ISAKMP_MODECFG_ATTRIB_CISCO_BANNER:
+			if (a->af != isakmp_attr_lots) {
+				reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
+				break;
+			}
+			strbuf = xallocc(a->u.lots.length + 1);
+			memcpy(strbuf, a->u.lots.data, a->u.lots.length);
+			addenv("CISCO_BANNER", strbuf);
+			free(strbuf);
+			DEBUG(1, printf("Banner: "));
+			DEBUG(1, fwrite(a->u.lots.data, a->u.lots.length, 1, stdout));
+			DEBUG(1, printf("\n"));
+			break;
+
+		case ISAKMP_MODECFG_ATTRIB_APPLICATION_VERSION:
+			DEBUG(2, printf("Remote Application Version: "));
+			DEBUG(2, fwrite(a->u.lots.data, a->u.lots.length, 1, stdout));
+			DEBUG(2, printf("\n"));
+			break;
+
+		case ISAKMP_MODECFG_ATTRIB_CISCO_DO_PFS:
+			if (a->af != isakmp_attr_16)
+				reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
+			else {
+				s->ipsec.do_pfs = a->u.attr_16;
+				DEBUG(2, printf("got pfs setting: %d\n", s->ipsec.do_pfs));
+			}
+			break;
+
+		case ISAKMP_MODECFG_ATTRIB_CISCO_UDP_ENCAP_PORT:
+			if (a->af != isakmp_attr_16)
+				reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
+			else {
+				s->ipsec.peer_udpencap_port = a->u.attr_16;
+				DEBUG(2, printf("got peer udp encapsulation port: %hu\n", s->ipsec.peer_udpencap_port));
+			}
+			break;
+
+		case ISAKMP_MODECFG_ATTRIB_CISCO_SPLIT_INC:
+			if (a->af != isakmp_attr_acl) {
+				reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
+				break;
+			}
+			
+			DEBUG(2, printf("got %d acls for split include\n", a->u.acl.count));
+			asprintf(&strbuf, "%d", a->u.acl.count);
+			setenv("CISCO_SPLIT_INC", strbuf, 1);
+			free(strbuf);
+			
+			for (i = 0; i < a->u.acl.count; i++) {
+				DEBUG(2, printf("acl %d: ", i));
+				/* NOTE: inet_ntoa returns one static buffer */
+				
+				asprintf(&strbuf, "CISCO_SPLIT_INC_%d_ADDR", i);
+				asprintf(&strbuf2, "%s", inet_ntoa(a->u.acl.acl_ent[i].addr));
+				DEBUG(2, printf("addr: %s/", strbuf2));
+				setenv(strbuf, strbuf2, 1);
+				free(strbuf); free(strbuf2);
+				
+				asprintf(&strbuf, "CISCO_SPLIT_INC_%d_MASK", i);
+				asprintf(&strbuf2, "%s", inet_ntoa(a->u.acl.acl_ent[i].mask));
+				DEBUG(2, printf("%s ", strbuf2));
+				setenv(strbuf, strbuf2, 1);
+				free(strbuf); free(strbuf2);
+				
+				{ /* this is just here because ip route does not accept netmasks */
+					int len;
+					uint32_t addr;
+					
+					for (len = 0, addr = ntohl(a->u.acl.acl_ent[i].mask.s_addr);
+						addr; addr <<= 1, len++)
+						; /* do nothing */
+					
+					asprintf(&strbuf, "CISCO_SPLIT_INC_%d_MASKLEN", i);
+					asprintf(&strbuf2, "%d", len);
+					DEBUG(2, printf("(%s), ", strbuf2));
+					setenv(strbuf, strbuf2, 1);
+					free(strbuf); free(strbuf2);
+				}
+				
+				asprintf(&strbuf, "CISCO_SPLIT_INC_%d_PROTOCOL", i);
+				asprintf(&strbuf2, "%hu", a->u.acl.acl_ent[i].protocol);
+				DEBUG(2, printf("protocol: %s, ", strbuf2));
+				setenv(strbuf, strbuf2, 1);
+				free(strbuf); free(strbuf2);
+				
+				asprintf(&strbuf, "CISCO_SPLIT_INC_%d_SPORT", i);
+				asprintf(&strbuf2, "%hu", a->u.acl.acl_ent[i].sport);
+				DEBUG(2, printf("sport: %s, ", strbuf2));
+				setenv(strbuf, strbuf2, 1);
+				free(strbuf); free(strbuf2);
+				
+				asprintf(&strbuf, "CISCO_SPLIT_INC_%d_DPORT", i);
+				asprintf(&strbuf2, "%hu", a->u.acl.acl_ent[i].dport);
+				DEBUG(2, printf("dport: %s\n", strbuf2));
+				setenv(strbuf, strbuf2, 1);
+				free(strbuf); free(strbuf2);
+			}
+			break;
+			
+		case ISAKMP_MODECFG_ATTRIB_CISCO_SAVE_PW:
+			DEBUG(2, printf("got save password setting: %d\n", a->u.attr_16));
+			break;
+			
+		default:
+			DEBUG(2, printf("unknown attribute %d / 0x%X\n", a->type, a->type));
+			break;
+		}
+
+	if (reject == 0 && !seen_address)
+		reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
+	
+	return reject;
+}
+
 /* * */
 
-struct isakmp_attribute *make_transform_ike(int dh_group, int crypt, int hash, int keylen, int auth)
+static struct isakmp_attribute *make_transform_ike(int dh_group, int crypt, int hash, int keylen, int auth)
 {
 	struct isakmp_attribute *a = NULL;
-
-#if 0
+#if NORTELVPN
+	a = new_isakmp_attribute_16(32767, 10, a);
+#else
 	a = new_isakmp_attribute(IKE_ATTRIB_LIFE_DURATION, a);
 	a->af = isakmp_attr_lots;
 	a->u.lots.length = 4;
@@ -708,7 +994,6 @@
 	*((uint32_t *) a->u.lots.data) = htonl(2147483);
 	a = new_isakmp_attribute_16(IKE_ATTRIB_LIFE_TYPE, IKE_LIFE_TYPE_SECONDS, a);
 #endif
-	a = new_isakmp_attribute_16(32767, 10, a);
 	a = new_isakmp_attribute_16(IKE_ATTRIB_GROUP_DESC, dh_group, a);
 	a = new_isakmp_attribute_16(IKE_ATTRIB_AUTH_METHOD, auth, a);
 	a = new_isakmp_attribute_16(IKE_ATTRIB_HASH, hash, a);
@@ -718,7 +1003,7 @@
 	return a;
 }
 
-struct isakmp_payload *make_our_sa_ike(void)
+static struct isakmp_payload *make_our_sa_ike(void)
 {
 	struct isakmp_payload *r = new_isakmp_payload(ISAKMP_PAYLOAD_SA);
 	struct isakmp_payload *t = NULL, *tn;
@@ -731,10 +1016,40 @@
 	r->u.sa.situation = ISAKMP_IPSEC_SIT_IDENTITY_ONLY;
 	r->u.sa.proposals = new_isakmp_payload(ISAKMP_PAYLOAD_P);
 	r->u.sa.proposals->u.p.prot_id = ISAKMP_IPSEC_PROTO_ISAKMP;
-	/* for (auth = 0; supp_auth[auth].name != NULL; auth++) { */ auth = 0;
+
+#if NORTELVPN
+    auth = 0;
+	if ((opt_auth_mode == AUTH_MODE_CERT) && 
+	        (supp_auth[auth].ike_sa_id != IKE_AUTH_RSA_SIG) &&
+			(supp_auth[auth].ike_sa_id != IKE_AUTH_DSS)) {
+	} else if ((opt_auth_mode == AUTH_MODE_HYBRID) &&
+		    (supp_auth[auth].ike_sa_id != IKE_AUTH_HybridInitRSA) &&
+			(supp_auth[auth].ike_sa_id != IKE_AUTH_HybridInitDSS)) { 
+	} else if (supp_auth[auth].ike_sa_id == IKE_AUTH_HybridInitRSA ||
+			supp_auth[auth].ike_sa_id == IKE_AUTH_HybridInitDSS ||
+			supp_auth[auth].ike_sa_id == IKE_AUTH_RSA_SIG ||
+			supp_auth[auth].ike_sa_id == IKE_AUTH_DSS) {
+	} else {
+#else
+	for (auth = 0; supp_auth[auth].name != NULL; auth++) {
+		if (opt_auth_mode == AUTH_MODE_CERT) {
+			if ((supp_auth[auth].ike_sa_id != IKE_AUTH_RSA_SIG) &&
+				(supp_auth[auth].ike_sa_id != IKE_AUTH_DSS))
+				continue;
+		} else if (opt_auth_mode == AUTH_MODE_HYBRID) {
+			if ((supp_auth[auth].ike_sa_id != IKE_AUTH_HybridInitRSA) &&
+				(supp_auth[auth].ike_sa_id != IKE_AUTH_HybridInitDSS)) 
+				continue;
+		} else {
+			if (supp_auth[auth].ike_sa_id == IKE_AUTH_HybridInitRSA ||
+				supp_auth[auth].ike_sa_id == IKE_AUTH_HybridInitDSS ||
+				supp_auth[auth].ike_sa_id == IKE_AUTH_RSA_SIG ||
+				supp_auth[auth].ike_sa_id == IKE_AUTH_DSS)
+				continue;
+		}
+#endif	
+
 		for (crypt = 0; supp_crypt[crypt].name != NULL; crypt++) {
-			if ((supp_crypt[crypt].my_id == GCRY_CIPHER_DES) && (opt_1des == 0))
-				continue;
 			keylen = supp_crypt[crypt].keylen;
 			for (hash = 0; supp_hash[hash].name != NULL; hash++) {
 				tn = t;
@@ -746,53 +1061,100 @@
 				t->next = tn;
 			}
 		}
-	/* } */
+	}
 	for (i = 0, tn = t; tn; tn = tn->next)
 		tn->u.t.number = i++;
 	r->u.sa.proposals->u.p.transforms = t;
 	return r;
 }
 
-void do_phase_1(const char *key_id, const char *shared_key, struct sa_block *s)
+static void lifetime_ike_process(struct sa_block *s, struct isakmp_attribute *a)
+{
+	uint32_t value;
+	
+	assert(a != NULL);
+	assert(a->type == IKE_ATTRIB_LIFE_TYPE);
+	assert(a->af == isakmp_attr_16);
+	assert(a->u.attr_16 == IKE_LIFE_TYPE_SECONDS || a->u.attr_16 == IKE_LIFE_TYPE_K);
+	assert(a->next != NULL);
+	assert(a->next->type == IKE_ATTRIB_LIFE_DURATION);
+	
+	if (a->next->af == isakmp_attr_16)
+		value = a->next->u.attr_16;
+	else if (a->next->af == isakmp_attr_lots && a->next->u.lots.length == 4)
+		value = ntohl(*((uint32_t *) a->next->u.lots.data));
+	else
+		assert(0);
+	
+	DEBUG(2, printf("got ike lifetime attributes: %d %s\n", value,
+		(a->u.attr_16 == IKE_LIFE_TYPE_SECONDS) ? "seconds" : "kilobyte"));
+	
+	if (a->u.attr_16 == IKE_LIFE_TYPE_SECONDS)
+		s->ike.life.seconds = value;
+	else
+		s->ike.life.kbytes = value;
+}
+
+static void lifetime_ipsec_process(struct sa_block *s, struct isakmp_attribute *a)
+{
+	uint32_t value;
+	
+	assert(a != NULL);
+	assert(a->type == ISAKMP_IPSEC_ATTRIB_SA_LIFE_TYPE);
+	assert(a->af == isakmp_attr_16);
+	assert(a->u.attr_16 == IPSEC_LIFE_SECONDS || a->u.attr_16 == IPSEC_LIFE_K);
+	assert(a->next != NULL);
+	assert(a->next->type == ISAKMP_IPSEC_ATTRIB_SA_LIFE_DURATION);
+	
+	if (a->next->af == isakmp_attr_16)
+		value = a->next->u.attr_16;
+	else if (a->next->af == isakmp_attr_lots && a->next->u.lots.length == 4)
+		value = ntohl(*((uint32_t *) a->next->u.lots.data));
+	else
+		assert(0);
+	
+	DEBUG(2, printf("got ipsec lifetime attributes: %d %s\n", value,
+		(a->u.attr_16 == IPSEC_LIFE_SECONDS) ? "seconds" : "kilobyte"));
+	
+	if (a->u.attr_16 == IPSEC_LIFE_SECONDS)
+		s->ipsec.life.seconds = value;
+	else
+		s->ipsec.life.kbytes = value;
+}
+
+static void do_phase1(const char *key_id, const char *shared_key, struct sa_block *s)
 {
 	unsigned char i_nonce[20];
 	struct group *dh_grp;
 	unsigned char *dh_public;
 	unsigned char *returned_hash;
-	static const uint8_t xauth_vid[] = XAUTH_VENDOR_ID;
-	static const uint8_t unity_vid[] = UNITY_VENDOR_ID;
-	static const uint8_t unknown_vid[] = UNKNOWN_VENDOR_ID;
-	static const uint8_t natt_vid[] = NATT_VENDOR_ID; /* NAT traversal */
-#if 0
-	static const uint8_t dpd_vid[] = DPD_VENDOR_ID; /* dead peer detection */
-	static const uint8_t my_vid[] = {
-		0x35, 0x53, 0x07, 0x6c, 0x4f, 0x65, 0x12, 0x68, 0x02, 0x82, 0xf2, 0x15,
-		0x8a, 0xa8, 0xa0, 0x9e
-	};
-#endif
+	unsigned char *psk_hash;
 
 	struct isakmp_packet *p1;
 	int seen_natt_vid = 0, seen_natd = 0, seen_natd_them = 0, seen_natd_us = 0, natd_type = 0;
 	unsigned char *natd_us = NULL, *natd_them = NULL;
-
-	DEBUG(2, printf("S4.1\n"));
-	gcry_randomize(s->i_cookie, ISAKMP_COOKIE_LENGTH, GCRY_STRONG_RANDOM);
-	s->do_pfs = -1;
-	if (s->i_cookie[0] == 0)
-		s->i_cookie[0] = 1;
-	hex_dump("i_cookie", s->i_cookie, ISAKMP_COOKIE_LENGTH);
-	gcry_randomize(i_nonce, sizeof(i_nonce), GCRY_STRONG_RANDOM);
-	hex_dump("i_nonce", i_nonce, sizeof(i_nonce));
-	DEBUG(2, printf("S4.2\n"));
+	int natt_draft = -1;
+	unsigned char *dh_shared_secret;
+	
+	DEBUGTOP(2, printf("S4.1 create_nonce\n"));
+	gcry_create_nonce(s->ike.i_cookie, ISAKMP_COOKIE_LENGTH);
+	s->ike.life.start = time(NULL);
+	s->ipsec.do_pfs = -1;
+	if (s->ike.i_cookie[0] == 0)
+		s->ike.i_cookie[0] = 1;
+	hex_dump("i_cookie", s->ike.i_cookie, ISAKMP_COOKIE_LENGTH, NULL);
+	gcry_create_nonce(i_nonce, sizeof(i_nonce));
+	hex_dump("i_nonce", i_nonce, sizeof(i_nonce), NULL);
+	DEBUGTOP(2, printf("S4.2 dh setup\n"));
 	/* Set up the Diffie-Hellman stuff.  */
 	{
 		dh_grp = group_get(get_dh_group_ike()->my_id);
 		dh_public = xallocc(dh_getlen(dh_grp));
 		dh_create_exchange(dh_grp, dh_public);
-		hex_dump("dh_public", dh_public, dh_getlen(dh_grp));
-	}
-
-	DEBUG(2, printf("S4.3\n"));
+		hex_dump("dh_public", dh_public, dh_getlen(dh_grp), NULL);
+	}
+
+	DEBUGTOP(2, printf("S4.3 AM packet_1\n"));
 	/* Create the first packet.  */
 	{
 		struct isakmp_payload *l;
@@ -800,7 +1162,7 @@
 		size_t pkt_len;
 
 		p1 = new_isakmp_packet();
-		memcpy(p1->i_cookie, s->i_cookie, ISAKMP_COOKIE_LENGTH);
+		memcpy(p1->i_cookie, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH);
 		p1->isakmp_version = ISAKMP_VERSION;
 		p1->exchange_type = ISAKMP_EXCHANGE_AGGRESSIVE;
 		p1->payload = l = make_our_sa_ike();
@@ -810,31 +1172,54 @@
 		l = l->next->next;
 		l->next = new_isakmp_payload(ISAKMP_PAYLOAD_ID);
 		l = l->next;
-		l->u.id.type = ISAKMP_IPSEC_ID_KEY_ID;
+		if (opt_vendor == VENDOR_CISCO)
+			l->u.id.type = ISAKMP_IPSEC_ID_KEY_ID;
+		else
+			l->u.id.type = ISAKMP_IPSEC_ID_USER_FQDN;
 		l->u.id.protocol = IPPROTO_UDP;
-		l->u.id.port = ntohs(local_port);
+		l->u.id.port = ISAKMP_PORT; /* this must be 500, see rfc2407, 4.6.2 */
+#ifdef NORTELVPN
 		l->u.id.length = 24;
 		l->u.id.data = xallocc(l->u.id.length);
 		gcry_md_hash_buffer(GCRY_MD_SHA1, l->u.id.data, key_id, strlen(key_id));
 		/* memcpy(l->u.id.data, key_id, strlen(key_id)); */
+#else
+		l->u.id.length = strlen(key_id);
+		l->u.id.data = xallocc(l->u.id.length);
+		memcpy(l->u.id.data, key_id, strlen(key_id));
+#endif
 		l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID,
-			xauth_vid, sizeof(xauth_vid));
+			VID_XAUTH, sizeof(VID_XAUTH));
 		l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID,
-			unity_vid, sizeof(unity_vid));
-		if (!config[CONFIG_DISABLE_NATT])
+			VID_UNITY, sizeof(VID_UNITY));
+		if ((opt_natt_mode == NATT_NORMAL) || (opt_natt_mode == NATT_FORCE)) {
 			l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID,
-				natt_vid, sizeof(natt_vid));
-#if 0
-		l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID,
-			dpd_vid, sizeof(dpd_vid));
-#endif
+				VID_NATT_RFC, sizeof(VID_NATT_RFC));
+			l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID,
+				VID_NATT_02N, sizeof(VID_NATT_02N));
+			l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID,
+				VID_NATT_02, sizeof(VID_NATT_02));
+			l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID,
+				VID_NATT_01, sizeof(VID_NATT_01));
+			l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID,
+				VID_NATT_00, sizeof(VID_NATT_00));
+		}
+		s->ike.dpd_idle = atoi(config[CONFIG_DPD_IDLE]);
+		if (s->ike.dpd_idle != 0) {
+			if (s->ike.dpd_idle < 10)
+				s->ike.dpd_idle = 10;
+			if (s->ike.dpd_idle > 86400)
+				s->ike.dpd_idle = 86400;
+			l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID,
+				VID_DPD, sizeof(VID_DPD));
+		}
 		flatten_isakmp_packet(p1, &pkt, &pkt_len, 0);
 
 		/* Now, send that packet and receive a new one.  */
-		r_length = sendrecv(r_packet, sizeof(r_packet), pkt, pkt_len, 0);
+		r_length = sendrecv(s, r_packet, sizeof(r_packet), pkt, pkt_len, 0);
 		free(pkt);
 	}
-	DEBUG(2, printf("S4.4\n"));
+	DEBUGTOP(2, printf("S4.4 AM_packet2\n"));
 	/* Decode the recieved packet.  */
 	{
 		struct isakmp_packet *r;
@@ -843,19 +1228,28 @@
 		struct isakmp_payload *nonce = NULL;
 		struct isakmp_payload *ke = NULL;
 		struct isakmp_payload *hash = NULL;
+		struct isakmp_payload *last_cert = NULL;
+		struct isakmp_payload *sig = NULL;
 		struct isakmp_payload *idp = NULL;
 		int seen_sa = 0, seen_xauth_vid = 0;
+		unsigned char *psk_skeyid;
 		unsigned char *skeyid;
 		gcry_md_hd_t skeyid_ctx;
 
+#ifdef OPENSSL_GPL_VIOLATION
+		X509 *current_cert;
+		/* structure to store the certificate chain */
+		STACK_OF(X509) *cert_stack = sk_X509_new_null();
+#endif /* OPENSSL_GPL_VIOLATION */
+
 		reject = 0;
 		r = parse_isakmp_packet(r_packet, r_length, &reject);
 
 		/* Verify the correctness of the recieved packet.  */
-		if (reject == 0 && memcmp(r->i_cookie, s->i_cookie, ISAKMP_COOKIE_LENGTH) != 0)
+		if (reject == 0 && memcmp(r->i_cookie, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH) != 0)
 			reject = ISAKMP_N_INVALID_COOKIE;
 		if (reject == 0)
-			memcpy(s->r_cookie, r->r_cookie, ISAKMP_COOKIE_LENGTH);
+			memcpy(s->ike.r_cookie, r->r_cookie, ISAKMP_COOKIE_LENGTH);
 		if (reject == 0 && r->exchange_type != ISAKMP_EXCHANGE_AGGRESSIVE)
 			reject = ISAKMP_N_INVALID_EXCHANGE_TYPE;
 		if (reject == 0 && r->flags != 0)
@@ -863,7 +1257,7 @@
 		if (reject == 0 && r->message_id != 0)
 			reject = ISAKMP_N_INVALID_MESSAGE_ID;
 		if (reject != 0)
-			error(1, 0, "response was invalid [1]: %s", isakmp_notify_to_error(reject));
+			error(1, 0, "response was invalid [1]: %s(%d)", val_to_string(reject, isakmp_notify_enum_array), reject);
 		for (rp = r->payload; rp && reject == 0; rp = rp->next)
 			switch (rp->type) {
 			case ISAKMP_PAYLOAD_SA:
@@ -932,8 +1326,17 @@
 								reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
 							break;
 						case IKE_ATTRIB_LIFE_TYPE:
+							/* lifetime duration MUST follow lifetype attribute */
+							if (a->next->type == IKE_ATTRIB_LIFE_DURATION) {
+								lifetime_ike_process(s, a);
+							} else
+								reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+							break;
 						case IKE_ATTRIB_LIFE_DURATION:
+							/* already processed above in IKE_ATTRIB_LIFE_TYPE: */
+#ifdef NORTELVPN
 						case 32767:
+#endif
 							break;
 						default:
 							DEBUG(1, printf
@@ -957,14 +1360,14 @@
 
 					if (reject == 0) {
 						seen_sa = 1;
-						s->auth_algo = seen_auth;
-						s->cry_algo =
+						s->ike.auth_algo = seen_auth;
+						s->ike.cry_algo =
 							get_algo(SUPP_ALGO_CRYPT, SUPP_ALGO_IKE_SA,
 							seen_enc, NULL, seen_keylen)->my_id;
-						s->md_algo =
+						s->ike.md_algo =
 							get_algo(SUPP_ALGO_HASH, SUPP_ALGO_IKE_SA,
 							seen_hash, NULL, 0)->my_id;
-						s->md_len = gcry_md_get_algo_dlen(s->md_algo);
+						s->ike.md_len = gcry_md_get_algo_dlen(s->ike.md_algo);
 						DEBUG(1, printf("IKE SA selected %s-%s-%s\n",
 								get_algo(SUPP_ALGO_AUTH,
 									SUPP_ALGO_IKE_SA, seen_auth,
@@ -975,6 +1378,12 @@
 								get_algo(SUPP_ALGO_HASH,
 									SUPP_ALGO_IKE_SA, seen_hash,
 									NULL, 0)->name));
+						if (s->ike.cry_algo == GCRY_CIPHER_DES && !opt_1des) {
+							error(1, 0, "peer selected (single) DES as \"encryption\" method.\n"
+								"This algorithm is considered too weak today\n"
+								"If your vpn concentrator admin still insists on using DES\n"
+								"use the \"--enable-1des\" option.\n");
+						}
 					}
 				}
 				break;
@@ -991,55 +1400,113 @@
 			case ISAKMP_PAYLOAD_HASH:
 				hash = rp;
 				break;
+			case ISAKMP_PAYLOAD_CERT:
+				last_cert = rp;
+				if (last_cert->u.cert.encoding == ISAKMP_CERT_X509_SIG) {
+#ifdef OPENSSL_GPL_VIOLATION
+					/* convert the certificate to an openssl-X509 structure and push it onto the chain stack */
+					current_cert = d2i_X509(NULL, (const unsigned char **)&last_cert->u.cert.data, last_cert->u.cert.length);
+					sk_X509_push(cert_stack, current_cert);
+					last_cert->u.cert.data -= last_cert->u.cert.length; /* 'rewind' the pointer */
+#endif /* OPENSSL_GPL_VIOLATION */
+				}
+				break;
+			case ISAKMP_PAYLOAD_SIG:
+				sig = rp;
+				break;
 			case ISAKMP_PAYLOAD_VID:
-				if (rp->u.vid.length == sizeof(xauth_vid)
-					&& memcmp(rp->u.vid.data, xauth_vid,
-						sizeof(xauth_vid)) == 0)
+				if (rp->u.vid.length == sizeof(VID_XAUTH)
+					&& memcmp(rp->u.vid.data, VID_XAUTH,
+						sizeof(VID_XAUTH)) == 0) {
 					seen_xauth_vid = 1;
-				else if (rp->u.vid.length == sizeof(natt_vid)
-					&& memcmp(rp->u.vid.data, natt_vid,
-						sizeof(natt_vid)) == 0)
+				} else if (rp->u.vid.length == sizeof(VID_NATT_RFC)
+					&& memcmp(rp->u.vid.data, VID_NATT_RFC,
+						sizeof(VID_NATT_RFC)) == 0) {
 					seen_natt_vid = 1;
+					if (natt_draft < 1) natt_draft = 2;
+					DEBUG(2, printf("peer is NAT-T capable (RFC 3947)\n"));
+				} else if (rp->u.vid.length == sizeof(VID_NATT_02N)
+					&& memcmp(rp->u.vid.data, VID_NATT_02N,
+						sizeof(VID_NATT_02N)) == 0) {
+					seen_natt_vid = 1;
+					if (natt_draft < 1) natt_draft = 2;
+					DEBUG(2, printf("peer is NAT-T capable (draft-02)\\n\n")); /* sic! */
+				} else if (rp->u.vid.length == sizeof(VID_NATT_02)
+					&& memcmp(rp->u.vid.data, VID_NATT_02,
+						sizeof(VID_NATT_02)) == 0) {
+					seen_natt_vid = 1;
+					if (natt_draft < 1) natt_draft = 2;
+					DEBUG(2, printf("peer is NAT-T capable (draft-02)\n"));
+				} else if (rp->u.vid.length == sizeof(VID_NATT_01)
+					&& memcmp(rp->u.vid.data, VID_NATT_01,
+						sizeof(VID_NATT_01)) == 0) {
+					seen_natt_vid = 1;
+					if (natt_draft < 1) natt_draft = 1;
+					DEBUG(2, printf("peer is NAT-T capable (draft-01)\n"));
+				} else if (rp->u.vid.length == sizeof(VID_NATT_00)
+					&& memcmp(rp->u.vid.data, VID_NATT_00,
+						sizeof(VID_NATT_00)) == 0) {
+					seen_natt_vid = 1;
+					if (natt_draft < 0) natt_draft = 0;
+					DEBUG(2, printf("peer is NAT-T capable (draft-00)\n"));
+				} else if (rp->u.vid.length == sizeof(VID_DPD)
+					&& memcmp(rp->u.vid.data, VID_DPD,
+						sizeof(VID_DPD)) == 0) {
+					if (s->ike.dpd_idle != 0) {
+						gcry_create_nonce(&s->ike.dpd_seqno, sizeof(s->ike.dpd_seqno));
+						s->ike.dpd_seqno &= 0x7FFFFFFF;
+						s->ike.dpd_seqno_ack = s->ike.dpd_seqno;
+						s->ike.do_dpd = 1;
+						DEBUG(2, printf("peer is DPD capable (RFC3706)\n"));
+					} else {
+						DEBUG(2, printf("ignoring that peer is DPD capable (RFC3706)\n"));
+					}
+				} else {
+					hex_dump("unknown ISAKMP_PAYLOAD_VID",
+						rp->u.vid.data, rp->u.vid.length, NULL);
+				}
 				break;
 			case ISAKMP_PAYLOAD_NAT_D_OLD:
 			case ISAKMP_PAYLOAD_NAT_D:
 				natd_type = rp->type;
-				DEBUG(2, printf("peer is using type %d for NAT-Discovery payloads\n", natd_type));
-				if (!seen_sa || !seen_natt_vid) {
+				DEBUG(2, printf("peer is using type %d%s for NAT-Discovery payloads\n",
+					natd_type, val_to_string(natd_type, isakmp_payload_enum_array)));
+				if (!seen_sa /*|| !seen_natt_vid*/) {
 					reject = ISAKMP_N_INVALID_PAYLOAD_TYPE;
-				} else if (config[CONFIG_DISABLE_NATT]) {
+				} else if (opt_natt_mode == NATT_NONE) {
 					;
-				} else if (rp->u.natd.length != s->md_len) {
+				} else if (rp->u.natd.length != s->ike.md_len) {
 					reject = ISAKMP_N_PAYLOAD_MALFORMED;
 				} else if (seen_natd == 0) {
 					gcry_md_hd_t hm;
-					natd_us = xallocc(s->md_len);
-					natd_them = xallocc(s->md_len);
-					memcpy(natd_us, rp->u.natd.data, s->md_len);
-					gcry_md_open(&hm, s->md_algo, 0);
-					gcry_md_write(hm, s->i_cookie, ISAKMP_COOKIE_LENGTH);
-					gcry_md_write(hm, s->r_cookie, ISAKMP_COOKIE_LENGTH);
-					gcry_md_write(hm, &((struct sockaddr_in *)dest_addr)->sin_addr,
-						sizeof(struct in_addr));
-					gcry_md_write(hm, &((struct sockaddr_in *)dest_addr)->sin_port,
-						sizeof(uint16_t));
+					uint16_t n_dst_port = htons(s->ike.dst_port);
+					
+					natd_us = xallocc(s->ike.md_len);
+					natd_them = xallocc(s->ike.md_len);
+					memcpy(natd_us, rp->u.natd.data, s->ike.md_len);
+					gcry_md_open(&hm, s->ike.md_algo, 0);
+					gcry_md_write(hm, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH);
+					gcry_md_write(hm, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH);
+					gcry_md_write(hm, &s->dst, sizeof(struct in_addr));
+					gcry_md_write(hm, &n_dst_port, sizeof(uint16_t));
 					gcry_md_final(hm);
-					memcpy(natd_them, gcry_md_read(hm, 0), s->md_len);
+					memcpy(natd_them, gcry_md_read(hm, 0), s->ike.md_len);
 					gcry_md_close(hm);
 					seen_natd = 1;
 				} else {
-					if (memcmp(natd_them, rp->u.natd.data, s->md_len) == 0)
+					if (memcmp(natd_them, rp->u.natd.data, s->ike.md_len) == 0)
 						seen_natd_them = 1;
 				}
 				break;
 			default:
+				DEBUG(1, printf("rejecting invalid payload type %d\n", rp->type));
 				reject = ISAKMP_N_INVALID_PAYLOAD_TYPE;
 				break;
 			}
 
 		if (reject == 0) {
-			gcry_cipher_algo_info(s->cry_algo, GCRYCTL_GET_BLKLEN, NULL, &(s->ivlen));
-			gcry_cipher_algo_info(s->cry_algo, GCRYCTL_GET_KEYLEN, NULL, &(s->keylen));
+			gcry_cipher_algo_info(s->ike.cry_algo, GCRYCTL_GET_BLKLEN, NULL, &(s->ike.ivlen));
+			gcry_cipher_algo_info(s->ike.cry_algo, GCRYCTL_GET_KEYLEN, NULL, &(s->ike.keylen));
 		}
 
 		if (reject == 0 && (ke == NULL || ke->u.ke.length != dh_getlen(dh_grp)))
@@ -1047,16 +1514,29 @@
 		if (reject == 0 && nonce == NULL)
 			reject = ISAKMP_N_INVALID_HASH_INFORMATION;
 		if (reject != 0)
-			error(1, 0, "response was invalid [2]: %s", isakmp_notify_to_error(reject));
+			error(1, 0, "response was invalid [2]: %s(%d)", val_to_string(reject, isakmp_notify_enum_array), reject);
 		if (reject == 0 && idp == NULL)
 			reject = ISAKMP_N_INVALID_ID_INFORMATION;
-		if (reject == 0 && (hash == NULL || hash->u.hash.length != s->md_len))
+  
+		/* Decide if signature or hash is expected (sig only if vpnc is initiator of hybrid-auth */
+		if (reject == 0 && opt_auth_mode == AUTH_MODE_PSK && (hash == NULL || hash->u.hash.length != s->ike.md_len))
 			reject = ISAKMP_N_INVALID_HASH_INFORMATION;
+		if (reject == 0 && sig == NULL &&
+			(opt_auth_mode == AUTH_MODE_CERT ||
+			 opt_auth_mode == AUTH_MODE_HYBRID))
+			reject = ISAKMP_N_INVALID_SIGNATURE;
 		if (reject != 0)
-			error(1, 0, "response was invalid [3]: %s", isakmp_notify_to_error(reject));
-
+			error(1, 0, "response was invalid [3]: %s(%d)", val_to_string(reject, isakmp_notify_enum_array), reject);
+
+		/* Determine the shared secret.  */
+		dh_shared_secret = xallocc(dh_getlen(dh_grp));
+		dh_create_shared(dh_grp, dh_shared_secret, ke->u.ke.data);
+		hex_dump("dh_shared_secret", dh_shared_secret, dh_getlen(dh_grp), NULL);
 		/* Generate SKEYID.  */
 		{
+#ifdef NORTELVPN
+
+			
 			char shared_key_sha1[20];
 			gcry_md_hd_t hm;
 			uint8_t *hmac;
@@ -1068,16 +1548,65 @@
 			gcry_md_write(hm, key_id, strlen(key_id));
 			gcry_md_final(hm);
 			hmac = gcry_md_read(hm, 0);
-
-			gcry_md_open(&skeyid_ctx, s->md_algo, GCRY_MD_FLAG_HMAC);
+				
+			gcry_md_open(&skeyid_ctx, s->ike.md_algo, GCRY_MD_FLAG_HMAC);
 			gcry_md_setkey(skeyid_ctx, hmac, 20);
+#else
+			gcry_md_open(&skeyid_ctx, s->ike.md_algo, GCRY_MD_FLAG_HMAC);
+			gcry_md_setkey(skeyid_ctx, shared_key, strlen(shared_key));
+#endif
+			
 			gcry_md_write(skeyid_ctx, i_nonce, sizeof(i_nonce));
 			gcry_md_write(skeyid_ctx, nonce->u.nonce.data, nonce->u.nonce.length);
 			gcry_md_final(skeyid_ctx);
+#ifndef NORTELVPN			
+			psk_skeyid = xallocc(s->ike.md_len);
+			memcpy(psk_skeyid, gcry_md_read(skeyid_ctx, 0), s->ike.md_len);
+			if (opt_debug < 99)
+				DEBUG(3, printf("(not dumping psk hash)\n"));
+			else
+				hex_dump("psk_skeyid", psk_skeyid, s->ike.md_len, NULL);
+			gcry_md_close(skeyid_ctx);
+			DEBUG(99, printf("shared-key: %s\n",shared_key));
+			
+			/* SKEYID - psk only */
+			if (s->ike.auth_algo == IKE_AUTH_PRESHARED ||
+				s->ike.auth_algo == IKE_AUTH_XAUTHInitPreShared ||
+				s->ike.auth_algo == IKE_AUTH_XAUTHRespPreShared) {
+				gcry_md_open(&skeyid_ctx, s->ike.md_algo, GCRY_MD_FLAG_HMAC);
+				gcry_md_setkey(skeyid_ctx, shared_key, strlen(shared_key));
+				gcry_md_write(skeyid_ctx, i_nonce, sizeof(i_nonce));
+				gcry_md_write(skeyid_ctx, nonce->u.nonce.data, nonce->u.nonce.length);
+				gcry_md_final(skeyid_ctx);
+			} else if (s->ike.auth_algo == IKE_AUTH_DSS ||
+				s->ike.auth_algo == IKE_AUTH_RSA_SIG ||
+				s->ike.auth_algo == IKE_AUTH_ECDSA_SIG ||
+				s->ike.auth_algo == IKE_AUTH_HybridInitRSA ||
+				s->ike.auth_algo == IKE_AUTH_HybridRespRSA || 
+				s->ike.auth_algo == IKE_AUTH_HybridInitDSS ||
+				s->ike.auth_algo == IKE_AUTH_HybridRespDSS ||
+				s->ike.auth_algo == IKE_AUTH_XAUTHInitDSS ||
+				s->ike.auth_algo == IKE_AUTH_XAUTHRespDSS ||
+				s->ike.auth_algo == IKE_AUTH_XAUTHInitRSA ||
+				s->ike.auth_algo == IKE_AUTH_XAUTHRespRSA) {
+				unsigned char *key;
+				int key_len;
+				key_len = sizeof(i_nonce) + nonce->u.nonce.length;
+				key = xallocc(key_len);
+				memcpy(key, i_nonce, sizeof(i_nonce));
+				memcpy(key + sizeof(i_nonce), nonce->u.nonce.data, nonce->u.nonce.length);
+				gcry_md_open(&skeyid_ctx, s->ike.md_algo, GCRY_MD_FLAG_HMAC);
+				gcry_md_setkey(skeyid_ctx, key, key_len);
+				gcry_md_write(skeyid_ctx, dh_shared_secret, dh_getlen(dh_grp));
+				gcry_md_final(skeyid_ctx);
+			} else
+				error(1, 0, "SKEYID could not be computed: %s", "the selected authentication method is not supported");
+#endif
 			skeyid = gcry_md_read(skeyid_ctx, 0);
-			hex_dump("skeyid", skeyid, s->md_len);
-
+			hex_dump("skeyid", skeyid, s->ike.md_len, NULL);
+#ifdef NORTELVPN
 			gcry_md_close(hm);
+#endif
 		}
 
 		/* Verify the hash.  */
@@ -1097,36 +1626,167 @@
 			flatten_isakmp_payload(idi, &idi_f, &idi_size);
 			flatten_isakmp_payload(idp, &idp_f, &idp_size);
 
-			gcry_md_open(&hm, s->md_algo, GCRY_MD_FLAG_HMAC);
-			gcry_md_setkey(hm, skeyid, s->md_len);
+			gcry_md_open(&hm, s->ike.md_algo, GCRY_MD_FLAG_HMAC);
+			gcry_md_setkey(hm, skeyid, s->ike.md_len);
 			gcry_md_write(hm, ke->u.ke.data, ke->u.ke.length);
 			gcry_md_write(hm, dh_public, dh_getlen(dh_grp));
-			gcry_md_write(hm, s->r_cookie, ISAKMP_COOKIE_LENGTH);
-			gcry_md_write(hm, s->i_cookie, ISAKMP_COOKIE_LENGTH);
+			gcry_md_write(hm, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH);
+			gcry_md_write(hm, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH);
 			gcry_md_write(hm, sa_f + 4, sa_size - 4);
 			gcry_md_write(hm, idp_f + 4, idp_size - 4);
 			gcry_md_final(hm);
 			expected_hash = gcry_md_read(hm, 0);
-
-			if (memcmp(expected_hash, hash->u.hash.data, s->md_len) != 0) {
-				error(1, 0, "hash comparison failed: %s\ncheck group password!",
-					isakmp_notify_to_error(ISAKMP_N_AUTHENTICATION_FAILED));
+			hex_dump("expected hash", expected_hash, s->ike.md_len, NULL);
+
+			if (opt_auth_mode == AUTH_MODE_PSK) {
+				if (memcmp(expected_hash, hash->u.hash.data, s->ike.md_len) != 0)
+					error(2, 0, "hash comparison failed: %s(%d)\ncheck group password!",
+						val_to_string(ISAKMP_N_AUTHENTICATION_FAILED, isakmp_notify_enum_array),
+						ISAKMP_N_AUTHENTICATION_FAILED);
+				hex_dump("received hash", hash->u.hash.data, hash->u.hash.length, NULL);
+			} else if (opt_auth_mode == AUTH_MODE_CERT ||
+				opt_auth_mode == AUTH_MODE_HYBRID) {
+#ifdef OPENSSL_GPL_VIOLATION
+				
+				/* BEGIN - check the signature using OpenSSL */
+			
+				X509 		*x509;
+				EVP_PKEY 	*pkey;
+				RSA 		*rsa;
+				X509_STORE 	*store;
+				/* X509_LOOKUP	*lookup; */
+				X509_STORE_CTX	*verify_ctx;
+				unsigned char	*rec_hash;
+				int		decr_size;
+	
+			  	hex_dump("received signature", sig->u.sig.data, sig->u.sig.length, NULL);
+				OpenSSL_add_all_ciphers();
+				OpenSSL_add_all_digests();
+				OpenSSL_add_all_algorithms();
+	
+				ERR_load_crypto_strings();
+	
+				hex_dump("last cert", last_cert->u.cert.data, last_cert->u.cert.length, NULL);
+				x509 = d2i_X509(NULL, (const unsigned char **)&last_cert->u.cert.data, last_cert->u.cert.length);
+				if (x509 == NULL) {
+					ERR_print_errors_fp (stderr);
+					error(1, 0, "x509 error\n");
+				}
+				DEBUG(3, printf("Subject name hash: %08lx\n",X509_subject_name_hash(x509)));
+	
+				/* BEGIN - verify certificate chain */
+				/* create the cert store */
+				if (!(store = X509_STORE_new())) {
+					error(1, 0, "Error creating X509_STORE object\n");
+				}
+				/* load the CA certificates */
+				if (X509_STORE_load_locations (store, config[CONFIG_CA_FILE], config[CONFIG_CA_DIR]) != 1) {
+					error(1, 0, "Error loading the CA file or directory\n");
+				}
+				if (X509_STORE_set_default_paths (store) != 1) {
+					error(1, 0, "Error loading the system-wide CA certificates\n");
+				}
+
+#if 0
+				/* check CRLs */
+				/* add the corresponding CRL for each CA in the chain to the lookup */
+#define CRL_FILE "root-ca-crl.crl.pem"
+
+				if (!(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()))) {
+					error(1, 0, "Error creating X509 lookup object.\n");
+				}
+				if (X509_load_crl_file(lookup, CRL_FILE, X509_FILETYPE_PEM) != 1) {
+					ERR_print_errors_fp(stderr);
+					error(1, 0, "Error reading CRL file\n");
+				}
+				X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
+#endif /* 0 */
+				/* create a verification context and initialize it */
+				if (!(verify_ctx = X509_STORE_CTX_new ())) {
+					error(1, 0, "Error creating X509_STORE_CTX object\n");
+				}
+				/* X509_STORE_CTX_init did not return an error condition
+				in prior versions */
+				if (X509_STORE_CTX_init (verify_ctx, store, x509, cert_stack) != 1)
+					printf("Error intializing verification context\n");
+	
+				/* verify the certificate */
+				if (X509_verify_cert(verify_ctx) != 1) {
+					ERR_print_errors_fp(stderr);
+					error(2, 0, "Error verifying the certificate-chain\n");
+				} else
+					DEBUG(3, printf("Certificate-chain verified correctly!\n"));
+	
+				/* END   - verify certificate chain */
+	
+	
+				/* BEGIN - Signature Verification */
+				pkey = X509_get_pubkey(x509);
+				if (pkey == NULL) {
+					ERR_print_errors_fp (stderr);
+					exit (1);
+				}
+	
+				rsa = EVP_PKEY_get1_RSA(pkey);
+				if (rsa == NULL) {
+					ERR_print_errors_fp (stderr);
+					exit (1);
+				}
+				rec_hash = xallocc(s->ike.md_len);
+				decr_size = RSA_public_decrypt(sig->u.sig.length, sig->u.sig.data, rec_hash, rsa, RSA_PKCS1_PADDING);
+	
+				if (decr_size != (int) s->ike.md_len) {
+					printf("Decrypted-Size: %d\n",decr_size);
+					hex_dump("    decr_hash", rec_hash, decr_size, NULL);
+					hex_dump("expected hash", expected_hash, s->ike.md_len, NULL);
+				
+					error(2, 0, "The hash-value, which was decrypted from the received signature, and the expected hash-value differ in size.\n");
+				} else {
+					if (memcmp(rec_hash, expected_hash, decr_size) != 0) {
+						printf("Decrypted-Size: %d\n",decr_size);
+						hex_dump("    decr_hash", rec_hash, decr_size, NULL);
+						hex_dump("expected hash", expected_hash, s->ike.md_len, NULL);
+	
+						error(2, 0, "The hash-value, which was decrypted from the received signature, and the expected hash-value differ.\n");
+					} else {
+						DEBUG(3, printf("Signature MATCH!!\n"));
+					}
+				}
+				/* END - Signature Verification */
+	
+				EVP_PKEY_free(pkey);
+				free(rec_hash);
+	
+				/* END   - check the signature using OpenSSL */
+#endif /* OPENSSL_GPL_VIOLATION */
 			}
+
 			gcry_md_close(hm);
 
-			gcry_md_open(&hm, s->md_algo, GCRY_MD_FLAG_HMAC);
-			gcry_md_setkey(hm, skeyid, s->md_len);
+			gcry_md_open(&hm, s->ike.md_algo, GCRY_MD_FLAG_HMAC);
+			gcry_md_setkey(hm, skeyid, s->ike.md_len);
 			gcry_md_write(hm, dh_public, dh_getlen(dh_grp));
 			gcry_md_write(hm, ke->u.ke.data, ke->u.ke.length);
-			gcry_md_write(hm, s->i_cookie, ISAKMP_COOKIE_LENGTH);
-			gcry_md_write(hm, s->r_cookie, ISAKMP_COOKIE_LENGTH);
+			gcry_md_write(hm, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH);
+			gcry_md_write(hm, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH);
 			gcry_md_write(hm, sa_f + 4, sa_size - 4);
 			gcry_md_write(hm, idi_f + 4, idi_size - 4);
 			gcry_md_final(hm);
-			returned_hash = xallocc(s->md_len);
-			memcpy(returned_hash, gcry_md_read(hm, 0), s->md_len);
+			returned_hash = xallocc(s->ike.md_len);
+			memcpy(returned_hash, gcry_md_read(hm, 0), s->ike.md_len);
 			gcry_md_close(hm);
-			hex_dump("returned_hash", returned_hash, s->md_len);
+			hex_dump("returned_hash", returned_hash, s->ike.md_len, NULL);
+
+			/* PRESHARED_KEY_HASH */
+			gcry_md_open(&hm, s->ike.md_algo, GCRY_MD_FLAG_HMAC);
+			gcry_md_setkey(hm, skeyid, s->ike.md_len);
+			gcry_md_write(hm, shared_key, strlen(shared_key));
+			gcry_md_final(hm);
+			psk_hash = xallocc(s->ike.md_len);
+			memcpy(psk_hash, gcry_md_read(hm, 0), s->ike.md_len);
+			gcry_md_close(hm);
+			hex_dump("psk_hash", psk_hash, s->ike.md_len, NULL);
+			/* End PRESHARED_KEY_HASH */
 
 			free(sa_f);
 			free(idi);
@@ -1144,141 +1804,175 @@
 			/* Determine the shared secret.  */
 			dh_shared_secret = xallocc(dh_getlen(dh_grp));
 			dh_create_shared(dh_grp, dh_shared_secret, ke->u.ke.data);
-			hex_dump("dh_shared_secret", dh_shared_secret, dh_getlen(dh_grp));
-
-			gcry_md_open(&hm, s->md_algo, GCRY_MD_FLAG_HMAC);
-			gcry_md_setkey(hm, skeyid, s->md_len);
+			hex_dump("dh_shared_secret", dh_shared_secret, dh_getlen(dh_grp), NULL);
+
+			gcry_md_open(&hm, s->ike.md_algo, GCRY_MD_FLAG_HMAC);
+			gcry_md_setkey(hm, skeyid, s->ike.md_len);
 			gcry_md_write(hm, dh_shared_secret, dh_getlen(dh_grp));
-			gcry_md_write(hm, s->i_cookie, ISAKMP_COOKIE_LENGTH);
-			gcry_md_write(hm, s->r_cookie, ISAKMP_COOKIE_LENGTH);
+			gcry_md_write(hm, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH);
+			gcry_md_write(hm, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH);
 			gcry_md_write(hm, c012 + 0, 1);
 			gcry_md_final(hm);
-			s->skeyid_d = xallocc(s->md_len);
-			memcpy(s->skeyid_d, gcry_md_read(hm, 0), s->md_len);
+			s->ike.skeyid_d = xallocc(s->ike.md_len);
+			memcpy(s->ike.skeyid_d, gcry_md_read(hm, 0), s->ike.md_len);
 			gcry_md_close(hm);
-			hex_dump("skeyid_d", s->skeyid_d, s->md_len);
-
-			gcry_md_open(&hm, s->md_algo, GCRY_MD_FLAG_HMAC);
-			gcry_md_setkey(hm, skeyid, s->md_len);
-			gcry_md_write(hm, s->skeyid_d, s->md_len);
+			hex_dump("skeyid_d", s->ike.skeyid_d, s->ike.md_len, NULL);
+
+			gcry_md_open(&hm, s->ike.md_algo, GCRY_MD_FLAG_HMAC);
+			gcry_md_setkey(hm, skeyid, s->ike.md_len);
+			gcry_md_write(hm, s->ike.skeyid_d, s->ike.md_len);
 			gcry_md_write(hm, dh_shared_secret, dh_getlen(dh_grp));
-			gcry_md_write(hm, s->i_cookie, ISAKMP_COOKIE_LENGTH);
-			gcry_md_write(hm, s->r_cookie, ISAKMP_COOKIE_LENGTH);
+			gcry_md_write(hm, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH);
+			gcry_md_write(hm, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH);
 			gcry_md_write(hm, c012 + 1, 1);
 			gcry_md_final(hm);
-			s->skeyid_a = xallocc(s->md_len);
-			memcpy(s->skeyid_a, gcry_md_read(hm, 0), s->md_len);
+			s->ike.skeyid_a = xallocc(s->ike.md_len);
+			memcpy(s->ike.skeyid_a, gcry_md_read(hm, 0), s->ike.md_len);
 			gcry_md_close(hm);
-			hex_dump("skeyid_a", s->skeyid_a, s->md_len);
-
-			gcry_md_open(&hm, s->md_algo, GCRY_MD_FLAG_HMAC);
-			gcry_md_setkey(hm, skeyid, s->md_len);
-			gcry_md_write(hm, s->skeyid_a, s->md_len);
+			hex_dump("skeyid_a", s->ike.skeyid_a, s->ike.md_len, NULL);
+
+			gcry_md_open(&hm, s->ike.md_algo, GCRY_MD_FLAG_HMAC);
+			gcry_md_setkey(hm, skeyid, s->ike.md_len);
+			gcry_md_write(hm, s->ike.skeyid_a, s->ike.md_len);
 			gcry_md_write(hm, dh_shared_secret, dh_getlen(dh_grp));
-			gcry_md_write(hm, s->i_cookie, ISAKMP_COOKIE_LENGTH);
-			gcry_md_write(hm, s->r_cookie, ISAKMP_COOKIE_LENGTH);
+			gcry_md_write(hm, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH);
+			gcry_md_write(hm, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH);
 			gcry_md_write(hm, c012 + 2, 1);
 			gcry_md_final(hm);
-			skeyid_e = xallocc(s->md_len);
-			memcpy(skeyid_e, gcry_md_read(hm, 0), s->md_len);
+			skeyid_e = xallocc(s->ike.md_len);
+			memcpy(skeyid_e, gcry_md_read(hm, 0), s->ike.md_len);
 			gcry_md_close(hm);
-			hex_dump("skeyid_e", skeyid_e, s->md_len);
+			hex_dump("skeyid_e", skeyid_e, s->ike.md_len, NULL);
 
 			memset(dh_shared_secret, 0, sizeof(dh_shared_secret));
 
 			/* Determine the IKE encryption key.  */
-			s->key = xallocc(s->keylen);
-
-			if (s->keylen > s->md_len) {
-				for (i = 0; i * s->md_len < s->keylen; i++) {
-					gcry_md_open(&hm, s->md_algo, GCRY_MD_FLAG_HMAC);
-					gcry_md_setkey(hm, skeyid_e, s->md_len);
+			s->ike.key = xallocc(s->ike.keylen);
+
+			if (s->ike.keylen > s->ike.md_len) {
+				for (i = 0; i * s->ike.md_len < s->ike.keylen; i++) {
+					gcry_md_open(&hm, s->ike.md_algo, GCRY_MD_FLAG_HMAC);
+					gcry_md_setkey(hm, skeyid_e, s->ike.md_len);
 					if (i == 0)
 						gcry_md_write(hm, "" /* &'\0' */ , 1);
 					else
-						gcry_md_write(hm, s->key + (i - 1) * s->md_len,
-							s->md_len);
+						gcry_md_write(hm, s->ike.key + (i - 1) * s->ike.md_len,
+							s->ike.md_len);
 					gcry_md_final(hm);
-					memcpy(s->key + i * s->md_len, gcry_md_read(hm, 0),
-						min(s->md_len, s->keylen - i * s->md_len));
+					memcpy(s->ike.key + i * s->ike.md_len, gcry_md_read(hm, 0),
+						min(s->ike.md_len, s->ike.keylen - i * s->ike.md_len));
 					gcry_md_close(hm);
 				}
 			} else { /* keylen <= md_len */
-				memcpy(s->key, skeyid_e, s->keylen);
+				memcpy(s->ike.key, skeyid_e, s->ike.keylen);
 			}
-			hex_dump("enc-key", s->key, s->keylen);
-
-			memset(skeyid_e, 0, s->md_len);
+			hex_dump("enc-key", s->ike.key, s->ike.keylen, NULL);
+
+			memset(skeyid_e, 0, s->ike.md_len);
 		}
 
 		/* Determine the initial IV.  */
 		{
 			gcry_md_hd_t hm;
 
-			assert(s->ivlen <= s->md_len);
-			gcry_md_open(&hm, s->md_algo, 0);
+			assert(s->ike.ivlen <= s->ike.md_len);
+			gcry_md_open(&hm, s->ike.md_algo, 0);
 			gcry_md_write(hm, dh_public, dh_getlen(dh_grp));
 			gcry_md_write(hm, ke->u.ke.data, ke->u.ke.length);
 			gcry_md_final(hm);
-			s->current_iv = xallocc(s->ivlen);
-			memcpy(s->current_iv, gcry_md_read(hm, 0), s->ivlen);
+			s->ike.current_iv = xallocc(s->ike.ivlen);
+			memcpy(s->ike.current_iv, gcry_md_read(hm, 0), s->ike.ivlen);
 			gcry_md_close(hm);
-			hex_dump("current_iv", s->current_iv, s->ivlen);
-			memset(s->current_iv_msgid, 0, 4);
+			hex_dump("current_iv", s->ike.current_iv, s->ike.ivlen, NULL);
+			memset(s->ike.current_iv_msgid, 0, 4);
 		}
 
 		gcry_md_close(skeyid_ctx);
 	}
 
-	DEBUG(2, printf("S4.5\n"));
+	DEBUGTOP(2, printf("S4.5 AM_packet3\n"));
 	/* Send final phase 1 packet.  */
 	{
 		struct isakmp_packet *p2;
 		uint8_t *p2kt;
 		size_t p2kt_len;
 		struct isakmp_payload *pl;
+#if 0 /* cert support */
+#ifdef OPENSSL_GPL_VIOLATION
+		struct isakmp_payload *last_cert = NULL;
+		struct isakmp_payload *sig = NULL;
+
+
+		X509 *current_cert;
+		/* structure to store the certificate chain */
+		STACK_OF(X509) *cert_stack = sk_X509_new_null();
+#endif /* OPENSSL_GPL_VIOLATION */
+#endif /* 0 */
 
 		p2 = new_isakmp_packet();
-		memcpy(p2->i_cookie, s->i_cookie, ISAKMP_COOKIE_LENGTH);
-		memcpy(p2->r_cookie, s->r_cookie, ISAKMP_COOKIE_LENGTH);
+		memcpy(p2->i_cookie, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH);
+		memcpy(p2->r_cookie, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH);
 		p2->flags = ISAKMP_FLAG_E;
 		p2->isakmp_version = ISAKMP_VERSION;
 		p2->exchange_type = ISAKMP_EXCHANGE_AGGRESSIVE;
+	/* XXX CERT Add id(?), cert and sig here in case of cert auth */
 		p2->payload = new_isakmp_data_payload(ISAKMP_PAYLOAD_HASH,
-			returned_hash, s->md_len);
+			returned_hash, s->ike.md_len);
 		p2->payload->next = pl = new_isakmp_payload(ISAKMP_PAYLOAD_N);
 		pl->u.n.doi = ISAKMP_DOI_IPSEC;
 		pl->u.n.protocol = ISAKMP_IPSEC_PROTO_ISAKMP;
 		pl->u.n.type = ISAKMP_N_IPSEC_INITIAL_CONTACT;
 		pl->u.n.spi_length = 2 * ISAKMP_COOKIE_LENGTH;
 		pl->u.n.spi = xallocc(2 * ISAKMP_COOKIE_LENGTH);
-		memcpy(pl->u.n.spi + ISAKMP_COOKIE_LENGTH * 0, s->i_cookie, ISAKMP_COOKIE_LENGTH);
-		memcpy(pl->u.n.spi + ISAKMP_COOKIE_LENGTH * 1, s->r_cookie, ISAKMP_COOKIE_LENGTH);
+		memcpy(pl->u.n.spi + ISAKMP_COOKIE_LENGTH * 0, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH);
+		memcpy(pl->u.n.spi + ISAKMP_COOKIE_LENGTH * 1, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH);
+
+		/* send PSK-hash if hybrid authentication is negotiated */
+		if (s->ike.auth_algo == IKE_AUTH_HybridInitRSA ||
+			s->ike.auth_algo == IKE_AUTH_HybridInitDSS) {
+			/* Notify - PRESHARED_KEY_HASH */
+			pl = pl->next = new_isakmp_payload(ISAKMP_PAYLOAD_N);
+			pl->u.n.doi = ISAKMP_DOI_IPSEC;
+			pl->u.n.protocol = ISAKMP_IPSEC_PROTO_ISAKMP;
+			/* Notify Message - Type: PRESHARED_KEY_HASH */
+			pl->u.n.type =  ISAKMP_N_CISCO_PRESHARED_KEY_HASH;
+			pl->u.n.spi_length = 2 * ISAKMP_COOKIE_LENGTH;
+			pl->u.n.spi = xallocc(2 * ISAKMP_COOKIE_LENGTH);
+			memcpy(pl->u.n.spi + ISAKMP_COOKIE_LENGTH * 0,
+				s->ike.i_cookie, ISAKMP_COOKIE_LENGTH);
+			memcpy(pl->u.n.spi + ISAKMP_COOKIE_LENGTH * 1,
+				s->ike.r_cookie, ISAKMP_COOKIE_LENGTH);
+			pl->u.n.data_length = s->ike.md_len;
+			pl->u.n.data = xallocc(pl->u.n.data_length);
+			memcpy(pl->u.n.data, psk_hash, pl->u.n.data_length);
+			/* End Notify - PRESHARED_KEY_HASH */
+		}
 		pl = pl->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID,
-			unknown_vid, sizeof(unknown_vid));
+			VID_UNKNOWN, sizeof(VID_UNKNOWN));
 		pl = pl->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID,
-			unity_vid, sizeof(unity_vid));
+			VID_UNITY, sizeof(VID_UNITY));
 
 		/* include NAT traversal discovery payloads */
 		if (seen_natt_vid) {
 			assert(natd_type != 0);
 			pl = pl->next = new_isakmp_data_payload(natd_type,
-				natd_them, s->md_len);
+				natd_them, s->ike.md_len);
 			/* this could be repeated fo any known outbound interfaces */
 			{
 				gcry_md_hd_t hm;
-				struct sockaddr_in src_addr;
-				src_addr.sin_port=local_port;
-				find_local_addr((struct sockaddr_in *)dest_addr, &src_addr);
-				gcry_md_open(&hm, s->md_algo, 0);
-				gcry_md_write(hm, s->i_cookie, ISAKMP_COOKIE_LENGTH);
-				gcry_md_write(hm, s->r_cookie, ISAKMP_COOKIE_LENGTH);
-				gcry_md_write(hm, &src_addr.sin_addr, sizeof(struct in_addr));
-				gcry_md_write(hm, &local_port, sizeof(uint16_t));
+				uint16_t n_src_port = htons(s->ike.src_port);
+				
+				gcry_md_open(&hm, s->ike.md_algo, 0);
+				gcry_md_write(hm, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH);
+				gcry_md_write(hm, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH);
+				gcry_md_write(hm, &s->src, sizeof(struct in_addr));
+				gcry_md_write(hm, &n_src_port, sizeof(uint16_t));
 				gcry_md_final(hm);
 				pl = pl->next = new_isakmp_data_payload(natd_type,
-					gcry_md_read(hm, 0), s->md_len);
-				if (seen_natd && memcmp(natd_us, pl->u.ke.data, s->md_len) == 0)
+					gcry_md_read(hm, 0), s->ike.md_len);
+				if (opt_natt_mode == NATT_FORCE) /* force detection of "this end behind NAT" */
+					pl->u.ke.data[0] ^= 1; /* by flipping a bit in the nat-detection-hash */
+				if (seen_natd && memcmp(natd_us, pl->u.ke.data, s->ike.md_len) == 0)
 					seen_natd_us = 1;
 				gcry_md_close(hm);
 			}
@@ -1292,18 +1986,22 @@
 					seen_natd_us ? "no" : "YES", seen_natd_them ? "no" : "YES"));
 				switch (natd_type) {
 					case ISAKMP_PAYLOAD_NAT_D:
-						encap_mode = IPSEC_ENCAP_UDP_TUNNEL;
+						s->ipsec.encap_mode = IPSEC_ENCAP_UDP_TUNNEL;
 						break;
 					case ISAKMP_PAYLOAD_NAT_D_OLD:
-						encap_mode = IPSEC_ENCAP_UDP_TUNNEL_OLD;
+						s->ipsec.encap_mode = IPSEC_ENCAP_UDP_TUNNEL_OLD;
 						break;
 					default:
 						abort();
 				}
-				((struct sockaddr_in *)dest_addr)->sin_port = htons(4500);
-				if (local_port == htons(500)) {
-					close(sockfd);
-					sockfd = make_socket(local_port = htons(4500));
+				if (natt_draft >= 2) {
+					s->ipsec.natt_active_mode = NATT_ACTIVE_RFC;
+					close(s->ike_fd);
+					if (s->ike.src_port == ISAKMP_PORT)
+						s->ike.src_port = ISAKMP_PORT_NATT;
+					s->ike_fd = make_socket(s, s->ike.src_port, s->ike.dst_port = ISAKMP_PORT_NATT);
+				} else {
+					s->ipsec.natt_active_mode = NATT_ACTIVE_DRAFT_OLD;
 				}
 			} else {
 				DEBUG(1, printf("NAT status: NAT-T VID seen, no NAT device detected\n"));
@@ -1313,21 +2011,24 @@
 		}
 
 		
-		flatten_isakmp_packet(p2, &p2kt, &p2kt_len, s->ivlen);
+		flatten_isakmp_packet(p2, &p2kt, &p2kt_len, s->ike.ivlen);
 		free_isakmp_packet(p2);
 		isakmp_crypt(s, p2kt, p2kt_len, 1);
 
-		s->initial_iv = xallocc(s->ivlen);
-		memcpy(s->initial_iv, s->current_iv, s->ivlen);
-		hex_dump("initial_iv", s->initial_iv, s->ivlen);
+		s->ike.initial_iv = xallocc(s->ike.ivlen);
+		memcpy(s->ike.initial_iv, s->ike.current_iv, s->ike.ivlen);
+		hex_dump("initial_iv", s->ike.initial_iv, s->ike.ivlen, NULL);
 
 		/* Now, send that packet and receive a new one.  */
-		r_length = sendrecv(r_packet, sizeof(r_packet), p2kt, p2kt_len, 0);
+		r_length = sendrecv(s, r_packet, sizeof(r_packet), p2kt, p2kt_len, 0);
 		free(p2kt);
 	}
-	DEBUG(2, printf("S4.6\n"));
-
+	DEBUGTOP(2, printf("S4.6 cleanup\n"));
+
+	free_isakmp_packet(p1);
 	free(returned_hash);
+	free(dh_public);
+	group_free(dh_grp);
 }
 
 static int do_phase2_notice_check(struct sa_block *s, struct isakmp_packet **r_p)
@@ -1338,8 +2039,12 @@
 	while (1) {
 		reject = unpack_verify_phase2(s, r_packet, r_length, r_p, NULL, 0);
 		if (reject == ISAKMP_N_INVALID_COOKIE) {
-			r_length = sendrecv(r_packet, sizeof(r_packet), NULL, 0, 0);
+			r_length = sendrecv(s, r_packet, sizeof(r_packet), NULL, 0, 0);
 			continue;
+		}
+		if (*r_p == NULL) {
+			assert(reject != 0);
+			return reject;
 		}
 		r = *r_p;
 		
@@ -1347,34 +2052,47 @@
 		if (r->exchange_type == ISAKMP_EXCHANGE_INFORMATIONAL &&
 			r->payload->next != NULL) {
 			if (r->payload->next->type == ISAKMP_PAYLOAD_N) {
-					if (r->payload->next->u.n.type == ISAKMP_N_CISCO_LOAD_BALANCE) {
+				if (r->payload->next->u.n.type == ISAKMP_N_CISCO_LOAD_BALANCE) {
 					/* load balancing notice ==> restart with new gw */
 					if (r->payload->next->u.n.data_length != 4)
-					error(1, 0, "malformed loadbalance target");
-					memcpy(&((struct sockaddr_in *)dest_addr)->sin_addr,
-						r->payload->next->u.n.data, 4);
-					((struct sockaddr_in *)dest_addr)->sin_port = htons(ISAKMP_PORT);
-					encap_mode = IPSEC_ENCAP_TUNNEL;
-					if (local_port == htons(4500)) {
-						close(sockfd);
-						sockfd = make_socket(local_port = htons(500));
-					}
+						error(1, 0, "malformed loadbalance target");
+					s->dst = *(struct in_addr *)r->payload->next->u.n.data;
+					s->ike.dst_port = ISAKMP_PORT;
+					s->ipsec.encap_mode = IPSEC_ENCAP_TUNNEL;
+					s->ipsec.natt_active_mode = NATT_ACTIVE_NONE;
+					if (s->ike.src_port == ISAKMP_PORT_NATT)
+						s->ike.src_port = ISAKMP_PORT;
+					close(s->ike_fd);
+					s->ike_fd = make_socket(s, s->ike.src_port, s->ike.dst_port);
 					DEBUG(2, printf("got cisco loadbalancing notice, diverting to %s\n",
-							inet_ntoa(((struct sockaddr_in *)dest_addr)->
-								sin_addr)));
+							inet_ntoa(s->dst)));
 					return -1;
-				}
-				if (r->payload->next->u.n.type == ISAKMP_N_IPSEC_RESPONDER_LIFETIME) {
-					/* responder liftime notice ==> ignore */
-					DEBUG(2, printf("got responder liftime notice, ignoring..\n"));
-					r_length = sendrecv(r_packet, sizeof(r_packet), NULL, 0, 0);
+				} else if (r->payload->next->u.n.type == ISAKMP_N_IPSEC_RESPONDER_LIFETIME) {
+					if (r->payload->next->u.n.protocol == ISAKMP_IPSEC_PROTO_ISAKMP)
+						lifetime_ike_process(s, r->payload->next->u.n.attributes);
+					else if (r->payload->next->u.n.protocol == ISAKMP_IPSEC_PROTO_IPSEC_ESP)
+						lifetime_ipsec_process(s, r->payload->next->u.n.attributes);
+					else
+						DEBUG(2, printf("got unknown lifetime notice, ignoring..\n"));
+					r_length = sendrecv(s, r_packet, sizeof(r_packet), NULL, 0, 0);
 					continue;
+				} else if (r->payload->next->u.n.type == ISAKMP_N_IPSEC_INITIAL_CONTACT) {
+					/* why in hell do we get this?? */
+					DEBUG(2, printf("got initial contact notice, ignoring..\n"));
+					r_length = sendrecv(s, r_packet, sizeof(r_packet), NULL, 0, 0);
+					continue;
+				} else {
+					/* whatever */
+					printf("received notice of type %s(%d), giving up\n",
+						val_to_string(r->payload->next->u.n.type, isakmp_notify_enum_array),
+						r->payload->next->u.n.type);
+					return reject;
 				}
 			}
 			if (r->payload->next->type == ISAKMP_PAYLOAD_D) {
-				/* responder liftime notice ==> ignore */
+				/* delete notice ==> ignore */
 				DEBUG(2, printf("got delete for old connection, ignoring..\n"));
-				r_length = sendrecv(r_packet, sizeof(r_packet), NULL, 0, 0);
+				r_length = sendrecv(s, r_packet, sizeof(r_packet), NULL, 0, 0);
 				continue;
 			}
 		}
@@ -1384,29 +2102,32 @@
 	return reject;
 }
 
-static int do_phase_2_xauth(struct sa_block *s)
+static int do_phase2_xauth(struct sa_block *s)
 {
 	struct isakmp_packet *r;
 	int loopcount;
-
-	DEBUG(2, printf("S5.1\n"));
+	int reject;
+	int passwd_used = 0;
+
+	DEBUGTOP(2, printf("S5.1 xauth_start\n"));
 	/* This can go around for a while.  */
 	for (loopcount = 0;; loopcount++) {
-		int reject;
+#ifdef NORTELVPN
 		uint16_t xauth_type_requested = 5;
+#endif
 		struct isakmp_payload *rp;
 		struct isakmp_attribute *a, *ap, *reply_attr;
 		char ntop_buf[32];
 		int seen_answer = 0;
 
-		DEBUG(2, printf("S5.2\n"));
+		DEBUGTOP(2, printf("S5.2 notice_check\n"));
 		
 		/* recv and check for notices */
 		reject = do_phase2_notice_check(s, &r);
 		if (reject == -1)
 			return 1;
 		
-		DEBUG(2, printf("S5.3\n"));
+		DEBUGTOP(2, printf("S5.3 type-is-xauth check\n"));
 		/* Check the transaction type is OK.  */
 		if (reject == 0 && r->exchange_type != ISAKMP_EXCHANGE_MODECFG_TRANSACTION)
 			reject = ISAKMP_N_INVALID_EXCHANGE_TYPE;
@@ -1420,6 +2141,8 @@
 
 		if (reject == 0 && r->payload->next->u.modecfg.type == ISAKMP_MODECFG_CFG_SET)
 			break;
+
+#ifdef NORTELVPN
 		if (reject == 0 && r->payload->next->u.modecfg.type == 5)
 			break;
 		if (reject == 0 && r->payload->next->u.modecfg.type == 6)
@@ -1427,32 +2150,47 @@
 			printf("authentication failed\n");
 			break;
 		}
+#endif
+
 		if (reject == 0 && r->payload->next->u.modecfg.type != ISAKMP_MODECFG_CFG_REQUEST)
 			reject = ISAKMP_N_INVALID_PAYLOAD_TYPE;
 
 		if (reject != 0)
-			phase2_fatal(s, "expected xauth packet; rejected: %s", reject);
-
-		DEBUG(2, printf("S5.4\n"));
+			phase2_fatal(s, "expected xauth packet; rejected: %s(%d)", reject);
+
+		DEBUGTOP(2, printf("S5.4 xauth type check\n"));
 		a = r->payload->next->u.modecfg.attributes;
 		/* First, print any messages, and verify that we understand the
-		 * conversation.  */
+		 * conversation. This looks for any place were input is
+		 * required - in these cases, we need to print the prompt
+		 * regardless of whether the user requested interactive mode
+		 * or not. */
 		for (ap = a; ap && seen_answer == 0; ap = ap->next)
-			if (ap->type == ISAKMP_XAUTH_ATTRIB_ANSWER)
+			if (ap->type == ISAKMP_XAUTH_ATTRIB_ANSWER
+			    || ap->type == ISAKMP_XAUTH_ATTRIB_NEXT_PIN
+			    || ap->type == ISAKMP_XAUTH_ATTRIB_PASSCODE)
 				seen_answer = 1;
 
 		for (ap = a; ap && reject == 0; ap = ap->next)
 			switch (ap->type) {
 			case ISAKMP_XAUTH_ATTRIB_TYPE:
+
+#ifdef NORTELVPN_XAUTHTYPE_AS_REQUEST
 				if (ap->af != isakmp_attr_16 || !(ap->u.attr_16 == 0 || ap->u.attr_16 == 5))
 					reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
-                                xauth_type_requested = ap->u.attr_16;
+                xauth_type_requested = ap->u.attr_16;
+#else
+
+				if (ap->af != isakmp_attr_16 || ap->u.attr_16 != 0)
+					reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
+#endif
 				break;
 			case ISAKMP_XAUTH_ATTRIB_USER_NAME:
 			case ISAKMP_XAUTH_ATTRIB_USER_PASSWORD:
 			case ISAKMP_XAUTH_ATTRIB_PASSCODE:
 			case ISAKMP_XAUTH_ATTRIB_DOMAIN:
 			case ISAKMP_XAUTH_ATTRIB_ANSWER:
+			case ISAKMP_XAUTH_ATTRIB_NEXT_PIN:
 			case ISAKMP_XAUTH_ATTRIB_CISCOEXT_VENDOR:
 				break;
 			case ISAKMP_XAUTH_ATTRIB_MESSAGE:
@@ -1471,12 +2209,11 @@
 			default:
 				reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
 			}
-		DEBUG(2, printf("S5.5\n"));
 		if (reject != 0)
-			phase2_fatal(s, "xauth packet unsupported: %s", reject);
-
-		inet_ntop(dest_addr->sa_family,
-			&((struct sockaddr_in *)dest_addr)->sin_addr, ntop_buf, sizeof(ntop_buf));
+			phase2_fatal(s, "xauth packet unsupported: %s(%d)", reject);
+
+		DEBUGTOP(2, printf("S5.5 do xauth authentication\n"));
+		inet_ntop(AF_INET, &s->dst, ntop_buf, sizeof(ntop_buf));
 
 		/* Collect data from the user.  */
 		reply_attr = NULL;
@@ -1487,10 +2224,10 @@
 					struct isakmp_attribute *na;
 					na = new_isakmp_attribute(ap->type, reply_attr);
 					reply_attr = na;
-					na->u.lots.length = strlen(config[CONFIG_DOMAIN]);
-					if (na->u.lots.length == 0)
+					if (!config[CONFIG_DOMAIN])
 						error(1, 0,
 							"server requested domain, but none set (use \"Domain ...\" in config or --domain");
+					na->u.lots.length = strlen(config[CONFIG_DOMAIN]);
 					na->u.lots.data = xallocc(na->u.lots.length);
 					memcpy(na->u.lots.data, config[CONFIG_DOMAIN],
 						na->u.lots.length);
@@ -1510,7 +2247,12 @@
 			case ISAKMP_XAUTH_ATTRIB_ANSWER:
 			case ISAKMP_XAUTH_ATTRIB_USER_PASSWORD:
 			case ISAKMP_XAUTH_ATTRIB_PASSCODE:
-				if (seen_answer || config[CONFIG_XAUTH_INTERACTIVE]) {
+			case ISAKMP_XAUTH_ATTRIB_NEXT_PIN:
+				if (passwd_used && config[CONFIG_NON_INTERACTIVE]) {
+					reject = ISAKMP_N_AUTHENTICATION_FAILED;
+					phase2_fatal(s, "noninteractive can't reuse password", reject);
+					error(2, 0, "authentication unsuccessful");
+				} else if (seen_answer || passwd_used || config[CONFIG_XAUTH_INTERACTIVE]) {
 					char *pass, *prompt = NULL;
 					struct isakmp_attribute *na;
 
@@ -1531,19 +2273,26 @@
 					memset(pass, 0, na->u.lots.length);
 				} else {
 					struct isakmp_attribute *na;
+#ifdef NORTELVPN
 					na = reply_attr->next = new_isakmp_attribute(ISAKMP_XAUTH_ATTRIB_PASSCODE, /* reply_attr */ NULL);
-					/* reply_attr = na; */
+#else
+					na = new_isakmp_attribute(ap->type, reply_attr);
+					reply_attr = na;
+#endif
 					na->u.lots.length = strlen(config[CONFIG_XAUTH_PASSWORD]);
 					na->u.lots.data = xallocc(na->u.lots.length);
 					memcpy(na->u.lots.data, config[CONFIG_XAUTH_PASSWORD],
 						na->u.lots.length);
+					passwd_used = 1; /* Provide canned password at most once */
 				}
 				break;
 			default:
 				;
 			}
 
+#ifdef NORTELVPN
 		reply_attr = new_isakmp_attribute_16(ISAKMP_XAUTH_ATTRIB_TYPE, xauth_type_requested, reply_attr);
+#endif
 
 		/* Send the response.  */
 		rp = new_isakmp_payload(ISAKMP_PAYLOAD_MODECFG_ATTR);
@@ -1553,24 +2302,47 @@
 		sendrecv_phase2(s, rp, ISAKMP_EXCHANGE_MODECFG_TRANSACTION,
 			r->message_id, 0, 0, 0, 0, 0, 0, 0);
 
-		free_isakmp_packet(r);
-	}
-
-	DEBUG(2, printf("S5.6\n"));
-#if 0
+	}
+	
+	if ((opt_vendor == VENDOR_NETSCREEN) &&
+		(r->payload->next->u.modecfg.type == ISAKMP_MODECFG_CFG_SET)) {
+		struct isakmp_attribute *a = r->payload->next->u.modecfg.attributes;
+		
+		DEBUGTOP(2, printf("S5.5.1 do netscreen modecfg extra\n"));
+		
+		do_config_to_env(s, a);
+		
+		for (; a; a = a->next)
+			if(a->af == isakmp_attr_lots)
+				a->u.lots.length = 0;
+
+		r->payload->next->u.modecfg.type = ISAKMP_MODECFG_CFG_ACK;
+		sendrecv_phase2(s, r->payload->next,
+			ISAKMP_EXCHANGE_MODECFG_TRANSACTION,
+			r->message_id, 0, 0, 0, 0, 0, 0, 0);
+		
+		reject = do_phase2_notice_check(s, &r);
+		if (reject == -1)
+			return 1;
+	}
+	
+	DEBUGTOP(2, printf("S5.6 process xauth response\n"));
+
+#ifndef NORTELVPN
+
 	{
 		/* The final SET should have just one attribute.  */
-		int reject = 0;
 		struct isakmp_attribute *a = r->payload->next->u.modecfg.attributes;
-		uint16_t set_result;
+		uint16_t set_result = 1;
 
 		if (a == NULL
 			|| a->type != ISAKMP_XAUTH_ATTRIB_STATUS
 			|| a->af != isakmp_attr_16 || a->next != NULL) {
 			reject = ISAKMP_N_INVALID_PAYLOAD_TYPE;
-			phase2_fatal(s, "xauth SET response rejected: %s", reject);
+			phase2_fatal(s, "xauth SET response rejected: %s(%d)", reject);
+		} else {
+			set_result = a->u.attr_16;
 		}
-		set_result = a->u.attr_16;
 
 		/* ACK the SET.  */
 		r->payload->next->u.modecfg.type = ISAKMP_MODECFG_CFG_ACK;
@@ -1582,20 +2354,93 @@
 		if (set_result == 0)
 			error(2, 0, "authentication unsuccessful");
 	}
+
 #endif
-	DEBUG(2, printf("S5.7\n"));
+
+	DEBUGTOP(2, printf("S5.7 xauth done\n"));
 	return 0;
 }
 
-static void process_config(struct sa_block *s, struct isakmp_packet *r)
-{
-	struct isakmp_attribute *a;
-	int reject = 0, seen_address = 0;
-	char *strbuf;
-
+static int do_phase2_config(struct sa_block *s)
+{
+	struct isakmp_packet *r;
+	struct isakmp_payload *rp;
+	struct isakmp_attribute *a, *reply_attr = NULL;
+	int reject;
+
+#ifndef NORTELVPN
+	struct utsname uts;
+	uint32_t msgid;
+
+	uname(&uts);
+
+	gcry_create_nonce((uint8_t *) & msgid, sizeof(msgid));
+	if (msgid == 0)
+		msgid = 1;
+
+	rp = new_isakmp_payload(ISAKMP_PAYLOAD_MODECFG_ATTR);
+	rp->u.modecfg.type = ISAKMP_MODECFG_CFG_REQUEST;
+	rp->u.modecfg.id = 20;
+	a = NULL;
+
+	a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_APPLICATION_VERSION, a);
+	a->u.lots.length = strlen(config[CONFIG_VERSION]);
+	a->u.lots.data = xallocc(a->u.lots.length);
+	memcpy(a->u.lots.data, config[CONFIG_VERSION], a->u.lots.length);
+
+	a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_CISCO_DDNS_HOSTNAME, a);
+	a->u.lots.length = strlen(uts.nodename);
+	a->u.lots.data = xallocc(a->u.lots.length);
+	memcpy(a->u.lots.data, uts.nodename, a->u.lots.length);
+
+	a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_CISCO_SPLIT_INC, a);
+	a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_CISCO_SAVE_PW, a);
+	
+	a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_CISCO_BANNER, a);
+	a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_CISCO_DO_PFS, a);
+	if (opt_natt_mode == NATT_CISCO_UDP)
+		a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_CISCO_UDP_ENCAP_PORT, a);
+	a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_CISCO_DEF_DOMAIN, a);
+	a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_NBNS, a);
+	a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_DNS, a);
+	a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_NETMASK, a);
+	a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_ADDRESS, a);
+
+	rp->u.modecfg.attributes = a;
+	sendrecv_phase2(s, rp, ISAKMP_EXCHANGE_MODECFG_TRANSACTION, msgid, 0, 0, 0, 0, 0, 0, 0);
+#else
+	r_length = sendrecv(s,r_packet, sizeof(r_packet), NULL, 0, 0);
+#endif
+
+	/* recv and check for notices */
+	reject = do_phase2_notice_check(s, &r);
+	if (reject == -1)
+		return 1;
+
+#ifndef NORTELVPN
+	/* Check the transaction type & message ID are OK.  */
+	if (reject == 0 && r->message_id != msgid)
+		reject = ISAKMP_N_INVALID_MESSAGE_ID;
+	if (reject == 0 && r->exchange_type != ISAKMP_EXCHANGE_MODECFG_TRANSACTION)
+		reject = ISAKMP_N_INVALID_EXCHANGE_TYPE;
+#else
 	if (r->exchange_type != ISAKMP_EXCHANGE_MODECFG_TRANSACTION)
 		reject = ISAKMP_N_INVALID_EXCHANGE_TYPE;
-
+#endif
+
+	/* After the hash, expect an attribute block.  */
+
+#ifndef NORTELVPN
+	if (reject == 0
+		&& (r->payload->next == NULL
+			|| r->payload->next->next != NULL
+			|| r->payload->next->type != ISAKMP_PAYLOAD_MODECFG_ATTR
+#if 0
+			|| r->payload->next->u.modecfg.id != 20
+#endif
+			|| r->payload->next->u.modecfg.type != ISAKMP_MODECFG_CFG_REPLY))
+		reject = ISAKMP_N_PAYLOAD_MALFORMED;
+#else
 	/* After the hash, expect an attribute block.  */
 	if (reject == 0
 		&& (r->payload->next == NULL
@@ -1607,133 +2452,20 @@
 		&& (r->payload->next->u.modecfg.type != ISAKMP_MODECFG_CFG_REPLY)
 		&& (r->payload->next->u.modecfg.type != ISAKMP_MODECFG_CFG_SET))
 		reject = ISAKMP_N_PAYLOAD_MALFORMED;
-
-
+#endif
+	
 	if (reject != 0)
-		phase2_fatal(s, "configuration response rejected: %s", reject);
-
-	unsetenv("CISCO_BANNER");
-	unsetenv("CISCO_DEF_DOMAIN");
-	unsetenv("INTERNAL_IP4_NBNS");
-	unsetenv("INTERNAL_IP4_DNS");
-	unsetenv("INTERNAL_IP4_NETMASK");
-	unsetenv("INTERNAL_IP4_ADDRESS");
-
-	for (a = r->payload->next->u.modecfg.attributes; a && reject == 0; a = a->next)
-		switch (a->type) {
-		case ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_ADDRESS:
-			if (a->af != isakmp_attr_lots || a->u.lots.length != 4)
-				reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
-			else {
-				addenv_ipv4("INTERNAL_IP4_ADDRESS", a->u.lots.data);
-				memcpy(s->our_address, a->u.lots.data, 4);
-			}
-			seen_address = 1;
-			break;
-
-		case ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_NETMASK:
-			if (a->af == isakmp_attr_lots && a->u.lots.length == 0) {
-				DEBUG(2, printf("ignoring zero length netmask\n"));
-				continue;
-			}
-			if (a->af != isakmp_attr_lots || a->u.lots.length != 4)
-				reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
-			else
-				addenv_ipv4("INTERNAL_IP4_NETMASK", a->u.lots.data);
-			break;
-
-		case ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_DNS:
-			if (a->af != isakmp_attr_lots || a->u.lots.length != 4)
-				reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
-			else
-				addenv_ipv4("INTERNAL_IP4_DNS", a->u.lots.data);
-			break;
-
-		case ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_NBNS:
-			if (a->af != isakmp_attr_lots || a->u.lots.length != 4)
-				reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
-			else
-				addenv_ipv4("INTERNAL_IP4_NBNS", a->u.lots.data);
-			break;
-
-		case ISAKMP_MODECFG_ATTRIB_CISCO_DEF_DOMAIN:
-			if (a->af != isakmp_attr_lots) {
-				reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
-				break;
-			}
-			strbuf = xallocc(a->u.lots.length + 1);
-			memcpy(strbuf, a->u.lots.data, a->u.lots.length);
-			addenv("CISCO_DEF_DOMAIN", strbuf);
-			free(strbuf);
-			break;
-
-		case ISAKMP_MODECFG_ATTRIB_CISCO_BANNER:
-			if (a->af != isakmp_attr_lots) {
-				reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
-				break;
-			}
-			strbuf = xallocc(a->u.lots.length + 1);
-			memcpy(strbuf, a->u.lots.data, a->u.lots.length);
-			addenv("CISCO_BANNER", strbuf);
-			free(strbuf);
-			DEBUG(1, printf("Banner: "));
-			DEBUG(1, fwrite(a->u.lots.data, a->u.lots.length, 1, stdout));
-			DEBUG(1, printf("\n"));
-			break;
-
-		case ISAKMP_MODECFG_ATTRIB_APPLICATION_VERSION:
-			DEBUG(2, printf("Remote Application Version: "));
-			DEBUG(2, fwrite(a->u.lots.data, a->u.lots.length, 1, stdout));
-			DEBUG(2, printf("\n"));
-			break;
-
-		case ISAKMP_MODECFG_ATTRIB_CISCO_DO_PFS:
-			if (a->af != isakmp_attr_16)
-				reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
-			else {
-				s->do_pfs = a->u.attr_16;
-				DEBUG(2, printf("got pfs setting: %d\n", s->do_pfs));
-			}
-			break;
-
-		case ISAKMP_MODECFG_ATTRIB_CISCO_UDP_ENCAP_PORT:
-			if (a->af != isakmp_attr_16)
-				reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
-			else {
-				s->peer_udpencap_port = a->u.attr_16;
-				DEBUG(2, printf("got peer udp encapsulation port: %hu\n", s->peer_udpencap_port));
-			}
-			break;
-
-		default:
-			DEBUG(2, printf("unknown attriube %d / 0x%X\n", a->type, a->type));
-			break;
-		}
-
-
-	if (reject == 0 && !seen_address)
-		reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
-
+		phase2_fatal(s, "configuration response rejected: %s(%d)", reject);
+
+	if (reject == 0)
+		reject = do_config_to_env(s, r->payload->next->u.modecfg.attributes);
+	
 	if (reject != 0)
-		phase2_fatal(s, "configuration response rejected: %s", reject);
+		phase2_fatal(s, "configuration response rejected: %s(%d)", reject);
 
 	DEBUG(1, printf("got address %s\n", getenv("INTERNAL_IP4_ADDRESS")));
-}
-
-static int do_phase_2_config(struct sa_block *s)
-{
-	struct isakmp_packet *r;
-	struct isakmp_payload *rp;
-	struct isakmp_attribute *a, *reply_attr = NULL;
-	int reject;
-
-	r_length = sendrecv(r_packet, sizeof(r_packet), NULL, 0, 0);
-	reject = do_phase2_notice_check(s, &r);
-	if (reject == -1)
-		return 1;
-
-	process_config(s, r);
-
+
+#ifdef NORTELVPN
 	for (a = r->payload->next->u.modecfg.attributes; a; a = a->next)
 	{
 		if (a->af == isakmp_attr_16)
@@ -1748,70 +2480,12 @@
 	rp->u.modecfg.attributes = reply_attr;
 	sendrecv_phase2(s, rp, ISAKMP_EXCHANGE_MODECFG_TRANSACTION,
 		r->message_id, 1, 0, 0, 0, 0, 0, 0);
+#endif
+
 	return 0;
 }
 
-#if 0
-static int do_phase_2_config(struct sa_block *s)
-{
-	struct isakmp_payload *rp;
-	struct isakmp_attribute *a;
-	struct isakmp_packet *r;
-	struct utsname uts;
-	uint32_t msgid;
-	int reject;
-
-	uname(&uts);
-
-	gcry_randomize((uint8_t *) & msgid, sizeof(msgid), GCRY_WEAK_RANDOM);
-	if (msgid == 0)
-		msgid = 1;
-
-	rp = new_isakmp_payload(ISAKMP_PAYLOAD_MODECFG_ATTR);
-	rp->u.modecfg.type = ISAKMP_MODECFG_CFG_REQUEST;
-	rp->u.modecfg.id = 20;
-	a = NULL;
-
-	a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_APPLICATION_VERSION, a);
-	a->u.lots.length = strlen(config[CONFIG_VERSION]);
-	a->u.lots.data = xallocc(a->u.lots.length);
-	memcpy(a->u.lots.data, config[CONFIG_VERSION], a->u.lots.length);
-
-	a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_CISCO_DDNS_HOSTNAME, a);
-	a->u.lots.length = strlen(uts.nodename);
-	a->u.lots.data = xallocc(a->u.lots.length);
-	memcpy(a->u.lots.data, uts.nodename, a->u.lots.length);
-
-	a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_CISCO_BANNER, a);
-	a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_CISCO_DO_PFS, a);
-	if (opt_udpencap)
-		a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_CISCO_UDP_ENCAP_PORT, a);
-	a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_CISCO_DEF_DOMAIN, a);
-	a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_NBNS, a);
-	a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_DNS, a);
-	a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_NETMASK, a);
-	a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_ADDRESS, a);
-
-	rp->u.modecfg.attributes = a;
-	sendrecv_phase2(s, rp, ISAKMP_EXCHANGE_MODECFG_TRANSACTION, msgid, 0, 0, 0, 0, 0, 0, 0);
-
-	/* recv and check for notices */
-	reject = do_phase2_notice_check(s, &r);
-	if (reject == -1)
-		return 1;
-
-	if (reject == 0 && r->message_id != msgid)
-		reject = ISAKMP_N_INVALID_MESSAGE_ID;
-
-	if (reject != 0)
-		phase2_fatal(s, "configuration response rejected: %s", reject);
-
-	process_config(s, r);
-	return 0;
-}
-#endif
-
-struct isakmp_attribute *make_transform_ipsec(int dh_group, int hash, int keylen)
+static struct isakmp_attribute *make_transform_ipsec(struct sa_block *s, int dh_group, int hash, int keylen)
 {
 	struct isakmp_attribute *a = NULL;
 
@@ -1825,38 +2499,50 @@
 	if (dh_group)
 		a = new_isakmp_attribute_16(ISAKMP_IPSEC_ATTRIB_GROUP_DESC, dh_group, a);
 	a = new_isakmp_attribute_16(ISAKMP_IPSEC_ATTRIB_AUTH_ALG, hash, a);
-	a = new_isakmp_attribute_16(ISAKMP_IPSEC_ATTRIB_ENCAP_MODE, encap_mode, a);
+	a = new_isakmp_attribute_16(ISAKMP_IPSEC_ATTRIB_ENCAP_MODE, s->ipsec.encap_mode, a);
 	if (keylen != 0)
 		a = new_isakmp_attribute_16(ISAKMP_IPSEC_ATTRIB_KEY_LENGTH, keylen, a);
 
 	return a;
 }
 
-struct isakmp_payload *make_our_sa_ipsec(struct sa_block *s, struct isakmp_payload *transform, int proposal_number)
+#ifdef NORTELVPN
+static struct isakmp_payload *make_our_sa_ipsec(struct sa_block *s, struct isakmp_payload *transform, int proposal_number)
+#else
+static struct isakmp_payload *make_our_sa_ipsec(struct sa_block *s)
+#endif
 {
 	struct isakmp_payload *r = new_isakmp_payload(ISAKMP_PAYLOAD_SA);
 	struct isakmp_payload *p = NULL, *pn;
 	struct isakmp_attribute *a;
-	int dh_grp = get_dh_group_ipsec(s->do_pfs)->ipsec_sa_id;
+	int dh_grp = get_dh_group_ipsec(s->ipsec.do_pfs)->ipsec_sa_id;
 	unsigned int crypt, hash, keylen;
 	int i;
 
 	r = new_isakmp_payload(ISAKMP_PAYLOAD_SA);
 	r->u.sa.doi = ISAKMP_DOI_IPSEC;
 	r->u.sa.situation = ISAKMP_IPSEC_SIT_IDENTITY_ONLY;
+
+#ifndef NORTELVPN
+	r->u.sa.proposals = new_isakmp_payload(ISAKMP_PAYLOAD_P);
+	r->u.sa.proposals->u.p.spi_size = 4;
+	r->u.sa.proposals->u.p.spi = xallocc(4);
+	/* The sadb_sa_spi field is already in network order.  */
+	memcpy(r->u.sa.proposals->u.p.spi, &s->ipsec.rx.spi, 4);
+	r->u.sa.proposals->u.p.prot_id = ISAKMP_IPSEC_PROTO_IPSEC_ESP;
+#else
 	if (transform) {
 		p = new_isakmp_payload(ISAKMP_PAYLOAD_P);
 		p->u.p.spi_size = 4;
 		p->u.p.spi = xallocc(4);
 		/* The sadb_sa_spi field is already in network order.  */
-		memcpy(p->u.p.spi, &s->tous_esp_spi, 4);
+		memcpy(p->u.p.spi, &s->ipsec.rx.spi, 4);
 		p->u.p.prot_id = ISAKMP_IPSEC_PROTO_IPSEC_ESP;
 		p->u.p.transforms = dup_isakmp_payload(transform);
 		p->u.p.number = proposal_number;
 	} else {
+#endif
 		for (crypt = 0; supp_crypt[crypt].name != NULL; crypt++) {
-			if ((supp_crypt[crypt].my_id == GCRY_CIPHER_DES) && (opt_1des == 0))
-				continue;
 			keylen = supp_crypt[crypt].keylen;
 			for (hash = 0; supp_hash[hash].name != NULL; hash++) {
 				pn = p;
@@ -1864,28 +2550,36 @@
 				p->u.p.spi_size = 4;
 				p->u.p.spi = xallocc(4);
 				/* The sadb_sa_spi field is already in network order.  */
-				memcpy(p->u.p.spi, &s->tous_esp_spi, 4);
+				memcpy(p->u.p.spi, &s->ipsec.rx.spi, 4);
 				p->u.p.prot_id = ISAKMP_IPSEC_PROTO_IPSEC_ESP;
 				p->u.p.transforms = new_isakmp_payload(ISAKMP_PAYLOAD_T);
 				p->u.p.transforms->u.t.id = supp_crypt[crypt].ipsec_sa_id;
-				a = make_transform_ipsec(dh_grp, supp_hash[hash].ipsec_sa_id, keylen);
+				a = make_transform_ipsec(s, dh_grp, supp_hash[hash].ipsec_sa_id, keylen);
 				p->u.p.transforms->u.t.attributes = a;
 				p->next = pn;
 			}
 		}
-		for (i = 0, pn = p; pn; pn = pn->next)
-			pn->u.p.number = i++;
-	}
+#ifdef NORTELVPN
+	}
+#endif
+	for (i = 0, pn = p; pn; pn = pn->next)
+		pn->u.p.number = i++;
 	r->u.sa.proposals = p;
 	return r;
 }
 
-static int check_transform(struct isakmp_payload *transform)
+#ifdef NORTELVPN
+
+static int check_transform(struct sa_block *s,struct isakmp_payload *transform)
 {
 	int seen_enc, seen_auth = 0, seen_encap = 0, seen_group = 0, seen_keylen = 0;
 	struct isakmp_attribute *a;
 	const supported_algo_t *cry_algo, *hash_algo;
 	int reject = 0;
+
+	/* moved to do_phase2 for NortelVPN
+     * 	memcpy(&s->ipsec.tx.spi, rp->u.sa.proposals->u.p.spi, 4);
+     */
 
 	seen_enc = transform->u.t.id;
 	for (a = transform->u.t.attributes; a; a = a->next)
@@ -1897,13 +2591,13 @@
 				reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
 			break;
 		case ISAKMP_IPSEC_ATTRIB_ENCAP_MODE:
-			if (a->af == isakmp_attr_16)
+			if (a->af == isakmp_attr_16 )
 				seen_encap = a->u.attr_16;
 			else
 				reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
 			break;
 		case ISAKMP_IPSEC_ATTRIB_GROUP_DESC:
-			if (a->af == isakmp_attr_16)
+			if (a->af == isakmp_attr_16 )
 				seen_group = a->u.attr_16;
 			else
 				reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
@@ -1914,13 +2608,41 @@
 			else
 				reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
 			break;
+
 		case ISAKMP_IPSEC_ATTRIB_SA_LIFE_TYPE:
+			/* lifetime duration MUST follow lifetype attribute */
+			if (a->next->type == ISAKMP_IPSEC_ATTRIB_SA_LIFE_DURATION) {
+				lifetime_ipsec_process(s, a);
+			} else
+				reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+			break;
 		case ISAKMP_IPSEC_ATTRIB_SA_LIFE_DURATION:
+			/* already processed above in ISAKMP_IPSEC_ATTRIB_SA_LIFE_TYPE: */
 			break;
 		default:
-			continue;
+/* removed for NortelVPN
+ * 			reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
+ */
+			break;
 		}
 
+/* removed for NortelVPN
+
+    	if (reject == 0 && (!seen_auth || !seen_encap ||
+    			(dh_grp && !seen_group)))
+    		reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+
+    	if (reject == 0
+    			&& get_algo(SUPP_ALGO_HASH, SUPP_ALGO_IPSEC_SA, seen_auth,
+    			NULL, 0) == NULL)
+    		reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+    	if (reject == 0
+    			&& get_algo(SUPP_ALGO_CRYPT, SUPP_ALGO_IPSEC_SA, seen_enc,
+    			NULL, seen_keylen) == NULL)
+    		reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+ */
+	
+	
 	if (!seen_auth
 	    || (seen_encap && (seen_encap != encap_mode))
 	    || (dh_group && (seen_group != dh_group)))
@@ -1931,24 +2653,54 @@
 	if ((cry_algo == NULL) || (hash_algo == NULL))
 		return 0;
 
-	ipsec_cry_algo = cry_algo->my_id;
-	ipsec_hash_algo = hash_algo->my_id;
 	dh_group = seen_group;
-	DEBUG(1, printf("IPSEC SA selected %s-%s\n", cry_algo->name, hash_algo->name));
+
+	s->ipsec.cry_algo = ipsec_cry_algo  = cry_algo->my_id;
+	s->ipsec.md_algo  = ipsec_hash_algo = hash_algo->my_id;	
+	if (s->ipsec.cry_algo) {
+		gcry_cipher_algo_info(s->ipsec.cry_algo, GCRYCTL_GET_KEYLEN, NULL, &(s->ipsec.key_len));
+		gcry_cipher_algo_info(s->ipsec.cry_algo, GCRYCTL_GET_BLKLEN, NULL, &(s->ipsec.blk_len));
+		s->ipsec.iv_len = s->ipsec.blk_len;
+	} else {
+		s->ipsec.key_len = 0;
+		s->ipsec.iv_len = 0;
+		s->ipsec.blk_len = 8; /* seems to be this without encryption... */
+	}
+	s->ipsec.md_len = gcry_md_get_algo_dlen(s->ipsec.md_algo);
+	DEBUG(1, printf("IPSEC SA selected %s-%s\n",
+			get_algo(SUPP_ALGO_CRYPT,
+				SUPP_ALGO_IPSEC_SA, seen_enc, NULL,
+				seen_keylen)->name,
+			get_algo(SUPP_ALGO_HASH, SUPP_ALGO_IPSEC_SA,
+				seen_auth, NULL, 0)->name));
+	if (s->ipsec.cry_algo == GCRY_CIPHER_DES && !opt_1des) {
+		error(1, 0, "peer selected (single) DES as \"encrytion\" method.\n"
+			"This algorithm is considered to weak today\n"
+			"If your vpn concentrator admin still insists on using DES\n"
+			"use the \"--enable-1des\" option.\n");
+	} else if (s->ipsec.cry_algo == GCRY_CIPHER_NONE && !opt_no_encryption) {
+		error(1, 0, "peer selected NULL as \"encrytion\" method.\n"
+			"This is _no_ encryption at all.\n"
+			"Your traffic is still protected against modification with %s\n"
+			"If your vpn concentrator admin still insists on not using encryption\n"
+			"use the \"--enable-no-encryption\" option.\n",
+			get_algo(SUPP_ALGO_HASH, SUPP_ALGO_IPSEC_SA, seen_auth, NULL, 0)->name);
+	}
+	
 	return 1;
 }
 
-
-static void do_phase_2(struct sa_block *s)
-{
+static void do_phase2(struct sa_block *s)
+{
+	
 	struct isakmp_packet *r;
-	struct isakmp_payload *rp, *proposal = NULL, *transform = NULL, *ke = NULL, *nonce_i = NULL, *idci = NULL, *idcr = NULL;
+	struct isakmp_payload *rp, *proposal = NULL, *transform = NULL, *ke = NULL, *nonce_r = NULL, *idci = NULL, *idcr = NULL;
 	uint8_t nonce[20], *dh_public, *dh_shared_secret = NULL;
 
 	static struct group *dh_grp;
 	int reject;
 
-	r_length = sendrecv(r_packet, sizeof(r_packet), NULL, 0, 0);
+	r_length = sendrecv(s,r_packet, sizeof(r_packet), NULL, 0, 0);
 	reject = do_phase2_notice_check(s, &r);
 
 	if (reject == 0 && r->exchange_type != ISAKMP_EXCHANGE_IKE_QUICK)
@@ -1958,6 +2710,22 @@
 	if (reject == 0 && r->payload->next->type != ISAKMP_PAYLOAD_SA)
 		reject = ISAKMP_N_INVALID_PAYLOAD_TYPE;
 
+	DEBUGTOP(2, printf("do_phase2: S7.5 QM_packet2 check reject offer\n"));
+	if (reject != 0)
+	phase2_fatal(s, "quick mode response rejected: %s(%d)\n"
+		"this means the concentrator did not like what we had to offer.\n"
+		"Possible reasons are:\n"
+		"  * concentrator configured to require a firewall\n"
+		"     this locks out even Cisco clients on any platform expect windows\n"
+		"     which is an obvious security improvment. There is no workaround (yet).\n"
+		"  * concentrator configured to require IP compression\n"
+		"     this is not yet supported by vpnc.\n"
+		"     Note: the Cisco Concentrator Documentation recommends against using\n"
+		"     compression, expect on low-bandwith (read: ISDN) links, because it\n"
+		"     uses much CPU-resources on the concentrator\n",
+		reject);
+
+	DEBUGTOP(2, printf("do_phase2: S7.6 QM_packet2 check and process proposal\n"));
 	for (rp = r->payload->next; rp && reject == 0; rp = rp->next)
 		switch (rp->type) {
 		case ISAKMP_PAYLOAD_SA:
@@ -1965,53 +2733,99 @@
 				reject = ISAKMP_N_DOI_NOT_SUPPORTED;
 			if (reject == 0 && rp->u.sa.situation != ISAKMP_IPSEC_SIT_IDENTITY_ONLY)
 				reject = ISAKMP_N_SITUATION_NOT_SUPPORTED;
-			if (reject != 0)
-				break;
-			for (proposal = rp->u.sa.proposals; proposal; proposal = proposal->next)
-			{
-				if (proposal->u.p.prot_id != ISAKMP_IPSEC_PROTO_IPSEC_ESP)
-					continue;
-				if (proposal->u.p.spi_size != 4)
-					continue;
-				for (transform = proposal->u.p.transforms; transform; transform = transform->next)
-					if (check_transform(transform))
-						break;
-				if (!transform)
-					continue;
-				break;
+
+/* Removed for NORTELVPN
+			if (reject == 0 &&
+				(rp->u.sa.proposals == NULL || rp->u.sa.proposals->next != NULL))
+				reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+			if (reject == 0 &&
+				rp->u.sa.proposals->u.p.prot_id != ISAKMP_IPSEC_PROTO_IPSEC_ESP)
+				reject = ISAKMP_N_INVALID_PROTOCOL_ID;
+			if (reject == 0 && rp->u.sa.proposals->u.p.spi_size != 4)
+				reject = ISAKMP_N_INVALID_SPI;
+			if (reject == 0 &&
+				(rp->u.sa.proposals->u.p.transforms == NULL
+					|| rp->u.sa.proposals->u.p.transforms->next != NULL))
+				reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+*/
+			if (reject == 0) {			
+				for (proposal = rp->u.sa.proposals; proposal; proposal = proposal->next)
+				{
+					if (proposal->u.p.prot_id != ISAKMP_IPSEC_PROTO_IPSEC_ESP)
+						continue;
+					if (proposal->u.p.spi_size != 4)
+						continue;
+					for (transform = proposal->u.p.transforms; transform; transform = transform->next)
+					{
+						if (check_transform(s,transform))
+						{
+							break;
+						}
+					}
+					if (!transform)
+						continue;
+					break;
+				}
+			
 			}
 			if (!proposal)
+			{
 				reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
-			break;
+			}
+			break;
+	
 		case ISAKMP_PAYLOAD_N:
+			if (reject == 0 && rp->u.n.type == ISAKMP_N_IPSEC_RESPONDER_LIFETIME) {
+				if (rp->u.n.protocol == ISAKMP_IPSEC_PROTO_ISAKMP)
+					lifetime_ike_process(s, rp->u.n.attributes);
+				else if (rp->u.n.protocol == ISAKMP_IPSEC_PROTO_IPSEC_ESP)
+					lifetime_ipsec_process(s, rp->u.n.attributes);
+				else
+					DEBUG(2, printf("got unknown lifetime notice, ignoring..\n"));
+			}
 			break;
 		case ISAKMP_PAYLOAD_ID:
+			/* FIXME: Parse payload ID and add route-env in case of ipv4_prefix */
 			if (idci == NULL) idci = rp;
 			else if (idcr == NULL) idcr = rp;
 			else abort();
-			break;
+			break;	
 		case ISAKMP_PAYLOAD_KE:
 			ke = rp;
 			break;
 		case ISAKMP_PAYLOAD_NONCE:
-			nonce_i = rp;
-			break;
-
+			nonce_r = rp;
+			break;
+	
 		default:
 			reject = ISAKMP_N_INVALID_PAYLOAD_TYPE;
 			break;
 		}
+/* Removed for NORTELVPN
+
+	if (reject == 0 && nonce_r == NULL)
+		reject = ISAKMP_N_INVALID_HASH_INFORMATION;
+	if (reject == 0 && dh_grp && (ke == NULL || ke->u.ke.length != dh_getlen(dh_grp)))
+		reject = ISAKMP_N_INVALID_KEY_INFORMATION;
+*/
+
 
 	if (reject != 0)
-		phase2_fatal(s, "quick mode response rejected: %s\ncheck pfs setting", reject);
-
-	memcpy((uint8_t *) & s->tothem_esp_spi, proposal->u.p.spi, sizeof(s->tothem_esp_spi));
-	gcry_randomize((uint8_t *) & s->tous_esp_spi, sizeof(s->tous_esp_spi), GCRY_WEAK_RANDOM);
+		phase2_fatal(s, "quick mode response rejected [2]: %s(%d)", reject);
+
+/*
+ * Moved from check_transform()
+ */
+ 	memcpy(&s->ipsec.tx.spi, proposal->u.p.spi, proposal->u.p.spi_size);
+
+	DEBUGTOP(2, printf("do_phase2: S7.1 QM_packet1\n"));
+
+	gcry_create_nonce((uint8_t *) & s->ipsec.rx.spi, sizeof(s->ipsec.rx.spi));
 	rp = make_our_sa_ipsec(s, transform, proposal->u.p.number);
-	gcry_randomize((uint8_t *) nonce, sizeof(nonce), GCRY_WEAK_RANDOM);
+	gcry_create_nonce((uint8_t *) nonce, sizeof(nonce));
 	rp->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_NONCE, nonce, sizeof(nonce));
 
-	/* copy IDci and IDcr from initiator */
+	/* copy IDci and IDcr from initiator ISAKMP_PAYLOAD_ID */
 	if (idci)
 		idci = dup_isakmp_payload(idci);
 	if (idcr) {
@@ -2024,44 +2838,53 @@
 		dh_grp = NULL;
 	} else {
 		const supported_algo_t *algo;
-
+		
+		/*NORTELVPN from S7.1 QM_packet1 */
+		/* Set up the Diffie-Hellman stuff.  ??*/
 		algo = get_algo(SUPP_ALGO_DH_GROUP, SUPP_ALGO_IPSEC_SA, dh_group, NULL, 0);
 		if (algo == NULL)
 			abort();
-
 		dh_grp = group_get(algo->my_id);
 		DEBUG(3, printf("len = %d\n", dh_getlen(dh_grp)));
 		dh_public = xallocc(dh_getlen(dh_grp));
 		dh_create_exchange(dh_grp, dh_public);
-		hex_dump("dh_public", dh_public, dh_getlen(dh_grp));
-
-		/* Determine the shared secret.  */
+		hex_dump("dh_public", dh_public, dh_getlen(dh_grp), NULL);
+
+		/* Determine the shared secret.  from S7.8 setup ipsec tunnel*/
 		dh_shared_secret = xallocc(dh_getlen(dh_grp));
 		dh_create_shared(dh_grp, dh_shared_secret, ke->u.ke.data);
-		hex_dump("dh_shared_secret", dh_shared_secret, dh_getlen(dh_grp));
-
+		hex_dump("dh_shared_secret", dh_shared_secret, dh_getlen(dh_grp), NULL);
+   
+		/*NORTELVPN from S7.1 QM_packet1 */
 		rp->next->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_KE,
 			dh_public, dh_getlen(dh_grp));
 		rp->next->next->next = idci;
 	}
 
+	/* send final packet S7.7 QM_packet3 sent - run script */
 	sendrecv_phase2(s, rp, ISAKMP_EXCHANGE_IKE_QUICK,
-			r->message_id, 0, 0, 0, nonce_i->u.nonce.data, nonce_i->u.nonce.length, 0, 0);
-
-	tous_keys = gen_keymat(s, ISAKMP_IPSEC_PROTO_IPSEC_ESP, s->tous_esp_spi,
-			       ipsec_hash_algo, ipsec_cry_algo,
-			       dh_shared_secret, dh_grp ? dh_getlen(dh_grp) : 0,
-			       nonce_i->u.nonce.data, nonce_i->u.nonce.length, nonce, sizeof(nonce));
-	tothem_keys = gen_keymat(s, ISAKMP_IPSEC_PROTO_IPSEC_ESP, s->tothem_esp_spi,
-				 ipsec_hash_algo, ipsec_cry_algo,
-				 dh_shared_secret, dh_grp ? dh_getlen(dh_grp) : 0,
-				 nonce_i->u.nonce.data, nonce_i->u.nonce.length, nonce, sizeof(nonce));
-
-}
-
-#if 0
-static void do_phase_2(struct sa_block *s)
-{
+		r->message_id, 0, 0, 0, nonce_r->u.nonce.data, nonce_r->u.nonce.length,0,0);
+	
+	DEBUGTOP(2, printf("do_phase2: S7.7 QM_packet3 sent - run script\n"));
+
+	tous_keys = s->ipsec.rx.key = gen_keymat(s, ISAKMP_IPSEC_PROTO_IPSEC_ESP, s->ipsec.rx.spi,
+		dh_shared_secret, dh_grp ? dh_getlen(dh_grp) : 0,
+		nonce_r->u.nonce.data, nonce_r->u.nonce.length, nonce, sizeof(nonce));
+
+	tothem_keys = s->ipsec.tx.key = gen_keymat(s, ISAKMP_IPSEC_PROTO_IPSEC_ESP, s->ipsec.tx.spi,
+		dh_shared_secret, dh_grp ? dh_getlen(dh_grp) : 0,
+		nonce_r->u.nonce.data, nonce_r->u.nonce.length, nonce, sizeof(nonce));
+
+}
+
+#endif //NORTELVPN
+
+
+static void setup_link(struct sa_block *s)
+{
+#ifndef NORTELVPN
+
+	
 	struct isakmp_payload *rp, *us, *ke = NULL, *them, *nonce_r = NULL;
 	struct isakmp_packet *r;
 	struct group *dh_grp = NULL;
@@ -2069,28 +2892,23 @@
 	int reject;
 	uint8_t *p_flat = NULL, *realiv = NULL, realiv_msgid[4];
 	size_t p_size = 0;
-	uint8_t nonce[20], *dh_public = NULL;
-	int ipsec_cry_algo = 0, ipsec_hash_algo = 0, i;
-
-	DEBUG(2, printf("S7.1\n"));
+	uint8_t nonce_i[20], *dh_public = NULL;
+	int i;
+
+	DEBUGTOP(2, printf("S7.1 QM_packet1\n"));
 	/* Set up the Diffie-Hellman stuff.  */
-	if (get_dh_group_ipsec(s->do_pfs)->my_id) {
-		dh_grp = group_get(get_dh_group_ipsec(s->do_pfs)->my_id);
+	if (get_dh_group_ipsec(s->ipsec.do_pfs)->my_id) {
+		dh_grp = group_get(get_dh_group_ipsec(s->ipsec.do_pfs)->my_id);
 		DEBUG(3, printf("len = %d\n", dh_getlen(dh_grp)));
 		dh_public = xallocc(dh_getlen(dh_grp));
 		dh_create_exchange(dh_grp, dh_public);
-		hex_dump("dh_public", dh_public, dh_getlen(dh_grp));
-
-		/* Determine the shared secret.  */
-		dh_shared_secret = xallocc(dh_getlen(dh_grp));
-		dh_create_shared(dh_grp, dh_shared_secret, ke->u.ke.data);
-		hex_dump("dh_shared_secret", dh_shared_secret, dh_getlen(dh_grp));
-	}
-
-	gcry_randomize((uint8_t *) & s->tous_esp_spi, sizeof(s->tous_esp_spi), GCRY_WEAK_RANDOM);
-	rp = make_our_sa_ipsec(s, NULL, 0);
-	gcry_randomize((uint8_t *) nonce, sizeof(nonce), GCRY_WEAK_RANDOM);
-	rp->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_NONCE, nonce, sizeof(nonce));
+		hex_dump("dh_public", dh_public, dh_getlen(dh_grp), NULL);
+	}
+
+	gcry_create_nonce((uint8_t *) & s->ipsec.rx.spi, sizeof(s->ipsec.rx.spi));
+	rp = make_our_sa_ipsec(s);
+	gcry_create_nonce((uint8_t *) nonce_i, sizeof(nonce_i));
+	rp->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_NONCE, nonce_i, sizeof(nonce_i));
 
 	us = new_isakmp_payload(ISAKMP_PAYLOAD_ID);
 	us->u.id.type = ISAKMP_IPSEC_ID_IPV4_ADDR;
@@ -2101,8 +2919,10 @@
 	them->u.id.type = ISAKMP_IPSEC_ID_IPV4_ADDR_SUBNET;
 	them->u.id.length = 8;
 	them->u.id.data = xallocc(8);
-	memset(them->u.id.data, 0, 8);
+	init_netaddr((struct in_addr *)them->u.id.data,
+		     config[CONFIG_IPSEC_TARGET_NETWORK]);
 	us->next = them;
+	s->ipsec.life.start = time(NULL);
 
 	if (!dh_grp) {
 		rp->next->next = us;
@@ -2112,38 +2932,42 @@
 		rp->next->next->next = us;
 	}
 
-	gcry_randomize((uint8_t *) & msgid, sizeof(&msgid), GCRY_WEAK_RANDOM);
+	gcry_create_nonce((uint8_t *) & msgid, sizeof(msgid));
 	if (msgid == 0)
 		msgid = 1;
 
-	DEBUG(2, printf("S7.2\n"));
 	for (i = 0; i < 4; i++) {
+		DEBUGTOP(2, printf("S7.2 QM_packet2 send_receive\n"));
 		sendrecv_phase2(s, rp, ISAKMP_EXCHANGE_IKE_QUICK,
 			msgid, 0, &p_flat, &p_size, 0, 0, 0, 0);
 
 		if (realiv == NULL) {
-			realiv = xallocc(s->ivlen);
-			memcpy(realiv, s->current_iv, s->ivlen);
-			memcpy(realiv_msgid, s->current_iv_msgid, 4);
+			realiv = xallocc(s->ike.ivlen);
+			memcpy(realiv, s->ike.current_iv, s->ike.ivlen);
+			memcpy(realiv_msgid, s->ike.current_iv_msgid, 4);
 		}
 
-		DEBUG(2, printf("S7.3\n"));
-		reject = unpack_verify_phase2(s, r_packet, r_length, &r, nonce, sizeof(nonce));
-
-		DEBUG(2, printf("S7.4\n"));
+		DEBUGTOP(2, printf("S7.3 QM_packet2 validate type\n"));
+		reject = unpack_verify_phase2(s, r_packet, r_length, &r, nonce_i, sizeof(nonce_i));
+
 		if (((reject == 0) || (reject == ISAKMP_N_AUTHENTICATION_FAILED))
 			&& r->exchange_type == ISAKMP_EXCHANGE_INFORMATIONAL) {
-			/* handle notifie responder-lifetime (ignore) */
+			DEBUGTOP(2, printf("S7.4 process and skip lifetime notice\n"));
+			/* handle notifie responder-lifetime */
 			/* (broken hash => ignore AUTHENTICATION_FAILED) */
 			if (reject == 0 && r->payload->next->type != ISAKMP_PAYLOAD_N)
 				reject = ISAKMP_N_INVALID_PAYLOAD_TYPE;
 
 			if (reject == 0
-				&& r->payload->next->u.n.type ==
-				ISAKMP_N_IPSEC_RESPONDER_LIFETIME) {
-				DEBUG(2, printf("ignoring responder-lifetime notify\n"));
-				memcpy(s->current_iv, realiv, s->ivlen);
-				memcpy(s->current_iv_msgid, realiv_msgid, 4);
+				&& r->payload->next->u.n.type == ISAKMP_N_IPSEC_RESPONDER_LIFETIME) {
+				if (r->payload->next->u.n.protocol == ISAKMP_IPSEC_PROTO_ISAKMP)
+					lifetime_ike_process(s, r->payload->next->u.n.attributes);
+				else if (r->payload->next->u.n.protocol == ISAKMP_IPSEC_PROTO_IPSEC_ESP)
+					lifetime_ipsec_process(s, r->payload->next->u.n.attributes);
+				else
+					DEBUG(2, printf("got unknown lifetime notice, ignoring..\n"));
+				memcpy(s->ike.current_iv, realiv, s->ike.ivlen);
+				memcpy(s->ike.current_iv_msgid, realiv_msgid, 4);
 				continue;
 			}
 		}
@@ -2166,11 +2990,22 @@
 		break;
 	}
 
-	DEBUG(2, printf("S7.5\n"));
+	DEBUGTOP(2, printf("S7.5 QM_packet2 check reject offer\n"));
 	if (reject != 0)
-		phase2_fatal(s, "quick mode response rejected: %s\ncheck pfs setting", reject);
-
-	DEBUG(2, printf("S7.6\n"));
+		phase2_fatal(s, "quick mode response rejected: %s(%d)\n"
+			"this means the concentrator did not like what we had to offer.\n"
+			"Possible reasons are:\n"
+			"  * concentrator configured to require a firewall\n"
+			"     this locks out even Cisco clients on any platform expect windows\n"
+			"     which is an obvious security improvment. There is no workaround (yet).\n"
+			"  * concentrator configured to require IP compression\n"
+			"     this is not yet supported by vpnc.\n"
+			"     Note: the Cisco Concentrator Documentation recommends against using\n"
+			"     compression, expect on low-bandwith (read: ISDN) links, because it\n"
+			"     uses much CPU-resources on the concentrator\n",
+			reject);
+
+	DEBUGTOP(2, printf("S7.6 QM_packet2 check and process proposal\n"));
 	for (rp = r->payload->next; rp && reject == 0; rp = rp->next)
 		switch (rp->type) {
 		case ISAKMP_PAYLOAD_SA:
@@ -2196,7 +3031,7 @@
 				int seen_enc = rp->u.sa.proposals->u.p.transforms->u.t.id;
 				int seen_auth = 0, seen_encap = 0, seen_group = 0, seen_keylen = 0;
 
-				memcpy(&s->tothem_esp_spi, rp->u.sa.proposals->u.p.spi, 4);
+				memcpy(&s->ipsec.tx.spi, rp->u.sa.proposals->u.p.spi, 4);
 
 				for (; a && reject == 0; a = a->next)
 					switch (a->type) {
@@ -2208,7 +3043,7 @@
 						break;
 					case ISAKMP_IPSEC_ATTRIB_ENCAP_MODE:
 						if (a->af == isakmp_attr_16 &&
-							a->u.attr_16 == encap_mode)
+							a->u.attr_16 == s->ipsec.encap_mode)
 							seen_encap = 1;
 						else
 							reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
@@ -2217,7 +3052,7 @@
 						if (dh_grp &&
 							a->af == isakmp_attr_16 &&
 							a->u.attr_16 ==
-							get_dh_group_ipsec(s->do_pfs)->ipsec_sa_id)
+							get_dh_group_ipsec(s->ipsec.do_pfs)->ipsec_sa_id)
 							seen_group = 1;
 						else
 							reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
@@ -2229,7 +3064,14 @@
 							reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
 						break;
 					case ISAKMP_IPSEC_ATTRIB_SA_LIFE_TYPE:
+						/* lifetime duration MUST follow lifetype attribute */
+						if (a->next->type == ISAKMP_IPSEC_ATTRIB_SA_LIFE_DURATION) {
+							lifetime_ipsec_process(s, a);
+						} else
+							reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+						break;
 					case ISAKMP_IPSEC_ATTRIB_SA_LIFE_DURATION:
+						/* already processed above in ISAKMP_IPSEC_ATTRIB_SA_LIFE_TYPE: */
 						break;
 					default:
 						reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
@@ -2249,26 +3091,57 @@
 					reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
 
 				if (reject == 0) {
-					ipsec_cry_algo =
+					s->ipsec.cry_algo =
 						get_algo(SUPP_ALGO_CRYPT, SUPP_ALGO_IPSEC_SA,
 						seen_enc, NULL, seen_keylen)->my_id;
-					ipsec_hash_algo =
+					s->ipsec.md_algo =
 						get_algo(SUPP_ALGO_HASH, SUPP_ALGO_IPSEC_SA,
 						seen_auth, NULL, 0)->my_id;
+					if (s->ipsec.cry_algo) {
+						gcry_cipher_algo_info(s->ipsec.cry_algo, GCRYCTL_GET_KEYLEN, NULL, &(s->ipsec.key_len));
+						gcry_cipher_algo_info(s->ipsec.cry_algo, GCRYCTL_GET_BLKLEN, NULL, &(s->ipsec.blk_len));
+						s->ipsec.iv_len = s->ipsec.blk_len;
+					} else {
+						s->ipsec.key_len = 0;
+						s->ipsec.iv_len = 0;
+						s->ipsec.blk_len = 8; /* seems to be this without encryption... */
+					}
+					s->ipsec.md_len = gcry_md_get_algo_dlen(s->ipsec.md_algo);
 					DEBUG(1, printf("IPSEC SA selected %s-%s\n",
 							get_algo(SUPP_ALGO_CRYPT,
 								SUPP_ALGO_IPSEC_SA, seen_enc, NULL,
 								seen_keylen)->name,
 							get_algo(SUPP_ALGO_HASH, SUPP_ALGO_IPSEC_SA,
 								seen_auth, NULL, 0)->name));
+					if (s->ipsec.cry_algo == GCRY_CIPHER_DES && !opt_1des) {
+						error(1, 0, "peer selected (single) DES as \"encrytion\" method.\n"
+							"This algorithm is considered to weak today\n"
+							"If your vpn concentrator admin still insists on using DES\n"
+							"use the \"--enable-1des\" option.\n");
+					} else if (s->ipsec.cry_algo == GCRY_CIPHER_NONE && !opt_no_encryption) {
+						error(1, 0, "peer selected NULL as \"encrytion\" method.\n"
+							"This is _no_ encryption at all.\n"
+							"Your traffic is still protected against modification with %s\n"
+							"If your vpn concentrator admin still insists on not using encryption\n"
+							"use the \"--enable-no-encryption\" option.\n",
+							get_algo(SUPP_ALGO_HASH, SUPP_ALGO_IPSEC_SA, seen_auth, NULL, 0)->name);
+					}
 				}
 			}
 			break;
 
-
 		case ISAKMP_PAYLOAD_N:
+			if (reject == 0 && rp->u.n.type == ISAKMP_N_IPSEC_RESPONDER_LIFETIME) {
+				if (rp->u.n.protocol == ISAKMP_IPSEC_PROTO_ISAKMP)
+					lifetime_ike_process(s, rp->u.n.attributes);
+				else if (rp->u.n.protocol == ISAKMP_IPSEC_PROTO_IPSEC_ESP)
+					lifetime_ipsec_process(s, rp->u.n.attributes);
+				else
+					DEBUG(2, printf("got unknown lifetime notice, ignoring..\n"));
+			}
 			break;
 		case ISAKMP_PAYLOAD_ID:
+			/* FIXME: Parse payload ID and add route-env in case of ipv4_prefix */
 			break;
 		case ISAKMP_PAYLOAD_KE:
 			ke = rp;
@@ -2287,20 +3160,98 @@
 	if (reject == 0 && dh_grp && (ke == NULL || ke->u.ke.length != dh_getlen(dh_grp)))
 		reject = ISAKMP_N_INVALID_KEY_INFORMATION;
 	if (reject != 0)
-		phase2_fatal(s, "quick mode response rejected [2]: %s", reject);
+		phase2_fatal(s, "quick mode response rejected [2]: %s(%d)", reject);
 
 	/* send final packet */
 	sendrecv_phase2(s, NULL, ISAKMP_EXCHANGE_IKE_QUICK,
-		msgid, 1, 0, 0, nonce, sizeof(nonce),
+		msgid, 1, 0, 0, nonce_i, sizeof(nonce_i),
 		nonce_r->u.nonce.data, nonce_r->u.nonce.length);
 
-	DEBUG(2, printf("S7.7\n"));
-	/* Create the delete payload, now that we have all the information.  */
+	DEBUGTOP(2, printf("S7.7 QM_packet3 sent - run script\n"));
+
+#endif //NORTELVPN
+
+	/* Set up the interface here so it's ready when our acknowledgement
+	 * arrives.  */
+	config_tunnel(s);
+	DEBUGTOP(2, printf("S7.8 setup ipsec tunnel\n"));
+	{
+#ifndef NORTELVPN
+
+		unsigned char *dh_shared_secret = NULL;
+
+		if (dh_grp) {
+			/* Determine the shared secret.  */
+			dh_shared_secret = xallocc(dh_getlen(dh_grp));
+			dh_create_shared(dh_grp, dh_shared_secret, ke->u.ke.data);
+			hex_dump("dh_shared_secret", dh_shared_secret, dh_getlen(dh_grp), NULL);
+		}
+		
+		s->ipsec.rx.key = gen_keymat(s, ISAKMP_IPSEC_PROTO_IPSEC_ESP, s->ipsec.rx.spi,
+			dh_shared_secret, dh_grp ? dh_getlen(dh_grp) : 0,
+			nonce_i, sizeof(nonce_i), nonce_r->u.nonce.data, nonce_r->u.nonce.length);
+		
+		s->ipsec.tx.key = gen_keymat(s, ISAKMP_IPSEC_PROTO_IPSEC_ESP, s->ipsec.tx.spi,
+			dh_shared_secret, dh_grp ? dh_getlen(dh_grp) : 0,
+			nonce_i, sizeof(nonce_i), nonce_r->u.nonce.data, nonce_r->u.nonce.length);
+		
+		if (dh_grp)
+			group_free(dh_grp);
+#endif		
+		
+		if ((opt_natt_mode == NATT_CISCO_UDP) && s->ipsec.peer_udpencap_port) {
+			s->esp_fd = make_socket(s, opt_udpencapport, s->ipsec.peer_udpencap_port);
+			s->ipsec.encap_mode = IPSEC_ENCAP_UDP_TUNNEL;
+			s->ipsec.natt_active_mode = NATT_ACTIVE_CISCO_UDP;
+		} else if (s->ipsec.encap_mode != IPSEC_ENCAP_TUNNEL) {
+			s->esp_fd = s->ike_fd;
+		} else {
+#ifdef IP_HDRINCL
+			int hincl = 1;
+#endif
+		
+			s->esp_fd = socket(PF_INET, SOCK_RAW, IPPROTO_ESP);
+			if (s->esp_fd == -1) {
+				close_tunnel();
+				error(1, errno, "Couldn't open socket of ESP. Maybe something registered ESP already.\nPlease try '--natt-mode force-natt' or disable whatever is using ESP.\nsocket(PF_INET, SOCK_RAW, IPPROTO_ESP)");
+			}
+#ifdef IP_HDRINCL
+			if (setsockopt(s->esp_fd, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof(hincl)) == -1) {
+				close_tunnel();
+				error(1, errno, "setsockopt(esp_fd, IPPROTO_IP, IP_HDRINCL, 1)");
+			}
+#endif
+		}
+		
+		s->ipsec.rx.seq_id = s->ipsec.tx.seq_id = 1;
+		DEBUGTOP(2, printf("S7.9 main loop (receive and transmit ipsec packets)\n"));
+		vpnc_doit(s);
+	}
+	
+	DEBUGTOP(2, printf("S7.10 send termination message\n"));
+	/* finished, send the delete message */
 	{
 		struct isakmp_payload *d_isakmp, *d_ipsec;
-		uint32_t del_msgid;
-
-		gcry_randomize((uint8_t *) & del_msgid, sizeof(del_msgid), GCRY_WEAK_RANDOM);
+		uint8_t del_msgid;
+
+		/* 2007-08-31 JKU/ZID: Sonicwall doesn't like the chained
+		 * request but wants them split. Cisco does fine with it */
+		gcry_create_nonce((uint8_t *) & del_msgid, sizeof(del_msgid));
+		d_ipsec = new_isakmp_payload(ISAKMP_PAYLOAD_D);
+		d_ipsec->u.d.doi = ISAKMP_DOI_IPSEC;
+		d_ipsec->u.d.protocol = ISAKMP_IPSEC_PROTO_IPSEC_ESP;
+		d_ipsec->u.d.spi_length = 4;
+		d_ipsec->u.d.num_spi = 2;
+		d_ipsec->u.d.spi = xallocc(2 * sizeof(uint8_t *));
+		d_ipsec->u.d.spi[0] = xallocc(d_ipsec->u.d.spi_length);
+		memcpy(d_ipsec->u.d.spi[0], &s->ipsec.rx.spi, 4);
+		d_ipsec->u.d.spi[1] = xallocc(d_ipsec->u.d.spi_length);
+		memcpy(d_ipsec->u.d.spi[1], &s->ipsec.tx.spi, 4);
+		sendrecv_phase2(s, d_ipsec, ISAKMP_EXCHANGE_INFORMATIONAL,
+			del_msgid, 1, NULL, NULL,
+			NULL, 0, NULL, 0);
+
+		gcry_create_nonce((uint8_t *) & del_msgid, sizeof(del_msgid));
 		d_isakmp = new_isakmp_payload(ISAKMP_PAYLOAD_D);
 		d_isakmp->u.d.doi = ISAKMP_DOI_IPSEC;
 		d_isakmp->u.d.protocol = ISAKMP_IPSEC_PROTO_ISAKMP;
@@ -2308,97 +3259,365 @@
 		d_isakmp->u.d.num_spi = 1;
 		d_isakmp->u.d.spi = xallocc(1 * sizeof(uint8_t *));
 		d_isakmp->u.d.spi[0] = xallocc(2 * ISAKMP_COOKIE_LENGTH);
-		memcpy(d_isakmp->u.d.spi[0] + ISAKMP_COOKIE_LENGTH * 0, s->i_cookie,
+		memcpy(d_isakmp->u.d.spi[0] + ISAKMP_COOKIE_LENGTH * 0, s->ike.i_cookie,
 			ISAKMP_COOKIE_LENGTH);
-		memcpy(d_isakmp->u.d.spi[0] + ISAKMP_COOKIE_LENGTH * 1, s->r_cookie,
+		memcpy(d_isakmp->u.d.spi[0] + ISAKMP_COOKIE_LENGTH * 1, s->ike.r_cookie,
 			ISAKMP_COOKIE_LENGTH);
-		d_ipsec = new_isakmp_payload(ISAKMP_PAYLOAD_D);
-		d_ipsec->next = d_isakmp;
-		d_ipsec->u.d.doi = ISAKMP_DOI_IPSEC;
-		d_ipsec->u.d.protocol = ISAKMP_IPSEC_PROTO_IPSEC_ESP;
-		d_ipsec->u.d.spi_length = 4;
-		d_ipsec->u.d.num_spi = 2;
-		d_ipsec->u.d.spi = xallocc(2 * sizeof(uint8_t *));
-		d_ipsec->u.d.spi[0] = xallocc(d_ipsec->u.d.spi_length);
-		memcpy(d_ipsec->u.d.spi[0], &s->tous_esp_spi, 4);
-		d_ipsec->u.d.spi[1] = xallocc(d_ipsec->u.d.spi_length);
-		memcpy(d_ipsec->u.d.spi[1], &s->tothem_esp_spi, 4);
-		phase2_authpacket(s, d_ipsec, ISAKMP_EXCHANGE_INFORMATIONAL,
-			del_msgid, &s->kill_packet, &s->kill_packet_size,
-			nonce, sizeof(nonce), nonce_r->u.nonce.data, nonce_r->u.nonce.length);
-		isakmp_crypt(s, s->kill_packet, s->kill_packet_size, 1);
-	}
-}
-#endif
-
-static void setup_link(struct sa_block *s)
-{
-	/* Set up the interface here so it's ready when our acknowledgement
-	 * arrives.  */
-	DEBUG(2, printf("S7.8\n"));
-	config_tunnel(s);
-	DEBUG(2, printf("S7.9\n"));
-	{
-		struct sockaddr_in tothem_dest, tous_dest;
-		int tunnelfd = sockfd;
-		memset(&tothem_dest, 0, sizeof(tothem_dest));
-		tothem_dest.sin_family = AF_INET;
-		memcpy(&tothem_dest.sin_addr, s->our_address, 4);
-		memcpy(&tous_dest, dest_addr, sizeof(tous_dest));
-		if (opt_udpencap && s->peer_udpencap_port) {
-			close(tunnelfd);
-			tunnelfd = make_socket(htons(opt_udpencapport));
-			tous_dest.sin_port = htons(s->peer_udpencap_port);
-			encap_mode = IPSEC_ENCAP_UDP_TUNNEL;
+		sendrecv_phase2(s, d_isakmp, ISAKMP_EXCHANGE_INFORMATIONAL,
+			del_msgid, 1, NULL, NULL,
+			NULL, 0, NULL, 0);
+	}
+}
+
+static int do_rekey(struct sa_block *s, struct isakmp_packet *r)
+{
+	struct isakmp_payload *rp, *ke = NULL, *nonce_i = NULL;
+	struct isakmp_attribute *a;
+	int seen_enc;
+	int seen_auth = 0, seen_encap = 0, seen_group = 0, seen_keylen = 0;
+	int nonce_i_copy_len;
+	struct group *dh_grp = NULL;
+	uint8_t nonce_r[20], *dh_public = NULL, *nonce_i_copy = NULL;
+	unsigned char *dh_shared_secret = NULL;
+	
+	if (get_dh_group_ipsec(s->ipsec.do_pfs)->my_id) {
+		dh_grp = group_get(get_dh_group_ipsec(s->ipsec.do_pfs)->my_id);
+		DEBUG(3, printf("len = %d\n", dh_getlen(dh_grp)));
+		dh_public = xallocc(dh_getlen(dh_grp));
+		dh_create_exchange(dh_grp, dh_public);
+		hex_dump("dh_public", dh_public, dh_getlen(dh_grp), NULL);
+	}
+	
+	rp = r->payload->next;
+	/* rp->type == ISAKMP_PAYLOAD_SA, verified by caller */
+	
+	if (rp->u.sa.doi != ISAKMP_DOI_IPSEC)
+		return ISAKMP_N_DOI_NOT_SUPPORTED;
+	if (rp->u.sa.situation != ISAKMP_IPSEC_SIT_IDENTITY_ONLY)
+		return ISAKMP_N_SITUATION_NOT_SUPPORTED;
+	if (rp->u.sa.proposals == NULL)
+		return ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+	if (rp->u.sa.proposals->u.p.prot_id != ISAKMP_IPSEC_PROTO_IPSEC_ESP)
+		return ISAKMP_N_INVALID_PROTOCOL_ID;
+	if (rp->u.sa.proposals->u.p.spi_size != 4)
+		return ISAKMP_N_INVALID_SPI;
+	if (rp->u.sa.proposals->u.p.transforms == NULL || rp->u.sa.proposals->u.p.transforms->next != NULL)
+		return ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+	
+	seen_enc = rp->u.sa.proposals->u.p.transforms->u.t.id;
+	
+	memcpy(&s->ipsec.tx.spi, rp->u.sa.proposals->u.p.spi, 4);
+	
+	for (a = rp->u.sa.proposals->u.p.transforms->u.t.attributes; a; a = a->next)
+		switch (a->type) {
+		case ISAKMP_IPSEC_ATTRIB_AUTH_ALG:
+			if (a->af == isakmp_attr_16)
+				seen_auth = a->u.attr_16;
+			else
+				return ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+			break;
+		case ISAKMP_IPSEC_ATTRIB_ENCAP_MODE:
+			if (a->af == isakmp_attr_16 &&
+				a->u.attr_16 == (
+					(s->ipsec.natt_active_mode != NATT_ACTIVE_CISCO_UDP) ?
+					s->ipsec.encap_mode :
+					IPSEC_ENCAP_TUNNEL /* cisco-udp claims to use encap tunnel... */
+				))
+				seen_encap = 1;
+			else
+				return ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+			break;
+		case ISAKMP_IPSEC_ATTRIB_GROUP_DESC:
+			if (dh_grp && a->af == isakmp_attr_16 &&
+				a->u.attr_16 == get_dh_group_ipsec(s->ipsec.do_pfs)->ipsec_sa_id)
+				seen_group = 1;
+			else
+				return ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+			break;
+		case ISAKMP_IPSEC_ATTRIB_KEY_LENGTH:
+			if (a->af == isakmp_attr_16)
+				seen_keylen = a->u.attr_16;
+			else
+				return ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+			break;
+		case ISAKMP_IPSEC_ATTRIB_SA_LIFE_TYPE:
+			/* lifetime duration MUST follow lifetype attribute */
+			if (a->next->type == ISAKMP_IPSEC_ATTRIB_SA_LIFE_DURATION) {
+				lifetime_ipsec_process(s, a);
+			} else
+				return ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+			break;
+		case ISAKMP_IPSEC_ATTRIB_SA_LIFE_DURATION:
+			/* already processed above in ISAKMP_IPSEC_ATTRIB_SA_LIFE_TYPE: */
+			break;
+		default:
+			return ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
+			break;
 		}
-		DEBUG(2, printf("S7.10\n"));
-		vpnc_doit(s->tous_esp_spi, tous_keys, &tothem_dest,
-			s->tothem_esp_spi, tothem_keys, (struct sockaddr_in *)&tous_dest,
-			s->tun_fd, ipsec_hash_algo, ipsec_cry_algo,
-			s->kill_packet, s->kill_packet_size, dest_addr,
-			encap_mode, tunnelfd,
-			config[CONFIG_PID_FILE]);
-	}
+	if (!seen_auth || !seen_encap || (dh_grp && !seen_group))
+		return ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+	
+	/* FIXME: Current code has a limitation that will cause problems if
+	 * different algorithms are negotiated during re-keying
+	 */
+	if ((get_algo(SUPP_ALGO_HASH, SUPP_ALGO_IPSEC_SA, seen_auth, NULL, 0) == NULL) || 
+	    (get_algo(SUPP_ALGO_CRYPT, SUPP_ALGO_IPSEC_SA, seen_enc, NULL, seen_keylen) == NULL)) {
+		printf("\nFIXME: vpnc doesn't support change of algorightms during rekeying\n");
+		return ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+	}
+	
+	/* we don't want to change ciphers during rekeying */
+	if (s->ipsec.cry_algo != get_algo(SUPP_ALGO_CRYPT, SUPP_ALGO_IPSEC_SA, seen_enc,  NULL, seen_keylen)->my_id)
+		return ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+	if (s->ipsec.md_algo  != get_algo(SUPP_ALGO_HASH,  SUPP_ALGO_IPSEC_SA, seen_auth, NULL, 0)->my_id)
+		return ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+	
+	for (rp = rp->next; rp; rp = rp->next)
+		switch (rp->type) {
+		case ISAKMP_PAYLOAD_ID:
+			/* FIXME: Parse payload ID and add route-env in case of ipv4_prefix */
+			break;
+		case ISAKMP_PAYLOAD_KE:
+			ke = rp;
+			break;
+		case ISAKMP_PAYLOAD_NONCE:
+			nonce_i = rp;
+			break;
+		default:
+			return ISAKMP_N_INVALID_PAYLOAD_TYPE;
+			break;
+		}
+	
+	if ((dh_grp && ke == NULL) || nonce_i == NULL)
+		return ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+	
+	DEBUG(3, printf("everything fine so far...\n"));
+	gcry_create_nonce((uint8_t *) nonce_r, sizeof(nonce_r));
+	gcry_create_nonce((uint8_t *) & s->ipsec.rx.spi, sizeof(s->ipsec.rx.spi));
+	
+	if (dh_grp) {
+		/* Determine the shared secret.  */
+		dh_shared_secret = xallocc(dh_getlen(dh_grp));
+		dh_create_shared(dh_grp, dh_shared_secret, ke->u.ke.data);
+		hex_dump("dh_shared_secret", dh_shared_secret, dh_getlen(dh_grp), NULL);
+	}
+	
+	free(s->ipsec.rx.key);
+	free(s->ipsec.tx.key);
+		
+	s->ipsec.rx.key = gen_keymat(s, ISAKMP_IPSEC_PROTO_IPSEC_ESP, s->ipsec.rx.spi,
+		dh_shared_secret, dh_grp ? dh_getlen(dh_grp) : 0,
+		nonce_i->u.nonce.data, nonce_i->u.nonce.length, nonce_r, sizeof(nonce_r));
+	
+	s->ipsec.tx.key = gen_keymat(s, ISAKMP_IPSEC_PROTO_IPSEC_ESP, s->ipsec.tx.spi,
+		dh_shared_secret, dh_grp ? dh_getlen(dh_grp) : 0,
+		nonce_i->u.nonce.data, nonce_i->u.nonce.length, nonce_r, sizeof(nonce_r));
+	
+	s->ipsec.rx.key_cry = s->ipsec.rx.key;
+	s->ipsec.rx.key_md  = s->ipsec.rx.key + s->ipsec.key_len;
+	s->ipsec.tx.key_cry = s->ipsec.tx.key;
+	s->ipsec.tx.key_md  = s->ipsec.tx.key + s->ipsec.key_len;
+	
+	nonce_i_copy_len = nonce_i->u.nonce.length;
+	nonce_i_copy = xallocc(nonce_i_copy_len);
+	memcpy(nonce_i_copy, nonce_i->u.nonce.data, nonce_i_copy_len);
+	
+	s->ipsec.rx.seq_id = s->ipsec.tx.seq_id = 1;
+	s->ipsec.life.start = time(NULL);
+	s->ipsec.life.tx = 0;
+	s->ipsec.life.rx = 0;
+	
+	if (s->ipsec.cry_algo) {
+		gcry_cipher_setkey(s->ipsec.rx.cry_ctx, s->ipsec.rx.key_cry, s->ipsec.key_len);
+		gcry_cipher_setkey(s->ipsec.tx.cry_ctx, s->ipsec.tx.key_cry, s->ipsec.key_len);
+	}
+	
+	/* use request as template and just exchange some values */
+	/* this overwrites data in nonce_i, ke! */
+	rp = r->payload->next;
+	/* SA, change the SPI */
+	memcpy(rp->u.sa.proposals->u.p.spi, &s->ipsec.rx.spi, 4);
+	
+	for (rp = rp->next; rp; rp = rp->next)
+		switch (rp->type) {
+		case ISAKMP_PAYLOAD_ID:
+			break;
+		case ISAKMP_PAYLOAD_KE:
+			memcpy(rp->u.ke.data, dh_public, dh_getlen(dh_grp));
+			break;
+		case ISAKMP_PAYLOAD_NONCE:
+			memcpy(rp->u.nonce.data, nonce_r, sizeof(nonce_r));
+			break;
+		default:
+			assert(0);
+			break;
+		}
+	
+	sendrecv_phase2(s, r->payload->next, ISAKMP_EXCHANGE_IKE_QUICK,
+		r->message_id, 0, 0, 0, nonce_i_copy, nonce_i_copy_len, 0,0);
+	unpack_verify_phase2(s, r_packet, r_length, &r, NULL, 0);
+	free(nonce_i_copy);
+	/* don't care about answer ... */
+	
+	return 0;
+}
+
+void process_late_ike(struct sa_block *s, uint8_t *r_packet, ssize_t r_length)
+{
+	int reject;
+	struct isakmp_packet *r;
+	struct isakmp_payload *rp;
+	
+	DEBUG(2,printf("got late ike paket: %zd bytes\n", r_length));
+	/* we should ignore resent pakets here.
+	 * unpack_verify_phase2 will fail to decode them probably */
+	reject = unpack_verify_phase2(s, r_packet, r_length, &r, NULL, 0);
+	
+	/* just ignore broken stuff for now */
+	if (reject != 0)
+		return;
+	
+	/* everything must be encrypted by now */
+	if (r->payload == NULL || r->payload->type != ISAKMP_PAYLOAD_HASH)
+		return;
+	
+	/* empty packet? well, nothing to see here */
+	if (r->payload->next == NULL)
+		return;
+	
+	/* do we get an SA proposal for rekeying? */
+	if (r->exchange_type == ISAKMP_EXCHANGE_IKE_QUICK &&
+		r->payload->next->type == ISAKMP_PAYLOAD_SA) {
+		reject = do_rekey(s, r);
+		DEBUG(3, printf("do_rekey returned: %d\n", reject));
+		return;
+	}
+
+	if (r->exchange_type == ISAKMP_EXCHANGE_INFORMATIONAL) {
+		/* Search for notify payloads */
+		for (rp = r->payload->next; rp; rp = rp->next) {
+			if (rp->type != ISAKMP_PAYLOAD_N)
+				continue;
+			/* did we get a DPD request or ACK? */
+			if (rp->u.n.protocol != ISAKMP_IPSEC_PROTO_ISAKMP) {
+				DEBUG(2, printf("got non isakmp-notify, ignoring...\n"));
+				continue;
+			}
+			if (rp->u.n.type == ISAKMP_N_R_U_THERE) {
+				uint32_t seq;
+				if (rp->u.n.data_length != 4) {
+					DEBUG(2, printf("ignoring bad data length R-U-THERE request\n"));
+					continue;
+				}
+				memcpy(&seq, rp->u.n.data, 4);
+				send_dpd(s, 1, seq);
+				DEBUG(2, printf("got r-u-there request sent ack\n"));
+				continue;
+			} else if (rp->u.n.type == ISAKMP_N_R_U_THERE_ACK) {
+				uint32_t seqack;
+				if (rp->u.n.data_length != 4) {
+					DEBUG(2, printf("ignoring bad data length R-U-THERE-ACK\n"));
+					continue;
+				}
+				memcpy(&seqack, rp->u.n.data, 4);
+				if (seqack == s->ike.dpd_seqno) {
+					s->ike.dpd_seqno_ack = seqack;
+				} else {
+					DEBUG(2, printf("ignoring r-u-there ack %u (expecting %u)\n", seqack, s->ike.dpd_seqno));
+					continue;
+				}
+				DEBUG(2, printf("got r-u-there ack\n"));
+			}
+		}
+	}
+	
+	/* check if our isakmp sa gets deleted */
+	for (rp = r->payload->next; rp; rp = rp->next) {
+		/* search for delete payloads */
+		if (rp->type != ISAKMP_PAYLOAD_D)
+			continue;
+		
+		/* skip ipsec-esp delete */
+		if (rp->u.d.protocol != ISAKMP_IPSEC_PROTO_ISAKMP) {
+			DEBUG(2, printf("got non isakmp-delete, ignoring...\n"));
+			continue;
+		};
+		
+		/*
+		 * RFC 2408, 3.15 Delete Payload
+		 * it is not stated that the SPI field of a delete
+		 * payload can be ignored, because it is given in
+		 * the headers, but I assume so. In other cases
+		 * RFC 2408 (notifications) states this.
+		 */
+		do_kill = -1;
+		DEBUG(2, printf("got isakmp-delete, terminating...\n"));
+		return;
+	}
+	
+	return;
 }
 
 int main(int argc, char **argv)
 {
 	int do_load_balance;
 	const uint8_t hex_test[] = { 0, 1, 2, 3 };
+	struct sa_block oursa[1];
+	struct sa_block *s = oursa;
 
 	test_pack_unpack();
+#if defined(__CYGWIN__)
+	gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+#endif
 	gcry_check_version("1.1.90");
 	gcry_control(GCRYCTL_INIT_SECMEM, 16384, 0);
 	group_init();
-	memset(oursa, 0, sizeof(oursa));
+	
+	memset(s, 0, sizeof(*s));
+	s->ipsec.encap_mode = IPSEC_ENCAP_TUNNEL;
+	s->ike.timeout = 1000; /* 1 second */
 
 	do_config(argc, argv);
 	
-	hex_dump("hex_test", hex_test, sizeof(hex_test));
-
-	DEBUG(1, printf("vpnc version " VERSION "\n"));
-	DEBUG(2, printf("S1\n"));
-	dest_addr = init_sockaddr(config[CONFIG_IPSEC_GATEWAY], ISAKMP_PORT);
-	DEBUG(2, printf("S2\n"));
-	local_port = htons(atoi(config[CONFIG_LOCAL_PORT]));
-	sockfd = make_socket(local_port);
-	DEBUG(2, printf("S3\n"));
-	setup_tunnel(oursa);
+	DEBUG(1, printf("\nvpnc version " VERSION "\n"));
+	hex_dump("hex_test", hex_test, sizeof(hex_test), NULL);
+
+	DEBUGTOP(2, printf("S1 init_sockaddr\n"));
+	init_sockaddr(&s->dst, config[CONFIG_IPSEC_GATEWAY]);
+	init_sockaddr(&s->opt_src_ip, config[CONFIG_LOCAL_ADDR]);
+	DEBUGTOP(2, printf("S2 make_socket\n"));
+	s->ike.src_port = atoi(config[CONFIG_LOCAL_PORT]);
+	s->ike.dst_port = ISAKMP_PORT;
+	s->ike_fd = make_socket(s, s->ike.src_port, s->ike.dst_port);
+	DEBUGTOP(2, printf("S3 setup_tunnel\n"));
+	setup_tunnel(s);
 
 	do_load_balance = 0;
 	do {
-		DEBUG(2, printf("S4\n"));
-		do_phase_1(config[CONFIG_IPSEC_ID], config[CONFIG_IPSEC_SECRET], oursa);
-		DEBUG(2, printf("S5\n"));
-		/* if (oursa->auth_algo == IKE_AUTH_XAUTHInitPreShared) */
-		do_load_balance = do_phase_2_xauth(oursa);
-		DEBUG(2, printf("S6\n"));
-		/* if (do_load_balance == 0) */
-		do_load_balance = do_phase_2_config(oursa);
-		do_phase_2(oursa);
+		DEBUGTOP(2, printf("S4 do_phase1\n"));
+		do_phase1(config[CONFIG_IPSEC_ID], config[CONFIG_IPSEC_SECRET], s);
+		DEBUGTOP(2, printf("S5 do_phase2_xauth\n"));
+
+#ifdef NORTELVPN
+		do_load_balance = do_phase2_xauth(s);
+		DEBUGTOP(2, printf("S6 do_phase2_config\n"));
+		do_load_balance = do_phase2_config(s);
+		DEBUGTOP(2, printf("S6 do_phase2\n"));
+		do_phase2(s);
+#else
+		/* FIXME: Create and use a generic function in supp.[hc] */
+		if (s->ike.auth_algo >= IKE_AUTH_HybridInitRSA)
+			do_load_balance = do_phase2_xauth(s);
+		DEBUGTOP(2, printf("S6 do_phase2_config\n"));
+		if ((opt_vendor == VENDOR_CISCO) && (do_load_balance == 0))
+			do_load_balance = do_phase2_config(s);
+#endif
+
 	} while (do_load_balance);
-	DEBUG(2, printf("S7\n"));
-	setup_link(oursa);
+	DEBUGTOP(2, printf("S7 setup_link (phase 2 + main_loop)\n"));
+	setup_link(s);
+	DEBUGTOP(2, printf("S8 close_tunnel\n"));
+	close_tunnel();
 
 	return 0;
 }

Modified: branches/vpnc-nortel/vpnc.conf
==============================================================================
--- branches/vpnc-nortel/vpnc.conf (original)
+++ branches/vpnc-nortel/vpnc.conf Wed Jun 11 01:19:11 2008
@@ -1,4 +1,6 @@
-IPSec gateway 131.246.118.240
-IPSec ID unikl
-IPSec secret unikl
-Xauth username abcdef
+IPSec gateway <gateway>
+IPSec ID <group-id>
+IPSec secret <group-psk>
+IKE Authmode hybrid
+Xauth username <username>
+Xauth password <password>

Modified: branches/vpnc-nortel/vpnc.h
==============================================================================
--- branches/vpnc-nortel/vpnc.h (original)
+++ branches/vpnc-nortel/vpnc.h Wed Jun 11 01:19:11 2008
@@ -14,52 +14,18 @@
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   $Id$
 */
 
 #ifndef __VPNC_H__
 #define __VPNC_H__
 
-#include <sys/socket.h>
-#include <net/if.h>
-#include "sysdep.h"
-#include "isakmp.h"
+#include "tunip.h"
 
-typedef struct {
-	const char *name;
-	int my_id, ike_sa_id, ipsec_sa_id;
-	int keylen;
-} supported_algo_t;
-
-struct sa_block {
-	int tun_fd;
-	char tun_name[IFNAMSIZ];
-	uint8_t i_cookie[ISAKMP_COOKIE_LENGTH];
-	uint8_t r_cookie[ISAKMP_COOKIE_LENGTH];
-	uint8_t *key; /* ike encryption key */
-	size_t keylen;
-	uint8_t *initial_iv;
-	uint8_t *skeyid_a;
-	uint8_t *skeyid_d;
-	int auth_algo, cry_algo, md_algo;
-	size_t ivlen, md_len;
-	uint8_t current_iv_msgid[4];
-	uint8_t *current_iv;
-	uint8_t our_address[4], our_netmask[4];
-	uint32_t tous_esp_spi, tothem_esp_spi;
-	uint8_t *kill_packet;
-	size_t kill_packet_size;
-	uint16_t peer_udpencap_port;
-	int do_pfs;
-};
-
-extern struct sa_block oursa[];
-
-extern supported_algo_t supp_dh_group[];
-extern supported_algo_t supp_hash[];
-extern supported_algo_t supp_crypt[];
-extern supported_algo_t supp_auth[];
-
-extern const supported_algo_t *get_dh_group_ike(void);
-extern const supported_algo_t *get_dh_group_ipsec(int server_setting);
+void process_late_ike(struct sa_block *s, uint8_t *r_packet, ssize_t r_length);
+void keepalive_ike(struct sa_block *s);
+void dpd_ike(struct sa_block *s);
+void print_vid(const unsigned char *vid, uint16_t len);
 
 #endif



More information about the vpnc-devel mailing list