diff options
author | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2006-05-22 05:12:18 +0000 |
---|---|---|
committer | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2006-05-22 05:12:18 +0000 |
commit | aa0f5b38aec14428b4b80e06f90ff781f8bca5f1 (patch) | |
tree | 95f3d0c8cb0d59d88900dbbd72110d7ab6e15b2a /linux/net/ipsec | |
parent | 7c383bc22113b23718be89fe18eeb251942d7356 (diff) | |
download | vyos-strongswan-aa0f5b38aec14428b4b80e06f90ff781f8bca5f1.tar.gz vyos-strongswan-aa0f5b38aec14428b4b80e06f90ff781f8bca5f1.zip |
Import initial strongswan 2.7.0 version into SVN.
Diffstat (limited to 'linux/net/ipsec')
46 files changed, 22691 insertions, 0 deletions
diff --git a/linux/net/ipsec/.cvsignore b/linux/net/ipsec/.cvsignore new file mode 100644 index 000000000..63cb2042f --- /dev/null +++ b/linux/net/ipsec/.cvsignore @@ -0,0 +1,47 @@ +.addrtoa.o.flags +.adler32.o.flags +.cbc_enc.o.flags +.datatot.o.flags +.deflate.o.flags +.des_enc.o.flags +.ecb_enc.o.flags +.goodmask.o.flags +.infblock.o.flags +.infcodes.o.flags +.inffast.o.flags +.inflate.o.flags +.inftrees.o.flags +.infutil.o.flags +.ipcomp.o.flags +.ipsec.o.flags +.ipsec_init.o.flags +.ipsec_life.o.flags +.ipsec_md5c.o.flags +.ipsec_proc.o.flags +.ipsec_radij.o.flags +.ipsec_rcv.o.flags +.ipsec_sa.o.flags +.ipsec_sha1.o.flags +.ipsec_tunnel.o.flags +.pfkey_v2.o.flags +.pfkey_v2_build.o.flags +.pfkey_v2_debug.o.flags +.pfkey_v2_ext_bits.o.flags +.pfkey_v2_ext_process.o.flags +.pfkey_v2_parse.o.flags +.pfkey_v2_parser.o.flags +.prng.o.flags +.radij.o.flags +.rangetoa.o.flags +.satoa.o.flags +.set_key.o.flags +.subnetof.o.flags +.subnettoa.o.flags +.sysctl_net_ipsec.o.flags +.trees.o.flags +.ultoa.o.flags +.version.o.flags +.zutil.o.flags +version.c +.*.o.flags +*.o diff --git a/linux/net/ipsec/Config.in b/linux/net/ipsec/Config.in new file mode 100644 index 000000000..379738a69 --- /dev/null +++ b/linux/net/ipsec/Config.in @@ -0,0 +1,41 @@ +# +# IPSEC configuration +# Copyright (C) 1998, 1999, 2000,2001 Richard Guy Briggs. +# +# 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. See <http://www.fsf.org/copyleft/gpl.txt>. +# +# 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. +# +# RCSID $Id: Config.in,v 1.3 2004/03/30 21:11:11 as Exp $ + +comment 'IPsec options (strongSwan)' + +bool ' IPSEC: IP-in-IP encapsulation (tunnel mode)' CONFIG_IPSEC_IPIP + +bool ' IPSEC: Authentication Header' CONFIG_IPSEC_AH +if [ "$CONFIG_IPSEC_AH" = "y" -o "$CONFIG_IPSEC_ESP" = "y" ]; then + bool ' HMAC-MD5 authentication algorithm' CONFIG_IPSEC_AUTH_HMAC_MD5 + bool ' HMAC-SHA1 authentication algorithm' CONFIG_IPSEC_AUTH_HMAC_SHA1 +fi + +bool ' IPSEC: Encapsulating Security Payload' CONFIG_IPSEC_ESP +if [ "$CONFIG_IPSEC_ESP" = "y" ]; then + bool ' 3DES encryption algorithm' CONFIG_IPSEC_ENC_3DES +fi + +bool ' IPSEC Modular Extensions' CONFIG_IPSEC_ALG +if [ "$CONFIG_IPSEC_ALG" != "n" ]; then + source net/ipsec/alg/Config.in +fi + +bool ' IPSEC: IP Compression' CONFIG_IPSEC_IPCOMP + +bool ' IPSEC Debugging Option' CONFIG_IPSEC_DEBUG + +bool ' IPSEC NAT-Traversal' CONFIG_IPSEC_NAT_TRAVERSAL diff --git a/linux/net/ipsec/Makefile b/linux/net/ipsec/Makefile new file mode 100644 index 000000000..6d834a067 --- /dev/null +++ b/linux/net/ipsec/Makefile @@ -0,0 +1,529 @@ +# Makefile for KLIPS kernel code as a module +# Copyright (C) 1998, 1999, 2000,2001 Richard Guy Briggs. +# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org> +# +# 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. See <http://www.fsf.org/copyleft/gpl.txt>. +# +# 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. +# +# RCSID $Id: Makefile,v 1.2 2004/03/22 21:53:19 as Exp $ +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# + +ifeq ($(strip $(KLIPSMODULE)),) +FREESWANSRCDIR=. +else +FREESWANSRCDIR=../../.. +endif +-include ${FREESWANSRCDIR}/Makefile.ver + +ifeq ($(strip $(KLIPS_TOP)),) +KLIPS_TOP=../.. +endif + +ifneq ($(strip $(KLIPSMODULE)),) + +ifndef TOPDIR +TOPDIR:=/usr/src/linux +endif +export TOPDIR + +endif + +# +# This magic from User-Mode-Linux list. It gets list of -I options, as +# UML needs some extra, that varry by revision. +# +KERNEL_CFLAGS= $(shell $(MAKE) -C $(TOPDIR) --no-print-directory -s -f Makefile ARCH=$(ARCH) MAKEFLAGS= script SCRIPT='@echo $$(CFLAGS)' ) + +MODULE_CFLAGS= $(shell $(MAKE) -C $(TOPDIR) --no-print-directory -s -f Makefile ARCH=$(ARCH) MAKEFLAGS= script SCRIPT='@echo $$(MODFLAGS)' ) + +subdir- := +subdir-n := +subdir-y := +subdir-m := + + +MOD_DESTDIR:=net/ipsec + +export TOPDIR + +all: ipsec.o + +foo: + echo KERNEL: ${KERNEL_CFLAGS} + echo MODULE: ${MODULE_CFLAGS} + +ipsec.o: foo + +O_TARGET := ipsec.o +obj-y := ipsec_init.o ipsec_sa.o ipsec_radij.o radij.o +obj-y += ipsec_life.o ipsec_proc.o +obj-y += ipsec_tunnel.o ipsec_xmit.o ipsec_rcv.o +obj-y += sysctl_net_ipsec.o +obj-y += pfkey_v2.o pfkey_v2_parser.o pfkey_v2_ext_process.o +#obj-y += version.o + +LIBDESDIR=${KLIPS_TOP}/crypto/ciphers/des +VPATH+= ${LIBDESDIR} + +include ${LIBDESDIR}/Makefile.objs + +LIBFREESWANDIR=${KLIPS_TOP}/lib/libfreeswan +VPATH+=${LIBFREESWANDIR} + +include ${LIBFREESWANDIR}/Makefile.objs + +# IPcomp stuff +obj-$(CONFIG_IPSEC_IPCOMP) += ipcomp.o + +LIBZLIBSRCDIR=${KLIPS_TOP}/lib/zlib +VPATH+=${LIBZLIBSRCDIR} + +# LIBCRYPTO Will be overriden eg. when doing "make module" +# from freeswan-2 src root +# Default value assumes already symlinked libcrypto under $TOPDIR/lib +LIBCRYPTO=$(TOPDIR)/lib/libcrypto +VPATH+=${LIBCRYPTO} + +alg/static_init_mod.o: dummy + $(MAKE) -C alg CC='$(CC)' TOPDIR='$(TOPDIR)' \ + 'EXTRA_CFLAGS=$(EXTRA_CFLAGS)' \ + static_init_mod.o + + +alg_modules: dummy + $(MAKE) $(MODULE_FLAGS) -C alg CC='$(CC)' TOPDIR='$(TOPDIR)' \ + 'LIBCRYPTO=$(LIBCRYPTO)' \ + 'EXTRA_CFLAGS=$(EXTRA_CFLAGS)' \ + modules + +# CFLAGS='$(CFLAGS)' \ +# MODULE_CFLAGS='$(MODULE_CFLAGS)' KERNEL_CFLAGS='$(KERNEL_CFLAGS)' \ +# +include ${LIBZLIBSRCDIR}/Makefile.objs + +export-objs := radij.o + +# New handling of KERNEL_CFLAGS and MODULE_CFLAGS introduced in 2.0 +# tosses export-objs logic :( +CFLAGS_ipsec_alg.o += -DEXPORT_SYMTAB +obj-$(CONFIG_IPSEC_ALG) +=ipsec_alg.o alg/static_init_mod.o +export-objs += ipsec_alg.o +subdir-m += alg + +EXTRA_CFLAGS += $(ALGO_FLAGS) + + +# include file with .h-style macros that would otherwise be created by +# config. Must occur before other includes. +ifneq ($(strip $(MODULE_DEF_INCLUDE)),) +EXTRA_CFLAGS += -include ${MODULE_DEF_INCLUDE} +endif + +# 'override CFLAGS' should really be 'EXTRA_CFLAGS' +#EXTRA_CFLAGS += -nostdinc +EXTRA_CFLAGS += -I${KLIPS_TOP}/include + +EXTRA_CFLAGS += -I${TOPDIR}/include +EXTRA_CFLAGS += -I${LIBZLIBSRCDIR} + +ifeq ($(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION),2.4.2-2) +EXTRA_CFLAGS += -DREDHAT_BOGOSITY +endif + +ifeq ($(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION),2.4.3-12) +EXTRA_CFLAGS += -DREDHAT_BOGOSITY +endif + + +#ifeq ($(CONFIG_IPSEC_DEBUG),y) +#EXTRA_CFLAGS += -g +#endif + +#ifeq ($(CONFIG_IPSEC_ALG), y) +EXTRA_CFLAGS += -DCONFIG_IPSEC_ALG +#endif +# MOST of these flags are in KERNEL_CFLAGS already! + +EXTRA_CFLAGS += $(KLIPSCOMPILE) +EXTRA_CFLAGS += -Wall +#EXTRA_CFLAGS += -Werror +#EXTRA_CFLAGS += -Wconversion +#EXTRA_CFLAGS += -Wmissing-prototypes +# cannot use both -Wpointer-arith and -Werror with CONFIG_HIGHMEM +# include/linux/highmem.h has an inline function definition that uses void* arithmentic. +ifeq ($(CONFIG_NOHIGHMEM),y) +EXTRA_CFLAGS += -Wpointer-arith +endif +#EXTRA_CFLAGS += -Wcast-qual +#EXTRA_CFLAGS += -Wmissing-declarations +#EXTRA_CFLAGS += -Wstrict-prototypes +#EXTRA_CFLAGS += -pedantic +#EXTRA_CFLAGS += -O3 +#EXTRA_CFLAGS += -W +#EXTRA_CFLAGS += -Wwrite-strings +#EXTRA_CFLAGS += -Wbad-function-cast + +ifneq ($(strip $(KLIPSMODULE)),) +# for when we aren't building in the kernel tree +EXTRA_CFLAGS += -DARCH=${ARCH} +EXTRA_CFLAGS += -DMODVERSIONS +EXTRA_CFLAGS += -include ${TOPDIR}/include/linux/modversions.h +EXTRA_CFLAGS += ${MODULE_CFLAGS} +endif + +EXTRA_CFLAGS += ${KERNEL_CFLAGS} + + +# GCC 3.2 (and we presume any other 3.x) wants -falign-functions +# in place of the traditional -malign-functions. Getting this +# wrong leads to a warning, which is fatal due to our use of -Werror. +ifeq ($(patsubst 3.%,3,$(shell $(CC) -dumpversion)),3) +override CFLAGS:=$(subst -malign-functions=,-falign-functions=,$(CFLAGS)) +endif + + +obj-$(CONFIG_IPSEC_AUTH_HMAC_MD5) += ipsec_md5c.o +obj-$(CONFIG_IPSEC_AUTH_HMAC_SHA1) += ipsec_sha1.o + +### +### Pre Rules.make +### +# undo O_TARGET, obj-y if no static +ifneq ($(CONFIG_IPSEC),y) +O_TARGET := +ipsec_obj-y := $(obj-y) +obj-y := +subdir-y := +endif + +# Define obj-m if modular ipsec +ifeq ($(CONFIG_IPSEC),m) +obj-m += ipsec.o +endif + + +# These rules translate from new to old makefile rules +# Translate to Rules.make lists. +multi-used := $(filter $(list-multi), $(obj-y) $(obj-m)) +multi-objs := $(foreach m, $(multi-used), $($(basename $(m))-objs)) +active-objs := $(sort $(multi-objs) $(obj-y) $(obj-m)) +O_OBJS := $(obj-y) +M_OBJS := $(obj-m) +MIX_OBJS := $(filter $(export-objs), $(active-objs)) +OX_OBJS := $(export-objs) +SUB_DIRS := $(subdir-y) +ALL_SUB_DIRS := $(subdir-y) $(subdir-m) +MOD_SUB_DIRS := $(subdir-m) + +# dunno why, but some 2.2 setups may need explicit -DEXPORT_SYMTAB +# uncomment next line if ipsec_alg.c compilation fails with +# "parse error before `EXPORT_SYMTAB_not_defined'" --Juanjo +# CFLAGS_ipsec_alg.o += -DEXPORT_SYMTAB +# + +include $(TOPDIR)/Rules.make + +### +### Post Rules.make +### +# for modular ipsec, no O_TARGET defined => define ipsec.o creation rules +ifeq ($(CONFIG_IPSEC),m) +ipsec.o : $(ipsec_obj-y) + rm -f $@ + $(LD) $(LD_EXTRAFLAGS) -r $(ipsec_obj-y) -o $@ +endif + +$(ipsec_obj-y) $(obj-y) $(obj-m): $(TOPDIR)/include/linux/config.h $(TOPDIR)/include/linux/autoconf.h + +#$(obj-y) $(obj-m): $(TOPDIR)/include/linux/config.h $(TOPDIR)/include/linux/autoconf.h + +USE_STANDARD_AS_RULE=true + +clean: + $(MAKE) -C alg clean + -rm -f *.o + -rm -f .*.o.flags + -rm version.c + +tags TAGS: *.c *.h libfreeswan/*.c libfreeswan/*.h + etags *.c ../../include/*.h ../../include/freeswan/*.h + ctags *.c ../../include/*.h ../../include/freeswan/*.h + +tar: + tar -cvf /dev/f1 . + +# +# $Log: Makefile,v $ +# Revision 1.2 2004/03/22 21:53:19 as +# merged alg-0.8.1 branch with HEAD +# +# Revision 1.1.4.1 2004/03/16 09:48:19 as +# alg-0.8.1rc12 patch merged +# +# Revision 1.1 2004/03/15 20:35:26 as +# added files from freeswan-2.04-x509-1.5.3 +# +# Revision 1.61 2003/06/22 21:07:46 mcr +# adjusted TAGS target in makefile to be useful in 2.00 source layout. +# +# Revision 1.60 2003/05/03 23:45:23 mcr +# rm .o.flags and generated version.c file. +# +# Revision 1.59 2003/02/12 19:32:47 rgb +# Added ipsec_xmit to the list of object files. +# +# Revision 1.58 2003/01/03 00:36:44 rgb +# +# Added emacs compile-command. +# +# Revision 1.57 2002/11/08 23:49:53 mcr +# use KERNEL_CFLAGS and MODULE_CFLAGS to get proper list +# of include directories. +# This also eliminates some of the guesswork in the kernel +# configuration file. +# +# Revision 1.56 2002/11/08 23:23:18 mcr +# attempt to guess kernel compilation flags (i.e. list of -I) +# by using some magic targets in the kernel makefile. +# +# Revision 1.55 2002/11/08 10:13:33 mcr +# added additional include directories for module builds for 2.4.19. +# +# Revision 1.54 2002/10/20 06:10:30 build +# CONFIG_NOHIGHMEM for -Wpointer-arith RPM building issues. +# +# Revision 1.53 2002/10/17 16:32:01 mcr +# enable standard AS rules. +# +# Revision 1.52 2002/10/06 06:13:44 sam +# Altering order of includes, so that architecture-specific header files, +# used for building RPM modules specifically, are processed first. +# +# Revision 1.51 2002/10/05 15:06:38 dhr +# +# - To allow for gcc3.2 (used in Red Hat Linux 8.0): adjust CFLAGS (set +# by kernel machinery) to use -falign-functions= in place of +# -malign-functions=. Eliminates a warning (fatal with -Werror). +# +# - When CONFIG_HIGHMEM is on, -Wpointer-arith will warn about +# include/linux/highmem.h. Since this is fatal with -Werror, we +# suppress -Wpointer-arith if CONFIG_HIGHMEM is set. +# +# Revision 1.50 2002/09/16 21:19:45 mcr +# enable -Werror for production - this helps a lot (found a bug in ipsec_rcv.c) +# +# Revision 1.49 2002/07/29 05:12:39 mcr +# get rid of some extraneous stuff, now handled by a prefix +# Makefile when building as a module. +# +# Revision 1.48 2002/07/28 23:13:49 mcr +# set KLIPS_TOP and use it instead of ../.. +# if KLIPSMODULE, then include a bunch of stuff defined in Makefile.inc +# that gets us the "typical" configuration that we want. +# +# Revision 1.47 2002/06/02 21:51:41 mcr +# changed TOPDIR->FREESWANSRCDIR in all Makefiles. +# (note that linux/net/ipsec/Makefile uses TOPDIR because this is the +# kernel sense.) +# +# Revision 1.46 2002/05/14 02:35:51 rgb +# Added file pfkey_v2_ext_process.c. +# +# Revision 1.45 2002/05/13 17:21:40 mcr +# mkdep dies when given a -I to a directory that does not exist. +# arch/${ARCH}/include is for UM arch only, so include it for that +# ARCH only. +# +# Revision 1.44 2002/04/24 20:38:12 mcr +# moved more stuff behind $KLIPSMODULE=y to get static linking to work. +# +# Revision 1.43 2002/04/24 09:16:18 mcr +# include local Makefile.ver as well as FS_rootdir version. +# +# Revision 1.42 2002/04/24 08:50:08 mcr +# critical patch is to set TOPDIR with :=. +# +# Revision 1.40 2002/04/24 00:41:07 mcr +# Moved from ./klips/net/ipsec/Makefile,v +# +# Revision 1.39 2002/01/17 04:39:40 rgb +# Take compile options from top level Makefile.inc +# +# Revision 1.38 2001/11/27 05:28:07 rgb +# Shut off -Werror until we figure out a graceful way of quieting down the +# pfkey_ops defined but not used complaint in the case of SMP in +# pfkey_v2.c. +# +# Revision 1.37 2001/11/27 05:10:15 rgb +# Added -Ilibdes and removed lib/des* symlinks. +# +# Revision 1.36 2001/11/26 09:23:47 rgb +# Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. +# +# Revision 1.35.2.1 2001/09/25 02:17:50 mcr +# added ipsec_sa, ipsec_life, ipsec_proc. +# added -Werror to compile flags (see fix for zlib/zutil.h) +# +# Revision 1.3 2001/09/21 04:41:26 mcr +# actually, ipsec_proc.c and ipsec_life.c were never actually compiled. +# +# Revision 1.2 2001/09/21 04:11:33 mcr +# first compilable version. +# +# Revision 1.1.1.2 2001/09/17 01:17:52 mcr +# snapshot 2001-09-16 +# +# Revision 1.35 2001/09/07 22:09:12 rgb +# Quiet down compilation. +# +# Revision 1.34 2001/08/11 17:10:23 henry +# update bogosity stuff to cover RH7.1 update +# +# Revision 1.33 2001/06/14 19:35:07 rgb +# Update copyright date. +# +# Revision 1.32 2001/06/13 21:00:50 rgb +# Added a kludge to get around RedHat kernel version bogosity... +# +# Revision 1.31 2001/01/29 22:19:06 rgb +# Convert to 2.4 new style with back compat. +# +# Revision 1.30 2000/09/29 19:51:57 rgb +# Moved klips/net/ipsec/ipcomp_* to zlib/* (Svenning). +# +# Revision 1.29 2000/09/15 11:37:01 rgb +# Merge in heavily modified Svenning Soerensen's <svenning@post5.tele.dk> +# IPCOMP zlib deflate code. +# +# Revision 1.28 2000/09/15 04:55:25 rgb +# Clean up pfkey object inclusion into the default object. +# +# Revision 1.27 2000/09/12 03:20:47 rgb +# Cleared out now unused pfkeyv2 switch. +# Enabled sysctl. +# +# Revision 1.26 2000/09/08 19:12:55 rgb +# Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. +# +# Revision 1.25 2000/06/16 03:09:16 rgb +# Shut up cast lost warning due to changes in 2.4.0-test1. +# +# Revision 1.24 2000/03/16 06:40:48 rgb +# Hardcode PF_KEYv2 support. +# +# Revision 1.23 2000/02/14 21:10:38 rgb +# Added gcc debug flag when KLIPS_DEBUG is swtiched on. +# +# Revision 1.22 2000/01/21 09:44:29 rgb +# Added compiler switches to be a lot more fussy. +# +# Revision 1.21 1999/11/25 23:35:20 rgb +# Removed quotes to fix Alpha compile issues. +# +# Revision 1.20 1999/11/17 15:49:34 rgb +# Changed all occurrences of ../../../lib in pathnames to libfreeswan, +# which refers to the /usr/src/linux/net/ipsec/lib directory setup by the +# klink target in the top-level Makefile; and libdeslite.o to +# libdes/libdes.a. +# Added SUB_DIRS := lib definition for the kernel libraries. +# +# Revision 1.19 1999/04/27 19:06:47 rgb +# dd libs and dependancies to tags generation. +# +# Revision 1.18 1999/04/16 16:28:12 rgb +# Minor bugfix to avoid including DES if only AH is used. +# +# Revision 1.17 1999/04/15 15:37:23 rgb +# Forward check changes from POST1_00 branch. +# +# Revision 1.14.2.1 1999/03/30 17:29:17 rgb +# Add support for pfkey. +# +# Revision 1.16 1999/04/11 00:28:56 henry +# GPL boilerplate +# +# Revision 1.15 1999/04/06 04:54:25 rgb +# Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes +# patch shell fixes. +# +# Revision 1.14 1999/02/18 16:50:45 henry +# update for new DES library +# +# Revision 1.13 1999/02/12 21:11:45 rgb +# Prepare for newer LIBDES (patch from P.Onion). +# +# Revision 1.12 1999/01/26 02:05:08 rgb +# Remove references to INET_GET_PROTOCOL. +# Removed CONFIG_IPSEC_ALGO_SWITCH macro. +# Change from transform switch to algorithm switch. +# +# Revision 1.11 1999/01/22 06:16:09 rgb +# Added algorithm switch code config option. +# +# Revision 1.10 1998/11/08 05:31:21 henry +# be a little fussier +# +# Revision 1.9 1998/11/08 05:29:41 henry +# revisions for new libdes handling +# +# Revision 1.8 1998/08/12 00:05:48 rgb +# Added new xforms to Makefile (moved des-cbc to des-old). +# +# Revision 1.7 1998/07/27 21:48:47 rgb +# Add libkernel. +# +# Revision 1.6 1998/07/14 15:50:47 rgb +# Add dependancies on linux config files. +# +# Revision 1.5 1998/07/09 17:44:06 rgb +# Added 'clean' and 'tags' targets. +# Added TOPDIR macro. +# Change module back from symbol exporting to not. +# +# Revision 1.3 1998/06/25 19:25:04 rgb +# Rearrange to support static linking and objects with exported symbol +# tables. +# +# Revision 1.1 1998/06/18 21:27:42 henry +# move sources from klips/src to klips/net/ipsec, to keep stupid +# kernel-build scripts happier in the presence of symlinks +# +# Revision 1.3 1998/04/15 23:18:43 rgb +# Unfixed the ../../libdes fix to avoid messing up Henry's script. +# +# Revision 1.2 1998/04/14 17:50:47 rgb +# Fixed to find the new location of libdes. +# +# Revision 1.1 1998/04/09 03:05:22 henry +# sources moved up from linux/net/ipsec +# modifications to centralize libdes code +# +# Revision 1.1.1.1 1998/04/08 05:35:02 henry +# RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 +# +# Revision 0.5 1997/06/03 04:24:48 ji +# Added ESP-3DES-MD5-96 +# +# Revision 0.4 1997/01/15 01:32:59 ji +# Added new transforms. +# +# Revision 0.3 1996/11/20 14:22:53 ji +# *** empty log message *** +# +# +# Local Variables: +# compile-command: "(cd ../../.. && source umlsetup.sh && make -C ${POOLSPACE} module/ipsec.o)" +# End Variables: +# + diff --git a/linux/net/ipsec/Makefile.algtest b/linux/net/ipsec/Makefile.algtest new file mode 100644 index 000000000..e68b4ac77 --- /dev/null +++ b/linux/net/ipsec/Makefile.algtest @@ -0,0 +1,125 @@ +IPSECVERSION=2.03 +# vim:aw:ai +# +# null-patch, non-root GNUmakefile addon for freeswan modules compilation +# +# It will not "affect" normal KLIPS building because this GNUmakefile +# it's not copied to /usr/src/linux +# +# Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar> +# $Id: Makefile.algtest,v 1.2 2004/03/22 21:53:19 as Exp $ +# +# 1) Copy me to linux/net/ipsec +# 2) +# cd klibs/net/ipsec +# make prep TOPDIR=/path/to/usr/src/linux \ +# [CONFIG=/path/to/.config | CONFIG=/dev/null] +# 3) +# make all TOPDIR=.... CONFIG=.... +#CONFIG_IPSEC_ENC_3DES=y +#CONFIG_IPSEC_AUTH_HMAC_MD5=y +#CONFIG_IPSEC_AUTH_HMAC_SHA1=y +CONFIG_IPSEC_ALG_AES=m + +ifndef TOPDIR +$(error You _must_ pass TOPDIR= and optionally CONFIG=) +endif +CONFIG=$(TOPDIR)/.config +include $(CONFIG) + +ifdef CONFIG_USERMODE + ARCH=um +endif +CONFIG_IPSEC=m +CONFIG_IPSEC_MODULE=y +CONFIG_IPSEC_IPIP=y +CONFIG_IPSEC_AH=y +CONFIG_IPSEC_ESP=y +CONFIG_IPSEC_ALG=y +CONFIG_IPSEC_IPCOMP=y + +CONFIG_M586 :=$(shell uname -m | sed -n "s/i586/y/p" ) +CONFIG_M686 :=$(shell uname -m | sed -n "s/i686/y/p" ) +export CONFIG_M586 CONFIG_M686 +cflags-arch-$(CONFIG_M586) += -march=i586 +cflags-arch-$(CONFIG_M586_TSC) += -march=i586 +cflags-arch-$(CONFIG_M686) += -march=i686 +cflags-arch-$(CONFIG_MPENTIUMIII) += -march=i686 +cflags-arch-$(CONFIG_MK7) += -march=i686 -malign-functions=4 +CFLAGS_ARCH := $(cflags-arch-y) + +ifndef $(CONFIG_SHELL) +CONFIG_SHELL=/bin/bash +endif +export CONFIG_SHELL TOPDIR + +ifdef CONFIG_SMP +EXTRA_CFLAGS += -D__SMP__ +EXTRA_AFLAGS += -D__SMP__ +endif + +CFLAGS_IPSEC:=\ + -DMODVERSIONS \ + -DCONFIG_IPSEC_MODULE=1\ + -DCONFIG_IPSEC_IPIP=1\ + -DCONFIG_IPSEC_AH=1\ + -DCONFIG_IPSEC_ESP=1\ + -DCONFIG_IPSEC_IPCOMP=1\ + -DCONFIG_IPSEC_DEBUG=1 \ + -DCONFIG_IPSEC_ALG=1 \ + +# -DCONFIG_IPSEC_DEBUG=1 \ +# +cflags-ipsec-$(CONFIG_IPSEC_ENC_3DES) += -DCONFIG_IPSEC_ENC_3DES=1 +cflags-ipsec-$(CONFIG_IPSEC_ALG_AES) += -DCONFIG_IPSEC_ALG_AES=1 +cflags-ipsec-$(CONFIG_IPSEC_AUTH_HMAC_MD5)+= -DCONFIG_IPSEC_AUTH_HMAC_MD5=1 +cflags-ipsec-$(CONFIG_IPSEC_AUTH_HMAC_SHA1)+= -DCONFIG_IPSEC_AUTH_HMAC_SHA1=1 +CFLAGS_IPSEC+=$(cflags-ipsec-y) +export CONFIG_IPSEC +export CONFIG_IPSEC_MODULE + + +# last bits over CFLAGS ... +CFLAGS+=$(KINCLUDE) $(CFLAGS_IPSEC) $(CFLAGS_ARCH) $(CFLAGS_KERNEL) +EXTRA_CFLAGS:=-I$(LOCALKLIPS) -I$(IPSEC_ROOT)/lib +# libdes options: OPTS1 +OPTS1:=$(CFLAGS) $(EXTRA_CFLAGS) +export OPTS1 CFLAGS + +#include Makefile +KERNEL_CFLAGS= $(shell $(MAKE) -C $(TOPDIR) --no-print-directory -s -f Makefile ARCH=$(ARCH) MAKEFLAGS= script SCRIPT='@echo $$(CFLAGS)' ) + +MODULE_CFLAGS= $(shell $(MAKE) -C $(TOPDIR) --no-print-directory -s -f Makefile ARCH=$(ARCH) MAKEFLAGS= script SCRIPT='@echo $$(MODFLAGS)' ) + + +ALGO_FLAGS=$(CFLAGS_IPSEC) +export ALGO_FLAGS +all: modules alg_modules +modules: + $(MAKE) -C $(TOPDIR) SUBDIRS=$(PWD) modules + +ifdef CONFIG_USERMODE +local_modversions_h: + > local_modversions.h +else +local_modversions_h: + (echo "#ifndef _LINUX_MODVERSIONS_H";\ + echo "#define _LINUX_MODVERSIONS_H"; \ + echo "#include <linux/modsetver.h>"; \ + cd $(TOPDIR)/include/linux/modules; \ + perl -ne 'print "#define __ver_$$1\t$$2$$3\n#define $$1\t_set_ver($$1)\n" if (/ (.*)_R(smp)?([a-z0-9]{8})\W/);' /proc/ksyms ;\ + echo "#endif"; \ + ) > local_modversions.h +endif +un_local_modversions_h: + @rm -f local_modversions.h + +all_alg_modules: + (cd alg && \ + $(MAKE) CC='$(CC)' CFLAGS='$(CFLAGS) $(EXTRA_CFLAGS)' \ + LIBCRYPTO=$(LOCALKLIPS)/../../../lib/libcrypto \ + all_alg_modules;) + +.PHONY: local_modversions_h + + diff --git a/linux/net/ipsec/alg/Config.alg_aes.in b/linux/net/ipsec/alg/Config.alg_aes.in new file mode 100644 index 000000000..4a2f81a0b --- /dev/null +++ b/linux/net/ipsec/alg/Config.alg_aes.in @@ -0,0 +1,3 @@ +if [ "$CONFIG_IPSEC_ALG" = "y" ]; then + tristate ' AES encryption algorithm' CONFIG_IPSEC_ALG_AES +fi diff --git a/linux/net/ipsec/alg/Config.alg_blowfish.in b/linux/net/ipsec/alg/Config.alg_blowfish.in new file mode 100644 index 000000000..a4e5709b0 --- /dev/null +++ b/linux/net/ipsec/alg/Config.alg_blowfish.in @@ -0,0 +1,3 @@ +if [ "$CONFIG_IPSEC_ALG" = "y" ]; then + tristate ' BLOWFISH encryption algorithm' CONFIG_IPSEC_ALG_BLOWFISH +fi diff --git a/linux/net/ipsec/alg/Config.alg_cryptoapi.in b/linux/net/ipsec/alg/Config.alg_cryptoapi.in new file mode 100644 index 000000000..c2c66eed8 --- /dev/null +++ b/linux/net/ipsec/alg/Config.alg_cryptoapi.in @@ -0,0 +1,3 @@ +if [ "$CONFIG_IPSEC_ALG" = "y" ]; then + dep_tristate ' CRYPTOAPI ciphers support (needs cryptoapi patch)' CONFIG_IPSEC_ALG_CRYPTOAPI $CONFIG_CRYPTO +fi diff --git a/linux/net/ipsec/alg/Config.alg_serpent.in b/linux/net/ipsec/alg/Config.alg_serpent.in new file mode 100644 index 000000000..fb1a88460 --- /dev/null +++ b/linux/net/ipsec/alg/Config.alg_serpent.in @@ -0,0 +1,3 @@ +if [ "$CONFIG_IPSEC_ALG" = "y" ]; then + tristate ' SERPENT encryption algorithm' CONFIG_IPSEC_ALG_SERPENT +fi diff --git a/linux/net/ipsec/alg/Config.alg_sha2.in b/linux/net/ipsec/alg/Config.alg_sha2.in new file mode 100644 index 000000000..2d26c814b --- /dev/null +++ b/linux/net/ipsec/alg/Config.alg_sha2.in @@ -0,0 +1,3 @@ +if [ "$CONFIG_IPSEC_ALG" = "y" ]; then + tristate ' HMAC_SHA2 auth algorithm' CONFIG_IPSEC_ALG_SHA2 +fi diff --git a/linux/net/ipsec/alg/Config.alg_twofish.in b/linux/net/ipsec/alg/Config.alg_twofish.in new file mode 100644 index 000000000..13655649d --- /dev/null +++ b/linux/net/ipsec/alg/Config.alg_twofish.in @@ -0,0 +1,3 @@ +if [ "$CONFIG_IPSEC_ALG" = "y" ]; then + tristate ' TWOFISH encryption algorithm' CONFIG_IPSEC_ALG_TWOFISH +fi diff --git a/linux/net/ipsec/alg/Config.in b/linux/net/ipsec/alg/Config.in new file mode 100644 index 000000000..be5990e3a --- /dev/null +++ b/linux/net/ipsec/alg/Config.in @@ -0,0 +1,7 @@ +#Placeholder +source net/ipsec/alg/Config.alg_aes.in +source net/ipsec/alg/Config.alg_blowfish.in +source net/ipsec/alg/Config.alg_twofish.in +source net/ipsec/alg/Config.alg_serpent.in +source net/ipsec/alg/Config.alg_cryptoapi.in +source net/ipsec/alg/Config.alg_sha2.in diff --git a/linux/net/ipsec/alg/Makefile b/linux/net/ipsec/alg/Makefile new file mode 100644 index 000000000..2249668f5 --- /dev/null +++ b/linux/net/ipsec/alg/Makefile @@ -0,0 +1,112 @@ +# $Id: Makefile,v 1.2 2004/03/22 21:53:19 as Exp $ +ifeq ($(strip $(KLIPSMODULE)),) +FREESWANSRCDIR=. +else +FREESWANSRCDIR=../../../.. +endif +ifeq ($(strip $(KLIPS_TOP)),) +KLIPS_TOP=../../.. +override EXTRA_CFLAGS += -I$(KLIPS_TOP)/include +endif + +ifeq ($(CONFIG_IPSEC_DEBUG),y) +override EXTRA_CFLAGS += -g +endif + +# LIBCRYPTO normally comes as an argument from "parent" Makefile +# (this applies both to FS' "make module" and eg. Linux' "make modules" +# But make dep doest follow same evaluations, so we need this default: +LIBCRYPTO=$(TOPDIR)/lib/libcrypto + +override EXTRA_CFLAGS += -I$(LIBCRYPTO)/include +override EXTRA_CFLAGS += -Wall -Wpointer-arith -Wstrict-prototypes + +MOD_LIST_NAME := NET_MISC_MODULES + +#O_TARGET := static_init.o + +subdir- := +subdir-n := +subdir-y := +subdir-m := + +obj-y := static_init.o + +ARCH_ASM-y := +ARCH_ASM-$(CONFIG_M586) := i586 +ARCH_ASM-$(CONFIG_M586TSC) := i586 +ARCH_ASM-$(CONFIG_M586MMX) := i586 +ARCH_ASM-$(CONFIG_MK6) := i586 +ARCH_ASM-$(CONFIG_M686) := i686 +ARCH_ASM-$(CONFIG_MPENTIUMIII) := i686 +ARCH_ASM-$(CONFIG_MPENTIUM4) := i686 +ARCH_ASM-$(CONFIG_MK7) := i686 +ARCH_ASM-$(CONFIG_MCRUSOE) := i586 +ARCH_ASM-$(CONFIG_MWINCHIPC6) := i586 +ARCH_ASM-$(CONFIG_MWINCHIP2) := i586 +ARCH_ASM-$(CONFIG_MWINCHIP3D) := i586 +ARCH_ASM-$(CONFIG_USERMODE) := i586 + +ARCH_ASM :=$(ARCH_ASM-y) +ifdef NO_ASM +ARCH_ASM := +endif + +# The algorithm makefiles may put dependences, short-circuit them +null: + +makefiles=$(filter-out %.preipsec, $(wildcard Makefile.alg_*)) +ifneq ($(makefiles),) +#include Makefile.alg_aes +#include Makefile.alg_aes-opt +include $(makefiles) +endif + +# These rules translate from new to old makefile rules +# Translate to Rules.make lists. +multi-used := $(filter $(list-multi), $(obj-y) $(obj-m)) +multi-objs := $(foreach m, $(multi-used), $($(basename $(m))-objs)) +active-objs := $(sort $(multi-objs) $(obj-y) $(obj-m)) +O_OBJS := $(obj-y) +M_OBJS := $(obj-m) +MIX_OBJS := $(filter $(export-objs), $(active-objs)) +#OX_OBJS := $(export-objs) +SUB_DIRS := $(subdir-y) +ALL_SUB_DIRS := $(subdir-y) $(subdir-m) +MOD_SUB_DIRS := $(subdir-m) + + +static_init_mod.o: $(obj-y) + rm -f $@ + $(LD) $(LD_EXTRAFLAGS) $(obj-y) -r -o $@ + +perlasm: $(LIBCRYPTO)/perlasm + ln -sf $? $@ + +$(obj-y) $(obj-m): $(TOPDIR)/include/linux/config.h $(TOPDIR)/include/linux/autoconf.h $(KLIPS_TOP)/include/freeswan/ipsec_alg.h +$(alg_obj-y) $(alg_obj-m): perlasm $(TOPDIR)/include/linux/config.h $(TOPDIR)/include/linux/autoconf.h $(KLIPS_TOP)/include/freeswan/ipsec_alg.h + + +all_alg_modules: perlasm $(ALG_MODULES) + @echo "ALG_MODULES=$(ALG_MODULES)" + + +# +# Construct alg. init. function: call ipsec_ALGO_init() for every static algo +# Needed when there are static algos (with static or modular ipsec.o) +# +static_init.c: $(TOPDIR)/include/linux/autoconf.h Makefile $(makefiles) scripts/mk-static_init.c.sh + @echo "Re-creating $@" + $(SHELL) scripts/mk-static_init.c.sh $(static_init-func-y) > $@ + +clean: + @for i in $(ALG_SUBDIRS);do test -d $$i && make -C $$i clean;done;exit 0 + @find . -type l -exec rm -f {} \; + -rm -f perlasm + -rm -rf $(ALG_SUBDIRS) + -rm -f *.o .*.o.flags static_init.c + +ifdef TOPDIR +include $(TOPDIR)/Rules.make +endif + diff --git a/linux/net/ipsec/alg/Makefile.alg_aes b/linux/net/ipsec/alg/Makefile.alg_aes new file mode 100644 index 000000000..75284c47a --- /dev/null +++ b/linux/net/ipsec/alg/Makefile.alg_aes @@ -0,0 +1,23 @@ +MOD_AES := ipsec_aes.o + +ALG_MODULES += $(MOD_AES) +ALG_SUBDIRS += libaes + +obj-$(CONFIG_IPSEC_ALG_AES) += $(MOD_AES) +static_init-func-$(CONFIG_IPSEC_ALG_AES)+= ipsec_aes_init +alg_obj-$(CONFIG_IPSEC_ALG_AES) += ipsec_alg_aes.o + +AES_OBJS := ipsec_alg_aes.o libaes/libaes.a + +$(MOD_AES): libaes $(AES_OBJS) + $(LD) $(EXTRA_LDFLAGS) -r $(AES_OBJS) -o $@ + +libaes: $(LIBCRYPTO)/libaes + test -d $@ || mkdir $@ ;exit 0 + test -d $@/asm || mkdir $@/asm;exit 0 + cd $@ && ln -sf $?/Makefile $?/*.[chS] . + cd $@/asm && ln -sf $?/asm/*.S . + +libaes/libaes.a: libaes + ( cd libaes && \ + $(MAKE) CC='$(CC)' 'ARCH_ASM=$(ARCH_ASM)' CFLAGS='$(CFLAGS) $(EXTRA_CFLAGS)' libaes.a ;) diff --git a/linux/net/ipsec/alg/Makefile.alg_blowfish b/linux/net/ipsec/alg/Makefile.alg_blowfish new file mode 100644 index 000000000..9413a9f1c --- /dev/null +++ b/linux/net/ipsec/alg/Makefile.alg_blowfish @@ -0,0 +1,23 @@ +MOD_BLOWFISH := ipsec_blowfish.o + +ALG_MODULES += $(MOD_BLOWFISH) +ALG_SUBDIRS += libblowfish + +obj-$(CONFIG_IPSEC_ALG_BLOWFISH) += $(MOD_BLOWFISH) +static_init-func-$(CONFIG_IPSEC_ALG_BLOWFISH)+= ipsec_blowfish_init +alg_obj-$(CONFIG_IPSEC_ALG_BLOWFISH) += ipsec_alg_blowfish.o + +BLOWFISH_OBJS:= ipsec_alg_blowfish.o libblowfish/libblowfish.a + +$(MOD_BLOWFISH): libblowfish $(BLOWFISH_OBJS) + $(LD) -r $(BLOWFISH_OBJS) -o $@ + +libblowfish : $(LIBCRYPTO)/libblowfish + test -d $@ || mkdir $@ ;exit 0 + test -d $@/asm || mkdir $@/asm;exit 0 + cd $@ && ln -sf $?/Makefile $?/*.[chS] . + cd $@/asm && ln -sf $?/asm/*.pl . + +libblowfish/libblowfish.a: + ( cd libblowfish && \ + $(MAKE) CC='$(CC)' 'ARCH_ASM=$(ARCH_ASM)' CFLAGS='$(CFLAGS) $(EXTRA_CFLAGS)' EXTRA_CFLAGS='$(EXTRA_CFLAGS)' libblowfish.a ;) diff --git a/linux/net/ipsec/alg/Makefile.alg_cryptoapi b/linux/net/ipsec/alg/Makefile.alg_cryptoapi new file mode 100644 index 000000000..77ee6481f --- /dev/null +++ b/linux/net/ipsec/alg/Makefile.alg_cryptoapi @@ -0,0 +1,14 @@ +MOD_CRYPTOAPI := ipsec_cryptoapi.o + +ifneq ($(wildcard $(TOPDIR)/include/linux/crypto.h),) +ALG_MODULES += $(MOD_CRYPTOAPI) +obj-$(CONFIG_IPSEC_ALG_CRYPTOAPI) += $(MOD_CRYPTOAPI) +static_init-func-$(CONFIG_IPSEC_ALG_CRYPTOAPI)+= ipsec_cryptoapi_init +alg_obj-$(CONFIG_IPSEC_ALG_CRYPTOAPI) += ipsec_alg_cryptoapi.o +else +$(warning "Linux CryptoAPI (2.4.22+ or 2.6.x) not found, not building ipsec_cryptoapi.o") +endif + +CRYPTOAPI_OBJS := ipsec_alg_cryptoapi.o +$(MOD_CRYPTOAPI): $(CRYPTOAPI_OBJS) + $(LD) -r $(CRYPTOAPI_OBJS) -o $@ diff --git a/linux/net/ipsec/alg/Makefile.alg_serpent b/linux/net/ipsec/alg/Makefile.alg_serpent new file mode 100644 index 000000000..1a2383a6a --- /dev/null +++ b/linux/net/ipsec/alg/Makefile.alg_serpent @@ -0,0 +1,21 @@ +MOD_SERPENT := ipsec_serpent.o + +ALG_MODULES += $(MOD_SERPENT) +ALG_SUBDIRS += libserpent + +obj-$(CONFIG_IPSEC_ALG_SERPENT) += $(MOD_SERPENT) +static_init-func-$(CONFIG_IPSEC_ALG_SERPENT)+= ipsec_serpent_init +alg_obj-$(CONFIG_IPSEC_ALG_SERPENT) += ipsec_alg_serpent.o + +SERPENT_OBJS=ipsec_alg_serpent.o libserpent/libserpent.a +$(MOD_SERPENT) : libserpent $(SERPENT_OBJS) + $(LD) -r $(SERPENT_OBJS) -o $@ + +libserpent : $(LIBCRYPTO)/libserpent + test -d $@ || mkdir $@ ;exit 0 + test -d $@/asm || mkdir $@/asm;exit 0 + cd $@ && ln -sf $?/Makefile $?/*.[chS] . + +libserpent/libserpent.a: + ( cd libserpent && \ + $(MAKE) CC='$(CC)' CFLAGS='$(CFLAGS) $(EXTRA_CFLAGS)' EXTRA_CFLAGS='$(EXTRA_CFLAGS)' libserpent.a ;) diff --git a/linux/net/ipsec/alg/Makefile.alg_sha2 b/linux/net/ipsec/alg/Makefile.alg_sha2 new file mode 100644 index 000000000..956a0f1a3 --- /dev/null +++ b/linux/net/ipsec/alg/Makefile.alg_sha2 @@ -0,0 +1,22 @@ +MOD_SHA2 := ipsec_sha2.o + +ALG_MODULES += $(MOD_SHA2) +ALG_SUBDIRS += libsha2 + +obj-$(CONFIG_IPSEC_ALG_SHA2) += $(MOD_SHA2) +static_init-func-$(CONFIG_IPSEC_ALG_SHA2)+= ipsec_sha2_init +alg_obj-$(CONFIG_IPSEC_ALG_SHA2) += ipsec_alg_sha2.o + +SHA2_OBJS := ipsec_alg_sha2.o libsha2/libsha2.a + +$(MOD_SHA2): libsha2 $(SHA2_OBJS) + $(LD) $(EXTRA_LDFLAGS) -r $(SHA2_OBJS) -o $@ + +libsha2 : $(LIBCRYPTO)/libsha2 + test -d $@ || mkdir $@ ;exit 0 + test -d $@/asm || mkdir $@/asm;exit 0 + cd $@ && ln -sf $?/Makefile $?/*.[chS] . + +libsha2/libsha2.a: + ( cd libsha2 && \ + $(MAKE) CC='$(CC)' CFLAGS='$(CFLAGS) $(EXTRA_CFLAGS)' EXTRA_CFLAGS='$(EXTRA_CFLAGS)' libsha2.a ;) diff --git a/linux/net/ipsec/alg/Makefile.alg_twofish b/linux/net/ipsec/alg/Makefile.alg_twofish new file mode 100644 index 000000000..559285ddd --- /dev/null +++ b/linux/net/ipsec/alg/Makefile.alg_twofish @@ -0,0 +1,21 @@ +MOD_TWOFISH := ipsec_twofish.o + +ALG_MODULES += $(MOD_TWOFISH) +ALG_SUBDIRS += libtwofish + +obj-$(CONFIG_IPSEC_ALG_TWOFISH) += $(MOD_TWOFISH) +static_init-func-$(CONFIG_IPSEC_ALG_TWOFISH)+= ipsec_twofish_init +alg_obj-$(CONFIG_IPSEC_ALG_TWOFISH) += ipsec_alg_twofish.o + +TWOFISH_OBJS := ipsec_alg_twofish.o libtwofish/libtwofish.a +$(MOD_TWOFISH): libtwofish $(TWOFISH_OBJS) + $(LD) -r $(TWOFISH_OBJS) -o $@ + +libtwofish : $(LIBCRYPTO)/libtwofish + test -d $@ || mkdir $@ ;exit 0 + test -d $@/asm || mkdir $@/asm;exit 0 + cd $@ && ln -sf $?/Makefile $?/*.[chS] . + +libtwofish/libtwofish.a: + ( cd libtwofish && \ + $(MAKE) CC='$(CC)' CFLAGS='$(CFLAGS) $(EXTRA_CFLAGS)' EXTRA_CFLAGS='$(EXTRA_CFLAGS)' libtwofish.a ;) diff --git a/linux/net/ipsec/alg/ipsec_alg_aes.c b/linux/net/ipsec/alg/ipsec_alg_aes.c new file mode 100644 index 000000000..c6b390281 --- /dev/null +++ b/linux/net/ipsec/alg/ipsec_alg_aes.c @@ -0,0 +1,253 @@ +/* + * ipsec_alg AES cipher stubs + * + * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar> + * + * $Id: ipsec_alg_aes.c,v 1.2 2004/03/22 21:53:19 as Exp $ + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + * + * Fixes by: + * PK: Pawel Krawczyk <kravietz@aba.krakow.pl> + * Fixes list: + * PK: make XCBC comply with latest draft (keylength) + * + */ +#include <linux/config.h> +#include <linux/version.h> + +/* + * special case: ipsec core modular with this static algo inside: + * must avoid MODULE magic for this file + */ +#if CONFIG_IPSEC_MODULE && CONFIG_IPSEC_ALG_AES +#undef MODULE +#endif + +#include <linux/module.h> +#include <linux/init.h> + +#include <linux/kernel.h> /* printk() */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/string.h> + +/* Check if __exit is defined, if not null it */ +#ifndef __exit +#define __exit +#endif + +/* Low freeswan header coupling */ +#include "freeswan/ipsec_alg.h" +#include "libaes/aes_cbc.h" + +#define CONFIG_IPSEC_ALG_AES_MAC 1 + +#define AES_CONTEXT_T aes_context +MODULE_AUTHOR("JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>"); +static int debug=0; +MODULE_PARM(debug, "i"); +static int test=0; +MODULE_PARM(test, "i"); +static int excl=0; +MODULE_PARM(excl, "i"); +static int keyminbits=0; +MODULE_PARM(keyminbits, "i"); +static int keymaxbits=0; +MODULE_PARM(keymaxbits, "i"); + +#if CONFIG_IPSEC_ALG_AES_MAC +#include "libaes/aes_xcbc_mac.h" + +/* + * Not IANA number yet (draft-ietf-ipsec-ciph-aes-xcbc-mac-00.txt). + * We use 9 for non-modular algorithm and none for modular, thus + * forcing user to specify one on module load. -kravietz + */ +#ifdef MODULE +static int auth_id=0; +#else +static int auth_id=9; +#endif +MODULE_PARM(auth_id, "i"); +#endif + +#define ESP_AES 12 /* truely _constant_ :) */ + +/* 128, 192 or 256 */ +#define ESP_AES_KEY_SZ_MIN 16 /* 128 bit secret key */ +#define ESP_AES_KEY_SZ_MAX 32 /* 256 bit secret key */ +#define ESP_AES_CBC_BLK_LEN 16 /* AES-CBC block size */ + +/* Values according to draft-ietf-ipsec-ciph-aes-xcbc-mac-02.txt + * -kravietz + */ +#define ESP_AES_MAC_KEY_SZ 16 /* 128 bit MAC key */ +#define ESP_AES_MAC_BLK_LEN 16 /* 128 bit block */ + +static int _aes_set_key(struct ipsec_alg_enc *alg, __u8 * key_e, const __u8 * key, size_t keysize) { + int ret; + AES_CONTEXT_T *ctx=(AES_CONTEXT_T*)key_e; + ret=AES_set_key(ctx, key, keysize)!=0? 0: -EINVAL; + if (debug > 0) + printk(KERN_DEBUG "klips_debug:_aes_set_key:" + "ret=%d key_e=%p key=%p keysize=%d\n", + ret, key_e, key, keysize); + return ret; +} +static int _aes_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt) { + AES_CONTEXT_T *ctx=(AES_CONTEXT_T*)key_e; + if (debug > 0) + printk(KERN_DEBUG "klips_debug:_aes_cbc_encrypt:" + "key_e=%p in=%p ilen=%d iv=%p encrypt=%d\n", + key_e, in, ilen, iv, encrypt); + return AES_cbc_encrypt(ctx, in, in, ilen, iv, encrypt); +} +#if CONFIG_IPSEC_ALG_AES_MAC +static int _aes_mac_set_key(struct ipsec_alg_auth *alg, __u8 * key_a, const __u8 * key, int keylen) { + aes_context_mac *ctxm=(aes_context_mac *)key_a; + return AES_xcbc_mac_set_key(ctxm, key, keylen)? 0 : -EINVAL; +} +static int _aes_mac_hash(struct ipsec_alg_auth *alg, __u8 * key_a, const __u8 * dat, int len, __u8 * hash, int hashlen) { + int ret; + char hash_buf[16]; + aes_context_mac *ctxm=(aes_context_mac *)key_a; + ret=AES_xcbc_mac_hash(ctxm, dat, len, hash_buf); + memcpy(hash, hash_buf, hashlen); + return ret; +} +static struct ipsec_alg_auth ipsec_alg_AES_MAC = { + ixt_version: IPSEC_ALG_VERSION, + ixt_module: THIS_MODULE, + ixt_refcnt: ATOMIC_INIT(0), + ixt_alg_type: IPSEC_ALG_TYPE_AUTH, + ixt_alg_id: 0, + ixt_name: "aes_mac", + ixt_blocksize: ESP_AES_MAC_BLK_LEN, + ixt_keyminbits: ESP_AES_MAC_KEY_SZ*8, + ixt_keymaxbits: ESP_AES_MAC_KEY_SZ*8, + ixt_a_keylen: ESP_AES_MAC_KEY_SZ, + ixt_a_ctx_size: sizeof(aes_context_mac), + ixt_a_hmac_set_key: _aes_mac_set_key, + ixt_a_hmac_hash:_aes_mac_hash, +}; +#endif /* CONFIG_IPSEC_ALG_AES_MAC */ +static struct ipsec_alg_enc ipsec_alg_AES = { + ixt_version: IPSEC_ALG_VERSION, + ixt_module: THIS_MODULE, + ixt_refcnt: ATOMIC_INIT(0), + ixt_alg_type: IPSEC_ALG_TYPE_ENCRYPT, + ixt_alg_id: ESP_AES, + ixt_name: "aes", + ixt_blocksize: ESP_AES_CBC_BLK_LEN, + ixt_keyminbits: ESP_AES_KEY_SZ_MIN*8, + ixt_keymaxbits: ESP_AES_KEY_SZ_MAX*8, + ixt_e_keylen: ESP_AES_KEY_SZ_MAX, + ixt_e_ctx_size: sizeof(AES_CONTEXT_T), + ixt_e_set_key: _aes_set_key, + ixt_e_cbc_encrypt:_aes_cbc_encrypt, +}; + +IPSEC_ALG_MODULE_INIT( ipsec_aes_init ) +{ + int ret, test_ret; + if (keyminbits) + ipsec_alg_AES.ixt_keyminbits=keyminbits; + if (keymaxbits) { + ipsec_alg_AES.ixt_keymaxbits=keymaxbits; + if (keymaxbits*8>ipsec_alg_AES.ixt_keymaxbits) + ipsec_alg_AES.ixt_e_keylen=keymaxbits*8; + } + if (excl) ipsec_alg_AES.ixt_state |= IPSEC_ALG_ST_EXCL; + ret=register_ipsec_alg_enc(&ipsec_alg_AES); + printk("ipsec_aes_init(alg_type=%d alg_id=%d name=%s): ret=%d\n", + ipsec_alg_AES.ixt_alg_type, + ipsec_alg_AES.ixt_alg_id, + ipsec_alg_AES.ixt_name, + ret); + if (ret==0 && test) { + test_ret=ipsec_alg_test( + ipsec_alg_AES.ixt_alg_type, + ipsec_alg_AES.ixt_alg_id, + test); + printk("ipsec_aes_init(alg_type=%d alg_id=%d): test_ret=%d\n", + ipsec_alg_AES.ixt_alg_type, + ipsec_alg_AES.ixt_alg_id, + test_ret); + } +#if CONFIG_IPSEC_ALG_AES_MAC + if (auth_id!=0){ + int ret; + ipsec_alg_AES_MAC.ixt_alg_id=auth_id; + ret=register_ipsec_alg_auth(&ipsec_alg_AES_MAC); + printk("ipsec_aes_init(alg_type=%d alg_id=%d name=%s): ret=%d\n", + ipsec_alg_AES_MAC.ixt_alg_type, + ipsec_alg_AES_MAC.ixt_alg_id, + ipsec_alg_AES_MAC.ixt_name, + ret); + if (ret==0 && test) { + test_ret=ipsec_alg_test( + ipsec_alg_AES_MAC.ixt_alg_type, + ipsec_alg_AES_MAC.ixt_alg_id, + test); + printk("ipsec_aes_init(alg_type=%d alg_id=%d): test_ret=%d\n", + ipsec_alg_AES_MAC.ixt_alg_type, + ipsec_alg_AES_MAC.ixt_alg_id, + test_ret); + } + } else { + printk(KERN_DEBUG "klips_debug: experimental ipsec_alg_AES_MAC not registered [Ok] (auth_id=%d)\n", auth_id); + } +#endif /* CONFIG_IPSEC_ALG_AES_MAC */ + return ret; +} +IPSEC_ALG_MODULE_EXIT( ipsec_aes_fini ) +{ +#if CONFIG_IPSEC_ALG_AES_MAC + if (auth_id) unregister_ipsec_alg_auth(&ipsec_alg_AES_MAC); +#endif /* CONFIG_IPSEC_ALG_AES_MAC */ + unregister_ipsec_alg_enc(&ipsec_alg_AES); + return; +} +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if 0+NOT_YET +#ifndef MODULE +/* + * This is intended for static module setups, currently + * doesn't work for modular ipsec.o with static algos inside + */ +static int setup_keybits(const char *str) +{ + unsigned aux; + char *end; + + aux = simple_strtoul(str,&end,0); + if (aux != 128 && aux != 192 && aux != 256) + return 0; + keyminbits = aux; + + if (*end == 0 || *end != ',') + return 1; + str=end+1; + aux = simple_strtoul(str, NULL, 0); + if (aux != 128 && aux != 192 && aux != 256) + return 0; + if (aux >= keyminbits) + keymaxbits = aux; + return 1; +} +__setup("ipsec_aes_keybits=", setup_keybits); +#endif +#endif +EXPORT_NO_SYMBOLS; diff --git a/linux/net/ipsec/alg/ipsec_alg_blowfish.c b/linux/net/ipsec/alg/ipsec_alg_blowfish.c new file mode 100644 index 000000000..6adc22b22 --- /dev/null +++ b/linux/net/ipsec/alg/ipsec_alg_blowfish.c @@ -0,0 +1,142 @@ +/* ipsec_alg BLOWFISH cipher stubs + * + * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar> + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + * + * RCS ID $Id: ipsec_alg_blowfish.c,v 1.3 2004/09/17 18:57:30 as Exp $ + */ + +#include <linux/config.h> +#include <linux/version.h> + +/* + * special case: ipsec core modular with this static algo inside: + * must avoid MODULE magic for this file + */ +#if CONFIG_IPSEC_MODULE && CONFIG_IPSEC_ALG_BLOWFISH +#undef MODULE +#endif + +#include <linux/module.h> +#include <linux/init.h> + +#include <linux/kernel.h> /* printk() */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/string.h> + +/* Check if __exit is defined, if not null it */ +#ifndef __exit +#define __exit +#endif + +/* Low freeswan header coupling */ +#include "freeswan/ipsec_alg.h" +#include "libblowfish/blowfish.h" +#define blowfish_context BF_KEY + +#define ESP_BLOWFISH 7 /* truly _constant_ :) */ + +#define ESP_BLOWFISH_KEY_SZ_MIN 16 /* 128 bit secret key min */ +#define ESP_BLOWFISH_KEY_SZ 16 /* 128 bit secret key */ +#define ESP_BLOWFISH_KEY_SZ_MAX 56 /* 448 bit secret key max */ +#define ESP_BLOWFISH_CBC_BLK_LEN 8 /* block size */ + +MODULE_AUTHOR("JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>"); +static int debug=0; +MODULE_PARM(debug, "i"); +static int test=0; +MODULE_PARM(test, "i"); +static int excl=0; +MODULE_PARM(excl, "i"); +static int keyminbits=0; +MODULE_PARM(keyminbits, "i"); +static int keymaxbits=0; +MODULE_PARM(keymaxbits, "i"); + +static int _blowfish_set_key(struct ipsec_alg_enc *alg, __u8 * key_e, const __u8 * key, size_t keysize) { + blowfish_context *ctx=(blowfish_context*)key_e; + if (debug > 0) + printk(KERN_DEBUG "klips_debug:_blowfish_set_key:" + "key_e=%p key=%p keysize=%d\n", + key_e, key, keysize); + BF_set_key(ctx, keysize, (unsigned char *)key); + return 0; +} +static int _blowfish_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 *iv, int encrypt) { + /* blowfish toasts passed IV */ + __u8 iv_buf[ESP_BLOWFISH_CBC_BLK_LEN]; + blowfish_context *ctx=(blowfish_context*)key_e; + *((__u32*)&(iv_buf)) = ((__u32*)(iv))[0]; + *((__u32*)&(iv_buf)+1) = ((__u32*)(iv))[1]; + if (debug > 0) + printk(KERN_DEBUG "klips_debug:_blowfish_cbc_encrypt:" + "key_e=%p in=%p ilen=%d iv=%p encrypt=%d\n", + key_e, in, ilen, iv_buf, encrypt); + BF_cbc_encrypt(in, in, ilen, ctx, iv_buf, encrypt); + return ilen; +} +static struct ipsec_alg_enc ipsec_alg_BLOWFISH = { + ixt_version: IPSEC_ALG_VERSION, + ixt_module: THIS_MODULE, + ixt_refcnt: ATOMIC_INIT(0), + ixt_alg_type: IPSEC_ALG_TYPE_ENCRYPT, + ixt_alg_id: ESP_BLOWFISH, + ixt_name: "blowfish", + ixt_blocksize: ESP_BLOWFISH_CBC_BLK_LEN, + ixt_keyminbits: ESP_BLOWFISH_KEY_SZ_MIN*8, + ixt_keymaxbits: ESP_BLOWFISH_KEY_SZ_MAX*8, + ixt_e_keylen: ESP_BLOWFISH_KEY_SZ, + ixt_e_ctx_size: sizeof(blowfish_context), + ixt_e_set_key: _blowfish_set_key, + ixt_e_cbc_encrypt:_blowfish_cbc_encrypt, +}; + +IPSEC_ALG_MODULE_INIT(ipsec_blowfish_init) +{ + int ret, test_ret; + if (keyminbits) + ipsec_alg_BLOWFISH.ixt_keyminbits=keyminbits; + if (keymaxbits) { + ipsec_alg_BLOWFISH.ixt_keymaxbits=keymaxbits; + if (keymaxbits*8>ipsec_alg_BLOWFISH.ixt_keymaxbits) + ipsec_alg_BLOWFISH.ixt_e_keylen=keymaxbits*8; + } + if (excl) ipsec_alg_BLOWFISH.ixt_state |= IPSEC_ALG_ST_EXCL; + ret=register_ipsec_alg_enc(&ipsec_alg_BLOWFISH); + printk("ipsec_blowfish_init(alg_type=%d alg_id=%d name=%s): ret=%d\n", + ipsec_alg_BLOWFISH.ixt_alg_type, + ipsec_alg_BLOWFISH.ixt_alg_id, + ipsec_alg_BLOWFISH.ixt_name, + ret); + if (ret==0 && test) { + test_ret=ipsec_alg_test( + ipsec_alg_BLOWFISH.ixt_alg_type, + ipsec_alg_BLOWFISH.ixt_alg_id, + test); + printk("ipsec_blowfish_init(alg_type=%d alg_id=%d): test_ret=%d\n", + ipsec_alg_BLOWFISH.ixt_alg_type, + ipsec_alg_BLOWFISH.ixt_alg_id, + test_ret); + } + return ret; +} +IPSEC_ALG_MODULE_EXIT(ipsec_blowfish_fini) +{ + unregister_ipsec_alg_enc(&ipsec_alg_BLOWFISH); + return; +} +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +EXPORT_NO_SYMBOLS; diff --git a/linux/net/ipsec/alg/ipsec_alg_cryptoapi.c b/linux/net/ipsec/alg/ipsec_alg_cryptoapi.c new file mode 100644 index 000000000..fc68094c2 --- /dev/null +++ b/linux/net/ipsec/alg/ipsec_alg_cryptoapi.c @@ -0,0 +1,421 @@ +/* + * ipsec_alg to linux cryptoapi GLUE + * + * Authors: CODE.ar TEAM + * Harpo MAxx <harpo@linuxmendoza.org.ar> + * JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar> + * Luciano Ruete <docemeses@softhome.net> + * + * $Id: ipsec_alg_cryptoapi.c,v 1.3 2004/09/17 18:57:30 as Exp $ + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + * + * Example usage: + * modinfo -p ipsec_cryptoapi (quite useful info, including supported algos) + * modprobe ipsec_cryptoapi + * modprobe ipsec_cryptoapi test=1 + * modprobe ipsec_cryptoapi excl=1 (exclusive cipher/algo) + * modprobe ipsec_cryptoapi noauto=1 aes=1 twofish=1 (only these ciphers) + * modprobe ipsec_cryptoapi aes=128,128 (force these keylens) + * modprobe ipsec_cryptoapi des_ede3=0 (everything but 3DES) + */ +#include <linux/config.h> +#include <linux/version.h> + +/* + * special case: ipsec core modular with this static algo inside: + * must avoid MODULE magic for this file + */ +#if CONFIG_IPSEC_MODULE && CONFIG_IPSEC_ALG_CRYPTOAPI +#undef MODULE +#endif + +#include <linux/module.h> +#include <linux/init.h> + +#include <linux/kernel.h> /* printk() */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/string.h> + +/* Check if __exit is defined, if not null it */ +#ifndef __exit +#define __exit +#endif + +/* warn the innocent */ +#if !defined (CONFIG_CRYPTO) && !defined (CONFIG_CRYPTO_MODULE) +#warning "No linux CryptoAPI found, install 2.4.22+ or 2.6.x" +#define NO_CRYPTOAPI_SUPPORT +#endif +/* Low freeswan header coupling */ +#include "freeswan/ipsec_alg.h" + +#include <linux/crypto.h> +#ifdef CRYPTO_API_VERSION_CODE +#warning "Old CryptoAPI is not supported. Only linux-2.4.22+ or linux-2.6.x are supported" +#define NO_CRYPTOAPI_SUPPORT +#endif + +#ifdef NO_CRYPTOAPI_SUPPORT +#warning "Building an unusable module :P" +/* Catch old CryptoAPI by not allowing module to load */ +IPSEC_ALG_MODULE_INIT( ipsec_cryptoapi_init ) +{ + printk(KERN_WARNING "ipsec_cryptoapi.o was not built on stock Linux CryptoAPI (2.4.22+ or 2.6.x), not loading.\n"); + return -EINVAL; +} +#else +#include <asm/scatterlist.h> +#include <asm/pgtable.h> +#include <linux/mm.h> + +#define CIPHERNAME_AES "aes" +#define CIPHERNAME_3DES "des3_ede" +#define CIPHERNAME_BLOWFISH "blowfish" +#define CIPHERNAME_CAST "cast5" +#define CIPHERNAME_SERPENT "serpent" +#define CIPHERNAME_TWOFISH "twofish" + +#define ESP_3DES 3 +#define ESP_AES 12 +#define ESP_BLOWFISH 7 /* truly _constant_ :) */ +#define ESP_CAST 6 /* quite constant :) */ +#define ESP_SERPENT 252 /* from ipsec drafts */ +#define ESP_TWOFISH 253 /* from ipsec drafts */ + +#define AH_MD5 2 +#define AH_SHA 3 +#define DIGESTNAME_MD5 "md5" +#define DIGESTNAME_SHA1 "sha1" + +MODULE_AUTHOR("Juanjo Ciarlante, Harpo MAxx, Luciano Ruete"); +static int debug=0; +MODULE_PARM(debug, "i"); +static int test=0; +MODULE_PARM(test, "i"); +static int excl=0; +MODULE_PARM(excl, "i"); + +static int noauto = 0; +MODULE_PARM(noauto,"i"); +MODULE_PARM_DESC(noauto, "Dont try all known algos, just setup enabled ones"); + +static int des_ede3[] = {-1, -1}; +static int aes[] = {-1, -1}; +static int blowfish[] = {-1, -1}; +static int cast[] = {-1, -1}; +static int serpent[] = {-1, -1}; +static int twofish[] = {-1, -1}; + +MODULE_PARM(des_ede3,"1-2i"); +MODULE_PARM(aes,"1-2i"); +MODULE_PARM(blowfish,"1-2i"); +MODULE_PARM(cast,"1-2i"); +MODULE_PARM(serpent,"1-2i"); +MODULE_PARM(twofish,"1-2i"); +MODULE_PARM_DESC(des_ede3, "0: disable | 1: force_enable | min,max: dontuse"); +MODULE_PARM_DESC(aes, "0: disable | 1: force_enable | min,max: keybitlens"); +MODULE_PARM_DESC(blowfish, "0: disable | 1: force_enable | min,max: keybitlens"); +MODULE_PARM_DESC(cast, "0: disable | 1: force_enable | min,max: keybitlens"); +MODULE_PARM_DESC(serpent, "0: disable | 1: force_enable | min,max: keybitlens"); +MODULE_PARM_DESC(twofish, "0: disable | 1: force_enable | min,max: keybitlens"); + +struct ipsec_alg_capi_cipher { + const char *ciphername; /* cryptoapi's ciphername */ + unsigned blocksize; + unsigned short minbits; + unsigned short maxbits; + int *parm; /* lkm param for this cipher */ + struct ipsec_alg_enc alg; /* note it's not a pointer */ +}; +static struct ipsec_alg_capi_cipher alg_capi_carray[] = { + { CIPHERNAME_AES , 16, 128, 256, aes , { ixt_alg_id: ESP_AES, }}, + { CIPHERNAME_TWOFISH , 16, 128, 256, twofish, { ixt_alg_id: ESP_TWOFISH, }}, + { CIPHERNAME_SERPENT , 16, 128, 256, serpent, { ixt_alg_id: ESP_SERPENT, }}, + { CIPHERNAME_CAST , 8, 128, 128, cast , { ixt_alg_id: ESP_CAST, }}, + { CIPHERNAME_BLOWFISH , 8, 128, 448, blowfish,{ ixt_alg_id: ESP_BLOWFISH, }}, + { CIPHERNAME_3DES , 8, 192, 192, des_ede3,{ ixt_alg_id: ESP_3DES, }}, + { NULL, 0, 0, 0, NULL, {} } +}; +#ifdef NOT_YET +struct ipsec_alg_capi_digest { + const char *digestname; /* cryptoapi's digestname */ + struct digest_implementation *di; + struct ipsec_alg_auth alg; /* note it's not a pointer */ +}; +static struct ipsec_alg_capi_cipher alg_capi_darray[] = { + { DIGESTNAME_MD5, NULL, { ixt_alg_id: AH_MD5, }}, + { DIGESTNAME_SHA1, NULL, { ixt_alg_id: AH_SHA, }}, + { NULL, NULL, {} } +}; +#endif +/* + * "generic" linux cryptoapi setup_cipher() function + */ +int setup_cipher(const char *ciphername) +{ + return crypto_alg_available(ciphername, 0); +} + +/* + * setups ipsec_alg_capi_cipher "hyper" struct components, calling + * register_ipsec_alg for cointaned ipsec_alg object + */ +static void _capi_destroy_key (struct ipsec_alg_enc *alg, __u8 *key_e); +static __u8 * _capi_new_key (struct ipsec_alg_enc *alg, const __u8 *key, size_t keylen); +static int _capi_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt); + +static int +setup_ipsec_alg_capi_cipher(struct ipsec_alg_capi_cipher *cptr) +{ + int ret; + cptr->alg.ixt_version = IPSEC_ALG_VERSION; + cptr->alg.ixt_module = THIS_MODULE; + atomic_set (& cptr->alg.ixt_refcnt, 0); + strncpy (cptr->alg.ixt_name , cptr->ciphername, sizeof (cptr->alg.ixt_name)); + + cptr->alg.ixt_blocksize=cptr->blocksize; + cptr->alg.ixt_keyminbits=cptr->minbits; + cptr->alg.ixt_keymaxbits=cptr->maxbits; + cptr->alg.ixt_state = 0; + if (excl) cptr->alg.ixt_state |= IPSEC_ALG_ST_EXCL; + cptr->alg.ixt_e_keylen=cptr->alg.ixt_keymaxbits/8; + cptr->alg.ixt_e_ctx_size = 0; + cptr->alg.ixt_alg_type = IPSEC_ALG_TYPE_ENCRYPT; + cptr->alg.ixt_e_new_key = _capi_new_key; + cptr->alg.ixt_e_destroy_key = _capi_destroy_key; + cptr->alg.ixt_e_cbc_encrypt = _capi_cbc_encrypt; + cptr->alg.ixt_data = cptr; + + ret=register_ipsec_alg_enc(&cptr->alg); + printk("setup_ipsec_alg_capi_cipher(): " + "alg_type=%d alg_id=%d name=%s " + "keyminbits=%d keymaxbits=%d, ret=%d\n", + cptr->alg.ixt_alg_type, + cptr->alg.ixt_alg_id, + cptr->alg.ixt_name, + cptr->alg.ixt_keyminbits, + cptr->alg.ixt_keymaxbits, + ret); + return ret; +} +/* + * called in ipsec_sa_wipe() time, will destroy key contexts + * and do 1 unbind() + */ +static void +_capi_destroy_key (struct ipsec_alg_enc *alg, __u8 *key_e) +{ + struct crypto_tfm *tfm=(struct crypto_tfm*)key_e; + + if (debug > 0) + printk(KERN_DEBUG "klips_debug: _capi_destroy_key:" + "name=%s key_e=%p \n", + alg->ixt_name, key_e); + if (!key_e) { + printk(KERN_ERR "klips_debug: _capi_destroy_key:" + "name=%s NULL key_e!\n", + alg->ixt_name); + return; + } + crypto_free_tfm(tfm); +} + +/* + * create new key context, need alg->ixt_data to know which + * (of many) cipher inside this module is the target + */ +static __u8 * +_capi_new_key (struct ipsec_alg_enc *alg, const __u8 *key, size_t keylen) +{ + struct ipsec_alg_capi_cipher *cptr; + struct crypto_tfm *tfm=NULL; + + cptr = alg->ixt_data; + if (!cptr) { + printk(KERN_ERR "_capi_new_key(): " + "NULL ixt_data (?!) for \"%s\" algo\n" + , alg->ixt_name); + goto err; + } + if (debug > 0) + printk(KERN_DEBUG "klips_debug:_capi_new_key:" + "name=%s cptr=%p key=%p keysize=%d\n", + alg->ixt_name, cptr, key, keylen); + + /* + * alloc tfm + */ + tfm = crypto_alloc_tfm(cptr->ciphername, CRYPTO_TFM_MODE_CBC); + if (!tfm) { + printk(KERN_ERR "_capi_new_key(): " + "NULL tfm for \"%s\" cryptoapi (\"%s\") algo\n" + , alg->ixt_name, cptr->ciphername); + goto err; + } + if (crypto_cipher_setkey(tfm, key, keylen) < 0) { + printk(KERN_ERR "_capi_new_key(): " + "failed new_key() for \"%s\" cryptoapi algo (keylen=%d)\n" + , alg->ixt_name, keylen); + crypto_free_tfm(tfm); + tfm=NULL; + } +err: + if (debug > 0) + printk(KERN_DEBUG "klips_debug:_capi_new_key:" + "name=%s key=%p keylen=%d tfm=%p\n", + alg->ixt_name, key, keylen, tfm); + return (__u8 *) tfm; +} +/* + * core encryption function: will use cx->ci to call actual cipher's + * cbc function + */ +static int +_capi_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt) { + int error =0; + struct crypto_tfm *tfm=(struct crypto_tfm *)key_e; + struct scatterlist sg = { + .page = virt_to_page(in), + .offset = (unsigned long)(in) % PAGE_SIZE, + .length=ilen, + }; + if (debug > 1) + printk(KERN_DEBUG "klips_debug:_capi_cbc_encrypt:" + "key_e=%p " + "in=%p out=%p ilen=%d iv=%p encrypt=%d\n" + , key_e + , in, in, ilen, iv, encrypt); + crypto_cipher_set_iv(tfm, iv, crypto_tfm_alg_ivsize(tfm)); + if (encrypt) + error = crypto_cipher_encrypt (tfm, &sg, &sg, ilen); + else + error = crypto_cipher_decrypt (tfm, &sg, &sg, ilen); + if (debug > 1) + printk(KERN_DEBUG "klips_debug:_capi_cbc_encrypt:" + "error=%d\n" + , error); + return (error<0)? error : ilen; +} +/* + * main initialization loop: for each cipher in list, do + * 1) setup cryptoapi cipher else continue + * 2) register ipsec_alg object + */ +static int +setup_cipher_list (struct ipsec_alg_capi_cipher* clist) +{ + struct ipsec_alg_capi_cipher *cptr; + /* foreach cipher in list ... */ + for (cptr=clist;cptr->ciphername;cptr++) { + /* + * see if cipher has been disabled (0) or + * if noauto set and not enabled (1) + */ + if (cptr->parm[0] == 0 || (noauto && cptr->parm[0] < 0)) { + if (debug>0) + printk(KERN_INFO "setup_cipher_list(): " + "ciphername=%s skipped at user request: " + "noauto=%d parm[0]=%d parm[1]=%d\n" + , cptr->ciphername + , noauto + , cptr->parm[0] + , cptr->parm[1]); + continue; + } + /* + * use a local ci to avoid touching cptr->ci, + * if register ipsec_alg success then bind cipher + */ + if( setup_cipher(cptr->ciphername) ) { + if (debug > 0) + printk(KERN_DEBUG "klips_debug:" + "setup_cipher_list():" + "ciphername=%s found\n" + , cptr->ciphername); + if (setup_ipsec_alg_capi_cipher(cptr) == 0) { + + + } else { + printk(KERN_ERR "klips_debug:" + "setup_cipher_list():" + "ciphername=%s failed ipsec_alg_register\n" + , cptr->ciphername); + } + } else { + if (debug>0) + printk(KERN_INFO "setup_cipher_list(): lookup for ciphername=%s: not found \n", + cptr->ciphername); + } + } + return 0; +} +/* + * deregister ipsec_alg objects and unbind ciphers + */ +static int +unsetup_cipher_list (struct ipsec_alg_capi_cipher* clist) +{ + struct ipsec_alg_capi_cipher *cptr; + /* foreach cipher in list ... */ + for (cptr=clist;cptr->ciphername;cptr++) { + if (cptr->alg.ixt_state & IPSEC_ALG_ST_REGISTERED) { + unregister_ipsec_alg_enc(&cptr->alg); + } + } + return 0; +} +/* + * test loop for registered algos + */ +static int +test_cipher_list (struct ipsec_alg_capi_cipher* clist) +{ + int test_ret; + struct ipsec_alg_capi_cipher *cptr; + /* foreach cipher in list ... */ + for (cptr=clist;cptr->ciphername;cptr++) { + if (cptr->alg.ixt_state & IPSEC_ALG_ST_REGISTERED) { + test_ret=ipsec_alg_test( + cptr->alg.ixt_alg_type, + cptr->alg.ixt_alg_id, + test); + printk("test_cipher_list(alg_type=%d alg_id=%d): test_ret=%d\n", + cptr->alg.ixt_alg_type, + cptr->alg.ixt_alg_id, + test_ret); + } + } + return 0; +} + +IPSEC_ALG_MODULE_INIT( ipsec_cryptoapi_init ) +{ + int ret, test_ret; + if ((ret=setup_cipher_list(alg_capi_carray)) < 0) + return -EPROTONOSUPPORT; + if (ret==0 && test) { + test_ret=test_cipher_list(alg_capi_carray); + } + return ret; +} +IPSEC_ALG_MODULE_EXIT( ipsec_cryptoapi_fini ) +{ + unsetup_cipher_list(alg_capi_carray); + return; +} +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +EXPORT_NO_SYMBOLS; +#endif /* NO_CRYPTOAPI_SUPPORT */ diff --git a/linux/net/ipsec/alg/ipsec_alg_serpent.c b/linux/net/ipsec/alg/ipsec_alg_serpent.c new file mode 100644 index 000000000..1f26b0b01 --- /dev/null +++ b/linux/net/ipsec/alg/ipsec_alg_serpent.c @@ -0,0 +1,139 @@ +/* + * ipsec_alg SERPENT cipher stubs + * + * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar> + * + * $Id: ipsec_alg_serpent.c,v 1.2 2004/03/22 21:53:19 as Exp $ + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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 <linux/config.h> +#include <linux/version.h> + +/* + * special case: ipsec core modular with this static algo inside: + * must avoid MODULE magic for this file + */ +#if CONFIG_IPSEC_MODULE && CONFIG_IPSEC_ALG_SERPENT +#undef MODULE +#endif + +#include <linux/module.h> +#include <linux/init.h> + +#include <linux/kernel.h> /* printk() */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/string.h> + +/* Check if __exit is defined, if not null it */ +#ifndef __exit +#define __exit +#endif + +/* Low freeswan header coupling */ +#include "freeswan/ipsec_alg.h" +#include "libserpent/serpent.h" +#include "libserpent/serpent_cbc.h" + +#define ESP_SERPENT 252 /* from ipsec drafts */ + +/* 128, 192 or 256 */ +#define ESP_SERPENT_KEY_SZ_MIN 16 /* 128 bit secret key */ +#define ESP_SERPENT_KEY_SZ_MAX 32 /* 256 bit secret key */ +#define ESP_SERPENT_CBC_BLK_LEN 16 /* SERPENT-CBC block size */ + +MODULE_AUTHOR("JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>"); +static int debug=0; +MODULE_PARM(debug, "i"); +static int test=0; +MODULE_PARM(test, "i"); +static int excl=0; +MODULE_PARM(excl, "i"); +static int keyminbits=0; +MODULE_PARM(keyminbits, "i"); +static int keymaxbits=0; +MODULE_PARM(keymaxbits, "i"); + +static int _serpent_set_key(struct ipsec_alg_enc *alg, __u8 * key_e, const __u8 * key, size_t keysize) { + serpent_context *ctx=(serpent_context *)key_e; + if (debug > 0) + printk(KERN_DEBUG "klips_debug:_serpent_set_key:" + "key_e=%p key=%p keysize=%d\n", + key_e, key, keysize); + serpent_set_key(ctx, key, keysize); + return 0; +} +static int _serpent_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt) { + serpent_context *ctx=(serpent_context *)key_e; + if (debug > 0) + printk(KERN_DEBUG "klips_debug:_serpent_cbc_encrypt:" + "key_e=%p in=%p ilen=%d iv=%p encrypt=%d\n", + key_e, in, ilen, iv, encrypt); + serpent_cbc_encrypt(ctx, in, in, ilen, iv, encrypt); + return ilen; +} +static struct ipsec_alg_enc ipsec_alg_SERPENT = { + ixt_version: IPSEC_ALG_VERSION, + ixt_module: THIS_MODULE, + ixt_refcnt: ATOMIC_INIT(0), + ixt_alg_type: IPSEC_ALG_TYPE_ENCRYPT, + ixt_alg_id: ESP_SERPENT, + ixt_name: "serpent", + ixt_blocksize: ESP_SERPENT_CBC_BLK_LEN, + ixt_keyminbits: ESP_SERPENT_KEY_SZ_MIN * 8, + ixt_keymaxbits: ESP_SERPENT_KEY_SZ_MAX * 8, + ixt_e_keylen: ESP_SERPENT_KEY_SZ_MAX, + ixt_e_ctx_size: sizeof(serpent_context), + ixt_e_set_key: _serpent_set_key, + ixt_e_cbc_encrypt:_serpent_cbc_encrypt, +}; + +IPSEC_ALG_MODULE_INIT(ipsec_serpent_init) +{ + int ret, test_ret; + if (keyminbits) + ipsec_alg_SERPENT.ixt_keyminbits=keyminbits; + if (keymaxbits) { + ipsec_alg_SERPENT.ixt_keymaxbits=keymaxbits; + if (keymaxbits*8>ipsec_alg_SERPENT.ixt_keymaxbits) + ipsec_alg_SERPENT.ixt_e_keylen=keymaxbits*8; + } + if (excl) ipsec_alg_SERPENT.ixt_state |= IPSEC_ALG_ST_EXCL; + ret=register_ipsec_alg_enc(&ipsec_alg_SERPENT); + printk("ipsec_serpent_init(alg_type=%d alg_id=%d name=%s): ret=%d\n", + ipsec_alg_SERPENT.ixt_alg_type, + ipsec_alg_SERPENT.ixt_alg_id, + ipsec_alg_SERPENT.ixt_name, + ret); + if (ret==0 && test) { + test_ret=ipsec_alg_test( + ipsec_alg_SERPENT.ixt_alg_type, + ipsec_alg_SERPENT.ixt_alg_id, + test); + printk("ipsec_serpent_init(alg_type=%d alg_id=%d): test_ret=%d\n", + ipsec_alg_SERPENT.ixt_alg_type, + ipsec_alg_SERPENT.ixt_alg_id, + test_ret); + } + return ret; +} +IPSEC_ALG_MODULE_EXIT(ipsec_serpent_fini) +{ + unregister_ipsec_alg_enc(&ipsec_alg_SERPENT); + return; +} +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +EXPORT_NO_SYMBOLS; diff --git a/linux/net/ipsec/alg/ipsec_alg_sha2.c b/linux/net/ipsec/alg/ipsec_alg_sha2.c new file mode 100644 index 000000000..548585c16 --- /dev/null +++ b/linux/net/ipsec/alg/ipsec_alg_sha2.c @@ -0,0 +1,185 @@ +/* + * ipsec_alg SHA2 hash stubs + * + * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar> + * + * $Id: ipsec_alg_sha2.c,v 1.2 2004/03/22 21:53:19 as Exp $ + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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 <linux/config.h> +#include <linux/version.h> + +/* + * special case: ipsec core modular with this static algo inside: + * must avoid MODULE magic for this file + */ +#if CONFIG_IPSEC_MODULE && CONFIG_IPSEC_ALG_SHA2 +#undef MODULE +#endif + +#include <linux/module.h> +#include <linux/init.h> + +#include <linux/kernel.h> /* printk() */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/string.h> + +/* Check if __exit is defined, if not null it */ +#ifndef __exit +#define __exit +#endif + +/* Low freeswan header coupling */ +#include "freeswan/ipsec_alg.h" +#include "libsha2/sha2.h" +#include "libsha2/hmac_sha2.h" + +MODULE_AUTHOR("JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>"); +static int debug=0; +MODULE_PARM(debug, "i"); +static int test=0; +MODULE_PARM(test, "i"); +static int excl=0; +MODULE_PARM(excl, "i"); + +/* almost constants ...: draft-ietf-ipsec-ciph-aes-cbc-03.txt */ +#define AH_SHA2_256 5 +#define AH_SHA2_384 6 +#define AH_SHA2_512 7 + +static int _sha256_hmac_set_key(struct ipsec_alg_auth *alg, __u8 * key_a, const __u8 * key, int keylen) { + sha256_hmac_context *hctx=(sha256_hmac_context*)(key_a); + sha256_hmac_set_key(hctx, key, keylen); + if (debug > 0) + printk(KERN_DEBUG "klips_debug: _sha256_hmac_set_key(): " + "key_a=%p key=%p keysize=%d\n", + key_a, key, keylen); + return 0; +} +static int _sha256_hmac_hash(struct ipsec_alg_auth *alg, __u8 * key_a, const __u8 * dat, int len, __u8 * hash, int hashlen) { + sha256_hmac_context *hctx=(sha256_hmac_context*)(key_a); + if (debug > 0) + printk(KERN_DEBUG "klips_debug: _sha256_hmac_hash(): " + "key_a=%p dat=%p len=%d hash=%p hashlen=%d\n", + key_a, dat, len, hash, hashlen); + sha256_hmac_hash(hctx, dat, len, hash, hashlen); + return 0; +} +static int _sha512_hmac_set_key(struct ipsec_alg_auth *alg, __u8 * key_a, const __u8 * key, int keylen) { + sha512_hmac_context *hctx=(sha512_hmac_context*)(key_a); + sha512_hmac_set_key(hctx, key, keylen); + if (debug > 0) + printk(KERN_DEBUG "klips_debug: _sha512_hmac_set_key(): " + "key_a=%p key=%p keysize=%d\n", + key_a, key, keylen); + return 0; +} +static int _sha512_hmac_hash(struct ipsec_alg_auth *alg, __u8 * key_a, const __u8 * dat, int len, __u8 * hash, int hashlen) { + sha512_hmac_context *hctx=(sha512_hmac_context*)(key_a); + if (debug > 0) + printk(KERN_DEBUG "klips_debug: _sha512_hmac_hash(): " + "key_a=%p dat=%p len=%d hash=%p hashlen=%d\n", + key_a, dat, len, hash, hashlen); + sha512_hmac_hash(hctx, dat, len, hash, hashlen); + return 0; +} +static struct ipsec_alg_auth ipsec_alg_SHA2_256 = { + ixt_version: IPSEC_ALG_VERSION, + ixt_module: THIS_MODULE, + ixt_refcnt: ATOMIC_INIT(0), + ixt_alg_type: IPSEC_ALG_TYPE_AUTH, + ixt_alg_id: AH_SHA2_256, + ixt_name: "sha2_256", + ixt_blocksize: SHA256_BLOCKSIZE, + ixt_keyminbits: 256, + ixt_keymaxbits: 256, + ixt_a_keylen: 256/8, + ixt_a_ctx_size: sizeof(sha256_hmac_context), + ixt_a_hmac_set_key: _sha256_hmac_set_key, + ixt_a_hmac_hash: _sha256_hmac_hash, +}; +static struct ipsec_alg_auth ipsec_alg_SHA2_512 = { + ixt_version: IPSEC_ALG_VERSION, + ixt_module: THIS_MODULE, + ixt_refcnt: ATOMIC_INIT(0), + ixt_alg_type: IPSEC_ALG_TYPE_AUTH, + ixt_alg_id: AH_SHA2_512, + ixt_name: "sha2_512", + ixt_blocksize: SHA512_BLOCKSIZE, + ixt_keyminbits: 512, + ixt_keymaxbits: 512, + ixt_a_keylen: 512/8, + ixt_a_ctx_size: sizeof(sha512_hmac_context), + ixt_a_hmac_set_key: _sha512_hmac_set_key, + ixt_a_hmac_hash: _sha512_hmac_hash, +}; + +IPSEC_ALG_MODULE_INIT( ipsec_sha2_init ) +{ + int ret, test_ret; + if (excl) ipsec_alg_SHA2_256.ixt_state |= IPSEC_ALG_ST_EXCL; + ret=register_ipsec_alg_auth(&ipsec_alg_SHA2_256); + printk("ipsec_sha2_init(alg_type=%d alg_id=%d name=%s): ret=%d\n", + ipsec_alg_SHA2_256.ixt_alg_type, + ipsec_alg_SHA2_256.ixt_alg_id, + ipsec_alg_SHA2_256.ixt_name, + ret); + if (ret != 0) + goto out; + if (ret==0 && test) { + test_ret=ipsec_alg_test( + ipsec_alg_SHA2_256.ixt_alg_type, + ipsec_alg_SHA2_256.ixt_alg_id, + test); + printk("ipsec_sha2_init(alg_type=%d alg_id=%d): test_ret=%d\n", + ipsec_alg_SHA2_256.ixt_alg_type, + ipsec_alg_SHA2_256.ixt_alg_id, + test_ret); + } + if (excl) ipsec_alg_SHA2_512.ixt_state |= IPSEC_ALG_ST_EXCL; + ret=register_ipsec_alg_auth(&ipsec_alg_SHA2_512); + printk("ipsec_sha2_init(alg_type=%d alg_id=%d name=%s): ret=%d\n", + ipsec_alg_SHA2_512.ixt_alg_type, + ipsec_alg_SHA2_512.ixt_alg_id, + ipsec_alg_SHA2_512.ixt_name, + ret); + if (ret != 0) + goto out_256; + if (ret==0 && test) { + test_ret=ipsec_alg_test( + ipsec_alg_SHA2_512.ixt_alg_type, + ipsec_alg_SHA2_512.ixt_alg_id, + test); + printk("ipsec_sha2_init(alg_type=%d alg_id=%d): test_ret=%d\n", + ipsec_alg_SHA2_512.ixt_alg_type, + ipsec_alg_SHA2_512.ixt_alg_id, + test_ret); + } + goto out; +out_256: + unregister_ipsec_alg_auth(&ipsec_alg_SHA2_256); +out: + return ret; +} +IPSEC_ALG_MODULE_EXIT( ipsec_sha2_fini ) +{ + unregister_ipsec_alg_auth(&ipsec_alg_SHA2_512); + unregister_ipsec_alg_auth(&ipsec_alg_SHA2_256); + return; +} +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +EXPORT_NO_SYMBOLS; diff --git a/linux/net/ipsec/alg/ipsec_alg_twofish.c b/linux/net/ipsec/alg/ipsec_alg_twofish.c new file mode 100644 index 000000000..dfeba1f1b --- /dev/null +++ b/linux/net/ipsec/alg/ipsec_alg_twofish.c @@ -0,0 +1,138 @@ +/* + * ipsec_alg TWOFISH cipher stubs + * + * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar> + * + * $Id: ipsec_alg_twofish.c,v 1.2 2004/03/22 21:53:19 as Exp $ + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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 <linux/config.h> +#include <linux/version.h> + +/* + * special case: ipsec core modular with this static algo inside: + * must avoid MODULE magic for this file + */ +#if CONFIG_IPSEC_MODULE && CONFIG_IPSEC_ALG_TWOFISH +#undef MODULE +#endif + +#include <linux/module.h> +#include <linux/init.h> + +#include <linux/kernel.h> /* printk() */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/string.h> + +/* Check if __exit is defined, if not null it */ +#ifndef __exit +#define __exit +#endif + +/* Low freeswan header coupling */ +#include "freeswan/ipsec_alg.h" +#include "libtwofish/twofish.h" +#include "libtwofish/twofish_cbc.h" + +#define ESP_TWOFISH 253 /* from ipsec drafts */ + +/* 128, 192 or 256 */ +#define ESP_TWOFISH_KEY_SZ_MIN 16 /* 128 bit secret key */ +#define ESP_TWOFISH_KEY_SZ_MAX 32 /* 256 bit secret key */ +#define ESP_TWOFISH_CBC_BLK_LEN 16 /* TWOFISH-CBC block size */ + +MODULE_AUTHOR("JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>"); +static int debug=0; +MODULE_PARM(debug, "i"); +static int test=0; +MODULE_PARM(test, "i"); +static int excl=0; +MODULE_PARM(excl, "i"); +static int keyminbits=0; +MODULE_PARM(keyminbits, "i"); +static int keymaxbits=0; +MODULE_PARM(keymaxbits, "i"); + +static int _twofish_set_key(struct ipsec_alg_enc *alg, __u8 * key_e, const __u8 * key, size_t keysize) { + twofish_context *ctx=(twofish_context *)key_e; + if (debug > 0) + printk(KERN_DEBUG "klips_debug:_twofish_set_key:" + "key_e=%p key=%p keysize=%d\n", + key_e, key, keysize); + twofish_set_key(ctx, key, keysize); + return 0; +} +static int _twofish_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt) { + twofish_context *ctx=(twofish_context *)key_e; + if (debug > 0) + printk(KERN_DEBUG "klips_debug:_twofish_cbc_encrypt:" + "key_e=%p in=%p ilen=%d iv=%p encrypt=%d\n", + key_e, in, ilen, iv, encrypt); + twofish_cbc_encrypt(ctx, in, in, ilen, iv, encrypt); + return ilen; +} +static struct ipsec_alg_enc ipsec_alg_TWOFISH = { + ixt_version: IPSEC_ALG_VERSION, + ixt_module: THIS_MODULE, + ixt_refcnt: ATOMIC_INIT(0), + ixt_alg_type: IPSEC_ALG_TYPE_ENCRYPT, + ixt_alg_id: ESP_TWOFISH, + ixt_name: "twofish", + ixt_blocksize: ESP_TWOFISH_CBC_BLK_LEN, + ixt_keyminbits: ESP_TWOFISH_KEY_SZ_MIN * 8, + ixt_keymaxbits: ESP_TWOFISH_KEY_SZ_MAX * 8, + ixt_e_keylen: ESP_TWOFISH_KEY_SZ_MAX, + ixt_e_ctx_size: sizeof(twofish_context), + ixt_e_set_key: _twofish_set_key, + ixt_e_cbc_encrypt:_twofish_cbc_encrypt, +}; + +IPSEC_ALG_MODULE_INIT( ipsec_twofish_init ) +{ + int ret, test_ret; + if (keyminbits) + ipsec_alg_TWOFISH.ixt_keyminbits=keyminbits; + if (keymaxbits) { + ipsec_alg_TWOFISH.ixt_keymaxbits=keymaxbits; + if (keymaxbits*8>ipsec_alg_TWOFISH.ixt_keymaxbits) + ipsec_alg_TWOFISH.ixt_e_keylen=keymaxbits*8; + } + if (excl) ipsec_alg_TWOFISH.ixt_state |= IPSEC_ALG_ST_EXCL; + ret=register_ipsec_alg_enc(&ipsec_alg_TWOFISH); + printk("ipsec_twofish_init(alg_type=%d alg_id=%d name=%s): ret=%d\n", + ipsec_alg_TWOFISH.ixt_alg_type, + ipsec_alg_TWOFISH.ixt_alg_id, + ipsec_alg_TWOFISH.ixt_name, ret); + if (ret==0 && test) { + test_ret=ipsec_alg_test( + ipsec_alg_TWOFISH.ixt_alg_type, + ipsec_alg_TWOFISH.ixt_alg_id, + test); + printk("ipsec_twofish_init(alg_type=%d alg_id=%d): test_ret=%d\n", + ipsec_alg_TWOFISH.ixt_alg_type, + ipsec_alg_TWOFISH.ixt_alg_id, + ret); + } + return ret; +} +IPSEC_ALG_MODULE_EXIT( ipsec_twofish_fini ) +{ + unregister_ipsec_alg_enc(&ipsec_alg_TWOFISH); + return; +} +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); + +EXPORT_NO_SYMBOLS; +#endif diff --git a/linux/net/ipsec/alg/scripts/mk-static_init.c.sh b/linux/net/ipsec/alg/scripts/mk-static_init.c.sh new file mode 100644 index 000000000..8a17c670e --- /dev/null +++ b/linux/net/ipsec/alg/scripts/mk-static_init.c.sh @@ -0,0 +1,18 @@ +#!/bin/sh +cat << EOF +#include <linux/kernel.h> +#include <linux/list.h> +#include "freeswan/ipsec_alg.h" +$(for i in $*; do + test -z "$i" && continue + echo "extern int $i(void);" +done) +void ipsec_alg_static_init(void){ + int __attribute__ ((unused)) err=0; +$(for i in $*; do + test -z "$i" && continue + echo " if ((err=$i()) < 0)" + echo " printk(KERN_WARNING \"$i() returned %d\", err);" +done) +} +EOF diff --git a/linux/net/ipsec/defconfig b/linux/net/ipsec/defconfig new file mode 100644 index 000000000..84be04318 --- /dev/null +++ b/linux/net/ipsec/defconfig @@ -0,0 +1,140 @@ + +# +# RCSID $Id: defconfig,v 1.2 2004/03/22 21:53:19 as Exp $ +# + +# +# FreeS/WAN IPSec implementation, KLIPS kernel config defaults +# + +# +# First, lets override stuff already set or not in the kernel config. +# +# We can't even think about leaving this off... +CONFIG_INET=y + +# +# This must be on for subnet protection. +CONFIG_IP_FORWARD=y + +# Shut off IPSEC masquerading if it has been enabled, since it will +# break the compile. IPPROTO_ESP and IPPROTO_AH were included in +# net/ipv4/ip_masq.c when they should have gone into include/linux/in.h. +CONFIG_IP_MASQUERADE_IPSEC=n + +# +# Next, lets set the recommended FreeS/WAN configuration. +# + +# To config as static (preferred), 'y'. To config as module, 'm'. +CONFIG_IPSEC=y + +# To do tunnel mode IPSec, this must be enabled. +CONFIG_IPSEC_IPIP=y + +# To enable authentication, say 'y'. (Highly recommended) +CONFIG_IPSEC_AH=y + +# Authentication algorithm(s): +CONFIG_IPSEC_AUTH_HMAC_MD5=y +CONFIG_IPSEC_AUTH_HMAC_SHA1=y + +# To enable encryption, say 'y'. (Highly recommended) +CONFIG_IPSEC_ESP=y + +# Encryption algorithm(s): +CONFIG_IPSEC_ENC_3DES=y + +# modular algo extensions (and new ALGOs) +CONFIG_IPSEC_ALG=y +CONFIG_IPSEC_ALG_3DES=m +CONFIG_IPSEC_ALG_AES=m +CONFIG_IPSEC_ALG_TWOFISH=m +CONFIG_IPSEC_ALG_BLOWFISH=m +CONFIG_IPSEC_ALG_SERPENT=m +CONFIG_IPSEC_ALG_MD5=m +CONFIG_IPSEC_ALG_SHA1=m +CONFIG_IPSEC_ALG_SHA2=m +#CONFIG_IPSEC_ALG_CAST=n +#CONFIG_IPSEC_ALG_NULL=n + +# Use CryptoAPI for ALG? +CONFIG_IPSEC_ALG_CRYPTOAPI=m + + +# IP Compression: new, probably still has minor bugs. +CONFIG_IPSEC_IPCOMP=y + +# To enable userspace-switchable KLIPS debugging, say 'y'. +CONFIG_IPSEC_DEBUG=y + +# NAT Traversal +CONFIG_IPSEC_NAT_TRAVERSAL=y + +# +# +# $Log: defconfig,v $ +# Revision 1.2 2004/03/22 21:53:19 as +# merged alg-0.8.1 branch with HEAD +# +# Revision 1.1.2.1.2.1 2004/03/16 09:48:19 as +# alg-0.8.1rc12 patch merged +# +# Revision 1.1.2.1 2004/03/15 22:30:06 as +# nat-0.6c patch merged +# +# Revision 1.1 2004/03/15 20:35:26 as +# added files from freeswan-2.04-x509-1.5.3 +# +# Revision 1.22 2003/02/24 19:37:27 mcr +# changed default compilation mode to static. +# +# Revision 1.21 2002/04/24 07:36:27 mcr +# Moved from ./klips/net/ipsec/defconfig,v +# +# Revision 1.20 2002/04/02 04:07:40 mcr +# default build is now 'm'odule for KLIPS +# +# Revision 1.19 2002/03/08 18:57:17 rgb +# Added a blank line at the beginning of the file to make it easier for +# other projects to patch ./arch/i386/defconfig, for example +# LIDS+grSecurity requested by Jason Pattie. +# +# Revision 1.18 2000/11/30 17:26:56 rgb +# Cleaned out unused options and enabled ipcomp by default. +# +# Revision 1.17 2000/09/15 11:37:01 rgb +# Merge in heavily modified Svenning Soerensen's <svenning@post5.tele.dk> +# IPCOMP zlib deflate code. +# +# Revision 1.16 2000/09/08 19:12:55 rgb +# Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. +# +# Revision 1.15 2000/05/24 19:37:13 rgb +# *** empty log message *** +# +# Revision 1.14 2000/05/11 21:14:57 henry +# just commenting the FOOBAR=y lines out is not enough +# +# Revision 1.13 2000/05/10 20:17:58 rgb +# Comment out netlink defaults, which are no longer needed. +# +# Revision 1.12 2000/05/10 19:13:38 rgb +# Added configure option to shut off no eroute passthrough. +# +# Revision 1.11 2000/03/16 07:09:46 rgb +# Hardcode PF_KEYv2 support. +# Disable IPSEC_ICMP by default. +# Remove DES config option from defaults file. +# +# Revision 1.10 2000/01/11 03:09:42 rgb +# Added a default of 'y' to PF_KEYv2 keying I/F. +# +# Revision 1.9 1999/05/08 21:23:12 rgb +# Added support for 2.2.x kernels. +# +# Revision 1.8 1999/04/06 04:54:25 rgb +# Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes +# patch shell fixes. +# +# diff --git a/linux/net/ipsec/ipcomp.c b/linux/net/ipsec/ipcomp.c new file mode 100644 index 000000000..ff12f2cdd --- /dev/null +++ b/linux/net/ipsec/ipcomp.c @@ -0,0 +1,725 @@ +/* + * IPCOMP zlib interface code. + * Copyright (C) 2000 Svenning Soerensen <svenning@post5.tele.dk> + * Copyright (C) 2000, 2001 Richard Guy Briggs <rgb@conscoop.ottawa.on.ca> + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + */ + +char ipcomp_c_version[] = "RCSID $Id: ipcomp.c,v 1.2 2004/06/13 19:57:49 as Exp $"; + +/* SSS */ + +#include <linux/config.h> +#include <linux/version.h> + +#define __NO_VERSION__ +#include <linux/module.h> +#include <linux/kernel.h> /* printk() */ + +#include "freeswan/ipsec_param.h" + +#ifdef MALLOC_SLAB +# include <linux/slab.h> /* kmalloc() */ +#else /* MALLOC_SLAB */ +# include <linux/malloc.h> /* kmalloc() */ +#endif /* MALLOC_SLAB */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> +#include <linux/netdevice.h> +#include <linux/ip.h> +#include <linux/skbuff.h> + +#include <linux/netdevice.h> /* struct device, and other headers */ +#include <linux/etherdevice.h> /* eth_type_trans */ +#include <linux/ip.h> /* struct iphdr */ +#include <linux/skbuff.h> + +#include <freeswan.h> + +#ifdef NET_21 +# include <net/dst.h> +# include <asm/uaccess.h> +# include <linux/in6.h> +# define proto_priv cb +#endif /* NET21 */ +#include <asm/checksum.h> +#include <net/ip.h> + +#include "freeswan/radij.h" +#include "freeswan/ipsec_encap.h" +#include "freeswan/ipsec_sa.h" + +#include "freeswan/ipsec_xform.h" +#include "freeswan/ipsec_tunnel.h" +#include "freeswan/ipsec_rcv.h" /* sysctl_ipsec_inbound_policy_check */ +#include "freeswan/ipcomp.h" +#include "zlib/zlib.h" +#include "zlib/zutil.h" + +#include <pfkeyv2.h> /* SADB_X_CALG_DEFLATE */ + +#ifdef CONFIG_IPSEC_DEBUG +int sysctl_ipsec_debug_ipcomp = 0; +#endif /* CONFIG_IPSEC_DEBUG */ + +static +struct sk_buff *skb_copy_ipcomp(struct sk_buff *skb, int data_growth, int gfp_mask); + +static +voidpf my_zcalloc(voidpf opaque, uInt items, uInt size) +{ + return (voidpf) kmalloc(items*size, GFP_ATOMIC); +} + +static +void my_zfree(voidpf opaque, voidpf address) +{ + kfree(address); +} + +struct sk_buff *skb_compress(struct sk_buff *skb, struct ipsec_sa *ips, unsigned int *flags) +{ + struct iphdr *iph; + unsigned int iphlen, pyldsz, cpyldsz; + unsigned char *buffer; + z_stream zs; + int zresult; + + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_debug:skb_compress: .\n"); + + if(skb == NULL) { + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_debug:skb_compress: " + "passed in NULL skb, returning ERROR.\n"); + if(flags != NULL) { + *flags |= IPCOMP_PARMERROR; + } + return skb; + } + + if(ips == NULL) { + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_debug:skb_compress: " + "passed in NULL ipsec_sa needed for cpi, returning ERROR.\n"); + if(flags) { + *flags |= IPCOMP_PARMERROR; + } + return skb; + } + + if (flags == NULL) { + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_debug:skb_compress: " + "passed in NULL flags, returning ERROR.\n"); + ipsec_kfree_skb(skb); + return NULL; + } + +#ifdef NET_21 + iph = skb->nh.iph; +#else /* NET_21 */ + iph = skb->ip_hdr; +#endif /* NET_21 */ + + switch (iph->protocol) { + case IPPROTO_COMP: + case IPPROTO_AH: + case IPPROTO_ESP: + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_debug:skb_compress: " + "skipping compression of packet with ip protocol %d.\n", + iph->protocol); + *flags |= IPCOMP_UNCOMPRESSABLE; + return skb; + } + + /* Don't compress packets already fragmented */ + if (iph->frag_off & __constant_htons(IP_MF | IP_OFFSET)) { + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_debug:skb_compress: " + "skipping compression of fragmented packet.\n"); + *flags |= IPCOMP_UNCOMPRESSABLE; + return skb; + } + + iphlen = iph->ihl << 2; + pyldsz = ntohs(iph->tot_len) - iphlen; + + /* Don't compress less than 90 bytes (rfc 2394) */ + if (pyldsz < 90) { + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_debug:skb_compress: " + "skipping compression of tiny packet, len=%d.\n", + pyldsz); + *flags |= IPCOMP_UNCOMPRESSABLE; + return skb; + } + + /* Adaptive decision */ + if (ips->ips_comp_adapt_skip) { + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_debug:skb_compress: " + "skipping compression: ips_comp_adapt_skip=%d.\n", + ips->ips_comp_adapt_skip); + ips->ips_comp_adapt_skip--; + *flags |= IPCOMP_UNCOMPRESSABLE; + return skb; + } + + zs.zalloc = my_zcalloc; + zs.zfree = my_zfree; + zs.opaque = 0; + + /* We want to use deflateInit2 because we don't want the adler + header. */ + zresult = deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -11, + DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); + if (zresult != Z_OK) { + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_error:skb_compress: " + "deflateInit2() returned error %d (%s), " + "skipping compression.\n", + zresult, + zs.msg ? zs.msg : zError(zresult)); + *flags |= IPCOMP_COMPRESSIONERROR; + return skb; + } + + + /* Max output size. Result should be max this size. + * Implementation specific tweak: + * If it's not at least 32 bytes and 6.25% smaller than + * the original packet, it's probably not worth wasting + * the receiver's CPU cycles decompressing it. + * Your mileage may vary. + */ + cpyldsz = pyldsz - sizeof(struct ipcomphdr) - (pyldsz <= 512 ? 32 : pyldsz >> 4); + + buffer = kmalloc(cpyldsz, GFP_ATOMIC); + if (!buffer) { + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_error:skb_compress: " + "unable to kmalloc(%d, GFP_ATOMIC), " + "skipping compression.\n", + cpyldsz); + *flags |= IPCOMP_COMPRESSIONERROR; + deflateEnd(&zs); + return skb; + } + +#ifdef CONFIG_IPSEC_DEBUG + if(sysctl_ipsec_debug_ipcomp && sysctl_ipsec_debug_verbose) { + __u8 *c; + int i; + + c = (__u8*)iph + iphlen; + for(i = 0; i < pyldsz; i++, c++) { + if(!(i % 16)) { + printk(KERN_INFO "skb_compress: before:"); + } + printk("%02x ", *c); + if(!((i + 1) % 16)) { + printk("\n"); + } + } + if(i % 16) { + printk("\n"); + } + } +#endif /* CONFIG_IPSEC_DEBUG */ + + zs.next_in = (char *) iph + iphlen; /* start of payload */ + zs.avail_in = pyldsz; + zs.next_out = buffer; /* start of compressed payload */ + zs.avail_out = cpyldsz; + + /* Finish compression in one step */ + zresult = deflate(&zs, Z_FINISH); + + /* Free all dynamically allocated buffers */ + deflateEnd(&zs); + if (zresult != Z_STREAM_END) { + *flags |= IPCOMP_UNCOMPRESSABLE; + kfree(buffer); + + /* Adjust adaptive counters */ + if (++(ips->ips_comp_adapt_tries) == IPCOMP_ADAPT_INITIAL_TRIES) { + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_debug:skb_compress: " + "first %d packets didn't compress, " + "skipping next %d\n", + IPCOMP_ADAPT_INITIAL_TRIES, + IPCOMP_ADAPT_INITIAL_SKIP); + ips->ips_comp_adapt_skip = IPCOMP_ADAPT_INITIAL_SKIP; + } + else if (ips->ips_comp_adapt_tries == IPCOMP_ADAPT_INITIAL_TRIES + IPCOMP_ADAPT_SUBSEQ_TRIES) { + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_debug:skb_compress: " + "next %d packets didn't compress, " + "skipping next %d\n", + IPCOMP_ADAPT_SUBSEQ_TRIES, + IPCOMP_ADAPT_SUBSEQ_SKIP); + ips->ips_comp_adapt_skip = IPCOMP_ADAPT_SUBSEQ_SKIP; + ips->ips_comp_adapt_tries = IPCOMP_ADAPT_INITIAL_TRIES; + } + + return skb; + } + + /* resulting compressed size */ + cpyldsz -= zs.avail_out; + + /* Insert IPCOMP header */ + ((struct ipcomphdr*) ((char*) iph + iphlen))->ipcomp_nh = iph->protocol; + ((struct ipcomphdr*) ((char*) iph + iphlen))->ipcomp_flags = 0; + /* use the bottom 16 bits of the spi for the cpi. The top 16 bits are + for internal reference only. */ + ((struct ipcomphdr*) (((char*)iph) + iphlen))->ipcomp_cpi = htons((__u16)(ntohl(ips->ips_said.spi) & 0x0000ffff)); + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_debug:skb_compress: " + "spi=%08x, spi&0xffff=%04x, cpi=%04x, payload size: raw=%d, comp=%d.\n", + ntohl(ips->ips_said.spi), + ntohl(ips->ips_said.spi) & 0x0000ffff, + ntohs(((struct ipcomphdr*)(((char*)iph)+iphlen))->ipcomp_cpi), + pyldsz, + cpyldsz); + + /* Update IP header */ + iph->protocol = IPPROTO_COMP; + iph->tot_len = htons(iphlen + sizeof(struct ipcomphdr) + cpyldsz); +#if 1 /* XXX checksum is done by ipsec_tunnel ? */ + iph->check = 0; + iph->check = ip_fast_csum((char *) iph, iph->ihl); +#endif + + /* Copy compressed payload */ + memcpy((char *) iph + iphlen + sizeof(struct ipcomphdr), + buffer, + cpyldsz); + kfree(buffer); + + /* Update skb length/tail by "unputting" the shrinkage */ + skb_put(skb, + cpyldsz + sizeof(struct ipcomphdr) - pyldsz); + +#ifdef CONFIG_IPSEC_DEBUG + if(sysctl_ipsec_debug_ipcomp && sysctl_ipsec_debug_verbose) { + __u8 *c; + int i; + + c = (__u8*)iph + iphlen + sizeof(struct ipcomphdr); + for(i = 0; i < cpyldsz; i++, c++) { + if(!(i % 16)) { + printk(KERN_INFO "skb_compress: result:"); + } + printk("%02x ", *c); + if(!((i + 1) % 16)) { + printk("\n"); + } + } + if(i % 16) { + printk("\n"); + } + } +#endif /* CONFIG_IPSEC_DEBUG */ + + ips->ips_comp_adapt_skip = 0; + ips->ips_comp_adapt_tries = 0; + + return skb; +} + +struct sk_buff *skb_decompress(struct sk_buff *skb, struct ipsec_sa *ips, unsigned int *flags) +{ + struct sk_buff *nskb = NULL; + + /* original ip header */ + struct iphdr *oiph, *iph; + unsigned int iphlen, pyldsz, cpyldsz; + z_stream zs; + int zresult; + + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_debug:skb_decompress: .\n"); + + if(!skb) { + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_error:skb_decompress: " + "passed in NULL skb, returning ERROR.\n"); + if (flags) *flags |= IPCOMP_PARMERROR; + return skb; + } + + if(!ips && sysctl_ipsec_inbound_policy_check) { + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_error:skb_decompress: " + "passed in NULL ipsec_sa needed for comp alg, returning ERROR.\n"); + if (flags) *flags |= IPCOMP_PARMERROR; + return skb; + } + + if (!flags) { + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_error:skb_decompress: " + "passed in NULL flags, returning ERROR.\n"); + ipsec_kfree_skb(skb); + return NULL; + } + +#ifdef NET_21 + oiph = skb->nh.iph; +#else /* NET_21 */ + oiph = skb->ip_hdr; +#endif /* NET_21 */ + + iphlen = oiph->ihl << 2; + + if (oiph->protocol != IPPROTO_COMP) { + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_error:skb_decompress: " + "called with non-IPCOMP packet (protocol=%d)," + "skipping decompression.\n", + oiph->protocol); + *flags |= IPCOMP_PARMERROR; + return skb; + } + + if ( (((struct ipcomphdr*)((char*) oiph + iphlen))->ipcomp_flags != 0) + || ((((struct ipcomphdr*) ((char*) oiph + iphlen))->ipcomp_cpi + != htons(SADB_X_CALG_DEFLATE)) + && sysctl_ipsec_inbound_policy_check + && (!ips || (ips && (ips->ips_encalg != SADB_X_CALG_DEFLATE)))) ) { + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_error:skb_decompress: " + "called with incompatible IPCOMP packet (flags=%d, " + "cpi=%d), ips-compalg=%d, skipping decompression.\n", + ntohs(((struct ipcomphdr*) ((char*) oiph + iphlen))->ipcomp_flags), + ntohs(((struct ipcomphdr*) ((char*) oiph + iphlen))->ipcomp_cpi), + ips ? ips->ips_encalg : 0); + *flags |= IPCOMP_PARMERROR; + + return skb; + } + + if (ntohs(oiph->frag_off) & ~0x4000) { + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_error:skb_decompress: " + "called with fragmented IPCOMP packet, " + "skipping decompression.\n"); + *flags |= IPCOMP_PARMERROR; + return skb; + } + + /* original compressed payload size */ + cpyldsz = ntohs(oiph->tot_len) - iphlen - sizeof(struct ipcomphdr); + + zs.zalloc = my_zcalloc; + zs.zfree = my_zfree; + zs.opaque = 0; + + zs.next_in = (char *) oiph + iphlen + sizeof(struct ipcomphdr); + zs.avail_in = cpyldsz; + + /* Maybe we should be a bit conservative about memory + requirements and use inflateInit2 */ + /* Beware, that this might make us unable to decompress packets + from other implementations - HINT: check PGPnet source code */ + /* We want to use inflateInit2 because we don't want the adler + header. */ + zresult = inflateInit2(&zs, -15); + if (zresult != Z_OK) { + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_error:skb_decompress: " + "inflateInit2() returned error %d (%s), " + "skipping decompression.\n", + zresult, + zs.msg ? zs.msg : zError(zresult)); + *flags |= IPCOMP_DECOMPRESSIONERROR; + + return skb; + } + + /* We have no way of knowing the exact length of the resulting + decompressed output before we have actually done the decompression. + For now, we guess that the packet will not be bigger than the + attached ipsec device's mtu or 16260, whichever is biggest. + This may be wrong, since the sender's mtu may be bigger yet. + XXX This must be dealt with later XXX + */ + + /* max payload size */ + pyldsz = skb->dev ? (skb->dev->mtu < 16260 ? 16260 : skb->dev->mtu) + : (65520 - iphlen); + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_debug:skb_decompress: " + "max payload size: %d\n", pyldsz); + + while (pyldsz > (cpyldsz + sizeof(struct ipcomphdr)) && + (nskb = skb_copy_ipcomp(skb, + pyldsz - cpyldsz - sizeof(struct ipcomphdr), + GFP_ATOMIC)) == NULL) { + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_error:skb_decompress: " + "unable to skb_copy_ipcomp(skb, %d, GFP_ATOMIC), " + "trying with less payload size.\n", + (int)(pyldsz - cpyldsz - sizeof(struct ipcomphdr))); + pyldsz >>=1; + } + + if (!nskb) { + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_error:skb_decompress: " + "unable to allocate memory, dropping packet.\n"); + *flags |= IPCOMP_DECOMPRESSIONERROR; + inflateEnd(&zs); + + return skb; + } + +#ifdef CONFIG_IPSEC_DEBUG + if(sysctl_ipsec_debug_ipcomp && sysctl_ipsec_debug_verbose) { + __u8 *c; + int i; + + c = (__u8*)oiph + iphlen + sizeof(struct ipcomphdr); + for(i = 0; i < cpyldsz; i++, c++) { + if(!(i % 16)) { + printk(KERN_INFO "skb_decompress: before:"); + } + printk("%02x ", *c); + if(!((i + 1) % 16)) { + printk("\n"); + } + } + if(i % 16) { + printk("\n"); + } + } +#endif /* CONFIG_IPSEC_DEBUG */ + +#ifdef NET_21 + iph = nskb->nh.iph; +#else /* NET_21 */ + iph = nskb->ip_hdr; +#endif /* NET_21 */ + zs.next_out = (char *)iph + iphlen; + zs.avail_out = pyldsz; + + zresult = inflate(&zs, Z_SYNC_FLUSH); + + /* work around a bug in zlib, which sometimes wants to taste an extra + * byte when being used in the (undocumented) raw deflate mode. + */ + if (zresult == Z_OK && !zs.avail_in && zs.avail_out) { + __u8 zerostuff = 0; + + zs.next_in = &zerostuff; + zs.avail_in = 1; + zresult = inflate(&zs, Z_FINISH); + } + + inflateEnd(&zs); + if (zresult != Z_STREAM_END) { + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_error:skb_decompress: " + "inflate() returned error %d (%s), " + "skipping decompression.\n", + zresult, + zs.msg ? zs.msg : zError(zresult)); + *flags |= IPCOMP_DECOMPRESSIONERROR; + ipsec_kfree_skb(nskb); + + return skb; + } + + /* Update IP header */ + /* resulting decompressed size */ + pyldsz -= zs.avail_out; + iph->tot_len = htons(iphlen + pyldsz); + iph->protocol = ((struct ipcomphdr*) ((char*) oiph + iphlen))->ipcomp_nh; + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_debug:skb_decompress: " + "spi=%08x, spi&0xffff=%04x, cpi=%04x, payload size: comp=%d, raw=%d, nh=%d.\n", + ips ? ntohl(ips->ips_said.spi) : 0, + ips ? ntohl(ips->ips_said.spi) & 0x0000ffff : 0, + ntohs(((struct ipcomphdr*)(((char*)oiph)+iphlen))->ipcomp_cpi), + cpyldsz, + pyldsz, + iph->protocol); + +#if 1 /* XXX checksum is done by ipsec_rcv ? */ + iph->check = 0; + iph->check = ip_fast_csum((char*) iph, iph->ihl); +#endif + + /* Update skb length/tail by "unputting" the unused data area */ + skb_put(nskb, -zs.avail_out); + + ipsec_kfree_skb(skb); + + if (iph->protocol == IPPROTO_COMP) + { +#ifdef CONFIG_IPSEC_DEBUG + if(sysctl_ipsec_debug_ipcomp) + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_debug:skb_decompress: " + "Eh? inner packet is also compressed, dropping.\n"); +#endif /* CONFIG_IPSEC_DEBUG */ + + ipsec_kfree_skb(nskb); + return NULL; + } + +#ifdef CONFIG_IPSEC_DEBUG + if(sysctl_ipsec_debug_ipcomp && sysctl_ipsec_debug_verbose) { + __u8 *c; + int i; + + c = (__u8*)iph + iphlen; + for(i = 0; i < pyldsz; i++, c++) { + if(!(i % 16)) { + printk(KERN_INFO "skb_decompress: result:"); + } + printk("%02x ", *c); + if(!((i + 1) % 16)) { + printk("\n"); + } + } + if(i % 16) { + printk("\n"); + } + } +#endif /* CONFIG_IPSEC_DEBUG */ + + return nskb; +} + + +/* this is derived from skb_copy() in linux 2.2.14 */ +/* May be incompatible with other kernel versions!! */ +static +struct sk_buff *skb_copy_ipcomp(struct sk_buff *skb, int data_growth, int gfp_mask) +{ + struct sk_buff *n; + struct iphdr *iph; + unsigned long offset; + unsigned int iphlen; + + if(!skb) { + KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, + "klips_debug:skb_copy_ipcomp: " + "passed in NULL skb, returning NULL.\n"); + return NULL; + } + + /* + * Allocate the copy buffer + */ + +#ifdef NET_21 + iph = skb->nh.iph; +#else /* NET_21 */ + iph = skb->ip_hdr; +#endif /* NET_21 */ + if (!iph) return NULL; + iphlen = iph->ihl << 2; + + n=alloc_skb(skb->end - skb->head + data_growth, gfp_mask); + if(n==NULL) + return NULL; + + /* + * Shift between the two data areas in bytes + */ + + offset=n->head-skb->head; + + /* Set the data pointer */ + skb_reserve(n,skb->data-skb->head); + /* Set the tail pointer and length */ + skb_put(n,skb->len+data_growth); + /* Copy the bytes up to and including the ip header */ + memcpy(n->head, + skb->head, + ((char *)iph - (char *)skb->head) + iphlen); + n->list=NULL; + n->next=NULL; + n->prev=NULL; + n->sk=NULL; + n->dev=skb->dev; + if (skb->h.raw) + n->h.raw=skb->h.raw+offset; + else + n->h.raw=NULL; + n->protocol=skb->protocol; +#ifdef NET_21 + n->csum = 0; + n->priority=skb->priority; + n->dst=dst_clone(skb->dst); + n->nh.raw=skb->nh.raw+offset; +#ifndef NETDEV_23 + n->is_clone=0; +#endif /* NETDEV_23 */ + atomic_set(&n->users, 1); + n->destructor = NULL; + n->security=skb->security; + memcpy(n->cb, skb->cb, sizeof(skb->cb)); +#ifdef CONFIG_IP_FIREWALL + n->fwmark = skb->fwmark; +#endif +#else /* NET_21 */ + n->link3=NULL; + n->when=skb->when; + n->ip_hdr=(struct iphdr *)(((char *)skb->ip_hdr)+offset); + n->saddr=skb->saddr; + n->daddr=skb->daddr; + n->raddr=skb->raddr; + n->seq=skb->seq; + n->end_seq=skb->end_seq; + n->ack_seq=skb->ack_seq; + n->acked=skb->acked; + n->free=1; + n->arp=skb->arp; + n->tries=0; + n->lock=0; + n->users=0; + memcpy(n->proto_priv, skb->proto_priv, sizeof(skb->proto_priv)); +#endif /* NET_21 */ + if (skb->mac.raw) + n->mac.raw=skb->mac.raw+offset; + else + n->mac.raw=NULL; +#ifndef NETDEV_23 + n->used=skb->used; +#endif /* !NETDEV_23 */ + n->pkt_type=skb->pkt_type; +#ifndef NETDEV_23 + n->pkt_bridged=skb->pkt_bridged; +#endif /* NETDEV_23 */ + n->ip_summed=0; + n->stamp=skb->stamp; +#ifndef NETDEV_23 /* this seems to have been removed in 2.4 */ +#if defined(CONFIG_SHAPER) || defined(CONFIG_SHAPER_MODULE) + n->shapelatency=skb->shapelatency; /* Latency on frame */ + n->shapeclock=skb->shapeclock; /* Time it should go out */ + n->shapelen=skb->shapelen; /* Frame length in clocks */ + n->shapestamp=skb->shapestamp; /* Stamp for shaper */ + n->shapepend=skb->shapepend; /* Pending */ +#endif /* defined(CONFIG_SHAPER) || defined(CONFIG_SHAPER_MODULE) */ +#endif /* NETDEV_23 */ +#ifdef CONFIG_HIPPI + n->private.ifield=skb->private.ifield; +#endif /* CONFIG_HIPPI */ + + return n; +} diff --git a/linux/net/ipsec/ipsec_alg.c b/linux/net/ipsec/ipsec_alg.c new file mode 100644 index 000000000..c402b7e5b --- /dev/null +++ b/linux/net/ipsec/ipsec_alg.c @@ -0,0 +1,927 @@ +/* + * Modular extensions service and registration functions + * + * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar> + * + * Version: 0.8.1 + * + * $Id: ipsec_alg.c,v 1.4 2004/06/13 19:57:49 as Exp $ + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + * + */ +#ifdef CONFIG_IPSEC_ALG +#define __NO_VERSION__ +#include <linux/module.h> +#include <linux/kernel.h> /* printk() */ + +#include <linux/netdevice.h> /* struct device, and other headers */ +#include <linux/etherdevice.h> /* eth_type_trans */ +#include <linux/ip.h> /* struct iphdr */ +#include <linux/skbuff.h> +#include <linux/socket.h> +#include <linux/in.h> +#include <linux/types.h> +#include <linux/string.h> /* memcmp() */ +#include <linux/random.h> /* get_random_bytes() */ +#include <linux/errno.h> /* error codes */ +#ifdef SPINLOCK +# ifdef SPINLOCK_23 +# include <linux/spinlock.h> /* *lock* */ +# else /* SPINLOCK_23 */ +# include <asm/spinlock.h> /* *lock* */ +# endif /* SPINLOCK_23 */ +#endif /* SPINLOCK */ +#ifdef NET_21 +# include <asm/uaccess.h> +# include <linux/in6.h> +# define proto_priv cb +#endif /* NET21 */ +#include "freeswan/ipsec_param.h" +#include <freeswan.h> +#include "freeswan/ipsec_sa.h" +#include "freeswan/radij.h" +#include "freeswan/ipsec_encap.h" +#include "freeswan/ipsec_radij.h" +#include "freeswan/ipsec_xform.h" +#include "freeswan/ipsec_tunnel.h" +#include "freeswan/ipsec_rcv.h" +#if defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH) +# include "freeswan/ipsec_ah.h" +#endif /* defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH) */ +#ifdef CONFIG_IPSEC_ESP +# include "freeswan/ipsec_esp.h" +#endif /* !CONFIG_IPSEC_ESP */ +#ifdef CONFIG_IPSEC_IPCOMP +# include "freeswan/ipcomp.h" +#endif /* CONFIG_IPSEC_COMP */ + +#include <pfkeyv2.h> +#include <pfkey.h> + +#include "freeswan/ipsec_alg.h" + +#ifndef CONFIG_IPSEC_ALG +#error This file _MUST_ be compiled with CONFIG_IPSEC_ALG enabled ! +#endif +#if SADB_EALG_MAX < 255 +#warning Compiling with limited ESP support ( SADB_EALG_MAX < 256 ) +#endif + +static rwlock_t ipsec_alg_lock = RW_LOCK_UNLOCKED; +#define IPSEC_ALG_HASHSZ 16 /* must be power of 2, even 2^0=1 */ +static struct list_head ipsec_alg_hash_table[IPSEC_ALG_HASHSZ]; + +/* Old gcc's will fail here */ +#define barf_out(fmt, args...) do { printk(KERN_ERR "%s: (%s) " fmt, __FUNCTION__, ixt->ixt_name , ## args)\ + ; goto out; } while(0) + +/* + * Must be already protected by lock + */ +static void __ipsec_alg_usage_inc(struct ipsec_alg *ixt) { + if (ixt->ixt_module) + __MOD_INC_USE_COUNT(ixt->ixt_module); + atomic_inc(&ixt->ixt_refcnt); +} +static void __ipsec_alg_usage_dec(struct ipsec_alg *ixt) { + atomic_dec(&ixt->ixt_refcnt); + if (ixt->ixt_module) + __MOD_DEC_USE_COUNT(ixt->ixt_module); +} +/* + * simple hash function, optimized for 0-hash (1 list) special + * case + */ +#if IPSEC_ALG_HASHSZ > 1 +static inline unsigned ipsec_alg_hashfn(int alg_type, int alg_id) { + return ((alg_type^alg_id)&(IPSEC_ALG_HASHSZ-1)); +} +#else +#define ipsec_alg_hashfn(x,y) (0) +#endif + +/***************************************************************** + * + * INTERNAL table handling: insert, delete, find + * + *****************************************************************/ + +/* + * hash table initialization, called from ipsec_alg_init() + */ +static void ipsec_alg_hash_init(void) { + struct list_head *head = ipsec_alg_hash_table; + int i = IPSEC_ALG_HASHSZ; + do { + INIT_LIST_HEAD(head); + head++; + i--; + } while (i); +} +/* + * hash list lookup by {alg_type, alg_id} and table head, + * must be already protected by lock + */ +static struct ipsec_alg *__ipsec_alg_find(unsigned alg_type, unsigned alg_id, struct list_head * head) { + struct list_head *p; + struct ipsec_alg *ixt=NULL; + for (p=head->next; p!=head; p=p->next) { + ixt = list_entry(p, struct ipsec_alg, ixt_list); + if (ixt->ixt_alg_type == alg_type && ixt->ixt_alg_id==alg_id) { + goto out; + } + } + ixt=NULL; +out: + return ixt; +} +/* + * inserts (in front) a new entry in hash table, + * called from ipsec_alg_register() when new algorithm is registered. + */ +static int ipsec_alg_insert(struct ipsec_alg *ixt) { + int ret=-EINVAL; + unsigned hashval=ipsec_alg_hashfn(ixt->ixt_alg_type, ixt->ixt_alg_id); + struct list_head *head= ipsec_alg_hash_table + hashval; + struct ipsec_alg *ixt_cur; + /* new element must be virgin ... */ + if (ixt->ixt_list.next != &ixt->ixt_list || + ixt->ixt_list.prev != &ixt->ixt_list) { + printk(KERN_ERR "ipsec_alg_insert: ixt object \"%s\" " + "list head not initialized\n", + ixt->ixt_name); + return ret; + } + write_lock_bh(&ipsec_alg_lock); + ixt_cur = __ipsec_alg_find(ixt->ixt_alg_type, ixt->ixt_alg_id, head); + /* if previous (current) ipsec_alg found check excl flag of _anyone_ */ + if (ixt_cur && ((ixt->ixt_state|ixt_cur->ixt_state) & IPSEC_ALG_ST_EXCL)) + barf_out("ipsec_alg for alg_type=%d, alg_id=%d already exist. " + "Not loaded (ret=%d).\n", + ixt->ixt_alg_type, + ixt->ixt_alg_id, ret=-EEXIST); + list_add(&ixt->ixt_list, head); + ixt->ixt_state |= IPSEC_ALG_ST_REGISTERED; + ret=0; +out: + write_unlock_bh(&ipsec_alg_lock); + return ret; +} +/* + * deletes an existing entry in hash table, + * called from ipsec_alg_unregister() when algorithm is unregistered. + */ +static int ipsec_alg_delete(struct ipsec_alg *ixt) { + write_lock_bh(&ipsec_alg_lock); + list_del(&ixt->ixt_list); + write_unlock_bh(&ipsec_alg_lock); + return 0; +} +/* + * here @user context (read-only when @kernel bh context) + * -> no bh disabling + * + * called from ipsec_sa_init() -> ipsec_alg_sa_init() + */ +static struct ipsec_alg *ipsec_alg_get(int alg_type, int alg_id) { + unsigned hashval=ipsec_alg_hashfn(alg_type, alg_id); + struct list_head *head= ipsec_alg_hash_table + hashval; + struct ipsec_alg *ixt; + read_lock(&ipsec_alg_lock); + ixt=__ipsec_alg_find(alg_type, alg_id, head); + if (ixt) __ipsec_alg_usage_inc(ixt); + read_unlock(&ipsec_alg_lock); + return ixt; +} + +static void ipsec_alg_put(struct ipsec_alg *ixt) { + __ipsec_alg_usage_dec((struct ipsec_alg *)ixt); +} + +/***************************************************************** + * + * INTERFACE for ENC services: key creation, encrypt function + * + *****************************************************************/ + +/* + * main encrypt service entry point + * called from ipsec_rcv() with encrypt=IPSEC_ALG_DECRYPT and + * ipsec_tunnel_start_xmit with encrypt=IPSEC_ALG_ENCRYPT + */ +int ipsec_alg_esp_encrypt(struct ipsec_sa *sa_p, __u8 * idat, int ilen, const __u8 * iv, int encrypt) { + int ret; + struct ipsec_alg_enc *ixt_e=sa_p->ips_alg_enc; + KLIPS_PRINT(debug_rcv||debug_tunnel, + "klips_debug:ipsec_alg_esp_encrypt: " + "entering with encalg=%d, ixt_e=%p\n", + sa_p->ips_encalg, ixt_e); + if (!ixt_e) { + KLIPS_PRINT(debug_rcv||debug_tunnel, + "klips_debug:ipsec_alg_esp_encrypt: " + "NULL ipsec_alg_enc object\n"); + return -1; + } + KLIPS_PRINT(debug_rcv||debug_tunnel, + "klips_debug:ipsec_alg_esp_encrypt: " + "calling cbc_encrypt encalg=%d " + "ips_key_e=%p idat=%p ilen=%d iv=%p, encrypt=%d\n", + sa_p->ips_encalg, + sa_p->ips_key_e, idat, ilen, iv, encrypt); + ret=ixt_e->ixt_e_cbc_encrypt(ixt_e, sa_p->ips_key_e, idat, ilen, iv, encrypt); + KLIPS_PRINT(debug_rcv||debug_tunnel, + "klips_debug:ipsec_alg_esp_encrypt: " + "returned ret=%d\n", + ret); + return ret; +} +/* + * encryption key context creation function + * called from pfkey_v2_parser.c:pfkey_ips_init() + */ +int ipsec_alg_enc_key_create(struct ipsec_sa *sa_p) { + int ret=-EINVAL; + int keyminbits, keymaxbits; + caddr_t ekp; + struct ipsec_alg_enc *ixt_e=sa_p->ips_alg_enc; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:ipsec_alg_enc_key_create: " + "entering with encalg=%d ixt_e=%p\n", + sa_p->ips_encalg, ixt_e); + if (!ixt_e) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:ipsec_alg_enc_key_create: " + "NULL ipsec_alg_enc object\n"); + return -EPROTO; + } + + /* + * grRRR... DES 7bits jurassic stuff ... f*ckk --jjo + */ + switch(ixt_e->ixt_alg_id) { + case ESP_3DES: + keyminbits=keymaxbits=192;break; + case ESP_DES: + keyminbits=keymaxbits=64;break; + default: + keyminbits=ixt_e->ixt_keyminbits; + keymaxbits=ixt_e->ixt_keymaxbits; + } + if(sa_p->ips_key_bits_e<keyminbits || + sa_p->ips_key_bits_e>keymaxbits) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:ipsec_alg_enc_key_create: " + "incorrect encryption key size for id=%d: %d bits -- " + "must be between %d,%d bits\n" /*octets (bytes)\n"*/, + ixt_e->ixt_alg_id, + sa_p->ips_key_bits_e, keyminbits, keymaxbits); + ret=-EINVAL; + goto ixt_out; + } + /* save encryption key pointer */ + ekp = sa_p->ips_key_e; + + + if (ixt_e->ixt_e_new_key) { + sa_p->ips_key_e = ixt_e->ixt_e_new_key(ixt_e, + ekp, sa_p->ips_key_bits_e/8); + ret = (sa_p->ips_key_e)? 0 : -EINVAL; + } else { + if((sa_p->ips_key_e = (caddr_t) + kmalloc((sa_p->ips_key_e_size = ixt_e->ixt_e_ctx_size), + GFP_ATOMIC)) == NULL) { + ret=-ENOMEM; + goto ixt_out; + } + /* zero-out key_e */ + memset(sa_p->ips_key_e, 0, sa_p->ips_key_e_size); + + /* I cast here to allow more decoupling in alg module */ + KLIPS_PRINT(debug_pfkey, + "klips_debug:ipsec_alg_enc_key_create: about to call:" + "set_key(key_e=%p, ekp=%p, key_size=%d)\n", + (caddr_t)sa_p->ips_key_e, ekp, sa_p->ips_key_bits_e/8); + ret = ixt_e->ixt_e_set_key(ixt_e, (caddr_t)sa_p->ips_key_e, ekp, sa_p->ips_key_bits_e/8); + } + /* paranoid */ + memset(ekp, 0, sa_p->ips_key_bits_e/8); + kfree(ekp); +ixt_out: + return ret; +} + +/*************************************************************** + * + * INTERFACE for AUTH services: key creation, hash functions + * + ***************************************************************/ + +/* + * auth key context creation function + * called from pfkey_v2_parser.c:pfkey_ips_init() + */ +int ipsec_alg_auth_key_create(struct ipsec_sa *sa_p) { + int ret=-EINVAL; + struct ipsec_alg_auth *ixt_a=sa_p->ips_alg_auth; + int keyminbits, keymaxbits; + unsigned char *akp; + unsigned int aks; + KLIPS_PRINT(debug_pfkey, + "klips_debug:ipsec_alg_auth_key_create: " + "entering with authalg=%d ixt_a=%p\n", + sa_p->ips_authalg, ixt_a); + if (!ixt_a) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:ipsec_alg_auth_key_create: " + "NULL ipsec_alg_auth object\n"); + return -EPROTO; + } + keyminbits=ixt_a->ixt_keyminbits; + keymaxbits=ixt_a->ixt_keymaxbits; + if(sa_p->ips_key_bits_a<keyminbits || sa_p->ips_key_bits_a>keymaxbits) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:ipsec_alg_auth_key_create: incorrect auth" + "key size: %d bits -- must be between %d,%d bits\n"/*octets (bytes)\n"*/, + sa_p->ips_key_bits_a, keyminbits, keymaxbits); + ret=-EINVAL; + goto ixt_out; + } + /* save auth key pointer */ + sa_p->ips_auth_bits = ixt_a->ixt_a_keylen * 8; /* XXX XXX */ + akp = sa_p->ips_key_a; + aks = sa_p->ips_key_a_size; + + /* will hold: 2 ctx and a blocksize buffer: kb */ + sa_p->ips_key_a_size = ixt_a->ixt_a_ctx_size; + if((sa_p->ips_key_a = + (caddr_t) kmalloc(sa_p->ips_key_a_size, GFP_ATOMIC)) == NULL) { + ret=-ENOMEM; + goto ixt_out; + } + ixt_a->ixt_a_hmac_set_key(ixt_a, sa_p->ips_key_a, akp, sa_p->ips_key_bits_a/8); /* XXX XXX */ + ret=0; + memset(akp, 0, aks); + kfree(akp); + +ixt_out: + return ret; +} +int ipsec_alg_sa_esp_hash(const struct ipsec_sa *sa_p, const __u8 *espp, int len, __u8 *hash, int hashlen) { + struct ipsec_alg_auth *ixt_a=sa_p->ips_alg_auth; + if (!ixt_a) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:ipsec_sa_esp_hash: " + "NULL ipsec_alg_auth object\n"); + return -EPROTO; + } + KLIPS_PRINT(debug_tunnel|debug_rcv, + "klips_debug:ipsec_sa_esp_hash: " + "hashing %p (%d bytes) to %p (%d bytes)\n", + espp, len, + hash, hashlen); + ixt_a->ixt_a_hmac_hash(ixt_a, + sa_p->ips_key_a, + espp, len, + hash, hashlen); + return 0; +} + +/*************************************************************** + * + * INTERFACE for module loading,testing, and unloading + * + ***************************************************************/ + +/* validation for registering (enc) module */ +static int check_enc(struct ipsec_alg_enc *ixt) { + int ret=-EINVAL; + if (ixt->ixt_alg_id==0 || ixt->ixt_alg_id > SADB_EALG_MAX) + barf_out("invalid alg_id=%d >= %d\n", ixt->ixt_alg_id, SADB_EALG_MAX); + if (ixt->ixt_blocksize==0) /* || ixt->ixt_blocksize%2) need for ESP_NULL */ + barf_out(KERN_ERR "invalid blocksize=%d\n", ixt->ixt_blocksize); + if (ixt->ixt_keyminbits==0 && ixt->ixt_keymaxbits==0 && ixt->ixt_e_keylen==0) + goto zero_key_ok; + if (ixt->ixt_keyminbits==0) + barf_out(KERN_ERR "invalid keyminbits=%d\n", ixt->ixt_keyminbits); + if (ixt->ixt_keymaxbits==0) + barf_out(KERN_ERR "invalid keymaxbits=%d\n", ixt->ixt_keymaxbits); + if (ixt->ixt_e_keylen==0) + barf_out(KERN_ERR "invalid keysize=%d\n", ixt->ixt_e_keylen); +zero_key_ok: + if (ixt->ixt_e_ctx_size==0 && ixt->ixt_e_new_key == NULL) + barf_out(KERN_ERR "invalid key_e_size=%d and ixt_e_new_key=NULL\n", ixt->ixt_e_ctx_size); + if (ixt->ixt_e_cbc_encrypt==NULL) + barf_out(KERN_ERR "e_cbc_encrypt() must be not NULL\n"); + ret=0; +out: + return ret; +} + +/* validation for registering (auth) module */ +static int check_auth(struct ipsec_alg_auth *ixt) { + int ret=-EINVAL; + if (ixt->ixt_alg_id==0 || ixt->ixt_alg_id > SADB_AALG_MAX) + barf_out("invalid alg_id=%d > %d (SADB_AALG_MAX)\n", ixt->ixt_alg_id, SADB_AALG_MAX); + if (ixt->ixt_blocksize==0 || ixt->ixt_blocksize%2) + barf_out(KERN_ERR "invalid blocksize=%d\n", ixt->ixt_blocksize); + if (ixt->ixt_blocksize>AH_BLKLEN_MAX) + barf_out(KERN_ERR "sorry blocksize=%d > %d. " + "Please increase AH_BLKLEN_MAX and recompile\n", + ixt->ixt_blocksize, + AH_BLKLEN_MAX); + if (ixt->ixt_keyminbits==0 && ixt->ixt_keymaxbits==0 && ixt->ixt_a_keylen==0) + goto zero_key_ok; + if (ixt->ixt_keyminbits==0) + barf_out(KERN_ERR "invalid keyminbits=%d\n", ixt->ixt_keyminbits); + if (ixt->ixt_keymaxbits==0) + barf_out(KERN_ERR "invalid keymaxbits=%d\n", ixt->ixt_keymaxbits); + if (ixt->ixt_keymaxbits!=ixt->ixt_keyminbits) + barf_out(KERN_ERR "keymaxbits must equal keyminbits (not sure).\n"); + if (ixt->ixt_a_keylen==0) + barf_out(KERN_ERR "invalid keysize=%d\n", ixt->ixt_a_keylen); +zero_key_ok: + if (ixt->ixt_a_ctx_size==0) + barf_out(KERN_ERR "invalid a_ctx_size=%d\n", ixt->ixt_a_ctx_size); + if (ixt->ixt_a_hmac_set_key==NULL) + barf_out(KERN_ERR "a_hmac_set_key() must be not NULL\n"); + if (ixt->ixt_a_hmac_hash==NULL) + barf_out(KERN_ERR "a_hmac_hash() must be not NULL\n"); + ret=0; +out: + return ret; +} + +/* + * Generic (enc, auth) registration entry point + */ +int register_ipsec_alg(struct ipsec_alg *ixt) { + int ret=-EINVAL; + /* Validation */ + if (ixt==NULL) + barf_out("NULL ipsec_alg object passed\n"); + if ((ixt->ixt_version&0xffffff00) != (IPSEC_ALG_VERSION&0xffffff00)) + barf_out("incorrect version: %d.%d.%d-%d, " + "must be %d.%d.%d[-%d]\n", + IPSEC_ALG_VERSION_QUAD(ixt->ixt_version), + IPSEC_ALG_VERSION_QUAD(IPSEC_ALG_VERSION)); + switch(ixt->ixt_alg_type) { + case IPSEC_ALG_TYPE_AUTH: + if ((ret=check_auth((struct ipsec_alg_auth *)ixt)<0)) + goto out; + break; + case IPSEC_ALG_TYPE_ENCRYPT: + if ((ret=check_enc((struct ipsec_alg_enc *)ixt)<0)) + goto out; + /* + * Adapted two lines below: + * ivlen == 0 is possible (NULL enc has blocksize==1) + * + * fixed NULL support by David De Reu <DeReu@tComLabs.com> + */ + if (ixt->ixt_ivlen == 0 && ixt->ixt_blocksize > 1) + ixt->ixt_ivlen = ixt->ixt_blocksize*8; + break; + default: + barf_out("alg_type=%d not supported\n", ixt->ixt_alg_type); + } + INIT_LIST_HEAD(&ixt->ixt_list); + ret = ipsec_alg_insert(ixt); + if (ret<0) + barf_out(KERN_WARNING "ipsec_alg for alg_id=%d failed." + "Not loaded (ret=%d).\n", + ixt->ixt_alg_id, ret); + + ret = pfkey_list_insert_supported((struct supported *)&ixt->ixt_support, &(pfkey_supported_list[SADB_SATYPE_ESP])); + if (ret==0) { + ixt->ixt_state |= IPSEC_ALG_ST_SUPP; + /* send register event to userspace */ + pfkey_register_reply(SADB_SATYPE_ESP, NULL); + } else + printk(KERN_ERR "pfkey_list_insert_supported returned %d. " + "Loading anyway.\n", ret); + ret=0; +out: + return ret; +} + +/* + * unregister ipsec_alg object from own tables, if + * success => calls pfkey_list_remove_supported() + */ +int unregister_ipsec_alg(struct ipsec_alg *ixt) { + int ret= -EINVAL; + switch(ixt->ixt_alg_type) { + case IPSEC_ALG_TYPE_AUTH: + case IPSEC_ALG_TYPE_ENCRYPT: + break; + default: + /* this is not a typo :) */ + barf_out("frog found in list (\"%s\"): ixt_p=NULL\n", + ixt->ixt_name); + } + + ret=ipsec_alg_delete(ixt); + if (ixt->ixt_state&IPSEC_ALG_ST_SUPP) { + ixt->ixt_state &= ~IPSEC_ALG_ST_SUPP; + pfkey_list_remove_supported((struct supported *)&ixt->ixt_support, &(pfkey_supported_list[SADB_SATYPE_ESP])); + /* send register event to userspace */ + pfkey_register_reply(SADB_SATYPE_ESP, NULL); + } + +out: + return ret; +} +/* + * Must be called from user context + * used at module load type for testing algo implementation + */ +static int ipsec_alg_test_encrypt(int enc_alg, int test) { + int ret; + caddr_t buf = NULL; + int iv_size, keysize, key_e_size; + struct ipsec_alg_enc *ixt_e; + void *tmp_key_e = NULL; + #define BUFSZ 1024 + #define MARGIN 0 + #define test_enc (buf+MARGIN) + #define test_dec (test_enc+BUFSZ+MARGIN) + #define test_tmp (test_dec+BUFSZ+MARGIN) + #define test_key_e (test_tmp+BUFSZ+MARGIN) + #define test_iv (test_key_e+key_e_size+MARGIN) + #define test_key (test_iv+iv_size+MARGIN) + #define test_size (BUFSZ*3+key_e_size+iv_size+keysize+MARGIN*7) + ixt_e=(struct ipsec_alg_enc *)ipsec_alg_get(IPSEC_ALG_TYPE_ENCRYPT, enc_alg); + if (ixt_e==NULL) { + KLIPS_PRINT(1, + "klips_debug: ipsec_alg_test_encrypt: " + "encalg=%d object not found\n", + enc_alg); + ret=-EINVAL; + goto out; + } + iv_size=ixt_e->ixt_ivlen / 8; + key_e_size=ixt_e->ixt_e_ctx_size; + keysize=ixt_e->ixt_e_keylen; + KLIPS_PRINT(1, + "klips_debug: ipsec_alg_test_encrypt: " + "enc_alg=%d blocksize=%d key_e_size=%d keysize=%d\n", + enc_alg, iv_size, key_e_size, keysize); + if ((buf=kmalloc (test_size, GFP_KERNEL)) == NULL) { + ret= -ENOMEM; + goto out; + } + get_random_bytes(test_key, keysize); + get_random_bytes(test_iv, iv_size); + if (ixt_e->ixt_e_new_key) { + tmp_key_e = ixt_e->ixt_e_new_key(ixt_e, test_key, keysize); + ret = tmp_key_e ? 0 : -EINVAL; + } else { + tmp_key_e = test_key_e; + ret = ixt_e->ixt_e_set_key(ixt_e, test_key_e, test_key, keysize); + } + if (ret < 0) + goto out; + get_random_bytes(test_enc, BUFSZ); + memcpy(test_tmp, test_enc, BUFSZ); + ret=ixt_e->ixt_e_cbc_encrypt(ixt_e, tmp_key_e, test_enc, BUFSZ, test_iv, 1); + printk(KERN_INFO + "klips_info: ipsec_alg_test_encrypt: " + "cbc_encrypt=1 ret=%d\n", + ret); + ret=memcmp(test_enc, test_tmp, BUFSZ); + printk(KERN_INFO + "klips_info: ipsec_alg_test_encrypt: " + "memcmp(enc, tmp) ret=%d: %s\n", ret, + ret!=0? "OK. (encr->DIFFers)" : "FAIL! (encr->SAME)" ); + memcpy(test_dec, test_enc, BUFSZ); + ret=ixt_e->ixt_e_cbc_encrypt(ixt_e, tmp_key_e, test_dec, BUFSZ, test_iv, 0); + printk(KERN_INFO + "klips_info: ipsec_alg_test_encrypt: " + "cbc_encrypt=0 ret=%d\n", ret); + ret=memcmp(test_dec, test_tmp, BUFSZ); + printk(KERN_INFO + "klips_info: ipsec_alg_test_encrypt: " + "memcmp(dec,tmp) ret=%d: %s\n", ret, + ret==0? "OK. (encr->decr->SAME)" : "FAIL! (encr->decr->DIFFers)" ); + { + /* Shamelessly taken from drivers/md sources O:) */ + unsigned long now; + int i, count, max=0; + int encrypt, speed; + for (encrypt=0; encrypt <2;encrypt ++) { + for (i = 0; i < 5; i++) { + now = jiffies; + count = 0; + while (jiffies == now) { + mb(); + ixt_e->ixt_e_cbc_encrypt(ixt_e, + tmp_key_e, test_tmp, + BUFSZ, test_iv, encrypt); + mb(); + count++; + mb(); + } + if (count > max) + max = count; + } + speed = max * (HZ * BUFSZ / 1024); + printk(KERN_INFO + "klips_info: ipsec_alg_test_encrypt: " + "%s %s speed=%d KB/s\n", + ixt_e->ixt_name, + encrypt? "encrypt": "decrypt", speed); + } + } +out: + if (tmp_key_e && ixt_e->ixt_e_destroy_key) ixt_e->ixt_e_destroy_key(ixt_e, tmp_key_e); + if (buf) kfree(buf); + if (ixt_e) ipsec_alg_put((struct ipsec_alg *)ixt_e); + return ret; + #undef test_enc + #undef test_dec + #undef test_tmp + #undef test_key_e + #undef test_iv + #undef test_key + #undef test_size +} +/* + * Must be called from user context + * used at module load type for testing algo implementation + */ +static int ipsec_alg_test_auth(int auth_alg, int test) { + int ret; + caddr_t buf = NULL; + int blocksize, keysize, key_a_size; + struct ipsec_alg_auth *ixt_a; + #define BUFSZ 1024 + #define MARGIN 0 + #define test_auth (buf+MARGIN) + #define test_key_a (test_auth+BUFSZ+MARGIN) + #define test_key (test_key_a+key_a_size+MARGIN) + #define test_hash (test_key+keysize+MARGIN) + #define test_size (BUFSZ+key_a_size+keysize+AHHMAC_HASHLEN+MARGIN*4) + ixt_a=(struct ipsec_alg_auth *)ipsec_alg_get(IPSEC_ALG_TYPE_AUTH, auth_alg); + if (ixt_a==NULL) { + KLIPS_PRINT(1, + "klips_debug: ipsec_alg_test_auth: " + "encalg=%d object not found\n", + auth_alg); + ret=-EINVAL; + goto out; + } + blocksize=ixt_a->ixt_blocksize; + key_a_size=ixt_a->ixt_a_ctx_size; + keysize=ixt_a->ixt_a_keylen; + KLIPS_PRINT(1, + "klips_debug: ipsec_alg_test_auth: " + "auth_alg=%d blocksize=%d key_a_size=%d keysize=%d\n", + auth_alg, blocksize, key_a_size, keysize); + if ((buf=kmalloc (test_size, GFP_KERNEL)) == NULL) { + ret= -ENOMEM; + goto out; + } + get_random_bytes(test_key, keysize); + ret = ixt_a->ixt_a_hmac_set_key(ixt_a, test_key_a, test_key, keysize); + if (ret < 0 ) + goto out; + get_random_bytes(test_auth, BUFSZ); + ret=ixt_a->ixt_a_hmac_hash(ixt_a, test_key_a, test_auth, BUFSZ, test_hash, AHHMAC_HASHLEN); + printk(KERN_INFO + "klips_info: ipsec_alg_test_auth: " + "ret=%d\n", ret); + { + /* Shamelessly taken from drivers/md sources O:) */ + unsigned long now; + int i, count, max=0; + int speed; + for (i = 0; i < 5; i++) { + now = jiffies; + count = 0; + while (jiffies == now) { + mb(); + ixt_a->ixt_a_hmac_hash(ixt_a, test_key_a, test_auth, BUFSZ, test_hash, AHHMAC_HASHLEN); + mb(); + count++; + mb(); + } + if (count > max) + max = count; + } + speed = max * (HZ * BUFSZ / 1024); + printk(KERN_INFO + "klips_info: ipsec_alg_test_auth: " + "%s hash speed=%d KB/s\n", + ixt_a->ixt_name, + speed); + } +out: + if (buf) kfree(buf); + if (ixt_a) ipsec_alg_put((struct ipsec_alg *)ixt_a); + return ret; + #undef test_auth + #undef test_key_a + #undef test_key + #undef test_hash + #undef test_size +} +int ipsec_alg_test(unsigned alg_type, unsigned alg_id, int test) { + switch(alg_type) { + case IPSEC_ALG_TYPE_ENCRYPT: + return ipsec_alg_test_encrypt(alg_id, test); + break; + case IPSEC_ALG_TYPE_AUTH: + return ipsec_alg_test_auth(alg_id, test); + break; + } + printk(KERN_ERR "klips_info: ipsec_alg_test() called incorrectly: " + "alg_type=%d alg_id=%d\n", + alg_type, alg_id); + return -EINVAL; +} +int ipsec_alg_init(void) { + KLIPS_PRINT(1, "klips_info:ipsec_alg_init: " + "KLIPS alg v=%d.%d.%d-%d (EALG_MAX=%d, AALG_MAX=%d)\n", + IPSEC_ALG_VERSION_QUAD(IPSEC_ALG_VERSION), + SADB_EALG_MAX, SADB_AALG_MAX); + /* Initialize tables */ + write_lock_bh(&ipsec_alg_lock); + ipsec_alg_hash_init(); + write_unlock_bh(&ipsec_alg_lock); + /* Initialize static algos */ + KLIPS_PRINT(1, "klips_info:ipsec_alg_init: " + "calling ipsec_alg_static_init()\n"); + ipsec_alg_static_init(); + return 0; +} + +/********************************************** + * + * INTERFACE for ipsec_sa init and wipe + * + **********************************************/ + +/* + * Called from pluto -> pfkey_v2_parser.c:pfkey_ipsec_sa_init() + */ +int ipsec_alg_sa_init(struct ipsec_sa *sa_p) { + struct ipsec_alg_enc *ixt_e; + struct ipsec_alg_auth *ixt_a; + + /* Only ESP for now ... */ + if (sa_p->ips_said.proto != IPPROTO_ESP) + return -EPROTONOSUPPORT; + KLIPS_PRINT(debug_pfkey, "klips_debug: ipsec_alg_sa_init() :" + "entering for encalg=%d, authalg=%d\n", + sa_p->ips_encalg, sa_p->ips_authalg); + if ((ixt_e=(struct ipsec_alg_enc *) + ipsec_alg_get(IPSEC_ALG_TYPE_ENCRYPT, sa_p->ips_encalg))) { + KLIPS_PRINT(debug_pfkey, + "klips_debug: ipsec_alg_sa_init() :" + "found ipsec_alg (ixt_e=%p) for encalg=%d\n", + ixt_e, sa_p->ips_encalg); + sa_p->ips_alg_enc=ixt_e; + } + if ((ixt_a=(struct ipsec_alg_auth *) + ipsec_alg_get(IPSEC_ALG_TYPE_AUTH, sa_p->ips_authalg))) { + KLIPS_PRINT(debug_pfkey, + "klips_debug: ipsec_alg_sa_init() :" + "found ipsec_alg (ixt_a=%p) for auth=%d\n", + ixt_a, sa_p->ips_authalg); + sa_p->ips_alg_auth=ixt_a; + } + return 0; +} + +/* + * Called from pluto -> ipsec_sa.c:ipsec_sa_delchain() + */ +int ipsec_alg_sa_wipe(struct ipsec_sa *sa_p) { + struct ipsec_alg *ixt; + if ((ixt=(struct ipsec_alg *)sa_p->ips_alg_enc)) { + KLIPS_PRINT(debug_pfkey, "klips_debug: ipsec_alg_sa_wipe() :" + "unlinking for encalg=%d\n", + ixt->ixt_alg_id); + ipsec_alg_put(ixt); + } + if ((ixt=(struct ipsec_alg *)sa_p->ips_alg_auth)) { + KLIPS_PRINT(debug_pfkey, "klips_debug: ipsec_alg_sa_wipe() :" + "unlinking for authalg=%d\n", + ixt->ixt_alg_id); + ipsec_alg_put(ixt); + } + return 0; +} + +IPSEC_PROCFS_DEBUG_NO_STATIC +int +ipsec_xform_get_info(char *buffer, + char **start, + off_t offset, + int length IPSEC_PROC_LAST_ARG) +{ + int len = 0; + off_t begin = 0; + int i; + struct list_head *head; + struct ipsec_alg *ixt; + + KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, + "klips_debug:ipsec_tncfg_get_info: " + "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n", + buffer, + *start, + (int)offset, + length); + + for(i = 0, head = ipsec_alg_hash_table; i< IPSEC_ALG_HASHSZ; i++, head++) + { + struct list_head *p; + for (p=head->next; p!=head; p=p->next) + { + ixt = list_entry(p, struct ipsec_alg, ixt_list); + len += ipsec_snprintf(buffer+len, length-len, + "VERSION=%d TYPE=%d ID=%d NAME=%s REFCNT=%d ", + ixt->ixt_version, ixt->ixt_alg_type, ixt->ixt_alg_id, + ixt->ixt_name, ixt->ixt_refcnt); + + len += ipsec_snprintf(buffer+len, length-len, + "STATE=%08x BLOCKSIZE=%d IVLEN=%d KEYMINBITS=%d KEYMAXBITS=%d ", + ixt->ixt_state, ixt->ixt_blocksize, + ixt->ixt_ivlen, ixt->ixt_keyminbits, ixt->ixt_keymaxbits); + + len += ipsec_snprintf(buffer+len, length-len, + "IVLEN=%d KEYMINBITS=%d KEYMAXBITS=%d ", + ixt->ixt_ivlen, ixt->ixt_keyminbits, ixt->ixt_keymaxbits); + + switch(ixt->ixt_alg_type) + { + case IPSEC_ALG_TYPE_AUTH: + { + struct ipsec_alg_auth *auth = (struct ipsec_alg_auth *)ixt; + + len += ipsec_snprintf(buffer+len, length-len, + "KEYLEN=%d CTXSIZE=%d AUTHLEN=%d ", + auth->ixt_a_keylen, auth->ixt_a_ctx_size, + auth->ixt_a_authlen); + break; + } + case IPSEC_ALG_TYPE_ENCRYPT: + { + struct ipsec_alg_enc *enc = (struct ipsec_alg_enc *)ixt; + len += ipsec_snprintf(buffer+len, length-len, + "KEYLEN=%d CTXSIZE=%d ", + enc->ixt_e_keylen, enc->ixt_e_ctx_size); + + break; + } + } + + len += ipsec_snprintf(buffer+len, length-len, "\n"); + } + } + + *start = buffer + (offset - begin); /* Start of wanted data */ + len -= (offset - begin); /* Start slop */ + if (len > length) + len = length; + return len; +} + +/* + * As the author of this module, I ONLY ALLOW using it from + * GPL (or same LICENSE TERMS as kernel source) modules. + * + * In respect to hardware crypto engines this means: + * * Closed-source device drivers ARE NOT ALLOWED to use + * this interface. + * * Closed-source VHDL/Verilog firmware running on + * the crypto hardware device IS ALLOWED to use this interface + * via a GPL (or same LICENSE TERMS as kernel source) device driver. + * --Juan Jose Ciarlante 20/03/2002 (thanks RGB for the correct wording) + */ + +/* + * These symbols can only be used from GPL modules + * for now, I'm disabling this because it creates false + * symbol problems for old modutils. + */ + +/* #ifndef EXPORT_SYMBOL_GPL */ +#undef EXPORT_SYMBOL_GPL +#define EXPORT_SYMBOL_GPL EXPORT_SYMBOL +/* #endif */ +EXPORT_SYMBOL_GPL(register_ipsec_alg); +EXPORT_SYMBOL_GPL(unregister_ipsec_alg); +EXPORT_SYMBOL_GPL(ipsec_alg_test); +#endif /* CONFIG_IPSEC_ALG */ diff --git a/linux/net/ipsec/ipsec_init.c b/linux/net/ipsec/ipsec_init.c new file mode 100644 index 000000000..56512acb6 --- /dev/null +++ b/linux/net/ipsec/ipsec_init.c @@ -0,0 +1,755 @@ +/* + * @(#) Initialization code. + * Copyright (C) 1996, 1997 John Ioannidis. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Richard Guy Briggs <rgb@freeswan.org> + * 2001 Michael Richardson <mcr@freeswan.org> + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + * + * /proc system code was split out into ipsec_proc.c after rev. 1.70. + * + */ + +char ipsec_init_c_version[] = "RCSID $Id: ipsec_init.c,v 1.3 2004/06/13 19:57:49 as Exp $"; + +#include <linux/config.h> +#include <linux/version.h> +#include <linux/module.h> +#include <linux/kernel.h> /* printk() */ + +#include "freeswan/ipsec_param.h" + +#ifdef MALLOC_SLAB +# include <linux/slab.h> /* kmalloc() */ +#else /* MALLOC_SLAB */ +# include <linux/malloc.h> /* kmalloc() */ +#endif /* MALLOC_SLAB */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/interrupt.h> /* mark_bh */ + +#include <linux/netdevice.h> /* struct device, and other headers */ +#include <linux/etherdevice.h> /* eth_type_trans */ +#include <linux/ip.h> /* struct iphdr */ +#include <linux/in.h> /* struct sockaddr_in */ +#include <linux/skbuff.h> +#include <linux/random.h> /* get_random_bytes() */ +#include <freeswan.h> + +#ifdef SPINLOCK +# ifdef SPINLOCK_23 +# include <linux/spinlock.h> /* *lock* */ +# else /* 23_SPINLOCK */ +# include <asm/spinlock.h> /* *lock* */ +# endif /* 23_SPINLOCK */ +#endif /* SPINLOCK */ + +#ifdef NET_21 +# include <asm/uaccess.h> +# include <linux/in6.h> +#endif /* NET_21 */ + +#include <asm/checksum.h> +#include <net/ip.h> + +#ifdef CONFIG_PROC_FS +# include <linux/proc_fs.h> +#endif /* CONFIG_PROC_FS */ + +#ifdef NETLINK_SOCK +# include <linux/netlink.h> +#else +# include <net/netlink.h> +#endif + +#include "freeswan/radij.h" + +#include "freeswan/ipsec_life.h" +#include "freeswan/ipsec_stats.h" +#include "freeswan/ipsec_sa.h" + +#include "freeswan/ipsec_encap.h" +#include "freeswan/ipsec_radij.h" +#include "freeswan/ipsec_xform.h" +#include "freeswan/ipsec_tunnel.h" + +#include "freeswan/ipsec_rcv.h" +#include "freeswan/ipsec_ah.h" +#include "freeswan/ipsec_esp.h" + +#ifdef CONFIG_IPSEC_IPCOMP +# include "freeswan/ipcomp.h" +#endif /* CONFIG_IPSEC_IPCOMP */ + +#include "freeswan/ipsec_proto.h" +#include "freeswan/ipsec_alg.h" + +#include <pfkeyv2.h> +#include <pfkey.h> + +#if !defined(CONFIG_IPSEC_ESP) && !defined(CONFIG_IPSEC_AH) +#error "kernel configuration must include ESP or AH" +#endif + +/* + * seems to be present in 2.4.10 (Linus), but also in some RH and other + * distro kernels of a lower number. + */ +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#ifdef CONFIG_IPSEC_DEBUG +int debug_eroute = 0; +int debug_spi = 0; +int debug_netlink = 0; +#endif /* CONFIG_IPSEC_DEBUG */ + +struct prng ipsec_prng; + +extern int ipsec_device_event(struct notifier_block *dnot, unsigned long event, void *ptr); +/* + * the following structure is required so that we receive + * event notifications when network devices are enabled and + * disabled (ifconfig up and down). + */ +static struct notifier_block ipsec_dev_notifier={ + ipsec_device_event, + NULL, + 0 +}; + +#ifdef CONFIG_SYSCTL +extern int ipsec_sysctl_register(void); +extern void ipsec_sysctl_unregister(void); +#endif + +static inline int +freeswan_inet_add_protocol(struct inet_protocol *prot, unsigned protocol) +{ +#ifdef NETDEV_25 + return inet_add_protocol(prot, protocol); +#else + inet_add_protocol(prot); + return 0; +#endif +} + +static inline int +freeswan_inet_del_protocol(struct inet_protocol *prot, unsigned protocol) +{ +#ifdef NETDEV_25 + return inet_del_protocol(prot, protocol); +#else + inet_del_protocol(prot); + return 0; +#endif +} + +/* void */ +int +ipsec_init(void) +{ + int error = 0; + unsigned char seed[256]; +#ifdef CONFIG_IPSEC_ENC_3DES + extern int des_check_key; + + /* turn off checking of keys */ + des_check_key=0; +#endif /* CONFIG_IPSEC_ENC_3DES */ + + KLIPS_PRINT(1, "klips_info:ipsec_init: " + "KLIPS startup, FreeS/WAN IPSec version: %s\n", + ipsec_version_code()); + + error |= ipsec_proc_init(); + +#ifdef SPINLOCK + ipsec_sadb.sadb_lock = SPIN_LOCK_UNLOCKED; +#else /* SPINLOCK */ + ipsec_sadb.sadb_lock = 0; +#endif /* SPINLOCK */ + +#ifndef SPINLOCK + tdb_lock.lock = 0; + eroute_lock.lock = 0; +#endif /* !SPINLOCK */ + + error |= ipsec_sadb_init(); + error |= ipsec_radijinit(); + + error |= pfkey_init(); + + error |= register_netdevice_notifier(&ipsec_dev_notifier); + +#ifdef CONFIG_IPSEC_ESP + freeswan_inet_add_protocol(&esp_protocol, IPPROTO_ESP); +#endif /* CONFIG_IPSEC_ESP */ + +#ifdef CONFIG_IPSEC_AH + freeswan_inet_add_protocol(&ah_protocol, IPPROTO_AH); +#endif /* CONFIG_IPSEC_AH */ + +/* we never actually link IPCOMP to the stack */ +#ifdef IPCOMP_USED_ALONE +#ifdef CONFIG_IPSEC_IPCOMP + freeswan_inet_add_protocol(&comp_protocol, IPPROTO_COMP); +#endif /* CONFIG_IPSEC_IPCOMP */ +#endif + + error |= ipsec_tunnel_init_devices(); + + +#ifdef CONFIG_SYSCTL + error |= ipsec_sysctl_register(); +#endif + +#ifdef CONFIG_IPSEC_ALG + ipsec_alg_init(); +#endif + + get_random_bytes((void *)seed, sizeof(seed)); + prng_init(&ipsec_prng, seed, sizeof(seed)); + + return error; +} + + +/* void */ +int +ipsec_cleanup(void) +{ + int error = 0; + +#ifdef CONFIG_SYSCTL + ipsec_sysctl_unregister(); +#endif + KLIPS_PRINT(debug_netlink, /* debug_tunnel & DB_TN_INIT, */ + "klips_debug:ipsec_cleanup: " + "calling ipsec_tunnel_cleanup_devices.\n"); + error |= ipsec_tunnel_cleanup_devices(); + + KLIPS_PRINT(debug_netlink, "called ipsec_tunnel_cleanup_devices"); + +/* we never actually link IPCOMP to the stack */ +#ifdef IPCOMP_USED_ALONE +#ifdef CONFIG_IPSEC_IPCOMP + if (freeswan_inet_del_protocol(&comp_protocol, IPPROTO_COMP) < 0) + printk(KERN_INFO "klips_debug:ipsec_cleanup: " + "comp close: can't remove protocol\n"); +#endif /* CONFIG_IPSEC_IPCOMP */ +#endif /* IPCOMP_USED_ALONE */ + +#ifdef CONFIG_IPSEC_AH + if (freeswan_inet_del_protocol(&ah_protocol, IPPROTO_AH) < 0) + printk(KERN_INFO "klips_debug:ipsec_cleanup: " + "ah close: can't remove protocol\n"); +#endif /* CONFIG_IPSEC_AH */ + +#ifdef CONFIG_IPSEC_ESP + if (freeswan_inet_del_protocol(&esp_protocol, IPPROTO_ESP) < 0) + printk(KERN_INFO "klips_debug:ipsec_cleanup: " + "esp close: can't remove protocol\n"); +#endif /* CONFIG_IPSEC_ESP */ + + error |= unregister_netdevice_notifier(&ipsec_dev_notifier); + + KLIPS_PRINT(debug_netlink, /* debug_tunnel & DB_TN_INIT, */ + "klips_debug:ipsec_cleanup: " + "calling ipsec_sadb_cleanup.\n"); + error |= ipsec_sadb_cleanup(0); + error |= ipsec_sadb_free(); + + KLIPS_PRINT(debug_netlink, /* debug_tunnel & DB_TN_INIT, */ + "klips_debug:ipsec_cleanup: " + "calling ipsec_radijcleanup.\n"); + error |= ipsec_radijcleanup(); + + KLIPS_PRINT(debug_pfkey, /* debug_tunnel & DB_TN_INIT, */ + "klips_debug:ipsec_cleanup: " + "calling pfkey_cleanup.\n"); + error |= pfkey_cleanup(); + + ipsec_proc_cleanup(); + + prng_final(&ipsec_prng); + + return error; +} + +#ifdef MODULE +int +init_module(void) +{ + int error = 0; + + error |= ipsec_init(); + + return error; +} + +int +cleanup_module(void) +{ + int error = 0; + + KLIPS_PRINT(debug_netlink, /* debug_tunnel & DB_TN_INIT, */ + "klips_debug:cleanup_module: " + "calling ipsec_cleanup.\n"); + + error |= ipsec_cleanup(); + + KLIPS_PRINT(1, "klips_info:cleanup_module: " + "ipsec module unloaded.\n"); + + return error; +} +#endif /* MODULE */ + +/* + * $Log: ipsec_init.c,v $ + * Revision 1.3 2004/06/13 19:57:49 as + * removed inclusion of ipsec_netlink.h + * + * Revision 1.2 2004/03/22 21:53:19 as + * merged alg-0.8.1 branch with HEAD + * + * Revision 1.1.4.1 2004/03/16 09:48:19 as + * alg-0.8.1rc12 patch merged + * + * Revision 1.1 2004/03/15 20:35:26 as + * added files from freeswan-2.04-x509-1.5.3 + * + * Revision 1.89 2003/07/31 22:47:16 mcr + * preliminary (untested by FS-team) 2.5 patches. + * + * Revision 1.88 2003/06/22 20:05:36 mcr + * clarified why IPCOMP was not being registered, and put a new + * #ifdef in rather than #if 0. + * + * Revision 1.87 2002/09/20 15:40:51 rgb + * Added a lock to the global ipsec_sadb struct for future use. + * Split ipsec_sadb_cleanup from new funciton ipsec_sadb_free to avoid problem + * of freeing newly created structures when clearing the reftable upon startup + * to start from a known state. + * + * Revision 1.86 2002/08/15 18:39:15 rgb + * Move ipsec_prng outside debug code. + * + * Revision 1.85 2002/05/14 02:35:29 rgb + * Change reference to tdb to ipsa. + * + * Revision 1.84 2002/04/24 07:55:32 mcr + * #include patches and Makefiles for post-reorg compilation. + * + * Revision 1.83 2002/04/24 07:36:28 mcr + * Moved from ./klips/net/ipsec/ipsec_init.c,v + * + * Revision 1.82 2002/04/20 00:12:25 rgb + * Added esp IV CBC attack fix, disabled. + * + * Revision 1.81 2002/04/09 16:13:32 mcr + * switch license to straight GPL. + * + * Revision 1.80 2002/03/24 07:34:08 rgb + * Sanity check for at least one of AH or ESP configured. + * + * Revision 1.79 2002/02/05 22:55:15 mcr + * added MODULE_LICENSE declaration. + * This macro does not appear in all kernel versions (see comment). + * + * Revision 1.78 2002/01/29 17:17:55 mcr + * moved include of ipsec_param.h to after include of linux/kernel.h + * otherwise, it seems that some option that is set in ipsec_param.h + * screws up something subtle in the include path to kernel.h, and + * it complains on the snprintf() prototype. + * + * Revision 1.77 2002/01/29 04:00:51 mcr + * more excise of kversions.h header. + * + * Revision 1.76 2002/01/29 02:13:17 mcr + * introduction of ipsec_kversion.h means that include of + * ipsec_param.h must preceed any decisions about what files to + * include to deal with differences in kernel source. + * + * Revision 1.75 2001/11/26 09:23:48 rgb + * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. + * + * Revision 1.74 2001/11/22 05:44:11 henry + * new version stuff + * + * Revision 1.71.2.2 2001/10/22 20:51:00 mcr + * explicitely set des_check_key. + * + * Revision 1.71.2.1 2001/09/25 02:19:39 mcr + * /proc manipulation code moved to new ipsec_proc.c + * + * Revision 1.73 2001/11/06 19:47:17 rgb + * Changed lifetime_packets to uint32 from uint64. + * + * Revision 1.72 2001/10/18 04:45:19 rgb + * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h, + * lib/freeswan.h version macros moved to lib/kversions.h. + * Other compiler directive cleanups. + * + * Revision 1.71 2001/09/20 15:32:45 rgb + * Minor pfkey lifetime fixes. + * + * Revision 1.70 2001/07/06 19:51:21 rgb + * Added inbound policy checking code for IPIP SAs. + * + * Revision 1.69 2001/06/14 19:33:26 rgb + * Silence startup message for console, but allow it to be logged. + * Update copyright date. + * + * Revision 1.68 2001/05/29 05:14:36 rgb + * Added PMTU to /proc/net/ipsec_tncfg output. See 'man 5 ipsec_tncfg'. + * + * Revision 1.67 2001/05/04 16:34:52 rgb + * Rremove erroneous checking of return codes for proc_net_* in 2.4. + * + * Revision 1.66 2001/05/03 19:40:34 rgb + * Check error return codes in startup and shutdown. + * + * Revision 1.65 2001/02/28 05:03:27 rgb + * Clean up and rationalise startup messages. + * + * Revision 1.64 2001/02/27 22:24:53 rgb + * Re-formatting debug output (line-splitting, joining, 1arg/line). + * Check for satoa() return codes. + * + * Revision 1.63 2000/11/29 20:14:06 rgb + * Add src= to the output of /proc/net/ipsec_spi and delete dst from IPIP. + * + * Revision 1.62 2000/11/06 04:31:24 rgb + * Ditched spin_lock_irqsave in favour of spin_lock_bh. + * Fixed longlong for pre-2.4 kernels (Svenning). + * Add Svenning's adaptive content compression. + * Disabled registration of ipcomp handler. + * + * Revision 1.61 2000/10/11 13:37:54 rgb + * #ifdef out debug print that causes proc/net/ipsec_version to oops. + * + * Revision 1.60 2000/09/20 03:59:01 rgb + * Change static info functions to DEBUG_NO_STATIC to reveal function names + * in oopsen. + * + * Revision 1.59 2000/09/16 01:06:26 rgb + * Added cast of var to silence compiler warning about long fed to int + * format. + * + * Revision 1.58 2000/09/15 11:37:01 rgb + * Merge in heavily modified Svenning Soerensen's <svenning@post5.tele.dk> + * IPCOMP zlib deflate code. + * + * Revision 1.57 2000/09/12 03:21:50 rgb + * Moved radij_c_version printing to ipsec_version_get_info(). + * Reformatted ipsec_version_get_info(). + * Added sysctl_{,un}register() calls. + * + * Revision 1.56 2000/09/08 19:16:50 rgb + * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. + * Removed all references to CONFIG_IPSEC_PFKEYv2. + * + * Revision 1.55 2000/08/30 05:19:03 rgb + * Cleaned up no longer used spi_next, netlink register/unregister, other + * minor cleanup. + * Removed cruft replaced by TDB_XFORM_NAME. + * Removed all the rest of the references to tdb_spi, tdb_proto, tdb_dst. + * Moved debug version strings to printk when /proc/net/ipsec_version is + * called. + * + * Revision 1.54 2000/08/20 18:31:05 rgb + * Changed cosmetic alignment in spi_info. + * Changed addtime and usetime to use actual value which is relative + * anyways, as intended. (Momchil) + * + * Revision 1.53 2000/08/18 17:37:03 rgb + * Added an (int) cast to shut up the compiler... + * + * Revision 1.52 2000/08/01 14:51:50 rgb + * Removed _all_ remaining traces of DES. + * + * Revision 1.51 2000/07/25 20:41:22 rgb + * Removed duplicate parameter in spi_getinfo. + * + * Revision 1.50 2000/07/17 03:21:45 rgb + * Removed /proc/net/ipsec_spinew. + * + * Revision 1.49 2000/06/28 05:46:51 rgb + * Renamed ivlen to iv_bits for consistency. + * Changed output of add and use times to be relative to now. + * + * Revision 1.48 2000/05/11 18:26:10 rgb + * Commented out calls to netlink_attach/detach to avoid activating netlink + * in the kenrel config. + * + * Revision 1.47 2000/05/10 22:35:26 rgb + * Comment out most of the startup version information. + * + * Revision 1.46 2000/03/22 16:15:36 rgb + * Fixed renaming of dev_get (MB). + * + * Revision 1.45 2000/03/16 06:40:48 rgb + * Hardcode PF_KEYv2 support. + * + * Revision 1.44 2000/01/22 23:19:20 rgb + * Simplified code to use existing macro TDB_XFORM_NAME(). + * + * Revision 1.43 2000/01/21 06:14:04 rgb + * Print individual stats only if non-zero. + * Removed 'bits' from each keylength for brevity. + * Shortened lifetimes legend for brevity. + * Changed wording from 'last_used' to the clearer 'idle'. + * + * Revision 1.42 1999/12/31 14:57:19 rgb + * MB fix for new dummy-less proc_get_info in 2.3.35. + * + * Revision 1.41 1999/11/23 23:04:03 rgb + * Use provided macro ADDRTOA_BUF instead of hardcoded value. + * Sort out pfkey and freeswan headers, putting them in a library path. + * + * Revision 1.40 1999/11/18 18:47:01 rgb + * Added dynamic proc registration for 2.3.25+. + * Changed all device registrations for static linking to + * dynamic to reduce the number and size of patches. + * Changed all protocol registrations for static linking to + * dynamic to reduce the number and size of patches. + * + * Revision 1.39 1999/11/18 04:12:07 rgb + * Replaced all kernel version macros to shorter, readable form. + * Added Marc Boucher's 2.3.25 proc patches. + * Converted all PROC_FS entries to dynamic to reduce kernel patching. + * Added CONFIG_PROC_FS compiler directives in case it is shut off. + * + * Revision 1.38 1999/11/17 15:53:38 rgb + * Changed all occurrences of #include "../../../lib/freeswan.h" + * to #include <freeswan.h> which works due to -Ilibfreeswan in the + * klips/net/ipsec/Makefile. + * + * Revision 1.37 1999/10/16 04:23:06 rgb + * Add stats for replaywin_errs, replaywin_max_sequence_difference, + * authentication errors, encryption size errors, encryption padding + * errors, and time since last packet. + * + * Revision 1.36 1999/10/16 00:30:47 rgb + * Added SA lifetime counting. + * + * Revision 1.35 1999/10/15 22:14:00 rgb + * Clean out cruft. + * + * Revision 1.34 1999/10/03 18:46:28 rgb + * Spinlock fixes for 2.0.xx and 2.3.xx. + * + * Revision 1.33 1999/10/01 17:08:10 rgb + * Disable spinlock init. + * + * Revision 1.32 1999/10/01 16:22:24 rgb + * Switch from assignment init. to functional init. of spinlocks. + * + * Revision 1.31 1999/10/01 15:44:52 rgb + * Move spinlock header include to 2.1> scope. + * + * Revision 1.30 1999/10/01 00:00:16 rgb + * Added eroute structure locking. + * Added tdb structure locking. + * Minor formatting changes. + * Add call to initialize tdb hash table. + * + * Revision 1.29 1999/09/23 20:22:40 rgb + * Enable, tidy and fix network notifier code. + * + * Revision 1.28 1999/09/18 11:39:56 rgb + * Start to add (disabled) netdevice notifier code. + * + * Revision 1.27 1999/08/28 08:24:47 rgb + * Add compiler directives to compile cleanly without debugging. + * + * Revision 1.26 1999/08/06 16:03:22 rgb + * Correct error messages on failure to unload /proc entries. + * + * Revision 1.25 1999/08/03 17:07:25 rgb + * Report device MTU, not private MTU. + * + * Revision 1.24 1999/05/25 22:24:37 rgb + * /PROC/NET/ipsec* init problem fix. + * + * Revision 1.23 1999/05/25 02:16:38 rgb + * Make modular proc_fs entries dynamic and fix for 2.2.x. + * + * Revision 1.22 1999/05/09 03:25:35 rgb + * Fix bug introduced by 2.2 quick-and-dirty patch. + * + * Revision 1.21 1999/05/05 22:02:30 rgb + * Add a quick and dirty port to 2.2 kernels by Marc Boucher <marc@mbsi.ca>. + * + * Revision 1.20 1999/04/29 15:15:50 rgb + * Fix undetected iv_len reporting bug. + * Add sanity checking for null pointer to private data space. + * Add return values to init and cleanup functions. + * + * Revision 1.19 1999/04/27 19:24:44 rgb + * Added /proc/net/ipsec_klipsdebug support for reading the current debug + * settings. + * Instrument module load/init/unload. + * + * Revision 1.18 1999/04/15 15:37:24 rgb + * Forward check changes from POST1_00 branch. + * + * Revision 1.15.2.3 1999/04/13 20:29:19 rgb + * /proc/net/ipsec_* cleanup. + * + * Revision 1.15.2.2 1999/04/02 04:28:23 rgb + * /proc/net/ipsec_* formatting enhancements. + * + * Revision 1.15.2.1 1999/03/30 17:08:33 rgb + * Add pfkey initialisation. + * + * Revision 1.17 1999/04/11 00:28:57 henry + * GPL boilerplate + * + * Revision 1.16 1999/04/06 04:54:25 rgb + * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes + * patch shell fixes. + * + * Revision 1.15 1999/02/24 20:15:07 rgb + * Update output format. + * + * Revision 1.14 1999/02/17 16:49:39 rgb + * Convert DEBUG_IPSEC to KLIPS_PRINT + * Ditch NET_IPIP dependancy. + * + * Revision 1.13 1999/01/26 02:06:37 rgb + * Remove ah/esp switching on include files. + * Removed CONFIG_IPSEC_ALGO_SWITCH macro. + * Removed dead code. + * Remove references to INET_GET_PROTOCOL. + * + * Revision 1.12 1999/01/22 06:19:18 rgb + * Cruft clean-out. + * 64-bit clean-up. + * Added algorithm switch code. + * + * Revision 1.11 1998/12/01 05:54:53 rgb + * Cleanup and order debug version output. + * + * Revision 1.10 1998/11/30 13:22:54 rgb + * Rationalised all the klips kernel file headers. They are much shorter + * now and won't conflict under RH5.2. + * + * Revision 1.9 1998/11/10 05:35:13 rgb + * Print direction in/out flag from /proc/net/ipsec_spi. + * + * Revision 1.8 1998/10/27 13:48:10 rgb + * Cleaned up /proc/net/ipsec_* filesystem for easy parsing by scripts. + * Fixed less(1) truncated output bug. + * Code clean-up. + * + * Revision 1.7 1998/10/22 06:43:16 rgb + * Convert to use satoa for printk. + * + * Revision 1.6 1998/10/19 14:24:35 rgb + * Added inclusion of freeswan.h. + * + * Revision 1.5 1998/10/09 04:43:35 rgb + * Added 'klips_debug' prefix to all klips printk debug statements. + * + * Revision 1.4 1998/07/27 21:50:22 rgb + * Not necessary to traverse mask tree for /proc/net/ipsec_eroute. + * + * Revision 1.3 1998/06/25 19:51:20 rgb + * Clean up #endif comments. + * Shift debugging comment control for procfs to debug_tunnel. + * Make proc_dir_entries visible to rest of kernel for static link. + * Replace hardwired fileperms with macros. + * Use macros for procfs inode numbers. + * Rearrange initialisations between ipsec_init and module_init as appropriate + * for static loading. + * + * Revision 1.2 1998/06/23 02:55:43 rgb + * Slightly quieted init-time messages. + * Re-introduced inet_add_protocol after it mysteriously disappeared... + * Check for and warn of absence of IPIP protocol on install of module. + * Move tdbcleanup to ipsec_xform.c. + * + * Revision 1.10 1998/06/18 21:29:04 henry + * move sources from klips/src to klips/net/ipsec, to keep stupid kernel + * build scripts happier in presence of symbolic links + * + * Revision 1.9 1998/06/14 23:49:40 rgb + * Clarify version reporting on module loading. + * + * Revision 1.8 1998/06/11 05:54:23 rgb + * Added /proc/net/ipsec_version to report freeswan and transform versions. + * Added /proc/net/ipsec_spinew to generate new and unique spi's.. + * Fixed /proc/net/ipsec_tncfg bug. + * + * Revision 1.7 1998/05/25 20:23:13 rgb + * proc_register changed to dynamic registration to avoid arbitrary inode + * numbers. + * + * Implement memory recovery from tdb and eroute tables. + * + * Revision 1.6 1998/05/21 13:08:58 rgb + * Rewrote procinfo subroutines to avoid *bad things* when more that 3k of + * information is available for printout. + * + * Revision 1.5 1998/05/18 21:29:48 rgb + * Cleaned up /proc/net/ipsec_* output, including a title line, algorithm + * names instead of numbers, standard format for numerical output base, + * whitespace for legibility, and the names themselves for consistency. + * + * Added /proc/net/ipsec_spigrp and /proc/net/ipsec_tncfg. + * + * Revision 1.4 1998/04/30 15:42:24 rgb + * Silencing attach for normal operations with #ifdef IPSEC_DEBUG. + * + * Revision 1.3 1998/04/21 21:28:58 rgb + * Rearrange debug switches to change on the fly debug output from user + * space. Only kernel changes checked in at this time. radij.c was also + * changed to temporarily remove buggy debugging code in rj_delete causing + * an OOPS and hence, netlink device open errors. + * + * Revision 1.2 1998/04/12 22:03:22 rgb + * Updated ESP-3DES-HMAC-MD5-96, + * ESP-DES-HMAC-MD5-96, + * AH-HMAC-MD5-96, + * AH-HMAC-SHA1-96 since Henry started freeswan cvs repository + * from old standards (RFC182[5-9] to new (as of March 1998) drafts. + * + * Fixed eroute references in /proc/net/ipsec*. + * + * Started to patch module unloading memory leaks in ipsec_netlink and + * radij tree unloading. + * + * Revision 1.1 1998/04/09 03:06:05 henry + * sources moved up from linux/net/ipsec + * + * Revision 1.1.1.1 1998/04/08 05:35:02 henry + * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 + * + * Revision 0.4 1997/01/15 01:28:15 ji + * No changes. + * + * Revision 0.3 1996/11/20 14:39:04 ji + * Fixed problem with node names of /proc/net entries. + * Other minor cleanups. + * Rationalized debugging code. + * + * Revision 0.2 1996/11/02 00:18:33 ji + * First limited release. + * + * Local variables: + * c-file-style: "linux" + * End: + * + */ diff --git a/linux/net/ipsec/ipsec_life.c b/linux/net/ipsec/ipsec_life.c new file mode 100644 index 000000000..384866c06 --- /dev/null +++ b/linux/net/ipsec/ipsec_life.c @@ -0,0 +1,210 @@ +/* + * @(#) lifetime structure utilities + * + * Copyright (C) 2001 Richard Guy Briggs <rgb@freeswan.org> + * and Michael Richardson <mcr@freeswan.org> + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + * + * RCSID $Id: ipsec_life.c,v 1.3 2004/04/28 08:06:22 as Exp $ + * + */ + +/* + * This provides series of utility functions for dealing with lifetime + * structures. + * + * ipsec_check_lifetime - returns -1 hard lifetime exceeded + * 0 soft lifetime exceeded + * 1 everything is okay + * based upon whether or not the count exceeds hard/soft + * + */ + +#define __NO_VERSION__ +#include <linux/module.h> +#include <linux/config.h> /* for CONFIG_IP_FORWARD */ +#include <linux/version.h> +#include <linux/kernel.h> /* printk() */ + +#include "freeswan/ipsec_param.h" + +#include <linux/netdevice.h> /* struct device, struct net_device_stats and other headers */ +#include <linux/etherdevice.h> /* eth_type_trans */ +#include <linux/skbuff.h> +#include <freeswan.h> + +#include "freeswan/radij.h" +#include "freeswan/ipsec_life.h" +#include "freeswan/ipsec_xform.h" +#include "freeswan/ipsec_eroute.h" +#include "freeswan/ipsec_encap.h" +#include "freeswan/ipsec_radij.h" + +#include "freeswan/ipsec_sa.h" +#include "freeswan/ipsec_tunnel.h" +#include "freeswan/ipsec_ipe4.h" +#include "freeswan/ipsec_ah.h" +#include "freeswan/ipsec_esp.h" + +#ifdef CONFIG_IPSEC_IPCOMP +#include "freeswan/ipcomp.h" +#endif /* CONFIG_IPSEC_IPCOMP */ + +#include <pfkeyv2.h> +#include <pfkey.h> + +#include "freeswan/ipsec_proto.h" + + +enum ipsec_life_alive +ipsec_lifetime_check(struct ipsec_lifetime64 *il64, + const char *lifename, + const char *saname, + enum ipsec_life_type ilt, + enum ipsec_direction idir, + struct ipsec_sa *ips) +{ + __u64 count; + const char *dir; + + if(saname == NULL) { + saname = "unknown-SA"; + } + + if(idir == ipsec_incoming) { + dir = "incoming"; + } else { + dir = "outgoing"; + } + + + if(ilt == ipsec_life_timebased) { + count = jiffies/HZ - il64->ipl_count; + } else { + count = il64->ipl_count; + } + + if(il64->ipl_hard && + (count > il64->ipl_hard)) { + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_lifetime_check: " + "hard %s lifetime of SA:<%s%s%s> %s has been reached, SA expired, " + "%s packet dropped.\n", + lifename, + IPS_XFORM_NAME(ips), + saname, + dir); + + pfkey_expire(ips, 1); + return ipsec_life_harddied; + } + + if(il64->ipl_soft && + (count > il64->ipl_soft)) { + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_lifetime_check: " + "soft %s lifetime of SA:<%s%s%s> %s has been reached, SA expiring, " + "soft expire message sent up, %s packet still processed.\n", + lifename, + IPS_XFORM_NAME(ips), + saname, + dir); + + if(ips->ips_state != SADB_SASTATE_DYING) { + pfkey_expire(ips, 0); + } + ips->ips_state = SADB_SASTATE_DYING; + + return ipsec_life_softdied; + } + return ipsec_life_okay; +} + + +/* + * This function takes a buffer (with length), a lifetime name and type, + * and formats a string to represent the current values of the lifetime. + * + * It returns the number of bytes that the format took (or would take, + * if the buffer were large enough: snprintf semantics). + * This is used in /proc routines and in debug output. + */ +int +ipsec_lifetime_format(char *buffer, + int buflen, + char *lifename, + enum ipsec_life_type timebaselife, + struct ipsec_lifetime64 *lifetime) +{ + int len = 0; + __u64 count; + + if(timebaselife == ipsec_life_timebased) { + count = jiffies/HZ - lifetime->ipl_count; + } else { + count = lifetime->ipl_count; + } + + if(lifetime->ipl_count > 1 || + lifetime->ipl_soft || + lifetime->ipl_hard) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) + len = ipsec_snprintf(buffer, buflen, + "%s(%Lu,%Lu,%Lu)", + lifename, + count, + lifetime->ipl_soft, + lifetime->ipl_hard); +#else /* XXX high 32 bits are not displayed */ + len = ipsec_snprintf(buffer, buflen, + "%s(%lu,%lu,%lu)", + lifename, + (unsigned long)count, + (unsigned long)lifetime->ipl_soft, + (unsigned long)lifetime->ipl_hard); +#endif + } + + return len; +} + +void +ipsec_lifetime_update_hard(struct ipsec_lifetime64 *lifetime, + __u64 newvalue) +{ + if(newvalue && + (!lifetime->ipl_hard || + (newvalue < lifetime->ipl_hard))) { + lifetime->ipl_hard = newvalue; + + if(!lifetime->ipl_soft && + (lifetime->ipl_hard < lifetime->ipl_soft)) { + lifetime->ipl_soft = lifetime->ipl_hard; + } + } +} + +void +ipsec_lifetime_update_soft(struct ipsec_lifetime64 *lifetime, + __u64 newvalue) +{ + if(newvalue && + (!lifetime->ipl_soft || + (newvalue < lifetime->ipl_soft))) { + lifetime->ipl_soft = newvalue; + + if(lifetime->ipl_hard && + (lifetime->ipl_hard < lifetime->ipl_soft)) { + lifetime->ipl_soft = lifetime->ipl_hard; + } + } +} diff --git a/linux/net/ipsec/ipsec_mast.c b/linux/net/ipsec/ipsec_mast.c new file mode 100644 index 000000000..f5216b541 --- /dev/null +++ b/linux/net/ipsec/ipsec_mast.c @@ -0,0 +1,1064 @@ +/* + * IPSEC MAST code. + * Copyright (C) 1996, 1997 John Ioannidis. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Richard Guy Briggs. + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + */ + +char ipsec_mast_c_version[] = "RCSID $Id: ipsec_mast.c,v 1.2 2004/06/13 19:57:49 as Exp $"; + +#define __NO_VERSION__ +#include <linux/module.h> +#include <linux/config.h> /* for CONFIG_IP_FORWARD */ +#include <linux/version.h> +#include <linux/kernel.h> /* printk() */ + +#include "freeswan/ipsec_param.h" + +#ifdef MALLOC_SLAB +# include <linux/slab.h> /* kmalloc() */ +#else /* MALLOC_SLAB */ +# include <linux/malloc.h> /* kmalloc() */ +#endif /* MALLOC_SLAB */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/interrupt.h> /* mark_bh */ + +#include <linux/netdevice.h> /* struct device, struct net_device_stats, dev_queue_xmit() and other headers */ +#include <linux/etherdevice.h> /* eth_type_trans */ +#include <linux/ip.h> /* struct iphdr */ +#include <linux/tcp.h> /* struct tcphdr */ +#include <linux/udp.h> /* struct udphdr */ +#include <linux/skbuff.h> +#include <freeswan.h> +#include <asm/uaccess.h> +#include <linux/in6.h> +#include <net/dst.h> +#undef dev_kfree_skb +#define dev_kfree_skb(a,b) kfree_skb(a) +#define PHYSDEV_TYPE +#include <asm/checksum.h> +#include <net/icmp.h> /* icmp_send() */ +#include <net/ip.h> +#include <linux/netfilter_ipv4.h> + +#include <linux/if_arp.h> + +#include "freeswan/radij.h" +#include "freeswan/ipsec_life.h" +#include "freeswan/ipsec_xform.h" +#include "freeswan/ipsec_eroute.h" +#include "freeswan/ipsec_encap.h" +#include "freeswan/ipsec_radij.h" +#include "freeswan/ipsec_sa.h" +#include "freeswan/ipsec_tunnel.h" +#include "freeswan/ipsec_mast.h" +#include "freeswan/ipsec_ipe4.h" +#include "freeswan/ipsec_ah.h" +#include "freeswan/ipsec_esp.h" + +#include <pfkeyv2.h> +#include <pfkey.h> + +#include "freeswan/ipsec_proto.h" + +int ipsec_maxdevice_count = -1; + +DEBUG_NO_STATIC int +ipsec_mast_open(struct device *dev) +{ + struct ipsecpriv *prv = dev->priv; + + /* + * Can't open until attached. + */ + + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_open: " + "dev = %s, prv->dev = %s\n", + dev->name, prv->dev?prv->dev->name:"NONE"); + + if (prv->dev == NULL) + return -ENODEV; + + MOD_INC_USE_COUNT; + return 0; +} + +DEBUG_NO_STATIC int +ipsec_mast_close(struct device *dev) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +static inline int ipsec_mast_xmit2(struct sk_buff *skb) +{ + return ip_send(skb); +} + +enum ipsec_xmit_value +ipsec_mast_send(struct ipsec_xmit_state*ixs) +{ + /* new route/dst cache code from James Morris */ + ixs->skb->dev = ixs->physdev; + /*skb_orphan(ixs->skb);*/ + if((ixs->error = ip_route_output(&ixs->route, + ixs->skb->nh.iph->daddr, + ixs->pass ? 0 : ixs->skb->nh.iph->saddr, + RT_TOS(ixs->skb->nh.iph->tos), + ixs->physdev->iflink /* rgb: should this be 0? */))) { + ixs->stats->tx_errors++; + KLIPS_PRINT(debug_mast & DB_MAST_XMIT, + "klips_debug:ipsec_xmit_send: " + "ip_route_output failed with error code %d, rt->u.dst.dev=%s, dropped\n", + ixs->error, + ixs->route->u.dst.dev->name); + return IPSEC_XMIT_ROUTEERR; + } + if(ixs->dev == ixs->route->u.dst.dev) { + ip_rt_put(ixs->route); + /* This is recursion, drop it. */ + ixs->stats->tx_errors++; + KLIPS_PRINT(debug_mast & DB_MAST_XMIT, + "klips_debug:ipsec_xmit_send: " + "suspect recursion, dev=rt->u.dst.dev=%s, dropped\n", + ixs->dev->name); + return IPSEC_XMIT_RECURSDETECT; + } + dst_release(ixs->skb->dst); + ixs->skb->dst = &ixs->route->u.dst; + ixs->stats->tx_bytes += ixs->skb->len; + if(ixs->skb->len < ixs->skb->nh.raw - ixs->skb->data) { + ixs->stats->tx_errors++; + printk(KERN_WARNING + "klips_error:ipsec_xmit_send: " + "tried to __skb_pull nh-data=%ld, %d available. This should never happen, please report.\n", + (unsigned long)(ixs->skb->nh.raw - ixs->skb->data), + ixs->skb->len); + return IPSEC_XMIT_PUSHPULLERR; + } + __skb_pull(ixs->skb, ixs->skb->nh.raw - ixs->skb->data); +#ifdef SKB_RESET_NFCT + nf_conntrack_put(ixs->skb->nfct); + ixs->skb->nfct = NULL; +#ifdef CONFIG_NETFILTER_DEBUG + ixs->skb->nf_debug = 0; +#endif /* CONFIG_NETFILTER_DEBUG */ +#endif /* SKB_RESET_NFCT */ + KLIPS_PRINT(debug_mast & DB_MAST_XMIT, + "klips_debug:ipsec_xmit_send: " + "...done, calling ip_send() on device:%s\n", + ixs->skb->dev ? ixs->skb->dev->name : "NULL"); + KLIPS_IP_PRINT(debug_mast & DB_MAST_XMIT, ixs->skb->nh.iph); + { + int err; + + err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, ixs->skb, NULL, ixs->route->u.dst.dev, + ipsec_mast_xmit2); + if(err != NET_XMIT_SUCCESS && err != NET_XMIT_CN) { + if(net_ratelimit()) + printk(KERN_ERR + "klips_error:ipsec_xmit_send: " + "ip_send() failed, err=%d\n", + -err); + ixs->stats->tx_errors++; + ixs->stats->tx_aborted_errors++; + ixs->skb = NULL; + return IPSEC_XMIT_IPSENDFAILURE; + } + } + ixs->stats->tx_packets++; + + ixs->skb = NULL; + + return IPSEC_XMIT_OK; +} + +void +ipsec_mast_cleanup(struct ipsec_xmit_state*ixs) +{ +#if defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE) + netif_wake_queue(ixs->dev); +#else /* defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE) */ + ixs->dev->tbusy = 0; +#endif /* defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE) */ + if(ixs->saved_header) { + kfree(ixs->saved_header); + } + if(ixs->skb) { + dev_kfree_skb(ixs->skb, FREE_WRITE); + } + if(ixs->oskb) { + dev_kfree_skb(ixs->oskb, FREE_WRITE); + } + if (ixs->ips.ips_ident_s.data) { + kfree(ixs->ips.ips_ident_s.data); + } + if (ixs->ips.ips_ident_d.data) { + kfree(ixs->ips.ips_ident_d.data); + } +} + +#if 0 +/* + * This function assumes it is being called from dev_queue_xmit() + * and that skb is filled properly by that function. + */ +int +ipsec_mast_start_xmit(struct sk_buff *skb, struct device *dev, IPsecSAref_t SAref) +{ + struct ipsec_xmit_state ixs_mem; + struct ipsec_xmit_state *ixs = &ixs_mem; + enum ipsec_xmit_value stat = IPSEC_XMIT_OK; + + /* dev could be a mast device, but should be optional, I think... */ + /* SAref is also optional, but one of the two must be present. */ + /* I wonder if it could accept no device or saref and guess? */ + +/* ipsec_xmit_sanity_check_dev(ixs); */ + + ipsec_xmit_sanity_check_skb(ixs); + + ipsec_xmit_adjust_hard_header(ixs); + + stat = ipsec_xmit_encap_bundle(ixs); + if(stat != IPSEC_XMIT_OK) { + /* SA processing failed */ + } + + ipsec_xmit_hard_header_restore(); +} +#endif + +DEBUG_NO_STATIC struct net_device_stats * +ipsec_mast_get_stats(struct device *dev) +{ + return &(((struct ipsecpriv *)(dev->priv))->mystats); +} + +/* + * Revectored calls. + * For each of these calls, a field exists in our private structure. + */ + +DEBUG_NO_STATIC int +ipsec_mast_hard_header(struct sk_buff *skb, struct device *dev, + unsigned short type, void *daddr, void *saddr, unsigned len) +{ + struct ipsecpriv *prv = dev->priv; + struct device *tmp; + int ret; + struct net_device_stats *stats; /* This device's statistics */ + + if(skb == NULL) { + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_hard_header: " + "no skb...\n"); + return -ENODATA; + } + + if(dev == NULL) { + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_hard_header: " + "no device...\n"); + return -ENODEV; + } + + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_hard_header: " + "skb->dev=%s dev=%s.\n", + skb->dev ? skb->dev->name : "NULL", + dev->name); + + if(prv == NULL) { + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_hard_header: " + "no private space associated with dev=%s\n", + dev->name ? dev->name : "NULL"); + return -ENODEV; + } + + stats = (struct net_device_stats *) &(prv->mystats); + + if(prv->dev == NULL) { + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_hard_header: " + "no physical device associated with dev=%s\n", + dev->name ? dev->name : "NULL"); + stats->tx_dropped++; + return -ENODEV; + } + + /* check if we have to send a IPv6 packet. It might be a Router + Solicitation, where the building of the packet happens in + reverse order: + 1. ll hdr, + 2. IPv6 hdr, + 3. ICMPv6 hdr + -> skb->nh.raw is still uninitialized when this function is + called!! If this is no IPv6 packet, we can print debugging + messages, otherwise we skip all debugging messages and just + build the ll header */ + if(type != ETH_P_IPV6) { + /* execute this only, if we don't have to build the + header for a IPv6 packet */ + if(!prv->hard_header) { + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_hard_header: " + "physical device has been detached, packet dropped 0p%p->0p%p len=%d type=%d dev=%s->NULL ", + saddr, + daddr, + len, + type, + dev->name); + KLIPS_PRINTMORE(debug_mast & DB_MAST_REVEC, + "ip=%08x->%08x\n", + (__u32)ntohl(skb->nh.iph->saddr), + (__u32)ntohl(skb->nh.iph->daddr) ); + stats->tx_dropped++; + return -ENODEV; + } + +#define da ((struct device *)(prv->dev))->dev_addr + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_hard_header: " + "Revectored 0p%p->0p%p len=%d type=%d dev=%s->%s dev_addr=%02x:%02x:%02x:%02x:%02x:%02x ", + saddr, + daddr, + len, + type, + dev->name, + prv->dev->name, + da[0], da[1], da[2], da[3], da[4], da[5]); + KLIPS_PRINTMORE(debug_mast & DB_MAST_REVEC, + "ip=%08x->%08x\n", + (__u32)ntohl(skb->nh.iph->saddr), + (__u32)ntohl(skb->nh.iph->daddr) ); + } else { + KLIPS_PRINT(debug_mast, + "klips_debug:ipsec_mast_hard_header: " + "is IPv6 packet, skip debugging messages, only revector and build linklocal header.\n"); + } + tmp = skb->dev; + skb->dev = prv->dev; + ret = prv->hard_header(skb, prv->dev, type, (void *)daddr, (void *)saddr, len); + skb->dev = tmp; + return ret; +} + +DEBUG_NO_STATIC int +ipsec_mast_rebuild_header(struct sk_buff *skb) +{ + struct ipsecpriv *prv = skb->dev->priv; + struct device *tmp; + int ret; + struct net_device_stats *stats; /* This device's statistics */ + + if(skb->dev == NULL) { + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_rebuild_header: " + "no device..."); + return -ENODEV; + } + + if(prv == NULL) { + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_rebuild_header: " + "no private space associated with dev=%s", + skb->dev->name ? skb->dev->name : "NULL"); + return -ENODEV; + } + + stats = (struct net_device_stats *) &(prv->mystats); + + if(prv->dev == NULL) { + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_rebuild_header: " + "no physical device associated with dev=%s", + skb->dev->name ? skb->dev->name : "NULL"); + stats->tx_dropped++; + return -ENODEV; + } + + if(!prv->rebuild_header) { + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_rebuild_header: " + "physical device has been detached, packet dropped skb->dev=%s->NULL ", + skb->dev->name); + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "ip=%08x->%08x\n", + (__u32)ntohl(skb->nh.iph->saddr), + (__u32)ntohl(skb->nh.iph->daddr) ); + stats->tx_dropped++; + return -ENODEV; + } + + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast: " + "Revectored rebuild_header dev=%s->%s ", + skb->dev->name, prv->dev->name); + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "ip=%08x->%08x\n", + (__u32)ntohl(skb->nh.iph->saddr), + (__u32)ntohl(skb->nh.iph->daddr) ); + tmp = skb->dev; + skb->dev = prv->dev; + + ret = prv->rebuild_header(skb); + skb->dev = tmp; + return ret; +} + +DEBUG_NO_STATIC int +ipsec_mast_set_mac_address(struct device *dev, void *addr) +{ + struct ipsecpriv *prv = dev->priv; + + struct net_device_stats *stats; /* This device's statistics */ + + if(dev == NULL) { + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_set_mac_address: " + "no device..."); + return -ENODEV; + } + + if(prv == NULL) { + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_set_mac_address: " + "no private space associated with dev=%s", + dev->name ? dev->name : "NULL"); + return -ENODEV; + } + + stats = (struct net_device_stats *) &(prv->mystats); + + if(prv->dev == NULL) { + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_set_mac_address: " + "no physical device associated with dev=%s", + dev->name ? dev->name : "NULL"); + stats->tx_dropped++; + return -ENODEV; + } + + if(!prv->set_mac_address) { + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_set_mac_address: " + "physical device has been detached, cannot set - skb->dev=%s->NULL\n", + dev->name); + return -ENODEV; + } + + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_set_mac_address: " + "Revectored dev=%s->%s addr=0p%p\n", + dev->name, prv->dev->name, addr); + return prv->set_mac_address(prv->dev, addr); + +} + +DEBUG_NO_STATIC void +ipsec_mast_cache_update(struct hh_cache *hh, struct device *dev, unsigned char * haddr) +{ + struct ipsecpriv *prv = dev->priv; + + struct net_device_stats *stats; /* This device's statistics */ + + if(dev == NULL) { + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_cache_update: " + "no device..."); + return; + } + + if(prv == NULL) { + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_cache_update: " + "no private space associated with dev=%s", + dev->name ? dev->name : "NULL"); + return; + } + + stats = (struct net_device_stats *) &(prv->mystats); + + if(prv->dev == NULL) { + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_cache_update: " + "no physical device associated with dev=%s", + dev->name ? dev->name : "NULL"); + stats->tx_dropped++; + return; + } + + if(!prv->header_cache_update) { + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_cache_update: " + "physical device has been detached, cannot set - skb->dev=%s->NULL\n", + dev->name); + return; + } + + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast: " + "Revectored cache_update\n"); + prv->header_cache_update(hh, prv->dev, haddr); + return; +} + +DEBUG_NO_STATIC int +ipsec_mast_neigh_setup(struct neighbour *n) +{ + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_neigh_setup:\n"); + + if (n->nud_state == NUD_NONE) { + n->ops = &arp_broken_ops; + n->output = n->ops->output; + } + return 0; +} + +DEBUG_NO_STATIC int +ipsec_mast_neigh_setup_dev(struct device *dev, struct neigh_parms *p) +{ + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_neigh_setup_dev: " + "setting up %s\n", + dev ? dev->name : "NULL"); + + if (p->tbl->family == AF_INET) { + p->neigh_setup = ipsec_mast_neigh_setup; + p->ucast_probes = 0; + p->mcast_probes = 0; + } + return 0; +} + +/* + * We call the attach routine to attach another device. + */ + +DEBUG_NO_STATIC int +ipsec_mast_attach(struct device *dev, struct device *physdev) +{ + int i; + struct ipsecpriv *prv = dev->priv; + + if(dev == NULL) { + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_attach: " + "no device..."); + return -ENODEV; + } + + if(prv == NULL) { + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_attach: " + "no private space associated with dev=%s", + dev->name ? dev->name : "NULL"); + return -ENODATA; + } + + prv->dev = physdev; + prv->hard_start_xmit = physdev->hard_start_xmit; + prv->get_stats = physdev->get_stats; + + if (physdev->hard_header) { + prv->hard_header = physdev->hard_header; + dev->hard_header = ipsec_mast_hard_header; + } else + dev->hard_header = NULL; + + if (physdev->rebuild_header) { + prv->rebuild_header = physdev->rebuild_header; + dev->rebuild_header = ipsec_mast_rebuild_header; + } else + dev->rebuild_header = NULL; + + if (physdev->set_mac_address) { + prv->set_mac_address = physdev->set_mac_address; + dev->set_mac_address = ipsec_mast_set_mac_address; + } else + dev->set_mac_address = NULL; + + if (physdev->header_cache_update) { + prv->header_cache_update = physdev->header_cache_update; + dev->header_cache_update = ipsec_mast_cache_update; + } else + dev->header_cache_update = NULL; + + dev->hard_header_len = physdev->hard_header_len; + +/* prv->neigh_setup = physdev->neigh_setup; */ + dev->neigh_setup = ipsec_mast_neigh_setup_dev; + dev->mtu = 16260; /* 0xfff0; */ /* dev->mtu; */ + prv->mtu = physdev->mtu; + +#ifdef PHYSDEV_TYPE + dev->type = physdev->type; /* ARPHRD_MAST; */ +#endif /* PHYSDEV_TYPE */ + + dev->addr_len = physdev->addr_len; + for (i=0; i<dev->addr_len; i++) { + dev->dev_addr[i] = physdev->dev_addr[i]; + } +#ifdef CONFIG_IPSEC_DEBUG + if(debug_mast & DB_MAST_INIT) { + printk(KERN_INFO "klips_debug:ipsec_mast_attach: " + "physical device %s being attached has HW address: %2x", + physdev->name, physdev->dev_addr[0]); + for (i=1; i < physdev->addr_len; i++) { + printk(":%02x", physdev->dev_addr[i]); + } + printk("\n"); + } +#endif /* CONFIG_IPSEC_DEBUG */ + + return 0; +} + +/* + * We call the detach routine to detach the ipsec mast from another device. + */ + +DEBUG_NO_STATIC int +ipsec_mast_detach(struct device *dev) +{ + int i; + struct ipsecpriv *prv = dev->priv; + + if(dev == NULL) { + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_detach: " + "no device..."); + return -ENODEV; + } + + if(prv == NULL) { + KLIPS_PRINT(debug_mast & DB_MAST_REVEC, + "klips_debug:ipsec_mast_detach: " + "no private space associated with dev=%s", + dev->name ? dev->name : "NULL"); + return -ENODATA; + } + + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_detach: " + "physical device %s being detached from virtual device %s\n", + prv->dev ? prv->dev->name : "NULL", + dev->name); + + prv->dev = NULL; + prv->hard_start_xmit = NULL; + prv->get_stats = NULL; + + prv->hard_header = NULL; +#ifdef DETACH_AND_DOWN + dev->hard_header = NULL; +#endif /* DETACH_AND_DOWN */ + + prv->rebuild_header = NULL; +#ifdef DETACH_AND_DOWN + dev->rebuild_header = NULL; +#endif /* DETACH_AND_DOWN */ + + prv->set_mac_address = NULL; +#ifdef DETACH_AND_DOWN + dev->set_mac_address = NULL; +#endif /* DETACH_AND_DOWN */ + + prv->header_cache_update = NULL; +#ifdef DETACH_AND_DOWN + dev->header_cache_update = NULL; +#endif /* DETACH_AND_DOWN */ + +#ifdef DETACH_AND_DOWN + dev->neigh_setup = NULL; +#endif /* DETACH_AND_DOWN */ + + dev->hard_header_len = 0; +#ifdef DETACH_AND_DOWN + dev->mtu = 0; +#endif /* DETACH_AND_DOWN */ + prv->mtu = 0; + for (i=0; i<MAX_ADDR_LEN; i++) { + dev->dev_addr[i] = 0; + } + dev->addr_len = 0; +#ifdef PHYSDEV_TYPE + dev->type = ARPHRD_VOID; /* ARPHRD_MAST; */ +#endif /* PHYSDEV_TYPE */ + + return 0; +} + +/* + * We call the clear routine to detach all ipsec masts from other devices. + */ +DEBUG_NO_STATIC int +ipsec_mast_clear(void) +{ + int i; + struct device *ipsecdev = NULL, *prvdev; + struct ipsecpriv *prv; + char name[9]; + int ret; + + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_clear: .\n"); + + for(i = 0; i < IPSEC_NUM_IF; i++) { + sprintf(name, IPSEC_DEV_FORMAT, i); + if((ipsecdev = ipsec_dev_get(name)) != NULL) { + if((prv = (struct ipsecpriv *)(ipsecdev->priv))) { + prvdev = (struct device *)(prv->dev); + if(prvdev) { + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_clear: " + "physical device for device %s is %s\n", + name, prvdev->name); + if((ret = ipsec_mast_detach(ipsecdev))) { + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_clear: " + "error %d detatching device %s from device %s.\n", + ret, name, prvdev->name); + return ret; + } + } + } + } + } + return 0; +} + +DEBUG_NO_STATIC int +ipsec_mast_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +{ + struct ipsecmastconf *cf = (struct ipsecmastconf *)&ifr->ifr_data; + struct ipsecpriv *prv = dev->priv; + struct device *them; /* physical device */ +#ifdef CONFIG_IP_ALIAS + char *colon; + char realphysname[IFNAMSIZ]; +#endif /* CONFIG_IP_ALIAS */ + + if(dev == NULL) { + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_ioctl: " + "device not supplied.\n"); + return -ENODEV; + } + + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_ioctl: " + "tncfg service call #%d for dev=%s\n", + cmd, + dev->name ? dev->name : "NULL"); + switch (cmd) { + /* attach a virtual ipsec? device to a physical device */ + case IPSEC_SET_DEV: + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_ioctl: " + "calling ipsec_mast_attatch...\n"); +#ifdef CONFIG_IP_ALIAS + /* If this is an IP alias interface, get its real physical name */ + strncpy(realphysname, cf->cf_name, IFNAMSIZ); + realphysname[IFNAMSIZ-1] = 0; + colon = strchr(realphysname, ':'); + if (colon) *colon = 0; + them = ipsec_dev_get(realphysname); +#else /* CONFIG_IP_ALIAS */ + them = ipsec_dev_get(cf->cf_name); +#endif /* CONFIG_IP_ALIAS */ + + if (them == NULL) { + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_ioctl: " + "physical device %s requested is null\n", + cf->cf_name); + return -ENXIO; + } + +#if 0 + if (them->flags & IFF_UP) { + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_ioctl: " + "physical device %s requested is not up.\n", + cf->cf_name); + return -ENXIO; + } +#endif + + if (prv && prv->dev) { + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_ioctl: " + "virtual device is already connected to %s.\n", + prv->dev->name ? prv->dev->name : "NULL"); + return -EBUSY; + } + return ipsec_mast_attach(dev, them); + + case IPSEC_DEL_DEV: + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_ioctl: " + "calling ipsec_mast_detatch.\n"); + if (! prv->dev) { + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_ioctl: " + "physical device not connected.\n"); + return -ENODEV; + } + return ipsec_mast_detach(dev); + + case IPSEC_CLR_DEV: + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_ioctl: " + "calling ipsec_mast_clear.\n"); + return ipsec_mast_clear(); + + default: + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_ioctl: " + "unknown command %d.\n", + cmd); + return -EOPNOTSUPP; + } +} + +int +ipsec_mast_device_event(struct notifier_block *unused, unsigned long event, void *ptr) +{ + struct device *dev = ptr; + struct device *ipsec_dev; + struct ipsecpriv *priv; + char name[9]; + int i; + + if (dev == NULL) { + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_device_event: " + "dev=NULL for event type %ld.\n", + event); + return(NOTIFY_DONE); + } + + /* check for loopback devices */ + if (dev && (dev->flags & IFF_LOOPBACK)) { + return(NOTIFY_DONE); + } + + switch (event) { + case NETDEV_DOWN: + /* look very carefully at the scope of these compiler + directives before changing anything... -- RGB */ + + case NETDEV_UNREGISTER: + switch (event) { + case NETDEV_DOWN: + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_device_event: " + "NETDEV_DOWN dev=%s flags=%x\n", + dev->name, + dev->flags); + if(strncmp(dev->name, "ipsec", strlen("ipsec")) == 0) { + printk(KERN_CRIT "IPSEC EVENT: KLIPS device %s shut down.\n", + dev->name); + } + break; + case NETDEV_UNREGISTER: + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_device_event: " + "NETDEV_UNREGISTER dev=%s flags=%x\n", + dev->name, + dev->flags); + break; + } + + /* find the attached physical device and detach it. */ + for(i = 0; i < IPSEC_NUM_IF; i++) { + sprintf(name, IPSEC_DEV_FORMAT, i); + ipsec_dev = ipsec_dev_get(name); + if(ipsec_dev) { + priv = (struct ipsecpriv *)(ipsec_dev->priv); + if(priv) { + ; + if(((struct device *)(priv->dev)) == dev) { + /* dev_close(ipsec_dev); */ + /* return */ ipsec_mast_detach(ipsec_dev); + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_device_event: " + "device '%s' has been detached.\n", + ipsec_dev->name); + break; + } + } else { + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_device_event: " + "device '%s' has no private data space!\n", + ipsec_dev->name); + } + } + } + break; + case NETDEV_UP: + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_device_event: " + "NETDEV_UP dev=%s\n", + dev->name); + break; + case NETDEV_REBOOT: + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_device_event: " + "NETDEV_REBOOT dev=%s\n", + dev->name); + break; + case NETDEV_CHANGE: + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_device_event: " + "NETDEV_CHANGE dev=%s flags=%x\n", + dev->name, + dev->flags); + break; + case NETDEV_REGISTER: + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_device_event: " + "NETDEV_REGISTER dev=%s\n", + dev->name); + break; + case NETDEV_CHANGEMTU: + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_device_event: " + "NETDEV_CHANGEMTU dev=%s to mtu=%d\n", + dev->name, + dev->mtu); + break; + case NETDEV_CHANGEADDR: + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_device_event: " + "NETDEV_CHANGEADDR dev=%s\n", + dev->name); + break; + case NETDEV_GOING_DOWN: + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_device_event: " + "NETDEV_GOING_DOWN dev=%s\n", + dev->name); + break; + case NETDEV_CHANGENAME: + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_device_event: " + "NETDEV_CHANGENAME dev=%s\n", + dev->name); + break; + default: + KLIPS_PRINT(debug_mast & DB_MAST_INIT, + "klips_debug:ipsec_mast_device_event: " + "event type %ld unrecognised for dev=%s\n", + event, + dev->name); + break; + } + return NOTIFY_DONE; +} + +/* + * Called when an ipsec mast device is initialized. + * The ipsec mast device structure is passed to us. + */ + +int +ipsec_mast_init(struct device *dev) +{ + int i; + + KLIPS_PRINT(debug_mast, + "klips_debug:ipsec_mast_init: " + "allocating %lu bytes initialising device: %s\n", + (unsigned long) sizeof(struct ipsecpriv), + dev->name ? dev->name : "NULL"); + + /* Add our mast functions to the device */ + dev->open = ipsec_mast_open; + dev->stop = ipsec_mast_close; + dev->hard_start_xmit = ipsec_mast_start_xmit; + dev->get_stats = ipsec_mast_get_stats; + + dev->priv = kmalloc(sizeof(struct ipsecpriv), GFP_KERNEL); + if (dev->priv == NULL) + return -ENOMEM; + memset((caddr_t)(dev->priv), 0, sizeof(struct ipsecpriv)); + + for(i = 0; i < sizeof(zeroes); i++) { + ((__u8*)(zeroes))[i] = 0; + } + + dev->set_multicast_list = NULL; + dev->do_ioctl = ipsec_mast_ioctl; + dev->hard_header = NULL; + dev->rebuild_header = NULL; + dev->set_mac_address = NULL; + dev->header_cache_update= NULL; + dev->neigh_setup = ipsec_mast_neigh_setup_dev; + dev->hard_header_len = 0; + dev->mtu = 0; + dev->addr_len = 0; + dev->type = ARPHRD_VOID; /* ARPHRD_MAST; */ /* ARPHRD_ETHER; */ + dev->tx_queue_len = 10; /* Small queue */ + memset((caddr_t)(dev->broadcast),0xFF, ETH_ALEN); /* what if this is not attached to ethernet? */ + + /* New-style flags. */ + dev->flags = IFF_NOARP /* 0 */ /* Petr Novak */; + dev_init_buffers(dev); + + /* We're done. Have I forgotten anything? */ + return 0; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* Module specific interface (but it links with the rest of IPSEC) */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int +ipsec_mast_probe(struct device *dev) +{ + ipsec_mast_init(dev); + return 0; +} + +int +ipsec_mast_init_devices(void) +{ + return 0; +} + +/* void */ +int +ipsec_mast_cleanup_devices(void) +{ + int error = 0; + int i; + char name[10]; + struct device *dev_mast; + + for(i = 0; i < ipsec_mastdevice_count; i++) { + sprintf(name, MAST_DEV_FORMAT, i); + if((dev_mast = ipsec_dev_get(name)) == NULL) { + break; + } + unregister_netdev(dev_mast); + kfree(dev_mast->priv); + dev_mast->priv=NULL; + } + return error; +} diff --git a/linux/net/ipsec/ipsec_md5c.c b/linux/net/ipsec/ipsec_md5c.c new file mode 100644 index 000000000..41a1551c1 --- /dev/null +++ b/linux/net/ipsec/ipsec_md5c.c @@ -0,0 +1,448 @@ +/* + * RCSID $Id: ipsec_md5c.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ + +/* + * The rest of the code is derived from MD5C.C by RSADSI. Minor cosmetic + * changes to accomodate it in the kernel by ji. + */ + +#include <asm/byteorder.h> +#include <linux/string.h> + +#include "freeswan/ipsec_md5h.h" + +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +/* + * Additions by JI + * + * HAVEMEMCOPY is defined if mem* routines are available + * + * HAVEHTON is defined if htons() and htonl() can be used + * for big/little endian conversions + * + */ + +#define HAVEMEMCOPY +#ifdef __LITTLE_ENDIAN +#define LITTLENDIAN +#endif +#ifdef __BIG_ENDIAN +#define BIGENDIAN +#endif + +/* Constants for MD5Transform routine. + */ + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64])); + +#ifdef LITTLEENDIAN +#define Encode MD5_memcpy +#define Decode MD5_memcpy +#else +static void Encode PROTO_LIST + ((unsigned char *, UINT4 *, unsigned int)); +static void Decode PROTO_LIST + ((UINT4 *, unsigned char *, unsigned int)); +#endif + +#ifdef HAVEMEMCOPY +/* no need to include <memory.h> here; <linux/string.h> defines these */ +#define MD5_memcpy memcpy +#define MD5_memset memset +#else +#ifdef HAVEBCOPY +#define MD5_memcpy(_a,_b,_c) bcopy((_b),(_a),(_c)) +#define MD5_memset(_a,_b,_c) bzero((_a),(_c)) +#else +static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int)); +static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int)); +#endif +#endif +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* + * MD5 initialization. Begins an MD5 operation, writing a new context. + */ +void MD5Init(void *vcontext) +{ + MD5_CTX *context = vcontext; + + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. +*/ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. + */ +void MD5Update (vcontext, input, inputLen) + void *vcontext; + unsigned char *input; /* input block */ + __u32 inputLen; /* length of input block */ +{ + MD5_CTX *context = vcontext; + __u32 i; + unsigned int index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) + < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. +*/ + if (inputLen >= partLen) { + MD5_memcpy + ((POINTER)&context->buffer[index], (POINTER)input, partLen); + MD5Transform (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform (context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy + ((POINTER)&context->buffer[index], (POINTER)&input[i], + inputLen-i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + */ +void MD5Final (digest, vcontext) +unsigned char digest[16]; /* message digest */ +void *vcontext; /* context */ +{ + MD5_CTX *context = vcontext; + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. +*/ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update (context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update (context, bits, 8); + + if (digest != NULL) /* Bill Simpson's padding */ + { + /* store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information. + */ + MD5_memset ((POINTER)context, 0, sizeof (*context)); + } +} + +/* MD5 basic transformation. Transforms state based on block. + */ +static void MD5Transform (state, block) +UINT4 state[4]; +unsigned char block[64]; +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. +*/ + MD5_memset ((POINTER)x, 0, sizeof (x)); +} + +#ifndef LITTLEENDIAN + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + */ +static void Encode (output, input, len) +unsigned char *output; +UINT4 *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + */ +static void Decode (output, input, len) +UINT4 *output; +unsigned char *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} + +#endif + +#ifndef HAVEMEMCOPY +#ifndef HAVEBCOPY +/* Note: Replace "for loop" with standard memcpy if possible. + */ + +static void MD5_memcpy (output, input, len) +POINTER output; +POINTER input; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + + output[i] = input[i]; +} + +/* Note: Replace "for loop" with standard memset if possible. + */ + +static void MD5_memset (output, value, len) +POINTER output; +int value; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + ((char *)output)[i] = (char)value; +} +#endif +#endif + +/* + * $Log: ipsec_md5c.c,v $ + * Revision 1.1 2004/03/15 20:35:26 as + * added files from freeswan-2.04-x509-1.5.3 + * + * Revision 1.7 2002/09/10 01:45:14 mcr + * changed type of MD5_CTX and SHA1_CTX to void * so that + * the function prototypes would match, and could be placed + * into a pointer to a function. + * + * Revision 1.6 2002/04/24 07:55:32 mcr + * #include patches and Makefiles for post-reorg compilation. + * + * Revision 1.5 2002/04/24 07:36:28 mcr + * Moved from ./klips/net/ipsec/ipsec_md5c.c,v + * + * Revision 1.4 1999/12/13 13:59:12 rgb + * Quick fix to argument size to Update bugs. + * + * Revision 1.3 1999/05/21 18:09:28 henry + * unnecessary <memory.h> include causes trouble in 2.2 + * + * Revision 1.2 1999/04/06 04:54:26 rgb + * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes + * patch shell fixes. + * + * Revision 1.1 1998/06/18 21:27:48 henry + * move sources from klips/src to klips/net/ipsec, to keep stupid + * kernel-build scripts happier in the presence of symlinks + * + * Revision 1.2 1998/04/23 20:54:02 rgb + * Fixed md5 and sha1 include file nesting issues, to be cleaned up when + * verified. + * + * Revision 1.1 1998/04/09 03:06:08 henry + * sources moved up from linux/net/ipsec + * + * Revision 1.1.1.1 1998/04/08 05:35:04 henry + * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 + * + * Revision 0.3 1996/11/20 14:48:53 ji + * Release update only. + * + * Revision 0.2 1996/11/02 00:18:33 ji + * First limited release. + * + * + */ diff --git a/linux/net/ipsec/ipsec_proc.c b/linux/net/ipsec/ipsec_proc.c new file mode 100644 index 000000000..5d2bba554 --- /dev/null +++ b/linux/net/ipsec/ipsec_proc.c @@ -0,0 +1,1003 @@ +/* + * @(#) /proc file system interface code. + * + * Copyright (C) 1996, 1997 John Ioannidis. + * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs <rgb@freeswan.org> + * 2001 Michael Richardson <mcr@freeswan.org> + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + * + * Split out from ipsec_init.c version 1.70. + */ + +char ipsec_proc_c_version[] = "RCSID $Id: ipsec_proc.c,v 1.8 2004/04/28 08:06:22 as Exp $"; + +#include <linux/config.h> +#include <linux/version.h> +#define __NO_VERSION__ +#include <linux/module.h> +#include <linux/kernel.h> /* printk() */ + +#include "freeswan/ipsec_param.h" + +#ifdef MALLOC_SLAB +# include <linux/slab.h> /* kmalloc() */ +#else /* MALLOC_SLAB */ +# include <linux/malloc.h> /* kmalloc() */ +#endif /* MALLOC_SLAB */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/interrupt.h> /* mark_bh */ + +#include <linux/netdevice.h> /* struct device, and other headers */ +#include <linux/etherdevice.h> /* eth_type_trans */ +#include <linux/ip.h> /* struct iphdr */ +#include <linux/in.h> /* struct sockaddr_in */ +#include <linux/skbuff.h> +#include <freeswan.h> +#ifdef SPINLOCK +#ifdef SPINLOCK_23 +#include <linux/spinlock.h> /* *lock* */ +#else /* SPINLOCK_23 */ +#include <asm/spinlock.h> /* *lock* */ +#endif /* SPINLOCK_23 */ +#endif /* SPINLOCK */ +#ifdef NET_21 +#include <asm/uaccess.h> +#include <linux/in6.h> +#endif /* NET_21 */ +#include <asm/checksum.h> +#include <net/ip.h> +#ifdef CONFIG_PROC_FS +#include <linux/proc_fs.h> +#endif /* CONFIG_PROC_FS */ +#ifdef NETLINK_SOCK +#include <linux/netlink.h> +#else +#include <net/netlink.h> +#endif + +#include "freeswan/radij.h" + +#include "freeswan/ipsec_life.h" +#include "freeswan/ipsec_stats.h" +#include "freeswan/ipsec_sa.h" + +#include "freeswan/ipsec_encap.h" +#include "freeswan/ipsec_radij.h" +#include "freeswan/ipsec_xform.h" +#include "freeswan/ipsec_tunnel.h" +#include "freeswan/ipsec_xmit.h" + +#include "freeswan/ipsec_rcv.h" +#include "freeswan/ipsec_ah.h" +#include "freeswan/ipsec_esp.h" + +#ifdef CONFIG_IPSEC_IPCOMP +#include "freeswan/ipcomp.h" +#endif /* CONFIG_IPSEC_IPCOMP */ + +#include "freeswan/ipsec_proto.h" + +#include <pfkeyv2.h> +#include <pfkey.h> + +#ifdef CONFIG_PROC_FS + +#ifdef IPSEC_PROC_SUBDIRS +static struct proc_dir_entry *proc_net_ipsec_dir = NULL; +static struct proc_dir_entry *proc_eroute_dir = NULL; +static struct proc_dir_entry *proc_spi_dir = NULL; +static struct proc_dir_entry *proc_spigrp_dir = NULL; +static struct proc_dir_entry *proc_birth_dir = NULL; +static struct proc_dir_entry *proc_stats_dir = NULL; +#endif + +struct ipsec_birth_reply ipsec_ipv4_birth_packet; +struct ipsec_birth_reply ipsec_ipv6_birth_packet; + +extern int ipsec_xform_get_info(char *buffer, char **start, + off_t offset, int length IPSEC_PROC_LAST_ARG); + + +/* ipsec_snprintf: like snprintf except + * - size is signed and a negative value is treated as if it were 0 + * - the returned result is never negative -- + * an error generates a "?" or null output (depending on space). + * (Our callers are too lazy to check for an error return.) + * + * @param buf String buffer + * @param size Size of the string + * @param fmt printf string + * @param ... Variables to be displayed in fmt + * @return int Return code + */ +int ipsec_snprintf(char *buf, ssize_t size, const char *fmt, ...) +{ + va_list args; + int i; + size_t possize = size < 0? 0 : size; + va_start(args, fmt); + i = vsnprintf(buf,possize,fmt,args); + va_end(args); + if (i < 0) { + /* create empty output in place of error */ + i = 0; + if (size > 0) { + *buf = '\0'; + } + } + return i; +} + + +IPSEC_PROCFS_DEBUG_NO_STATIC +int +ipsec_eroute_get_info(char *buffer, + char **start, + off_t offset, + int length IPSEC_PROC_LAST_ARG) +{ + struct wsbuf w = {buffer, length, offset, 0, 0}; + +#ifdef CONFIG_IPSEC_DEBUG + if (debug_radij & DB_RJ_DUMPTREES) + rj_dumptrees(); /* XXXXXXXXX */ +#endif /* CONFIG_IPSEC_DEBUG */ + + KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, + "klips_debug:ipsec_eroute_get_info: " + "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n", + buffer, + *start, + (int)offset, + length); + + spin_lock_bh(&eroute_lock); + + rj_walktree(rnh, ipsec_rj_walker_procprint, &w); +/* rj_walktree(mask_rjhead, ipsec_rj_walker_procprint, &w); */ + + spin_unlock_bh(&eroute_lock); + + *start = buffer + (offset - w.begin); /* Start of wanted data */ + return w.len - (offset - w.begin); +} + +IPSEC_PROCFS_DEBUG_NO_STATIC +int +ipsec_spi_get_info(char *buffer, + char **start, + off_t offset, + int length IPSEC_PROC_LAST_ARG) +{ + /* Limit of useful snprintf output */ + const int max_content = length > 0? length-1 : 0; + + int len = 0; + off_t begin = 0; + int i; + struct ipsec_sa *sa_p; + char sa[SATOA_BUF]; + char buf_s[SUBNETTOA_BUF]; + char buf_d[SUBNETTOA_BUF]; + size_t sa_len; + + KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, + "klips_debug:ipsec_spi_get_info: " + "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n", + buffer, + *start, + (int)offset, + length); + + spin_lock_bh(&tdb_lock); + + + + for (i = 0; i < SADB_HASHMOD; i++) { + for (sa_p = ipsec_sadb_hash[i]; + sa_p; + sa_p = sa_p->ips_hnext) { + atomic_inc(&sa_p->ips_refcount); + sa_len = satoa(sa_p->ips_said, 0, sa, SATOA_BUF); + len += ipsec_snprintf(buffer+len, length-len, "%s ", + sa_len ? sa : " (error)"); + + len += ipsec_snprintf(buffer+len, length-len, "%s%s%s", + IPS_XFORM_NAME(sa_p)); + + len += ipsec_snprintf(buffer+len, length-len, ": dir=%s", + (sa_p->ips_flags & EMT_INBOUND) ? + "in " : "out"); + + if(sa_p->ips_addr_s) { + addrtoa(((struct sockaddr_in*)(sa_p->ips_addr_s))->sin_addr, + 0, buf_s, sizeof(buf_s)); + len += ipsec_snprintf(buffer+len, length-len, " src=%s", + buf_s); + } + + if((sa_p->ips_said.proto == IPPROTO_IPIP) + && (sa_p->ips_flags & SADB_X_SAFLAGS_INFLOW)) { + subnettoa(sa_p->ips_flow_s.u.v4.sin_addr, + sa_p->ips_mask_s.u.v4.sin_addr, + 0, + buf_s, + sizeof(buf_s)); + + subnettoa(sa_p->ips_flow_d.u.v4.sin_addr, + sa_p->ips_mask_d.u.v4.sin_addr, + 0, + buf_d, + sizeof(buf_d)); + + len += ipsec_snprintf(buffer+len, length-len, " policy=%s->%s", + buf_s, buf_d); + } + + if(sa_p->ips_iv_bits) { + int j; + len += ipsec_snprintf(buffer+len, length-len, " iv_bits=%dbits iv=0x", + sa_p->ips_iv_bits); + + for(j = 0; j < sa_p->ips_iv_bits / 8; j++) { + len += ipsec_snprintf(buffer+len, length-len, "%02x", + (__u32)((__u8*)(sa_p->ips_iv))[j]); + } + } + + if(sa_p->ips_encalg || sa_p->ips_authalg) { + if(sa_p->ips_replaywin) { + len += ipsec_snprintf(buffer+len, length-len, " ooowin=%d", + sa_p->ips_replaywin); + } + if(sa_p->ips_errs.ips_replaywin_errs) { + len += ipsec_snprintf(buffer+len, length-len, " ooo_errs=%d", + sa_p->ips_errs.ips_replaywin_errs); + } + if(sa_p->ips_replaywin_lastseq) { + len += ipsec_snprintf(buffer+len, length-len, " seq=%d", + sa_p->ips_replaywin_lastseq); + } + if(sa_p->ips_replaywin_bitmap) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) + len += ipsec_snprintf(buffer+len, length-len, " bit=0x%Lx", + sa_p->ips_replaywin_bitmap); +#else + len += ipsec_snprintf(buffer+len, length-len, " bit=0x%x%08x", + (__u32)(sa_p->ips_replaywin_bitmap >> 32), + (__u32)sa_p->ips_replaywin_bitmap); +#endif + } + if(sa_p->ips_replaywin_maxdiff) { + len += ipsec_snprintf(buffer+len, length-len, " max_seq_diff=%d", + sa_p->ips_replaywin_maxdiff); + } + } + if(sa_p->ips_flags & ~EMT_INBOUND) { + len += ipsec_snprintf(buffer+len, length-len, " flags=0x%x", + sa_p->ips_flags & ~EMT_INBOUND); + len += ipsec_snprintf(buffer+len, length-len, "<"); + /* flag printing goes here */ + len += ipsec_snprintf(buffer+len, length-len, ">"); + } + if(sa_p->ips_auth_bits) { + len += ipsec_snprintf(buffer+len, length-len, " alen=%d", + sa_p->ips_auth_bits); + } + if(sa_p->ips_key_bits_a) { + len += ipsec_snprintf(buffer+len, length-len, " aklen=%d", + sa_p->ips_key_bits_a); + } + if(sa_p->ips_errs.ips_auth_errs) { + len += ipsec_snprintf(buffer+len, length-len, " auth_errs=%d", + sa_p->ips_errs.ips_auth_errs); + } + if(sa_p->ips_key_bits_e) { + len += ipsec_snprintf(buffer+len, length-len, " eklen=%d", + sa_p->ips_key_bits_e); + } + if(sa_p->ips_errs.ips_encsize_errs) { + len += ipsec_snprintf(buffer+len, length-len, " encr_size_errs=%d", + sa_p->ips_errs.ips_encsize_errs); + } + if(sa_p->ips_errs.ips_encpad_errs) { + len += ipsec_snprintf(buffer+len, length-len, " encr_pad_errs=%d", + sa_p->ips_errs.ips_encpad_errs); + } + + len += ipsec_snprintf(buffer+len, length-len, " life(c,s,h)="); + + len += ipsec_lifetime_format(buffer + len, + length - len, + "alloc", + ipsec_life_countbased, + &sa_p->ips_life.ipl_allocations); + + len += ipsec_lifetime_format(buffer + len, + length - len, + "bytes", + ipsec_life_countbased, + &sa_p->ips_life.ipl_bytes); + + len += ipsec_lifetime_format(buffer + len, + length - len, + "addtime", + ipsec_life_timebased, + &sa_p->ips_life.ipl_addtime); + + len += ipsec_lifetime_format(buffer + len, + length - len, + "usetime", + ipsec_life_timebased, + &sa_p->ips_life.ipl_usetime); + + len += ipsec_lifetime_format(buffer + len, + length - len, + "packets", + ipsec_life_countbased, + &sa_p->ips_life.ipl_packets); + + if(sa_p->ips_life.ipl_usetime.ipl_last) { /* XXX-MCR should be last? */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) + len += ipsec_snprintf(buffer+len, length-len, " idle=%Ld", + jiffies / HZ - sa_p->ips_life.ipl_usetime.ipl_last); +#else + len += ipsec_snprintf(buffer+len, length-len, " idle=%lu", + jiffies / HZ - (unsigned long)sa_p->ips_life.ipl_usetime.ipl_last); +#endif + } + +#ifdef CONFIG_IPSEC_IPCOMP + if(sa_p->ips_said.proto == IPPROTO_COMP && + (sa_p->ips_comp_ratio_dbytes || + sa_p->ips_comp_ratio_cbytes)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) + len += ipsec_snprintf(buffer+len, length-len, " ratio=%Ld:%Ld", + sa_p->ips_comp_ratio_dbytes, + sa_p->ips_comp_ratio_cbytes); +#else + len += ipsec_snprintf(buffer+len, length-len, " ratio=%lu:%lu", + (unsigned long)sa_p->ips_comp_ratio_dbytes, + (unsigned long)sa_p->ips_comp_ratio_cbytes); +#endif + } +#endif /* CONFIG_IPSEC_IPCOMP */ + +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL + if(sa_p->ips_natt_type != 0) { + char *natttype_name; + + switch(sa_p->ips_natt_type) + { + case ESPINUDP_WITH_NON_IKE: + natttype_name="nonike"; + break; + case ESPINUDP_WITH_NON_ESP: + natttype_name="nonesp"; + break; + default: + natttype_name="unknown"; + break; + } + + len += ipsec_snprintf(buffer+len, length-len, " natencap=%s", + natttype_name); + + len += ipsec_snprintf(buffer+len, length-len, " natsport=%d", + sa_p->ips_natt_sport); + + len += ipsec_snprintf(buffer+len, length-len, " natdport=%d", + sa_p->ips_natt_dport); + } +#endif /* CONFIG_IPSEC_NAT_TRAVERSAL */ + + len += ipsec_snprintf(buffer+len, length-len, " refcount=%d", + atomic_read(&sa_p->ips_refcount)); + + len += ipsec_snprintf(buffer+len, length-len, " ref=%d", + sa_p->ips_ref); +#ifdef CONFIG_IPSEC_DEBUG + if(debug_xform) { + len += ipsec_snprintf(buffer+len, length-len, " reftable=%lu refentry=%lu", + (unsigned long)IPsecSAref2table(sa_p->ips_ref), + (unsigned long)IPsecSAref2entry(sa_p->ips_ref)); + } +#endif /* CONFIG_IPSEC_DEBUG */ + + len += ipsec_snprintf(buffer+len, length-len, "\n"); + + atomic_dec(&sa_p->ips_refcount); + + if (len >= max_content) { + /* we've done all that can fit -- stop loops */ + len = max_content; /* truncate crap */ + goto done_spi_i; + } else { + const off_t pos = begin + len; + + if (pos <= offset) { + /* all is before first interesting character: + * discard, but note where we are. + */ + len = 0; + begin = pos; + } + } + } + } + +done_spi_i: + spin_unlock_bh(&tdb_lock); + + *start = buffer + (offset - begin); /* Start of wanted data */ + return len - (offset - begin); +} + +IPSEC_PROCFS_DEBUG_NO_STATIC +int +ipsec_spigrp_get_info(char *buffer, + char **start, + off_t offset, + int length IPSEC_PROC_LAST_ARG) +{ + /* limit of useful snprintf output */ + const int max_content = length > 0? length-1 : 0; + + int len = 0; + off_t begin = 0; + int i; + struct ipsec_sa *sa_p, *sa_p2; + char sa[SATOA_BUF]; + size_t sa_len; + + KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, + "klips_debug:ipsec_spigrp_get_info: " + "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n", + buffer, + *start, + (int)offset, + length); + + spin_lock_bh(&tdb_lock); + + for (i = 0; i < SADB_HASHMOD; i++) { + for (sa_p = ipsec_sadb_hash[i]; + sa_p != NULL; + sa_p = sa_p->ips_hnext) + { + atomic_inc(&sa_p->ips_refcount); + if(sa_p->ips_inext == NULL) { + sa_p2 = sa_p; + while(sa_p2 != NULL) { + atomic_inc(&sa_p2->ips_refcount); + sa_len = satoa(sa_p2->ips_said, + 0, sa, SATOA_BUF); + + len += ipsec_snprintf(buffer+len, length-len, "%s ", + sa_len ? sa : " (error)"); + atomic_dec(&sa_p2->ips_refcount); + sa_p2 = sa_p2->ips_onext; + } + len += ipsec_snprintf(buffer+len, length-len, "\n"); + } + + atomic_dec(&sa_p->ips_refcount); + + if (len >= max_content) { + /* we've done all that can fit -- stop loops */ + len = max_content; /* truncate crap */ + goto done_spigrp_i; + } else { + const off_t pos = begin + len; + + if (pos <= offset) { + /* all is before first interesting character: + * discard, but note where we are. + */ + len = 0; + begin = pos; + } + } + } + } + + done_spigrp_i: + spin_unlock_bh(&tdb_lock); + + *start = buffer + (offset - begin); /* Start of wanted data */ + return len - (offset - begin); +} + +IPSEC_PROCFS_DEBUG_NO_STATIC +int +ipsec_tncfg_get_info(char *buffer, + char **start, + off_t offset, + int length IPSEC_PROC_LAST_ARG) +{ + /* limit of useful snprintf output */ + const int max_content = length > 0? length-1 : 0; + + int len = 0; + off_t begin = 0; + int i; + char name[9]; + struct device *dev, *privdev; + struct ipsecpriv *priv; + + KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, + "klips_debug:ipsec_tncfg_get_info: " + "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n", + buffer, + *start, + (int)offset, + length); + + for(i = 0; i < IPSEC_NUM_IF; i++) { + ipsec_snprintf(name, (ssize_t) sizeof(name), IPSEC_DEV_FORMAT, i); + dev = __ipsec_dev_get(name); + if(dev) { + priv = (struct ipsecpriv *)(dev->priv); + len += ipsec_snprintf(buffer+len, length-len, "%s", + dev->name); + if(priv) { + privdev = (struct device *)(priv->dev); + len += ipsec_snprintf(buffer+len, length-len, " -> %s", + privdev ? privdev->name : "NULL"); + len += ipsec_snprintf(buffer+len, length-len, " mtu=%d(%d) -> %d", + dev->mtu, + priv->mtu, + privdev ? privdev->mtu : 0); + } else { + KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, + "klips_debug:ipsec_tncfg_get_info: device '%s' has no private data space!\n", + dev->name); + } + len += ipsec_snprintf(buffer+len, length-len, "\n"); + + if (len >= max_content) { + /* we've done all that can fit -- stop loop */ + len = max_content; /* truncate crap */ + break; + } else { + const off_t pos = begin + len; + if (pos <= offset) { + len = 0; + begin = pos; + } + } + } + } + *start = buffer + (offset - begin); /* Start of wanted data */ + len -= (offset - begin); /* Start slop */ + if (len > length) + len = length; + return len; +} + +IPSEC_PROCFS_DEBUG_NO_STATIC +int +ipsec_version_get_info(char *buffer, + char **start, + off_t offset, + int length IPSEC_PROC_LAST_ARG) +{ + int len = 0; + off_t begin = 0; + + KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, + "klips_debug:ipsec_version_get_info: " + "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n", + buffer, + *start, + (int)offset, + length); + + len += ipsec_snprintf(buffer+len, length-len, "strongSwan version: %s\n", + ipsec_version_code()); +#if 0 + KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, + "klips_debug:ipsec_version_get_info: " + "ipsec_init version: %s\n", + ipsec_init_c_version); + KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, + "klips_debug:ipsec_version_get_info: " + "ipsec_tunnel version: %s\n", + ipsec_tunnel_c_version); + KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, + "klips_debug:ipsec_version_get_info: " + "ipsec_netlink version: %s\n", + ipsec_netlink_c_version); + KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, + "klips_debug:ipsec_version_get_info: " + "radij_c_version: %s\n", + radij_c_version); +#endif + + *start = buffer + (offset - begin); /* Start of wanted data */ + len -= (offset - begin); /* Start slop */ + if (len > length) + len = length; + return len; +} + +IPSEC_PROCFS_DEBUG_NO_STATIC +int +ipsec_birth_info(char *page, + char **start, + off_t offset, + int count, + int *eof, + void *data) +{ + struct ipsec_birth_reply *ibr = (struct ipsec_birth_reply *)data; + int len; + + if(offset >= ibr->packet_template_len) { + if(eof) { + *eof=1; + } + return 0; + } + + len = ibr->packet_template_len; + len -= offset; + if (len > count) + len = count; + + memcpy(page + offset, ibr->packet_template+offset, len); + + return len; +} + +IPSEC_PROCFS_DEBUG_NO_STATIC +int +ipsec_birth_set(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + struct ipsec_birth_reply *ibr = (struct ipsec_birth_reply *)data; + int len; + + MOD_INC_USE_COUNT; + if(count > IPSEC_BIRTH_TEMPLATE_MAXLEN) { + len = IPSEC_BIRTH_TEMPLATE_MAXLEN; + } else { + len = count; + } + + if(copy_from_user(ibr->packet_template, buffer, len)) { + MOD_DEC_USE_COUNT; + return -EFAULT; + } + ibr->packet_template_len = len; + + MOD_DEC_USE_COUNT; + + return len; +} + + +#ifdef CONFIG_IPSEC_DEBUG +IPSEC_PROCFS_DEBUG_NO_STATIC +int +ipsec_klipsdebug_get_info(char *buffer, + char **start, + off_t offset, + int length IPSEC_PROC_LAST_ARG) +{ + int len = 0; + off_t begin = 0; + + KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, + "klips_debug:ipsec_klipsdebug_get_info: " + "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n", + buffer, + *start, + (int)offset, + length); + + len += ipsec_snprintf(buffer+len, length-len, "debug_tunnel=%08x.\n", debug_tunnel); + len += ipsec_snprintf(buffer+len, length-len, "debug_xform=%08x.\n", debug_xform); + len += ipsec_snprintf(buffer+len, length-len, "debug_eroute=%08x.\n", debug_eroute); + len += ipsec_snprintf(buffer+len, length-len, "debug_spi=%08x.\n", debug_spi); + len += ipsec_snprintf(buffer+len, length-len, "debug_radij=%08x.\n", debug_radij); + len += ipsec_snprintf(buffer+len, length-len, "debug_esp=%08x.\n", debug_esp); + len += ipsec_snprintf(buffer+len, length-len, "debug_ah=%08x.\n", debug_ah); + len += ipsec_snprintf(buffer+len, length-len, "debug_rcv=%08x.\n", debug_rcv); + len += ipsec_snprintf(buffer+len, length-len, "debug_pfkey=%08x.\n", debug_pfkey); + + *start = buffer + (offset - begin); /* Start of wanted data */ + len -= (offset - begin); /* Start slop */ + if (len > length) + len = length; + return len; +} +#endif /* CONFIG_IPSEC_DEBUG */ + +IPSEC_PROCFS_DEBUG_NO_STATIC +int +ipsec_stats_get_int_info(char *buffer, + char **start, + off_t offset, + int length, + int *eof, + void *data) +{ + /* Limit of useful snprintf output */ + const int max_content = length > 0? length-1 : 0; + + int len = 0; + int *thing; + + thing = (int *)data; + + len = ipsec_snprintf(buffer+len, length-len, "%08x\n", *thing); + + if (len >= max_content) + len = max_content; /* truncate crap */ + + *start = buffer + offset; /* Start of wanted data */ + return len > offset? len - offset : 0; +} + +#ifndef PROC_FS_2325 +struct proc_dir_entry ipsec_eroute = +{ + 0, + 12, "ipsec_eroute", + S_IFREG | S_IRUGO, 1, 0, 0, 0, + &proc_net_inode_operations, + ipsec_eroute_get_info, + NULL, NULL, NULL, NULL, NULL +}; + +struct proc_dir_entry ipsec_spi = +{ + 0, + 9, "ipsec_spi", + S_IFREG | S_IRUGO, 1, 0, 0, 0, + &proc_net_inode_operations, + ipsec_spi_get_info, + NULL, NULL, NULL, NULL, NULL +}; + +struct proc_dir_entry ipsec_spigrp = +{ + 0, + 12, "ipsec_spigrp", + S_IFREG | S_IRUGO, 1, 0, 0, 0, + &proc_net_inode_operations, + ipsec_spigrp_get_info, + NULL, NULL, NULL, NULL, NULL +}; + +struct proc_dir_entry ipsec_tncfg = +{ + 0, + 11, "ipsec_tncfg", + S_IFREG | S_IRUGO, 1, 0, 0, 0, + &proc_net_inode_operations, + ipsec_tncfg_get_info, + NULL, NULL, NULL, NULL, NULL +}; + +struct proc_dir_entry ipsec_version = +{ + 0, + 13, "ipsec_version", + S_IFREG | S_IRUGO, 1, 0, 0, 0, + &proc_net_inode_operations, + ipsec_version_get_info, + NULL, NULL, NULL, NULL, NULL +}; + +#ifdef CONFIG_IPSEC_DEBUG +struct proc_dir_entry ipsec_klipsdebug = +{ + 0, + 16, "ipsec_klipsdebug", + S_IFREG | S_IRUGO, 1, 0, 0, 0, + &proc_net_inode_operations, + ipsec_klipsdebug_get_info, + NULL, NULL, NULL, NULL, NULL +}; +#endif /* CONFIG_IPSEC_DEBUG */ +#endif /* !PROC_FS_2325 */ +#endif /* CONFIG_PROC_FS */ + +#if defined(PROC_FS_2325) +struct ipsec_proc_list { + char *name; + struct proc_dir_entry **parent; + struct proc_dir_entry **dir; + read_proc_t *readthing; + write_proc_t *writething; + void *data; +}; +static struct ipsec_proc_list proc_items[]={ +#ifdef CONFIG_IPSEC_DEBUG + {"klipsdebug", &proc_net_ipsec_dir, NULL, ipsec_klipsdebug_get_info, NULL, NULL}, +#endif + {"eroute", &proc_net_ipsec_dir, &proc_eroute_dir, NULL, NULL, NULL}, + {"all", &proc_eroute_dir, NULL, ipsec_eroute_get_info, NULL, NULL}, + {"spi", &proc_net_ipsec_dir, &proc_spi_dir, NULL, NULL, NULL}, + {"all", &proc_spi_dir, NULL, ipsec_spi_get_info, NULL, NULL}, + {"spigrp", &proc_net_ipsec_dir, &proc_spigrp_dir, NULL, NULL, NULL}, + {"all", &proc_spigrp_dir, NULL, ipsec_spigrp_get_info, NULL, NULL}, + {"birth", &proc_net_ipsec_dir, &proc_birth_dir, NULL, NULL, NULL}, + {"ipv4", &proc_birth_dir, NULL, ipsec_birth_info, ipsec_birth_set, (void *)&ipsec_ipv4_birth_packet}, + {"ipv6", &proc_birth_dir, NULL, ipsec_birth_info, ipsec_birth_set, (void *)&ipsec_ipv6_birth_packet}, + {"xforms", &proc_net_ipsec_dir, NULL, ipsec_xform_get_info, NULL, NULL}, + {"tncfg", &proc_net_ipsec_dir, NULL, ipsec_tncfg_get_info, NULL, NULL}, + {"stats", &proc_net_ipsec_dir, &proc_stats_dir, NULL, NULL, NULL}, + {"trap_count", &proc_stats_dir, NULL, ipsec_stats_get_int_info, NULL, &ipsec_xmit_trap_count}, + {"trap_sendcount", &proc_stats_dir, NULL, ipsec_stats_get_int_info, NULL, &ipsec_xmit_trap_sendcount}, + {"version", &proc_net_ipsec_dir, NULL, ipsec_version_get_info, NULL, NULL}, + {NULL, NULL, NULL, NULL, NULL, NULL} +}; +#endif + +int +ipsec_proc_init() +{ + int error = 0; +#ifdef IPSEC_PROC_SUBDIRS + struct proc_dir_entry *item; +#endif + + /* + * just complain because pluto won't run without /proc! + */ +#ifndef CONFIG_PROC_FS +#error You must have PROC_FS built in to use KLIPS +#endif + + /* for 2.0 kernels */ +#if !defined(PROC_FS_2325) && !defined(PROC_FS_21) + error |= proc_register_dynamic(&proc_net, &ipsec_eroute); + error |= proc_register_dynamic(&proc_net, &ipsec_spi); + error |= proc_register_dynamic(&proc_net, &ipsec_spigrp); + error |= proc_register_dynamic(&proc_net, &ipsec_tncfg); + error |= proc_register_dynamic(&proc_net, &ipsec_version); +#ifdef CONFIG_IPSEC_DEBUG + error |= proc_register_dynamic(&proc_net, &ipsec_klipsdebug); +#endif /* CONFIG_IPSEC_DEBUG */ +#endif + + /* for 2.2 kernels */ +#if !defined(PROC_FS_2325) && defined(PROC_FS_21) + error |= proc_register(proc_net, &ipsec_eroute); + error |= proc_register(proc_net, &ipsec_spi); + error |= proc_register(proc_net, &ipsec_spigrp); + error |= proc_register(proc_net, &ipsec_tncfg); + error |= proc_register(proc_net, &ipsec_version); +#ifdef CONFIG_IPSEC_DEBUG + error |= proc_register(proc_net, &ipsec_klipsdebug); +#endif /* CONFIG_IPSEC_DEBUG */ +#endif + + /* for 2.4 kernels */ +#if defined(PROC_FS_2325) + /* create /proc/net/ipsec */ + + /* zero these out before we initialize /proc/net/ipsec/birth/stuff */ + memset(&ipsec_ipv4_birth_packet, 0, sizeof(struct ipsec_birth_reply)); + memset(&ipsec_ipv6_birth_packet, 0, sizeof(struct ipsec_birth_reply)); + + proc_net_ipsec_dir = proc_mkdir("ipsec", proc_net); + if(proc_net_ipsec_dir == NULL) { + /* no point in continuing */ + return 1; + } + + { + struct ipsec_proc_list *it; + + it=proc_items; + while(it->name!=NULL) { + if(it->dir) { + /* make a dir instead */ + item = proc_mkdir(it->name, *it->parent); + *it->dir = item; + } else { + item = create_proc_entry(it->name, 0400, *it->parent); + } + if(item) { + item->read_proc = it->readthing; + item->write_proc = it->writething; + item->data = it->data; +#ifdef MODULE + item->owner = THIS_MODULE; +#endif + } else { + error |= 1; + } + it++; + } + } + + /* now create some symlinks to provide compatibility */ + proc_symlink("ipsec_eroute", proc_net, "ipsec/eroute/all"); + proc_symlink("ipsec_spi", proc_net, "ipsec/spi/all"); + proc_symlink("ipsec_spigrp", proc_net, "ipsec/spigrp/all"); + proc_symlink("ipsec_tncfg", proc_net, "ipsec/tncfg"); + proc_symlink("ipsec_version",proc_net, "ipsec/version"); + proc_symlink("ipsec_klipsdebug",proc_net,"ipsec/klipsdebug"); + +#endif /* !PROC_FS_2325 */ + + return error; +} + +void +ipsec_proc_cleanup() +{ + + /* for 2.0 and 2.2 kernels */ +#if !defined(PROC_FS_2325) + +#ifdef CONFIG_IPSEC_DEBUG + if (proc_net_unregister(ipsec_klipsdebug.low_ino) != 0) + printk("klips_debug:ipsec_cleanup: " + "cannot unregister /proc/net/ipsec_klipsdebug\n"); +#endif /* CONFIG_IPSEC_DEBUG */ + + if (proc_net_unregister(ipsec_version.low_ino) != 0) + printk("klips_debug:ipsec_cleanup: " + "cannot unregister /proc/net/ipsec_version\n"); + if (proc_net_unregister(ipsec_eroute.low_ino) != 0) + printk("klips_debug:ipsec_cleanup: " + "cannot unregister /proc/net/ipsec_eroute\n"); + if (proc_net_unregister(ipsec_spi.low_ino) != 0) + printk("klips_debug:ipsec_cleanup: " + "cannot unregister /proc/net/ipsec_spi\n"); + if (proc_net_unregister(ipsec_spigrp.low_ino) != 0) + printk("klips_debug:ipsec_cleanup: " + "cannot unregister /proc/net/ipsec_spigrp\n"); + if (proc_net_unregister(ipsec_tncfg.low_ino) != 0) + printk("klips_debug:ipsec_cleanup: " + "cannot unregister /proc/net/ipsec_tncfg\n"); +#endif + + /* for 2.4 kernels */ +#if defined(PROC_FS_2325) + { + struct ipsec_proc_list *it; + + /* find end of list */ + it=proc_items; + while(it->name!=NULL) { + it++; + } + it--; + + do { + remove_proc_entry(it->name, *it->parent); + it--; + } while(it > proc_items); + } + + +#ifdef CONFIG_IPSEC_DEBUG + remove_proc_entry("ipsec_klipsdebug", proc_net); +#endif /* CONFIG_IPSEC_DEBUG */ + remove_proc_entry("ipsec_eroute", proc_net); + remove_proc_entry("ipsec_spi", proc_net); + remove_proc_entry("ipsec_spigrp", proc_net); + remove_proc_entry("ipsec_tncfg", proc_net); + remove_proc_entry("ipsec_version", proc_net); + remove_proc_entry("ipsec", proc_net); +#endif /* 2.4 kernel */ +} + + diff --git a/linux/net/ipsec/ipsec_radij.c b/linux/net/ipsec/ipsec_radij.c new file mode 100644 index 000000000..b20eb7a6f --- /dev/null +++ b/linux/net/ipsec/ipsec_radij.c @@ -0,0 +1,550 @@ +/* + * Interface between the IPSEC code and the radix (radij) tree code + * Copyright (C) 1996, 1997 John Ioannidis. + * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + * + * RCSID $Id: ipsec_radij.c,v 1.5 2005/04/10 21:38:32 as Exp $ + */ + +#include <linux/config.h> +#include <linux/version.h> +#include <linux/kernel.h> /* printk() */ + +#include "freeswan/ipsec_param.h" + +#ifdef MALLOC_SLAB +# include <linux/slab.h> /* kmalloc() */ +#else /* MALLOC_SLAB */ +# include <linux/malloc.h> /* kmalloc() */ +#endif /* MALLOC_SLAB */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/interrupt.h> /* mark_bh */ + +#include <linux/netdevice.h> /* struct device, struct net_device_stats and other headers */ +#include <linux/etherdevice.h> /* eth_type_trans */ +#include <linux/ip.h> /* struct iphdr */ +#include <linux/skbuff.h> +#include <freeswan.h> +#ifdef SPINLOCK +# ifdef SPINLOCK_23 +# include <linux/spinlock.h> /* *lock* */ +# else /* 23_SPINLOCK */ +# include <asm/spinlock.h> /* *lock* */ +# endif /* 23_SPINLOCK */ +#endif /* SPINLOCK */ +#ifdef NET_21 +# include <asm/uaccess.h> +# include <linux/in6.h> +#endif +#include <asm/checksum.h> +#include <net/ip.h> + +#include "freeswan/ipsec_eroute.h" +#include "freeswan/ipsec_sa.h" + +#include "freeswan/radij.h" +#include "freeswan/ipsec_encap.h" +#include "freeswan/radij.h" +#include "freeswan/ipsec_encap.h" +#include "freeswan/ipsec_radij.h" +#include "freeswan/ipsec_tunnel.h" /* struct ipsecpriv */ +#include "freeswan/ipsec_xform.h" + +#include <pfkeyv2.h> +#include <pfkey.h> + +#include "freeswan/ipsec_proto.h" + +#ifdef CONFIG_IPSEC_DEBUG +int debug_radij = 0; +#endif /* CONFIG_IPSEC_DEBUG */ + +struct radij_node_head *rnh = NULL; +#ifdef SPINLOCK +spinlock_t eroute_lock = SPIN_LOCK_UNLOCKED; +#else /* SPINLOCK */ +spinlock_t eroute_lock; +#endif /* SPINLOCK */ + +int +ipsec_radijinit(void) +{ + maj_keylen = sizeof (struct sockaddr_encap); + + rj_init(); + + if (rj_inithead((void **)&rnh, /*16*/offsetof(struct sockaddr_encap, sen_type) * sizeof(__u8)) == 0) /* 16 is bit offset of sen_type */ + return -1; + return 0; +} + +int +ipsec_radijcleanup(void) +{ + int error; + + spin_lock_bh(&eroute_lock); + + error = radijcleanup(); + + spin_unlock_bh(&eroute_lock); + + return error; +} + +int +ipsec_cleareroutes(void) +{ + int error; + + spin_lock_bh(&eroute_lock); + + error = radijcleartree(); + + spin_unlock_bh(&eroute_lock); + + return error; +} + +int +ipsec_breakroute(struct sockaddr_encap *eaddr, + struct sockaddr_encap *emask, + struct sk_buff **first, + struct sk_buff **last) +{ + struct eroute *ro; + struct radij_node *rn; + int error; +#ifdef CONFIG_IPSEC_DEBUG + + if (debug_eroute) { + char buf1[SUBNETTOA_BUF], buf2[SUBNETTOA_BUF]; + + subnettoa(eaddr->sen_ip_src, emask->sen_ip_src, 0, buf1, sizeof(buf1)); + subnettoa(eaddr->sen_ip_dst, emask->sen_ip_dst, 0, buf2, sizeof(buf2)); + KLIPS_PRINT(debug_eroute, + "klips_debug:ipsec_breakroute: " + "attempting to delete eroute for %s:%d->%s:%d %d\n", + buf1, ntohs(eaddr->sen_sport), + buf2, ntohs(eaddr->sen_dport), eaddr->sen_proto); + } +#endif /* CONFIG_IPSEC_DEBUG */ + + spin_lock_bh(&eroute_lock); + + if ((error = rj_delete(eaddr, emask, rnh, &rn)) != 0) { + spin_unlock_bh(&eroute_lock); + KLIPS_PRINT(debug_eroute, + "klips_debug:ipsec_breakroute: " + "node not found, eroute delete failed.\n"); + return error; + } + + spin_unlock_bh(&eroute_lock); + + ro = (struct eroute *)rn; + + KLIPS_PRINT(debug_eroute, + "klips_debug:ipsec_breakroute: " + "deleted eroute=0p%p, ident=0p%p->0p%p, first=0p%p, last=0p%p\n", + ro, + ro->er_ident_s.data, + ro->er_ident_d.data, + ro->er_first, + ro->er_last); + + if (ro->er_ident_s.data != NULL) { + kfree(ro->er_ident_s.data); + } + if (ro->er_ident_d.data != NULL) { + kfree(ro->er_ident_d.data); + } + if (ro->er_first != NULL) { +#if 0 + struct net_device_stats *stats = (struct net_device_stats *) &(((struct ipsecpriv *)(ro->er_first->dev->priv))->mystats); + stats->tx_dropped--; +#endif + *first = ro->er_first; + } + if (ro->er_last != NULL) { +#if 0 + struct net_device_stats *stats = (struct net_device_stats *) &(((struct ipsecpriv *)(ro->er_last->dev->priv))->mystats); + stats->tx_dropped--; +#endif + *last = ro->er_last; + } + + if (rn->rj_flags & (RJF_ACTIVE | RJF_ROOT)) + panic ("ipsec_breakroute RMT_DELEROUTE root or active node\n"); + memset((caddr_t)rn, 0, sizeof (struct eroute)); + kfree(rn); + + return 0; +} + +int +ipsec_makeroute(struct sockaddr_encap *eaddr, + struct sockaddr_encap *emask, + struct sa_id said, + uint32_t pid, + struct sk_buff *skb, + struct ident *ident_s, + struct ident *ident_d) +{ + struct eroute *retrt; + int error; + char sa[SATOA_BUF]; + size_t sa_len; +#ifdef CONFIG_IPSEC_DEBUG + + if (debug_eroute) { + { + char buf1[SUBNETTOA_BUF], buf2[SUBNETTOA_BUF]; + + subnettoa(eaddr->sen_ip_src, emask->sen_ip_src, 0, buf1, sizeof(buf1)); + subnettoa(eaddr->sen_ip_dst, emask->sen_ip_dst, 0, buf2, sizeof(buf2)); + sa_len = satoa(said, 0, sa, SATOA_BUF); + KLIPS_PRINT(debug_eroute, + "klips_debug:ipsec_makeroute: " + "attempting to allocate %lu bytes to insert eroute for %s:%d->%s:%d %d, SA: %s, PID:%d, skb=0p%p, ident:%s->%s\n", + (unsigned long) sizeof(struct eroute), + buf1, ntohs(eaddr->sen_sport), + buf2, ntohs(eaddr->sen_dport), + eaddr->sen_proto, + sa_len ? sa : " (error)", + pid, + skb, + (ident_s ? (ident_s->data ? ident_s->data : "NULL") : "NULL"), + (ident_d ? (ident_d->data ? ident_d->data : "NULL") : "NULL")); + } + { + char buf1[sizeof(struct sockaddr_encap)*2 + 1]; + char buf2[sizeof(struct sockaddr_encap)*2 + 1]; + int i; + unsigned char *b1 = buf1, + *b2 = buf2, + *ea = (unsigned char *)eaddr, + *em = (unsigned char *)emask; + + for (i=0; i<sizeof(struct sockaddr_encap); i++) { + sprintf(b1, "%02x", ea[i]); + sprintf(b2, "%02x", em[i]); + b1+=2; + b2+=2; + } + KLIPS_PRINT(debug_eroute, "klips_debug:ipsec_makeroute: %s / %s \n", buf1, buf2); + } + } +#endif /* CONFIG_IPSEC_DEBUG */ + + retrt = (struct eroute *)kmalloc(sizeof (struct eroute), GFP_ATOMIC); + if (retrt == NULL) { + printk("klips_error:ipsec_makeroute: " + "not able to allocate kernel memory"); + return -ENOMEM; + } + memset((caddr_t)retrt, 0, sizeof (struct eroute)); + + retrt->er_eaddr = *eaddr; + retrt->er_emask = *emask; + retrt->er_said = said; + retrt->er_pid = pid; + retrt->er_count = 0; + retrt->er_lasttime = jiffies/HZ; + { + struct sockaddr_encap **rkeyp = (struct sockaddr_encap**)&((retrt->er_rjt).rd_nodes->rj_key); + *rkeyp = &(retrt->er_eaddr); + } + + if (ident_s && ident_s->type != SADB_IDENTTYPE_RESERVED) { + int data_len = ident_s->len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident); + + retrt->er_ident_s.type = ident_s->type; + retrt->er_ident_s.id = ident_s->id; + retrt->er_ident_s.len = ident_s->len; + if(data_len) { + KLIPS_PRINT(debug_eroute, + "klips_debug:ipsec_makeroute: " + "attempting to allocate %u bytes for ident_s.\n", + data_len); + if(!(retrt->er_ident_s.data = kmalloc(data_len, GFP_KERNEL))) { + kfree(retrt); + printk("klips_error:ipsec_makeroute: not able to allocate kernel memory (%d)\n", data_len); + return ENOMEM; + } + memcpy(retrt->er_ident_s.data, ident_s->data, data_len); + } else { + retrt->er_ident_s.data = NULL; + } + } + + if (ident_d && ident_d->type != SADB_IDENTTYPE_RESERVED) { + int data_len = ident_d->len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident); + + retrt->er_ident_d.type = ident_d->type; + retrt->er_ident_d.id = ident_d->id; + retrt->er_ident_d.len = ident_d->len; + if(data_len) { + KLIPS_PRINT(debug_eroute, + "klips_debug:ipsec_makeroute: " + "attempting to allocate %u bytes for ident_d.\n", + data_len); + if(!(retrt->er_ident_d.data = kmalloc(data_len, GFP_KERNEL))) { + if (retrt->er_ident_s.data) + kfree(retrt->er_ident_s.data); + kfree(retrt); + printk("klips_error:ipsec_makeroute: not able to allocate kernel memory (%d)\n", data_len); + return ENOMEM; + } + memcpy(retrt->er_ident_d.data, ident_d->data, data_len); + } else { + retrt->er_ident_d.data = NULL; + } + } + retrt->er_first = skb; + retrt->er_last = NULL; + + KLIPS_PRINT(debug_eroute, + "klips_debug:ipsec_makeroute: " + "calling rj_addroute now\n"); + + spin_lock_bh(&eroute_lock); + + error = rj_addroute(&(retrt->er_eaddr), &(retrt->er_emask), + rnh, retrt->er_rjt.rd_nodes); + + spin_unlock_bh(&eroute_lock); + + if(error) { + sa_len = satoa(said, 0, sa, SATOA_BUF); + KLIPS_PRINT(debug_eroute, + "klips_debug:ipsec_makeroute: " + "rj_addroute not able to insert eroute for SA:%s (error:%d)\n", + sa_len ? sa : " (error)", error); + if (retrt->er_ident_s.data) + kfree(retrt->er_ident_s.data); + if (retrt->er_ident_d.data) + kfree(retrt->er_ident_d.data); + + kfree(retrt); + + return error; + } + +#ifdef CONFIG_IPSEC_DEBUG + if (debug_eroute) { + char buf1[SUBNETTOA_BUF], buf2[SUBNETTOA_BUF]; +/* + subnettoa(eaddr->sen_ip_src, emask->sen_ip_src, 0, buf1, sizeof(buf1)); + subnettoa(eaddr->sen_ip_dst, emask->sen_ip_dst, 0, buf2, sizeof(buf2)); +*/ + subnettoa(rd_key((&(retrt->er_rjt)))->sen_ip_src, rd_mask((&(retrt->er_rjt)))->sen_ip_src, 0, buf1, sizeof(buf1)); + subnettoa(rd_key((&(retrt->er_rjt)))->sen_ip_dst, rd_mask((&(retrt->er_rjt)))->sen_ip_dst, 0, buf2, sizeof(buf2)); + sa_len = satoa(retrt->er_said, 0, sa, SATOA_BUF); + + KLIPS_PRINT(debug_eroute, + "klips_debug:ipsec_makeroute: " + "pid=%05d " + "count=%10d " + "lasttime=%6d " + "%-18s -> %-18s => %s\n", + retrt->er_pid, + retrt->er_count, + (int)(jiffies/HZ - retrt->er_lasttime), + buf1, + buf2, + sa_len ? sa : " (error)"); + } +#endif /* CONFIG_IPSEC_DEBUG */ + KLIPS_PRINT(debug_eroute, + "klips_debug:ipsec_makeroute: " + "succeeded.\n"); + return 0; +} + +struct eroute * +ipsec_findroute(struct sockaddr_encap *eaddr) +{ + struct radij_node *rn; +#ifdef CONFIG_IPSEC_DEBUG + char buf1[ADDRTOA_BUF], buf2[ADDRTOA_BUF]; + + if (debug_radij & DB_RJ_FINDROUTE) { + addrtoa(eaddr->sen_ip_src, 0, buf1, sizeof(buf1)); + addrtoa(eaddr->sen_ip_dst, 0, buf2, sizeof(buf2)); + KLIPS_PRINT(debug_eroute, + "klips_debug:ipsec_findroute: " + "%s:%d->%s:%d %d\n", + buf1, ntohs(eaddr->sen_sport), + buf2, ntohs(eaddr->sen_dport), + eaddr->sen_proto); + } +#endif /* CONFIG_IPSEC_DEBUG */ + rn = rj_match((caddr_t)eaddr, rnh); + if(rn) { + KLIPS_PRINT(debug_eroute && sysctl_ipsec_debug_verbose, + "klips_debug:ipsec_findroute: " + "found, points to proto=%d, spi=%x, dst=%x.\n", + ((struct eroute*)rn)->er_said.proto, + ntohl(((struct eroute*)rn)->er_said.spi), + ntohl(((struct eroute*)rn)->er_said.dst.s_addr)); + } + return (struct eroute *)rn; +} + +#ifdef CONFIG_PROC_FS +/** ipsec_rj_walker_procprint: print one line of eroute table output. + * + * Theoretical BUG: if w->length is less than the length + * of some line we should produce, that line will never + * be finished. In effect, the "file" will stop part way + * through that line. + */ +int +ipsec_rj_walker_procprint(struct radij_node *rn, void *w0) +{ + struct eroute *ro = (struct eroute *)rn; + struct rjtentry *rd = (struct rjtentry *)rn; + struct wsbuf *w = (struct wsbuf *)w0; + char buf1[SUBNETTOA_BUF], buf2[SUBNETTOA_BUF]; + char buf3[16]; + char sa[SATOA_BUF]; + size_t sa_len, buf_len; + struct sockaddr_encap *key, *mask; + + KLIPS_PRINT(debug_radij, + "klips_debug:ipsec_rj_walker_procprint: " + "rn=0p%p, w0=0p%p\n", + rn, + w0); + if (rn->rj_b >= 0) { + return 0; + } + + key = rd_key(rd); + mask = rd_mask(rd); + + if (key == NULL || mask == NULL) { + return 0; + } + + buf_len = subnettoa(key->sen_ip_src, mask->sen_ip_src, 0, buf1, sizeof(buf1)); + if(key->sen_sport != 0) { + sprintf(buf1+buf_len-1, ":%d", ntohs(key->sen_sport)); + } + + buf_len = subnettoa(key->sen_ip_dst, mask->sen_ip_dst, 0, buf2, sizeof(buf2)); + if(key->sen_dport != 0) { + sprintf(buf2+buf_len-1, ":%d", ntohs(key->sen_dport)); + } + + buf3[0]='\0'; + if(key->sen_proto != 0) { + sprintf(buf3, ":%d", key->sen_proto); + } + + sa_len = satoa(ro->er_said, 0, sa, SATOA_BUF); + + w->len += ipsec_snprintf(w->buffer + w->len, + w->length - w->len, + "%-10d " + "%-18s -> %-18s => %s%s\n", + ro->er_count, + buf1, + buf2, + sa_len ? sa : " (error)", + buf3); + + { + /* snprintf can only fill the last character with NUL + * so the maximum useful character is w->length-1. + * However, if w->length == 0, we cannot go back. + * (w->length surely cannot be negative.) + */ + int max_content = w->length > 0? w->length-1 : 0; + + if (w->len >= max_content) { + /* we've done all that can fit -- stop treewalking */ + w->len = max_content; /* truncate crap */ + return -ENOBUFS; + } else { + const off_t pos = w->begin + w->len; /* file position of end of what we've generated */ + + if (pos <= w->offset) { + /* all is before first interesting character: + * discard, but note where we are. + */ + w->len = 0; + w->begin = pos; + } + return 0; + } + } +} +#endif /* CONFIG_PROC_FS */ + +int +ipsec_rj_walker_delete(struct radij_node *rn, void *w0) +{ + struct eroute *ro; + struct rjtentry *rd = (struct rjtentry *)rn; + struct radij_node *rn2; + int error; + struct sockaddr_encap *key, *mask; + + key = rd_key(rd); + mask = rd_mask(rd); + + if(!key || !mask) { + return -ENODATA; + } +#ifdef CONFIG_IPSEC_DEBUG + if(debug_radij) { + char buf1[SUBNETTOA_BUF], buf2[SUBNETTOA_BUF]; + + subnettoa(key->sen_ip_src, mask->sen_ip_src, 0, buf1, sizeof(buf1)); + subnettoa(key->sen_ip_dst, mask->sen_ip_dst, 0, buf2, sizeof(buf2)); + KLIPS_PRINT(debug_radij, + "klips_debug:ipsec_rj_walker_delete: " + "deleting: %s -> %s\n", + buf1, + buf2); + } +#endif /* CONFIG_IPSEC_DEBUG */ + + if((error = rj_delete(key, mask, rnh, &rn2))) { + KLIPS_PRINT(debug_radij, + "klips_debug:ipsec_rj_walker_delete: " + "rj_delete failed with error=%d.\n", error); + return error; + } + + if(rn2 != rn) { + printk("klips_debug:ipsec_rj_walker_delete: " + "tried to delete a different node?!? This should never happen!\n"); + } + + ro = (struct eroute *)rn; + + if (ro->er_ident_s.data) + kfree(ro->er_ident_s.data); + if (ro->er_ident_d.data) + kfree(ro->er_ident_d.data); + + memset((caddr_t)rn, 0, sizeof (struct eroute)); + kfree(rn); + + return 0; +} + diff --git a/linux/net/ipsec/ipsec_rcv.c b/linux/net/ipsec/ipsec_rcv.c new file mode 100644 index 000000000..4df839fe2 --- /dev/null +++ b/linux/net/ipsec/ipsec_rcv.c @@ -0,0 +1,2204 @@ +/* + * receive code + * Copyright (C) 1996, 1997 John Ioannidis. + * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + */ + +char ipsec_rcv_c_version[] = "RCSID $Id: ipsec_rcv.c,v 1.5 2005/04/10 21:38:32 as Exp $"; + +#include <linux/config.h> +#include <linux/version.h> + +#define __NO_VERSION__ +#include <linux/module.h> +#include <linux/kernel.h> /* printk() */ + +#include "freeswan/ipsec_param.h" + +#ifdef MALLOC_SLAB +# include <linux/slab.h> /* kmalloc() */ +#else /* MALLOC_SLAB */ +# include <linux/malloc.h> /* kmalloc() */ +#endif /* MALLOC_SLAB */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/interrupt.h> /* mark_bh */ + +#include <linux/netdevice.h> /* struct device, and other headers */ +#include <linux/etherdevice.h> /* eth_type_trans */ +#include <linux/ip.h> /* struct iphdr */ +#include <linux/skbuff.h> +#include <freeswan.h> +#ifdef SPINLOCK +# ifdef SPINLOCK_23 +# include <linux/spinlock.h> /* *lock* */ +# else /* SPINLOCK_23 */ +# include <asm/spinlock.h> /* *lock* */ +# endif /* SPINLOCK_23 */ +#endif /* SPINLOCK */ +#ifdef NET_21 +# include <asm/uaccess.h> +# include <linux/in6.h> +# define proto_priv cb +#endif /* NET21 */ +#include <asm/checksum.h> +#include <net/ip.h> + +#include "freeswan/radij.h" +#include "freeswan/ipsec_encap.h" +#include "freeswan/ipsec_sa.h" + +#include "freeswan/ipsec_radij.h" +#include "freeswan/ipsec_xform.h" +#include "freeswan/ipsec_tunnel.h" +#include "freeswan/ipsec_rcv.h" + +#if defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH) +#include "freeswan/ipsec_ah.h" +#endif /* defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH) */ + +#ifdef CONFIG_IPSEC_ESP +#include "freeswan/ipsec_esp.h" +#endif /* !CONFIG_IPSEC_ESP */ + +#ifdef CONFIG_IPSEC_IPCOMP +#include "freeswan/ipcomp.h" +#endif /* CONFIG_IPSEC_COMP */ + +#include <pfkeyv2.h> +#include <pfkey.h> + +#include "freeswan/ipsec_proto.h" +#include "freeswan/ipsec_alg.h" + +#ifdef CONFIG_IPSEC_DEBUG +int debug_ah = 0; +int debug_esp = 0; +int debug_rcv = 0; +#endif /* CONFIG_IPSEC_DEBUG */ + +int sysctl_ipsec_inbound_policy_check = 1; + +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL +#include <linux/udp.h> +#endif + +#ifdef CONFIG_IPSEC_DEBUG +static void +rcv_dmp(char *s, caddr_t bb, int len) +{ + int i; + unsigned char *b = bb; + + if (debug_rcv && sysctl_ipsec_debug_verbose) { + printk(KERN_INFO "klips_debug:ipsec_tunnel_:dmp: " + "at %s, len=%d:", + s, + len); + for (i=0; i < len; i++) { + if(!(i%16)){ + printk("\nklips_debug: "); + } + printk(" %02x", *b++); + } + printk("\n"); + } +} +#else /* CONFIG_IPSEC_DEBUG */ +#define rcv_dmp(_x, _y, _z) +#endif /* CONFIG_IPSEC_DEBUG */ + + +#if defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH) +__u32 zeroes[AH_AMAX]; +#endif /* defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH) */ + +/* + * Check-replay-window routine, adapted from the original + * by J. Hughes, from draft-ietf-ipsec-esp-des-md5-03.txt + * + * This is a routine that implements a 64 packet window. This is intend- + * ed on being an implementation sample. + */ + +DEBUG_NO_STATIC int +ipsec_checkreplaywindow(struct ipsec_sa*ipsp, __u32 seq) +{ + __u32 diff; + + if (ipsp->ips_replaywin == 0) /* replay shut off */ + return 1; + if (seq == 0) + return 0; /* first == 0 or wrapped */ + + /* new larger sequence number */ + if (seq > ipsp->ips_replaywin_lastseq) { + return 1; /* larger is good */ + } + diff = ipsp->ips_replaywin_lastseq - seq; + + /* too old or wrapped */ /* if wrapped, kill off SA? */ + if (diff >= ipsp->ips_replaywin) { + return 0; + } + /* this packet already seen */ + if (ipsp->ips_replaywin_bitmap & (1 << diff)) + return 0; + return 1; /* out of order but good */ +} + +DEBUG_NO_STATIC int +ipsec_updatereplaywindow(struct ipsec_sa*ipsp, __u32 seq) +{ + __u32 diff; + + if (ipsp->ips_replaywin == 0) /* replay shut off */ + return 1; + if (seq == 0) + return 0; /* first == 0 or wrapped */ + + /* new larger sequence number */ + if (seq > ipsp->ips_replaywin_lastseq) { + diff = seq - ipsp->ips_replaywin_lastseq; + + /* In win, set bit for this pkt */ + if (diff < ipsp->ips_replaywin) + ipsp->ips_replaywin_bitmap = + (ipsp->ips_replaywin_bitmap << diff) | 1; + else + /* This packet has way larger seq num */ + ipsp->ips_replaywin_bitmap = 1; + + if(seq - ipsp->ips_replaywin_lastseq - 1 > ipsp->ips_replaywin_maxdiff) { + ipsp->ips_replaywin_maxdiff = seq - ipsp->ips_replaywin_lastseq - 1; + } + ipsp->ips_replaywin_lastseq = seq; + return 1; /* larger is good */ + } + diff = ipsp->ips_replaywin_lastseq - seq; + + /* too old or wrapped */ /* if wrapped, kill off SA? */ + if (diff >= ipsp->ips_replaywin) { +/* + if(seq < 0.25*max && ipsp->ips_replaywin_lastseq > 0.75*max) { + ipsec_sa_delchain(ipsp); + } +*/ + return 0; + } + /* this packet already seen */ + if (ipsp->ips_replaywin_bitmap & (1 << diff)) + return 0; + ipsp->ips_replaywin_bitmap |= (1 << diff); /* mark as seen */ + return 1; /* out of order but good */ +} + +#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 +struct auth_alg ipsec_rcv_md5[]={ + {MD5Init, MD5Update, MD5Final, AHMD596_ALEN} +}; + +#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ + +#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 +struct auth_alg ipsec_rcv_sha1[]={ + {SHA1Init, SHA1Update, SHA1Final, AHSHA196_ALEN} +}; +#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ + +enum ipsec_rcv_value { + IPSEC_RCV_LASTPROTO=1, + IPSEC_RCV_OK=0, + IPSEC_RCV_BADPROTO=-1, + IPSEC_RCV_BADLEN=-2, + IPSEC_RCV_ESP_BADALG=-3, + IPSEC_RCV_3DES_BADBLOCKING=-4, + IPSEC_RCV_ESP_DECAPFAIL=-5, + IPSEC_RCV_DECAPFAIL=-6, + IPSEC_RCV_SAIDNOTFOUND=-7, + IPSEC_RCV_IPCOMPALONE=-8, + IPSEC_RCV_IPCOMPFAILED=-10, + IPSEC_RCV_SAIDNOTLIVE=-11, + IPSEC_RCV_FAILEDINBOUND=-12, + IPSEC_RCV_LIFETIMEFAILED=-13, + IPSEC_RCV_BADAUTH=-14, + IPSEC_RCV_REPLAYFAILED=-15, + IPSEC_RCV_AUTHFAILED=-16, + IPSEC_RCV_REPLAYROLLED=-17, + IPSEC_RCV_BAD_DECRYPT=-18 +}; + +struct ipsec_rcv_state { + struct sk_buff *skb; + struct net_device_stats *stats; + struct iphdr *ipp; + struct ipsec_sa *ipsp; + int len; + int ilen; + int authlen; + int hard_header_len; + int iphlen; + struct auth_alg *authfuncs; + struct sa_id said; + char sa[SATOA_BUF]; + size_t sa_len; + __u8 next_header; + __u8 hash[AH_AMAX]; + char ipsaddr_txt[ADDRTOA_BUF]; + char ipdaddr_txt[ADDRTOA_BUF]; + __u8 *octx; + __u8 *ictx; + int ictx_len; + int octx_len; + union { + struct { + struct esphdr *espp; + } espstuff; + struct { + struct ahhdr *ahp; + } ahstuff; + struct { + struct ipcomphdr *compp; + } ipcompstuff; + } protostuff; +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL + __u16 natt_len; + __u16 natt_sport; + __u16 natt_dport; + __u8 natt_type; +#endif +}; + +struct xform_functions { + enum ipsec_rcv_value (*checks)(struct ipsec_rcv_state *irs, + struct sk_buff *skb); + enum ipsec_rcv_value (*decrypt)(struct ipsec_rcv_state *irs); + + enum ipsec_rcv_value (*setup_auth)(struct ipsec_rcv_state *irs, + struct sk_buff *skb, + __u32 *replay, + unsigned char **authenticator); + enum ipsec_rcv_value (*calc_auth)(struct ipsec_rcv_state *irs, + struct sk_buff *skb); +}; + +#ifdef CONFIG_IPSEC_ESP +enum ipsec_rcv_value +ipsec_rcv_esp_checks(struct ipsec_rcv_state *irs, + struct sk_buff *skb) +{ + __u8 proto; + int len; /* packet length */ + + len = skb->len; + proto = irs->ipp->protocol; + + /* XXX this will need to be 8 for IPv6 */ + if ((proto == IPPROTO_ESP) && ((len - irs->iphlen) % 4)) { + printk("klips_error:ipsec_rcv: " + "got packet with content length = %d from %s -- should be on 4 octet boundary, packet dropped\n", + len - irs->iphlen, + irs->ipsaddr_txt); + if(irs->stats) { + irs->stats->rx_errors++; + } + return IPSEC_RCV_BADLEN; + } + + if(skb->len < (irs->hard_header_len + sizeof(struct iphdr) + sizeof(struct esphdr))) { + KLIPS_PRINT(debug_rcv & DB_RX_INAU, + "klips_debug:ipsec_rcv: " + "runt esp packet of skb->len=%d received from %s, dropped.\n", + skb->len, + irs->ipsaddr_txt); + if(irs->stats) { + irs->stats->rx_errors++; + } + return IPSEC_RCV_BADLEN; + } + + irs->protostuff.espstuff.espp = (struct esphdr *)(skb->data + irs->iphlen); + irs->said.spi = irs->protostuff.espstuff.espp->esp_spi; + + return IPSEC_RCV_OK; +} + +enum ipsec_rcv_value +ipsec_rcv_esp_decrypt_setup(struct ipsec_rcv_state *irs, + struct sk_buff *skb, + __u32 *replay, + unsigned char **authenticator) +{ + struct esphdr *espp = irs->protostuff.espstuff.espp; + + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "packet from %s received with seq=%d (iv)=0x%08x%08x iplen=%d esplen=%d sa=%s\n", + irs->ipsaddr_txt, + (__u32)ntohl(espp->esp_rpl), + (__u32)ntohl(*((__u32 *)(espp->esp_iv) )), + (__u32)ntohl(*((__u32 *)(espp->esp_iv) + 1)), + irs->len, + irs->ilen, + irs->sa_len ? irs->sa : " (error)"); + + *replay = ntohl(espp->esp_rpl); + *authenticator = &(skb->data[irs->len - irs->authlen]); + + return IPSEC_RCV_OK; +} + +enum ipsec_rcv_value +ipsec_rcv_esp_authcalc(struct ipsec_rcv_state *irs, + struct sk_buff *skb) +{ + struct auth_alg *aa; + struct esphdr *espp = irs->protostuff.espstuff.espp; + union { + MD5_CTX md5; + SHA1_CTX sha1; + } tctx; + +#ifdef CONFIG_IPSEC_ALG + if (irs->ipsp->ips_alg_auth) { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "ipsec_alg hashing proto=%d... ", + irs->said.proto); + if(irs->said.proto == IPPROTO_ESP) { + ipsec_alg_sa_esp_hash(irs->ipsp, + (caddr_t)espp, irs->ilen, + irs->hash, AHHMAC_HASHLEN); + return IPSEC_RCV_OK; + } + return IPSEC_RCV_BADPROTO; + } +#endif + aa = irs->authfuncs; + + /* copy the initialized keying material */ + memcpy(&tctx, irs->ictx, irs->ictx_len); + + (*aa->update)((void *)&tctx, (caddr_t)espp, irs->ilen); + + (*aa->final)(irs->hash, (void *)&tctx); + + memcpy(&tctx, irs->octx, irs->octx_len); + + (*aa->update)((void *)&tctx, irs->hash, aa->hashlen); + (*aa->final)(irs->hash, (void *)&tctx); + + return IPSEC_RCV_OK; +} + + +enum ipsec_rcv_value +ipsec_rcv_esp_decrypt(struct ipsec_rcv_state *irs) +{ + struct ipsec_sa *ipsp = irs->ipsp; + struct esphdr *espp = irs->protostuff.espstuff.espp; + int esphlen = 0; + __u8 *idat; /* pointer to content to be decrypted/authenticated */ +#ifdef CONFIG_IPSEC_ENC_3DES + __u32 iv[2]; +#endif /* !CONFIG_IPSEC_ENC_3DES */ + int pad = 0, padlen; + int badpad = 0; + int i; + struct sk_buff *skb; +#ifdef CONFIG_IPSEC_ALG + struct ipsec_alg_enc *ixt_e=NULL; +#endif /* CONFIG_IPSEC_ALG */ + + skb=irs->skb; + + idat = skb->data + irs->iphlen; + +#ifdef CONFIG_IPSEC_ALG + if ((ixt_e=ipsp->ips_alg_enc)) { + esphlen = ESP_HEADER_LEN + ixt_e->ixt_ivlen/8; + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "encalg=%d esphlen=%d\n", + ipsp->ips_encalg, esphlen); + } else +#endif /* CONFIG_IPSEC_ALG */ + switch(ipsp->ips_encalg) { +#ifdef CONFIG_IPSEC_ENC_3DES + case ESP_3DES: + iv[0] = *((__u32 *)(espp->esp_iv) ); + iv[1] = *((__u32 *)(espp->esp_iv) + 1); + esphlen = sizeof(struct esphdr); + break; +#endif /* !CONFIG_IPSEC_ENC_3DES */ + default: + ipsp->ips_errs.ips_alg_errs += 1; + if(irs->stats) { + irs->stats->rx_errors++; + } + return IPSEC_RCV_ESP_BADALG; + } + + idat += esphlen; + irs->ilen -= esphlen; + +#ifdef CONFIG_IPSEC_ALG + if (ixt_e) + { + if (ipsec_alg_esp_encrypt(ipsp, + idat, irs->ilen, espp->esp_iv, + IPSEC_ALG_DECRYPT) <= 0) + { + printk("klips_error:ipsec_rcv: " + "got packet with esplen = %d " + "from %s -- should be on " + "ENC(%d) octet boundary, " + "packet dropped\n", + irs->ilen, + irs->ipsaddr_txt, + ipsp->ips_encalg); + if(irs->stats) { + irs->stats->rx_errors++; + } + return IPSEC_RCV_BAD_DECRYPT; + } + } else +#endif /* CONFIG_IPSEC_ALG */ + switch(ipsp->ips_encalg) { +#ifdef CONFIG_IPSEC_ENC_3DES + case ESP_3DES: + if ((irs->ilen) % 8) { + ipsp->ips_errs.ips_encsize_errs += 1; + printk("klips_error:ipsec_rcv: " + "got packet with esplen = %d from %s -- should be on 8 octet boundary, packet dropped\n", + irs->ilen, + irs->ipsaddr_txt); + if(irs->stats) { + irs->stats->rx_errors++; + } + return IPSEC_RCV_3DES_BADBLOCKING; + } + des_ede3_cbc_encrypt((des_cblock *)idat, + (des_cblock *)idat, + irs->ilen, + ((struct des_eks *)(ipsp->ips_key_e))[0].ks, + ((struct des_eks *)(ipsp->ips_key_e))[1].ks, + ((struct des_eks *)(ipsp->ips_key_e))[2].ks, + (des_cblock *)iv, 0); + break; +#endif /* !CONFIG_IPSEC_ENC_3DES */ + } + + rcv_dmp("postdecrypt", skb->data, skb->len); + + irs->next_header = idat[irs->ilen - 1]; + padlen = idat[irs->ilen - 2]; + pad = padlen + 2 + irs->authlen; + + KLIPS_PRINT(debug_rcv & DB_RX_IPAD, + "klips_debug:ipsec_rcv: " + "padlen=%d, contents: 0x<offset>: 0x<value> 0x<value> ...\n", + padlen); + + for (i = 1; i <= padlen; i++) { + if((i % 16) == 1) { + KLIPS_PRINT(debug_rcv & DB_RX_IPAD, + "klips_debug: %02x:", + i - 1); + } + KLIPS_PRINTMORE(debug_rcv & DB_RX_IPAD, + " %02x", + idat[irs->ilen - 2 - padlen + i - 1]); + if(i != idat[irs->ilen - 2 - padlen + i - 1]) { + badpad = 1; + } + if((i % 16) == 0) { + KLIPS_PRINTMORE(debug_rcv & DB_RX_IPAD, + "\n"); + } + } + if((i % 16) != 1) { + KLIPS_PRINTMORE(debug_rcv & DB_RX_IPAD, + "\n"); + } + if(badpad) { + KLIPS_PRINT(debug_rcv & DB_RX_IPAD, + "klips_debug:ipsec_rcv: " + "warning, decrypted packet from %s has bad padding\n", + irs->ipsaddr_txt); + KLIPS_PRINT(debug_rcv & DB_RX_IPAD, + "klips_debug:ipsec_rcv: " + "...may be bad decryption -- not dropped\n"); + ipsp->ips_errs.ips_encpad_errs += 1; + } + + KLIPS_PRINT(debug_rcv & DB_RX_IPAD, + "klips_debug:ipsec_rcv: " + "packet decrypted from %s: next_header = %d, padding = %d\n", + irs->ipsaddr_txt, + irs->next_header, + pad - 2 - irs->authlen); + + irs->ipp->tot_len = htons(ntohs(irs->ipp->tot_len) - (esphlen + pad)); + + /* + * move the IP header forward by the size of the ESP header, which + * will remove the the ESP header from the packet. + */ + memmove((void *)(skb->data + esphlen), + (void *)(skb->data), irs->iphlen); + + rcv_dmp("esp postmove", skb->data, skb->len); + + /* skb_pull below, will move up by esphlen */ + + /* XXX not clear how this can happen, as the message indicates */ + if(skb->len < esphlen) { + printk(KERN_WARNING + "klips_error:ipsec_rcv: " + "tried to skb_pull esphlen=%d, %d available. This should never happen, please report.\n", + esphlen, (int)(skb->len)); + return IPSEC_RCV_ESP_DECAPFAIL; + } + skb_pull(skb, esphlen); + + irs->ipp = (struct iphdr *)skb->data; + + rcv_dmp("esp postpull", skb->data, skb->len); + + /* now, trip off the padding from the end */ + KLIPS_PRINT(debug_rcv & DB_RX_PKTRX, + "klips_debug:ipsec_rcv: " + "trimming to %d.\n", + irs->len - esphlen - pad); + if(pad + esphlen <= irs->len) { + skb_trim(skb, irs->len - esphlen - pad); + } else { + KLIPS_PRINT(debug_rcv & DB_RX_PKTRX, + "klips_debug:ipsec_rcv: " + "bogus packet, size is zero or negative, dropping.\n"); + return IPSEC_RCV_DECAPFAIL; + } + + return IPSEC_RCV_OK; +} + + +struct xform_functions esp_rcv_funcs[]={ + { checks: ipsec_rcv_esp_checks, + setup_auth: ipsec_rcv_esp_decrypt_setup, + calc_auth: ipsec_rcv_esp_authcalc, + decrypt: ipsec_rcv_esp_decrypt, + }, +}; +#endif /* !CONFIG_IPSEC_ESP */ + +#ifdef CONFIG_IPSEC_AH +enum ipsec_rcv_value +ipsec_rcv_ah_checks(struct ipsec_rcv_state *irs, + struct sk_buff *skb) +{ + int ahminlen; + + ahminlen = irs->hard_header_len + sizeof(struct iphdr); + + /* take care not to deref this pointer until we check the minlen though */ + irs->protostuff.ahstuff.ahp = (struct ahhdr *) (skb->data + irs->iphlen); + + if((skb->len < ahminlen+sizeof(struct ahhdr)) || + (skb->len < ahminlen+(irs->protostuff.ahstuff.ahp->ah_hl << 2))) { + KLIPS_PRINT(debug_rcv & DB_RX_INAU, + "klips_debug:ipsec_rcv: " + "runt ah packet of skb->len=%d received from %s, dropped.\n", + skb->len, + irs->ipsaddr_txt); + if(irs->stats) { + irs->stats->rx_errors++; + } + return IPSEC_RCV_BADLEN; + } + + irs->said.spi = irs->protostuff.ahstuff.ahp->ah_spi; + + /* XXX we only support the one 12-byte authenticator for now */ + if(irs->protostuff.ahstuff.ahp->ah_hl != ((AHHMAC_HASHLEN+AHHMAC_RPLLEN) >> 2)) { + KLIPS_PRINT(debug_rcv & DB_RX_INAU, + "klips_debug:ipsec_rcv: " + "bad authenticator length %ld, expected %lu from %s.\n", + (long)(irs->protostuff.ahstuff.ahp->ah_hl << 2), + (unsigned long) sizeof(struct ahhdr), + irs->ipsaddr_txt); + if(irs->stats) { + irs->stats->rx_errors++; + } + return IPSEC_RCV_BADLEN; + } + + return IPSEC_RCV_OK; +} + + +enum ipsec_rcv_value +ipsec_rcv_ah_setup_auth(struct ipsec_rcv_state *irs, + struct sk_buff *skb, + __u32 *replay, + unsigned char **authenticator) +{ + struct ahhdr *ahp = irs->protostuff.ahstuff.ahp; + + *replay = ntohl(ahp->ah_rpl); + *authenticator = ahp->ah_data; + + return IPSEC_RCV_OK; +} + +enum ipsec_rcv_value +ipsec_rcv_ah_authcalc(struct ipsec_rcv_state *irs, + struct sk_buff *skb) +{ + struct auth_alg *aa; + struct ahhdr *ahp = irs->protostuff.ahstuff.ahp; + union { + MD5_CTX md5; + SHA1_CTX sha1; + } tctx; + struct iphdr ipo; + int ahhlen; + + aa = irs->authfuncs; + + /* copy the initialized keying material */ + memcpy(&tctx, irs->ictx, irs->ictx_len); + + ipo = *irs->ipp; + ipo.tos = 0; /* mutable RFC 2402 3.3.3.1.1.1 */ + ipo.frag_off = 0; + ipo.ttl = 0; + ipo.check = 0; + + + /* do the sanitized header */ + (*aa->update)((void*)&tctx, (caddr_t)&ipo, sizeof(struct iphdr)); + + /* XXX we didn't do the options here! */ + + /* now do the AH header itself */ + ahhlen = AH_BASIC_LEN + (ahp->ah_hl << 2); + (*aa->update)((void*)&tctx, (caddr_t)ahp, ahhlen - AHHMAC_HASHLEN); + + /* now, do some zeroes */ + (*aa->update)((void*)&tctx, (caddr_t)zeroes, AHHMAC_HASHLEN); + + /* finally, do the packet contents themselves */ + (*aa->update)((void*)&tctx, + (caddr_t)skb->data + irs->iphlen + ahhlen, + skb->len - irs->iphlen - ahhlen); + + (*aa->final)(irs->hash, (void *)&tctx); + + memcpy(&tctx, irs->octx, irs->octx_len); + + (*aa->update)((void *)&tctx, irs->hash, aa->hashlen); + (*aa->final)(irs->hash, (void *)&tctx); + + return IPSEC_RCV_OK; +} + +enum ipsec_rcv_value +ipsec_rcv_ah_decap(struct ipsec_rcv_state *irs) +{ + struct ahhdr *ahp = irs->protostuff.ahstuff.ahp; + struct sk_buff *skb; + int ahhlen; + + skb=irs->skb; + + ahhlen = AH_BASIC_LEN + (ahp->ah_hl << 2); + + irs->ipp->tot_len = htons(ntohs(irs->ipp->tot_len) - ahhlen); + irs->next_header = ahp->ah_nh; + + /* + * move the IP header forward by the size of the AH header, which + * will remove the the AH header from the packet. + */ + memmove((void *)(skb->data + ahhlen), + (void *)(skb->data), irs->iphlen); + + rcv_dmp("ah postmove", skb->data, skb->len); + + /* skb_pull below, will move up by ahhlen */ + + /* XXX not clear how this can happen, as the message indicates */ + if(skb->len < ahhlen) { + printk(KERN_WARNING + "klips_error:ipsec_rcv: " + "tried to skb_pull ahhlen=%d, %d available. This should never happen, please report.\n", + ahhlen, + (int)(skb->len)); + return IPSEC_RCV_DECAPFAIL; + } + skb_pull(skb, ahhlen); + + irs->ipp = (struct iphdr *)skb->data; + + rcv_dmp("ah postpull", skb->data, skb->len); + + return IPSEC_RCV_OK; +} + + +struct xform_functions ah_rcv_funcs[]={ + { checks: ipsec_rcv_ah_checks, + setup_auth: ipsec_rcv_ah_setup_auth, + calc_auth: ipsec_rcv_ah_authcalc, + decrypt: ipsec_rcv_ah_decap, + }, +}; + +#endif /* CONFIG_IPSEC_AH */ + +#ifdef CONFIG_IPSEC_IPCOMP +enum ipsec_rcv_value +ipsec_rcv_ipcomp_checks(struct ipsec_rcv_state *irs, + struct sk_buff *skb) +{ + int ipcompminlen; + + ipcompminlen = irs->hard_header_len + sizeof(struct iphdr); + + if(skb->len < (ipcompminlen + sizeof(struct ipcomphdr))) { + KLIPS_PRINT(debug_rcv & DB_RX_INAU, + "klips_debug:ipsec_rcv: " + "runt comp packet of skb->len=%d received from %s, dropped.\n", + skb->len, + irs->ipsaddr_txt); + if(irs->stats) { + irs->stats->rx_errors++; + } + return IPSEC_RCV_BADLEN; + } + + irs->protostuff.ipcompstuff.compp = (struct ipcomphdr *)(skb->data + irs->iphlen); + irs->said.spi = htonl((__u32)ntohs(irs->protostuff.ipcompstuff.compp->ipcomp_cpi)); + return IPSEC_RCV_OK; +} + +enum ipsec_rcv_value +ipsec_rcv_ipcomp_decomp(struct ipsec_rcv_state *irs) +{ + unsigned int flags = 0; + struct ipsec_sa *ipsp = irs->ipsp; + struct sk_buff *skb; + + skb=irs->skb; + + rcv_dmp("ipcomp", skb->data, skb->len); + + if(ipsp == NULL) { + return IPSEC_RCV_SAIDNOTFOUND; + } + +#if 0 + /* we want to check that this wasn't the first SA on the list, because + * we don't support bare IPCOMP, for unexplained reasons. MCR + */ + if (ipsp->ips_onext != NULL) { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "Incoming packet with outer IPCOMP header SA:%s: not yet supported by KLIPS, dropped\n", + irs->sa_len ? irs->sa : " (error)"); + if(irs->stats) { + irs->stats->rx_dropped++; + } + + return IPSEC_RCV_IPCOMPALONE; + } +#endif + + if(sysctl_ipsec_inbound_policy_check && + ((((ntohl(ipsp->ips_said.spi) & 0x0000ffff) != ntohl(irs->said.spi)) && + (ipsp->ips_encalg != ntohl(irs->said.spi)) /* this is a workaround for peer non-compliance with rfc2393 */ + ))) { + char sa2[SATOA_BUF]; + size_t sa_len2 = 0; + + sa_len2 = satoa(ipsp->ips_said, 0, sa2, SATOA_BUF); + + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "Incoming packet with SA(IPCA):%s does not match policy SA(IPCA):%s cpi=%04x cpi->spi=%08x spi=%08x, spi->cpi=%04x for SA grouping, dropped.\n", + irs->sa_len ? irs->sa : " (error)", + ipsp != NULL ? (sa_len2 ? sa2 : " (error)") : "NULL", + ntohs(irs->protostuff.ipcompstuff.compp->ipcomp_cpi), + (__u32)ntohl(irs->said.spi), + ipsp != NULL ? (__u32)ntohl((ipsp->ips_said.spi)) : 0, + ipsp != NULL ? (__u16)(ntohl(ipsp->ips_said.spi) & 0x0000ffff) : 0); + if(irs->stats) { + irs->stats->rx_dropped++; + } + return IPSEC_RCV_SAIDNOTFOUND; + } + + ipsp->ips_comp_ratio_cbytes += ntohs(irs->ipp->tot_len); + irs->next_header = irs->protostuff.ipcompstuff.compp->ipcomp_nh; + + skb = skb_decompress(skb, ipsp, &flags); + if (!skb || flags) { + spin_unlock(&tdb_lock); + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "skb_decompress() returned error flags=%x, dropped.\n", + flags); + if (irs->stats) { + if (flags) + irs->stats->rx_errors++; + else + irs->stats->rx_dropped++; + } + return IPSEC_RCV_IPCOMPFAILED; + } + + /* make sure we update the pointer */ + irs->skb = skb; + +#ifdef NET_21 + irs->ipp = skb->nh.iph; +#else /* NET_21 */ + irs->ipp = skb->ip_hdr; +#endif /* NET_21 */ + + ipsp->ips_comp_ratio_dbytes += ntohs(irs->ipp->tot_len); + + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "packet decompressed SA(IPCA):%s cpi->spi=%08x spi=%08x, spi->cpi=%04x, nh=%d.\n", + irs->sa_len ? irs->sa : " (error)", + (__u32)ntohl(irs->said.spi), + ipsp != NULL ? (__u32)ntohl((ipsp->ips_said.spi)) : 0, + ipsp != NULL ? (__u16)(ntohl(ipsp->ips_said.spi) & 0x0000ffff) : 0, + irs->next_header); + KLIPS_IP_PRINT(debug_rcv & DB_RX_PKTRX, irs->ipp); + + return IPSEC_RCV_OK; +} + + +struct xform_functions ipcomp_rcv_funcs[]={ + {checks: ipsec_rcv_ipcomp_checks, + decrypt: ipsec_rcv_ipcomp_decomp, + }, +}; + +#endif /* CONFIG_IPSEC_IPCOMP */ + +enum ipsec_rcv_value +ipsec_rcv_decap_once(struct ipsec_rcv_state *irs) +{ + int iphlen; + unsigned char *dat; + __u8 proto; + struct in_addr ipsaddr; + struct in_addr ipdaddr; + int replay = 0; /* replay value in AH or ESP packet */ + struct ipsec_sa* ipsnext = NULL; /* next SA towards inside of packet */ + struct xform_functions *proto_funcs; + struct ipsec_sa *newipsp; + struct iphdr *ipp; + struct sk_buff *skb; +#ifdef CONFIG_IPSEC_ALG + struct ipsec_alg_auth *ixt_a=NULL; +#endif /* CONFIG_IPSEC_ALG */ + + skb = irs->skb; + irs->len = skb->len; + dat = skb->data; + ipp = irs->ipp; + proto = ipp->protocol; + ipsaddr.s_addr = ipp->saddr; + addrtoa(ipsaddr, 0, irs->ipsaddr_txt, sizeof(irs->ipsaddr_txt)); + ipdaddr.s_addr = ipp->daddr; + addrtoa(ipdaddr, 0, irs->ipdaddr_txt, sizeof(irs->ipdaddr_txt)); + + iphlen = ipp->ihl << 2; + irs->iphlen=iphlen; + ipp->check = 0; /* we know the sum is good */ + + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv_decap_once: " + "decap (%d) from %s -> %s\n", + proto, irs->ipsaddr_txt, irs->ipdaddr_txt); + + switch(proto) { +#ifdef CONFIG_IPSEC_ESP + case IPPROTO_ESP: + proto_funcs = esp_rcv_funcs; + break; +#endif /* !CONFIG_IPSEC_ESP */ + +#ifdef CONFIG_IPSEC_AH + case IPPROTO_AH: + proto_funcs = ah_rcv_funcs; + break; +#endif /* !CONFIG_IPSEC_AH */ + +#ifdef CONFIG_IPSEC_IPCOMP + case IPPROTO_COMP: + proto_funcs = ipcomp_rcv_funcs; + break; +#endif /* !CONFIG_IPSEC_IPCOMP */ + default: + if(irs->stats) { + irs->stats->rx_errors++; + } + return IPSEC_RCV_BADPROTO; + } + + /* + * Find tunnel control block and (indirectly) call the + * appropriate tranform routine. The resulting sk_buf + * is a valid IP packet ready to go through input processing. + */ + + irs->said.dst.s_addr = ipp->daddr; + + if(proto_funcs->checks) { + enum ipsec_rcv_value retval = (*proto_funcs->checks)(irs, skb); + + if(retval < 0) { + return retval; + } + } + + irs->said.proto = proto; + irs->sa_len = satoa(irs->said, 0, irs->sa, SATOA_BUF); + if(irs->sa_len == 0) { + strcpy(irs->sa, "(error)"); + } + + newipsp = ipsec_sa_getbyid(&irs->said); + if (newipsp == NULL) { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "no ipsec_sa for SA:%s: incoming packet with no SA dropped\n", + irs->sa_len ? irs->sa : " (error)"); + if(irs->stats) { + irs->stats->rx_dropped++; + } + return IPSEC_RCV_SAIDNOTFOUND; + } + + /* MCR - XXX this is bizarre. ipsec_sa_getbyid returned it, having incremented the refcount, + * why in the world would we decrement it here? + + ipsec_sa_put(irs->ipsp);*/ /* incomplete */ + + /* If it is in larval state, drop the packet, we cannot process yet. */ + if(newipsp->ips_state == SADB_SASTATE_LARVAL) { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "ipsec_sa in larval state, cannot be used yet, dropping packet.\n"); + if(irs->stats) { + irs->stats->rx_dropped++; + } + ipsec_sa_put(newipsp); + return IPSEC_RCV_SAIDNOTLIVE; + } + + if(newipsp->ips_state == SADB_SASTATE_DEAD) { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "ipsec_sa in dead state, cannot be used any more, dropping packet.\n"); + if(irs->stats) { + irs->stats->rx_dropped++; + } + ipsec_sa_put(newipsp); + return IPSEC_RCV_SAIDNOTLIVE; + } + + if(sysctl_ipsec_inbound_policy_check) { + if(irs->ipp->saddr != ((struct sockaddr_in*)(newipsp->ips_addr_s))->sin_addr.s_addr) { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "SA:%s, src=%s of pkt does not agree with expected SA source address policy.\n", + irs->sa_len ? irs->sa : " (error)", + irs->ipsaddr_txt); + if(irs->stats) { + irs->stats->rx_dropped++; + } + ipsec_sa_put(newipsp); + return IPSEC_RCV_FAILEDINBOUND; + } + + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "SA:%s, src=%s of pkt agrees with expected SA source address policy.\n", + irs->sa_len ? irs->sa : " (error)", + irs->ipsaddr_txt); + + /* + * at this point, we have looked up a new SA, and we want to make sure that if this + * isn't the first SA in the list, that the previous SA actually points at this one. + */ + if(irs->ipsp) { + if(irs->ipsp->ips_inext != newipsp) { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "unexpected SA:%s: does not agree with ips->inext policy, dropped\n", + irs->sa_len ? irs->sa : " (error)"); + if(irs->stats) { + irs->stats->rx_dropped++; + } + ipsec_sa_put(newipsp); + return IPSEC_RCV_FAILEDINBOUND; + } + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "SA:%s grouping from previous SA is OK.\n", + irs->sa_len ? irs->sa : " (error)"); + } else { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "SA:%s First SA in group.\n", + irs->sa_len ? irs->sa : " (error)"); + } + + /* + * previously, at this point, we checked if the back pointer from the new SA that + * we just found matched the back pointer. But, we won't do this check anymore, + * because we want to be able to nest SAs + */ +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "natt_type=%u tdbp->ips_natt_type=%u : %s\n", + irs->natt_type, newipsp->ips_natt_type, + (irs->natt_type==newipsp->ips_natt_type)?"ok":"bad"); + if (irs->natt_type != newipsp->ips_natt_type) { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "SA:%s does not agree with expected NAT-T policy.\n", + irs->sa_len ? irs->sa : " (error)"); + if(irs->stats) { + irs->stats->rx_dropped++; + } + ipsec_sa_put(newipsp); + return IPSEC_RCV_FAILEDINBOUND; + } +#endif + } + + /* okay, SA checks out, so free any previous SA, and record a new one */ + + if(irs->ipsp) { + ipsec_sa_put(irs->ipsp); + } + irs->ipsp=newipsp; + + /* note that the outer code will free the irs->ipsp if there is an error */ + + + /* now check the lifetimes */ + if(ipsec_lifetime_check(&irs->ipsp->ips_life.ipl_bytes, "bytes", irs->sa, + ipsec_life_countbased, ipsec_incoming, irs->ipsp) == ipsec_life_harddied || + ipsec_lifetime_check(&irs->ipsp->ips_life.ipl_addtime, "addtime",irs->sa, + ipsec_life_timebased, ipsec_incoming, irs->ipsp) == ipsec_life_harddied || + ipsec_lifetime_check(&irs->ipsp->ips_life.ipl_addtime, "usetime",irs->sa, + ipsec_life_timebased, ipsec_incoming, irs->ipsp) == ipsec_life_harddied || + ipsec_lifetime_check(&irs->ipsp->ips_life.ipl_packets, "packets",irs->sa, + ipsec_life_countbased, ipsec_incoming, irs->ipsp) == ipsec_life_harddied) { + ipsec_sa_delchain(irs->ipsp); + if(irs->stats) { + irs->stats->rx_dropped++; + } + + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv_decap_once: " + "decap (%d) failed lifetime check\n", + proto); + + return IPSEC_RCV_LIFETIMEFAILED; + } + +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL + if ((irs->natt_type) && + ( (irs->ipp->saddr != (((struct sockaddr_in*)(newipsp->ips_addr_s))->sin_addr.s_addr)) || + (irs->natt_sport != newipsp->ips_natt_sport) + )) { + struct sockaddr sipaddr; + /** Advertise NAT-T addr change to pluto **/ + sipaddr.sa_family = AF_INET; + ((struct sockaddr_in*)&sipaddr)->sin_addr.s_addr = irs->ipp->saddr; + ((struct sockaddr_in*)&sipaddr)->sin_port = htons(irs->natt_sport); + pfkey_nat_t_new_mapping(newipsp, &sipaddr, irs->natt_sport); + /** + * Then allow or block packet depending on + * sysctl_ipsec_inbound_policy_check. + * + * In all cases, pluto will update SA if new mapping is + * accepted. + */ + if (sysctl_ipsec_inbound_policy_check) { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "SA:%s, src=%s:%u of pkt does not agree with expected " + "SA source address policy (pluto has been informed).\n", + irs->sa_len ? irs->sa : " (error)", + irs->ipsaddr_txt, irs->natt_sport); + if(irs->stats) { + irs->stats->rx_dropped++; + } + ipsec_sa_put(newipsp); + return IPSEC_RCV_FAILEDINBOUND; + } + } +#endif + + irs->authfuncs=NULL; + /* authenticate, if required */ +#ifdef CONFIG_IPSEC_ALG + if ((ixt_a=irs->ipsp->ips_alg_auth)) { + irs->authlen = AHHMAC_HASHLEN; + irs->authfuncs = NULL; + irs->ictx = NULL; + irs->octx = NULL; + irs->ictx_len = 0; + irs->octx_len = 0; + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "authalg=%d authlen=%d\n", + irs->ipsp->ips_authalg, + irs->authlen); + } else +#endif /* CONFIG_IPSEC_ALG */ + switch(irs->ipsp->ips_authalg) { +#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 + case AH_MD5: + irs->authlen = AHHMAC_HASHLEN; + irs->authfuncs = ipsec_rcv_md5; + irs->ictx = (void *)&((struct md5_ctx*)(irs->ipsp->ips_key_a))->ictx; + irs->octx = (void *)&((struct md5_ctx*)(irs->ipsp->ips_key_a))->octx; + irs->ictx_len = sizeof(((struct md5_ctx*)(irs->ipsp->ips_key_a))->ictx); + irs->octx_len = sizeof(((struct md5_ctx*)(irs->ipsp->ips_key_a))->octx); + break; +#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ +#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 + case AH_SHA: + irs->authlen = AHHMAC_HASHLEN; + irs->authfuncs = ipsec_rcv_sha1; + irs->ictx = (void *)&((struct sha1_ctx*)(irs->ipsp->ips_key_a))->ictx; + irs->octx = (void *)&((struct sha1_ctx*)(irs->ipsp->ips_key_a))->octx; + irs->ictx_len = sizeof(((struct sha1_ctx*)(irs->ipsp->ips_key_a))->ictx); + irs->octx_len = sizeof(((struct sha1_ctx*)(irs->ipsp->ips_key_a))->octx); + break; +#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ + case AH_NONE: + irs->authlen = 0; + irs->authfuncs = NULL; + irs->ictx = NULL; + irs->octx = NULL; + irs->ictx_len = 0; + irs->octx_len = 0; + + break; + default: + irs->ipsp->ips_errs.ips_alg_errs += 1; + if(irs->stats) { + irs->stats->rx_errors++; + } + return IPSEC_RCV_BADAUTH; + } + + irs->ilen = irs->len - iphlen - irs->authlen; + if(irs->ilen <= 0) { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "runt %s packet with no data, dropping.\n", + (proto == IPPROTO_ESP ? "esp" : "ah")); + if(irs->stats) { + irs->stats->rx_dropped++; + } + return IPSEC_RCV_BADLEN; + } + +#ifdef CONFIG_IPSEC_ALG + if(irs->authfuncs || ixt_a) { +#else + if(irs->authfuncs) { +#endif + unsigned char *authenticator = NULL; + + if(proto_funcs->setup_auth) { + enum ipsec_rcv_value retval + = (*proto_funcs->setup_auth)(irs, skb, + &replay, + &authenticator); + if(retval < 0) { + return retval; + } + } + + if(!authenticator) { + irs->ipsp->ips_errs.ips_auth_errs += 1; + if(irs->stats) { + irs->stats->rx_dropped++; + } + return IPSEC_RCV_BADAUTH; + } + + if(!ipsec_checkreplaywindow(irs->ipsp, replay)) { + irs->ipsp->ips_errs.ips_replaywin_errs += 1; + KLIPS_PRINT(debug_rcv & DB_RX_REPLAY, + "klips_debug:ipsec_rcv: " + "duplicate frame from %s, packet dropped\n", + irs->ipsaddr_txt); + if(irs->stats) { + irs->stats->rx_dropped++; + } + return IPSEC_RCV_REPLAYFAILED; + } + + /* + * verify authenticator + */ + + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "encalg = %d, authalg = %d.\n", + irs->ipsp->ips_encalg, + irs->ipsp->ips_authalg); + + /* calculate authenticator */ + if(proto_funcs->calc_auth == NULL) { + return IPSEC_RCV_BADAUTH; + } + (*proto_funcs->calc_auth)(irs, skb); + + if (memcmp(irs->hash, authenticator, irs->authlen)) { + irs->ipsp->ips_errs.ips_auth_errs += 1; + KLIPS_PRINT(debug_rcv & DB_RX_INAU, + "klips_debug:ipsec_rcv: " + "auth failed on incoming packet from %s: hash=%08x%08x%08x auth=%08x%08x%08x, dropped\n", + irs->ipsaddr_txt, + ntohl(*(__u32*)&irs->hash[0]), + ntohl(*(__u32*)&irs->hash[4]), + ntohl(*(__u32*)&irs->hash[8]), + ntohl(*(__u32*)authenticator), + ntohl(*((__u32*)authenticator + 1)), + ntohl(*((__u32*)authenticator + 2))); + if(irs->stats) { + irs->stats->rx_dropped++; + } + return IPSEC_RCV_AUTHFAILED; + } else { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "authentication successful.\n"); + } + + /* Crypto hygiene: clear memory used to calculate autheticator. + * The length varies with the algorithm. + */ + memset(irs->hash, 0, irs->authlen); + + /* If the sequence number == 0, expire SA, it had rolled */ + if(irs->ipsp->ips_replaywin && !replay /* !irs->ipsp->ips_replaywin_lastseq */) { + ipsec_sa_delchain(irs->ipsp); + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "replay window counter rolled, expiring SA.\n"); + if(irs->stats) { + irs->stats->rx_dropped++; + } + return IPSEC_RCV_REPLAYROLLED; + } + + /* now update the replay counter */ + if (!ipsec_updatereplaywindow(irs->ipsp, replay)) { + irs->ipsp->ips_errs.ips_replaywin_errs += 1; + KLIPS_PRINT(debug_rcv & DB_RX_REPLAY, + "klips_debug:ipsec_rcv: " + "duplicate frame from %s, packet dropped\n", + irs->ipsaddr_txt); + if(irs->stats) { + irs->stats->rx_dropped++; + } + return IPSEC_RCV_REPLAYROLLED; + } + } + + if(proto_funcs->decrypt) { + enum ipsec_rcv_value retval = + (*proto_funcs->decrypt)(irs); + + if(retval != IPSEC_RCV_OK) { + return retval; + } + } + + /* + * Adjust pointers + */ + skb = irs->skb; + irs->len = skb->len; + dat = skb->data; + +#ifdef NET_21 +/* skb->h.ipiph=(struct iphdr *)skb->data; */ + skb->nh.raw = skb->data; + skb->h.raw = skb->nh.raw + (skb->nh.iph->ihl << 2); + + memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); +#else /* NET_21 */ + skb->h.iph=(struct iphdr *)skb->data; + skb->ip_hdr=(struct iphdr *)skb->data; + memset(skb->proto_priv, 0, sizeof(struct options)); +#endif /* NET_21 */ + + ipp = (struct iphdr *)dat; + ipsaddr.s_addr = ipp->saddr; + addrtoa(ipsaddr, 0, irs->ipsaddr_txt, sizeof(irs->ipsaddr_txt)); + ipdaddr.s_addr = ipp->daddr; + addrtoa(ipdaddr, 0, irs->ipdaddr_txt, sizeof(irs->ipdaddr_txt)); + /* + * Discard the original ESP/AH header + */ + ipp->protocol = irs->next_header; + + ipp->check = 0; /* NOTE: this will be included in checksum */ + ipp->check = ip_fast_csum((unsigned char *)dat, iphlen >> 2); + + KLIPS_PRINT(debug_rcv & DB_RX_PKTRX, + "klips_debug:ipsec_rcv: " + "after <%s%s%s>, SA:%s:\n", + IPS_XFORM_NAME(irs->ipsp), + irs->sa_len ? irs->sa : " (error)"); + KLIPS_IP_PRINT(debug_rcv & DB_RX_PKTRX, ipp); + + skb->protocol = htons(ETH_P_IP); + skb->ip_summed = 0; + + ipsnext = irs->ipsp->ips_inext; + if(sysctl_ipsec_inbound_policy_check) { + if(ipsnext) { + if( + ipp->protocol != IPPROTO_AH + && ipp->protocol != IPPROTO_ESP +#ifdef CONFIG_IPSEC_IPCOMP + && ipp->protocol != IPPROTO_COMP + && (ipsnext->ips_said.proto != IPPROTO_COMP + || ipsnext->ips_inext) +#endif /* CONFIG_IPSEC_IPCOMP */ + && ipp->protocol != IPPROTO_IPIP + ) { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "packet with incomplete policy dropped, last successful SA:%s.\n", + irs->sa_len ? irs->sa : " (error)"); + if(irs->stats) { + irs->stats->rx_dropped++; + } + return IPSEC_RCV_FAILEDINBOUND; + } + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "SA:%s, Another IPSEC header to process.\n", + irs->sa_len ? irs->sa : " (error)"); + } else { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "No ips_inext from this SA:%s.\n", + irs->sa_len ? irs->sa : " (error)"); + } + } + +#ifdef CONFIG_IPSEC_IPCOMP + /* update ipcomp ratio counters, even if no ipcomp packet is present */ + if (ipsnext + && ipsnext->ips_said.proto == IPPROTO_COMP + && ipp->protocol != IPPROTO_COMP) { + ipsnext->ips_comp_ratio_cbytes += ntohs(ipp->tot_len); + ipsnext->ips_comp_ratio_dbytes += ntohs(ipp->tot_len); + } +#endif /* CONFIG_IPSEC_IPCOMP */ + + irs->ipsp->ips_life.ipl_bytes.ipl_count += irs->len; + irs->ipsp->ips_life.ipl_bytes.ipl_last = irs->len; + + if(!irs->ipsp->ips_life.ipl_usetime.ipl_count) { + irs->ipsp->ips_life.ipl_usetime.ipl_count = jiffies / HZ; + } + irs->ipsp->ips_life.ipl_usetime.ipl_last = jiffies / HZ; + irs->ipsp->ips_life.ipl_packets.ipl_count += 1; + +#ifdef CONFIG_NETFILTER + if(proto == IPPROTO_ESP || proto == IPPROTO_AH) { + skb->nfmark = (skb->nfmark & (~(IPsecSAref2NFmark(IPSEC_SA_REF_MASK)))) + | IPsecSAref2NFmark(IPsecSA2SAref(irs->ipsp)); + KLIPS_PRINT(debug_rcv & DB_RX_PKTRX, + "klips_debug:ipsec_rcv: " + "%s SA sets skb->nfmark=0x%x.\n", + proto == IPPROTO_ESP ? "ESP" : "AH", + (unsigned)skb->nfmark); + } +#endif /* CONFIG_NETFILTER */ + + return IPSEC_RCV_OK; +} + + +int +#ifdef PROTO_HANDLER_SINGLE_PARM +ipsec_rcv(struct sk_buff *skb) +#else /* PROTO_HANDLER_SINGLE_PARM */ +#ifdef NET_21 +ipsec_rcv(struct sk_buff *skb, unsigned short xlen) +#else /* NET_21 */ +ipsec_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, + __u32 daddr_unused, unsigned short xlen, __u32 saddr, + int redo, struct inet_protocol *protocol) +#endif /* NET_21 */ +#endif /* PROTO_HANDLER_SINGLE_PARM */ +{ +#ifdef NET_21 +#ifdef CONFIG_IPSEC_DEBUG + struct device *dev = skb->dev; +#endif /* CONFIG_IPSEC_DEBUG */ +#endif /* NET_21 */ + unsigned char protoc; + struct iphdr *ipp; +#if defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH) +#endif /* defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH) */ + + struct ipsec_sa *ipsp = NULL; + struct net_device_stats *stats = NULL; /* This device's statistics */ + struct device *ipsecdev = NULL, *prvdev; + struct ipsecpriv *prv; + char name[9]; + int i; + struct in_addr ipsaddr; + struct in_addr ipdaddr; + + struct ipsec_sa* ipsnext = NULL; /* next SA towards inside of packet */ + struct ipsec_rcv_state irs; + + /* Don't unlink in the middle of a turnaround */ + MOD_INC_USE_COUNT; + + memset(&irs, 0, sizeof(struct ipsec_rcv_state)); + + if (skb == NULL) { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "NULL skb passed in.\n"); + goto rcvleave; + } + + if (skb->data == NULL) { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "NULL skb->data passed in, packet is bogus, dropping.\n"); + goto rcvleave; + } + +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL + if (skb->sk && skb->nh.iph && skb->nh.iph->protocol==IPPROTO_UDP) { + /** + * Packet comes from udp_queue_rcv_skb so it is already defrag, + * checksum verified, ... (ie safe to use) + * + * If the packet is not for us, return -1 and udp_queue_rcv_skb + * will continue to handle it (do not kfree skb !!). + */ + struct udp_opt *tp = &(skb->sk->tp_pinfo.af_udp); + struct iphdr *ip = (struct iphdr *)skb->nh.iph; + struct udphdr *udp = (struct udphdr *)((__u32 *)ip+ip->ihl); + __u8 *udpdata = (__u8 *)udp + sizeof(struct udphdr); + __u32 *udpdata32 = (__u32 *)udpdata; + + irs.natt_sport = ntohs(udp->source); + irs.natt_dport = ntohs(udp->dest); + + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "suspected ESPinUDP packet (NAT-Traversal) [%d].\n", + tp->esp_in_udp); + KLIPS_IP_PRINT(debug_rcv, ip); + + if (udpdata < skb->tail) { + unsigned int len = skb->tail - udpdata; + if ((len==1) && (udpdata[0]==0xff)) { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + /* not IPv6 compliant message */ + "NAT-keepalive from %d.%d.%d.%d.\n", NIPQUAD(ip->saddr)); + goto rcvleave; + } + else if ( (tp->esp_in_udp == ESPINUDP_WITH_NON_IKE) && + (len > (2*sizeof(__u32) + sizeof(struct esphdr))) && + (udpdata32[0]==0) && (udpdata32[1]==0) ) { + /* ESP Packet with Non-IKE header */ + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "ESPinUDP pkt with Non-IKE - spi=0x%x\n", + udpdata32[2]); + irs.natt_type = ESPINUDP_WITH_NON_IKE; + irs.natt_len = sizeof(struct udphdr)+(2*sizeof(__u32)); + } + else if ( (tp->esp_in_udp == ESPINUDP_WITH_NON_ESP) && + (len > sizeof(struct esphdr)) && + (udpdata32[0]!=0) ) { + /* ESP Packet without Non-ESP header */ + irs.natt_type = ESPINUDP_WITH_NON_ESP; + irs.natt_len = sizeof(struct udphdr); + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "ESPinUDP pkt without Non-ESP - spi=0x%x\n", + udpdata32[0]); + } + else { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "IKE packet - not handled here\n"); + MOD_DEC_USE_COUNT; + return -1; + } + } + else { + MOD_DEC_USE_COUNT; + return -1; + } + } +#endif + +#ifdef IPH_is_SKB_PULLED + /* In Linux 2.4.4, the IP header has been skb_pull()ed before the + packet is passed to us. So we'll skb_push() to get back to it. */ + if (skb->data == skb->h.raw) { + skb_push(skb, skb->h.raw - skb->nh.raw); + } +#endif /* IPH_is_SKB_PULLED */ + + /* dev->hard_header_len is unreliable and should not be used */ + irs.hard_header_len = skb->mac.raw ? (skb->data - skb->mac.raw) : 0; + if((irs.hard_header_len < 0) || (irs.hard_header_len > skb_headroom(skb))) + irs.hard_header_len = 0; + +#ifdef NET_21 + /* if skb was cloned (most likely due to a packet sniffer such as + tcpdump being momentarily attached to the interface), make + a copy of our own to modify */ + if(skb_cloned(skb)) { + /* include any mac header while copying.. */ + if(skb_headroom(skb) < irs.hard_header_len) { + printk(KERN_WARNING "klips_error:ipsec_rcv: " + "tried to skb_push hhlen=%d, %d available. This should never happen, please report.\n", + irs.hard_header_len, + skb_headroom(skb)); + goto rcvleave; + } + skb_push(skb, irs.hard_header_len); + if +#ifdef SKB_COW_NEW + (skb_cow(skb, skb_headroom(skb)) != 0) +#else /* SKB_COW_NEW */ + ((skb = skb_cow(skb, skb_headroom(skb))) == NULL) +#endif /* SKB_COW_NEW */ + { + goto rcvleave; + } + if(skb->len < irs.hard_header_len) { + printk(KERN_WARNING "klips_error:ipsec_rcv: " + "tried to skb_pull hhlen=%d, %d available. This should never happen, please report.\n", + irs.hard_header_len, + skb->len); + goto rcvleave; + } + skb_pull(skb, irs.hard_header_len); + } + +#endif /* NET_21 */ + +#if IP_FRAGMENT_LINEARIZE + /* In Linux 2.4.4, we may have to reassemble fragments. They are + not assembled automatically to save TCP from having to copy + twice. + */ + if (skb_is_nonlinear(skb)) { + if (skb_linearize(skb, GFP_ATOMIC) != 0) { + goto rcvleave; + } + } +#endif /* IP_FRAGMENT_LINEARIZE */ + +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL + if (irs.natt_len) { + /** + * Now, we are sure packet is ESPinUDP. Remove natt_len bytes from + * packet and modify protocol to ESP. + */ + if (((unsigned char *)skb->data > (unsigned char *)skb->nh.iph) && + ((unsigned char *)skb->nh.iph > (unsigned char *)skb->head)) { + unsigned int _len = (unsigned char *)skb->data - + (unsigned char *)skb->nh.iph; + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: adjusting skb: skb_push(%u)\n", + _len); + skb_push(skb, _len); + } + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "removing %d bytes from ESPinUDP packet\n", irs.natt_len); + ipp = (struct iphdr *)skb->data; + irs.iphlen = ipp->ihl << 2; + ipp->tot_len = htons(ntohs(ipp->tot_len) - irs.natt_len); + if (skb->len < irs.iphlen + irs.natt_len) { + printk(KERN_WARNING + "klips_error:ipsec_rcv: " + "ESPinUDP packet is too small (%d < %d+%d). " + "This should never happen, please report.\n", + (int)(skb->len), irs.iphlen, irs.natt_len); + goto rcvleave; + } + memmove(skb->data + irs.natt_len, skb->data, irs.iphlen); + skb_pull(skb, irs.natt_len); + + /* update nh.iph */ + ipp = skb->nh.iph = (struct iphdr *)skb->data; + + /* modify protocol */ + ipp->protocol = IPPROTO_ESP; + + skb->sk = NULL; + + KLIPS_IP_PRINT(debug_rcv, skb->nh.iph); + } +#endif + + ipp = skb->nh.iph; + ipsaddr.s_addr = ipp->saddr; + addrtoa(ipsaddr, 0, irs.ipsaddr_txt, sizeof(irs.ipsaddr_txt)); + ipdaddr.s_addr = ipp->daddr; + addrtoa(ipdaddr, 0, irs.ipdaddr_txt, sizeof(irs.ipdaddr_txt)); + irs.iphlen = ipp->ihl << 2; + + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "<<< Info -- "); + KLIPS_PRINTMORE(debug_rcv && skb->dev, "skb->dev=%s ", + skb->dev->name ? skb->dev->name : "NULL"); + KLIPS_PRINTMORE(debug_rcv && dev, "dev=%s ", + dev->name ? dev->name : "NULL"); + KLIPS_PRINTMORE(debug_rcv, "\n"); + + KLIPS_PRINT(debug_rcv && !(skb->dev && dev && (skb->dev == dev)), + "klips_debug:ipsec_rcv: " + "Informational -- **if this happens, find out why** skb->dev:%s is not equal to dev:%s\n", + skb->dev ? (skb->dev->name ? skb->dev->name : "NULL") : "NULL", + dev ? (dev->name ? dev->name : "NULL") : "NULL"); + + protoc = ipp->protocol; +#ifndef NET_21 + if((!protocol) || (protocol->protocol != protoc)) { + KLIPS_PRINT(debug_rcv & DB_RX_IPSA, + "klips_debug:ipsec_rcv: " + "protocol arg is NULL or unequal to the packet contents, this is odd, using value in packet.\n"); + } +#endif /* !NET_21 */ + + if( (protoc != IPPROTO_AH) && +#ifdef CONFIG_IPSEC_IPCOMP_disabled_until_we_register_IPCOMP_HANDLER + (protoc != IPPROTO_COMP) && +#endif /* CONFIG_IPSEC_IPCOMP */ + (protoc != IPPROTO_ESP) ) { + KLIPS_PRINT(debug_rcv & DB_RX_IPSA, + "klips_debug:ipsec_rcv: Why the hell is someone " + "passing me a non-ipsec protocol = %d packet? -- dropped.\n", + protoc); + goto rcvleave; + } + + if(skb->dev) { + for(i = 0; i < IPSEC_NUM_IF; i++) { + sprintf(name, IPSEC_DEV_FORMAT, i); + if(!strcmp(name, skb->dev->name)) { + prv = (struct ipsecpriv *)(skb->dev->priv); + if(prv) { + stats = (struct net_device_stats *) &(prv->mystats); + } + ipsecdev = skb->dev; + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "Info -- pkt already proc'ed a group of ipsec headers, processing next group of ipsec headers.\n"); + break; + } + if((ipsecdev = __ipsec_dev_get(name)) == NULL) { + KLIPS_PRINT(debug_rcv, + "klips_error:ipsec_rcv: " + "device %s does not exist\n", + name); + } + prv = ipsecdev ? (struct ipsecpriv *)(ipsecdev->priv) : NULL; + prvdev = prv ? (struct device *)(prv->dev) : NULL; + +#if 0 + KLIPS_PRINT(debug_rcv && prvdev, + "klips_debug:ipsec_rcv: " + "physical device for device %s is %s\n", + name, + prvdev->name); +#endif + if(prvdev && skb->dev && + !strcmp(prvdev->name, skb->dev->name)) { + stats = prv ? ((struct net_device_stats *) &(prv->mystats)) : NULL; + skb->dev = ipsecdev; + KLIPS_PRINT(debug_rcv && prvdev, + "klips_debug:ipsec_rcv: " + "assigning packet ownership to virtual device %s from physical device %s.\n", + name, prvdev->name); + if(stats) { + stats->rx_packets++; + } + break; + } + } + } else { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "device supplied with skb is NULL\n"); + } + + if(stats == NULL) { + KLIPS_PRINT((debug_rcv), + "klips_error:ipsec_rcv: " + "packet received from physical I/F (%s) not connected to ipsec I/F. Cannot record stats. May not have SA for decoding. Is IPSEC traffic expected on this I/F? Check routing.\n", + skb->dev ? (skb->dev->name ? skb->dev->name : "NULL") : "NULL"); + } + + KLIPS_IP_PRINT(debug_rcv, ipp); + + /* begin decapsulating loop here */ + + /* + The spinlock is to prevent any other process from + accessing or deleting the ipsec_sa hash table or any of the + ipsec_sa s while we are using and updating them. + + This is not optimal, but was relatively straightforward + at the time. A better way to do it has been planned for + more than a year, to lock the hash table and put reference + counts on each ipsec_sa instead. This is not likely to happen + in KLIPS1 unless a volunteer contributes it, but will be + designed into KLIPS2. + */ + spin_lock(&tdb_lock); + + /* set up for decap loop */ + irs.stats= stats; + irs.ipp = ipp; + irs.ipsp = NULL; + irs.ilen = 0; + irs.authlen=0; + irs.authfuncs=NULL; + irs.skb = skb; + + do { + int decap_stat; + + decap_stat = ipsec_rcv_decap_once(&irs); + + if(decap_stat != IPSEC_RCV_OK) { + spin_unlock(&tdb_lock); + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: decap_once failed: %d\n", + decap_stat); + + goto rcvleave; + } + /* end decapsulation loop here */ + } while( (irs.ipp->protocol == IPPROTO_ESP ) + || (irs.ipp->protocol == IPPROTO_AH ) +#ifdef CONFIG_IPSEC_IPCOMP + || (irs.ipp->protocol == IPPROTO_COMP) +#endif /* CONFIG_IPSEC_IPCOMP */ + ); + + /* set up for decap loop */ + ipp =irs.ipp; + ipsp =irs.ipsp; + ipsnext = ipsp->ips_inext; + skb = irs.skb; + + /* if there is an IPCOMP, but we don't have an IPPROTO_COMP, + * then we can just skip it + */ +#ifdef CONFIG_IPSEC_IPCOMP + if(ipsnext && ipsnext->ips_said.proto == IPPROTO_COMP) { + ipsp = ipsnext; + ipsnext = ipsp->ips_inext; + } +#endif /* CONFIG_IPSEC_IPCOMP */ + +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL + if ((irs.natt_type) && (ipp->protocol != IPPROTO_IPIP)) { + /** + * NAT-Traversal and Transport Mode: + * we need to correct TCP/UDP checksum + * + * If we've got NAT-OA, we can fix checksum without recalculation. + */ + __u32 natt_oa = ipsp->ips_natt_oa ? + ((struct sockaddr_in*)(ipsp->ips_natt_oa))->sin_addr.s_addr : 0; + __u16 pkt_len = skb->tail - (unsigned char *)ipp; + __u16 data_len = pkt_len - (ipp->ihl << 2); + + switch (ipp->protocol) { + case IPPROTO_TCP: + if (data_len >= sizeof(struct tcphdr)) { + struct tcphdr *tcp = (struct tcphdr *)((__u32 *)ipp+ipp->ihl); + if (natt_oa) { + __u32 buff[2] = { ~natt_oa, ipp->saddr }; + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "NAT-T & TRANSPORT: " + "fix TCP checksum using NAT-OA\n"); + tcp->check = csum_fold( + csum_partial((unsigned char *)buff, sizeof(buff), + tcp->check^0xffff)); + } + else { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "NAT-T & TRANSPORT: recalc TCP checksum\n"); + if (pkt_len > (ntohs(ipp->tot_len))) + data_len -= (pkt_len - ntohs(ipp->tot_len)); + tcp->check = 0; + tcp->check = csum_tcpudp_magic(ipp->saddr, ipp->daddr, + data_len, IPPROTO_TCP, + csum_partial((unsigned char *)tcp, data_len, 0)); + } + } + else { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "NAT-T & TRANSPORT: can't fix TCP checksum\n"); + } + break; + case IPPROTO_UDP: + if (data_len >= sizeof(struct udphdr)) { + struct udphdr *udp = (struct udphdr *)((__u32 *)ipp+ipp->ihl); + if (udp->check == 0) { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "NAT-T & TRANSPORT: UDP checksum already 0\n"); + } + else if (natt_oa) { + __u32 buff[2] = { ~natt_oa, ipp->saddr }; + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "NAT-T & TRANSPORT: " + "fix UDP checksum using NAT-OA\n"); + udp->check = csum_fold( + csum_partial((unsigned char *)buff, sizeof(buff), + udp->check^0xffff)); + } + else { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "NAT-T & TRANSPORT: zero UDP checksum\n"); + udp->check = 0; + } + } + else { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "NAT-T & TRANSPORT: can't fix UDP checksum\n"); + } + break; + default: + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "NAT-T & TRANSPORT: non TCP/UDP packet -- do nothing\n"); + break; + } + } +#endif + + /* + * XXX this needs to be locked from when it was first looked + * up in the decapsulation loop. Perhaps it is better to put + * the IPIP decap inside the loop. + */ + if(ipsnext) { + ipsp = ipsnext; + irs.sa_len = satoa(irs.said, 0, irs.sa, SATOA_BUF); + if(ipp->protocol != IPPROTO_IPIP) { + spin_unlock(&tdb_lock); + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "SA:%s, Hey! How did this get through? Dropped.\n", + irs.sa_len ? irs.sa : " (error)"); + if(stats) { + stats->rx_dropped++; + } + goto rcvleave; + } + if(sysctl_ipsec_inbound_policy_check) { + if((ipsnext = ipsp->ips_inext)) { + char sa2[SATOA_BUF]; + size_t sa_len2; + sa_len2 = satoa(ipsnext->ips_said, 0, sa2, SATOA_BUF); + spin_unlock(&tdb_lock); + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "unexpected SA:%s after IPIP SA:%s\n", + sa_len2 ? sa2 : " (error)", + irs.sa_len ? irs.sa : " (error)"); + if(stats) { + stats->rx_dropped++; + } + goto rcvleave; + } + if(ipp->saddr != ((struct sockaddr_in*)(ipsp->ips_addr_s))->sin_addr.s_addr) { + spin_unlock(&tdb_lock); + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "SA:%s, src=%s of pkt does not agree with expected SA source address policy.\n", + irs.sa_len ? irs.sa : " (error)", + irs.ipsaddr_txt); + if(stats) { + stats->rx_dropped++; + } + goto rcvleave; + } + } + + /* + * XXX this needs to be locked from when it was first looked + * up in the decapsulation loop. Perhaps it is better to put + * the IPIP decap inside the loop. + */ + ipsp->ips_life.ipl_bytes.ipl_count += skb->len; + ipsp->ips_life.ipl_bytes.ipl_last = skb->len; + + if(!ipsp->ips_life.ipl_usetime.ipl_count) { + ipsp->ips_life.ipl_usetime.ipl_count = jiffies / HZ; + } + ipsp->ips_life.ipl_usetime.ipl_last = jiffies / HZ; + ipsp->ips_life.ipl_packets.ipl_count += 1; + + if(skb->len < irs.iphlen) { + spin_unlock(&tdb_lock); + printk(KERN_WARNING "klips_debug:ipsec_rcv: " + "tried to skb_pull iphlen=%d, %d available. This should never happen, please report.\n", + irs.iphlen, + (int)(skb->len)); + + goto rcvleave; + } + skb_pull(skb, irs.iphlen); + +#ifdef NET_21 + skb->nh.raw = skb->data; + ipp = (struct iphdr *)skb->nh.raw; + skb->h.raw = skb->nh.raw + (skb->nh.iph->ihl << 2); + + memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); +#else /* NET_21 */ + ipp = skb->ip_hdr = skb->h.iph = (struct iphdr *)skb->data; + + memset(skb->proto_priv, 0, sizeof(struct options)); +#endif /* NET_21 */ + ipsaddr.s_addr = ipp->saddr; + addrtoa(ipsaddr, 0, irs.ipsaddr_txt, sizeof(irs.ipsaddr_txt)); + ipdaddr.s_addr = ipp->daddr; + addrtoa(ipdaddr, 0, irs.ipdaddr_txt, sizeof(irs.ipdaddr_txt)); + + skb->protocol = htons(ETH_P_IP); + skb->ip_summed = 0; + KLIPS_PRINT(debug_rcv & DB_RX_PKTRX, + "klips_debug:ipsec_rcv: " + "IPIP tunnel stripped.\n"); + KLIPS_IP_PRINT(debug_rcv & DB_RX_PKTRX, ipp); + + if(sysctl_ipsec_inbound_policy_check + /* + Note: "xor" (^) logically replaces "not equal" + (!=) and "bitwise or" (|) logically replaces + "boolean or" (||). This is done to speed up + execution by doing only bitwise operations and + no branch operations + */ + && (((ipp->saddr & ipsp->ips_mask_s.u.v4.sin_addr.s_addr) + ^ ipsp->ips_flow_s.u.v4.sin_addr.s_addr) + | ((ipp->daddr & ipsp->ips_mask_d.u.v4.sin_addr.s_addr) + ^ ipsp->ips_flow_d.u.v4.sin_addr.s_addr)) ) + { + char sflow_txt[SUBNETTOA_BUF], dflow_txt[SUBNETTOA_BUF]; + + subnettoa(ipsp->ips_flow_s.u.v4.sin_addr, + ipsp->ips_mask_s.u.v4.sin_addr, + 0, sflow_txt, sizeof(sflow_txt)); + subnettoa(ipsp->ips_flow_d.u.v4.sin_addr, + ipsp->ips_mask_d.u.v4.sin_addr, + 0, dflow_txt, sizeof(dflow_txt)); + spin_unlock(&tdb_lock); + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "SA:%s, inner tunnel policy [%s -> %s] does not agree with pkt contents [%s -> %s].\n", + irs.sa_len ? irs.sa : " (error)", + sflow_txt, + dflow_txt, + irs.ipsaddr_txt, + irs.ipdaddr_txt); + if(stats) { + stats->rx_dropped++; + } + goto rcvleave; + } +#ifdef CONFIG_NETFILTER + skb->nfmark = (skb->nfmark & (~(IPsecSAref2NFmark(IPSEC_SA_REF_TABLE_MASK)))) + | IPsecSAref2NFmark(IPsecSA2SAref(ipsp)); + KLIPS_PRINT(debug_rcv & DB_RX_PKTRX, + "klips_debug:ipsec_rcv: " + "IPIP SA sets skb->nfmark=0x%x.\n", + (unsigned)skb->nfmark); +#endif /* CONFIG_NETFILTER */ + } + + spin_unlock(&tdb_lock); + +#ifdef NET_21 + if(stats) { + stats->rx_bytes += skb->len; + } + if(skb->dst) { + dst_release(skb->dst); + skb->dst = NULL; + } + skb->pkt_type = PACKET_HOST; + if(irs.hard_header_len && + (skb->mac.raw != (skb->data - irs.hard_header_len)) && + (irs.hard_header_len <= skb_headroom(skb))) { + /* copy back original MAC header */ + memmove(skb->data - irs.hard_header_len, skb->mac.raw, irs.hard_header_len); + skb->mac.raw = skb->data - irs.hard_header_len; + } +#endif /* NET_21 */ + +#ifdef CONFIG_IPSEC_IPCOMP + if(ipp->protocol == IPPROTO_COMP) { + unsigned int flags = 0; + + if(sysctl_ipsec_inbound_policy_check) { + KLIPS_PRINT(debug_rcv & DB_RX_PKTRX, + "klips_debug:ipsec_rcv: " + "inbound policy checking enabled, IPCOMP follows IPIP, dropped.\n"); + if (stats) { + stats->rx_errors++; + } + goto rcvleave; + } + /* + XXX need a ipsec_sa for updating ratio counters but it is not + following policy anyways so it is not a priority + */ + skb = skb_decompress(skb, NULL, &flags); + if (!skb || flags) { + KLIPS_PRINT(debug_rcv & DB_RX_PKTRX, + "klips_debug:ipsec_rcv: " + "skb_decompress() returned error flags: %d, dropped.\n", + flags); + if (stats) { + stats->rx_errors++; + } + goto rcvleave; + } + } +#endif /* CONFIG_IPSEC_IPCOMP */ + +#ifdef SKB_RESET_NFCT + nf_conntrack_put(skb->nfct); + skb->nfct = NULL; +#ifdef CONFIG_NETFILTER_DEBUG + skb->nf_debug = 0; +#endif /* CONFIG_NETFILTER_DEBUG */ +#endif /* SKB_RESET_NFCT */ + KLIPS_PRINT(debug_rcv & DB_RX_PKTRX, + "klips_debug:ipsec_rcv: " + "netif_rx() called.\n"); + netif_rx(skb); + + MOD_DEC_USE_COUNT; + return(0); + + rcvleave: + if(skb) { + ipsec_kfree_skb(skb); + } + + MOD_DEC_USE_COUNT; + return(0); +} + +struct inet_protocol ah_protocol = +{ + ipsec_rcv, /* AH handler */ + NULL, /* TUNNEL error control */ +#ifdef NETDEV_25 + 1, /* no policy */ +#else + 0, /* next */ + IPPROTO_AH, /* protocol ID */ + 0, /* copy */ + NULL, /* data */ + "AH" /* name */ +#endif +}; + +struct inet_protocol esp_protocol = +{ + ipsec_rcv, /* ESP handler */ + NULL, /* TUNNEL error control */ +#ifdef NETDEV_25 + 1, /* no policy */ +#else + 0, /* next */ + IPPROTO_ESP, /* protocol ID */ + 0, /* copy */ + NULL, /* data */ + "ESP" /* name */ +#endif +}; + +#if 0 +/* We probably don't want to install a pure IPCOMP protocol handler, but + only want to handle IPCOMP if it is encapsulated inside an ESP payload + (which is already handled) */ +#ifdef CONFIG_IPSEC_IPCOMP +struct inet_protocol comp_protocol = +{ + ipsec_rcv, /* COMP handler */ + NULL, /* COMP error control */ +#ifdef NETDEV_25 + 1, /* no policy */ +#else + 0, /* next */ + IPPROTO_COMP, /* protocol ID */ + 0, /* copy */ + NULL, /* data */ + "COMP" /* name */ +#endif +}; +#endif /* CONFIG_IPSEC_IPCOMP */ +#endif diff --git a/linux/net/ipsec/ipsec_sa.c b/linux/net/ipsec/ipsec_sa.c new file mode 100644 index 000000000..4f73b92f2 --- /dev/null +++ b/linux/net/ipsec/ipsec_sa.c @@ -0,0 +1,1031 @@ +/* + * Common routines for IPsec SA maintenance routines. + * + * Copyright (C) 1996, 1997 John Ioannidis. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Richard Guy Briggs. + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + * + * RCSID $Id: ipsec_sa.c,v 1.3 2004/06/13 19:57:50 as Exp $ + * + * This is the file formerly known as "ipsec_xform.h" + * + */ + +#include <linux/config.h> +#include <linux/version.h> +#include <linux/kernel.h> /* printk() */ + +#include "freeswan/ipsec_param.h" + +#ifdef MALLOC_SLAB +# include <linux/slab.h> /* kmalloc() */ +#else /* MALLOC_SLAB */ +# include <linux/malloc.h> /* kmalloc() */ +#endif /* MALLOC_SLAB */ +#include <linux/vmalloc.h> /* vmalloc() */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/interrupt.h> /* mark_bh */ + +#include <linux/netdevice.h> /* struct device, and other headers */ +#include <linux/etherdevice.h> /* eth_type_trans */ +#include <linux/ip.h> /* struct iphdr */ +#include <linux/skbuff.h> +#include <freeswan.h> +#ifdef SPINLOCK +#ifdef SPINLOCK_23 +#include <linux/spinlock.h> /* *lock* */ +#else /* SPINLOCK_23 */ +#include <asm/spinlock.h> /* *lock* */ +#endif /* SPINLOCK_23 */ +#endif /* SPINLOCK */ +#ifdef NET_21 +#include <asm/uaccess.h> +#include <linux/in6.h> +#endif +#include <asm/checksum.h> +#include <net/ip.h> + +#include "freeswan/radij.h" + +#include "freeswan/ipsec_stats.h" +#include "freeswan/ipsec_life.h" +#include "freeswan/ipsec_sa.h" +#include "freeswan/ipsec_xform.h" + +#include "freeswan/ipsec_encap.h" +#include "freeswan/ipsec_radij.h" +#include "freeswan/ipsec_xform.h" +#include "freeswan/ipsec_ipe4.h" +#include "freeswan/ipsec_ah.h" +#include "freeswan/ipsec_esp.h" + +#include <pfkeyv2.h> +#include <pfkey.h> + +#include "freeswan/ipsec_proto.h" +#include "freeswan/ipsec_alg.h" + + +#ifdef CONFIG_IPSEC_DEBUG +int debug_xform = 0; +#endif /* CONFIG_IPSEC_DEBUG */ + +#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0) + +struct ipsec_sa *ipsec_sadb_hash[SADB_HASHMOD]; +#ifdef SPINLOCK +spinlock_t tdb_lock = SPIN_LOCK_UNLOCKED; +#else /* SPINLOCK */ +spinlock_t tdb_lock; +#endif /* SPINLOCK */ + +struct ipsec_sadb ipsec_sadb; + +#if IPSEC_SA_REF_CODE + +/* the sub table must be narrower (or equal) in bits than the variable type + in the main table to count the number of unused entries in it. */ +typedef struct { + int testSizeOf_refSubTable : + ((sizeof(IPsecRefTableUnusedCount) * 8) < IPSEC_SA_REF_SUBTABLE_IDX_WIDTH ? -1 : 1); +} dummy; + + +/* The field where the saref will be hosted in the skb must be wide enough to + accomodate the information it needs to store. */ +typedef struct { + int testSizeOf_refField : + (IPSEC_SA_REF_HOST_FIELD_WIDTH < IPSEC_SA_REF_TABLE_IDX_WIDTH ? -1 : 1 ); +} dummy2; + + +void +ipsec_SAtest(void) +{ + IPsecSAref_t SAref = 258; + struct ipsec_sa ips; + ips.ips_ref = 772; + + printk("klips_debug:ipsec_SAtest: " + "IPSEC_SA_REF_SUBTABLE_IDX_WIDTH=%u\n" + "IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES=%u\n" + "IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES=%u\n" + "IPSEC_SA_REF_HOST_FIELD_WIDTH=%lu\n" + "IPSEC_SA_REF_TABLE_MASK=%x\n" + "IPSEC_SA_REF_ENTRY_MASK=%x\n" + "IPsecSAref2table(%d)=%u\n" + "IPsecSAref2entry(%d)=%u\n" + "IPsecSAref2NFmark(%d)=%u\n" + "IPsecSAref2SA(%d)=%p\n" + "IPsecSA2SAref(%p)=%d\n" + , + IPSEC_SA_REF_SUBTABLE_IDX_WIDTH, + IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES, + IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES, + (unsigned long) IPSEC_SA_REF_HOST_FIELD_WIDTH, + IPSEC_SA_REF_TABLE_MASK, + IPSEC_SA_REF_ENTRY_MASK, + SAref, IPsecSAref2table(SAref), + SAref, IPsecSAref2entry(SAref), + SAref, IPsecSAref2NFmark(SAref), + SAref, IPsecSAref2SA(SAref), + (&ips), IPsecSA2SAref((&ips)) + ); + return; +} + +int +ipsec_SAref_recycle(void) +{ + int table; + int entry; + int error = 0; + + ipsec_sadb.refFreeListHead = -1; + ipsec_sadb.refFreeListTail = -1; + + if(ipsec_sadb.refFreeListCont == IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES * IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES) { + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_SAref_recycle: " + "end of table reached, continuing at start..\n"); + ipsec_sadb.refFreeListCont = 0; + } + + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_SAref_recycle: " + "recycling, continuing from SAref=%d (0p%p), table=%d, entry=%d.\n", + ipsec_sadb.refFreeListCont, + (ipsec_sadb.refTable[IPsecSAref2table(ipsec_sadb.refFreeListCont)] != NULL) ? IPsecSAref2SA(ipsec_sadb.refFreeListCont) : NULL, + IPsecSAref2table(ipsec_sadb.refFreeListCont), + IPsecSAref2entry(ipsec_sadb.refFreeListCont)); + + for(table = IPsecSAref2table(ipsec_sadb.refFreeListCont); + table < IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES; + table++) { + if(ipsec_sadb.refTable[table] == NULL) { + error = ipsec_SArefSubTable_alloc(table); + if(error) { + return error; + } + } + for(entry = IPsecSAref2entry(ipsec_sadb.refFreeListCont); + entry < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES; + entry++) { + if(ipsec_sadb.refTable[table]->entry[entry] == NULL) { + ipsec_sadb.refFreeList[++ipsec_sadb.refFreeListTail] = IPsecSArefBuild(table, entry); + if(ipsec_sadb.refFreeListTail == (IPSEC_SA_REF_FREELIST_NUM_ENTRIES - 1)) { + ipsec_sadb.refFreeListHead = 0; + ipsec_sadb.refFreeListCont = ipsec_sadb.refFreeList[ipsec_sadb.refFreeListTail] + 1; + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_SAref_recycle: " + "SArefFreeList refilled.\n"); + return 0; + } + } + } + } + + if(ipsec_sadb.refFreeListTail == -1) { + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_SAref_recycle: " + "out of room in the SArefTable.\n"); + + return(-ENOSPC); + } + + ipsec_sadb.refFreeListHead = 0; + ipsec_sadb.refFreeListCont = ipsec_sadb.refFreeList[ipsec_sadb.refFreeListTail] + 1; + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_SAref_recycle: " + "SArefFreeList partly refilled to %d of %d.\n", + ipsec_sadb.refFreeListTail, + IPSEC_SA_REF_FREELIST_NUM_ENTRIES); + return 0; +} + +int +ipsec_SArefSubTable_alloc(unsigned table) +{ + unsigned entry; + struct IPsecSArefSubTable* SArefsub; + + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_SArefSubTable_alloc: " + "allocating %lu bytes for table %u of %u.\n", + (unsigned long) (IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES * sizeof(struct ipsec_sa *)), + table, + IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES); + + /* allocate another sub-table */ + SArefsub = vmalloc(IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES * sizeof(struct ipsec_sa *)); + if(SArefsub == NULL) { + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_SArefSubTable_alloc: " + "error allocating memory for table %u of %u!\n", + table, + IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES); + return -ENOMEM; + } + + /* add this sub-table to the main table */ + ipsec_sadb.refTable[table] = SArefsub; + + /* initialise each element to NULL */ + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_SArefSubTable_alloc: " + "initialising %u elements (2 ^ %u) of table %u.\n", + IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES, + IPSEC_SA_REF_SUBTABLE_IDX_WIDTH, + table); + for(entry = 0; entry < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES; entry++) { + SArefsub->entry[entry] = NULL; + } + + return 0; +} +#endif /* IPSEC_SA_REF_CODE */ + +int +ipsec_saref_freelist_init(void) +{ + int i; + + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_saref_freelist_init: " + "initialising %u elements of FreeList.\n", + IPSEC_SA_REF_FREELIST_NUM_ENTRIES); + + for(i = 0; i < IPSEC_SA_REF_FREELIST_NUM_ENTRIES; i++) { + ipsec_sadb.refFreeList[i] = IPSEC_SAREF_NULL; + } + ipsec_sadb.refFreeListHead = -1; + ipsec_sadb.refFreeListCont = 0; + ipsec_sadb.refFreeListTail = -1; + + return 0; +} + +int +ipsec_sadb_init(void) +{ + int error = 0; + unsigned i; + + for(i = 0; i < SADB_HASHMOD; i++) { + ipsec_sadb_hash[i] = NULL; + } + /* parts above are for the old style SADB hash table */ + + +#if IPSEC_SA_REF_CODE + /* initialise SA reference table */ + + /* initialise the main table */ + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sadb_init: " + "initialising main table of size %u (2 ^ %u).\n", + IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES, + IPSEC_SA_REF_MAINTABLE_IDX_WIDTH); + { + unsigned table; + for(table = 0; table < IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES; table++) { + ipsec_sadb.refTable[table] = NULL; + } + } + + /* allocate the first sub-table */ + error = ipsec_SArefSubTable_alloc(0); + if(error) { + return error; + } + + error = ipsec_saref_freelist_init(); +#endif /* IPSEC_SA_REF_CODE */ + return error; +} + +#if IPSEC_SA_REF_CODE +IPsecSAref_t +ipsec_SAref_alloc(int*error) /* pass in error var by pointer */ +{ + IPsecSAref_t SAref; + + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_SAref_alloc: " + "SAref requested... head=%d, cont=%d, tail=%d, listsize=%d.\n", + ipsec_sadb.refFreeListHead, + ipsec_sadb.refFreeListCont, + ipsec_sadb.refFreeListTail, + IPSEC_SA_REF_FREELIST_NUM_ENTRIES); + + if(ipsec_sadb.refFreeListHead == -1) { + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_SAref_alloc: " + "FreeList empty, recycling...\n"); + *error = ipsec_SAref_recycle(); + if(*error) { + return IPSEC_SAREF_NULL; + } + } + + SAref = ipsec_sadb.refFreeList[ipsec_sadb.refFreeListHead]; + if(SAref == IPSEC_SAREF_NULL) { + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_SAref_alloc: " + "unexpected error, refFreeListHead = %d points to invalid entry.\n", + ipsec_sadb.refFreeListHead); + *error = -ESPIPE; + return IPSEC_SAREF_NULL; + } + + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_SAref_alloc: " + "allocating SAref=%d, table=%u, entry=%u of %u.\n", + SAref, + IPsecSAref2table(SAref), + IPsecSAref2entry(SAref), + IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES * IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES); + + ipsec_sadb.refFreeList[ipsec_sadb.refFreeListHead] = IPSEC_SAREF_NULL; + ipsec_sadb.refFreeListHead++; + if(ipsec_sadb.refFreeListHead > ipsec_sadb.refFreeListTail) { + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_SAref_alloc: " + "last FreeList entry allocated, resetting list head to empty.\n"); + ipsec_sadb.refFreeListHead = -1; + } + + return SAref; +} +#endif /* IPSEC_SA_REF_CODE */ + +int +ipsec_sa_print(struct ipsec_sa *ips) +{ + char sa[SATOA_BUF]; + size_t sa_len; + + printk(KERN_INFO "klips_debug: SA:"); + if(ips == NULL) { + printk("NULL\n"); + return -ENOENT; + } + printk(" ref=%d", ips->ips_ref); + printk(" refcount=%d", atomic_read(&ips->ips_refcount)); + if(ips->ips_hnext != NULL) { + printk(" hnext=0p%p", ips->ips_hnext); + } + if(ips->ips_inext != NULL) { + printk(" inext=0p%p", ips->ips_inext); + } + if(ips->ips_onext != NULL) { + printk(" onext=0p%p", ips->ips_onext); + } + sa_len = satoa(ips->ips_said, 0, sa, SATOA_BUF); + printk(" said=%s", sa_len ? sa : " (error)"); + if(ips->ips_seq) { + printk(" seq=%u", ips->ips_seq); + } + if(ips->ips_pid) { + printk(" pid=%u", ips->ips_pid); + } + if(ips->ips_authalg) { + printk(" authalg=%u", ips->ips_authalg); + } + if(ips->ips_encalg) { + printk(" encalg=%u", ips->ips_encalg); + } + printk(" XFORM=%s%s%s", IPS_XFORM_NAME(ips)); + if(ips->ips_replaywin) { + printk(" ooowin=%u", ips->ips_replaywin); + } + if(ips->ips_flags) { + printk(" flags=%u", ips->ips_flags); + } + if(ips->ips_addr_s) { + char buf[SUBNETTOA_BUF]; + addrtoa(((struct sockaddr_in*)(ips->ips_addr_s))->sin_addr, + 0, buf, sizeof(buf)); + printk(" src=%s", buf); + } + if(ips->ips_addr_d) { + char buf[SUBNETTOA_BUF]; + addrtoa(((struct sockaddr_in*)(ips->ips_addr_s))->sin_addr, + 0, buf, sizeof(buf)); + printk(" dst=%s", buf); + } + if(ips->ips_addr_p) { + char buf[SUBNETTOA_BUF]; + addrtoa(((struct sockaddr_in*)(ips->ips_addr_p))->sin_addr, + 0, buf, sizeof(buf)); + printk(" proxy=%s", buf); + } + if(ips->ips_key_bits_a) { + printk(" key_bits_a=%u", ips->ips_key_bits_a); + } + if(ips->ips_key_bits_e) { + printk(" key_bits_e=%u", ips->ips_key_bits_e); + } + + printk("\n"); + return 0; +} + +struct ipsec_sa* +ipsec_sa_alloc(int*error) /* pass in error var by pointer */ +{ + struct ipsec_sa* ips; + + if((ips = kmalloc(sizeof(*ips), GFP_ATOMIC) ) == NULL) { + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sa_alloc: " + "memory allocation error\n"); + *error = -ENOMEM; + return NULL; + } + memset((caddr_t)ips, 0, sizeof(*ips)); +#if IPSEC_SA_REF_CODE + ips->ips_ref = ipsec_SAref_alloc(error); /* pass in error return by pointer */ + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sa_alloc: " + "allocated %lu bytes for ipsec_sa struct=0p%p ref=%d.\n", + (unsigned long) sizeof(*ips), + ips, + ips->ips_ref); + if(ips->ips_ref == IPSEC_SAREF_NULL) { + kfree(ips); + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sa_alloc: " + "SAref allocation error\n"); + return NULL; + } + + atomic_inc(&ips->ips_refcount); + IPsecSAref2SA(ips->ips_ref) = ips; +#endif /* IPSEC_SA_REF_CODE */ + + *error = 0; + return(ips); +} + +int +ipsec_sa_free(struct ipsec_sa* ips) +{ + return ipsec_sa_wipe(ips); +} + +struct ipsec_sa * +ipsec_sa_getbyid(struct sa_id *said) +{ + int hashval; + struct ipsec_sa *ips; + char sa[SATOA_BUF]; + size_t sa_len; + + if(said == NULL) { + KLIPS_PRINT(debug_xform, + "klips_error:ipsec_sa_getbyid: " + "null pointer passed in!\n"); + return NULL; + } + + sa_len = satoa(*said, 0, sa, SATOA_BUF); + + hashval = (said->spi+said->dst.s_addr+said->proto) % SADB_HASHMOD; + + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sa_getbyid: " + "linked entry in ipsec_sa table for hash=%d of SA:%s requested.\n", + hashval, + sa_len ? sa : " (error)"); + + if((ips = ipsec_sadb_hash[hashval]) == NULL) { + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sa_getbyid: " + "no entries in ipsec_sa table for hash=%d of SA:%s.\n", + hashval, + sa_len ? sa : " (error)"); + return NULL; + } + + for (; ips; ips = ips->ips_hnext) { + if ((ips->ips_said.spi == said->spi) && + (ips->ips_said.dst.s_addr == said->dst.s_addr) && + (ips->ips_said.proto == said->proto)) { + atomic_inc(&ips->ips_refcount); + return ips; + } + } + + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sa_getbyid: " + "no entry in linked list for hash=%d of SA:%s.\n", + hashval, + sa_len ? sa : " (error)"); + return NULL; +} + +int +ipsec_sa_put(struct ipsec_sa *ips) +{ + char sa[SATOA_BUF]; + size_t sa_len; + + if(ips == NULL) { + KLIPS_PRINT(debug_xform, + "klips_error:ipsec_sa_put: " + "null pointer passed in!\n"); + return -1; + } + + sa_len = satoa(ips->ips_said, 0, sa, SATOA_BUF); + + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sa_put: " + "ipsec_sa SA:%s, ref:%d reference count decremented.\n", + sa_len ? sa : " (error)", + ips->ips_ref); + + atomic_dec(&ips->ips_refcount); + + return 0; +} + +/* + The ipsec_sa table better *NOT* be locked before it is handed in, or SMP locks will happen +*/ +int +ipsec_sa_add(struct ipsec_sa *ips) +{ + int error = 0; + unsigned int hashval; + + if(ips == NULL) { + KLIPS_PRINT(debug_xform, + "klips_error:ipsec_sa_add: " + "null pointer passed in!\n"); + return -ENODATA; + } + hashval = ((ips->ips_said.spi + ips->ips_said.dst.s_addr + ips->ips_said.proto) % SADB_HASHMOD); + + atomic_inc(&ips->ips_refcount); + spin_lock_bh(&tdb_lock); + + ips->ips_hnext = ipsec_sadb_hash[hashval]; + ipsec_sadb_hash[hashval] = ips; + + spin_unlock_bh(&tdb_lock); + + return error; +} + +/* + The ipsec_sa table better be locked before it is handed in, or races might happen +*/ +int +ipsec_sa_del(struct ipsec_sa *ips) +{ + unsigned int hashval; + struct ipsec_sa *ipstp; + char sa[SATOA_BUF]; + size_t sa_len; + + if(ips == NULL) { + KLIPS_PRINT(debug_xform, + "klips_error:ipsec_sa_del: " + "null pointer passed in!\n"); + return -ENODATA; + } + + sa_len = satoa(ips->ips_said, 0, sa, SATOA_BUF); + if(ips->ips_inext || ips->ips_onext) { + KLIPS_PRINT(debug_xform, + "klips_error:ipsec_sa_del: " + "SA:%s still linked!\n", + sa_len ? sa : " (error)"); + return -EMLINK; + } + + hashval = ((ips->ips_said.spi + ips->ips_said.dst.s_addr + ips->ips_said.proto) % SADB_HASHMOD); + + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sa_del: " + "deleting SA:%s, hashval=%d.\n", + sa_len ? sa : " (error)", + hashval); + if(ipsec_sadb_hash[hashval] == NULL) { + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sa_del: " + "no entries in ipsec_sa table for hash=%d of SA:%s.\n", + hashval, + sa_len ? sa : " (error)"); + return -ENOENT; + } + + if (ips == ipsec_sadb_hash[hashval]) { + ipsec_sadb_hash[hashval] = ipsec_sadb_hash[hashval]->ips_hnext; + ips->ips_hnext = NULL; + atomic_dec(&ips->ips_refcount); + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sa_del: " + "successfully deleted first ipsec_sa in chain.\n"); + return 0; + } else { + for (ipstp = ipsec_sadb_hash[hashval]; + ipstp; + ipstp = ipstp->ips_hnext) { + if (ipstp->ips_hnext == ips) { + ipstp->ips_hnext = ips->ips_hnext; + ips->ips_hnext = NULL; + atomic_dec(&ips->ips_refcount); + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sa_del: " + "successfully deleted link in ipsec_sa chain.\n"); + return 0; + } + } + } + + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sa_del: " + "no entries in linked list for hash=%d of SA:%s.\n", + hashval, + sa_len ? sa : " (error)"); + return -ENOENT; +} + +/* + The ipsec_sa table better be locked before it is handed in, or races + might happen +*/ +int +ipsec_sa_delchain(struct ipsec_sa *ips) +{ + struct ipsec_sa *ipsdel; + int error = 0; + char sa[SATOA_BUF]; + size_t sa_len; + + if(ips == NULL) { + KLIPS_PRINT(debug_xform, + "klips_error:ipsec_sa_delchain: " + "null pointer passed in!\n"); + return -ENODATA; + } + + sa_len = satoa(ips->ips_said, 0, sa, SATOA_BUF); + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sa_delchain: " + "passed SA:%s\n", + sa_len ? sa : " (error)"); + while(ips->ips_onext != NULL) { + ips = ips->ips_onext; + } + + while(ips) { + /* XXX send a pfkey message up to advise of deleted ipsec_sa */ + sa_len = satoa(ips->ips_said, 0, sa, SATOA_BUF); + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sa_delchain: " + "unlinking and delting SA:%s", + sa_len ? sa : " (error)"); + ipsdel = ips; + ips = ips->ips_inext; + if(ips != NULL) { + sa_len = satoa(ips->ips_said, 0, sa, SATOA_BUF); + KLIPS_PRINT(debug_xform, + ", inext=%s", + sa_len ? sa : " (error)"); + atomic_dec(&ipsdel->ips_refcount); + ipsdel->ips_inext = NULL; + atomic_dec(&ips->ips_refcount); + ips->ips_onext = NULL; + } + KLIPS_PRINT(debug_xform, + ".\n"); + if((error = ipsec_sa_del(ipsdel))) { + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sa_delchain: " + "ipsec_sa_del returned error %d.\n", -error); + return error; + } + if((error = ipsec_sa_wipe(ipsdel))) { + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sa_delchain: " + "ipsec_sa_wipe returned error %d.\n", -error); + return error; + } + } + return error; +} + +int +ipsec_sadb_cleanup(__u8 proto) +{ + unsigned i; + int error = 0; + struct ipsec_sa *ips, **ipsprev, *ipsdel; + char sa[SATOA_BUF]; + size_t sa_len; + + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sadb_cleanup: " + "cleaning up proto=%d.\n", + proto); + + spin_lock_bh(&tdb_lock); + + for (i = 0; i < SADB_HASHMOD; i++) { + ipsprev = &(ipsec_sadb_hash[i]); + ips = ipsec_sadb_hash[i]; + if(ips != NULL) { + atomic_inc(&ips->ips_refcount); + } + for(; ips != NULL;) { + sa_len = satoa(ips->ips_said, 0, sa, SATOA_BUF); + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sadb_cleanup: " + "checking SA:%s, hash=%d, ref=%d", + sa_len ? sa : " (error)", + i, + ips->ips_ref); + ipsdel = ips; + ips = ipsdel->ips_hnext; + if(ips != NULL) { + atomic_inc(&ips->ips_refcount); + sa_len = satoa(ips->ips_said, 0, sa, SATOA_BUF); + KLIPS_PRINT(debug_xform, + ", hnext=%s", + sa_len ? sa : " (error)"); + } + if(*ipsprev != NULL) { + sa_len = satoa((*ipsprev)->ips_said, 0, sa, SATOA_BUF); + KLIPS_PRINT(debug_xform, + ", *ipsprev=%s", + sa_len ? sa : " (error)"); + if((*ipsprev)->ips_hnext) { + sa_len = satoa((*ipsprev)->ips_hnext->ips_said, 0, sa, SATOA_BUF); + KLIPS_PRINT(debug_xform, + ", *ipsprev->ips_hnext=%s", + sa_len ? sa : " (error)"); + } + } + KLIPS_PRINT(debug_xform, + ".\n"); + if(proto == 0 || (proto == ipsdel->ips_said.proto)) { + sa_len = satoa(ipsdel->ips_said, 0, sa, SATOA_BUF); + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sadb_cleanup: " + "deleting SA chain:%s.\n", + sa_len ? sa : " (error)"); + if((error = ipsec_sa_delchain(ipsdel))) { + SENDERR(-error); + } + ipsprev = &(ipsec_sadb_hash[i]); + ips = ipsec_sadb_hash[i]; + + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sadb_cleanup: " + "deleted SA chain:%s", + sa_len ? sa : " (error)"); + if(ips != NULL) { + sa_len = satoa(ips->ips_said, 0, sa, SATOA_BUF); + KLIPS_PRINT(debug_xform, + ", ipsec_sadb_hash[%d]=%s", + i, + sa_len ? sa : " (error)"); + } + if(*ipsprev != NULL) { + sa_len = satoa((*ipsprev)->ips_said, 0, sa, SATOA_BUF); + KLIPS_PRINT(debug_xform, + ", *ipsprev=%s", + sa_len ? sa : " (error)"); + if((*ipsprev)->ips_hnext != NULL) { + sa_len = satoa((*ipsprev)->ips_hnext->ips_said, 0, sa, SATOA_BUF); + KLIPS_PRINT(debug_xform, + ", *ipsprev->ips_hnext=%s", + sa_len ? sa : " (error)"); + } + } + KLIPS_PRINT(debug_xform, + ".\n"); + } else { + ipsprev = &ipsdel; + } + if(ipsdel != NULL) { + ipsec_sa_put(ipsdel); + } + } + } + errlab: + + spin_unlock_bh(&tdb_lock); + + +#if IPSEC_SA_REF_CODE + /* clean up SA reference table */ + + /* go through the ref table and clean out all the SAs */ + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sadb_cleanup: " + "removing SAref entries and tables."); + { + unsigned table, entry; + for(table = 0; table < IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES; table++) { + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sadb_cleanup: " + "cleaning SAref table=%u.\n", + table); + if(ipsec_sadb.refTable[table] == NULL) { + printk("\n"); + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sadb_cleanup: " + "cleaned %u used refTables.\n", + table); + break; + } + for(entry = 0; entry < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES; entry++) { + if(ipsec_sadb.refTable[table]->entry[entry] != NULL) { + ipsec_sa_delchain(ipsec_sadb.refTable[table]->entry[entry]); + ipsec_sadb.refTable[table]->entry[entry] = NULL; + } + } + } + } +#endif /* IPSEC_SA_REF_CODE */ + + return(error); +} + +int +ipsec_sadb_free(void) +{ + int error = 0; + + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sadb_free: " + "freeing SArefTable memory.\n"); + + /* clean up SA reference table */ + + /* go through the ref table and clean out all the SAs if any are + left and free table memory */ + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sadb_free: " + "removing SAref entries and tables.\n"); + { + unsigned table, entry; + for(table = 0; table < IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES; table++) { + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sadb_free: " + "removing SAref table=%u.\n", + table); + if(ipsec_sadb.refTable[table] == NULL) { + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sadb_free: " + "removed %u used refTables.\n", + table); + break; + } + for(entry = 0; entry < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES; entry++) { + if(ipsec_sadb.refTable[table]->entry[entry] != NULL) { + ipsec_sa_delchain(ipsec_sadb.refTable[table]->entry[entry]); + ipsec_sadb.refTable[table]->entry[entry] = NULL; + } + } + vfree(ipsec_sadb.refTable[table]); + ipsec_sadb.refTable[table] = NULL; + } + } + + return(error); +} + +int +ipsec_sa_wipe(struct ipsec_sa *ips) +{ + if(ips == NULL) { + return -ENODATA; + } + + /* if(atomic_dec_and_test(ips)) { + }; */ + +#if IPSEC_SA_REF_CODE + /* remove me from the SArefTable */ + { + char sa[SATOA_BUF]; + size_t sa_len; + sa_len = satoa(ips->ips_said, 0, sa, SATOA_BUF); + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sa_wipe: " + "removing SA=%s(0p%p), SAref=%d, table=%d(0p%p), entry=%d from the refTable.\n", + sa_len ? sa : " (error)", + ips, + ips->ips_ref, + IPsecSAref2table(IPsecSA2SAref(ips)), + ipsec_sadb.refTable[IPsecSAref2table(IPsecSA2SAref(ips))], + IPsecSAref2entry(IPsecSA2SAref(ips))); + } + if(ips->ips_ref == IPSEC_SAREF_NULL) { + KLIPS_PRINT(debug_xform, + "klips_debug:ipsec_sa_wipe: " + "why does this SA not have a valid SAref?.\n"); + } + ipsec_sadb.refTable[IPsecSAref2table(IPsecSA2SAref(ips))]->entry[IPsecSAref2entry(IPsecSA2SAref(ips))] = NULL; + ips->ips_ref = IPSEC_SAREF_NULL; + ipsec_sa_put(ips); +#endif /* IPSEC_SA_REF_CODE */ + + /* paranoid clean up */ + if(ips->ips_addr_s != NULL) { + memset((caddr_t)(ips->ips_addr_s), 0, ips->ips_addr_s_size); + kfree(ips->ips_addr_s); + } + ips->ips_addr_s = NULL; + + if(ips->ips_addr_d != NULL) { + memset((caddr_t)(ips->ips_addr_d), 0, ips->ips_addr_d_size); + kfree(ips->ips_addr_d); + } + ips->ips_addr_d = NULL; + + if(ips->ips_addr_p != NULL) { + memset((caddr_t)(ips->ips_addr_p), 0, ips->ips_addr_p_size); + kfree(ips->ips_addr_p); + } + ips->ips_addr_p = NULL; + +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL + if(ips->ips_natt_oa) { + memset((caddr_t)(ips->ips_natt_oa), 0, ips->ips_natt_oa_size); + kfree(ips->ips_natt_oa); + } + ips->ips_natt_oa = NULL; +#endif + + if(ips->ips_key_a != NULL) { + memset((caddr_t)(ips->ips_key_a), 0, ips->ips_key_a_size); + kfree(ips->ips_key_a); + } + ips->ips_key_a = NULL; + + if(ips->ips_key_e != NULL) { +#ifdef CONFIG_IPSEC_ALG + if (ips->ips_alg_enc&&ips->ips_alg_enc->ixt_e_destroy_key) { + ips->ips_alg_enc->ixt_e_destroy_key(ips->ips_alg_enc, + ips->ips_key_e); + } else { +#endif /* CONFIG_IPSEC_ALG */ + memset((caddr_t)(ips->ips_key_e), 0, ips->ips_key_e_size); + kfree(ips->ips_key_e); +#ifdef CONFIG_IPSEC_ALG + } +#endif /* CONFIG_IPSEC_ALG */ + } + ips->ips_key_e = NULL; + + if(ips->ips_iv != NULL) { + memset((caddr_t)(ips->ips_iv), 0, ips->ips_iv_size); + kfree(ips->ips_iv); + } + ips->ips_iv = NULL; + + if(ips->ips_ident_s.data != NULL) { + memset((caddr_t)(ips->ips_ident_s.data), + 0, + ips->ips_ident_s.len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident)); + kfree(ips->ips_ident_s.data); + } + ips->ips_ident_s.data = NULL; + + if(ips->ips_ident_d.data != NULL) { + memset((caddr_t)(ips->ips_ident_d.data), + 0, + ips->ips_ident_d.len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident)); + kfree(ips->ips_ident_d.data); + } + ips->ips_ident_d.data = NULL; + +#ifdef CONFIG_IPSEC_ALG + if (ips->ips_alg_enc||ips->ips_alg_auth) { + ipsec_alg_sa_wipe(ips); + } +#endif /* CONFIG_IPSEC_ALG */ + + memset((caddr_t)ips, 0, sizeof(*ips)); + kfree(ips); + ips = NULL; + + return 0; +} diff --git a/linux/net/ipsec/ipsec_sha1.c b/linux/net/ipsec/ipsec_sha1.c new file mode 100644 index 000000000..389a55b06 --- /dev/null +++ b/linux/net/ipsec/ipsec_sha1.c @@ -0,0 +1,219 @@ +/* + * RCSID $Id: ipsec_sha1.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ + +/* + * The rest of the code is derived from sha1.c by Steve Reid, which is + * public domain. + * Minor cosmetic changes to accomodate it in the Linux kernel by ji. + */ + +#include <asm/byteorder.h> +#include <linux/string.h> + +#include "freeswan/ipsec_sha1.h" + +#if defined(rol) +#undef rol +#endif + +#define SHA1HANDSOFF + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +#ifdef __LITTLE_ENDIAN +#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ + |(rol(block->l[i],8)&0x00FF00FF)) +#else +#define blk0(i) block->l[i] +#endif +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + +/* Hash a single 512-bit block. This is the core of the algorithm. */ + +void SHA1Transform(__u32 state[5], __u8 buffer[64]) +{ +__u32 a, b, c, d, e; +typedef union { + unsigned char c[64]; + __u32 l[16]; +} CHAR64LONG16; +CHAR64LONG16* block; +#ifdef SHA1HANDSOFF +static unsigned char workspace[64]; + block = (CHAR64LONG16*)workspace; + memcpy(block, buffer, 64); +#else + block = (CHAR64LONG16*)buffer; +#endif + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; +} + + +/* SHA1Init - Initialize new context */ + +void SHA1Init(void *vcontext) +{ + SHA1_CTX* context = vcontext; + + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ + +void SHA1Update(void *vcontext, unsigned char* data, __u32 len) +{ + SHA1_CTX* context = vcontext; + __u32 i, j; + + j = context->count[0]; + if ((context->count[0] += len << 3) < j) + context->count[1]++; + context->count[1] += (len>>29); + j = (j >> 3) & 63; + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1Transform(context->state, &data[i]); + } + j = 0; + } + else i = 0; + memcpy(&context->buffer[j], &data[i], len - i); +} + + +/* Add padding and return the message digest. */ + +void SHA1Final(unsigned char digest[20], void *vcontext) +{ + __u32 i, j; + unsigned char finalcount[8]; + SHA1_CTX* context = vcontext; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + SHA1Update(context, (unsigned char *)"\200", 1); + while ((context->count[0] & 504) != 448) { + SHA1Update(context, (unsigned char *)"\0", 1); + } + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ + for (i = 0; i < 20; i++) { + digest[i] = (unsigned char) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + /* Wipe variables */ + i = j = 0; + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(&finalcount, 0, 8); +#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */ + SHA1Transform(context->state, context->buffer); +#endif +} + + +/* + * $Log: ipsec_sha1.c,v $ + * Revision 1.1 2004/03/15 20:35:26 as + * added files from freeswan-2.04-x509-1.5.3 + * + * Revision 1.8 2002/09/10 01:45:14 mcr + * changed type of MD5_CTX and SHA1_CTX to void * so that + * the function prototypes would match, and could be placed + * into a pointer to a function. + * + * Revision 1.7 2002/04/24 07:55:32 mcr + * #include patches and Makefiles for post-reorg compilation. + * + * Revision 1.6 2002/04/24 07:36:30 mcr + * Moved from ./klips/net/ipsec/ipsec_sha1.c,v + * + * Revision 1.5 1999/12/13 13:59:13 rgb + * Quick fix to argument size to Update bugs. + * + * Revision 1.4 1999/04/11 00:29:00 henry + * GPL boilerplate + * + * Revision 1.3 1999/04/06 04:54:27 rgb + * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes + * patch shell fixes. + * + * Revision 1.2 1999/01/22 06:55:50 rgb + * 64-bit clean-up. + * + * Revision 1.1 1998/06/18 21:27:50 henry + * move sources from klips/src to klips/net/ipsec, to keep stupid + * kernel-build scripts happier in the presence of symlinks + * + * Revision 1.2 1998/04/23 20:54:04 rgb + * Fixed md5 and sha1 include file nesting issues, to be cleaned up when + * verified. + * + * Revision 1.1 1998/04/09 03:06:11 henry + * sources moved up from linux/net/ipsec + * + * Revision 1.1.1.1 1998/04/08 05:35:05 henry + * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 + * + * Revision 0.4 1997/01/15 01:28:15 ji + * New transform + * + * + */ diff --git a/linux/net/ipsec/ipsec_tunnel.c b/linux/net/ipsec/ipsec_tunnel.c new file mode 100644 index 000000000..de86843bb --- /dev/null +++ b/linux/net/ipsec/ipsec_tunnel.c @@ -0,0 +1,1671 @@ +/* + * IPSEC Tunneling code. Heavily based on drivers/net/new_tunnel.c + * Copyright (C) 1996, 1997 John Ioannidis. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Richard Guy Briggs. + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + */ + +char ipsec_tunnel_c_version[] = "RCSID $Id: ipsec_tunnel.c,v 1.4 2005/06/16 21:21:02 as Exp $"; + +#define __NO_VERSION__ +#include <linux/module.h> +#include <linux/config.h> /* for CONFIG_IP_FORWARD */ +#include <linux/version.h> +#include <linux/kernel.h> /* printk() */ + +#include "freeswan/ipsec_param.h" + +#ifdef MALLOC_SLAB +# include <linux/slab.h> /* kmalloc() */ +#else /* MALLOC_SLAB */ +# include <linux/malloc.h> /* kmalloc() */ +#endif /* MALLOC_SLAB */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/interrupt.h> /* mark_bh */ + +#include <linux/netdevice.h> /* struct device, struct net_device_stats, dev_queue_xmit() and other headers */ +#include <linux/etherdevice.h> /* eth_type_trans */ +#include <linux/ip.h> /* struct iphdr */ +#include <linux/tcp.h> /* struct tcphdr */ +#include <linux/udp.h> /* struct udphdr */ +#include <linux/skbuff.h> +#include <freeswan.h> +#ifdef NET_21 +# include <asm/uaccess.h> +# include <linux/in6.h> +# define ip_chk_addr inet_addr_type +# define IS_MYADDR RTN_LOCAL +# include <net/dst.h> +# undef dev_kfree_skb +# define dev_kfree_skb(a,b) kfree_skb(a) +# define PHYSDEV_TYPE +#endif /* NET_21 */ +#include <asm/checksum.h> +#include <net/icmp.h> /* icmp_send() */ +#include <net/ip.h> +#ifdef NETDEV_23 +# include <linux/netfilter_ipv4.h> +#endif /* NETDEV_23 */ + +#include <linux/if_arp.h> + +#include "freeswan/radij.h" +#include "freeswan/ipsec_life.h" +#include "freeswan/ipsec_xform.h" +#include "freeswan/ipsec_eroute.h" +#include "freeswan/ipsec_encap.h" +#include "freeswan/ipsec_radij.h" +#include "freeswan/ipsec_sa.h" +#include "freeswan/ipsec_tunnel.h" +#include "freeswan/ipsec_xmit.h" +#include "freeswan/ipsec_ipe4.h" +#include "freeswan/ipsec_ah.h" +#include "freeswan/ipsec_esp.h" + +#include <pfkeyv2.h> +#include <pfkey.h> + +#include "freeswan/ipsec_proto.h" +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL +#include <linux/udp.h> +#endif + +static __u32 zeroes[64]; + +#ifdef CONFIG_IPSEC_DEBUG +int debug_tunnel = 0; +#endif /* CONFIG_IPSEC_DEBUG */ + +DEBUG_NO_STATIC int +ipsec_tunnel_open(struct device *dev) +{ + struct ipsecpriv *prv = dev->priv; + + /* + * Can't open until attached. + */ + + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_tunnel_open: " + "dev = %s, prv->dev = %s\n", + dev->name, prv->dev?prv->dev->name:"NONE"); + + if (prv->dev == NULL) + return -ENODEV; + + MOD_INC_USE_COUNT; + return 0; +} + +DEBUG_NO_STATIC int +ipsec_tunnel_close(struct device *dev) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +#ifdef NETDEV_23 +static inline int ipsec_tunnel_xmit2(struct sk_buff *skb) +{ +#ifdef NETDEV_25 /* 2.6 kernels */ + return dst_output(skb); +#else + return ip_send(skb); +#endif +} +#endif /* NETDEV_23 */ + +enum ipsec_xmit_value +ipsec_tunnel_strip_hard_header(struct ipsec_xmit_state *ixs) +{ + /* ixs->physdev->hard_header_len is unreliable and should not be used */ + ixs->hard_header_len = (unsigned char *)(ixs->iph) - ixs->skb->data; + + if(ixs->hard_header_len < 0) { + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_error:ipsec_xmit_strip_hard_header: " + "Negative hard_header_len (%d)?!\n", ixs->hard_header_len); + ixs->stats->tx_dropped++; + return IPSEC_XMIT_BADHHLEN; + } + + /* while ixs->physdev->hard_header_len is unreliable and + * should not be trusted, it accurate and required for ATM, GRE and + * some other interfaces to work. Thanks to Willy Tarreau + * <willy@w.ods.org>. + */ + if(ixs->hard_header_len == 0) { /* no hard header present */ + ixs->hard_header_stripped = 1; + ixs->hard_header_len = ixs->physdev->hard_header_len; + } + +#ifdef CONFIG_IPSEC_DEBUG + if (debug_tunnel & DB_TN_XMIT) { + int i; + char c; + + printk(KERN_INFO "klips_debug:ipsec_xmit_strip_hard_header: " + ">>> skb->len=%ld hard_header_len:%d", + (unsigned long int)ixs->skb->len, ixs->hard_header_len); + c = ' '; + for (i=0; i < ixs->hard_header_len; i++) { + printk("%c%02x", c, ixs->skb->data[i]); + c = ':'; + } + printk(" \n"); + } +#endif /* CONFIG_IPSEC_DEBUG */ + + KLIPS_IP_PRINT(debug_tunnel & DB_TN_XMIT, ixs->iph); + + KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, + "klips_debug:ipsec_xmit_strip_hard_header: " + "Original head,tailroom: %d,%d\n", + skb_headroom(ixs->skb), skb_tailroom(ixs->skb)); + + return IPSEC_XMIT_OK; +} + +enum ipsec_xmit_value +ipsec_tunnel_SAlookup(struct ipsec_xmit_state *ixs) +{ + /* + * First things first -- look us up in the erouting tables. + */ + ixs->matcher.sen_len = sizeof (struct sockaddr_encap); + ixs->matcher.sen_family = AF_ENCAP; + ixs->matcher.sen_type = SENT_IP4; + ixs->matcher.sen_ip_src.s_addr = ixs->iph->saddr; + ixs->matcher.sen_ip_dst.s_addr = ixs->iph->daddr; + ixs->matcher.sen_proto = ixs->iph->protocol; + ipsec_extract_ports(ixs->iph, &ixs->matcher); + + /* + * The spinlock is to prevent any other process from accessing or deleting + * the eroute while we are using and updating it. + */ + spin_lock(&eroute_lock); + + ixs->eroute = ipsec_findroute(&ixs->matcher); + + if(ixs->iph->protocol == IPPROTO_UDP) { + if(ixs->skb->sk) { + ixs->sport=ntohs(ixs->skb->sk->sport); + ixs->dport=ntohs(ixs->skb->sk->dport); + } else if((ntohs(ixs->iph->frag_off) & IP_OFFSET) == 0 && + ((ixs->skb->len - ixs->hard_header_len) >= + ((ixs->iph->ihl << 2) + sizeof(struct udphdr)))) { + ixs->sport=ntohs(((struct udphdr*)((caddr_t)ixs->iph+(ixs->iph->ihl<<2)))->source); + ixs->dport=ntohs(((struct udphdr*)((caddr_t)ixs->iph + (ixs->iph->ihl<<2)))->dest); + } else { + ixs->sport=0; ixs->dport=0; + } + } + + /* default to a %drop eroute */ + ixs->outgoing_said.proto = IPPROTO_INT; + ixs->outgoing_said.spi = htonl(SPI_DROP); + ixs->outgoing_said.dst.s_addr = INADDR_ANY; + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_xmit_SAlookup: " + "checking for local udp/500 IKE packet " + "saddr=%x, er=0p%p, daddr=%x, er_dst=%x, proto=%d sport=%d dport=%d\n", + ntohl((unsigned int)ixs->iph->saddr), + ixs->eroute, + ntohl((unsigned int)ixs->iph->daddr), + ixs->eroute ? ntohl((unsigned int)ixs->eroute->er_said.dst.s_addr) : 0, + ixs->iph->protocol, + ixs->sport, + ixs->dport); + + /* + * Quick cheat for now...are we udp/500 or udp/4500? If so, let it through + * without interference since it is most likely an IKE packet. + */ + + if (ip_chk_addr((unsigned long)ixs->iph->saddr) == IS_MYADDR + && (!ixs->eroute + || ixs->iph->daddr == ixs->eroute->er_said.dst.s_addr + || INADDR_ANY == ixs->eroute->er_said.dst.s_addr) + && ((ixs->sport == 500) || (ixs->sport == 4500))) { + /* Whatever the eroute, this is an IKE message + * from us (i.e. not being forwarded). + * Furthermore, if there is a tunnel eroute, + * the destination is the peer for this eroute. + * So %pass the packet: modify the default %drop. + */ + ixs->outgoing_said.spi = htonl(SPI_PASS); + if(!(ixs->skb->sk) && ((ntohs(ixs->iph->frag_off) & IP_MF) != 0)) { + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_xmit_SAlookup: " + "local UDP/500 (probably IKE) passthrough: base fragment, rest of fragments will probably get filtered.\n"); + } + } else if (ixs->eroute) { + ixs->eroute->er_count++; + ixs->eroute->er_lasttime = jiffies/HZ; + if(ixs->eroute->er_said.proto==IPPROTO_INT + && ixs->eroute->er_said.spi==htonl(SPI_HOLD)) { + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_xmit_SAlookup: " + "shunt SA of HOLD: skb stored in HOLD.\n"); + if(ixs->eroute->er_last != NULL) { + kfree_skb(ixs->eroute->er_last); + } + ixs->eroute->er_last = ixs->skb; + ixs->skb = NULL; + ixs->stats->tx_dropped++; + spin_unlock(&eroute_lock); + return IPSEC_XMIT_STOLEN; + } + ixs->outgoing_said = ixs->eroute->er_said; + ixs->eroute_pid = ixs->eroute->er_pid; + /* Copy of the ident for the TRAP/TRAPSUBNET eroutes */ + if(ixs->outgoing_said.proto==IPPROTO_INT + && (ixs->outgoing_said.spi==htonl(SPI_TRAP) + || (ixs->outgoing_said.spi==htonl(SPI_TRAPSUBNET)))) { + int len; + + ixs->ips.ips_ident_s.type = ixs->eroute->er_ident_s.type; + ixs->ips.ips_ident_s.id = ixs->eroute->er_ident_s.id; + ixs->ips.ips_ident_s.len = ixs->eroute->er_ident_s.len; + if (ixs->ips.ips_ident_s.len) { + len = ixs->ips.ips_ident_s.len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident); + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_xmit_SAlookup: " + "allocating %d bytes for ident_s shunt SA of HOLD: skb stored in HOLD.\n", + len); + if ((ixs->ips.ips_ident_s.data = kmalloc(len, GFP_ATOMIC)) == NULL) { + printk(KERN_WARNING "klips_debug:ipsec_xmit_SAlookup: " + "Failed, tried to allocate %d bytes for source ident.\n", + len); + ixs->stats->tx_dropped++; + spin_unlock(&eroute_lock); + return IPSEC_XMIT_ERRMEMALLOC; + } + memcpy(ixs->ips.ips_ident_s.data, ixs->eroute->er_ident_s.data, len); + } + ixs->ips.ips_ident_d.type = ixs->eroute->er_ident_d.type; + ixs->ips.ips_ident_d.id = ixs->eroute->er_ident_d.id; + ixs->ips.ips_ident_d.len = ixs->eroute->er_ident_d.len; + if (ixs->ips.ips_ident_d.len) { + len = ixs->ips.ips_ident_d.len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident); + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_xmit_SAlookup: " + "allocating %d bytes for ident_d shunt SA of HOLD: skb stored in HOLD.\n", + len); + if ((ixs->ips.ips_ident_d.data = kmalloc(len, GFP_ATOMIC)) == NULL) { + printk(KERN_WARNING "klips_debug:ipsec_xmit_SAlookup: " + "Failed, tried to allocate %d bytes for dest ident.\n", + len); + ixs->stats->tx_dropped++; + spin_unlock(&eroute_lock); + return IPSEC_XMIT_ERRMEMALLOC; + } + memcpy(ixs->ips.ips_ident_d.data, ixs->eroute->er_ident_d.data, len); + } + } + } + + spin_unlock(&eroute_lock); + return IPSEC_XMIT_OK; +} + +enum ipsec_xmit_value +ipsec_tunnel_restore_hard_header(struct ipsec_xmit_state*ixs) +{ + KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, + "klips_debug:ipsec_xmit_restore_hard_header: " + "After recursive xforms -- head,tailroom: %d,%d\n", + skb_headroom(ixs->skb), + skb_tailroom(ixs->skb)); + + if(ixs->saved_header) { + if(skb_headroom(ixs->skb) < ixs->hard_header_len) { + printk(KERN_WARNING + "klips_error:ipsec_xmit_restore_hard_header: " + "tried to skb_push hhlen=%d, %d available. This should never happen, please report.\n", + ixs->hard_header_len, + skb_headroom(ixs->skb)); + ixs->stats->tx_errors++; + return IPSEC_XMIT_PUSHPULLERR; + + } + skb_push(ixs->skb, ixs->hard_header_len); + { + int i; + for (i = 0; i < ixs->hard_header_len; i++) { + ixs->skb->data[i] = ixs->saved_header[i]; + } + } + } +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL + if (ixs->natt_type && ixs->natt_head) { + struct iphdr *ipp = ixs->skb->nh.iph; + struct udphdr *udp; + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_tunnel_start_xmit: " + "encapsulating packet into UDP (NAT-Traversal) (%d %d)\n", + ixs->natt_type, ixs->natt_head); + ixs->iphlen = ipp->ihl << 2; + ipp->tot_len = + htons(ntohs(ipp->tot_len) + ixs->natt_head); + if(skb_tailroom(ixs->skb) < ixs->natt_head) { + printk(KERN_WARNING "klips_error:ipsec_tunnel_start_xmit: " + "tried to skb_put %d, %d available. " + "This should never happen, please report.\n", + ixs->natt_head, + skb_tailroom(ixs->skb)); + ixs->stats->tx_errors++; + return IPSEC_XMIT_ESPUDP; + } + skb_put(ixs->skb, ixs->natt_head); + udp = (struct udphdr *)((char *)ipp + ixs->iphlen); + /* move ESP hdr after UDP hdr */ + memmove((void *)((char *)udp + ixs->natt_head), + (void *)(udp), + ntohs(ipp->tot_len) - ixs->iphlen - ixs->natt_head); + /* clear UDP & Non-IKE Markers (if any) */ + memset(udp, 0, ixs->natt_head); + /* fill UDP with usefull informations ;-) */ + udp->source = htons(ixs->natt_sport); + udp->dest = htons(ixs->natt_dport); + udp->len = htons(ntohs(ipp->tot_len) - ixs->iphlen); + /* set protocol */ + ipp->protocol = IPPROTO_UDP; + /* fix IP checksum */ + ipp->check = 0; + ipp->check = ip_fast_csum((unsigned char *)ipp, ipp->ihl); + } +#endif + KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, + "klips_debug:ipsec_xmit_restore_hard_header: " + "With hard_header, final head,tailroom: %d,%d\n", + skb_headroom(ixs->skb), + skb_tailroom(ixs->skb)); + + return IPSEC_XMIT_OK; +} + +enum ipsec_xmit_value +ipsec_tunnel_send(struct ipsec_xmit_state*ixs) +{ +#ifdef NETDEV_25 + struct flowi fl; +#endif + +#ifdef NET_21 /* 2.2 and 2.4 kernels */ + /* new route/dst cache code from James Morris */ + ixs->skb->dev = ixs->physdev; +#ifdef NETDEV_25 + fl.oif = ixs->physdev->iflink; + fl.nl_u.ip4_u.daddr = ixs->skb->nh.iph->daddr; + fl.nl_u.ip4_u.saddr = ixs->pass ? 0 : ixs->skb->nh.iph->saddr; + fl.nl_u.ip4_u.tos = RT_TOS(ixs->skb->nh.iph->tos); + fl.proto = ixs->skb->nh.iph->protocol; + if ((ixs->error = ip_route_output_key(&ixs->route, &fl))) { +#else + /*skb_orphan(ixs->skb);*/ + if((ixs->error = ip_route_output(&ixs->route, + ixs->skb->nh.iph->daddr, + ixs->pass ? 0 : ixs->skb->nh.iph->saddr, + RT_TOS(ixs->skb->nh.iph->tos), + /* mcr->rgb: should this be 0 instead? */ + ixs->physdev->iflink))) { +#endif + ixs->stats->tx_errors++; + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_xmit_send: " + "ip_route_output failed with error code %d, rt->u.dst.dev=%s, dropped\n", + ixs->error, + ixs->route->u.dst.dev->name); + return IPSEC_XMIT_ROUTEERR; + } + if(ixs->dev == ixs->route->u.dst.dev) { + ip_rt_put(ixs->route); + /* This is recursion, drop it. */ + ixs->stats->tx_errors++; + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_xmit_send: " + "suspect recursion, dev=rt->u.dst.dev=%s, dropped\n", + ixs->dev->name); + return IPSEC_XMIT_RECURSDETECT; + } + dst_release(ixs->skb->dst); + ixs->skb->dst = &ixs->route->u.dst; + ixs->stats->tx_bytes += ixs->skb->len; + if(ixs->skb->len < ixs->skb->nh.raw - ixs->skb->data) { + ixs->stats->tx_errors++; + printk(KERN_WARNING + "klips_error:ipsec_xmit_send: " + "tried to __skb_pull nh-data=%ld, %d available. This should never happen, please report.\n", + (unsigned long)(ixs->skb->nh.raw - ixs->skb->data), + ixs->skb->len); + return IPSEC_XMIT_PUSHPULLERR; + } + __skb_pull(ixs->skb, ixs->skb->nh.raw - ixs->skb->data); +#ifdef SKB_RESET_NFCT + if(!ixs->pass) { + nf_conntrack_put(ixs->skb->nfct); + ixs->skb->nfct = NULL; + } +#ifdef CONFIG_NETFILTER_DEBUG + ixs->skb->nf_debug = 0; +#endif /* CONFIG_NETFILTER_DEBUG */ +#endif /* SKB_RESET_NFCT */ + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_xmit_send: " + "...done, calling ip_send() on device:%s\n", + ixs->skb->dev ? ixs->skb->dev->name : "NULL"); + KLIPS_IP_PRINT(debug_tunnel & DB_TN_XMIT, ixs->skb->nh.iph); +#ifdef NETDEV_23 /* 2.4 kernels */ + { + int err; + + err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, ixs->skb, NULL, ixs->route->u.dst.dev, + ipsec_tunnel_xmit2); + if(err != NET_XMIT_SUCCESS && err != NET_XMIT_CN) { + if(net_ratelimit()) + printk(KERN_ERR + "klips_error:ipsec_xmit_send: " + "ip_send() failed, err=%d\n", + -err); + ixs->stats->tx_errors++; + ixs->stats->tx_aborted_errors++; + ixs->skb = NULL; + return IPSEC_XMIT_IPSENDFAILURE; + } + } +#else /* NETDEV_23 */ /* 2.2 kernels */ + ip_send(ixs->skb); +#endif /* NETDEV_23 */ +#else /* NET_21 */ /* 2.0 kernels */ + ixs->skb->arp = 1; + /* ISDN/ASYNC PPP from Matjaz Godec. */ + /* skb->protocol = htons(ETH_P_IP); */ + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_xmit_send: " + "...done, calling dev_queue_xmit() or ip_fragment().\n"); + IP_SEND(ixs->skb, ixs->physdev); +#endif /* NET_21 */ + ixs->stats->tx_packets++; + + ixs->skb = NULL; + + return IPSEC_XMIT_OK; +} + +void +ipsec_tunnel_cleanup(struct ipsec_xmit_state*ixs) +{ +#if defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE) + netif_wake_queue(ixs->dev); +#else /* defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE) */ + ixs->dev->tbusy = 0; +#endif /* defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE) */ + if(ixs->saved_header) { + kfree(ixs->saved_header); + } + if(ixs->skb) { + dev_kfree_skb(ixs->skb, FREE_WRITE); + } + if(ixs->oskb) { + dev_kfree_skb(ixs->oskb, FREE_WRITE); + } + if (ixs->ips.ips_ident_s.data) { + kfree(ixs->ips.ips_ident_s.data); + } + if (ixs->ips.ips_ident_d.data) { + kfree(ixs->ips.ips_ident_d.data); + } +} + +/* + * This function assumes it is being called from dev_queue_xmit() + * and that skb is filled properly by that function. + */ +int +ipsec_tunnel_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct ipsec_xmit_state ixs_mem; + struct ipsec_xmit_state *ixs = &ixs_mem; + enum ipsec_xmit_value stat; + +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL + ixs->natt_type = 0, ixs->natt_head = 0; + ixs->natt_sport = 0, ixs->natt_dport = 0; +#endif + + memset((caddr_t)ixs, 0, sizeof(*ixs)); + ixs->oskb = NULL; + ixs->saved_header = NULL; /* saved copy of the hard header */ + ixs->route = NULL; + memset((caddr_t)&(ixs->ips), 0, sizeof(ixs->ips)); + ixs->dev = dev; + ixs->skb = skb; + + stat = ipsec_xmit_sanity_check_dev(ixs); + if(stat != IPSEC_XMIT_OK) { + goto cleanup; + } + + stat = ipsec_xmit_sanity_check_skb(ixs); + if(stat != IPSEC_XMIT_OK) { + goto cleanup; + } + + stat = ipsec_tunnel_strip_hard_header(ixs); + if(stat != IPSEC_XMIT_OK) { + goto cleanup; + } + + stat = ipsec_tunnel_SAlookup(ixs); + if(stat != IPSEC_XMIT_OK) { + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_tunnel_start_xmit: SAlookup failed: %d\n", + stat); + goto cleanup; + } + + ixs->innersrc = ixs->iph->saddr; + /* start encapsulation loop here XXX */ + do { + stat = ipsec_xmit_encap_bundle(ixs); + if(stat != IPSEC_XMIT_OK) { + if(stat == IPSEC_XMIT_PASS) { + goto bypass; + } + + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_tunnel_start_xmit: encap_bundle failed: %d\n", + stat); + goto cleanup; + } + + ixs->matcher.sen_ip_src.s_addr = ixs->iph->saddr; + ixs->matcher.sen_ip_dst.s_addr = ixs->iph->daddr; + ixs->matcher.sen_proto = ixs->iph->protocol; + ipsec_extract_ports(ixs->iph, &ixs->matcher); + + spin_lock(&eroute_lock); + ixs->eroute = ipsec_findroute(&ixs->matcher); + if(ixs->eroute) { + ixs->outgoing_said = ixs->eroute->er_said; + ixs->eroute_pid = ixs->eroute->er_pid; + ixs->eroute->er_count++; + ixs->eroute->er_lasttime = jiffies/HZ; + } + spin_unlock(&eroute_lock); + + KLIPS_PRINT((debug_tunnel & DB_TN_XMIT) && + /* ((ixs->orgdst != ixs->newdst) || (ixs->orgsrc != ixs->newsrc)) */ + (ixs->orgedst != ixs->outgoing_said.dst.s_addr) && + ixs->outgoing_said.dst.s_addr && + ixs->eroute, + "klips_debug:ipsec_tunnel_start_xmit: " + "We are recursing here.\n"); + + } while(/*((ixs->orgdst != ixs->newdst) || (ixs->orgsrc != ixs->newsrc))*/ + (ixs->orgedst != ixs->outgoing_said.dst.s_addr) && + ixs->outgoing_said.dst.s_addr && + ixs->eroute); + + stat = ipsec_tunnel_restore_hard_header(ixs); + if(stat != IPSEC_XMIT_OK) { + goto cleanup; + } + + bypass: + stat = ipsec_tunnel_send(ixs); + + cleanup: + ipsec_tunnel_cleanup(ixs); + + return 0; +} + +DEBUG_NO_STATIC struct net_device_stats * +ipsec_tunnel_get_stats(struct device *dev) +{ + return &(((struct ipsecpriv *)(dev->priv))->mystats); +} + +/* + * Revectored calls. + * For each of these calls, a field exists in our private structure. + */ + +DEBUG_NO_STATIC int +ipsec_tunnel_hard_header(struct sk_buff *skb, struct device *dev, + unsigned short type, void *daddr, void *saddr, unsigned len) +{ + struct ipsecpriv *prv = dev->priv; + struct device *tmp; + int ret; + struct net_device_stats *stats; /* This device's statistics */ + + if(skb == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_hard_header: " + "no skb...\n"); + return -ENODATA; + } + + if(dev == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_hard_header: " + "no device...\n"); + return -ENODEV; + } + + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_hard_header: " + "skb->dev=%s dev=%s.\n", + skb->dev ? skb->dev->name : "NULL", + dev->name); + + if(prv == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_hard_header: " + "no private space associated with dev=%s\n", + dev->name ? dev->name : "NULL"); + return -ENODEV; + } + + stats = (struct net_device_stats *) &(prv->mystats); + + if(prv->dev == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_hard_header: " + "no physical device associated with dev=%s\n", + dev->name ? dev->name : "NULL"); + stats->tx_dropped++; + return -ENODEV; + } + + /* check if we have to send a IPv6 packet. It might be a Router + Solicitation, where the building of the packet happens in + reverse order: + 1. ll hdr, + 2. IPv6 hdr, + 3. ICMPv6 hdr + -> skb->nh.raw is still uninitialized when this function is + called!! If this is no IPv6 packet, we can print debugging + messages, otherwise we skip all debugging messages and just + build the ll header */ + if(type != ETH_P_IPV6) { + /* execute this only, if we don't have to build the + header for a IPv6 packet */ + if(!prv->hard_header) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_hard_header: " + "physical device has been detached, packet dropped 0p%p->0p%p len=%d type=%d dev=%s->NULL ", + saddr, + daddr, + len, + type, + dev->name); +#ifdef NET_21 + KLIPS_PRINTMORE(debug_tunnel & DB_TN_REVEC, + "ip=%08x->%08x\n", + (__u32)ntohl(skb->nh.iph->saddr), + (__u32)ntohl(skb->nh.iph->daddr) ); +#else /* NET_21 */ + KLIPS_PRINTMORE(debug_tunnel & DB_TN_REVEC, + "ip=%08x->%08x\n", + (__u32)ntohl(skb->ip_hdr->saddr), + (__u32)ntohl(skb->ip_hdr->daddr) ); +#endif /* NET_21 */ + stats->tx_dropped++; + return -ENODEV; + } + +#define da ((struct device *)(prv->dev))->dev_addr + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_hard_header: " + "Revectored 0p%p->0p%p len=%d type=%d dev=%s->%s dev_addr=%02x:%02x:%02x:%02x:%02x:%02x ", + saddr, + daddr, + len, + type, + dev->name, + prv->dev->name, + da[0], da[1], da[2], da[3], da[4], da[5]); +#ifdef NET_21 + KLIPS_PRINTMORE(debug_tunnel & DB_TN_REVEC, + "ip=%08x->%08x\n", + (__u32)ntohl(skb->nh.iph->saddr), + (__u32)ntohl(skb->nh.iph->daddr) ); +#else /* NET_21 */ + KLIPS_PRINTMORE(debug_tunnel & DB_TN_REVEC, + "ip=%08x->%08x\n", + (__u32)ntohl(skb->ip_hdr->saddr), + (__u32)ntohl(skb->ip_hdr->daddr) ); +#endif /* NET_21 */ + } else { + KLIPS_PRINT(debug_tunnel, + "klips_debug:ipsec_tunnel_hard_header: " + "is IPv6 packet, skip debugging messages, only revector and build linklocal header.\n"); + } + tmp = skb->dev; + skb->dev = prv->dev; + ret = prv->hard_header(skb, prv->dev, type, (void *)daddr, (void *)saddr, len); + skb->dev = tmp; + return ret; +} + +DEBUG_NO_STATIC int +#ifdef NET_21 +ipsec_tunnel_rebuild_header(struct sk_buff *skb) +#else /* NET_21 */ +ipsec_tunnel_rebuild_header(void *buff, struct device *dev, + unsigned long raddr, struct sk_buff *skb) +#endif /* NET_21 */ +{ + struct ipsecpriv *prv = skb->dev->priv; + struct device *tmp; + int ret; + struct net_device_stats *stats; /* This device's statistics */ + + if(skb->dev == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_rebuild_header: " + "no device..."); + return -ENODEV; + } + + if(prv == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_rebuild_header: " + "no private space associated with dev=%s", + skb->dev->name ? skb->dev->name : "NULL"); + return -ENODEV; + } + + stats = (struct net_device_stats *) &(prv->mystats); + + if(prv->dev == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_rebuild_header: " + "no physical device associated with dev=%s", + skb->dev->name ? skb->dev->name : "NULL"); + stats->tx_dropped++; + return -ENODEV; + } + + if(!prv->rebuild_header) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_rebuild_header: " + "physical device has been detached, packet dropped skb->dev=%s->NULL ", + skb->dev->name); +#ifdef NET_21 + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "ip=%08x->%08x\n", + (__u32)ntohl(skb->nh.iph->saddr), + (__u32)ntohl(skb->nh.iph->daddr) ); +#else /* NET_21 */ + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "ip=%08x->%08x\n", + (__u32)ntohl(skb->ip_hdr->saddr), + (__u32)ntohl(skb->ip_hdr->daddr) ); +#endif /* NET_21 */ + stats->tx_dropped++; + return -ENODEV; + } + + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel: " + "Revectored rebuild_header dev=%s->%s ", + skb->dev->name, prv->dev->name); +#ifdef NET_21 + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "ip=%08x->%08x\n", + (__u32)ntohl(skb->nh.iph->saddr), + (__u32)ntohl(skb->nh.iph->daddr) ); +#else /* NET_21 */ + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "ip=%08x->%08x\n", + (__u32)ntohl(skb->ip_hdr->saddr), + (__u32)ntohl(skb->ip_hdr->daddr) ); +#endif /* NET_21 */ + tmp = skb->dev; + skb->dev = prv->dev; + +#ifdef NET_21 + ret = prv->rebuild_header(skb); +#else /* NET_21 */ + ret = prv->rebuild_header(buff, prv->dev, raddr, skb); +#endif /* NET_21 */ + skb->dev = tmp; + return ret; +} + +DEBUG_NO_STATIC int +ipsec_tunnel_set_mac_address(struct device *dev, void *addr) +{ + struct ipsecpriv *prv = dev->priv; + + struct net_device_stats *stats; /* This device's statistics */ + + if(dev == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_set_mac_address: " + "no device..."); + return -ENODEV; + } + + if(prv == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_set_mac_address: " + "no private space associated with dev=%s", + dev->name ? dev->name : "NULL"); + return -ENODEV; + } + + stats = (struct net_device_stats *) &(prv->mystats); + + if(prv->dev == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_set_mac_address: " + "no physical device associated with dev=%s", + dev->name ? dev->name : "NULL"); + stats->tx_dropped++; + return -ENODEV; + } + + if(!prv->set_mac_address) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_set_mac_address: " + "physical device has been detached, cannot set - skb->dev=%s->NULL\n", + dev->name); + return -ENODEV; + } + + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_set_mac_address: " + "Revectored dev=%s->%s addr=0p%p\n", + dev->name, prv->dev->name, addr); + return prv->set_mac_address(prv->dev, addr); + +} + +#ifndef NET_21 +DEBUG_NO_STATIC void +ipsec_tunnel_cache_bind(struct hh_cache **hhp, struct device *dev, + unsigned short htype, __u32 daddr) +{ + struct ipsecpriv *prv = dev->priv; + + struct net_device_stats *stats; /* This device's statistics */ + + if(dev == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_cache_bind: " + "no device..."); + return; + } + + if(prv == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_cache_bind: " + "no private space associated with dev=%s", + dev->name ? dev->name : "NULL"); + return; + } + + stats = (struct net_device_stats *) &(prv->mystats); + + if(prv->dev == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_cache_bind: " + "no physical device associated with dev=%s", + dev->name ? dev->name : "NULL"); + stats->tx_dropped++; + return; + } + + if(!prv->header_cache_bind) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_cache_bind: " + "physical device has been detached, cannot set - skb->dev=%s->NULL\n", + dev->name); + stats->tx_dropped++; + return; + } + + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_cache_bind: " + "Revectored \n"); + prv->header_cache_bind(hhp, prv->dev, htype, daddr); + return; +} +#endif /* !NET_21 */ + + +DEBUG_NO_STATIC void +ipsec_tunnel_cache_update(struct hh_cache *hh, struct device *dev, unsigned char * haddr) +{ + struct ipsecpriv *prv = dev->priv; + + struct net_device_stats *stats; /* This device's statistics */ + + if(dev == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_cache_update: " + "no device..."); + return; + } + + if(prv == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_cache_update: " + "no private space associated with dev=%s", + dev->name ? dev->name : "NULL"); + return; + } + + stats = (struct net_device_stats *) &(prv->mystats); + + if(prv->dev == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_cache_update: " + "no physical device associated with dev=%s", + dev->name ? dev->name : "NULL"); + stats->tx_dropped++; + return; + } + + if(!prv->header_cache_update) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_cache_update: " + "physical device has been detached, cannot set - skb->dev=%s->NULL\n", + dev->name); + return; + } + + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel: " + "Revectored cache_update\n"); + prv->header_cache_update(hh, prv->dev, haddr); + return; +} + +#ifdef NET_21 +DEBUG_NO_STATIC int +ipsec_tunnel_neigh_setup(struct neighbour *n) +{ + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_neigh_setup:\n"); + + if (n->nud_state == NUD_NONE) { + n->ops = &arp_broken_ops; + n->output = n->ops->output; + } + return 0; +} + +DEBUG_NO_STATIC int +ipsec_tunnel_neigh_setup_dev(struct device *dev, struct neigh_parms *p) +{ + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_neigh_setup_dev: " + "setting up %s\n", + dev ? dev->name : "NULL"); + + if (p->tbl->family == AF_INET) { + p->neigh_setup = ipsec_tunnel_neigh_setup; + p->ucast_probes = 0; + p->mcast_probes = 0; + } + return 0; +} +#endif /* NET_21 */ + +/* + * We call the attach routine to attach another device. + */ + +DEBUG_NO_STATIC int +ipsec_tunnel_attach(struct device *dev, struct device *physdev) +{ + int i; + struct ipsecpriv *prv = dev->priv; + + if(dev == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_attach: " + "no device..."); + return -ENODEV; + } + + if(prv == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_attach: " + "no private space associated with dev=%s", + dev->name ? dev->name : "NULL"); + return -ENODATA; + } + + prv->dev = physdev; + prv->hard_start_xmit = physdev->hard_start_xmit; + prv->get_stats = physdev->get_stats; + + if (physdev->hard_header) { + prv->hard_header = physdev->hard_header; + dev->hard_header = ipsec_tunnel_hard_header; + } else + dev->hard_header = NULL; + + if (physdev->rebuild_header) { + prv->rebuild_header = physdev->rebuild_header; + dev->rebuild_header = ipsec_tunnel_rebuild_header; + } else + dev->rebuild_header = NULL; + + if (physdev->set_mac_address) { + prv->set_mac_address = physdev->set_mac_address; + dev->set_mac_address = ipsec_tunnel_set_mac_address; + } else + dev->set_mac_address = NULL; + +#ifndef NET_21 + if (physdev->header_cache_bind) { + prv->header_cache_bind = physdev->header_cache_bind; + dev->header_cache_bind = ipsec_tunnel_cache_bind; + } else + dev->header_cache_bind = NULL; +#endif /* !NET_21 */ + + if (physdev->header_cache_update) { + prv->header_cache_update = physdev->header_cache_update; + dev->header_cache_update = ipsec_tunnel_cache_update; + } else + dev->header_cache_update = NULL; + + dev->hard_header_len = physdev->hard_header_len; + +#ifdef NET_21 +/* prv->neigh_setup = physdev->neigh_setup; */ + dev->neigh_setup = ipsec_tunnel_neigh_setup_dev; +#endif /* NET_21 */ + dev->mtu = 16260; /* 0xfff0; */ /* dev->mtu; */ + prv->mtu = physdev->mtu; + +#ifdef PHYSDEV_TYPE + dev->type = physdev->type; /* ARPHRD_TUNNEL; */ +#endif /* PHYSDEV_TYPE */ + + dev->addr_len = physdev->addr_len; + for (i=0; i<dev->addr_len; i++) { + dev->dev_addr[i] = physdev->dev_addr[i]; + } +#ifdef CONFIG_IPSEC_DEBUG + if(debug_tunnel & DB_TN_INIT) { + printk(KERN_INFO "klips_debug:ipsec_tunnel_attach: " + "physical device %s being attached has HW address: %2x", + physdev->name, physdev->dev_addr[0]); + for (i=1; i < physdev->addr_len; i++) { + printk(":%02x", physdev->dev_addr[i]); + } + printk("\n"); + } +#endif /* CONFIG_IPSEC_DEBUG */ + + return 0; +} + +/* + * We call the detach routine to detach the ipsec tunnel from another device. + */ + +DEBUG_NO_STATIC int +ipsec_tunnel_detach(struct device *dev) +{ + int i; + struct ipsecpriv *prv = dev->priv; + + if(dev == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_detach: " + "no device..."); + return -ENODEV; + } + + if(prv == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, + "klips_debug:ipsec_tunnel_detach: " + "no private space associated with dev=%s", + dev->name ? dev->name : "NULL"); + return -ENODATA; + } + + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_tunnel_detach: " + "physical device %s being detached from virtual device %s\n", + prv->dev ? prv->dev->name : "NULL", + dev->name); + + ipsec_dev_put(prv->dev); + prv->dev = NULL; + prv->hard_start_xmit = NULL; + prv->get_stats = NULL; + + prv->hard_header = NULL; +#ifdef DETACH_AND_DOWN + dev->hard_header = NULL; +#endif /* DETACH_AND_DOWN */ + + prv->rebuild_header = NULL; +#ifdef DETACH_AND_DOWN + dev->rebuild_header = NULL; +#endif /* DETACH_AND_DOWN */ + + prv->set_mac_address = NULL; +#ifdef DETACH_AND_DOWN + dev->set_mac_address = NULL; +#endif /* DETACH_AND_DOWN */ + +#ifndef NET_21 + prv->header_cache_bind = NULL; +#ifdef DETACH_AND_DOWN + dev->header_cache_bind = NULL; +#endif /* DETACH_AND_DOWN */ +#endif /* !NET_21 */ + + prv->header_cache_update = NULL; +#ifdef DETACH_AND_DOWN + dev->header_cache_update = NULL; +#endif /* DETACH_AND_DOWN */ + +#ifdef NET_21 +/* prv->neigh_setup = NULL; */ +#ifdef DETACH_AND_DOWN + dev->neigh_setup = NULL; +#endif /* DETACH_AND_DOWN */ +#endif /* NET_21 */ + dev->hard_header_len = 0; +#ifdef DETACH_AND_DOWN + dev->mtu = 0; +#endif /* DETACH_AND_DOWN */ + prv->mtu = 0; + for (i=0; i<MAX_ADDR_LEN; i++) { + dev->dev_addr[i] = 0; + } + dev->addr_len = 0; +#ifdef PHYSDEV_TYPE + dev->type = ARPHRD_VOID; /* ARPHRD_TUNNEL; */ +#endif /* PHYSDEV_TYPE */ + + return 0; +} + +/* + * We call the clear routine to detach all ipsec tunnels from other devices. + */ +DEBUG_NO_STATIC int +ipsec_tunnel_clear(void) +{ + int i; + struct device *ipsecdev = NULL, *prvdev; + struct ipsecpriv *prv; + char name[9]; + int ret; + + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_tunnel_clear: .\n"); + + for(i = 0; i < IPSEC_NUM_IF; i++) { + ipsecdev = ipsecdevices[i]; + if(ipsecdev != NULL) { + if((prv = (struct ipsecpriv *)(ipsecdev->priv))) { + prvdev = (struct device *)(prv->dev); + if(prvdev) { + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_tunnel_clear: " + "physical device for device %s is %s\n", + name, prvdev->name); + if((ret = ipsec_tunnel_detach(ipsecdev))) { + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_tunnel_clear: " + "error %d detatching device %s from device %s.\n", + ret, name, prvdev->name); + return ret; + } + } + } + } + } + return 0; +} + +DEBUG_NO_STATIC int +ipsec_tunnel_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +{ + struct ipsectunnelconf *cf = (struct ipsectunnelconf *)&ifr->ifr_data; + struct ipsecpriv *prv = dev->priv; + struct device *them; /* physical device */ +#ifdef CONFIG_IP_ALIAS + char *colon; + char realphysname[IFNAMSIZ]; +#endif /* CONFIG_IP_ALIAS */ + + if(dev == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_tunnel_ioctl: " + "device not supplied.\n"); + return -ENODEV; + } + + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_tunnel_ioctl: " + "tncfg service call #%d for dev=%s\n", + cmd, + dev->name ? dev->name : "NULL"); + switch (cmd) { + /* attach a virtual ipsec? device to a physical device */ + case IPSEC_SET_DEV: + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_tunnel_ioctl: " + "calling ipsec_tunnel_attatch...\n"); +#ifdef CONFIG_IP_ALIAS + /* If this is an IP alias interface, get its real physical name */ + strncpy(realphysname, cf->cf_name, IFNAMSIZ); + realphysname[IFNAMSIZ-1] = 0; + colon = strchr(realphysname, ':'); + if (colon) *colon = 0; + them = ipsec_dev_get(realphysname); +#else /* CONFIG_IP_ALIAS */ + them = ipsec_dev_get(cf->cf_name); +#endif /* CONFIG_IP_ALIAS */ + + if (them == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_tunnel_ioctl: " + "physical device %s requested is null\n", + cf->cf_name); + return -ENXIO; + } + +#if 0 + if (them->flags & IFF_UP) { + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_tunnel_ioctl: " + "physical device %s requested is not up.\n", + cf->cf_name); + ipsec_dev_put(them); + return -ENXIO; + } +#endif + + if (prv && prv->dev) { + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_tunnel_ioctl: " + "virtual device is already connected to %s.\n", + prv->dev->name ? prv->dev->name : "NULL"); + ipsec_dev_put(them); + return -EBUSY; + } + return ipsec_tunnel_attach(dev, them); + + case IPSEC_DEL_DEV: + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_tunnel_ioctl: " + "calling ipsec_tunnel_detatch.\n"); + if (! prv->dev) { + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_tunnel_ioctl: " + "physical device not connected.\n"); + return -ENODEV; + } + return ipsec_tunnel_detach(dev); + + case IPSEC_CLR_DEV: + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_tunnel_ioctl: " + "calling ipsec_tunnel_clear.\n"); + return ipsec_tunnel_clear(); + + default: + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_tunnel_ioctl: " + "unknown command %d.\n", + cmd); + return -EOPNOTSUPP; + } +} + +int +ipsec_device_event(struct notifier_block *unused, unsigned long event, void *ptr) +{ + struct device *dev = ptr; + struct device *ipsec_dev; + struct ipsecpriv *priv; + int i; + + if (dev == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_device_event: " + "dev=NULL for event type %ld.\n", + event); + return(NOTIFY_DONE); + } + + /* check for loopback devices */ + if (dev && (dev->flags & IFF_LOOPBACK)) { + return(NOTIFY_DONE); + } + + switch (event) { + case NETDEV_DOWN: + /* look very carefully at the scope of these compiler + directives before changing anything... -- RGB */ +#ifdef NET_21 + case NETDEV_UNREGISTER: + switch (event) { + case NETDEV_DOWN: +#endif /* NET_21 */ + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_device_event: " + "NETDEV_DOWN dev=%s flags=%x\n", + dev->name, + dev->flags); + if(strncmp(dev->name, "ipsec", strlen("ipsec")) == 0) { + printk(KERN_CRIT "IPSEC EVENT: KLIPS device %s shut down.\n", + dev->name); + } +#ifdef NET_21 + break; + case NETDEV_UNREGISTER: + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_device_event: " + "NETDEV_UNREGISTER dev=%s flags=%x\n", + dev->name, + dev->flags); + break; + } +#endif /* NET_21 */ + + /* find the attached physical device and detach it. */ + for(i = 0; i < IPSEC_NUM_IF; i++) { + ipsec_dev = ipsecdevices[i]; + + if(ipsec_dev) { + priv = (struct ipsecpriv *)(ipsec_dev->priv); + if(priv) { + ; + if(((struct device *)(priv->dev)) == dev) { + /* dev_close(ipsec_dev); */ + /* return */ ipsec_tunnel_detach(ipsec_dev); + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_device_event: " + "device '%s' has been detached.\n", + ipsec_dev->name); + break; + } + } else { + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_device_event: " + "device '%s' has no private data space!\n", + ipsec_dev->name); + } + } + } + break; + case NETDEV_UP: + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_device_event: " + "NETDEV_UP dev=%s\n", + dev->name); + break; +#ifdef NET_21 + case NETDEV_REBOOT: + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_device_event: " + "NETDEV_REBOOT dev=%s\n", + dev->name); + break; + case NETDEV_CHANGE: + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_device_event: " + "NETDEV_CHANGE dev=%s flags=%x\n", + dev->name, + dev->flags); + break; + case NETDEV_REGISTER: + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_device_event: " + "NETDEV_REGISTER dev=%s\n", + dev->name); + break; + case NETDEV_CHANGEMTU: + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_device_event: " + "NETDEV_CHANGEMTU dev=%s to mtu=%d\n", + dev->name, + dev->mtu); + break; + case NETDEV_CHANGEADDR: + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_device_event: " + "NETDEV_CHANGEADDR dev=%s\n", + dev->name); + break; + case NETDEV_GOING_DOWN: + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_device_event: " + "NETDEV_GOING_DOWN dev=%s\n", + dev->name); + break; + case NETDEV_CHANGENAME: + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_device_event: " + "NETDEV_CHANGENAME dev=%s\n", + dev->name); + break; +#endif /* NET_21 */ + default: + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_device_event: " + "event type %ld unrecognised for dev=%s\n", + event, + dev->name); + break; + } + return NOTIFY_DONE; +} + +/* + * Called when an ipsec tunnel device is initialized. + * The ipsec tunnel device structure is passed to us. + */ + +int +ipsec_tunnel_init(struct device *dev) +{ + int i; + + KLIPS_PRINT(debug_tunnel, + "klips_debug:ipsec_tunnel_init: " + "allocating %lu bytes initialising device: %s\n", + (unsigned long) sizeof(struct ipsecpriv), + dev->name ? dev->name : "NULL"); + + /* Add our tunnel functions to the device */ + dev->open = ipsec_tunnel_open; + dev->stop = ipsec_tunnel_close; + dev->hard_start_xmit = ipsec_tunnel_start_xmit; + dev->get_stats = ipsec_tunnel_get_stats; + + dev->priv = kmalloc(sizeof(struct ipsecpriv), GFP_KERNEL); + if (dev->priv == NULL) + return -ENOMEM; + memset((caddr_t)(dev->priv), 0, sizeof(struct ipsecpriv)); + + for(i = 0; i < sizeof(zeroes); i++) { + ((__u8*)(zeroes))[i] = 0; + } + +#ifndef NET_21 + /* Initialize the tunnel device structure */ + for (i = 0; i < DEV_NUMBUFFS; i++) + skb_queue_head_init(&dev->buffs[i]); +#endif /* !NET_21 */ + + dev->set_multicast_list = NULL; + dev->do_ioctl = ipsec_tunnel_ioctl; + dev->hard_header = NULL; + dev->rebuild_header = NULL; + dev->set_mac_address = NULL; +#ifndef NET_21 + dev->header_cache_bind = NULL; +#endif /* !NET_21 */ + dev->header_cache_update= NULL; + +#ifdef NET_21 +/* prv->neigh_setup = NULL; */ + dev->neigh_setup = ipsec_tunnel_neigh_setup_dev; +#endif /* NET_21 */ + dev->hard_header_len = 0; + dev->mtu = 0; + dev->addr_len = 0; + dev->type = ARPHRD_VOID; /* ARPHRD_TUNNEL; */ /* ARPHRD_ETHER; */ + dev->tx_queue_len = 10; /* Small queue */ + memset((caddr_t)(dev->broadcast),0xFF, ETH_ALEN); /* what if this is not attached to ethernet? */ + + /* New-style flags. */ + dev->flags = IFF_NOARP /* 0 */ /* Petr Novak */; +#ifdef NET_21 + dev_init_buffers(dev); +#else /* NET_21 */ + dev->family = AF_INET; + dev->pa_addr = 0; + dev->pa_brdaddr = 0; + dev->pa_mask = 0; + dev->pa_alen = 4; +#endif /* NET_21 */ + + /* We're done. Have I forgotten anything? */ + return 0; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* Module specific interface (but it links with the rest of IPSEC) */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int +ipsec_tunnel_probe(struct device *dev) +{ + ipsec_tunnel_init(dev); + return 0; +} + +struct device *ipsecdevices[IPSEC_NUM_IF]; + +int +ipsec_tunnel_init_devices(void) +{ + int i; + char name[IFNAMSIZ]; + struct device *dev_ipsec; + + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_tunnel_init_devices: " + "creating and registering IPSEC_NUM_IF=%u devices, allocating %lu per device, IFNAMSIZ=%u.\n", + IPSEC_NUM_IF, + (unsigned long) (sizeof(struct device) + IFNAMSIZ), + IFNAMSIZ); + + for(i = 0; i < IPSEC_NUM_IF; i++) { + sprintf(name, IPSEC_DEV_FORMAT, i); + dev_ipsec = (struct device*)kmalloc(sizeof(struct device), GFP_KERNEL); + if (dev_ipsec == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_tunnel_init_devices: " + "failed to allocate memory for device %s, quitting device init.\n", + name); + return -ENOMEM; + } + memset((caddr_t)dev_ipsec, 0, sizeof(struct device)); +#ifdef NETDEV_23 + strncpy(dev_ipsec->name, name, sizeof(dev_ipsec->name)); +#else /* NETDEV_23 */ + dev_ipsec->name = (char*)kmalloc(IFNAMSIZ, GFP_KERNEL); + if (dev_ipsec->name == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_tunnel_init_devices: " + "failed to allocate memory for device %s name, quitting device init.\n", + name); + return -ENOMEM; + } + memset((caddr_t)dev_ipsec->name, 0, IFNAMSIZ); + strncpy(dev_ipsec->name, name, IFNAMSIZ); +#endif /* NETDEV_23 */ + dev_ipsec->next = NULL; + dev_ipsec->init = &ipsec_tunnel_probe; + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_tunnel_init_devices: " + "registering device %s\n", + dev_ipsec->name); + + /* reference and hold the device reference */ + dev_hold(dev_ipsec); + ipsecdevices[i]=dev_ipsec; + + if (register_netdev(dev_ipsec) != 0) { + KLIPS_PRINT(1 || debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_tunnel_init_devices: " + "registering device %s failed, quitting device init.\n", + dev_ipsec->name); + return -EIO; + } else { + KLIPS_PRINT(debug_tunnel & DB_TN_INIT, + "klips_debug:ipsec_tunnel_init_devices: " + "registering device %s succeeded, continuing...\n", + dev_ipsec->name); + } + } + return 0; +} + +/* void */ +int +ipsec_tunnel_cleanup_devices(void) +{ + int error = 0; + int i; + char name[32]; + struct device *dev_ipsec; + + for(i = 0; i < IPSEC_NUM_IF; i++) { + dev_ipsec = ipsecdevices[i]; + if(dev_ipsec == NULL) { + continue; + } + + /* release reference */ + ipsecdevices[i]=NULL; + ipsec_dev_put(dev_ipsec); + + KLIPS_PRINT(debug_tunnel, "Unregistering %s (refcnt=%d)\n", + name, + atomic_read(&dev_ipsec->refcnt)); + unregister_netdev(dev_ipsec); + KLIPS_PRINT(debug_tunnel, "Unregisted %s\n", name); +#ifndef NETDEV_23 + kfree(dev_ipsec->name); + dev_ipsec->name=NULL; +#endif /* !NETDEV_23 */ + kfree(dev_ipsec->priv); + dev_ipsec->priv=NULL; + } + return error; +} diff --git a/linux/net/ipsec/ipsec_xform.c b/linux/net/ipsec/ipsec_xform.c new file mode 100644 index 000000000..677f83aaf --- /dev/null +++ b/linux/net/ipsec/ipsec_xform.c @@ -0,0 +1,73 @@ +/* + * Common routines for IPSEC transformations. + * Copyright (C) 1996, 1997 John Ioannidis. + * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + * + * RCSID $Id: ipsec_xform.c,v 1.2 2004/06/13 19:57:50 as Exp $ + */ + +#include <linux/config.h> +#include <linux/version.h> +#include <linux/kernel.h> /* printk() */ + +#include "freeswan/ipsec_param.h" + +#ifdef MALLOC_SLAB +# include <linux/slab.h> /* kmalloc() */ +#else /* MALLOC_SLAB */ +# include <linux/malloc.h> /* kmalloc() */ +#endif /* MALLOC_SLAB */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/interrupt.h> /* mark_bh */ + +#include <linux/netdevice.h> /* struct device, and other headers */ +#include <linux/etherdevice.h> /* eth_type_trans */ +#include <linux/ip.h> /* struct iphdr */ +#include <linux/skbuff.h> +#include <linux/random.h> /* get_random_bytes() */ +#include <freeswan.h> +#ifdef SPINLOCK +# ifdef SPINLOCK_23 +# include <linux/spinlock.h> /* *lock* */ +# else /* SPINLOCK_23 */ +# include <asm/spinlock.h> /* *lock* */ +# endif /* SPINLOCK_23 */ +#endif /* SPINLOCK */ +#ifdef NET_21 +# include <asm/uaccess.h> +# include <linux/in6.h> +#endif +#include <asm/checksum.h> +#include <net/ip.h> + +#include "freeswan/radij.h" +#include "freeswan/ipsec_encap.h" +#include "freeswan/ipsec_radij.h" +#include "freeswan/ipsec_xform.h" +#include "freeswan/ipsec_ipe4.h" +#include "freeswan/ipsec_ah.h" +#include "freeswan/ipsec_esp.h" + +#include <pfkeyv2.h> +#include <pfkey.h> + +#ifdef CONFIG_IPSEC_DEBUG +int debug_xform = 0; +#endif /* CONFIG_IPSEC_DEBUG */ + +#ifdef SPINLOCK +spinlock_t tdb_lock = SPIN_LOCK_UNLOCKED; +#else /* SPINLOCK */ +spinlock_t tdb_lock; +#endif /* SPINLOCK */ diff --git a/linux/net/ipsec/ipsec_xmit.c b/linux/net/ipsec/ipsec_xmit.c new file mode 100644 index 000000000..bb390bcf9 --- /dev/null +++ b/linux/net/ipsec/ipsec_xmit.c @@ -0,0 +1,1782 @@ +/* + * IPSEC Transmit code. + * Copyright (C) 1996, 1997 John Ioannidis. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Richard Guy Briggs. + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + */ + +char ipsec_xmit_c_version[] = "RCSID $Id: ipsec_xmit.c,v 1.3 2004/06/13 19:37:23 as Exp $"; + +#define __NO_VERSION__ +#include <linux/module.h> +#include <linux/config.h> /* for CONFIG_IP_FORWARD */ +#include <linux/version.h> +#include <linux/kernel.h> /* printk() */ + +#include "freeswan/ipsec_param.h" + +#ifdef MALLOC_SLAB +# include <linux/slab.h> /* kmalloc() */ +#else /* MALLOC_SLAB */ +# include <linux/malloc.h> /* kmalloc() */ +#endif /* MALLOC_SLAB */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/interrupt.h> /* mark_bh */ + +#include <linux/netdevice.h> /* struct device, struct net_device_stats, dev_queue_xmit() and other headers */ +#include <linux/etherdevice.h> /* eth_type_trans */ +#include <linux/ip.h> /* struct iphdr */ +#include <linux/tcp.h> /* struct tcphdr */ +#include <linux/udp.h> /* struct udphdr */ +#include <linux/skbuff.h> +#include <freeswan.h> +#ifdef NET_21 +# define MSS_HACK_ /* experimental */ +# include <asm/uaccess.h> +# include <linux/in6.h> +# include <net/dst.h> +# define proto_priv cb +#endif /* NET_21 */ +#include <asm/checksum.h> +#include <net/icmp.h> /* icmp_send() */ +#include <net/ip.h> +#ifdef NETDEV_23 +# include <linux/netfilter_ipv4.h> +#endif /* NETDEV_23 */ + +#include <linux/if_arp.h> +#ifdef MSS_HACK +# include <net/tcp.h> /* TCP options */ +#endif /* MSS_HACK */ + +#include "freeswan/radij.h" +#include "freeswan/ipsec_life.h" +#include "freeswan/ipsec_xform.h" +#include "freeswan/ipsec_eroute.h" +#include "freeswan/ipsec_encap.h" +#include "freeswan/ipsec_radij.h" +#include "freeswan/ipsec_xmit.h" +#include "freeswan/ipsec_sa.h" +#include "freeswan/ipsec_tunnel.h" +#include "freeswan/ipsec_ipe4.h" +#include "freeswan/ipsec_ah.h" +#include "freeswan/ipsec_esp.h" + +#ifdef CONFIG_IPSEC_IPCOMP +#include "freeswan/ipcomp.h" +#endif /* CONFIG_IPSEC_IPCOMP */ + +#include <pfkeyv2.h> +#include <pfkey.h> + +#include "freeswan/ipsec_proto.h" +#include "freeswan/ipsec_alg.h" + + +/* + * Stupid kernel API differences in APIs. Not only do some + * kernels not have ip_select_ident, but some have differing APIs, + * and SuSE has one with one parameter, but no way of checking to + * see what is really what. + */ + +#ifdef SUSE_LINUX_2_4_19_IS_STUPID +#define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph) +#else + +/* simplest case, nothing */ +#if !defined(IP_SELECT_IDENT) +#define KLIPS_IP_SELECT_IDENT(iph, skb) do { iph->id = htons(ip_id_count++); } while(0) +#endif + +/* kernels > 2.3.37-ish */ +#if defined(IP_SELECT_IDENT) && !defined(IP_SELECT_IDENT_NEW) +#define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph, skb->dst) +#endif + +/* kernels > 2.4.2 */ +#if defined(IP_SELECT_IDENT) && defined(IP_SELECT_IDENT_NEW) +#define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph, skb->dst, NULL) +#endif + +#endif /* SUSE_LINUX_2_4_19_IS_STUPID */ + + +static __u32 zeroes[64]; + +#ifdef CONFIG_IPSEC_DEBUG +int sysctl_ipsec_debug_verbose = 0; +#endif /* CONFIG_IPSEC_DEBUG */ + +int ipsec_xmit_trap_count = 0; +int ipsec_xmit_trap_sendcount = 0; + +int sysctl_ipsec_icmp = 0; +int sysctl_ipsec_tos = 0; + +/* + * If the IP packet (iph) is a carrying TCP/UDP, then set the encaps + * source and destination ports to those from the TCP/UDP header. + */ +void ipsec_extract_ports(struct iphdr * iph, struct sockaddr_encap * er) +{ + struct udphdr *udp; + + switch (iph->protocol) { + case IPPROTO_UDP: + case IPPROTO_TCP: + /* + * The ports are at the same offsets in a TCP and UDP + * header so hack it ... + */ + udp = (struct udphdr*)(((char*)iph)+(iph->ihl<<2)); + er->sen_sport = udp->source; + er->sen_dport = udp->dest; + break; + default: + er->sen_sport = 0; + er->sen_dport = 0; + break; + } +} + +/* + * A TRAP eroute is installed and we want to replace it with a HOLD + * eroute. + */ +static int create_hold_eroute(struct sk_buff * skb, struct iphdr * iph, + uint32_t eroute_pid) +{ + struct eroute hold_eroute; + struct sa_id hold_said; + struct sk_buff *first, *last; + int error; + + first = last = NULL; + memset((caddr_t)&hold_eroute, 0, sizeof(hold_eroute)); + memset((caddr_t)&hold_said, 0, sizeof(hold_said)); + + hold_said.proto = IPPROTO_INT; + hold_said.spi = htonl(SPI_HOLD); + hold_said.dst.s_addr = INADDR_ANY; + + hold_eroute.er_eaddr.sen_len = sizeof(struct sockaddr_encap); + hold_eroute.er_emask.sen_len = sizeof(struct sockaddr_encap); + hold_eroute.er_eaddr.sen_family = AF_ENCAP; + hold_eroute.er_emask.sen_family = AF_ENCAP; + hold_eroute.er_eaddr.sen_type = SENT_IP4; + hold_eroute.er_emask.sen_type = 255; + + hold_eroute.er_eaddr.sen_ip_src.s_addr = iph->saddr; + hold_eroute.er_eaddr.sen_ip_dst.s_addr = iph->daddr; + hold_eroute.er_emask.sen_ip_src.s_addr = INADDR_BROADCAST; + hold_eroute.er_emask.sen_ip_dst.s_addr = INADDR_BROADCAST; + hold_eroute.er_emask.sen_sport = ~0; + hold_eroute.er_emask.sen_dport = ~0; + hold_eroute.er_pid = eroute_pid; + hold_eroute.er_count = 0; + hold_eroute.er_lasttime = jiffies/HZ; + + hold_eroute.er_eaddr.sen_proto = iph->protocol; + ipsec_extract_ports(iph, &hold_eroute.er_eaddr); + +#ifdef CONFIG_IPSEC_DEBUG + if (debug_pfkey) { + char buf1[64], buf2[64]; + subnettoa(hold_eroute.er_eaddr.sen_ip_src, + hold_eroute.er_emask.sen_ip_src, 0, buf1, sizeof(buf1)); + subnettoa(hold_eroute.er_eaddr.sen_ip_dst, + hold_eroute.er_emask.sen_ip_dst, 0, buf2, sizeof(buf2)); + KLIPS_PRINT(debug_pfkey, + "klips_debug:ipsec_tunnel_start_xmit: " + "calling breakeroute and makeroute for %s:%d->%s:%d %d HOLD eroute.\n", + buf1, ntohs(hold_eroute.er_eaddr.sen_sport), + buf2, ntohs(hold_eroute.er_eaddr.sen_dport), + hold_eroute.er_eaddr.sen_proto); + } +#endif /* CONFIG_IPSEC_DEBUG */ + + if (ipsec_breakroute(&(hold_eroute.er_eaddr), &(hold_eroute.er_emask), + &first, &last)) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:ipsec_tunnel_start_xmit: " + "HOLD breakeroute found nothing.\n"); + } else { + KLIPS_PRINT(debug_pfkey, + "klips_debug:ipsec_tunnel_start_xmit: " + "HOLD breakroute deleted %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u %u\n", + NIPQUAD(hold_eroute.er_eaddr.sen_ip_src), + ntohs(hold_eroute.er_eaddr.sen_sport), + NIPQUAD(hold_eroute.er_eaddr.sen_ip_dst), + ntohs(hold_eroute.er_eaddr.sen_dport), + hold_eroute.er_eaddr.sen_proto); + } + if (first != NULL) + kfree_skb(first); + if (last != NULL) + kfree_skb(last); + + error = ipsec_makeroute(&(hold_eroute.er_eaddr), + &(hold_eroute.er_emask), + hold_said, eroute_pid, skb, NULL, NULL); + if (error) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:ipsec_tunnel_start_xmit: " + "HOLD makeroute returned %d, failed.\n", error); + } else { + KLIPS_PRINT(debug_pfkey, + "klips_debug:ipsec_tunnel_start_xmit: " + "HOLD makeroute call successful.\n"); + } + return (error == 0); +} + +#ifdef CONFIG_IPSEC_DEBUG_ +DEBUG_NO_STATIC void +dmp(char *s, caddr_t bb, int len) +{ + int i; + unsigned char *b = bb; + + if (debug_tunnel) { + printk(KERN_INFO "klips_debug:ipsec_tunnel_:dmp: " + "at %s, len=%d:", + s, + len); + for (i=0; i < len; i++) { + if(!(i%16)){ + printk("\nklips_debug: "); + } + printk(" %02x", *b++); + } + printk("\n"); + } +} +#else /* CONFIG_IPSEC_DEBUG */ +#define dmp(_x, _y, _z) +#endif /* CONFIG_IPSEC_DEBUG */ + +#ifndef SKB_COPY_EXPAND +/* + * This is mostly skbuff.c:skb_copy(). + */ +struct sk_buff * +skb_copy_expand(struct sk_buff *skb, int headroom, int tailroom, int priority) +{ + struct sk_buff *n; + unsigned long offset; + + /* + * Do sanity checking + */ + if((headroom < 0) || (tailroom < 0) || ((headroom+tailroom) < 0)) { + printk(KERN_WARNING + "klips_error:skb_copy_expand: " + "Illegal negative head,tailroom %d,%d\n", + headroom, + tailroom); + return NULL; + } + /* + * Allocate the copy buffer + */ + +#ifndef NET_21 + IS_SKB(skb); +#endif /* !NET_21 */ + + + n=alloc_skb(skb->end - skb->head + headroom + tailroom, priority); + + KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, + "klips_debug:skb_copy_expand: " + "allocating %d bytes, head=0p%p data=0p%p tail=0p%p end=0p%p end-head=%d tail-data=%d\n", + skb->end - skb->head + headroom + tailroom, + skb->head, + skb->data, + skb->tail, + skb->end, + skb->end - skb->head, + skb->tail - skb->data); + + if(n==NULL) + return NULL; + + /* + * Shift between the two data areas in bytes + */ + + /* Set the data pointer */ + skb_reserve(n,skb->data-skb->head+headroom); + /* Set the tail pointer and length */ + if(skb_tailroom(n) < skb->len) { + printk(KERN_WARNING "klips_error:skb_copy_expand: " + "tried to skb_put %ld, %d available. This should never happen, please report.\n", + (unsigned long int)skb->len, + skb_tailroom(n)); + ipsec_kfree_skb(n); + return NULL; + } + skb_put(n,skb->len); + + offset=n->head + headroom - skb->head; + + /* Copy the bytes */ + memcpy(n->head + headroom, skb->head,skb->end-skb->head); +#ifdef NET_21 + n->csum=skb->csum; + n->priority=skb->priority; + n->dst=dst_clone(skb->dst); + if(skb->nh.raw) + n->nh.raw=skb->nh.raw+offset; +#ifndef NETDEV_23 + n->is_clone=0; +#endif /* NETDEV_23 */ + atomic_set(&n->users, 1); + n->destructor = NULL; + n->security=skb->security; +#else /* NET_21 */ + n->link3=NULL; + n->when=skb->when; + if(skb->ip_hdr) + n->ip_hdr=(struct iphdr *)(((char *)skb->ip_hdr)+offset); + n->saddr=skb->saddr; + n->daddr=skb->daddr; + n->raddr=skb->raddr; + n->seq=skb->seq; + n->end_seq=skb->end_seq; + n->ack_seq=skb->ack_seq; + n->acked=skb->acked; + n->free=1; + n->arp=skb->arp; + n->tries=0; + n->lock=0; + n->users=0; +#endif /* NET_21 */ + n->protocol=skb->protocol; + n->list=NULL; + n->sk=NULL; + n->dev=skb->dev; + if(skb->h.raw) + n->h.raw=skb->h.raw+offset; + if(skb->mac.raw) + n->mac.raw=skb->mac.raw+offset; + memcpy(n->proto_priv, skb->proto_priv, sizeof(skb->proto_priv)); +#ifndef NETDEV_23 + n->used=skb->used; +#endif /* !NETDEV_23 */ + n->pkt_type=skb->pkt_type; + n->stamp=skb->stamp; + +#ifndef NET_21 + IS_SKB(n); +#endif /* !NET_21 */ + return n; +} +#endif /* !SKB_COPY_EXPAND */ + +#ifdef CONFIG_IPSEC_DEBUG +void +ipsec_print_ip(struct iphdr *ip) +{ + char buf[ADDRTOA_BUF]; + + printk(KERN_INFO "klips_debug: IP:"); + printk(" ihl:%d", ip->ihl << 2); + printk(" ver:%d", ip->version); + printk(" tos:%d", ip->tos); + printk(" tlen:%d", ntohs(ip->tot_len)); + printk(" id:%d", ntohs(ip->id)); + printk(" %s%s%sfrag_off:%d", + ip->frag_off & __constant_htons(IP_CE) ? "CE " : "", + ip->frag_off & __constant_htons(IP_DF) ? "DF " : "", + ip->frag_off & __constant_htons(IP_MF) ? "MF " : "", + (ntohs(ip->frag_off) & IP_OFFSET) << 3); + printk(" ttl:%d", ip->ttl); + printk(" proto:%d", ip->protocol); + if(ip->protocol == IPPROTO_UDP) + printk(" (UDP)"); + if(ip->protocol == IPPROTO_TCP) + printk(" (TCP)"); + if(ip->protocol == IPPROTO_ICMP) + printk(" (ICMP)"); + printk(" chk:%d", ntohs(ip->check)); + addrtoa(*((struct in_addr*)(&ip->saddr)), 0, buf, sizeof(buf)); + printk(" saddr:%s", buf); + if(ip->protocol == IPPROTO_UDP) + printk(":%d", + ntohs(((struct udphdr*)((caddr_t)ip + (ip->ihl << 2)))->source)); + if(ip->protocol == IPPROTO_TCP) + printk(":%d", + ntohs(((struct tcphdr*)((caddr_t)ip + (ip->ihl << 2)))->source)); + addrtoa(*((struct in_addr*)(&ip->daddr)), 0, buf, sizeof(buf)); + printk(" daddr:%s", buf); + if(ip->protocol == IPPROTO_UDP) + printk(":%d", + ntohs(((struct udphdr*)((caddr_t)ip + (ip->ihl << 2)))->dest)); + if(ip->protocol == IPPROTO_TCP) + printk(":%d", + ntohs(((struct tcphdr*)((caddr_t)ip + (ip->ihl << 2)))->dest)); + if(ip->protocol == IPPROTO_ICMP) + printk(" type:code=%d:%d", + ((struct icmphdr*)((caddr_t)ip + (ip->ihl << 2)))->type, + ((struct icmphdr*)((caddr_t)ip + (ip->ihl << 2)))->code); + printk("\n"); + + if(sysctl_ipsec_debug_verbose) { + __u8 *c; + int i; + + c = ((__u8*)ip) + ip->ihl*4; + for(i = 0; i < ntohs(ip->tot_len) - ip->ihl*4; i++ /*, c++*/) { + if(!(i % 16)) { + printk(KERN_INFO + "klips_debug: @%03x:", + i); + } + printk(" %02x", /***/c[i]); + if(!((i + 1) % 16)) { + printk("\n"); + } + } + if(i % 16) { + printk("\n"); + } + } +} +#endif /* CONFIG_IPSEC_DEBUG */ + +#ifdef MSS_HACK +/* + * Issues: + * 1) Fragments arriving in the tunnel should probably be rejected. + * 2) How does this affect syncookies, mss_cache, dst cache ? + * 3) Path MTU discovery handling needs to be reviewed. For example, + * if we receive an ICMP 'packet too big' message from an intermediate + * router specifying it's next hop MTU, our stack may process this and + * adjust the MSS without taking our AH/ESP overheads into account. + */ + + +/* + * Recaclulate checksum using differences between changed datum, + * borrowed from netfilter. + */ +DEBUG_NO_STATIC u_int16_t +ipsec_fast_csum(u_int32_t oldvalinv, u_int32_t newval, u_int16_t oldcheck) +{ + u_int32_t diffs[] = { oldvalinv, newval }; + return csum_fold(csum_partial((char *)diffs, sizeof(diffs), + oldcheck^0xFFFF)); +} + +/* + * Determine effective MSS. + * + * Note that we assume that there is always an MSS option for our own + * SYN segments, which is mentioned in tcp_syn_build_options(), kernel 2.2.x. + * This could change, and we should probably parse TCP options instead. + * + */ +DEBUG_NO_STATIC u_int8_t +ipsec_adjust_mss(struct sk_buff *skb, struct tcphdr *tcph, u_int16_t mtu) +{ + u_int16_t oldmss, newmss; + u_int32_t *mssp; + struct sock *sk = skb->sk; + + newmss = tcp_sync_mss(sk, mtu); + printk(KERN_INFO "klips: setting mss to %u\n", newmss); + mssp = (u_int32_t *)tcph + sizeof(struct tcphdr) / sizeof(u_int32_t); + oldmss = ntohl(*mssp) & 0x0000FFFF; + *mssp = htonl((TCPOPT_MSS << 24) | (TCPOLEN_MSS << 16) | newmss); + tcph->check = ipsec_fast_csum(htons(~oldmss), + htons(newmss), tcph->check); + return 1; +} +#endif /* MSS_HACK */ + +/* + * Sanity checks + */ +enum ipsec_xmit_value +ipsec_xmit_sanity_check_dev(struct ipsec_xmit_state *ixs) +{ + + if (ixs->dev == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_error:ipsec_xmit_sanity_check_dev: " + "No device associated with skb!\n" ); + return IPSEC_XMIT_NODEV; + } + + ixs->prv = ixs->dev->priv; + if (ixs->prv == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_error:ipsec_xmit_sanity_check_dev: " + "Device has no private structure!\n" ); + return IPSEC_XMIT_NOPRIVDEV; + } + + ixs->physdev = ixs->prv->dev; + if (ixs->physdev == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_error:ipsec_xmit_sanity_check_dev: " + "Device is not attached to physical device!\n" ); + return IPSEC_XMIT_NOPHYSDEV; + } + + ixs->physmtu = ixs->physdev->mtu; + + ixs->stats = (struct net_device_stats *) &(ixs->prv->mystats); + + return IPSEC_XMIT_OK; +} + +enum ipsec_xmit_value +ipsec_xmit_sanity_check_skb(struct ipsec_xmit_state *ixs) +{ + /* + * Return if there is nothing to do. (Does this ever happen?) XXX + */ + if (ixs->skb == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_error:ipsec_xmit_sanity_check_skb: " + "Nothing to do!\n" ); + return IPSEC_XMIT_NOSKB; + } +#ifdef NET_21 + /* if skb was cloned (most likely due to a packet sniffer such as + tcpdump being momentarily attached to the interface), make + a copy of our own to modify */ + if(skb_cloned(ixs->skb)) { + if +#ifdef SKB_COW_NEW + (skb_cow(ixs->skb, skb_headroom(ixs->skb)) != 0) +#else /* SKB_COW_NEW */ + ((ixs->skb = skb_cow(ixs->skb, skb_headroom(ixs->skb))) == NULL) +#endif /* SKB_COW_NEW */ + { + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_error:ipsec_xmit_sanity_check_skb: " + "skb_cow failed to allocate buffer, dropping.\n" ); + ixs->stats->tx_dropped++; + return IPSEC_XMIT_ERRSKBALLOC; + } + } +#endif /* NET_21 */ + +#ifdef NET_21 + ixs->iph = ixs->skb->nh.iph; +#else /* NET_21 */ + ixs->iph = ixs->skb->ip_hdr; +#endif /* NET_21 */ + + /* sanity check for IP version as we can't handle IPv6 right now */ + if (ixs->iph->version != 4) { + KLIPS_PRINT(debug_tunnel, + "klips_debug:ipsec_xmit_sanity_check_skb: " + "found IP Version %d but cannot process other IP versions than v4.\n", + ixs->iph->version); /* XXX */ + ixs->stats->tx_dropped++; + return IPSEC_XMIT_NOIPV6; + } + +#if IPSEC_DISALLOW_IPOPTIONS + if ((ixs->iph->ihl << 2) != sizeof (struct iphdr)) { + KLIPS_PRINT(debug_tunnel, + "klips_debug:ipsec_xmit_sanity_check_skb: " + "cannot process IP header options yet. May be mal-formed packet.\n"); /* XXX */ + ixs->stats->tx_dropped++; + return IPSEC_XMIT_NOIPOPTIONS; + } +#endif /* IPSEC_DISALLOW_IPOPTIONS */ + +#ifndef NET_21 + if (ixs->iph->ttl <= 0) { + /* Tell the sender its packet died... */ + ICMP_SEND(ixs->skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0, ixs->physdev); + + KLIPS_PRINT(debug_tunnel, "klips_debug:ipsec_xmit_sanity_check_skb: " + "TTL=0, too many hops!\n"); + ixs->stats->tx_dropped++; + return IPSEC_XMIT_TTLEXPIRED; + } +#endif /* !NET_21 */ + + return IPSEC_XMIT_OK; +} + +enum ipsec_xmit_value +ipsec_xmit_encap_once(struct ipsec_xmit_state *ixs) +{ +#ifdef CONFIG_IPSEC_ESP + struct esphdr *espp; +#ifdef CONFIG_IPSEC_ENC_3DES + __u32 iv[ESP_IV_MAXSZ_INT]; +#endif /* !CONFIG_IPSEC_ENC_3DES */ + unsigned char *idat, *pad; + int authlen = 0, padlen = 0, i; +#endif /* !CONFIG_IPSEC_ESP */ +#ifdef CONFIG_IPSEC_AH + struct iphdr ipo; + struct ahhdr *ahp; +#endif /* CONFIG_IPSEC_AH */ +#if defined(CONFIG_IPSEC_AUTH_HMAC_MD5) || defined(CONFIG_IPSEC_AUTH_HMAC_SHA1) + union { +#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 + MD5_CTX md5; +#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ +#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 + SHA1_CTX sha1; +#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ + } tctx; + __u8 hash[AH_AMAX]; +#endif /* defined(CONFIG_IPSEC_AUTH_HMAC_MD5) || defined(CONFIG_IPSEC_AUTH_HMAC_SHA1) */ + int headroom = 0, tailroom = 0, ilen = 0, len = 0; + unsigned char *dat; + int blocksize = 8; /* XXX: should be inside ixs --jjo */ +#ifdef CONFIG_IPSEC_ALG + struct ipsec_alg_enc *ixt_e = NULL; + struct ipsec_alg_auth *ixt_a = NULL; +#endif /* CONFIG_IPSEC_ALG */ + + ixs->iphlen = ixs->iph->ihl << 2; + ixs->pyldsz = ntohs(ixs->iph->tot_len) - ixs->iphlen; + ixs->sa_len = satoa(ixs->ipsp->ips_said, 0, ixs->sa_txt, SATOA_BUF); + KLIPS_PRINT(debug_tunnel & DB_TN_OXFS, + "klips_debug:ipsec_xmit_encap_once: " + "calling output for <%s%s%s>, SA:%s\n", + IPS_XFORM_NAME(ixs->ipsp), + ixs->sa_len ? ixs->sa_txt : " (error)"); + + switch(ixs->ipsp->ips_said.proto) { +#ifdef CONFIG_IPSEC_AH + case IPPROTO_AH: + headroom += sizeof(struct ahhdr); + break; +#endif /* CONFIG_IPSEC_AH */ +#ifdef CONFIG_IPSEC_ESP + case IPPROTO_ESP: +#ifdef CONFIG_IPSEC_ALG + if ((ixt_e=ixs->ipsp->ips_alg_enc)) { + blocksize = ixt_e->ixt_blocksize; + headroom += ESP_HEADER_LEN + ixt_e->ixt_ivlen/8; + } else +#endif /* CONFIG_IPSEC_ALG */ + switch(ixs->ipsp->ips_encalg) { +#ifdef CONFIG_IPSEC_ENC_3DES + case ESP_3DES: + headroom += sizeof(struct esphdr); + break; +#endif /* CONFIG_IPSEC_ENC_3DES */ + default: + ixs->stats->tx_errors++; + return IPSEC_XMIT_ESP_BADALG; + } +#ifdef CONFIG_IPSEC_ALG + if ((ixt_a=ixs->ipsp->ips_alg_auth)) { + tailroom += AHHMAC_HASHLEN; + } else +#endif /* CONFIG_IPSEC_ALG */ + switch(ixs->ipsp->ips_authalg) { +#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 + case AH_MD5: + authlen = AHHMAC_HASHLEN; + break; +#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ +#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 + case AH_SHA: + authlen = AHHMAC_HASHLEN; + break; +#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ + case AH_NONE: + break; + default: + ixs->stats->tx_errors++; + return IPSEC_XMIT_ESP_BADALG; + } +#ifdef CONFIG_IPSEC_ALG + tailroom += blocksize != 1 ? + ((blocksize - ((ixs->pyldsz + 2) % blocksize)) % blocksize) + 2 : + ((4 - ((ixs->pyldsz + 2) % 4)) % 4) + 2; +#else + tailroom += ((8 - ((ixs->pyldsz + 2 * sizeof(unsigned char)) % 8)) % 8) + 2; +#endif /* CONFIG_IPSEC_ALG */ + tailroom += authlen; + break; +#endif /* !CONFIG_IPSEC_ESP */ +#ifdef CONFIG_IPSEC_IPIP + case IPPROTO_IPIP: + headroom += sizeof(struct iphdr); + ixs->iphlen = sizeof(struct iphdr); + break; +#endif /* !CONFIG_IPSEC_IPIP */ +#ifdef CONFIG_IPSEC_IPCOMP + case IPPROTO_COMP: + break; +#endif /* CONFIG_IPSEC_IPCOMP */ + default: + ixs->stats->tx_errors++; + return IPSEC_XMIT_BADPROTO; + } + + KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, + "klips_debug:ipsec_xmit_encap_once: " + "pushing %d bytes, putting %d, proto %d.\n", + headroom, tailroom, ixs->ipsp->ips_said.proto); + if(skb_headroom(ixs->skb) < headroom) { + printk(KERN_WARNING + "klips_error:ipsec_xmit_encap_once: " + "tried to skb_push headroom=%d, %d available. This should never happen, please report.\n", + headroom, skb_headroom(ixs->skb)); + ixs->stats->tx_errors++; + return IPSEC_XMIT_ESP_PUSHPULLERR; + } + dat = skb_push(ixs->skb, headroom); + ilen = ixs->skb->len - tailroom; + if(skb_tailroom(ixs->skb) < tailroom) { + printk(KERN_WARNING + "klips_error:ipsec_xmit_encap_once: " + "tried to skb_put %d, %d available. This should never happen, please report.\n", + tailroom, skb_tailroom(ixs->skb)); + ixs->stats->tx_errors++; + return IPSEC_XMIT_ESP_PUSHPULLERR; + } + skb_put(ixs->skb, tailroom); + KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, + "klips_debug:ipsec_xmit_encap_once: " + "head,tailroom: %d,%d before xform.\n", + skb_headroom(ixs->skb), skb_tailroom(ixs->skb)); + len = ixs->skb->len; + if(len > 0xfff0) { + printk(KERN_WARNING "klips_error:ipsec_xmit_encap_once: " + "tot_len (%d) > 65520. This should never happen, please report.\n", + len); + ixs->stats->tx_errors++; + return IPSEC_XMIT_BADLEN; + } + memmove((void *)dat, (void *)(dat + headroom), ixs->iphlen); + ixs->iph = (struct iphdr *)dat; + ixs->iph->tot_len = htons(ixs->skb->len); + + switch(ixs->ipsp->ips_said.proto) { +#ifdef CONFIG_IPSEC_ESP + case IPPROTO_ESP: + espp = (struct esphdr *)(dat + ixs->iphlen); + espp->esp_spi = ixs->ipsp->ips_said.spi; + espp->esp_rpl = htonl(++(ixs->ipsp->ips_replaywin_lastseq)); + +#ifdef CONFIG_IPSEC_ALG + if (!ixt_e) +#endif /* CONFIG_IPSEC_ALG */ + switch(ixs->ipsp->ips_encalg) { +#if defined(CONFIG_IPSEC_ENC_3DES) +#ifdef CONFIG_IPSEC_ENC_3DES + case ESP_3DES: +#endif /* CONFIG_IPSEC_ENC_3DES */ + iv[0] = *((__u32*)&(espp->esp_iv) ) = + ((__u32*)(ixs->ipsp->ips_iv))[0]; + iv[1] = *((__u32*)&(espp->esp_iv) + 1) = + ((__u32*)(ixs->ipsp->ips_iv))[1]; + break; +#endif /* defined(CONFIG_IPSEC_ENC_3DES) */ + default: + ixs->stats->tx_errors++; + return IPSEC_XMIT_ESP_BADALG; + } + + idat = dat + ixs->iphlen + headroom; + ilen = len - (ixs->iphlen + headroom + authlen); + + /* Self-describing padding */ + pad = &dat[len - tailroom]; + padlen = tailroom - 2 - authlen; + for (i = 0; i < padlen; i++) { + pad[i] = i + 1; + } + dat[len - authlen - 2] = padlen; + + dat[len - authlen - 1] = ixs->iph->protocol; + ixs->iph->protocol = IPPROTO_ESP; + +#ifdef CONFIG_IPSEC_ALG + /* Do all operations here: + * copy IV->ESP, encrypt, update ips IV + */ + if (ixt_e) { + int ret; + memcpy(espp->esp_iv, + ixs->ipsp->ips_iv, + ixt_e->ixt_ivlen/8); + ret=ipsec_alg_esp_encrypt(ixs->ipsp, + idat, ilen, espp->esp_iv, + IPSEC_ALG_ENCRYPT); + memcpy(ixs->ipsp->ips_iv, + idat + ilen - ixt_e->ixt_ivlen/8, + ixt_e->ixt_ivlen/8); + } else +#endif /* CONFIG_IPSEC_ALG */ + switch(ixs->ipsp->ips_encalg) { +#ifdef CONFIG_IPSEC_ENC_3DES + case ESP_3DES: + des_ede3_cbc_encrypt((des_cblock *)idat, + (des_cblock *)idat, + ilen, + ((struct des_eks *)(ixs->ipsp->ips_key_e))[0].ks, + ((struct des_eks *)(ixs->ipsp->ips_key_e))[1].ks, + ((struct des_eks *)(ixs->ipsp->ips_key_e))[2].ks, + (des_cblock *)iv, 1); + break; +#endif /* CONFIG_IPSEC_ENC_3DES */ + default: + ixs->stats->tx_errors++; + return IPSEC_XMIT_ESP_BADALG; + } + +#ifdef CONFIG_IPSEC_ALG + if (!ixt_e) +#endif /* CONFIG_IPSEC_ALG */ + switch(ixs->ipsp->ips_encalg) { +#if defined(CONFIG_IPSEC_ENC_3DES) +#ifdef CONFIG_IPSEC_ENC_3DES + case ESP_3DES: +#endif /* CONFIG_IPSEC_ENC_3DES */ + /* XXX update IV with the last 8 octets of the encryption */ +#if KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK + ((__u32*)(ixs->ipsp->ips_iv))[0] = + ((__u32 *)(idat))[(ilen >> 2) - 2]; + ((__u32*)(ixs->ipsp->ips_iv))[1] = + ((__u32 *)(idat))[(ilen >> 2) - 1]; +#else /* KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK */ + prng_bytes(&ipsec_prng, (char *)ixs->ipsp->ips_iv, EMT_ESPDES_IV_SZ); +#endif /* KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK */ + break; +#endif /* defined(CONFIG_IPSEC_ENC_3DES) */ + default: + ixs->stats->tx_errors++; + return IPSEC_XMIT_ESP_BADALG; + } + +#ifdef CONFIG_IPSEC_ALG + if (ixt_a) { + ipsec_alg_sa_esp_hash(ixs->ipsp, + (caddr_t)espp, len - ixs->iphlen - authlen, + &(dat[len - authlen]), authlen); + + } else +#endif /* CONFIG_IPSEC_ALG */ + switch(ixs->ipsp->ips_authalg) { +#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 + case AH_MD5: + dmp("espp", (char*)espp, len - ixs->iphlen - authlen); + tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->ictx; + dmp("ictx", (char*)&tctx.md5, sizeof(tctx.md5)); + MD5Update(&tctx.md5, (caddr_t)espp, len - ixs->iphlen - authlen); + dmp("ictx+dat", (char*)&tctx.md5, sizeof(tctx.md5)); + MD5Final(hash, &tctx.md5); + dmp("ictx hash", (char*)&hash, sizeof(hash)); + tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->octx; + dmp("octx", (char*)&tctx.md5, sizeof(tctx.md5)); + MD5Update(&tctx.md5, hash, AHMD596_ALEN); + dmp("octx+hash", (char*)&tctx.md5, sizeof(tctx.md5)); + MD5Final(hash, &tctx.md5); + dmp("octx hash", (char*)&hash, sizeof(hash)); + memcpy(&(dat[len - authlen]), hash, authlen); + + /* paranoid */ + memset((caddr_t)&tctx.md5, 0, sizeof(tctx.md5)); + memset((caddr_t)hash, 0, sizeof(*hash)); + break; +#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ +#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 + case AH_SHA: + tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->ictx; + SHA1Update(&tctx.sha1, (caddr_t)espp, len - ixs->iphlen - authlen); + SHA1Final(hash, &tctx.sha1); + tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->octx; + SHA1Update(&tctx.sha1, hash, AHSHA196_ALEN); + SHA1Final(hash, &tctx.sha1); + memcpy(&(dat[len - authlen]), hash, authlen); + + /* paranoid */ + memset((caddr_t)&tctx.sha1, 0, sizeof(tctx.sha1)); + memset((caddr_t)hash, 0, sizeof(*hash)); + break; +#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ + case AH_NONE: + break; + default: + ixs->stats->tx_errors++; + return IPSEC_XMIT_AH_BADALG; + } +#ifdef NET_21 + ixs->skb->h.raw = (unsigned char*)espp; +#endif /* NET_21 */ + break; +#endif /* !CONFIG_IPSEC_ESP */ +#ifdef CONFIG_IPSEC_AH + case IPPROTO_AH: + ahp = (struct ahhdr *)(dat + ixs->iphlen); + ahp->ah_spi = ixs->ipsp->ips_said.spi; + ahp->ah_rpl = htonl(++(ixs->ipsp->ips_replaywin_lastseq)); + ahp->ah_rv = 0; + ahp->ah_nh = ixs->iph->protocol; + ahp->ah_hl = (headroom >> 2) - sizeof(__u64)/sizeof(__u32); + ixs->iph->protocol = IPPROTO_AH; + dmp("ahp", (char*)ahp, sizeof(*ahp)); + + ipo = *ixs->iph; + ipo.tos = 0; + ipo.frag_off = 0; + ipo.ttl = 0; + ipo.check = 0; + dmp("ipo", (char*)&ipo, sizeof(ipo)); + + switch(ixs->ipsp->ips_authalg) { +#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 + case AH_MD5: + tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->ictx; + dmp("ictx", (char*)&tctx.md5, sizeof(tctx.md5)); + MD5Update(&tctx.md5, (unsigned char *)&ipo, sizeof (struct iphdr)); + dmp("ictx+ipo", (char*)&tctx.md5, sizeof(tctx.md5)); + MD5Update(&tctx.md5, (unsigned char *)ahp, headroom - sizeof(ahp->ah_data)); + dmp("ictx+ahp", (char*)&tctx.md5, sizeof(tctx.md5)); + MD5Update(&tctx.md5, (unsigned char *)zeroes, AHHMAC_HASHLEN); + dmp("ictx+zeroes", (char*)&tctx.md5, sizeof(tctx.md5)); + MD5Update(&tctx.md5, dat + ixs->iphlen + headroom, len - ixs->iphlen - headroom); + dmp("ictx+dat", (char*)&tctx.md5, sizeof(tctx.md5)); + MD5Final(hash, &tctx.md5); + dmp("ictx hash", (char*)&hash, sizeof(hash)); + tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->octx; + dmp("octx", (char*)&tctx.md5, sizeof(tctx.md5)); + MD5Update(&tctx.md5, hash, AHMD596_ALEN); + dmp("octx+hash", (char*)&tctx.md5, sizeof(tctx.md5)); + MD5Final(hash, &tctx.md5); + dmp("octx hash", (char*)&hash, sizeof(hash)); + + memcpy(ahp->ah_data, hash, AHHMAC_HASHLEN); + + /* paranoid */ + memset((caddr_t)&tctx.md5, 0, sizeof(tctx.md5)); + memset((caddr_t)hash, 0, sizeof(*hash)); + break; +#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ +#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 + case AH_SHA: + tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->ictx; + SHA1Update(&tctx.sha1, (unsigned char *)&ipo, sizeof (struct iphdr)); + SHA1Update(&tctx.sha1, (unsigned char *)ahp, headroom - sizeof(ahp->ah_data)); + SHA1Update(&tctx.sha1, (unsigned char *)zeroes, AHHMAC_HASHLEN); + SHA1Update(&tctx.sha1, dat + ixs->iphlen + headroom, len - ixs->iphlen - headroom); + SHA1Final(hash, &tctx.sha1); + tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->octx; + SHA1Update(&tctx.sha1, hash, AHSHA196_ALEN); + SHA1Final(hash, &tctx.sha1); + + memcpy(ahp->ah_data, hash, AHHMAC_HASHLEN); + + /* paranoid */ + memset((caddr_t)&tctx.sha1, 0, sizeof(tctx.sha1)); + memset((caddr_t)hash, 0, sizeof(*hash)); + break; +#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ + default: + ixs->stats->tx_errors++; + return IPSEC_XMIT_AH_BADALG; + } +#ifdef NET_21 + ixs->skb->h.raw = (unsigned char*)ahp; +#endif /* NET_21 */ + break; +#endif /* CONFIG_IPSEC_AH */ +#ifdef CONFIG_IPSEC_IPIP + case IPPROTO_IPIP: + ixs->iph->version = 4; + switch(sysctl_ipsec_tos) { + case 0: +#ifdef NET_21 + ixs->iph->tos = ixs->skb->nh.iph->tos; +#else /* NET_21 */ + ixs->iph->tos = ixs->skb->ip_hdr->tos; +#endif /* NET_21 */ + break; + case 1: + ixs->iph->tos = 0; + break; + default: + break; + } +#ifdef NET_21 +#ifdef NETDEV_23 + ixs->iph->ttl = sysctl_ip_default_ttl; +#else /* NETDEV_23 */ + ixs->iph->ttl = ip_statistics.IpDefaultTTL; +#endif /* NETDEV_23 */ +#else /* NET_21 */ + ixs->iph->ttl = 64; /* ip_statistics.IpDefaultTTL; */ +#endif /* NET_21 */ + ixs->iph->frag_off = 0; + ixs->iph->saddr = ((struct sockaddr_in*)(ixs->ipsp->ips_addr_s))->sin_addr.s_addr; + ixs->iph->daddr = ((struct sockaddr_in*)(ixs->ipsp->ips_addr_d))->sin_addr.s_addr; + ixs->iph->protocol = IPPROTO_IPIP; + ixs->iph->ihl = sizeof(struct iphdr) >> 2; + + KLIPS_IP_SELECT_IDENT(ixs->iph, ixs->skb); + + ixs->newdst = (__u32)ixs->iph->daddr; + ixs->newsrc = (__u32)ixs->iph->saddr; + +#ifdef NET_21 + ixs->skb->h.ipiph = ixs->skb->nh.iph; +#endif /* NET_21 */ + break; +#endif /* !CONFIG_IPSEC_IPIP */ +#ifdef CONFIG_IPSEC_IPCOMP + case IPPROTO_COMP: + { + unsigned int flags = 0; +#ifdef CONFIG_IPSEC_DEBUG + unsigned int old_tot_len = ntohs(ixs->iph->tot_len); +#endif /* CONFIG_IPSEC_DEBUG */ + ixs->ipsp->ips_comp_ratio_dbytes += ntohs(ixs->iph->tot_len); + + ixs->skb = skb_compress(ixs->skb, ixs->ipsp, &flags); + +#ifdef NET_21 + ixs->iph = ixs->skb->nh.iph; +#else /* NET_21 */ + ixs->iph = ixs->skb->ip_hdr; +#endif /* NET_21 */ + + ixs->ipsp->ips_comp_ratio_cbytes += ntohs(ixs->iph->tot_len); + +#ifdef CONFIG_IPSEC_DEBUG + if (debug_tunnel & DB_TN_CROUT) + { + if (old_tot_len > ntohs(ixs->iph->tot_len)) + KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, + "klips_debug:ipsec_xmit_encap_once: " + "packet shrunk from %d to %d bytes after compression, cpi=%04x (should be from spi=%08x, spi&0xffff=%04x.\n", + old_tot_len, ntohs(ixs->iph->tot_len), + ntohs(((struct ipcomphdr*)(((char*)ixs->iph) + ((ixs->iph->ihl) << 2)))->ipcomp_cpi), + ntohl(ixs->ipsp->ips_said.spi), + (__u16)(ntohl(ixs->ipsp->ips_said.spi) & 0x0000ffff)); + else + KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, + "klips_debug:ipsec_xmit_encap_once: " + "packet did not compress (flags = %d).\n", + flags); + } +#endif /* CONFIG_IPSEC_DEBUG */ + } + break; +#endif /* CONFIG_IPSEC_IPCOMP */ + default: + ixs->stats->tx_errors++; + return IPSEC_XMIT_BADPROTO; + } + +#ifdef NET_21 + ixs->skb->nh.raw = ixs->skb->data; +#else /* NET_21 */ + ixs->skb->ip_hdr = ixs->skb->h.iph = (struct iphdr *) ixs->skb->data; +#endif /* NET_21 */ + ixs->iph->check = 0; + ixs->iph->check = ip_fast_csum((unsigned char *)ixs->iph, ixs->iph->ihl); + + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_xmit_encap_once: " + "after <%s%s%s>, SA:%s:\n", + IPS_XFORM_NAME(ixs->ipsp), + ixs->sa_len ? ixs->sa_txt : " (error)"); + KLIPS_IP_PRINT(debug_tunnel & DB_TN_XMIT, ixs->iph); + + ixs->ipsp->ips_life.ipl_bytes.ipl_count += len; + ixs->ipsp->ips_life.ipl_bytes.ipl_last = len; + + if(!ixs->ipsp->ips_life.ipl_usetime.ipl_count) { + ixs->ipsp->ips_life.ipl_usetime.ipl_count = jiffies / HZ; + } + ixs->ipsp->ips_life.ipl_usetime.ipl_last = jiffies / HZ; + ixs->ipsp->ips_life.ipl_packets.ipl_count++; + + ixs->ipsp = ixs->ipsp->ips_onext; + + return IPSEC_XMIT_OK; +} + +enum ipsec_xmit_value +ipsec_xmit_encap_bundle(struct ipsec_xmit_state *ixs) +{ +#ifdef CONFIG_IPSEC_ALG + struct ipsec_alg_enc *ixt_e = NULL; + struct ipsec_alg_auth *ixt_a = NULL; + int blocksize = 8; +#endif /* CONFIG_IPSEC_ALG */ + enum ipsec_xmit_value bundle_stat = IPSEC_XMIT_OK; + + ixs->newdst = ixs->orgdst = ixs->iph->daddr; + ixs->newsrc = ixs->orgsrc = ixs->iph->saddr; + ixs->orgedst = ixs->outgoing_said.dst.s_addr; + ixs->iphlen = ixs->iph->ihl << 2; + ixs->pyldsz = ntohs(ixs->iph->tot_len) - ixs->iphlen; + ixs->max_headroom = ixs->max_tailroom = 0; + + if (ixs->outgoing_said.proto == IPPROTO_INT) { + switch (ntohl(ixs->outgoing_said.spi)) { + case SPI_DROP: + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_xmit_encap_bundle: " + "shunt SA of DROP or no eroute: dropping.\n"); + ixs->stats->tx_dropped++; + break; + + case SPI_REJECT: + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_xmit_encap_bundle: " + "shunt SA of REJECT: notifying and dropping.\n"); + ICMP_SEND(ixs->skb, + ICMP_DEST_UNREACH, + ICMP_PKT_FILTERED, + 0, + ixs->physdev); + ixs->stats->tx_dropped++; + break; + + case SPI_PASS: +#ifdef NET_21 + ixs->pass = 1; +#endif /* NET_21 */ + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_xmit_encap_bundle: " + "PASS: calling dev_queue_xmit\n"); + return IPSEC_XMIT_PASS; + goto cleanup; + +#if 1 /* now moved up to finderoute so we don't need to lock it longer */ + case SPI_HOLD: + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_xmit_encap_bundle: " + "shunt SA of HOLD: this does not make sense here, dropping.\n"); + ixs->stats->tx_dropped++; + break; +#endif + case SPI_TRAP: + case SPI_TRAPSUBNET: + { + struct sockaddr_in src, dst; +#ifdef CONFIG_IPSEC_DEBUG + char bufsrc[ADDRTOA_BUF], bufdst[ADDRTOA_BUF]; +#endif /* CONFIG_IPSEC_DEBUG */ + + /* Signal all listening KMds with a PF_KEY ACQUIRE */ + ixs->ips.ips_said.proto = ixs->iph->protocol; + src.sin_family = AF_INET; + dst.sin_family = AF_INET; + src.sin_addr.s_addr = ixs->iph->saddr; + dst.sin_addr.s_addr = ixs->iph->daddr; + src.sin_port = + (ixs->iph->protocol == IPPROTO_UDP + ? ((struct udphdr*) (((caddr_t)ixs->iph) + (ixs->iph->ihl << 2)))->source + : (ixs->iph->protocol == IPPROTO_TCP + ? ((struct tcphdr*)((caddr_t)ixs->iph + (ixs->iph->ihl << 2)))->source + : 0)); + dst.sin_port = + (ixs->iph->protocol == IPPROTO_UDP + ? ((struct udphdr*) (((caddr_t)ixs->iph) + (ixs->iph->ihl << 2)))->dest + : (ixs->iph->protocol == IPPROTO_TCP + ? ((struct tcphdr*)((caddr_t)ixs->iph + (ixs->iph->ihl << 2)))->dest + : 0)); + { + int i; + for(i = 0; + i < sizeof(struct sockaddr_in) + - offsetof(struct sockaddr_in, sin_zero); + i++) { + src.sin_zero[i] = 0; + dst.sin_zero[i] = 0; + } + } + + ixs->ips.ips_addr_s = (struct sockaddr*)(&src); + ixs->ips.ips_addr_d = (struct sockaddr*)(&dst); + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_xmit_encap_bundle: " + "SADB_ACQUIRE sent with src=%s:%d, dst=%s:%d, proto=%d.\n", + addrtoa(((struct sockaddr_in*)(ixs->ips.ips_addr_s))->sin_addr, 0, bufsrc, sizeof(bufsrc)) <= ADDRTOA_BUF ? bufsrc : "BAD_ADDR", + ntohs(((struct sockaddr_in*)(ixs->ips.ips_addr_s))->sin_port), + addrtoa(((struct sockaddr_in*)(ixs->ips.ips_addr_d))->sin_addr, 0, bufdst, sizeof(bufdst)) <= ADDRTOA_BUF ? bufdst : "BAD_ADDR", + ntohs(((struct sockaddr_in*)(ixs->ips.ips_addr_d))->sin_port), + ixs->ips.ips_said.proto); + + /* increment count of total traps needed */ + ipsec_xmit_trap_count++; + + if (pfkey_acquire(&ixs->ips) == 0) { + + /* note that we succeeded */ + ipsec_xmit_trap_sendcount++; + + if (ixs->outgoing_said.spi==htonl(SPI_TRAPSUBNET)) { + /* + * The spinlock is to prevent any other + * process from accessing or deleting + * the eroute while we are using and + * updating it. + */ + spin_lock(&eroute_lock); + ixs->eroute = ipsec_findroute(&ixs->matcher); + if(ixs->eroute) { + ixs->eroute->er_said.spi = htonl(SPI_HOLD); + ixs->eroute->er_first = ixs->skb; + ixs->skb = NULL; + } + spin_unlock(&eroute_lock); + } else if (create_hold_eroute(ixs->skb, ixs->iph, ixs->eroute_pid)) { + ixs->skb = NULL; + } + } + ixs->stats->tx_dropped++; + } + default: + /* XXX what do we do with an unknown shunt spi? */ + break; + } /* switch (ntohl(ixs->outgoing_said.spi)) */ + return IPSEC_XMIT_STOLEN; + } /* if (ixs->outgoing_said.proto == IPPROTO_INT) */ + + /* + The spinlock is to prevent any other process from + accessing or deleting the ipsec_sa hash table or any of the + ipsec_sa s while we are using and updating them. + + This is not optimal, but was relatively straightforward + at the time. A better way to do it has been planned for + more than a year, to lock the hash table and put reference + counts on each ipsec_sa instead. This is not likely to happen + in KLIPS1 unless a volunteer contributes it, but will be + designed into KLIPS2. + */ + spin_lock(&tdb_lock); + + ixs->ipsp = ipsec_sa_getbyid(&ixs->outgoing_said); + ixs->sa_len = satoa(ixs->outgoing_said, 0, ixs->sa_txt, SATOA_BUF); + + if (ixs->ipsp == NULL) { + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_xmit_encap_bundle: " + "no ipsec_sa for SA%s: outgoing packet with no SA, dropped.\n", + ixs->sa_len ? ixs->sa_txt : " (error)"); + ixs->stats->tx_dropped++; + bundle_stat = IPSEC_XMIT_SAIDNOTFOUND; + goto cleanup; + } + + ipsec_sa_put(ixs->ipsp); /* incomplete */ + + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_xmit_encap_bundle: " + "found ipsec_sa -- SA:<%s%s%s> %s\n", + IPS_XFORM_NAME(ixs->ipsp), + ixs->sa_len ? ixs->sa_txt : " (error)"); + + /* + * How much headroom do we need to be able to apply + * all the grouped transforms? + */ + ixs->ipsq = ixs->ipsp; /* save the head of the ipsec_sa chain */ + while (ixs->ipsp) { + ixs->sa_len = satoa(ixs->ipsp->ips_said, 0, ixs->sa_txt, SATOA_BUF); + if(ixs->sa_len == 0) { + strcpy(ixs->sa_txt, "(error)"); + } + + /* If it is in larval state, drop the packet, we cannot process yet. */ + if(ixs->ipsp->ips_state == SADB_SASTATE_LARVAL) { + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_xmit_encap_bundle: " + "ipsec_sa in larval state for SA:<%s%s%s> %s, cannot be used yet, dropping packet.\n", + IPS_XFORM_NAME(ixs->ipsp), + ixs->sa_len ? ixs->sa_txt : " (error)"); + ixs->stats->tx_errors++; + bundle_stat = IPSEC_XMIT_SAIDNOTLIVE; + goto cleanup; + } + + if(ixs->ipsp->ips_state == SADB_SASTATE_DEAD) { + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_xmit_encap_bundle: " + "ipsec_sa in dead state for SA:<%s%s%s> %s, can no longer be used, dropping packet.\n", + IPS_XFORM_NAME(ixs->ipsp), + ixs->sa_len ? ixs->sa_txt : " (error)"); + ixs->stats->tx_errors++; + bundle_stat = IPSEC_XMIT_SAIDNOTLIVE; + goto cleanup; + } + + /* If the replay window counter == -1, expire SA, it will roll */ + if(ixs->ipsp->ips_replaywin && ixs->ipsp->ips_replaywin_lastseq == -1) { + pfkey_expire(ixs->ipsp, 1); + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_xmit_encap_bundle: " + "replay window counter rolled for SA:<%s%s%s> %s, packet dropped, expiring SA.\n", + IPS_XFORM_NAME(ixs->ipsp), + ixs->sa_len ? ixs->sa_txt : " (error)"); + ipsec_sa_delchain(ixs->ipsp); + ixs->stats->tx_errors++; + bundle_stat = IPSEC_XMIT_REPLAYROLLED; + goto cleanup; + } + + /* + * if this is the first time we are using this SA, mark start time, + * and offset hard/soft counters by "now" for later checking. + */ +#if 0 + if(ixs->ipsp->ips_life.ipl_usetime.count == 0) { + ixs->ipsp->ips_life.ipl_usetime.count = jiffies; + ixs->ipsp->ips_life.ipl_usetime.hard += jiffies; + ixs->ipsp->ips_life.ipl_usetime.soft += jiffies; + } +#endif + + + if(ipsec_lifetime_check(&ixs->ipsp->ips_life.ipl_bytes, "bytes", ixs->sa_txt, + ipsec_life_countbased, ipsec_outgoing, ixs->ipsp) == ipsec_life_harddied || + ipsec_lifetime_check(&ixs->ipsp->ips_life.ipl_addtime, "addtime",ixs->sa_txt, + ipsec_life_timebased, ipsec_outgoing, ixs->ipsp) == ipsec_life_harddied || + ipsec_lifetime_check(&ixs->ipsp->ips_life.ipl_usetime, "usetime",ixs->sa_txt, + ipsec_life_timebased, ipsec_outgoing, ixs->ipsp) == ipsec_life_harddied || + ipsec_lifetime_check(&ixs->ipsp->ips_life.ipl_packets, "packets",ixs->sa_txt, + ipsec_life_countbased, ipsec_outgoing, ixs->ipsp) == ipsec_life_harddied) { + + ipsec_sa_delchain(ixs->ipsp); + ixs->stats->tx_errors++; + bundle_stat = IPSEC_XMIT_LIFETIMEFAILED; + goto cleanup; + } + + + ixs->headroom = ixs->tailroom = 0; + KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, + "klips_debug:ipsec_xmit_encap_bundle: " + "calling room for <%s%s%s>, SA:%s\n", + IPS_XFORM_NAME(ixs->ipsp), + ixs->sa_len ? ixs->sa_txt : " (error)"); + switch(ixs->ipsp->ips_said.proto) { +#ifdef CONFIG_IPSEC_AH + case IPPROTO_AH: + ixs->headroom += sizeof(struct ahhdr); + break; +#endif /* CONFIG_IPSEC_AH */ +#ifdef CONFIG_IPSEC_ESP + case IPPROTO_ESP: +#ifdef CONFIG_IPSEC_ALG + if ((ixt_e=ixs->ipsp->ips_alg_enc)) { + blocksize = ixt_e->ixt_blocksize; + ixs->headroom += ESP_HEADER_LEN + ixt_e->ixt_ivlen/8; + } else +#endif /* CONFIG_IPSEC_ALG */ + switch(ixs->ipsp->ips_encalg) { +#ifdef CONFIG_IPSEC_ENC_3DES + case ESP_3DES: + ixs->headroom += sizeof(struct esphdr); + break; +#endif /* CONFIG_IPSEC_ENC_3DES */ + default: + ixs->stats->tx_errors++; + bundle_stat = IPSEC_XMIT_ESP_BADALG; + goto cleanup; + } +#ifdef CONFIG_IPSEC_ALG + if ((ixt_a=ixs->ipsp->ips_alg_auth)) { + ixs->tailroom += AHHMAC_HASHLEN; + } else +#endif /* CONFIG_IPSEC_ALG */ + switch(ixs->ipsp->ips_authalg) { +#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 + case AH_MD5: + ixs->tailroom += AHHMAC_HASHLEN; + break; +#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ +#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 + case AH_SHA: + ixs->tailroom += AHHMAC_HASHLEN; + break; +#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ + case AH_NONE: + break; + default: + ixs->stats->tx_errors++; + bundle_stat = IPSEC_XMIT_AH_BADALG; + goto cleanup; + } +#ifdef CONFIG_IPSEC_ALG + ixs->tailroom += blocksize != 1 ? + ((blocksize - ((ixs->pyldsz + 2) % blocksize)) % blocksize) + 2 : + ((4 - ((ixs->pyldsz + 2) % 4)) % 4) + 2; +#else + ixs->tailroom += ((8 - ((ixs->pyldsz + 2 * sizeof(unsigned char)) % 8)) % 8) + 2; +#endif /* CONFIG_IPSEC_ALG */ +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL + if ((ixs->ipsp->ips_natt_type) && (!ixs->natt_type)) { + ixs->natt_type = ixs->ipsp->ips_natt_type; + ixs->natt_sport = ixs->ipsp->ips_natt_sport; + ixs->natt_dport = ixs->ipsp->ips_natt_dport; + switch (ixs->natt_type) { + case ESPINUDP_WITH_NON_IKE: + ixs->natt_head = sizeof(struct udphdr)+(2*sizeof(__u32)); + break; + case ESPINUDP_WITH_NON_ESP: + ixs->natt_head = sizeof(struct udphdr); + break; + default: + ixs->natt_head = 0; + break; + } + ixs->tailroom += ixs->natt_head; + } +#endif + break; +#endif /* !CONFIG_IPSEC_ESP */ +#ifdef CONFIG_IPSEC_IPIP + case IPPROTO_IPIP: + ixs->headroom += sizeof(struct iphdr); + break; +#endif /* !CONFIG_IPSEC_IPIP */ + case IPPROTO_COMP: +#ifdef CONFIG_IPSEC_IPCOMP + /* + We can't predict how much the packet will + shrink without doing the actual compression. + We could do it here, if we were the first + encapsulation in the chain. That might save + us a skb_copy_expand, since we might fit + into the existing skb then. However, this + would be a bit unclean (and this hack has + bit us once), so we better not do it. After + all, the skb_copy_expand is cheap in + comparison to the actual compression. + At least we know the packet will not grow. + */ + break; +#endif /* CONFIG_IPSEC_IPCOMP */ + default: + ixs->stats->tx_errors++; + bundle_stat = IPSEC_XMIT_BADPROTO; + goto cleanup; + } + ixs->ipsp = ixs->ipsp->ips_onext; + KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, + "klips_debug:ipsec_xmit_encap_bundle: " + "Required head,tailroom: %d,%d\n", + ixs->headroom, ixs->tailroom); + ixs->max_headroom += ixs->headroom; + ixs->max_tailroom += ixs->tailroom; + ixs->pyldsz += (ixs->headroom + ixs->tailroom); + } + ixs->ipsp = ixs->ipsq; /* restore the head of the ipsec_sa chain */ + + KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, + "klips_debug:ipsec_xmit_encap_bundle: " + "existing head,tailroom: %d,%d before applying xforms with head,tailroom: %d,%d .\n", + skb_headroom(ixs->skb), skb_tailroom(ixs->skb), + ixs->max_headroom, ixs->max_tailroom); + + ixs->tot_headroom += ixs->max_headroom; + ixs->tot_tailroom += ixs->max_tailroom; + + ixs->mtudiff = ixs->prv->mtu + ixs->tot_headroom + ixs->tot_tailroom - ixs->physmtu; + + KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, + "klips_debug:ipsec_xmit_encap_bundle: " + "mtu:%d physmtu:%d tothr:%d tottr:%d mtudiff:%d ippkttotlen:%d\n", + ixs->prv->mtu, ixs->physmtu, + ixs->tot_headroom, ixs->tot_tailroom, ixs->mtudiff, ntohs(ixs->iph->tot_len)); + if(ixs->mtudiff > 0) { + int newmtu = ixs->physmtu - (ixs->tot_headroom + ((ixs->tot_tailroom + 2) & ~7) + 5); + + KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, + "klips_info:ipsec_xmit_encap_bundle: " + "dev %s mtu of %d decreased by %d to %d\n", + ixs->dev->name, + ixs->prv->mtu, + ixs->prv->mtu - newmtu, + newmtu); + ixs->prv->mtu = newmtu; +#ifdef NET_21 +#if 0 + ixs->skb->dst->pmtu = ixs->prv->mtu; /* RGB */ +#endif /* 0 */ +#else /* NET_21 */ +#if 0 + ixs->dev->mtu = ixs->prv->mtu; /* RGB */ +#endif /* 0 */ +#endif /* NET_21 */ + } + + /* + If the sender is doing PMTU discovery, and the + packet doesn't fit within ixs->prv->mtu, notify him + (unless it was an ICMP packet, or it was not the + zero-offset packet) and send it anyways. + + Note: buggy firewall configuration may prevent the + ICMP packet from getting back. + */ + if(sysctl_ipsec_icmp + && ixs->prv->mtu < ntohs(ixs->iph->tot_len) + && (ixs->iph->frag_off & __constant_htons(IP_DF)) ) { + int notify = ixs->iph->protocol != IPPROTO_ICMP + && (ixs->iph->frag_off & __constant_htons(IP_OFFSET)) == 0; + +#ifdef IPSEC_obey_DF + KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, + "klips_debug:ipsec_xmit_encap_bundle: " + "fragmentation needed and DF set; %sdropping packet\n", + notify ? "sending ICMP and " : ""); + if (notify) + ICMP_SEND(ixs->skb, + ICMP_DEST_UNREACH, + ICMP_FRAG_NEEDED, + ixs->prv->mtu, + ixs->physdev); + ixs->stats->tx_errors++; + bundle_stat = IPSEC_XMIT_CANNOTFRAG; + goto cleanup; +#else /* IPSEC_obey_DF */ + KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, + "klips_debug:ipsec_xmit_encap_bundle: " + "fragmentation needed and DF set; %spassing packet\n", + notify ? "sending ICMP and " : ""); + if (notify) + ICMP_SEND(ixs->skb, + ICMP_DEST_UNREACH, + ICMP_FRAG_NEEDED, + ixs->prv->mtu, + ixs->physdev); +#endif /* IPSEC_obey_DF */ + } + +#ifdef MSS_HACK + /* + * If this is a transport mode TCP packet with + * SYN set, determine an effective MSS based on + * AH/ESP overheads determined above. + */ + if (ixs->iph->protocol == IPPROTO_TCP + && ixs->outgoing_said.proto != IPPROTO_IPIP) { + struct tcphdr *tcph = ixs->skb->h.th; + if (tcph->syn && !tcph->ack) { + if(!ipsec_adjust_mss(ixs->skb, tcph, ixs->prv->mtu)) { + printk(KERN_WARNING + "klips_warning:ipsec_xmit_encap_bundle: " + "ipsec_adjust_mss() failed\n"); + ixs->stats->tx_errors++; + bundle_stat = IPSEC_XMIT_MSSERR; + goto cleanup; + } + } + } +#endif /* MSS_HACK */ + +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL + if ((ixs->natt_type) && (ixs->outgoing_said.proto != IPPROTO_IPIP)) { + /** + * NAT-Traversal and Transport Mode: + * we need to correct TCP/UDP checksum + * + * If we've got NAT-OA, we can fix checksum without recalculation. + * If we don't we can zero udp checksum. + */ + __u32 natt_oa = ixs->ipsp->ips_natt_oa ? + ((struct sockaddr_in*)(ixs->ipsp->ips_natt_oa))->sin_addr.s_addr : 0; + __u16 pkt_len = ixs->skb->tail - (unsigned char *)ixs->iph; + __u16 data_len = pkt_len - (ixs->iph->ihl << 2); + switch (ixs->iph->protocol) { + case IPPROTO_TCP: + if (data_len >= sizeof(struct tcphdr)) { + struct tcphdr *tcp = (struct tcphdr *)((__u32 *)ixs->iph+ixs->iph->ihl); + if (natt_oa) { + __u32 buff[2] = { ~ixs->iph->daddr, natt_oa }; + KLIPS_PRINT(debug_tunnel, + "klips_debug:ipsec_tunnel_start_xmit: " + "NAT-T & TRANSPORT: " + "fix TCP checksum using NAT-OA\n"); + tcp->check = csum_fold( + csum_partial((unsigned char *)buff, sizeof(buff), + tcp->check^0xffff)); + } + else { + KLIPS_PRINT(debug_tunnel, + "klips_debug:ipsec_tunnel_start_xmit: " + "NAT-T & TRANSPORT: do not recalc TCP checksum\n"); + } + } + else { + KLIPS_PRINT(debug_tunnel, + "klips_debug:ipsec_tunnel_start_xmit: " + "NAT-T & TRANSPORT: can't fix TCP checksum\n"); + } + break; + case IPPROTO_UDP: + if (data_len >= sizeof(struct udphdr)) { + struct udphdr *udp = (struct udphdr *)((__u32 *)ixs->iph+ixs->iph->ihl); + if (udp->check == 0) { + KLIPS_PRINT(debug_tunnel, + "klips_debug:ipsec_tunnel_start_xmit: " + "NAT-T & TRANSPORT: UDP checksum already 0\n"); + } + else if (natt_oa) { + __u32 buff[2] = { ~ixs->iph->daddr, natt_oa }; + KLIPS_PRINT(debug_tunnel, + "klips_debug:ipsec_tunnel_start_xmit: " + "NAT-T & TRANSPORT: " + "fix UDP checksum using NAT-OA\n"); + udp->check = csum_fold( + csum_partial((unsigned char *)buff, sizeof(buff), + udp->check^0xffff)); + } + else { + KLIPS_PRINT(debug_tunnel, + "klips_debug:ipsec_tunnel_start_xmit: " + "NAT-T & TRANSPORT: zero UDP checksum\n"); + udp->check = 0; + } + } + else { + KLIPS_PRINT(debug_tunnel, + "klips_debug:ipsec_tunnel_start_xmit: " + "NAT-T & TRANSPORT: can't fix UDP checksum\n"); + } + break; + default: + KLIPS_PRINT(debug_tunnel, + "klips_debug:ipsec_tunnel_start_xmit: " + "NAT-T & TRANSPORT: non TCP/UDP packet -- do nothing\n"); + break; + } + } +#endif /* CONFIG_IPSEC_NAT_TRAVERSAL */ + + if(!ixs->hard_header_stripped) { + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_xmit_encap_bundle: " + "allocating %d bytes for hardheader.\n", + ixs->hard_header_len); + if((ixs->saved_header = kmalloc(ixs->hard_header_len, GFP_ATOMIC)) == NULL) { + printk(KERN_WARNING "klips_debug:ipsec_xmit_encap_bundle: " + "Failed, tried to allocate %d bytes for temp hard_header.\n", + ixs->hard_header_len); + ixs->stats->tx_errors++; + bundle_stat = IPSEC_XMIT_ERRMEMALLOC; + goto cleanup; + } + { + int i; + for (i = 0; i < ixs->hard_header_len; i++) { + ixs->saved_header[i] = ixs->skb->data[i]; + } + } + if(ixs->skb->len < ixs->hard_header_len) { + printk(KERN_WARNING "klips_error:ipsec_xmit_encap_bundle: " + "tried to skb_pull hhlen=%d, %d available. This should never happen, please report.\n", + ixs->hard_header_len, (int)(ixs->skb->len)); + ixs->stats->tx_errors++; + bundle_stat = IPSEC_XMIT_ESP_PUSHPULLERR; + goto cleanup; + } + skb_pull(ixs->skb, ixs->hard_header_len); + ixs->hard_header_stripped = 1; + +/* ixs->iph = (struct iphdr *) (ixs->skb->data); */ + KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, + "klips_debug:ipsec_xmit_encap_bundle: " + "head,tailroom: %d,%d after hard_header stripped.\n", + skb_headroom(ixs->skb), skb_tailroom(ixs->skb)); + KLIPS_IP_PRINT(debug_tunnel & DB_TN_CROUT, ixs->iph); + } else { + KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, + "klips_debug:ipsec_xmit_encap_bundle: " + "hard header already stripped.\n"); + } + + ixs->ll_headroom = (ixs->hard_header_len + 15) & ~15; + + if ((skb_headroom(ixs->skb) >= ixs->max_headroom + 2 * ixs->ll_headroom) && + (skb_tailroom(ixs->skb) >= ixs->max_tailroom) +#ifndef NET_21 + && ixs->skb->free +#endif /* !NET_21 */ + ) { + KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, + "klips_debug:ipsec_xmit_encap_bundle: " + "data fits in existing skb\n"); + } else { + struct sk_buff* tskb; + + if(!ixs->oskb) { + ixs->oskb = ixs->skb; + } + + tskb = skb_copy_expand(ixs->skb, + /* The need for 2 * link layer length here remains unexplained...RGB */ + ixs->max_headroom + 2 * ixs->ll_headroom, + ixs->max_tailroom, + GFP_ATOMIC); +#ifdef NET_21 + if(tskb && ixs->skb->sk) { + skb_set_owner_w(tskb, ixs->skb->sk); + } +#endif /* NET_21 */ + if(ixs->skb != ixs->oskb) { + ipsec_kfree_skb(ixs->skb); + } + ixs->skb = tskb; + if (!ixs->skb) { + printk(KERN_WARNING + "klips_debug:ipsec_xmit_encap_bundle: " + "Failed, tried to allocate %d head and %d tailroom\n", + ixs->max_headroom, ixs->max_tailroom); + ixs->stats->tx_errors++; + bundle_stat = IPSEC_XMIT_ERRSKBALLOC; + goto cleanup; + } + KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, + "klips_debug:ipsec_xmit_encap_bundle: " + "head,tailroom: %d,%d after allocation\n", + skb_headroom(ixs->skb), skb_tailroom(ixs->skb)); + } + + /* + * Apply grouped transforms to packet + */ + while (ixs->ipsp) { + enum ipsec_xmit_value encap_stat = IPSEC_XMIT_OK; + + encap_stat = ipsec_xmit_encap_once(ixs); + if(encap_stat != IPSEC_XMIT_OK) { + KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, + "klips_debug:ipsec_xmit_encap_bundle: encap_once failed: %d\n", + encap_stat); + + bundle_stat = IPSEC_XMIT_ENCAPFAIL; + goto cleanup; + } + } + /* end encapsulation loop here XXX */ + cleanup: + spin_unlock(&tdb_lock); + return bundle_stat; +} diff --git a/linux/net/ipsec/pfkey_v2.c b/linux/net/ipsec/pfkey_v2.c new file mode 100644 index 000000000..a78aaf26e --- /dev/null +++ b/linux/net/ipsec/pfkey_v2.c @@ -0,0 +1,2125 @@ +/* + * @(#) RFC2367 PF_KEYv2 Key management API domain socket I/F + * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs. + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + * + * RCSID $Id: pfkey_v2.c,v 1.4 2004/09/29 22:27:41 as Exp $ + */ + +/* + * Template from /usr/src/linux-2.0.36/net/unix/af_unix.c. + * Hints from /usr/src/linux-2.0.36/net/ipv4/udp.c. + */ + +#define __NO_VERSION__ +#include <linux/module.h> +#include <linux/version.h> +#include <linux/config.h> +#include <linux/kernel.h> + +#include "freeswan/ipsec_param.h" + +#include <linux/major.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/stat.h> +#include <linux/socket.h> +#include <linux/un.h> +#include <linux/fcntl.h> +#include <linux/termios.h> +#include <linux/socket.h> +#include <linux/sockios.h> +#include <linux/net.h> /* struct socket */ +#include <linux/in.h> +#include <linux/fs.h> +#ifdef MALLOC_SLAB +# include <linux/slab.h> /* kmalloc() */ +#else /* MALLOC_SLAB */ +# include <linux/malloc.h> /* kmalloc() */ +#endif /* MALLOC_SLAB */ +#include <asm/segment.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <net/sock.h> /* struct sock */ +/* #include <net/tcp.h> */ +#include <net/af_unix.h> +#ifdef CONFIG_PROC_FS +# include <linux/proc_fs.h> +#endif /* CONFIG_PROC_FS */ + +#include <linux/types.h> + +#include <freeswan.h> +#ifdef NET_21 +# include <asm/uaccess.h> +# include <linux/in6.h> +#endif /* NET_21 */ + +#include "freeswan/radij.h" +#include "freeswan/ipsec_encap.h" +#include "freeswan/ipsec_sa.h" + +#include <pfkeyv2.h> +#include <pfkey.h> + +#include "freeswan/ipsec_proto.h" + +#ifdef CONFIG_IPSEC_DEBUG +int debug_pfkey = 0; +extern int sysctl_ipsec_debug_verbose; +#endif /* CONFIG_IPSEC_DEBUG */ + +#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0) + +#ifndef SOCKOPS_WRAPPED +#define SOCKOPS_WRAPPED(name) name +#endif /* SOCKOPS_WRAPPED */ + +extern struct proto_ops pfkey_ops; +struct sock *pfkey_sock_list = NULL; +struct supported_list *pfkey_supported_list[SADB_SATYPE_MAX+1]; + +struct socket_list *pfkey_open_sockets = NULL; +struct socket_list *pfkey_registered_sockets[SADB_SATYPE_MAX+1]; + +int pfkey_msg_interp(struct sock *, struct sadb_msg *, struct sadb_msg **); + +int +pfkey_list_remove_socket(struct socket *socketp, struct socket_list **sockets) +{ + struct socket_list *socket_listp,*prev; + + if(!socketp) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_list_remove_socket: " + "NULL socketp handed in, failed.\n"); + return -EINVAL; + } + + if(!sockets) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_list_remove_socket: " + "NULL sockets list handed in, failed.\n"); + return -EINVAL; + } + + socket_listp = *sockets; + prev = NULL; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_list_remove_socket: " + "removing sock=0p%p\n", + socketp); + + while(socket_listp != NULL) { + if(socket_listp->socketp == socketp) { + if(prev != NULL) { + prev->next = socket_listp->next; + } else { + *sockets = socket_listp->next; + } + + kfree((void*)socket_listp); + + break; + } + prev = socket_listp; + socket_listp = socket_listp->next; + } + + return 0; +} + +int +pfkey_list_insert_socket(struct socket *socketp, struct socket_list **sockets) +{ + struct socket_list *socket_listp; + + if(!socketp) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_list_insert_socket: " + "NULL socketp handed in, failed.\n"); + return -EINVAL; + } + + if(!sockets) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_list_insert_socket: " + "NULL sockets list handed in, failed.\n"); + return -EINVAL; + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_list_insert_socket: " + "allocating %lu bytes for socketp=0p%p\n", + (unsigned long) sizeof(struct socket_list), + socketp); + + if((socket_listp = (struct socket_list *)kmalloc(sizeof(struct socket_list), GFP_KERNEL)) == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_list_insert_socket: " + "memory allocation error.\n"); + return -ENOMEM; + } + + socket_listp->socketp = socketp; + socket_listp->next = *sockets; + *sockets = socket_listp; + + return 0; +} + +int +pfkey_list_remove_supported(struct supported *supported, struct supported_list **supported_list) +{ + struct supported_list *supported_listp = *supported_list, *prev = NULL; + + if(!supported) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_list_remove_supported: " + "NULL supported handed in, failed.\n"); + return -EINVAL; + } + + if(!supported_list) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_list_remove_supported: " + "NULL supported_list handed in, failed.\n"); + return -EINVAL; + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_list_remove_supported: " + "removing supported=0p%p\n", + supported); + + while(supported_listp != NULL) { + if(supported_listp->supportedp == supported) { + if(prev != NULL) { + prev->next = supported_listp->next; + } else { + *supported_list = supported_listp->next; + } + + kfree((void*)supported_listp); + + break; + } + prev = supported_listp; + supported_listp = supported_listp->next; + } + + return 0; +} + +int +pfkey_list_insert_supported(struct supported *supported, struct supported_list **supported_list) +{ + struct supported_list *supported_listp; + + if(!supported) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_list_insert_supported: " + "NULL supported handed in, failed.\n"); + return -EINVAL; + } + + if(!supported_list) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_list_insert_supported: " + "NULL supported_list handed in, failed.\n"); + return -EINVAL; + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_list_insert_supported: " + "allocating %lu bytes for incoming, supported=0p%p, supported_list=0p%p\n", + (unsigned long) sizeof(struct supported_list), + supported, + supported_list); + + supported_listp = (struct supported_list *)kmalloc(sizeof(struct supported_list), GFP_KERNEL); + if(supported_listp == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_list_insert_supported: " + "memory allocation error.\n"); + return -ENOMEM; + } + + supported_listp->supportedp = supported; + supported_listp->next = *supported_list; + *supported_list = supported_listp; + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_list_insert_supported: " + "outgoing, supported=0p%p, supported_list=0p%p\n", + supported, + supported_list); + + return 0; +} + +#ifndef NET_21 +DEBUG_NO_STATIC void +pfkey_state_change(struct sock *sk) +{ + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_state_change: .\n"); + if(!sk->dead) { + wake_up_interruptible(sk->sleep); + } +} +#endif /* !NET_21 */ + +#ifndef NET_21 +DEBUG_NO_STATIC void +pfkey_data_ready(struct sock *sk, int len) +{ + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_data_ready: " + "sk=0p%p len=%d\n", + sk, + len); + if(!sk->dead) { + wake_up_interruptible(sk->sleep); + sock_wake_async(sk->socket, 1); + } +} + +DEBUG_NO_STATIC void +pfkey_write_space(struct sock *sk) +{ + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_write_space: .\n"); + if(!sk->dead) { + wake_up_interruptible(sk->sleep); + sock_wake_async(sk->socket, 2); + } +} +#endif /* !NET_21 */ + +DEBUG_NO_STATIC void +pfkey_insert_socket(struct sock *sk) +{ + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_insert_socket: " + "sk=0p%p\n", + sk); + cli(); + sk->next=pfkey_sock_list; + pfkey_sock_list=sk; + sti(); +} + +DEBUG_NO_STATIC void +pfkey_remove_socket(struct sock *sk) +{ + struct sock **s; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_remove_socket: .\n"); + cli(); + s=&pfkey_sock_list; + + while(*s!=NULL) { + if(*s==sk) { + *s=sk->next; + sk->next=NULL; + sti(); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_remove_socket: " + "succeeded.\n"); + return; + } + s=&((*s)->next); + } + sti(); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_remove_socket: " + "not found.\n"); + return; +} + +DEBUG_NO_STATIC void +pfkey_destroy_socket(struct sock *sk) +{ + struct sk_buff *skb; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_destroy_socket: .\n"); + pfkey_remove_socket(sk); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_destroy_socket: " + "pfkey_remove_socket called.\n"); + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_destroy_socket: " + "sk(0p%p)->(&0p%p)receive_queue.{next=0p%p,prev=0p%p}.\n", + sk, + &(sk->receive_queue), + sk->receive_queue.next, + sk->receive_queue.prev); + while(sk && ((skb=skb_dequeue(&(sk->receive_queue)))!=NULL)) { +#ifdef NET_21 +#ifdef CONFIG_IPSEC_DEBUG + if(debug_pfkey && sysctl_ipsec_debug_verbose) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_destroy_socket: " + "skb=0p%p dequeued.\n", skb); + printk(KERN_INFO "klips_debug:pfkey_destroy_socket: " + "pfkey_skb contents:"); + printk(" next:0p%p", skb->next); + printk(" prev:0p%p", skb->prev); + printk(" list:0p%p", skb->list); + printk(" sk:0p%p", skb->sk); + printk(" stamp:%ld.%ld", skb->stamp.tv_sec, skb->stamp.tv_usec); + printk(" dev:0p%p", skb->dev); + if(skb->dev) { + if(skb->dev->name) { + printk(" dev->name:%s", skb->dev->name); + } else { + printk(" dev->name:NULL?"); + } + } else { + printk(" dev:NULL"); + } + printk(" h:0p%p", skb->h.raw); + printk(" nh:0p%p", skb->nh.raw); + printk(" mac:0p%p", skb->mac.raw); + printk(" dst:0p%p", skb->dst); + if(sysctl_ipsec_debug_verbose) { + int i; + + printk(" cb"); + for(i=0; i<48; i++) { + printk(":%2x", skb->cb[i]); + } + } + printk(" len:%d", skb->len); + printk(" csum:%d", skb->csum); +#ifndef NETDEV_23 + printk(" used:%d", skb->used); + printk(" is_clone:%d", skb->is_clone); +#endif /* NETDEV_23 */ + printk(" cloned:%d", skb->cloned); + printk(" pkt_type:%d", skb->pkt_type); + printk(" ip_summed:%d", skb->ip_summed); + printk(" priority:%d", skb->priority); + printk(" protocol:%d", skb->protocol); + printk(" security:%d", skb->security); + printk(" truesize:%d", skb->truesize); + printk(" head:0p%p", skb->head); + printk(" data:0p%p", skb->data); + printk(" tail:0p%p", skb->tail); + printk(" end:0p%p", skb->end); + if(sysctl_ipsec_debug_verbose) { + unsigned char* i; + printk(" data"); + for(i = skb->head; i < skb->end; i++) { + printk(":%2x", (unsigned char)(*(i))); + } + } + printk(" destructor:0p%p", skb->destructor); + printk("\n"); + } +#endif /* CONFIG_IPSEC_DEBUG */ +#endif /* NET_21 */ + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_destroy_socket: " + "skb=0p%p freed.\n", + skb); + ipsec_kfree_skb(skb); + } + + sk->dead = 1; + sk_free(sk); + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_destroy_socket: destroyed.\n"); +} + +int +pfkey_upmsg(struct socket *sock, struct sadb_msg *pfkey_msg) +{ + int error = 0; + struct sk_buff * skb = NULL; + struct sock *sk; + + if(sock == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_upmsg: " + "NULL socket passed in.\n"); + return -EINVAL; + } + + if(pfkey_msg == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_upmsg: " + "NULL pfkey_msg passed in.\n"); + return -EINVAL; + } + +#ifdef NET_21 + sk = sock->sk; +#else /* NET_21 */ + sk = sock->data; +#endif /* NET_21 */ + + if(sk == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_upmsg: " + "NULL sock passed in.\n"); + return -EINVAL; + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_upmsg: " + "allocating %d bytes...\n", + (int)(pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN)); + if(!(skb = alloc_skb(pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN, GFP_ATOMIC) )) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_upmsg: " + "no buffers left to send up a message.\n"); + return -ENOBUFS; + } + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_upmsg: " + "...allocated at 0p%p.\n", + skb); + + skb->dev = NULL; + + if(skb_tailroom(skb) < pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN) { + printk(KERN_WARNING "klips_error:pfkey_upmsg: " + "tried to skb_put %ld, %d available. This should never happen, please report.\n", + (unsigned long int)pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN, + skb_tailroom(skb)); + ipsec_kfree_skb(skb); + return -ENOBUFS; + } + skb->h.raw = skb_put(skb, pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN); + memcpy(skb->h.raw, pfkey_msg, pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN); + +#ifndef NET_21 + skb->free = 1; +#endif /* !NET_21 */ + + if((error = sock_queue_rcv_skb(sk, skb)) < 0) { + skb->sk=NULL; + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_upmsg: " + "error=%d calling sock_queue_rcv_skb with skb=0p%p.\n", + error, + skb); + ipsec_kfree_skb(skb); + return error; + } + return error; +} + +DEBUG_NO_STATIC int +pfkey_create(struct socket *sock, int protocol) +{ + struct sock *sk; + + if(sock == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_create: " + "socket NULL.\n"); + return -EINVAL; + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_create: " + "sock=0p%p type:%d state:%d flags:%ld protocol:%d\n", + sock, + sock->type, + (unsigned int)(sock->state), + sock->flags, protocol); + + if(sock->type != SOCK_RAW) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_create: " + "only SOCK_RAW supported.\n"); + return -ESOCKTNOSUPPORT; + } + + if(protocol != PF_KEY_V2) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_create: " + "protocol not PF_KEY_V2.\n"); + return -EPROTONOSUPPORT; + } + + if((current->uid != 0)) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_create: " + "must be root to open pfkey sockets.\n"); + return -EACCES; + } + +#ifdef NET_21 + sock->state = SS_UNCONNECTED; +#endif /* NET_21 */ + MOD_INC_USE_COUNT; +#ifdef NET_21 + if((sk=(struct sock *)sk_alloc(PF_KEY, GFP_KERNEL, 1)) == NULL) +#else /* NET_21 */ + if((sk=(struct sock *)sk_alloc(GFP_KERNEL)) == NULL) +#endif /* NET_21 */ + { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_create: " + "Out of memory trying to allocate.\n"); + MOD_DEC_USE_COUNT; + return -ENOMEM; + } + +#ifndef NET_21 + memset(sk, 0, sizeof(*sk)); +#endif /* !NET_21 */ + +#ifdef NET_21 + sock_init_data(sock, sk); + + sk->destruct = NULL; + sk->reuse = 1; + sock->ops = &pfkey_ops; + + sk->zapped=0; + sk->family = PF_KEY; +/* sk->num = protocol; */ + sk->protocol = protocol; + key_pid(sk) = current->pid; + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_create: " + "sock->fasync_list=0p%p sk->sleep=0p%p.\n", + sock->fasync_list, + sk->sleep); +#else /* NET_21 */ + sk->type=sock->type; + init_timer(&sk->timer); + skb_queue_head_init(&sk->write_queue); + skb_queue_head_init(&sk->receive_queue); + skb_queue_head_init(&sk->back_log); + sk->rcvbuf=SK_RMEM_MAX; + sk->sndbuf=SK_WMEM_MAX; + sk->allocation=GFP_KERNEL; + sk->state=TCP_CLOSE; + sk->priority=SOPRI_NORMAL; + sk->state_change=pfkey_state_change; + sk->data_ready=pfkey_data_ready; + sk->write_space=pfkey_write_space; + sk->error_report=pfkey_state_change; + sk->mtu=4096; + sk->socket=sock; + sock->data=(void *)sk; + sk->sleep=sock->wait; +#endif /* NET_21 */ + + pfkey_insert_socket(sk); + pfkey_list_insert_socket(sock, &pfkey_open_sockets); + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_create: " + "Socket sock=0p%p sk=0p%p initialised.\n", sock, sk); + return 0; +} + +#ifndef NET_21 +DEBUG_NO_STATIC int +pfkey_dup(struct socket *newsock, struct socket *oldsock) +{ + struct sock *sk; + + if(newsock==NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_dup: " + "No new socket attached.\n"); + return -EINVAL; + } + + if(oldsock==NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_dup: " + "No old socket attached.\n"); + return -EINVAL; + } + +#ifdef NET_21 + sk=oldsock->sk; +#else /* NET_21 */ + sk=oldsock->data; +#endif /* NET_21 */ + + /* May not have data attached */ + if(sk==NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_dup: " + "No sock attached to old socket.\n"); + return -EINVAL; + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_dup: .\n"); + + return pfkey_create(newsock, sk->protocol); +} +#endif /* !NET_21 */ + +DEBUG_NO_STATIC int +#ifdef NETDEV_23 +pfkey_release(struct socket *sock) +#else /* NETDEV_23 */ +pfkey_release(struct socket *sock, struct socket *peersock) +#endif /* NETDEV_23 */ +{ + struct sock *sk; + int i; + + if(sock==NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_release: " + "No socket attached.\n"); + return 0; /* -EINVAL; */ + } + +#ifdef NET_21 + sk=sock->sk; +#else /* NET_21 */ + sk=sock->data; +#endif /* NET_21 */ + + /* May not have data attached */ + if(sk==NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_release: " + "No sk attached to sock=0p%p.\n", sock); + return 0; /* -EINVAL; */ + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_release: " + "sock=0p%p sk=0p%p\n", sock, sk); + +#ifdef NET_21 + if(!sk->dead) +#endif /* NET_21 */ + if(sk->state_change) { + sk->state_change(sk); + } + +#ifdef NET_21 + sock->sk = NULL; +#else /* NET_21 */ + sock->data = NULL; +#endif /* NET_21 */ + + /* Try to flush out this socket. Throw out buffers at least */ + pfkey_destroy_socket(sk); + pfkey_list_remove_socket(sock, &pfkey_open_sockets); + for(i = SADB_SATYPE_UNSPEC; i <= SADB_SATYPE_MAX; i++) { + pfkey_list_remove_socket(sock, &(pfkey_registered_sockets[i])); + } + + MOD_DEC_USE_COUNT; + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_release: " + "succeeded.\n"); + + return 0; +} + +#ifndef NET_21 +DEBUG_NO_STATIC int +pfkey_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) +{ + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_bind: " + "operation not supported.\n"); + return -EINVAL; +} + +DEBUG_NO_STATIC int +pfkey_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags) +{ + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_connect: " + "operation not supported.\n"); + return -EINVAL; +} + +DEBUG_NO_STATIC int +pfkey_socketpair(struct socket *a, struct socket *b) +{ + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_socketpair: " + "operation not supported.\n"); + return -EINVAL; +} + +DEBUG_NO_STATIC int +pfkey_accept(struct socket *sock, struct socket *newsock, int flags) +{ + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_aaccept: " + "operation not supported.\n"); + return -EINVAL; +} + +DEBUG_NO_STATIC int +pfkey_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, + int peer) +{ + struct sockaddr *ska = (struct sockaddr*)uaddr; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_getname: .\n"); + ska->sa_family = PF_KEY; + *uaddr_len = sizeof(*ska); + return 0; +} + +DEBUG_NO_STATIC int +pfkey_select(struct socket *sock, int sel_type, select_table *wait) +{ + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_select: " + ".sock=0p%p sk=0p%p sel_type=%d\n", + sock, + sock->data, + sel_type); + if(sock == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_select: " + "Null socket passed in.\n"); + return -EINVAL; + } + return datagram_select(sock->data, sel_type, wait); +} + +DEBUG_NO_STATIC int +pfkey_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_ioctl: " + "not supported.\n"); + return -EINVAL; +} + +DEBUG_NO_STATIC int +pfkey_listen(struct socket *sock, int backlog) +{ + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_listen: " + "not supported.\n"); + return -EINVAL; +} +#endif /* !NET_21 */ + +DEBUG_NO_STATIC int +pfkey_shutdown(struct socket *sock, int mode) +{ + struct sock *sk; + + if(sock == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_shutdown: " + "NULL socket passed in.\n"); + return -EINVAL; + } + +#ifdef NET_21 + sk=sock->sk; +#else /* NET_21 */ + sk=sock->data; +#endif /* NET_21 */ + + if(sk == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_shutdown: " + "No sock attached to socket.\n"); + return -EINVAL; + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_shutdown: " + "mode=%x.\n", mode); + mode++; + + if(mode&SEND_SHUTDOWN) { + sk->shutdown|=SEND_SHUTDOWN; + sk->state_change(sk); + } + + if(mode&RCV_SHUTDOWN) { + sk->shutdown|=RCV_SHUTDOWN; + sk->state_change(sk); + } + return 0; +} + +#ifndef NET_21 +DEBUG_NO_STATIC int +pfkey_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen) +{ +#ifndef NET_21 + struct sock *sk; + + if(sock == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_setsockopt: " + "Null socket passed in.\n"); + return -EINVAL; + } + + sk=sock->data; + + if(sk == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_setsockopt: " + "Null sock passed in.\n"); + return -EINVAL; + } +#endif /* !NET_21 */ + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_setsockopt: .\n"); + if(level!=SOL_SOCKET) { + return -EOPNOTSUPP; + } +#ifdef NET_21 + return sock_setsockopt(sock, level, optname, optval, optlen); +#else /* NET_21 */ + return sock_setsockopt(sk, level, optname, optval, optlen); +#endif /* NET_21 */ +} + +DEBUG_NO_STATIC int +pfkey_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) +{ +#ifndef NET_21 + struct sock *sk; + + if(sock == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_setsockopt: " + "Null socket passed in.\n"); + return -EINVAL; + } + + sk=sock->data; + + if(sk == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_setsockopt: " + "Null sock passed in.\n"); + return -EINVAL; + } +#endif /* !NET_21 */ + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_getsockopt: .\n"); + if(level!=SOL_SOCKET) { + return -EOPNOTSUPP; + } +#ifdef NET_21 + return sock_getsockopt(sock, level, optname, optval, optlen); +#else /* NET_21 */ + return sock_getsockopt(sk, level, optname, optval, optlen); +#endif /* NET_21 */ +} + +DEBUG_NO_STATIC int +pfkey_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_fcntl: " + "not supported.\n"); + return -EINVAL; +} +#endif /* !NET_21 */ + +/* + * Send PF_KEY data down. + */ + +DEBUG_NO_STATIC int +#ifdef NET_21 +pfkey_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm) +#else /* NET_21 */ +pfkey_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nonblock, int flags) +#endif /* NET_21 */ +{ + struct sock *sk; + int error = 0; + struct sadb_msg *pfkey_msg = NULL, *pfkey_reply = NULL; + + if(sock == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_sendmsg: " + "Null socket passed in.\n"); + SENDERR(EINVAL); + } + +#ifdef NET_21 + sk = sock->sk; +#else /* NET_21 */ + sk = sock->data; +#endif /* NET_21 */ + + if(sk == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_sendmsg: " + "Null sock passed in.\n"); + SENDERR(EINVAL); + } + + if(msg == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_sendmsg: " + "Null msghdr passed in.\n"); + SENDERR(EINVAL); + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_sendmsg: .\n"); + if(sk->err) { + error = sock_error(sk); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_sendmsg: " + "sk->err is non-zero, returns %d.\n", + error); + SENDERR(-error); + } + + if((current->uid != 0)) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_sendmsg: " + "must be root to send messages to pfkey sockets.\n"); + SENDERR(EACCES); + } + +#ifdef NET_21 + if(msg->msg_control) +#else /* NET_21 */ + if(flags || msg->msg_control) +#endif /* NET_21 */ + { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_sendmsg: " + "can't set flags or set msg_control.\n"); + SENDERR(EINVAL); + } + + if(sk->shutdown & SEND_SHUTDOWN) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_sendmsg: " + "shutdown.\n"); + send_sig(SIGPIPE, current, 0); + SENDERR(EPIPE); + } + + if(len < sizeof(struct sadb_msg)) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_sendmsg: " + "bogus msg len of %d, too small.\n", len); + SENDERR(EMSGSIZE); + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_sendmsg: " + "allocating %d bytes for downward message.\n", + len); + if((pfkey_msg = (struct sadb_msg*)kmalloc(len, GFP_KERNEL)) == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_sendmsg: " + "memory allocation error.\n"); + SENDERR(ENOBUFS); + } + + memcpy_fromiovec((void *)pfkey_msg, msg->msg_iov, len); + + if(pfkey_msg->sadb_msg_version != PF_KEY_V2) { + KLIPS_PRINT(1 || debug_pfkey, + "klips_debug:pfkey_sendmsg: " + "not PF_KEY_V2 msg, found %d, should be %d.\n", + pfkey_msg->sadb_msg_version, + PF_KEY_V2); + kfree((void*)pfkey_msg); + return -EINVAL; + } + + if(len != pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_sendmsg: " + "bogus msg len of %d, not %d byte aligned.\n", + len, (int)IPSEC_PFKEYv2_ALIGN); + SENDERR(EMSGSIZE); + } + +#if 0 + /* This check is questionable, since a downward message could be + the result of an ACQUIRE either from kernel (PID==0) or + userspace (some other PID). */ + /* check PID */ + if(pfkey_msg->sadb_msg_pid != current->pid) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_sendmsg: " + "pid (%d) does not equal sending process pid (%d).\n", + pfkey_msg->sadb_msg_pid, current->pid); + SENDERR(EINVAL); + } +#endif + + if(pfkey_msg->sadb_msg_reserved) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_sendmsg: " + "reserved field must be zero, set to %d.\n", + pfkey_msg->sadb_msg_reserved); + SENDERR(EINVAL); + } + + if((pfkey_msg->sadb_msg_type > SADB_MAX) || (!pfkey_msg->sadb_msg_type)){ + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_sendmsg: " + "msg type too large or small:%d.\n", + pfkey_msg->sadb_msg_type); + SENDERR(EINVAL); + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_sendmsg: " + "msg sent for parsing.\n"); + + if((error = pfkey_msg_interp(sk, pfkey_msg, &pfkey_reply))) { + struct socket_list *pfkey_socketsp; + + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_sendmsg: " + "pfkey_msg_parse returns %d.\n", + error); + + if((pfkey_reply = (struct sadb_msg*)kmalloc(sizeof(struct sadb_msg), GFP_KERNEL)) == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_sendmsg: " + "memory allocation error.\n"); + SENDERR(ENOBUFS); + } + memcpy((void*)pfkey_reply, (void*)pfkey_msg, sizeof(struct sadb_msg)); + pfkey_reply->sadb_msg_errno = -error; + pfkey_reply->sadb_msg_len = sizeof(struct sadb_msg) / IPSEC_PFKEYv2_ALIGN; + + for(pfkey_socketsp = pfkey_open_sockets; + pfkey_socketsp; + pfkey_socketsp = pfkey_socketsp->next) { + int error_upmsg = 0; + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_sendmsg: " + "sending up error=%d message=0p%p to socket=0p%p.\n", + error, + pfkey_reply, + pfkey_socketsp->socketp); + if((error_upmsg = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_sendmsg: " + "sending up error message to socket=0p%p failed with error=%d.\n", + pfkey_socketsp->socketp, + error_upmsg); + /* pfkey_msg_free(&pfkey_reply); */ + /* SENDERR(-error); */ + } + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_sendmsg: " + "sending up error message to socket=0p%p succeeded.\n", + pfkey_socketsp->socketp); + } + + pfkey_msg_free(&pfkey_reply); + + SENDERR(-error); + } + + errlab: + if (pfkey_msg) { + kfree((void*)pfkey_msg); + } + + if(error) { + return error; + } else { + return len; + } +} + +/* + * Receive PF_KEY data up. + */ + +DEBUG_NO_STATIC int +#ifdef NET_21 +pfkey_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm) +#else /* NET_21 */ +pfkey_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len) +#endif /* NET_21 */ +{ + struct sock *sk; +#ifdef NET_21 + int noblock = flags & MSG_DONTWAIT; +#endif /* NET_21 */ + struct sk_buff *skb; + int error; + + if(sock == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_recvmsg: " + "Null socket passed in.\n"); + return -EINVAL; + } + +#ifdef NET_21 + sk = sock->sk; +#else /* NET_21 */ + sk = sock->data; +#endif /* NET_21 */ + + if(sk == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_recvmsg: " + "Null sock passed in for sock=0p%p.\n", sock); + return -EINVAL; + } + + if(msg == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_recvmsg: " + "Null msghdr passed in for sock=0p%p, sk=0p%p.\n", + sock, sk); + return -EINVAL; + } + + KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, + "klips_debug:pfkey_recvmsg: sock=0p%p sk=0p%p msg=0p%p size=%d.\n", + sock, sk, msg, size); + if(flags & ~MSG_PEEK) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_sendmsg: " + "flags (%d) other than MSG_PEEK not supported.\n", + flags); + return -EOPNOTSUPP; + } + +#ifdef NET_21 + msg->msg_namelen = 0; /* sizeof(*ska); */ +#else /* NET_21 */ + if(addr_len) { + *addr_len = 0; /* sizeof(*ska); */ + } +#endif /* NET_21 */ + + if(sk->err) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_sendmsg: " + "sk->err=%d.\n", sk->err); + return sock_error(sk); + } + + if((skb = skb_recv_datagram(sk, flags, noblock, &error) ) == NULL) { + return error; + } + + if(size > skb->len) { + size = skb->len; + } +#ifdef NET_21 + else if(size <skb->len) { + msg->msg_flags |= MSG_TRUNC; + } +#endif /* NET_21 */ + + skb_copy_datagram_iovec(skb, 0, msg->msg_iov, size); + sk->stamp=skb->stamp; + + skb_free_datagram(sk, skb); + return size; +} + +#ifdef NET_21 +struct net_proto_family pfkey_family_ops = { + PF_KEY, + pfkey_create +}; + +struct proto_ops SOCKOPS_WRAPPED(pfkey_ops) = { +#ifdef NETDEV_23 + family: PF_KEY, + release: pfkey_release, + bind: sock_no_bind, + connect: sock_no_connect, + socketpair: sock_no_socketpair, + accept: sock_no_accept, + getname: sock_no_getname, + poll: datagram_poll, + ioctl: sock_no_ioctl, + listen: sock_no_listen, + shutdown: pfkey_shutdown, + setsockopt: sock_no_setsockopt, + getsockopt: sock_no_getsockopt, + sendmsg: pfkey_sendmsg, + recvmsg: pfkey_recvmsg, + mmap: sock_no_mmap, +#else /* NETDEV_23 */ + PF_KEY, + sock_no_dup, + pfkey_release, + sock_no_bind, + sock_no_connect, + sock_no_socketpair, + sock_no_accept, + sock_no_getname, + datagram_poll, + sock_no_ioctl, + sock_no_listen, + pfkey_shutdown, + sock_no_setsockopt, + sock_no_getsockopt, + sock_no_fcntl, + pfkey_sendmsg, + pfkey_recvmsg +#endif /* NETDEV_23 */ +}; + +#ifdef NETDEV_23 +#include <linux/smp_lock.h> +SOCKOPS_WRAP(pfkey, PF_KEY); +#endif /* NETDEV_23 */ + +#else /* NET_21 */ +struct proto_ops pfkey_proto_ops = { + PF_KEY, + pfkey_create, + pfkey_dup, + pfkey_release, + pfkey_bind, + pfkey_connect, + pfkey_socketpair, + pfkey_accept, + pfkey_getname, + pfkey_select, + pfkey_ioctl, + pfkey_listen, + pfkey_shutdown, + pfkey_setsockopt, + pfkey_getsockopt, + pfkey_fcntl, + pfkey_sendmsg, + pfkey_recvmsg +}; +#endif /* NET_21 */ + +#ifdef CONFIG_PROC_FS +#ifndef PROC_FS_2325 +DEBUG_NO_STATIC +#endif /* PROC_FS_2325 */ +int +pfkey_get_info(char *buffer, char **start, off_t offset, int length +#ifndef PROC_NO_DUMMY +, int dummy +#endif /* !PROC_NO_DUMMY */ +) +{ + const int max_content = length > 0? length-1 : 0; + + off_t begin=0; + int len=0; + struct sock *sk=pfkey_sock_list; + +#ifdef CONFIG_IPSEC_DEBUG + if(!sysctl_ipsec_debug_verbose) { +#endif /* CONFIG_IPSEC_DEBUG */ + len+= snprintf(buffer,length, + " sock pid socket next prev e n p sndbf Flags Type St\n"); +#ifdef CONFIG_IPSEC_DEBUG + } else { + len+= snprintf(buffer,length, + " sock pid d sleep socket next prev e r z n p sndbf stamp Flags Type St\n"); + } +#endif /* CONFIG_IPSEC_DEBUG */ + + while(sk!=NULL) { +#ifdef CONFIG_IPSEC_DEBUG + if(!sysctl_ipsec_debug_verbose) { +#endif /* CONFIG_IPSEC_DEBUG */ + len += ipsec_snprintf(buffer+len, length-len, + "%8p %5d %8p %8p %8p %d %d %d %5d %08lX %8X %2X\n", + sk, + key_pid(sk), + sk->socket, + sk->next, + sk->prev, + sk->err, + sk->num, + sk->protocol, + sk->sndbuf, + sk->socket->flags, + sk->socket->type, + sk->socket->state); +#ifdef CONFIG_IPSEC_DEBUG + } else { + len += ipsec_snprintf(buffer+len, length-len, + "%8p %5d %d %8p %8p %8p %8p %d %d %d %d %d %5d %d.%06d %08lX %8X %2X\n", + sk, + key_pid(sk), + sk->dead, + sk->sleep, + sk->socket, + sk->next, + sk->prev, + sk->err, + sk->reuse, + sk->zapped, + sk->num, + sk->protocol, + sk->sndbuf, + (unsigned int)sk->stamp.tv_sec, + (unsigned int)sk->stamp.tv_usec, + sk->socket->flags, + sk->socket->type, + sk->socket->state); + } +#endif /* CONFIG_IPSEC_DEBUG */ + + if (len >= max_content) { + /* we've done all that can fit -- stop loop */ + len = max_content; /* truncate crap */ + break; + } else { + const off_t pos = begin + len; /* file position of end of what we've generated */ + + if (pos <= offset) { + /* all is before first interesting character: + * discard, but note where we are. + */ + len = 0; + begin = pos; + } + } + sk=sk->next; + } + + *start = buffer + (offset - begin); /* Start of wanted data */ + return len - (offset - begin); +} + +#ifndef PROC_FS_2325 +DEBUG_NO_STATIC +#endif /* PROC_FS_2325 */ +int +pfkey_supported_get_info(char *buffer, char **start, off_t offset, int length +#ifndef PROC_NO_DUMMY +, int dummy +#endif /* !PROC_NO_DUMMY */ +) +{ + const int max_content = length > 0? length-1 : 0; + + off_t begin=0; + int len=0; + int satype; + struct supported_list *pfkey_supported_p; + + len += ipsec_snprintf(buffer, length, + "satype exttype alg_id ivlen minbits maxbits\n"); + + for(satype = SADB_SATYPE_UNSPEC; satype <= SADB_SATYPE_MAX; satype++) { + pfkey_supported_p = pfkey_supported_list[satype]; + while(pfkey_supported_p) { + len += ipsec_snprintf(buffer+len, length-len, + " %2d %2d %2d %3d %3d %3d\n", + satype, + pfkey_supported_p->supportedp->supported_alg_exttype, + pfkey_supported_p->supportedp->supported_alg_id, + pfkey_supported_p->supportedp->supported_alg_ivlen, + pfkey_supported_p->supportedp->supported_alg_minbits, + pfkey_supported_p->supportedp->supported_alg_maxbits); + + if (len >= max_content) { + /* we've done all that can fit -- stop loop */ + len = max_content; /* truncate crap */ + break; + } else { + const off_t pos = begin + len; /* file position of end of what we've generated */ + + if (pos <= offset) { + /* all is before first interesting character: + * discard, but note where we are. + */ + len = 0; + begin = pos; + } + } + + pfkey_supported_p = pfkey_supported_p->next; + } + } + + *start = buffer + (offset - begin); /* Start of wanted data */ + return len - (offset - begin); +} + +#ifndef PROC_FS_2325 +DEBUG_NO_STATIC +#endif /* PROC_FS_2325 */ +int +pfkey_registered_get_info(char *buffer, char **start, off_t offset, int length +#ifndef PROC_NO_DUMMY +, int dummy +#endif /* !PROC_NO_DUMMY */ +) +{ + const int max_content = length > 0? length-1 : 0; + + off_t begin=0; + int len=0; + int satype; + struct socket_list *pfkey_sockets; + + len += ipsec_snprintf(buffer, length, + "satype socket pid sk\n"); + + for(satype = SADB_SATYPE_UNSPEC; satype <= SADB_SATYPE_MAX; satype++) { + pfkey_sockets = pfkey_registered_sockets[satype]; + while(pfkey_sockets) { +#ifdef NET_21 + len += ipsec_snprintf(buffer+len, length-len, + " %2d %8p %5d %8p\n", + satype, + pfkey_sockets->socketp, + key_pid(pfkey_sockets->socketp->sk), + pfkey_sockets->socketp->sk); +#else /* NET_21 */ + len += ipsec_snprintf(buffer+len, length-len, + " %2d %8p N/A %8p\n", + satype, + pfkey_sockets->socketp, +#if 0 + key_pid((pfkey_sockets->socketp)->data), +#endif + (pfkey_sockets->socketp)->data); +#endif /* NET_21 */ + + if (len >= max_content) { + /* we've done all that can fit -- stop loop (could stop two) */ + len = max_content; /* truncate crap */ + break; + } else { + const off_t pos = begin + len; /* file position of end of what we've generated */ + + if (pos <= offset) { + /* all is before first interesting character: + * discard, but note where we are. + */ + len = 0; + begin = pos; + } + } + + pfkey_sockets = pfkey_sockets->next; + } + } + + *start = buffer + (offset - begin); /* Start of wanted data */ + return len - (offset - begin); +} + +#ifndef PROC_FS_2325 +struct proc_dir_entry proc_net_pfkey = +{ + 0, + 6, "pf_key", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_net_inode_operations, + pfkey_get_info +}; +struct proc_dir_entry proc_net_pfkey_supported = +{ + 0, + 16, "pf_key_supported", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_net_inode_operations, + pfkey_supported_get_info +}; +struct proc_dir_entry proc_net_pfkey_registered = +{ + 0, + 17, "pf_key_registered", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_net_inode_operations, + pfkey_registered_get_info +}; +#endif /* !PROC_FS_2325 */ +#endif /* CONFIG_PROC_FS */ + +DEBUG_NO_STATIC int +supported_add_all(int satype, struct supported supported[], int size) +{ + int i; + int error = 0; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:init_pfkey: " + "sizeof(supported_init_<satype=%d>)[%d]/sizeof(struct supported)[%d]=%d.\n", + satype, + size, + (int)sizeof(struct supported), + (int)(size/sizeof(struct supported))); + + for(i = 0; i < size / sizeof(struct supported); i++) { + + KLIPS_PRINT(debug_pfkey, + "klips_debug:init_pfkey: " + "i=%d inserting satype=%d exttype=%d id=%d ivlen=%d minbits=%d maxbits=%d.\n", + i, + satype, + supported[i].supported_alg_exttype, + supported[i].supported_alg_id, + supported[i].supported_alg_ivlen, + supported[i].supported_alg_minbits, + supported[i].supported_alg_maxbits); + + error |= pfkey_list_insert_supported(&(supported[i]), + &(pfkey_supported_list[satype])); + } + return error; +} + +DEBUG_NO_STATIC int +supported_remove_all(int satype) +{ + int error = 0; + struct supported*supportedp; + + while(pfkey_supported_list[satype]) { + supportedp = pfkey_supported_list[satype]->supportedp; + KLIPS_PRINT(debug_pfkey, + "klips_debug:init_pfkey: " + "removing satype=%d exttype=%d id=%d ivlen=%d minbits=%d maxbits=%d.\n", + satype, + supportedp->supported_alg_exttype, + supportedp->supported_alg_id, + supportedp->supported_alg_ivlen, + supportedp->supported_alg_minbits, + supportedp->supported_alg_maxbits); + + error |= pfkey_list_remove_supported(supportedp, + &(pfkey_supported_list[satype])); + } + return error; +} + +int +pfkey_init(void) +{ + int error = 0; + int i; + + static struct supported supported_init_ah[] = { +#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 + {SADB_EXT_SUPPORTED_AUTH, SADB_AALG_MD5_HMAC, 0, 128, 128}, +#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ +#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 + {SADB_EXT_SUPPORTED_AUTH, SADB_AALG_SHA1_HMAC, 0, 160, 160} +#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ + }; + static struct supported supported_init_esp[] = { +#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 + {SADB_EXT_SUPPORTED_AUTH, SADB_AALG_MD5_HMAC, 0, 128, 128}, +#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ +#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 + {SADB_EXT_SUPPORTED_AUTH, SADB_AALG_SHA1_HMAC, 0, 160, 160}, +#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ +#ifdef CONFIG_IPSEC_ENC_3DES + {SADB_EXT_SUPPORTED_ENCRYPT, SADB_EALG_3DES_CBC, 64, 168, 168}, +#endif /* CONFIG_IPSEC_ENC_3DES */ + }; + static struct supported supported_init_ipip[] = { + {SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_TALG_IPv4_in_IPv4, 0, 32, 32} +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + , {SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_TALG_IPv6_in_IPv4, 0, 128, 32} + , {SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_TALG_IPv4_in_IPv6, 0, 32, 128} + , {SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_TALG_IPv6_in_IPv6, 0, 128, 128} +#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ + }; +#ifdef CONFIG_IPSEC_IPCOMP + static struct supported supported_init_ipcomp[] = { + {SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_CALG_DEFLATE, 0, 1, 1} + }; +#endif /* CONFIG_IPSEC_IPCOMP */ + +#if 0 + printk(KERN_INFO + "klips_info:pfkey_init: " + "FreeS/WAN: initialising PF_KEYv2 domain sockets.\n"); +#endif + + for(i = SADB_SATYPE_UNSPEC; i <= SADB_SATYPE_MAX; i++) { + pfkey_registered_sockets[i] = NULL; + pfkey_supported_list[i] = NULL; + } + + error |= supported_add_all(SADB_SATYPE_AH, supported_init_ah, sizeof(supported_init_ah)); + error |= supported_add_all(SADB_SATYPE_ESP, supported_init_esp, sizeof(supported_init_esp)); +#ifdef CONFIG_IPSEC_IPCOMP + error |= supported_add_all(SADB_X_SATYPE_COMP, supported_init_ipcomp, sizeof(supported_init_ipcomp)); +#endif /* CONFIG_IPSEC_IPCOMP */ + error |= supported_add_all(SADB_X_SATYPE_IPIP, supported_init_ipip, sizeof(supported_init_ipip)); + +#ifdef NET_21 + error |= sock_register(&pfkey_family_ops); +#else /* NET_21 */ + error |= sock_register(pfkey_proto_ops.family, &pfkey_proto_ops); +#endif /* NET_21 */ + +#ifdef CONFIG_PROC_FS +# ifndef PROC_FS_2325 +# ifdef PROC_FS_21 + error |= proc_register(proc_net, &proc_net_pfkey); + error |= proc_register(proc_net, &proc_net_pfkey_supported); + error |= proc_register(proc_net, &proc_net_pfkey_registered); +# else /* PROC_FS_21 */ + error |= proc_register_dynamic(&proc_net, &proc_net_pfkey); + error |= proc_register_dynamic(&proc_net, &proc_net_pfkey_supported); + error |= proc_register_dynamic(&proc_net, &proc_net_pfkey_registered); +# endif /* PROC_FS_21 */ +# else /* !PROC_FS_2325 */ + proc_net_create ("pf_key", 0, pfkey_get_info); + proc_net_create ("pf_key_supported", 0, pfkey_supported_get_info); + proc_net_create ("pf_key_registered", 0, pfkey_registered_get_info); +# endif /* !PROC_FS_2325 */ +#endif /* CONFIG_PROC_FS */ + + return error; +} + +int +pfkey_cleanup(void) +{ + int error = 0; + + printk(KERN_INFO "klips_info:pfkey_cleanup: " + "shutting down PF_KEY domain sockets.\n"); +#ifdef NET_21 + error |= sock_unregister(PF_KEY); +#else /* NET_21 */ + error |= sock_unregister(pfkey_proto_ops.family); +#endif /* NET_21 */ + + error |= supported_remove_all(SADB_SATYPE_AH); + error |= supported_remove_all(SADB_SATYPE_ESP); +#ifdef CONFIG_IPSEC_IPCOMP + error |= supported_remove_all(SADB_X_SATYPE_COMP); +#endif /* CONFIG_IPSEC_IPCOMP */ + error |= supported_remove_all(SADB_X_SATYPE_IPIP); + +#ifdef CONFIG_PROC_FS +# ifndef PROC_FS_2325 + if (proc_net_unregister(proc_net_pfkey.low_ino) != 0) + printk("klips_debug:pfkey_cleanup: " + "cannot unregister /proc/net/pf_key\n"); + if (proc_net_unregister(proc_net_pfkey_supported.low_ino) != 0) + printk("klips_debug:pfkey_cleanup: " + "cannot unregister /proc/net/pf_key_supported\n"); + if (proc_net_unregister(proc_net_pfkey_registered.low_ino) != 0) + printk("klips_debug:pfkey_cleanup: " + "cannot unregister /proc/net/pf_key_registered\n"); +# else /* !PROC_FS_2325 */ + proc_net_remove ("pf_key"); + proc_net_remove ("pf_key_supported"); + proc_net_remove ("pf_key_registered"); +# endif /* !PROC_FS_2325 */ +#endif /* CONFIG_PROC_FS */ + + /* other module unloading cleanup happens here */ + return error; +} + +#ifdef MODULE +#if 0 +int +init_module(void) +{ + pfkey_init(); + return 0; +} + +void +cleanup_module(void) +{ + pfkey_cleanup(); +} +#endif /* 0 */ +#else /* MODULE */ +void +pfkey_proto_init(struct net_proto *pro) +{ + pfkey_init(); +} +#endif /* MODULE */ + +/* + * $Log: pfkey_v2.c,v $ + * Revision 1.4 2004/09/29 22:27:41 as + * changed SADB identifiers + * + * Revision 1.3 2004/04/28 08:06:22 as + * added dhr's freeswan-2.06 changes + * + * Revision 1.2 2004/03/22 21:53:19 as + * merged alg-0.8.1 branch with HEAD + * + * Revision 1.1.4.1 2004/03/16 09:48:20 as + * alg-0.8.1rc12 patch merged + * + * Revision 1.1 2004/03/15 20:35:26 as + * added files from freeswan-2.04-x509-1.5.3 + * + * Revision 1.78 2003/04/03 17:38:09 rgb + * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}. + * + * Revision 1.77 2002/10/17 16:49:36 mcr + * sock->ops should reference the unwrapped options so that + * we get hacked in locking on SMP systems. + * + * Revision 1.76 2002/10/12 23:11:53 dhr + * + * [KenB + DHR] more 64-bit cleanup + * + * Revision 1.75 2002/09/20 05:01:57 rgb + * Added memory allocation debugging. + * + * Revision 1.74 2002/09/19 02:42:50 mcr + * do not define the pfkey_ops function for now. + * + * Revision 1.73 2002/09/17 17:29:23 mcr + * #if 0 out some dead code - pfkey_ops is never used as written. + * + * Revision 1.72 2002/07/24 18:44:54 rgb + * Type fiddling to tame ia64 compiler. + * + * Revision 1.71 2002/05/23 07:14:11 rgb + * Cleaned up %p variants to 0p%p for test suite cleanup. + * + * Revision 1.70 2002/04/24 07:55:32 mcr + * #include patches and Makefiles for post-reorg compilation. + * + * Revision 1.69 2002/04/24 07:36:33 mcr + * Moved from ./klips/net/ipsec/pfkey_v2.c,v + * + * Revision 1.68 2002/03/08 01:15:17 mcr + * put some internal structure only debug messages behind + * && sysctl_ipsec_debug_verbose. + * + * Revision 1.67 2002/01/29 17:17:57 mcr + * moved include of ipsec_param.h to after include of linux/kernel.h + * otherwise, it seems that some option that is set in ipsec_param.h + * screws up something subtle in the include path to kernel.h, and + * it complains on the snprintf() prototype. + * + * Revision 1.66 2002/01/29 04:00:54 mcr + * more excise of kversions.h header. + * + * Revision 1.65 2002/01/29 02:13:18 mcr + * introduction of ipsec_kversion.h means that include of + * ipsec_param.h must preceed any decisions about what files to + * include to deal with differences in kernel source. + * + * Revision 1.64 2001/11/26 09:23:51 rgb + * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. + * + * Revision 1.61.2.1 2001/09/25 02:28:44 mcr + * cleaned up includes. + * + * Revision 1.63 2001/11/12 19:38:00 rgb + * Continue trying other sockets even if one fails and return only original + * error. + * + * Revision 1.62 2001/10/18 04:45:22 rgb + * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h, + * lib/freeswan.h version macros moved to lib/kversions.h. + * Other compiler directive cleanups. + * + * Revision 1.61 2001/09/20 15:32:59 rgb + * Min/max cleanup. + * + * Revision 1.60 2001/06/14 19:35:12 rgb + * Update copyright date. + * + * Revision 1.59 2001/06/13 15:35:48 rgb + * Fixed #endif comments. + * + * Revision 1.58 2001/05/04 16:37:24 rgb + * Remove erroneous checking of return codes for proc_net_* in 2.4. + * + * Revision 1.57 2001/05/03 19:43:36 rgb + * Initialise error return variable. + * Check error return codes in startup and shutdown. + * Standardise on SENDERR() macro. + * + * Revision 1.56 2001/04/21 23:05:07 rgb + * Define out skb->used for 2.4 kernels. + * + * Revision 1.55 2001/02/28 05:03:28 rgb + * Clean up and rationalise startup messages. + * + * Revision 1.54 2001/02/27 22:24:55 rgb + * Re-formatting debug output (line-splitting, joining, 1arg/line). + * Check for satoa() return codes. + * + * Revision 1.53 2001/02/27 06:48:18 rgb + * Fixed pfkey socket unregister log message to reflect type and function. + * + * Revision 1.52 2001/02/26 22:34:38 rgb + * Fix error return code that was getting overwritten by the error return + * code of an upmsg. + * + * Revision 1.51 2001/01/30 23:42:47 rgb + * Allow pfkey msgs from pid other than user context required for ACQUIRE + * and subsequent ADD or UDATE. + * + * Revision 1.50 2001/01/23 20:22:59 rgb + * 2.4 fix to remove removed is_clone member. + * + * Revision 1.49 2000/11/06 04:33:47 rgb + * Changed non-exported functions to DEBUG_NO_STATIC. + * + * Revision 1.48 2000/09/29 19:47:41 rgb + * Update copyright. + * + * Revision 1.47 2000/09/22 04:23:04 rgb + * Added more debugging to pfkey_upmsg() call from pfkey_sendmsg() error. + * + * Revision 1.46 2000/09/21 04:20:44 rgb + * Fixed array size off-by-one error. (Thanks Svenning!) + * + * Revision 1.45 2000/09/20 04:01:26 rgb + * Changed static functions to DEBUG_NO_STATIC for revealing function names + * in oopsen. + * + * Revision 1.44 2000/09/19 00:33:17 rgb + * 2.0 fixes. + * + * Revision 1.43 2000/09/16 01:28:13 rgb + * Fixed use of 0 in p format warning. + * + * Revision 1.42 2000/09/16 01:09:41 rgb + * Fixed debug format warning for pointers that was expecting ints. + * + * Revision 1.41 2000/09/13 15:54:00 rgb + * Rewrote pfkey_get_info(), added pfkey_{supported,registered}_get_info(). + * Moved supported algos add and remove to functions. + * + * Revision 1.40 2000/09/12 18:49:28 rgb + * Added IPIP tunnel and IPCOMP register support. + * + * Revision 1.39 2000/09/12 03:23:49 rgb + * Converted #if0 debugs to sysctl. + * Removed debug_pfkey initialisations that prevented no_debug loading or + * linking. + * + * Revision 1.38 2000/09/09 06:38:02 rgb + * Return positive errno in pfkey_reply error message. + * + * Revision 1.37 2000/09/08 19:19:09 rgb + * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. + * Clean-up of long-unused crud... + * Create pfkey error message on on failure. + * Give pfkey_list_{insert,remove}_{socket,supported}() some error + * checking. + * + * Revision 1.36 2000/09/01 18:49:38 rgb + * Reap experimental NET_21_ bits. + * Turned registered sockets list into an array of one list per satype. + * Remove references to deprecated sklist_{insert,remove}_socket. + * Removed leaking socket debugging code. + * Removed duplicate pfkey_insert_socket in pfkey_create. + * Removed all references to pfkey msg->msg_name, since it is not used for + * pfkey. + * Added a supported algorithms array lists, one per satype and registered + * existing algorithms. + * Fixed pfkey_list_{insert,remove}_{socket,support}() to allow change to + * list. + * Only send pfkey_expire() messages to sockets registered for that satype. + * + * Revision 1.35 2000/08/24 17:03:00 rgb + * Corrected message size error return code for PF_KEYv2. + * Removed downward error prohibition. + * + * Revision 1.34 2000/08/21 16:32:26 rgb + * Re-formatted for cosmetic consistency and readability. + * + * Revision 1.33 2000/08/20 21:38:24 rgb + * Added a pfkey_reply parameter to pfkey_msg_interp(). (Momchil) + * Extended the upward message initiation of pfkey_sendmsg(). (Momchil) + * + * Revision 1.32 2000/07/28 14:58:31 rgb + * Changed kfree_s to kfree, eliminating extra arg to fix 2.4.0-test5. + * + * Revision 1.31 2000/05/16 03:04:00 rgb + * Updates for 2.3.99pre8 from MB. + * + * Revision 1.30 2000/05/10 19:22:21 rgb + * Use sklist private functions for 2.3.xx compatibility. + * + * Revision 1.29 2000/03/22 16:17:03 rgb + * Fixed SOCKOPS_WRAPPED macro for SMP (MB). + * + * Revision 1.28 2000/02/21 19:30:45 rgb + * Removed references to pkt_bridged for 2.3.47 compatibility. + * + * Revision 1.27 2000/02/14 21:07:00 rgb + * Fixed /proc/net/pf-key legend spacing. + * + * Revision 1.26 2000/01/22 03:46:59 rgb + * Fixed pfkey error return mechanism so that we are able to free the + * local copy of the pfkey_msg, plugging a memory leak and silencing + * the bad object free complaints. + * + * Revision 1.25 2000/01/21 06:19:44 rgb + * Moved pfkey_list_remove_socket() calls to before MOD_USE_DEC_COUNT. + * Added debugging to pfkey_upmsg. + * + * Revision 1.24 2000/01/10 16:38:23 rgb + * MB fixups for 2.3.x. + * + * Revision 1.23 1999/12/09 23:22:16 rgb + * Added more instrumentation for debugging 2.0 socket + * selection/reading. + * Removed erroneous 2.0 wait==NULL check bug in select. + * + * Revision 1.22 1999/12/08 20:32:16 rgb + * Tidied up 2.0.xx support, after major pfkey work, eliminating + * msg->msg_name twiddling in the process, since it is not defined + * for PF_KEYv2. + * + * Revision 1.21 1999/12/01 22:17:19 rgb + * Set skb->dev to zero on new skb in case it is a reused skb. + * Added check for skb_put overflow and freeing to avoid upmsg on error. + * Added check for wrong pfkey version and freeing to avoid upmsg on + * error. + * Shut off content dumping in pfkey_destroy. + * Added debugging message for size of buffer allocated for upmsg. + * + * Revision 1.20 1999/11/27 12:11:00 rgb + * Minor clean-up, enabling quiet operation of pfkey if desired. + * + * Revision 1.19 1999/11/25 19:04:21 rgb + * Update proc_fs code for pfkey to use dynamic registration. + * + * Revision 1.18 1999/11/25 09:07:17 rgb + * Implemented SENDERR macro for propagating error codes. + * Fixed error return code bug. + * + * Revision 1.17 1999/11/23 23:07:20 rgb + * Change name of pfkey_msg_parser to pfkey_msg_interp since it no longer + * parses. (PJO) + * Sort out pfkey and freeswan headers, putting them in a library path. + * + * Revision 1.16 1999/11/20 22:00:22 rgb + * Moved socketlist type declarations and prototypes for shared use. + * Renamed reformatted and generically extended for use by other socket + * lists pfkey_{del,add}_open_socket to pfkey_list_{remove,insert}_socket. + * + * Revision 1.15 1999/11/18 04:15:09 rgb + * Make pfkey_data_ready temporarily available for 2.2.x testing. + * Clean up pfkey_destroy_socket() debugging statements. + * Add Peter Onion's code to send messages up to all listening sockets. + * Changed all occurrences of #include "../../../lib/freeswan.h" + * to #include <freeswan.h> which works due to -Ilibfreeswan in the + * klips/net/ipsec/Makefile. + * Replaced all kernel version macros to shorter, readable form. + * Added CONFIG_PROC_FS compiler directives in case it is shut off. + * + * Revision 1.14 1999/11/17 16:01:00 rgb + * Make pfkey_data_ready temporarily available for 2.2.x testing. + * Clean up pfkey_destroy_socket() debugging statements. + * Add Peter Onion's code to send messages up to all listening sockets. + * Changed #include "../../../lib/freeswan.h" to #include <freeswan.h> + * which works due to -Ilibfreeswan in the klips/net/ipsec/Makefile. + * + * Revision 1.13 1999/10/27 19:59:51 rgb + * Removed af_unix comments that are no longer relevant. + * Added debug prink statements. + * Added to the /proc output in pfkey_get_info. + * Made most functions non-static to enable oops tracing. + * Re-enable skb dequeueing and freeing. + * Fix skb_alloc() and skb_put() size bug in pfkey_upmsg(). + * + * Revision 1.12 1999/10/26 17:05:42 rgb + * Complete re-ordering based on proto_ops structure order. + * Separated out proto_ops structures for 2.0.x and 2.2.x for clarity. + * Simplification to use built-in socket ops where possible for 2.2.x. + * Add shorter macros for compiler directives to visually clean-up. + * Add lots of sk skb dequeueing debugging statements. + * Added to the /proc output in pfkey_get_info. + * + * Revision 1.11 1999/09/30 02:55:10 rgb + * Bogus skb detection. + * Fix incorrect /proc/net/ipsec-eroute printk message. + * + * Revision 1.10 1999/09/21 15:22:13 rgb + * Temporary fix while I figure out the right way to destroy sockets. + * + * Revision 1.9 1999/07/08 19:19:44 rgb + * Fix pointer format warning. + * Fix missing member error under 2.0.xx kernels. + * + * Revision 1.8 1999/06/13 07:24:04 rgb + * Add more debugging. + * + * Revision 1.7 1999/06/10 05:24:17 rgb + * Clarified compiler directives. + * Renamed variables to reduce confusion. + * Used sklist_*_socket() kernel functions to simplify 2.2.x socket support. + * Added lots of sanity checking. + * + * Revision 1.6 1999/06/03 18:59:50 rgb + * More updates to 2.2.x socket support. Almost works, oops at end of call. + * + * Revision 1.5 1999/05/25 22:44:05 rgb + * Start fixing 2.2 sockets. + * + * Revision 1.4 1999/04/29 15:21:34 rgb + * Move log to the end of the file. + * Eliminate min/max redefinition in #include <net/tcp.h>. + * Correct path for pfkey #includes + * Standardise an error return method. + * Add debugging instrumentation. + * Move message type checking to pfkey_msg_parse(). + * Add check for errno incorrectly set. + * Add check for valid PID. + * Add check for reserved illegally set. + * Add check for message out of bounds. + * + * Revision 1.3 1999/04/15 17:58:07 rgb + * Add RCSID labels. + * + * Revision 1.2 1999/04/15 15:37:26 rgb + * Forward check changes from POST1_00 branch. + * + * Revision 1.1.2.2 1999/04/13 20:37:12 rgb + * Header Title correction. + * + * Revision 1.1.2.1 1999/03/26 20:58:55 rgb + * Add pfkeyv2 support to KLIPS. + * + * + * RFC 2367 + * PF_KEY_v2 Key Management API + */ diff --git a/linux/net/ipsec/pfkey_v2_ext_process.c b/linux/net/ipsec/pfkey_v2_ext_process.c new file mode 100644 index 000000000..9269bd59e --- /dev/null +++ b/linux/net/ipsec/pfkey_v2_ext_process.c @@ -0,0 +1,851 @@ +/* + * @(#) RFC2367 PF_KEYv2 Key management API message parser + * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs <rgb@freeswan.org> + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + * + * RCSID $Id: pfkey_v2_ext_process.c,v 1.3 2004/06/13 19:57:50 as Exp $ + */ + +/* + * Template from klips/net/ipsec/ipsec/ipsec_netlink.c. + */ + +char pfkey_v2_ext_process_c_version[] = "$Id: pfkey_v2_ext_process.c,v 1.3 2004/06/13 19:57:50 as Exp $"; + +#include <linux/config.h> +#include <linux/version.h> +#include <linux/kernel.h> /* printk() */ + +#include "freeswan/ipsec_param.h" + +#ifdef MALLOC_SLAB +# include <linux/slab.h> /* kmalloc() */ +#else /* MALLOC_SLAB */ +# include <linux/malloc.h> /* kmalloc() */ +#endif /* MALLOC_SLAB */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/interrupt.h> /* mark_bh */ + +#include <linux/netdevice.h> /* struct device, and other headers */ +#include <linux/etherdevice.h> /* eth_type_trans */ +#include <linux/ip.h> /* struct iphdr */ +#include <linux/skbuff.h> + +#include <freeswan.h> + +#include <crypto/des.h> + +#ifdef SPINLOCK +# ifdef SPINLOCK_23 +# include <linux/spinlock.h> /* *lock* */ +# else /* SPINLOCK_23 */ +# include <asm/spinlock.h> /* *lock* */ +# endif /* SPINLOCK_23 */ +#endif /* SPINLOCK */ +#ifdef NET_21 +# include <asm/uaccess.h> +# include <linux/in6.h> +# define ip_chk_addr inet_addr_type +# define IS_MYADDR RTN_LOCAL +#endif +#include <asm/checksum.h> +#include <net/ip.h> +#ifdef NETLINK_SOCK +# include <linux/netlink.h> +#else +# include <net/netlink.h> +#endif + +#include <linux/random.h> /* get_random_bytes() */ + +#include "freeswan/radij.h" +#include "freeswan/ipsec_encap.h" +#include "freeswan/ipsec_sa.h" + +#include "freeswan/ipsec_radij.h" +#include "freeswan/ipsec_xform.h" +#include "freeswan/ipsec_ah.h" +#include "freeswan/ipsec_esp.h" +#include "freeswan/ipsec_tunnel.h" +#include "freeswan/ipsec_rcv.h" +#include "freeswan/ipcomp.h" + +#include <pfkeyv2.h> +#include <pfkey.h> + +#include "freeswan/ipsec_proto.h" +#include "freeswan/ipsec_alg.h" + +#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0) + +int +pfkey_sa_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) +{ + struct sadb_sa *pfkey_sa = (struct sadb_sa *)pfkey_ext; + int error = 0; + struct ipsec_sa* ipsp; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_sa_process: .\n"); + + if(!extr || !extr->ips) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_sa_process: " + "extr or extr->ips is NULL, fatal\n"); + SENDERR(EINVAL); + } + + switch(pfkey_ext->sadb_ext_type) { + case SADB_EXT_SA: + ipsp = extr->ips; + break; + case SADB_X_EXT_SA2: + if(extr->ips2 == NULL) { + extr->ips2 = ipsec_sa_alloc(&error); /* pass error var by pointer */ + } + if(extr->ips2 == NULL) { + SENDERR(-error); + } + ipsp = extr->ips2; + break; + default: + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_sa_process: " + "invalid exttype=%d.\n", + pfkey_ext->sadb_ext_type); + SENDERR(EINVAL); + } + + ipsp->ips_said.spi = pfkey_sa->sadb_sa_spi; + ipsp->ips_replaywin = pfkey_sa->sadb_sa_replay; + ipsp->ips_state = pfkey_sa->sadb_sa_state; + ipsp->ips_flags = pfkey_sa->sadb_sa_flags; + ipsp->ips_replaywin_lastseq = ipsp->ips_replaywin_bitmap = 0; + ipsp->ips_ref_rel = pfkey_sa->sadb_x_sa_ref; + + switch(ipsp->ips_said.proto) { + case IPPROTO_AH: + ipsp->ips_authalg = pfkey_sa->sadb_sa_auth; + ipsp->ips_encalg = SADB_EALG_NONE; + break; + case IPPROTO_ESP: + ipsp->ips_authalg = pfkey_sa->sadb_sa_auth; + ipsp->ips_encalg = pfkey_sa->sadb_sa_encrypt; +#ifdef CONFIG_IPSEC_ALG + ipsec_alg_sa_init(ipsp); +#endif /* CONFIG_IPSEC_ALG */ + break; + case IPPROTO_IPIP: + ipsp->ips_authalg = AH_NONE; + ipsp->ips_encalg = ESP_NONE; + break; +#ifdef CONFIG_IPSEC_IPCOMP + case IPPROTO_COMP: + ipsp->ips_authalg = AH_NONE; + ipsp->ips_encalg = pfkey_sa->sadb_sa_encrypt; + break; +#endif /* CONFIG_IPSEC_IPCOMP */ + case IPPROTO_INT: + ipsp->ips_authalg = AH_NONE; + ipsp->ips_encalg = ESP_NONE; + break; + case 0: + break; + default: + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_sa_process: " + "unknown proto=%d.\n", + ipsp->ips_said.proto); + SENDERR(EINVAL); + } + +errlab: + return error; +} + +int +pfkey_lifetime_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) +{ + int error = 0; + struct sadb_lifetime *pfkey_lifetime = (struct sadb_lifetime *)pfkey_ext; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_lifetime_process: .\n"); + + if(!extr || !extr->ips) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_lifetime_process: " + "extr or extr->ips is NULL, fatal\n"); + SENDERR(EINVAL); + } + + switch(pfkey_lifetime->sadb_lifetime_exttype) { + case SADB_EXT_LIFETIME_CURRENT: + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_lifetime_process: " + "lifetime_current not supported yet.\n"); + SENDERR(EINVAL); + break; + case SADB_EXT_LIFETIME_HARD: + ipsec_lifetime_update_hard(&extr->ips->ips_life.ipl_allocations, + pfkey_lifetime->sadb_lifetime_allocations); + + ipsec_lifetime_update_hard(&extr->ips->ips_life.ipl_bytes, + pfkey_lifetime->sadb_lifetime_bytes); + + ipsec_lifetime_update_hard(&extr->ips->ips_life.ipl_addtime, + pfkey_lifetime->sadb_lifetime_addtime); + + ipsec_lifetime_update_hard(&extr->ips->ips_life.ipl_usetime, + pfkey_lifetime->sadb_lifetime_usetime); + + break; + + case SADB_EXT_LIFETIME_SOFT: + ipsec_lifetime_update_soft(&extr->ips->ips_life.ipl_allocations, + pfkey_lifetime->sadb_lifetime_allocations); + + ipsec_lifetime_update_soft(&extr->ips->ips_life.ipl_bytes, + pfkey_lifetime->sadb_lifetime_bytes); + + ipsec_lifetime_update_soft(&extr->ips->ips_life.ipl_addtime, + pfkey_lifetime->sadb_lifetime_addtime); + + ipsec_lifetime_update_soft(&extr->ips->ips_life.ipl_usetime, + pfkey_lifetime->sadb_lifetime_usetime); + + break; + default: + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_lifetime_process: " + "invalid exttype=%d.\n", + pfkey_ext->sadb_ext_type); + SENDERR(EINVAL); + } + +errlab: + return error; +} + +int +pfkey_address_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) +{ + int error = 0; + int saddr_len = 0; + char ipaddr_txt[ADDRTOA_BUF]; + unsigned char **sap; + unsigned short * portp = 0; + struct sadb_address *pfkey_address = (struct sadb_address *)pfkey_ext; + struct sockaddr* s = (struct sockaddr*)((char*)pfkey_address + sizeof(*pfkey_address)); + struct ipsec_sa* ipsp; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_address_process:\n"); + + if(!extr || !extr->ips) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_address_process: " + "extr or extr->ips is NULL, fatal\n"); + SENDERR(EINVAL); + } + + switch(s->sa_family) { + case AF_INET: + saddr_len = sizeof(struct sockaddr_in); + addrtoa(((struct sockaddr_in*)s)->sin_addr, 0, ipaddr_txt, sizeof(ipaddr_txt)); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_address_process: " + "found address family=%d, AF_INET, %s.\n", + s->sa_family, + ipaddr_txt); + break; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + case AF_INET6: + saddr_len = sizeof(struct sockaddr_in6); + break; +#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ + default: + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_address_process: " + "s->sa_family=%d not supported.\n", + s->sa_family); + SENDERR(EPFNOSUPPORT); + } + + switch(pfkey_address->sadb_address_exttype) { + case SADB_EXT_ADDRESS_SRC: + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_address_process: " + "found src address.\n"); + sap = (unsigned char **)&(extr->ips->ips_addr_s); + extr->ips->ips_addr_s_size = saddr_len; + break; + case SADB_EXT_ADDRESS_DST: + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_address_process: " + "found dst address.\n"); + sap = (unsigned char **)&(extr->ips->ips_addr_d); + extr->ips->ips_addr_d_size = saddr_len; + break; + case SADB_EXT_ADDRESS_PROXY: + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_address_process: " + "found proxy address.\n"); + sap = (unsigned char **)&(extr->ips->ips_addr_p); + extr->ips->ips_addr_p_size = saddr_len; + break; + case SADB_X_EXT_ADDRESS_DST2: + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_address_process: " + "found 2nd dst address.\n"); + if(extr->ips2 == NULL) { + extr->ips2 = ipsec_sa_alloc(&error); /* pass error var by pointer */ + } + if(extr->ips2 == NULL) { + SENDERR(-error); + } + sap = (unsigned char **)&(extr->ips2->ips_addr_d); + extr->ips2->ips_addr_d_size = saddr_len; + break; + case SADB_X_EXT_ADDRESS_SRC_FLOW: + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_address_process: " + "found src flow address.\n"); + if(pfkey_alloc_eroute(&(extr->eroute)) == ENOMEM) { + SENDERR(ENOMEM); + } + sap = (unsigned char **)&(extr->eroute->er_eaddr.sen_ip_src); + portp = &(extr->eroute->er_eaddr.sen_sport); + break; + case SADB_X_EXT_ADDRESS_DST_FLOW: + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_address_process: " + "found dst flow address.\n"); + if(pfkey_alloc_eroute(&(extr->eroute)) == ENOMEM) { + SENDERR(ENOMEM); + } + sap = (unsigned char **)&(extr->eroute->er_eaddr.sen_ip_dst); + portp = &(extr->eroute->er_eaddr.sen_dport); + break; + case SADB_X_EXT_ADDRESS_SRC_MASK: + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_address_process: " + "found src mask address.\n"); + if(pfkey_alloc_eroute(&(extr->eroute)) == ENOMEM) { + SENDERR(ENOMEM); + } + sap = (unsigned char **)&(extr->eroute->er_emask.sen_ip_src); + portp = &(extr->eroute->er_emask.sen_sport); + break; + case SADB_X_EXT_ADDRESS_DST_MASK: + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_address_process: " + "found dst mask address.\n"); + if(pfkey_alloc_eroute(&(extr->eroute)) == ENOMEM) { + SENDERR(ENOMEM); + } + sap = (unsigned char **)&(extr->eroute->er_emask.sen_ip_dst); + portp = &(extr->eroute->er_emask.sen_dport); + break; +#ifdef NAT_TRAVERSAL + case SADB_X_EXT_NAT_T_OA: + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_address_process: " + "found NAT-OA address.\n"); + sap = (unsigned char **)&(extr->ips->ips_natt_oa); + extr->ips->ips_natt_oa_size = saddr_len; + break; +#endif + default: + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_address_process: " + "unrecognised ext_type=%d.\n", + pfkey_address->sadb_address_exttype); + SENDERR(EINVAL); + } + + switch(pfkey_address->sadb_address_exttype) { + case SADB_EXT_ADDRESS_SRC: + case SADB_EXT_ADDRESS_DST: + case SADB_EXT_ADDRESS_PROXY: + case SADB_X_EXT_ADDRESS_DST2: +#ifdef NAT_TRAVERSAL + case SADB_X_EXT_NAT_T_OA: +#endif + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_address_process: " + "allocating %d bytes for saddr.\n", + saddr_len); + if(!(*sap = kmalloc(saddr_len, GFP_KERNEL))) { + SENDERR(ENOMEM); + } + memcpy(*sap, s, saddr_len); + break; + default: + if(s->sa_family != AF_INET) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_address_process: " + "s->sa_family=%d not supported.\n", + s->sa_family); + SENDERR(EPFNOSUPPORT); + } + (unsigned long)(*sap) = ((struct sockaddr_in*)s)->sin_addr.s_addr; + if (portp != 0) + *portp = ((struct sockaddr_in*)s)->sin_port; +#ifdef CONFIG_IPSEC_DEBUG + if(extr->eroute) { + char buf1[64], buf2[64]; + if (debug_pfkey) { + subnettoa(extr->eroute->er_eaddr.sen_ip_src, + extr->eroute->er_emask.sen_ip_src, 0, buf1, sizeof(buf1)); + subnettoa(extr->eroute->er_eaddr.sen_ip_dst, + extr->eroute->er_emask.sen_ip_dst, 0, buf2, sizeof(buf2)); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_address_parse: " + "extr->eroute set to %s:%d->%s:%d\n", + buf1, + ntohs(extr->eroute->er_eaddr.sen_sport), + buf2, + ntohs(extr->eroute->er_eaddr.sen_dport)); + } + } +#endif /* CONFIG_IPSEC_DEBUG */ + } + + ipsp = extr->ips; + switch(pfkey_address->sadb_address_exttype) { + case SADB_X_EXT_ADDRESS_DST2: + ipsp = extr->ips2; + case SADB_EXT_ADDRESS_DST: + if(s->sa_family == AF_INET) { + ipsp->ips_said.dst.s_addr = ((struct sockaddr_in*)(ipsp->ips_addr_d))->sin_addr.s_addr; + addrtoa(((struct sockaddr_in*)(ipsp->ips_addr_d))->sin_addr, + 0, + ipaddr_txt, + sizeof(ipaddr_txt)); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_address_process: " + "ips_said.dst set to %s.\n", + ipaddr_txt); + } else { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_address_process: " + "uh, ips_said.dst doesn't do address family=%d yet, said will be invalid.\n", + s->sa_family); + } + default: + break; + } + + /* XXX check if port!=0 */ + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_address_process: successful.\n"); + errlab: + return error; +} + +int +pfkey_key_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) +{ + int error = 0; + struct sadb_key *pfkey_key = (struct sadb_key *)pfkey_ext; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_key_process: .\n"); + + if(!extr || !extr->ips) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_key_process: " + "extr or extr->ips is NULL, fatal\n"); + SENDERR(EINVAL); + } + + switch(pfkey_key->sadb_key_exttype) { + case SADB_EXT_KEY_AUTH: + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_key_process: " + "allocating %d bytes for authkey.\n", + DIVUP(pfkey_key->sadb_key_bits, 8)); + if(!(extr->ips->ips_key_a = kmalloc(DIVUP(pfkey_key->sadb_key_bits, 8), GFP_KERNEL))) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_key_process: " + "memory allocation error.\n"); + SENDERR(ENOMEM); + } + extr->ips->ips_key_bits_a = pfkey_key->sadb_key_bits; + extr->ips->ips_key_a_size = DIVUP(pfkey_key->sadb_key_bits, 8); + memcpy(extr->ips->ips_key_a, + (char*)pfkey_key + sizeof(struct sadb_key), + extr->ips->ips_key_a_size); + break; + case SADB_EXT_KEY_ENCRYPT: /* Key(s) */ + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_key_process: " + "allocating %d bytes for enckey.\n", + DIVUP(pfkey_key->sadb_key_bits, 8)); + if(!(extr->ips->ips_key_e = kmalloc(DIVUP(pfkey_key->sadb_key_bits, 8), GFP_KERNEL))) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_key_process: " + "memory allocation error.\n"); + SENDERR(ENOMEM); + } + extr->ips->ips_key_bits_e = pfkey_key->sadb_key_bits; + extr->ips->ips_key_e_size = DIVUP(pfkey_key->sadb_key_bits, 8); + memcpy(extr->ips->ips_key_e, + (char*)pfkey_key + sizeof(struct sadb_key), + extr->ips->ips_key_e_size); + break; + default: + SENDERR(EINVAL); + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_key_process: " + "success.\n"); +errlab: + return error; +} + +int +pfkey_ident_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) +{ + int error = 0; + struct sadb_ident *pfkey_ident = (struct sadb_ident *)pfkey_ext; + int data_len; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_ident_process: .\n"); + + if(!extr || !extr->ips) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_ident_process: " + "extr or extr->ips is NULL, fatal\n"); + SENDERR(EINVAL); + } + + switch(pfkey_ident->sadb_ident_exttype) { + case SADB_EXT_IDENTITY_SRC: + data_len = pfkey_ident->sadb_ident_len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident); + + extr->ips->ips_ident_s.type = pfkey_ident->sadb_ident_type; + extr->ips->ips_ident_s.id = pfkey_ident->sadb_ident_id; + extr->ips->ips_ident_s.len = pfkey_ident->sadb_ident_len; + if(data_len) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_ident_process: " + "allocating %d bytes for ident_s.\n", + data_len); + if(!(extr->ips->ips_ident_s.data + = kmalloc(data_len, GFP_KERNEL))) { + SENDERR(ENOMEM); + } + memcpy(extr->ips->ips_ident_s.data, + (char*)pfkey_ident + sizeof(struct sadb_ident), + data_len); + } else { + extr->ips->ips_ident_s.data = NULL; + } + break; + case SADB_EXT_IDENTITY_DST: /* Identity(ies) */ + data_len = pfkey_ident->sadb_ident_len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident); + + extr->ips->ips_ident_d.type = pfkey_ident->sadb_ident_type; + extr->ips->ips_ident_d.id = pfkey_ident->sadb_ident_id; + extr->ips->ips_ident_d.len = pfkey_ident->sadb_ident_len; + if(data_len) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_ident_process: " + "allocating %d bytes for ident_d.\n", + data_len); + if(!(extr->ips->ips_ident_d.data + = kmalloc(data_len, GFP_KERNEL))) { + SENDERR(ENOMEM); + } + memcpy(extr->ips->ips_ident_d.data, + (char*)pfkey_ident + sizeof(struct sadb_ident), + data_len); + } else { + extr->ips->ips_ident_d.data = NULL; + } + break; + default: + SENDERR(EINVAL); + } +errlab: + return error; +} + +int +pfkey_sens_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) +{ + int error = 0; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_sens_process: " + "Sorry, I can't process exttype=%d yet.\n", + pfkey_ext->sadb_ext_type); + SENDERR(EINVAL); /* don't process these yet */ + errlab: + return error; +} + +int +pfkey_prop_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) +{ + int error = 0; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_prop_process: " + "Sorry, I can't process exttype=%d yet.\n", + pfkey_ext->sadb_ext_type); + SENDERR(EINVAL); /* don't process these yet */ + + errlab: + return error; +} + +int +pfkey_supported_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) +{ + int error = 0; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_supported_process: " + "Sorry, I can't process exttype=%d yet.\n", + pfkey_ext->sadb_ext_type); + SENDERR(EINVAL); /* don't process these yet */ + +errlab: + return error; +} + +int +pfkey_spirange_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) +{ + int error = 0; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_spirange_process: .\n"); +/* errlab: */ + return error; +} + +int +pfkey_x_kmprivate_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) +{ + int error = 0; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_kmprivate_process: " + "Sorry, I can't process exttype=%d yet.\n", + pfkey_ext->sadb_ext_type); + SENDERR(EINVAL); /* don't process these yet */ + +errlab: + return error; +} + +int +pfkey_x_satype_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) +{ + int error = 0; + struct sadb_x_satype *pfkey_x_satype = (struct sadb_x_satype *)pfkey_ext; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_satype_process: .\n"); + + if(!extr || !extr->ips) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_satype_process: " + "extr or extr->ips is NULL, fatal\n"); + SENDERR(EINVAL); + } + + if(extr->ips2 == NULL) { + extr->ips2 = ipsec_sa_alloc(&error); /* pass error var by pointer */ + } + if(extr->ips2 == NULL) { + SENDERR(-error); + } + if(!(extr->ips2->ips_said.proto = satype2proto(pfkey_x_satype->sadb_x_satype_satype))) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_satype_process: " + "proto lookup from satype=%d failed.\n", + pfkey_x_satype->sadb_x_satype_satype); + SENDERR(EINVAL); + } + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_satype_process: " + "protocol==%d decoded from satype==%d(%s).\n", + extr->ips2->ips_said.proto, + pfkey_x_satype->sadb_x_satype_satype, + satype2name(pfkey_x_satype->sadb_x_satype_satype)); + +errlab: + return error; +} + + +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL +int +pfkey_x_nat_t_type_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) +{ + int error = 0; + struct sadb_x_nat_t_type *pfkey_x_nat_t_type = (struct sadb_x_nat_t_type *)pfkey_ext; + + if(!pfkey_x_nat_t_type) { + printk("klips_debug:pfkey_x_nat_t_type_process: " + "null pointer passed in\n"); + SENDERR(EINVAL); + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_nat_t_type_process: %d.\n", + pfkey_x_nat_t_type->sadb_x_nat_t_type_type); + + if(!extr || !extr->ips) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_nat_t_type_process: " + "extr or extr->ips is NULL, fatal\n"); + SENDERR(EINVAL); + } + + switch(pfkey_x_nat_t_type->sadb_x_nat_t_type_type) { + case ESPINUDP_WITH_NON_IKE: /* with Non-IKE */ + case ESPINUDP_WITH_NON_ESP: /* with Non-ESP */ + extr->ips->ips_natt_type = pfkey_x_nat_t_type->sadb_x_nat_t_type_type; + break; + default: + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_nat_t_type_process: " + "unknown type %d.\n", + pfkey_x_nat_t_type->sadb_x_nat_t_type_type); + SENDERR(EINVAL); + break; + } + +errlab: + return error; +} + +int +pfkey_x_nat_t_port_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) +{ + int error = 0; + struct sadb_x_nat_t_port *pfkey_x_nat_t_port = (struct sadb_x_nat_t_port *)pfkey_ext; + + if(!pfkey_x_nat_t_port) { + printk("klips_debug:pfkey_x_nat_t_port_process: " + "null pointer passed in\n"); + SENDERR(EINVAL); + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_nat_t_port_process: %d/%d.\n", + pfkey_x_nat_t_port->sadb_x_nat_t_port_exttype, + pfkey_x_nat_t_port->sadb_x_nat_t_port_port); + + if(!extr || !extr->ips) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_nat_t_type_process: " + "extr or extr->ips is NULL, fatal\n"); + SENDERR(EINVAL); + } + + switch(pfkey_x_nat_t_port->sadb_x_nat_t_port_exttype) { + case SADB_X_EXT_NAT_T_SPORT: + extr->ips->ips_natt_sport = pfkey_x_nat_t_port->sadb_x_nat_t_port_port; + break; + case SADB_X_EXT_NAT_T_DPORT: + extr->ips->ips_natt_dport = pfkey_x_nat_t_port->sadb_x_nat_t_port_port; + break; + default: + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_nat_t_port_process: " + "unknown exttype %d.\n", + pfkey_x_nat_t_port->sadb_x_nat_t_port_exttype); + SENDERR(EINVAL); + break; + } + +errlab: + return error; +} +#endif + +int +pfkey_x_debug_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) +{ + int error = 0; + struct sadb_x_debug *pfkey_x_debug = (struct sadb_x_debug *)pfkey_ext; + + if(!pfkey_x_debug) { + printk("klips_debug:pfkey_x_debug_process: " + "null pointer passed in\n"); + SENDERR(EINVAL); + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_debug_process: .\n"); + +#ifdef CONFIG_IPSEC_DEBUG + if(pfkey_x_debug->sadb_x_debug_netlink >> + (sizeof(pfkey_x_debug->sadb_x_debug_netlink) * 8 - 1)) { + pfkey_x_debug->sadb_x_debug_netlink &= + ~(1 << (sizeof(pfkey_x_debug->sadb_x_debug_netlink) * 8 -1)); + debug_tunnel |= pfkey_x_debug->sadb_x_debug_tunnel; + debug_netlink |= pfkey_x_debug->sadb_x_debug_netlink; + debug_xform |= pfkey_x_debug->sadb_x_debug_xform; + debug_eroute |= pfkey_x_debug->sadb_x_debug_eroute; + debug_spi |= pfkey_x_debug->sadb_x_debug_spi; + debug_radij |= pfkey_x_debug->sadb_x_debug_radij; + debug_esp |= pfkey_x_debug->sadb_x_debug_esp; + debug_ah |= pfkey_x_debug->sadb_x_debug_ah; + debug_rcv |= pfkey_x_debug->sadb_x_debug_rcv; + debug_pfkey |= pfkey_x_debug->sadb_x_debug_pfkey; +#ifdef CONFIG_IPSEC_IPCOMP + sysctl_ipsec_debug_ipcomp |= pfkey_x_debug->sadb_x_debug_ipcomp; +#endif /* CONFIG_IPSEC_IPCOMP */ + sysctl_ipsec_debug_verbose |= pfkey_x_debug->sadb_x_debug_verbose; + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_debug_process: " + "set\n"); + } else { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_debug_process: " + "unset\n"); + debug_tunnel &= pfkey_x_debug->sadb_x_debug_tunnel; + debug_netlink &= pfkey_x_debug->sadb_x_debug_netlink; + debug_xform &= pfkey_x_debug->sadb_x_debug_xform; + debug_eroute &= pfkey_x_debug->sadb_x_debug_eroute; + debug_spi &= pfkey_x_debug->sadb_x_debug_spi; + debug_radij &= pfkey_x_debug->sadb_x_debug_radij; + debug_esp &= pfkey_x_debug->sadb_x_debug_esp; + debug_ah &= pfkey_x_debug->sadb_x_debug_ah; + debug_rcv &= pfkey_x_debug->sadb_x_debug_rcv; + debug_pfkey &= pfkey_x_debug->sadb_x_debug_pfkey; +#ifdef CONFIG_IPSEC_IPCOMP + sysctl_ipsec_debug_ipcomp &= pfkey_x_debug->sadb_x_debug_ipcomp; +#endif /* CONFIG_IPSEC_IPCOMP */ + sysctl_ipsec_debug_verbose &= pfkey_x_debug->sadb_x_debug_verbose; + } +#else /* CONFIG_IPSEC_DEBUG */ + printk("klips_debug:pfkey_x_debug_process: " + "debugging not enabled\n"); + SENDERR(EINVAL); +#endif /* CONFIG_IPSEC_DEBUG */ + +errlab: + return error; +} diff --git a/linux/net/ipsec/pfkey_v2_parser.c b/linux/net/ipsec/pfkey_v2_parser.c new file mode 100644 index 000000000..d170ddea5 --- /dev/null +++ b/linux/net/ipsec/pfkey_v2_parser.c @@ -0,0 +1,3420 @@ +/* + * @(#) RFC2367 PF_KEYv2 Key management API message parser + * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs <rgb@freeswan.org> + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + * + * RCSID $Id: pfkey_v2_parser.c,v 1.4 2004/09/29 22:27:41 as Exp $ + */ + +/* + * Template from klips/net/ipsec/ipsec/ipsec_netlink.c. + */ + +char pfkey_v2_parser_c_version[] = "$Id: pfkey_v2_parser.c,v 1.4 2004/09/29 22:27:41 as Exp $"; + +#include <linux/config.h> +#include <linux/version.h> +#include <linux/kernel.h> /* printk() */ + +#include "freeswan/ipsec_param.h" + +#ifdef MALLOC_SLAB +# include <linux/slab.h> /* kmalloc() */ +#else /* MALLOC_SLAB */ +# include <linux/malloc.h> /* kmalloc() */ +#endif /* MALLOC_SLAB */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/interrupt.h> /* mark_bh */ + +#include <linux/netdevice.h> /* struct device, and other headers */ +#include <linux/etherdevice.h> /* eth_type_trans */ +#include <linux/ip.h> /* struct iphdr */ +#include <linux/skbuff.h> + +#include <freeswan.h> + +#include <crypto/des.h> + +#ifdef SPINLOCK +# ifdef SPINLOCK_23 +# include <linux/spinlock.h> /* *lock* */ +# else /* SPINLOCK_23 */ +# include <asm/spinlock.h> /* *lock* */ +# endif /* SPINLOCK_23 */ +#endif /* SPINLOCK */ +#ifdef NET_21 +# include <asm/uaccess.h> +# include <linux/in6.h> +# define ip_chk_addr inet_addr_type +# define IS_MYADDR RTN_LOCAL +#endif +#include <asm/checksum.h> +#include <net/ip.h> +#ifdef NETLINK_SOCK +# include <linux/netlink.h> +#else +# include <net/netlink.h> +#endif + +#include <linux/random.h> /* get_random_bytes() */ + +#include "freeswan/radij.h" +#include "freeswan/ipsec_encap.h" +#include "freeswan/ipsec_sa.h" + +#include "freeswan/ipsec_radij.h" +#include "freeswan/ipsec_xform.h" +#include "freeswan/ipsec_ah.h" +#include "freeswan/ipsec_esp.h" +#include "freeswan/ipsec_tunnel.h" +#include "freeswan/ipsec_rcv.h" +#include "freeswan/ipcomp.h" + +#include <pfkeyv2.h> +#include <pfkey.h> + +#include "freeswan/ipsec_proto.h" +#include "freeswan/ipsec_alg.h" + + +#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0) + +struct sklist_t { + struct socket *sk; + struct sklist_t* next; +} pfkey_sklist_head, *pfkey_sklist, *pfkey_sklist_prev; + +__u32 pfkey_msg_seq = 0; + +int +pfkey_alloc_eroute(struct eroute** eroute) +{ + int error = 0; + if(*eroute) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_alloc_eroute: " + "eroute struct already allocated\n"); + SENDERR(EEXIST); + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_alloc_eroute: " + "allocating %lu bytes for an eroute.\n", + (unsigned long) sizeof(**eroute)); + if((*eroute = kmalloc(sizeof(**eroute), GFP_ATOMIC) ) == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_alloc_eroute: " + "memory allocation error\n"); + SENDERR(ENOMEM); + } + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_alloc_eroute: " + "allocated eroute struct=0p%p.\n", eroute); + memset((caddr_t)*eroute, 0, sizeof(**eroute)); + (*eroute)->er_eaddr.sen_len = + (*eroute)->er_emask.sen_len = sizeof(struct sockaddr_encap); + (*eroute)->er_eaddr.sen_family = + (*eroute)->er_emask.sen_family = AF_ENCAP; + (*eroute)->er_eaddr.sen_type = SENT_IP4; + (*eroute)->er_emask.sen_type = 255; + (*eroute)->er_pid = 0; + (*eroute)->er_count = 0; + (*eroute)->er_lasttime = jiffies/HZ; + + errlab: + return(error); +} + +DEBUG_NO_STATIC int +pfkey_x_protocol_process(struct sadb_ext *pfkey_ext, + struct pfkey_extracted_data *extr) +{ + int error = 0; + struct sadb_protocol * p = (struct sadb_protocol *)pfkey_ext; + + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_protocol_process: %p\n", extr); + + if (extr == 0) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_protocol_process:" + "extr is NULL, fatal\n"); + SENDERR(EINVAL); + } + if (extr->eroute == 0) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_protocol_process:" + "extr->eroute is NULL, fatal\n"); + SENDERR(EINVAL); + } + extr->eroute->er_eaddr.sen_proto = p->sadb_protocol_proto; + extr->eroute->er_emask.sen_proto = p->sadb_protocol_proto ? ~0:0; + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_protocol_process: protocol = %d.\n", + p->sadb_protocol_proto); + errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_ipsec_sa_init(struct ipsec_sa *ipsp, struct sadb_ext **extensions) +{ + int error = 0; + char sa[SATOA_BUF]; + size_t sa_len; + char ipaddr_txt[ADDRTOA_BUF]; + char ipaddr2_txt[ADDRTOA_BUF]; +#if defined (CONFIG_IPSEC_AUTH_HMAC_MD5) || defined (CONFIG_IPSEC_AUTH_HMAC_SHA1) + int i; + unsigned char kb[AHMD596_BLKLEN]; +#endif +#ifdef CONFIG_IPSEC_ALG + struct ipsec_alg_enc *ixt_e = NULL; + struct ipsec_alg_auth *ixt_a = NULL; +#endif /* CONFIG_IPSEC_ALG */ + + if(ipsp == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_ipsec_sa_init: " + "ipsp is NULL, fatal\n"); + SENDERR(EINVAL); + } + + sa_len = satoa(ipsp->ips_said, 0, sa, SATOA_BUF); + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_ipsec_sa_init: " + "(pfkey defined) called for SA:%s\n", + sa_len ? sa : " (error)"); + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_ipsec_sa_init: " + "calling init routine of %s%s%s\n", + IPS_XFORM_NAME(ipsp)); + + switch(ipsp->ips_said.proto) { + +#ifdef CONFIG_IPSEC_IPIP + case IPPROTO_IPIP: { + addrtoa(((struct sockaddr_in*)(ipsp->ips_addr_s))->sin_addr, + 0, + ipaddr_txt, sizeof(ipaddr_txt)); + addrtoa(((struct sockaddr_in*)(ipsp->ips_addr_d))->sin_addr, + 0, + ipaddr2_txt, sizeof(ipaddr_txt)); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_ipsec_sa_init: " + "(pfkey defined) IPIP ipsec_sa set for %s->%s.\n", + ipaddr_txt, + ipaddr2_txt); + } + break; +#endif /* !CONFIG_IPSEC_IPIP */ +#ifdef CONFIG_IPSEC_AH + case IPPROTO_AH: + switch(ipsp->ips_authalg) { +# ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 + case AH_MD5: { + unsigned char *akp; + unsigned int aks; + MD5_CTX *ictx; + MD5_CTX *octx; + + if(ipsp->ips_key_bits_a != (AHMD596_KLEN * 8)) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_ipsec_sa_init: " + "incorrect key size: %d bits -- must be %d bits\n"/*octets (bytes)\n"*/, + ipsp->ips_key_bits_a, AHMD596_KLEN * 8); + SENDERR(EINVAL); + } + +# if KLIPS_DIVULGE_HMAC_KEY + KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, + "klips_debug:pfkey_ipsec_sa_init: " + "hmac md5-96 key is 0x%08x %08x %08x %08x\n", + ntohl(*(((__u32 *)ipsp->ips_key_a)+0)), + ntohl(*(((__u32 *)ipsp->ips_key_a)+1)), + ntohl(*(((__u32 *)ipsp->ips_key_a)+2)), + ntohl(*(((__u32 *)ipsp->ips_key_a)+3))); +# endif /* KLIPS_DIVULGE_HMAC_KEY */ + + ipsp->ips_auth_bits = AHMD596_ALEN * 8; + + /* save the pointer to the key material */ + akp = ipsp->ips_key_a; + aks = ipsp->ips_key_a_size; + + KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, + "klips_debug:pfkey_ipsec_sa_init: " + "allocating %lu bytes for md5_ctx.\n", + (unsigned long) sizeof(struct md5_ctx)); + if((ipsp->ips_key_a = (caddr_t) + kmalloc(sizeof(struct md5_ctx), GFP_ATOMIC)) == NULL) { + ipsp->ips_key_a = akp; + SENDERR(ENOMEM); + } + ipsp->ips_key_a_size = sizeof(struct md5_ctx); + + for (i = 0; i < DIVUP(ipsp->ips_key_bits_a, 8); i++) { + kb[i] = akp[i] ^ HMAC_IPAD; + } + for (; i < AHMD596_BLKLEN; i++) { + kb[i] = HMAC_IPAD; + } + + ictx = &(((struct md5_ctx*)(ipsp->ips_key_a))->ictx); + MD5Init(ictx); + MD5Update(ictx, kb, AHMD596_BLKLEN); + + for (i = 0; i < AHMD596_BLKLEN; i++) { + kb[i] ^= (HMAC_IPAD ^ HMAC_OPAD); + } + + octx = &(((struct md5_ctx*)(ipsp->ips_key_a))->octx); + MD5Init(octx); + MD5Update(octx, kb, AHMD596_BLKLEN); + +# if KLIPS_DIVULGE_HMAC_KEY + KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, + "klips_debug:pfkey_ipsec_sa_init: " + "MD5 ictx=0x%08x %08x %08x %08x octx=0x%08x %08x %08x %08x\n", + ((__u32*)ictx)[0], + ((__u32*)ictx)[1], + ((__u32*)ictx)[2], + ((__u32*)ictx)[3], + ((__u32*)octx)[0], + ((__u32*)octx)[1], + ((__u32*)octx)[2], + ((__u32*)octx)[3] ); +# endif /* KLIPS_DIVULGE_HMAC_KEY */ + + /* zero key buffer -- paranoid */ + memset(akp, 0, aks); + kfree(akp); + } + break; +# endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ +# ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 + case AH_SHA: { + unsigned char *akp; + unsigned int aks; + SHA1_CTX *ictx; + SHA1_CTX *octx; + + if(ipsp->ips_key_bits_a != (AHSHA196_KLEN * 8)) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_ipsec_sa_init: " + "incorrect key size: %d bits -- must be %d bits\n"/*octets (bytes)\n"*/, + ipsp->ips_key_bits_a, AHSHA196_KLEN * 8); + SENDERR(EINVAL); + } + +# if KLIPS_DIVULGE_HMAC_KEY + KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, + "klips_debug:pfkey_ipsec_sa_init: " + "hmac sha1-96 key is 0x%08x %08x %08x %08x\n", + ntohl(*(((__u32 *)ipsp->ips_key_a)+0)), + ntohl(*(((__u32 *)ipsp->ips_key_a)+1)), + ntohl(*(((__u32 *)ipsp->ips_key_a)+2)), + ntohl(*(((__u32 *)ipsp->ips_key_a)+3))); +# endif /* KLIPS_DIVULGE_HMAC_KEY */ + + ipsp->ips_auth_bits = AHSHA196_ALEN * 8; + + /* save the pointer to the key material */ + akp = ipsp->ips_key_a; + aks = ipsp->ips_key_a_size; + + KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, + "klips_debug:pfkey_ipsec_sa_init: " + "allocating %lu bytes for sha1_ctx.\n", + (unsigned long) sizeof(struct sha1_ctx)); + if((ipsp->ips_key_a = (caddr_t) + kmalloc(sizeof(struct sha1_ctx), GFP_ATOMIC)) == NULL) { + ipsp->ips_key_a = akp; + SENDERR(ENOMEM); + } + ipsp->ips_key_a_size = sizeof(struct sha1_ctx); + + for (i = 0; i < DIVUP(ipsp->ips_key_bits_a, 8); i++) { + kb[i] = akp[i] ^ HMAC_IPAD; + } + for (; i < AHMD596_BLKLEN; i++) { + kb[i] = HMAC_IPAD; + } + + ictx = &(((struct sha1_ctx*)(ipsp->ips_key_a))->ictx); + SHA1Init(ictx); + SHA1Update(ictx, kb, AHSHA196_BLKLEN); + + for (i = 0; i < AHSHA196_BLKLEN; i++) { + kb[i] ^= (HMAC_IPAD ^ HMAC_OPAD); + } + + octx = &(((struct sha1_ctx*)(ipsp->ips_key_a))->octx); + SHA1Init(octx); + SHA1Update(octx, kb, AHSHA196_BLKLEN); + +# if KLIPS_DIVULGE_HMAC_KEY + KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, + "klips_debug:pfkey_ipsec_sa_init: " + "SHA1 ictx=0x%08x %08x %08x %08x octx=0x%08x %08x %08x %08x\n", + ((__u32*)ictx)[0], + ((__u32*)ictx)[1], + ((__u32*)ictx)[2], + ((__u32*)ictx)[3], + ((__u32*)octx)[0], + ((__u32*)octx)[1], + ((__u32*)octx)[2], + ((__u32*)octx)[3] ); +# endif /* KLIPS_DIVULGE_HMAC_KEY */ + /* zero key buffer -- paranoid */ + memset(akp, 0, aks); + kfree(akp); + } + break; +# endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ + default: + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_ipsec_sa_init: " + "authalg=%d support not available in the kernel", + ipsp->ips_authalg); + SENDERR(EINVAL); + } + break; +#endif /* CONFIG_IPSEC_AH */ +#ifdef CONFIG_IPSEC_ESP + case IPPROTO_ESP: { +#if defined (CONFIG_IPSEC_AUTH_HMAC_MD5) || defined (CONFIG_IPSEC_AUTH_HMAC_SHA1) + unsigned char *akp; + unsigned int aks; +#endif +#if defined (CONFIG_IPSEC_ENC_3DES) + unsigned char *ekp; + unsigned int eks; +#endif + + ipsp->ips_iv_size = 0; +#ifdef CONFIG_IPSEC_ALG + if ((ixt_e=ipsp->ips_alg_enc)) { + ipsp->ips_iv_size = ixt_e->ixt_ivlen/8; + } else +#endif /* CONFIG_IPSEC_ALG */ + switch(ipsp->ips_encalg) { +# ifdef CONFIG_IPSEC_ENC_3DES + case ESP_3DES: +# endif /* CONFIG_IPSEC_ENC_3DES */ +# if defined(CONFIG_IPSEC_ENC_3DES) + KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, + "klips_debug:pfkey_ipsec_sa_init: " + "allocating %u bytes for iv.\n", + EMT_ESPDES_IV_SZ); + if((ipsp->ips_iv = (caddr_t) + kmalloc((ipsp->ips_iv_size = EMT_ESPDES_IV_SZ), GFP_ATOMIC)) == NULL) { + SENDERR(ENOMEM); + } + prng_bytes(&ipsec_prng, (char *)ipsp->ips_iv, EMT_ESPDES_IV_SZ); + ipsp->ips_iv_bits = ipsp->ips_iv_size * 8; + ipsp->ips_iv_size = EMT_ESPDES_IV_SZ; + break; +# endif /* defined(CONFIG_IPSEC_ENC_3DES) */ + case ESP_NONE: + break; + default: + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_ipsec_sa_init: " + "encalg=%d support not available in the kernel", + ipsp->ips_encalg); + SENDERR(EINVAL); + } + + /* Create IV */ + if (ipsp->ips_iv_size) { + if((ipsp->ips_iv = (caddr_t) + kmalloc(ipsp->ips_iv_size, GFP_ATOMIC)) == NULL) { + SENDERR(ENOMEM); + } + prng_bytes(&ipsec_prng, (char *)ipsp->ips_iv, ipsp->ips_iv_size); + ipsp->ips_iv_bits = ipsp->ips_iv_size * 8; + } + +#ifdef CONFIG_IPSEC_ALG + if (ixt_e) { + if ((error=ipsec_alg_enc_key_create(ipsp)) < 0) + SENDERR(-error); + } else +#endif /* CONFIG_IPSEC_ALG */ + switch(ipsp->ips_encalg) { +# ifdef CONFIG_IPSEC_ENC_3DES + case ESP_3DES: + if(ipsp->ips_key_bits_e != (EMT_ESP3DES_KEY_SZ * 8)) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_ipsec_sa_init: " + "incorrect encryption key size: %d bits -- must be %d bits\n"/*octets (bytes)\n"*/, + ipsp->ips_key_bits_e, EMT_ESP3DES_KEY_SZ * 8); + SENDERR(EINVAL); + } + + /* save encryption key pointer */ + ekp = ipsp->ips_key_e; + eks = ipsp->ips_key_e_size; + + KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, + "klips_debug:pfkey_ipsec_sa_init: " + "allocating %lu bytes for 3des.\n", + (unsigned long) (3 * sizeof(struct des_eks))); + if((ipsp->ips_key_e = (caddr_t) + kmalloc(3 * sizeof(struct des_eks), GFP_ATOMIC)) == NULL) { + ipsp->ips_key_e = ekp; + SENDERR(ENOMEM); + } + ipsp->ips_key_e_size = 3 * sizeof(struct des_eks); + + for(i = 0; i < 3; i++) { +#if KLIPS_DIVULGE_CYPHER_KEY + KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, + "klips_debug:pfkey_ipsec_sa_init: " + "3des key %d/3 is 0x%08x%08x\n", + i + 1, + ntohl(*((__u32 *)ekp + i * 2)), + ntohl(*((__u32 *)ekp + i * 2 + 1))); +# endif +#if KLIPS_FIXES_DES_PARITY + /* force parity */ + des_set_odd_parity((des_cblock *)(ekp + EMT_ESPDES_KEY_SZ * i)); +#endif + error = des_set_key((des_cblock *)(ekp + EMT_ESPDES_KEY_SZ * i), + ((struct des_eks *)(ipsp->ips_key_e))[i].ks); + if (error == -1) + printk("klips_debug:pfkey_ipsec_sa_init: " + "parity error in des key %d/3\n", + i + 1); + else if (error == -2) + printk("klips_debug:pfkey_ipsec_sa_init: " + "illegal weak des key %d/3\n", i + 1); + if (error) { + memset(ekp, 0, eks); + kfree(ekp); + SENDERR(EINVAL); + } + } + + /* paranoid */ + memset(ekp, 0, eks); + kfree(ekp); + break; +# endif /* CONFIG_IPSEC_ENC_3DES */ + case ESP_NONE: + break; + default: + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_ipsec_sa_init: " + "encalg=%d support not available in the kernel", + ipsp->ips_encalg); + SENDERR(EINVAL); + } + +#ifdef CONFIG_IPSEC_ALG + if ((ixt_a=ipsp->ips_alg_auth)) { + if ((error=ipsec_alg_auth_key_create(ipsp)) < 0) + SENDERR(-error); + } else +#endif /* CONFIG_IPSEC_ALG */ + + switch(ipsp->ips_authalg) { +# ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 + case AH_MD5: { + MD5_CTX *ictx; + MD5_CTX *octx; + + if(ipsp->ips_key_bits_a != (AHMD596_KLEN * 8)) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_ipsec_sa_init: " + "incorrect authorisation key size: %d bits -- must be %d bits\n"/*octets (bytes)\n"*/, + ipsp->ips_key_bits_a, + AHMD596_KLEN * 8); + SENDERR(EINVAL); + } + +# if KLIPS_DIVULGE_HMAC_KEY + KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, + "klips_debug:pfkey_ipsec_sa_init: " + "hmac md5-96 key is 0x%08x %08x %08x %08x\n", + ntohl(*(((__u32 *)(ipsp->ips_key_a))+0)), + ntohl(*(((__u32 *)(ipsp->ips_key_a))+1)), + ntohl(*(((__u32 *)(ipsp->ips_key_a))+2)), + ntohl(*(((__u32 *)(ipsp->ips_key_a))+3))); +# endif /* KLIPS_DIVULGE_HMAC_KEY */ + ipsp->ips_auth_bits = AHMD596_ALEN * 8; + + /* save the pointer to the key material */ + akp = ipsp->ips_key_a; + aks = ipsp->ips_key_a_size; + + KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, + "klips_debug:pfkey_ipsec_sa_init: " + "allocating %lu bytes for md5_ctx.\n", + (unsigned long) sizeof(struct md5_ctx)); + if((ipsp->ips_key_a = (caddr_t) + kmalloc(sizeof(struct md5_ctx), GFP_ATOMIC)) == NULL) { + ipsp->ips_key_a = akp; + SENDERR(ENOMEM); + } + ipsp->ips_key_a_size = sizeof(struct md5_ctx); + + for (i = 0; i < DIVUP(ipsp->ips_key_bits_a, 8); i++) { + kb[i] = akp[i] ^ HMAC_IPAD; + } + for (; i < AHMD596_BLKLEN; i++) { + kb[i] = HMAC_IPAD; + } + + ictx = &(((struct md5_ctx*)(ipsp->ips_key_a))->ictx); + MD5Init(ictx); + MD5Update(ictx, kb, AHMD596_BLKLEN); + + for (i = 0; i < AHMD596_BLKLEN; i++) { + kb[i] ^= (HMAC_IPAD ^ HMAC_OPAD); + } + + octx = &(((struct md5_ctx*)(ipsp->ips_key_a))->octx); + MD5Init(octx); + MD5Update(octx, kb, AHMD596_BLKLEN); + +# if KLIPS_DIVULGE_HMAC_KEY + KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, + "klips_debug:pfkey_ipsec_sa_init: " + "MD5 ictx=0x%08x %08x %08x %08x octx=0x%08x %08x %08x %08x\n", + ((__u32*)ictx)[0], + ((__u32*)ictx)[1], + ((__u32*)ictx)[2], + ((__u32*)ictx)[3], + ((__u32*)octx)[0], + ((__u32*)octx)[1], + ((__u32*)octx)[2], + ((__u32*)octx)[3] ); +# endif /* KLIPS_DIVULGE_HMAC_KEY */ + /* paranoid */ + memset(akp, 0, aks); + kfree(akp); + break; + } +# endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ +# ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 + case AH_SHA: { + SHA1_CTX *ictx; + SHA1_CTX *octx; + + if(ipsp->ips_key_bits_a != (AHSHA196_KLEN * 8)) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_ipsec_sa_init: " + "incorrect authorisation key size: %d bits -- must be %d bits\n"/*octets (bytes)\n"*/, + ipsp->ips_key_bits_a, + AHSHA196_KLEN * 8); + SENDERR(EINVAL); + } + +# if KLIPS_DIVULGE_HMAC_KEY + KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, + "klips_debug:pfkey_ipsec_sa_init: " + "hmac sha1-96 key is 0x%08x %08x %08x %08x\n", + ntohl(*(((__u32 *)ipsp->ips_key_a)+0)), + ntohl(*(((__u32 *)ipsp->ips_key_a)+1)), + ntohl(*(((__u32 *)ipsp->ips_key_a)+2)), + ntohl(*(((__u32 *)ipsp->ips_key_a)+3))); +# endif /* KLIPS_DIVULGE_HMAC_KEY */ + ipsp->ips_auth_bits = AHSHA196_ALEN * 8; + + /* save the pointer to the key material */ + akp = ipsp->ips_key_a; + aks = ipsp->ips_key_a_size; + + KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, + "klips_debug:pfkey_ipsec_sa_init: " + "allocating %lu bytes for sha1_ctx.\n", + (unsigned long) sizeof(struct sha1_ctx)); + if((ipsp->ips_key_a = (caddr_t) + kmalloc(sizeof(struct sha1_ctx), GFP_ATOMIC)) == NULL) { + ipsp->ips_key_a = akp; + SENDERR(ENOMEM); + } + ipsp->ips_key_a_size = sizeof(struct sha1_ctx); + + for (i = 0; i < DIVUP(ipsp->ips_key_bits_a, 8); i++) { + kb[i] = akp[i] ^ HMAC_IPAD; + } + for (; i < AHMD596_BLKLEN; i++) { + kb[i] = HMAC_IPAD; + } + + ictx = &(((struct sha1_ctx*)(ipsp->ips_key_a))->ictx); + SHA1Init(ictx); + SHA1Update(ictx, kb, AHSHA196_BLKLEN); + + for (i = 0; i < AHSHA196_BLKLEN; i++) { + kb[i] ^= (HMAC_IPAD ^ HMAC_OPAD); + } + + octx = &((struct sha1_ctx*)(ipsp->ips_key_a))->octx; + SHA1Init(octx); + SHA1Update(octx, kb, AHSHA196_BLKLEN); + +# if KLIPS_DIVULGE_HMAC_KEY + KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, + "klips_debug:pfkey_ipsec_sa_init: " + "SHA1 ictx=0x%08x %08x %08x %08x octx=0x%08x %08x %08x %08x\n", + ((__u32*)ictx)[0], + ((__u32*)ictx)[1], + ((__u32*)ictx)[2], + ((__u32*)ictx)[3], + ((__u32*)octx)[0], + ((__u32*)octx)[1], + ((__u32*)octx)[2], + ((__u32*)octx)[3] ); +# endif /* KLIPS_DIVULGE_HMAC_KEY */ + memset(akp, 0, aks); + kfree(akp); + break; + } +# endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ + case AH_NONE: + break; + default: + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_ipsec_sa_init: " + "authalg=%d support not available in the kernel.\n", + ipsp->ips_authalg); + SENDERR(EINVAL); + } + } + break; +#endif /* !CONFIG_IPSEC_ESP */ +#ifdef CONFIG_IPSEC_IPCOMP + case IPPROTO_COMP: + ipsp->ips_comp_adapt_tries = 0; + ipsp->ips_comp_adapt_skip = 0; + ipsp->ips_comp_ratio_cbytes = 0; + ipsp->ips_comp_ratio_dbytes = 0; + break; +#endif /* CONFIG_IPSEC_IPCOMP */ + default: + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_ipsec_sa_init: " + "proto=%d unknown.\n", + ipsp->ips_said.proto); + SENDERR(EINVAL); + } + + errlab: + return(error); +} + + +int +pfkey_safe_build(int error, struct sadb_ext *extensions[SADB_MAX+1]) +{ + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_safe_build: " + "error=%d\n", + error); + if (!error) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_safe_build:" + "success.\n"); + return 1; + } else { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_safe_build:" + "caught error %d\n", + error); + pfkey_extensions_free(extensions); + return 0; + } +} + + +DEBUG_NO_STATIC int +pfkey_getspi_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) +{ + int error = 0; + ipsec_spi_t minspi = htonl(256), maxspi = htonl(-1L); + int found_avail = 0; + struct ipsec_sa *ipsq; + char sa[SATOA_BUF]; + size_t sa_len; + struct sadb_ext *extensions_reply[SADB_EXT_MAX+1]; + struct sadb_msg *pfkey_reply = NULL; + struct socket_list *pfkey_socketsp; + uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_getspi_parse: .\n"); + + pfkey_extensions_init(extensions_reply); + + if(extr == NULL || extr->ips == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_getspi_parse: " + "error, extr or extr->ipsec_sa pointer NULL\n"); + SENDERR(EINVAL); + } + + if(extensions[SADB_EXT_SPIRANGE]) { + minspi = ((struct sadb_spirange *)extensions[SADB_EXT_SPIRANGE])->sadb_spirange_min; + maxspi = ((struct sadb_spirange *)extensions[SADB_EXT_SPIRANGE])->sadb_spirange_max; + } + + if(maxspi == minspi) { + extr->ips->ips_said.spi = maxspi; + ipsq = ipsec_sa_getbyid(&(extr->ips->ips_said)); + if(ipsq != NULL) { + sa_len = satoa(extr->ips->ips_said, 0, sa, SATOA_BUF); + ipsec_sa_put(ipsq); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_getspi_parse: " + "EMT_GETSPI found an old ipsec_sa for SA: %s, delete it first.\n", + sa_len ? sa : " (error)"); + SENDERR(EEXIST); + } else { + found_avail = 1; + } + } else { + int i = 0; + __u32 rand_val; + __u32 spi_diff; + while( ( i < (spi_diff = (ntohl(maxspi) - ntohl(minspi)))) && !found_avail ) { + prng_bytes(&ipsec_prng, (char *) &(rand_val), + ( (spi_diff < (2^8)) ? 1 : + ( (spi_diff < (2^16)) ? 2 : + ( (spi_diff < (2^24)) ? 3 : + 4 ) ) ) ); + extr->ips->ips_said.spi = htonl(ntohl(minspi) + + (rand_val % + (spi_diff + 1))); + i++; + ipsq = ipsec_sa_getbyid(&(extr->ips->ips_said)); + if(ipsq == NULL) { + found_avail = 1; + } else { + ipsec_sa_put(ipsq); + } + } + } + + sa_len = satoa(extr->ips->ips_said, 0, sa, SATOA_BUF); + + if (!found_avail) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_getspi_parse: " + "found an old ipsec_sa for SA: %s, delete it first.\n", + sa_len ? sa : " (error)"); + SENDERR(EEXIST); + } + + if(ip_chk_addr((unsigned long)extr->ips->ips_said.dst.s_addr) == IS_MYADDR) { + extr->ips->ips_flags |= EMT_INBOUND; + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_getspi_parse: " + "existing ipsec_sa not found (this is good) for SA: %s, %s-bound, allocating.\n", + sa_len ? sa : " (error)", + extr->ips->ips_flags & EMT_INBOUND ? "in" : "out"); + + /* XXX extr->ips->ips_rcvif = &(enc_softc[em->em_if].enc_if);*/ + extr->ips->ips_rcvif = NULL; + extr->ips->ips_life.ipl_addtime.ipl_count = jiffies/HZ; + + extr->ips->ips_state = SADB_SASTATE_LARVAL; + + if(!extr->ips->ips_life.ipl_allocations.ipl_count) { + extr->ips->ips_life.ipl_allocations.ipl_count += 1; + } + + if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0], + SADB_GETSPI, + satype, + 0, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid), + extensions_reply) + && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA], + SADB_EXT_SA, + extr->ips->ips_said.spi, + 0, + SADB_SASTATE_LARVAL, + 0, + 0, + 0, + extr->ips->ips_ref), + extensions_reply) + && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_SRC], + SADB_EXT_ADDRESS_SRC, + 0, /*extr->ips->ips_said.proto,*/ + 0, + extr->ips->ips_addr_s), + extensions_reply) + && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST], + SADB_EXT_ADDRESS_DST, + 0, /*extr->ips->ips_said.proto,*/ + 0, + extr->ips->ips_addr_d), + extensions_reply) )) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_getspi_parse: " + "failed to build the getspi reply message extensions\n"); + goto errlab; + } + + if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_getspi_parse: " + "failed to build the getspi reply message\n"); + SENDERR(-error); + } + for(pfkey_socketsp = pfkey_open_sockets; + pfkey_socketsp; + pfkey_socketsp = pfkey_socketsp->next) { + if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_getspi_parse: " + "sending up getspi reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp, + error); + SENDERR(-error); + } + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_getspi_parse: " + "sending up getspi reply message for satype=%d(%s) to socket=0p%p succeeded.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp); + } + + if((error = ipsec_sa_add(extr->ips))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_getspi_parse: " + "failed to add the larval SA=%s with error=%d.\n", + sa_len ? sa : " (error)", + error); + SENDERR(-error); + } + extr->ips = NULL; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_getspi_parse: " + "successful for SA: %s\n", + sa_len ? sa : " (error)"); + + errlab: + if (pfkey_reply) { + pfkey_msg_free(&pfkey_reply); + } + pfkey_extensions_free(extensions_reply); + return error; +} + +DEBUG_NO_STATIC int +pfkey_update_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) +{ + int error = 0; + struct ipsec_sa* ipsq; + char sa[SATOA_BUF]; + size_t sa_len; + struct sadb_ext *extensions_reply[SADB_EXT_MAX+1]; + struct sadb_msg *pfkey_reply = NULL; + struct socket_list *pfkey_socketsp; + uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype; +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL + struct ipsec_sa *nat_t_ips_saved = NULL; +#endif + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_update_parse: .\n"); + + pfkey_extensions_init(extensions_reply); + + if(((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state != SADB_SASTATE_MATURE) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_update_parse: " + "error, sa_state=%d must be MATURE=%d\n", + ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state, + SADB_SASTATE_MATURE); + SENDERR(EINVAL); + } + + if(extr == NULL || extr->ips == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_update_parse: " + "error, extr or extr->ips pointer NULL\n"); + SENDERR(EINVAL); + } + + sa_len = satoa(extr->ips->ips_said, 0, sa, SATOA_BUF); + + spin_lock_bh(&tdb_lock); + + ipsq = ipsec_sa_getbyid(&(extr->ips->ips_said)); + if (ipsq == NULL) { + spin_unlock_bh(&tdb_lock); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_update_parse: " + "reserved ipsec_sa for SA: %s not found. Call SADB_GETSPI first or call SADB_ADD instead.\n", + sa_len ? sa : " (error)"); + SENDERR(ENOENT); + } + + if(ip_chk_addr((unsigned long)extr->ips->ips_said.dst.s_addr) == IS_MYADDR) { + extr->ips->ips_flags |= EMT_INBOUND; + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_update_parse: " + "existing ipsec_sa found (this is good) for SA: %s, %s-bound, updating.\n", + sa_len ? sa : " (error)", + extr->ips->ips_flags & EMT_INBOUND ? "in" : "out"); + +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL + if (extr->ips->ips_natt_sport || extr->ips->ips_natt_dport) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_update_parse: only updating NAT-T ports " + "(%u:%u -> %u:%u)\n", + ipsq->ips_natt_sport, ipsq->ips_natt_dport, + extr->ips->ips_natt_sport, extr->ips->ips_natt_dport); + + if (extr->ips->ips_natt_sport) { + ipsq->ips_natt_sport = extr->ips->ips_natt_sport; + if (ipsq->ips_addr_s->sa_family == AF_INET) { + ((struct sockaddr_in *)(ipsq->ips_addr_s))->sin_port = htons(extr->ips->ips_natt_sport); + } + } + + if (extr->ips->ips_natt_dport) { + ipsq->ips_natt_dport = extr->ips->ips_natt_dport; + if (ipsq->ips_addr_d->sa_family == AF_INET) { + ((struct sockaddr_in *)(ipsq->ips_addr_d))->sin_port = htons(extr->ips->ips_natt_dport); + } + } + + nat_t_ips_saved = extr->ips; + extr->ips = ipsq; + } + else { +#endif + + /* XXX extr->ips->ips_rcvif = &(enc_softc[em->em_if].enc_if);*/ + extr->ips->ips_rcvif = NULL; + if ((error = pfkey_ipsec_sa_init(extr->ips, extensions))) { + ipsec_sa_put(ipsq); + spin_unlock_bh(&tdb_lock); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_update_parse: " + "not successful for SA: %s, deleting.\n", + sa_len ? sa : " (error)"); + SENDERR(-error); + } + + extr->ips->ips_life.ipl_addtime.ipl_count = ipsq->ips_life.ipl_addtime.ipl_count; + ipsec_sa_put(ipsq); + if((error = ipsec_sa_delchain(ipsq))) { + spin_unlock_bh(&tdb_lock); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_update_parse: " + "error=%d, trouble deleting intermediate ipsec_sa for SA=%s.\n", + error, + sa_len ? sa : " (error)"); + SENDERR(-error); + } +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL + } +#endif + + spin_unlock_bh(&tdb_lock); + + if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0], + SADB_UPDATE, + satype, + 0, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid), + extensions_reply) + && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA], + SADB_EXT_SA, + extr->ips->ips_said.spi, + extr->ips->ips_replaywin, + extr->ips->ips_state, + extr->ips->ips_authalg, + extr->ips->ips_encalg, + extr->ips->ips_flags, + extr->ips->ips_ref), + extensions_reply) + /* The 3 lifetime extentions should only be sent if non-zero. */ + && (extensions[SADB_EXT_LIFETIME_HARD] + ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_HARD], + SADB_EXT_LIFETIME_HARD, + extr->ips->ips_life.ipl_allocations.ipl_hard, + extr->ips->ips_life.ipl_bytes.ipl_hard, + extr->ips->ips_life.ipl_addtime.ipl_hard, + extr->ips->ips_life.ipl_usetime.ipl_hard, + extr->ips->ips_life.ipl_packets.ipl_hard), + extensions_reply) : 1) + && (extensions[SADB_EXT_LIFETIME_SOFT] + ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_SOFT], + SADB_EXT_LIFETIME_SOFT, + extr->ips->ips_life.ipl_allocations.ipl_count, + extr->ips->ips_life.ipl_bytes.ipl_count, + extr->ips->ips_life.ipl_addtime.ipl_count, + extr->ips->ips_life.ipl_usetime.ipl_count, + extr->ips->ips_life.ipl_packets.ipl_count), + extensions_reply) : 1) + && (extr->ips->ips_life.ipl_allocations.ipl_count + || extr->ips->ips_life.ipl_bytes.ipl_count + || extr->ips->ips_life.ipl_addtime.ipl_count + || extr->ips->ips_life.ipl_usetime.ipl_count + || extr->ips->ips_life.ipl_packets.ipl_count + + ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_CURRENT], + SADB_EXT_LIFETIME_CURRENT, + extr->ips->ips_life.ipl_allocations.ipl_count, + extr->ips->ips_life.ipl_bytes.ipl_count, + extr->ips->ips_life.ipl_addtime.ipl_count, + extr->ips->ips_life.ipl_usetime.ipl_count, + extr->ips->ips_life.ipl_packets.ipl_count), + extensions_reply) : 1) + && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_SRC], + SADB_EXT_ADDRESS_SRC, + 0, /*extr->ips->ips_said.proto,*/ + 0, + extr->ips->ips_addr_s), + extensions_reply) + && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST], + SADB_EXT_ADDRESS_DST, + 0, /*extr->ips->ips_said.proto,*/ + 0, + extr->ips->ips_addr_d), + extensions_reply) + && (extr->ips->ips_ident_s.data + ? pfkey_safe_build(error = pfkey_ident_build(&extensions_reply[SADB_EXT_IDENTITY_SRC], + SADB_EXT_IDENTITY_SRC, + extr->ips->ips_ident_s.type, + extr->ips->ips_ident_s.id, + extr->ips->ips_ident_s.len, + extr->ips->ips_ident_s.data), + extensions_reply) : 1) + && (extr->ips->ips_ident_d.data + ? pfkey_safe_build(error = pfkey_ident_build(&extensions_reply[SADB_EXT_IDENTITY_DST], + SADB_EXT_IDENTITY_DST, + extr->ips->ips_ident_d.type, + extr->ips->ips_ident_d.id, + extr->ips->ips_ident_d.len, + extr->ips->ips_ident_d.data), + extensions_reply) : 1) +#if 0 + /* FIXME: This won't work yet because I have not finished + it. */ + && (extr->ips->ips_sens_ + ? pfkey_safe_build(error = pfkey_sens_build(&extensions_reply[SADB_EXT_SENSITIVITY], + extr->ips->ips_sens_dpd, + extr->ips->ips_sens_sens_level, + extr->ips->ips_sens_sens_len, + extr->ips->ips_sens_sens_bitmap, + extr->ips->ips_sens_integ_level, + extr->ips->ips_sens_integ_len, + extr->ips->ips_sens_integ_bitmap), + extensions_reply) : 1) +#endif + )) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_update_parse: " + "failed to build the update reply message extensions\n"); + SENDERR(-error); + } + + if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_update_parse: " + "failed to build the update reply message\n"); + SENDERR(-error); + } + for(pfkey_socketsp = pfkey_open_sockets; + pfkey_socketsp; + pfkey_socketsp = pfkey_socketsp->next) { + if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_update_parse: " + "sending up update reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp, + error); + SENDERR(-error); + } + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_update_parse: " + "sending up update reply message for satype=%d(%s) to socket=0p%p succeeded.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp); + } + +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL + if (nat_t_ips_saved) { + /** + * As we _really_ update existing SA, we keep tdbq and need to delete + * parsed ips (nat_t_ips_saved, was extr->ips). + * + * goto errlab with extr->ips = nat_t_ips_saved will free it. + */ + + extr->ips = nat_t_ips_saved; + + error = 0; + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_update_parse (NAT-T ports): " + "successful for SA: %s\n", + sa_len ? sa : " (error)"); + + goto errlab; + } +#endif + + if((error = ipsec_sa_add(extr->ips))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_update_parse: " + "failed to update the mature SA=%s with error=%d.\n", + sa_len ? sa : " (error)", + error); + SENDERR(-error); + } + extr->ips = NULL; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_update_parse: " + "successful for SA: %s\n", + sa_len ? sa : " (error)"); + + errlab: + if (pfkey_reply) { + pfkey_msg_free(&pfkey_reply); + } + pfkey_extensions_free(extensions_reply); + return error; +} + +DEBUG_NO_STATIC int +pfkey_add_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) +{ + int error = 0; + struct ipsec_sa* ipsq; + char sa[SATOA_BUF]; + size_t sa_len; + struct sadb_ext *extensions_reply[SADB_EXT_MAX+1]; + struct sadb_msg *pfkey_reply = NULL; + struct socket_list *pfkey_socketsp; + uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_add_parse: .\n"); + + pfkey_extensions_init(extensions_reply); + + if(((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state != SADB_SASTATE_MATURE) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_add_parse: " + "error, sa_state=%d must be MATURE=%d\n", + ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state, + SADB_SASTATE_MATURE); + SENDERR(EINVAL); + } + + if(!extr || !extr->ips) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_add_parse: " + "extr or extr->ips pointer NULL\n"); + SENDERR(EINVAL); + } + + sa_len = satoa(extr->ips->ips_said, 0, sa, SATOA_BUF); + + ipsq = ipsec_sa_getbyid(&(extr->ips->ips_said)); + if(ipsq != NULL) { + ipsec_sa_put(ipsq); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_add_parse: " + "found an old ipsec_sa for SA%s, delete it first.\n", + sa_len ? sa : " (error)"); + SENDERR(EEXIST); + } + + if(ip_chk_addr((unsigned long)extr->ips->ips_said.dst.s_addr) == IS_MYADDR) { + extr->ips->ips_flags |= EMT_INBOUND; + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_add_parse: " + "existing ipsec_sa not found (this is good) for SA%s, %s-bound, allocating.\n", + sa_len ? sa : " (error)", + extr->ips->ips_flags & EMT_INBOUND ? "in" : "out"); + + /* XXX extr->ips->ips_rcvif = &(enc_softc[em->em_if].enc_if);*/ + extr->ips->ips_rcvif = NULL; + + if ((error = pfkey_ipsec_sa_init(extr->ips, extensions))) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_add_parse: " + "not successful for SA: %s, deleting.\n", + sa_len ? sa : " (error)"); + SENDERR(-error); + } + + extr->ips->ips_life.ipl_addtime.ipl_count = jiffies / HZ; + if(!extr->ips->ips_life.ipl_allocations.ipl_count) { + extr->ips->ips_life.ipl_allocations.ipl_count += 1; + } + + if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0], + SADB_ADD, + satype, + 0, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid), + extensions_reply) + && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA], + SADB_EXT_SA, + extr->ips->ips_said.spi, + extr->ips->ips_replaywin, + extr->ips->ips_state, + extr->ips->ips_authalg, + extr->ips->ips_encalg, + extr->ips->ips_flags, + extr->ips->ips_ref), + extensions_reply) + /* The 3 lifetime extentions should only be sent if non-zero. */ + && (extensions[SADB_EXT_LIFETIME_HARD] + ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_HARD], + SADB_EXT_LIFETIME_HARD, + extr->ips->ips_life.ipl_allocations.ipl_hard, + extr->ips->ips_life.ipl_bytes.ipl_hard, + extr->ips->ips_life.ipl_addtime.ipl_hard, + extr->ips->ips_life.ipl_usetime.ipl_hard, + extr->ips->ips_life.ipl_packets.ipl_hard), + extensions_reply) : 1) + && (extensions[SADB_EXT_LIFETIME_SOFT] + ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_SOFT], + SADB_EXT_LIFETIME_SOFT, + extr->ips->ips_life.ipl_allocations.ipl_soft, + extr->ips->ips_life.ipl_bytes.ipl_soft, + extr->ips->ips_life.ipl_addtime.ipl_soft, + extr->ips->ips_life.ipl_usetime.ipl_soft, + extr->ips->ips_life.ipl_packets.ipl_soft), + extensions_reply) : 1) + && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_SRC], + SADB_EXT_ADDRESS_SRC, + 0, /*extr->ips->ips_said.proto,*/ + 0, + extr->ips->ips_addr_s), + extensions_reply) + && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST], + SADB_EXT_ADDRESS_DST, + 0, /*extr->ips->ips_said.proto,*/ + 0, + extr->ips->ips_addr_d), + extensions_reply) + && (extr->ips->ips_ident_s.data + ? pfkey_safe_build(error = pfkey_ident_build(&extensions_reply[SADB_EXT_IDENTITY_SRC], + SADB_EXT_IDENTITY_SRC, + extr->ips->ips_ident_s.type, + extr->ips->ips_ident_s.id, + extr->ips->ips_ident_s.len, + extr->ips->ips_ident_s.data), + extensions_reply) : 1) + && (extr->ips->ips_ident_d.data + ? pfkey_safe_build(error = pfkey_ident_build(&extensions_reply[SADB_EXT_IDENTITY_DST], + SADB_EXT_IDENTITY_DST, + extr->ips->ips_ident_d.type, + extr->ips->ips_ident_d.id, + extr->ips->ips_ident_d.len, + extr->ips->ips_ident_d.data), + extensions_reply) : 1) +#if 0 + /* FIXME: This won't work yet because I have not finished + it. */ + && (extr->ips->ips_sens_ + ? pfkey_safe_build(error = pfkey_sens_build(&extensions_reply[SADB_EXT_SENSITIVITY], + extr->ips->ips_sens_dpd, + extr->ips->ips_sens_sens_level, + extr->ips->ips_sens_sens_len, + extr->ips->ips_sens_sens_bitmap, + extr->ips->ips_sens_integ_level, + extr->ips->ips_sens_integ_len, + extr->ips->ips_sens_integ_bitmap), + extensions_reply) : 1) +#endif + )) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_add_parse: " + "failed to build the add reply message extensions\n"); + SENDERR(-error); + } + + if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_add_parse: " + "failed to build the add reply message\n"); + SENDERR(-error); + } + for(pfkey_socketsp = pfkey_open_sockets; + pfkey_socketsp; + pfkey_socketsp = pfkey_socketsp->next) { + if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_add_parse: " + "sending up add reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp, + error); + SENDERR(-error); + } + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_add_parse: " + "sending up add reply message for satype=%d(%s) to socket=0p%p succeeded.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp); + } + + if((error = ipsec_sa_add(extr->ips))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_add_parse: " + "failed to add the mature SA=%s with error=%d.\n", + sa_len ? sa : " (error)", + error); + SENDERR(-error); + } + extr->ips = NULL; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_add_parse: " + "successful for SA: %s\n", + sa_len ? sa : " (error)"); + + errlab: + if (pfkey_reply) { + pfkey_msg_free(&pfkey_reply); + } + pfkey_extensions_free(extensions_reply); + return error; +} + +DEBUG_NO_STATIC int +pfkey_delete_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) +{ + struct ipsec_sa *ipsp; + char sa[SATOA_BUF]; + size_t sa_len; + int error = 0; + struct sadb_ext *extensions_reply[SADB_EXT_MAX+1]; + struct sadb_msg *pfkey_reply = NULL; + struct socket_list *pfkey_socketsp; + uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_delete_parse: .\n"); + + pfkey_extensions_init(extensions_reply); + + if(!extr || !extr->ips) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_delete_parse: " + "extr or extr->ips pointer NULL, fatal\n"); + SENDERR(EINVAL); + } + + sa_len = satoa(extr->ips->ips_said, 0, sa, SATOA_BUF); + + spin_lock_bh(&tdb_lock); + + ipsp = ipsec_sa_getbyid(&(extr->ips->ips_said)); + if (ipsp == NULL) { + spin_unlock_bh(&tdb_lock); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_delete_parse: " + "ipsec_sa not found for SA:%s, could not delete.\n", + sa_len ? sa : " (error)"); + SENDERR(ESRCH); + } + + ipsec_sa_put(ipsp); + if((error = ipsec_sa_delchain(ipsp))) { + spin_unlock_bh(&tdb_lock); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_delete_parse: " + "error=%d returned trying to delete ipsec_sa for SA:%s.\n", + error, + sa_len ? sa : " (error)"); + SENDERR(-error); + } + spin_unlock_bh(&tdb_lock); + + if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0], + SADB_DELETE, + satype, + 0, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid), + extensions_reply) + && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA], + SADB_EXT_SA, + extr->ips->ips_said.spi, + 0, + 0, + 0, + 0, + 0, + extr->ips->ips_ref), + extensions_reply) + && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_SRC], + SADB_EXT_ADDRESS_SRC, + 0, /*extr->ips->ips_said.proto,*/ + 0, + extr->ips->ips_addr_s), + extensions_reply) + && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST], + SADB_EXT_ADDRESS_DST, + 0, /*extr->ips->ips_said.proto,*/ + 0, + extr->ips->ips_addr_d), + extensions_reply) + )) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_delete_parse: " + "failed to build the delete reply message extensions\n"); + SENDERR(-error); + } + + if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_delete_parse: " + "failed to build the delete reply message\n"); + SENDERR(-error); + } + for(pfkey_socketsp = pfkey_open_sockets; + pfkey_socketsp; + pfkey_socketsp = pfkey_socketsp->next) { + if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_delete_parse: " + "sending up delete reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp, + error); + SENDERR(-error); + } + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_delete_parse: " + "sending up delete reply message for satype=%d(%s) to socket=0p%p succeeded.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp); + } + + errlab: + if (pfkey_reply) { + pfkey_msg_free(&pfkey_reply); + } + pfkey_extensions_free(extensions_reply); + return error; +} + +DEBUG_NO_STATIC int +pfkey_get_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) +{ + int error = 0; + struct ipsec_sa *ipsp; + char sa[SATOA_BUF]; + size_t sa_len; + struct sadb_ext *extensions_reply[SADB_EXT_MAX+1]; + struct sadb_msg *pfkey_reply = NULL; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_get_parse: .\n"); + + pfkey_extensions_init(extensions_reply); + + if(!extr || !extr->ips) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_get_parse: " + "extr or extr->ips pointer NULL, fatal\n"); + SENDERR(EINVAL); + } + + sa_len = satoa(extr->ips->ips_said, 0, sa, SATOA_BUF); + + spin_lock_bh(&tdb_lock); + + ipsp = ipsec_sa_getbyid(&(extr->ips->ips_said)); + if (ipsp == NULL) { + spin_unlock_bh(&tdb_lock); + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_get_parse: " + "ipsec_sa not found for SA=%s, could not get.\n", + sa_len ? sa : " (error)"); + SENDERR(ESRCH); + } + + if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0], + SADB_GET, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype, + 0, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid), + extensions_reply) + && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA], + SADB_EXT_SA, + extr->ips->ips_said.spi, + extr->ips->ips_replaywin, + extr->ips->ips_state, + extr->ips->ips_authalg, + extr->ips->ips_encalg, + extr->ips->ips_flags, + extr->ips->ips_ref), + extensions_reply) + /* The 3 lifetime extentions should only be sent if non-zero. */ + && (ipsp->ips_life.ipl_allocations.ipl_count + || ipsp->ips_life.ipl_bytes.ipl_count + || ipsp->ips_life.ipl_addtime.ipl_count + || ipsp->ips_life.ipl_usetime.ipl_count + || ipsp->ips_life.ipl_packets.ipl_count + ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_CURRENT], + SADB_EXT_LIFETIME_CURRENT, + ipsp->ips_life.ipl_allocations.ipl_count, + ipsp->ips_life.ipl_bytes.ipl_count, + ipsp->ips_life.ipl_addtime.ipl_count, + ipsp->ips_life.ipl_usetime.ipl_count, + ipsp->ips_life.ipl_packets.ipl_count), + extensions_reply) : 1) + && (ipsp->ips_life.ipl_allocations.ipl_hard + || ipsp->ips_life.ipl_bytes.ipl_hard + || ipsp->ips_life.ipl_addtime.ipl_hard + || ipsp->ips_life.ipl_usetime.ipl_hard + || ipsp->ips_life.ipl_packets.ipl_hard + ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_HARD], + SADB_EXT_LIFETIME_HARD, + ipsp->ips_life.ipl_allocations.ipl_hard, + ipsp->ips_life.ipl_bytes.ipl_hard, + ipsp->ips_life.ipl_addtime.ipl_hard, + ipsp->ips_life.ipl_usetime.ipl_hard, + ipsp->ips_life.ipl_packets.ipl_hard), + extensions_reply) : 1) + && (ipsp->ips_life.ipl_allocations.ipl_soft + || ipsp->ips_life.ipl_bytes.ipl_soft + || ipsp->ips_life.ipl_addtime.ipl_soft + || ipsp->ips_life.ipl_usetime.ipl_soft + || ipsp->ips_life.ipl_packets.ipl_soft + ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_SOFT], + SADB_EXT_LIFETIME_SOFT, + ipsp->ips_life.ipl_allocations.ipl_soft, + ipsp->ips_life.ipl_bytes.ipl_soft, + ipsp->ips_life.ipl_addtime.ipl_soft, + ipsp->ips_life.ipl_usetime.ipl_soft, + ipsp->ips_life.ipl_packets.ipl_soft), + extensions_reply) : 1) + && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_SRC], + SADB_EXT_ADDRESS_SRC, + 0, /*extr->ips->ips_said.proto,*/ + 0, + extr->ips->ips_addr_s), + extensions_reply) + && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST], + SADB_EXT_ADDRESS_DST, + 0, /*extr->ips->ips_said.proto,*/ + 0, + extr->ips->ips_addr_d), + extensions_reply) + && (extr->ips->ips_addr_p + ? pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_PROXY], + SADB_EXT_ADDRESS_PROXY, + 0, /*extr->ips->ips_said.proto,*/ + 0, + extr->ips->ips_addr_p), + extensions_reply) : 1) +#if 0 + /* FIXME: This won't work yet because the keys are not + stored directly in the ipsec_sa. They are stored as + contexts. */ + && (extr->ips->ips_key_a_size + ? pfkey_safe_build(error = pfkey_key_build(&extensions_reply[SADB_EXT_KEY_AUTH], + SADB_EXT_KEY_AUTH, + extr->ips->ips_key_a_size * 8, + extr->ips->ips_key_a), + extensions_reply) : 1) + /* FIXME: This won't work yet because the keys are not + stored directly in the ipsec_sa. They are stored as + key schedules. */ + && (extr->ips->ips_key_e_size + ? pfkey_safe_build(error = pfkey_key_build(&extensions_reply[SADB_EXT_KEY_ENCRYPT], + SADB_EXT_KEY_ENCRYPT, + extr->ips->ips_key_e_size * 8, + extr->ips->ips_key_e), + extensions_reply) : 1) +#endif + && (extr->ips->ips_ident_s.data + ? pfkey_safe_build(error = pfkey_ident_build(&extensions_reply[SADB_EXT_IDENTITY_SRC], + SADB_EXT_IDENTITY_SRC, + extr->ips->ips_ident_s.type, + extr->ips->ips_ident_s.id, + extr->ips->ips_ident_s.len, + extr->ips->ips_ident_s.data), + extensions_reply) : 1) + && (extr->ips->ips_ident_d.data + ? pfkey_safe_build(error = pfkey_ident_build(&extensions_reply[SADB_EXT_IDENTITY_DST], + SADB_EXT_IDENTITY_DST, + extr->ips->ips_ident_d.type, + extr->ips->ips_ident_d.id, + extr->ips->ips_ident_d.len, + extr->ips->ips_ident_d.data), + extensions_reply) : 1) +#if 0 + /* FIXME: This won't work yet because I have not finished + it. */ + && (extr->ips->ips_sens_ + ? pfkey_safe_build(error = pfkey_sens_build(&extensions_reply[SADB_EXT_SENSITIVITY], + extr->ips->ips_sens_dpd, + extr->ips->ips_sens_sens_level, + extr->ips->ips_sens_sens_len, + extr->ips->ips_sens_sens_bitmap, + extr->ips->ips_sens_integ_level, + extr->ips->ips_sens_integ_len, + extr->ips->ips_sens_integ_bitmap), + extensions_reply) : 1) +#endif + )) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_get_parse: " + "failed to build the get reply message extensions\n"); + ipsec_sa_put(ipsp); + spin_unlock_bh(&tdb_lock); + SENDERR(-error); + } + + ipsec_sa_put(ipsp); + spin_unlock_bh(&tdb_lock); + + if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_get_parse: " + "failed to build the get reply message\n"); + SENDERR(-error); + } + + if((error = pfkey_upmsg(sk->socket, pfkey_reply))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_get_parse: " + "failed to send the get reply message\n"); + SENDERR(-error); + } + + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_get_parse: " + "succeeded in sending get reply message.\n"); + + errlab: + if (pfkey_reply) { + pfkey_msg_free(&pfkey_reply); + } + pfkey_extensions_free(extensions_reply); + return error; +} + +DEBUG_NO_STATIC int +pfkey_acquire_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) +{ + int error = 0; + struct socket_list *pfkey_socketsp; + uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_acquire_parse: .\n"); + + /* XXX I don't know if we want an upper bound, since userspace may + want to register itself for an satype > SADB_SATYPE_MAX. */ + if((satype == 0) || (satype > SADB_SATYPE_MAX)) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_acquire_parse: " + "SATYPE=%d invalid.\n", + satype); + SENDERR(EINVAL); + } + + if(!(pfkey_registered_sockets[satype])) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_acquire_parse: " + "no sockets registered for SAtype=%d(%s).\n", + satype, + satype2name(satype)); + SENDERR(EPROTONOSUPPORT); + } + + for(pfkey_socketsp = pfkey_registered_sockets[satype]; + pfkey_socketsp; + pfkey_socketsp = pfkey_socketsp->next) { + if((error = pfkey_upmsg(pfkey_socketsp->socketp, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_acquire_parse: " + "sending up acquire reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp, + error); + SENDERR(-error); + } + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_acquire_parse: " + "sending up acquire reply message for satype=%d(%s) to socket=0p%p succeeded.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp); + } + + errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_register_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) +{ + int error = 0; + uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_register_parse: .\n"); + + /* XXX I don't know if we want an upper bound, since userspace may + want to register itself for an satype > SADB_SATYPE_MAX. */ + if((satype == 0) || (satype > SADB_SATYPE_MAX)) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_register_parse: " + "SATYPE=%d invalid.\n", + satype); + SENDERR(EINVAL); + } + + if(!pfkey_list_insert_socket(sk->socket, + &(pfkey_registered_sockets[satype]))) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_register_parse: " + "SATYPE=%02d(%s) successfully registered by KMd (pid=%d).\n", + satype, + satype2name(satype), + key_pid(sk)); + }; + + /* send up register msg with supported SATYPE algos */ + + error=pfkey_register_reply(satype, (struct sadb_msg*)extensions[SADB_EXT_RESERVED]); + errlab: + return error; +} +int +pfkey_register_reply(int satype, struct sadb_msg *sadb_msg) +{ + struct sadb_ext *extensions_reply[SADB_EXT_MAX+1]; + struct sadb_msg *pfkey_reply = NULL; + struct socket_list *pfkey_socketsp; + struct supported_list *pfkey_supported_listp; + unsigned int alg_num_a = 0, alg_num_e = 0; + struct sadb_alg *alg_a = NULL, *alg_e = NULL, *alg_ap = NULL, *alg_ep = NULL; + int error = 0; + + pfkey_extensions_init(extensions_reply); + + if((satype == 0) || (satype > SADB_SATYPE_MAX)) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_register_reply: " + "SAtype=%d unspecified or unknown.\n", + satype); + SENDERR(EINVAL); + } + if(!(pfkey_registered_sockets[satype])) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_register_reply: " + "no sockets registered for SAtype=%d(%s).\n", + satype, + satype2name(satype)); + SENDERR(EPROTONOSUPPORT); + } + /* send up register msg with supported SATYPE algos */ + pfkey_supported_listp = pfkey_supported_list[satype]; + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_register_reply: " + "pfkey_supported_list[%d]=0p%p\n", + satype, + pfkey_supported_list[satype]); + while(pfkey_supported_listp) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_register_reply: " + "checking supported=0p%p\n", + pfkey_supported_listp); + if(pfkey_supported_listp->supportedp->supported_alg_exttype == SADB_EXT_SUPPORTED_AUTH) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_register_reply: " + "adding auth alg.\n"); + alg_num_a++; + } + if(pfkey_supported_listp->supportedp->supported_alg_exttype == SADB_EXT_SUPPORTED_ENCRYPT) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_register_reply: " + "adding encrypt alg.\n"); + alg_num_e++; + } + pfkey_supported_listp = pfkey_supported_listp->next; + } + + if(alg_num_a) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_register_reply: " + "allocating %lu bytes for auth algs.\n", + (unsigned long) (alg_num_a * sizeof(struct sadb_alg))); + if((alg_a = kmalloc(alg_num_a * sizeof(struct sadb_alg), GFP_ATOMIC) ) == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_register_reply: " + "auth alg memory allocation error\n"); + SENDERR(ENOMEM); + } + alg_ap = alg_a; + } + + if(alg_num_e) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_register_reply: " + "allocating %lu bytes for enc algs.\n", + (unsigned long) (alg_num_e * sizeof(struct sadb_alg))); + if((alg_e = kmalloc(alg_num_e * sizeof(struct sadb_alg), GFP_ATOMIC) ) == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_register_reply: " + "enc alg memory allocation error\n"); + SENDERR(ENOMEM); + } + alg_ep = alg_e; + } + + pfkey_supported_listp = pfkey_supported_list[satype]; + while(pfkey_supported_listp) { + if(alg_num_a) { + if(pfkey_supported_listp->supportedp->supported_alg_exttype == SADB_EXT_SUPPORTED_AUTH) { + alg_ap->sadb_alg_id = pfkey_supported_listp->supportedp->supported_alg_id; + alg_ap->sadb_alg_ivlen = pfkey_supported_listp->supportedp->supported_alg_ivlen; + alg_ap->sadb_alg_minbits = pfkey_supported_listp->supportedp->supported_alg_minbits; + alg_ap->sadb_alg_maxbits = pfkey_supported_listp->supportedp->supported_alg_maxbits; + alg_ap->sadb_alg_reserved = 0; + KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, + "klips_debug:pfkey_register_reply: " + "adding auth=0p%p\n", + alg_ap); + alg_ap++; + } + } + if(alg_num_e) { + if(pfkey_supported_listp->supportedp->supported_alg_exttype == SADB_EXT_SUPPORTED_ENCRYPT) { + alg_ep->sadb_alg_id = pfkey_supported_listp->supportedp->supported_alg_id; + alg_ep->sadb_alg_ivlen = pfkey_supported_listp->supportedp->supported_alg_ivlen; + alg_ep->sadb_alg_minbits = pfkey_supported_listp->supportedp->supported_alg_minbits; + alg_ep->sadb_alg_maxbits = pfkey_supported_listp->supportedp->supported_alg_maxbits; + alg_ep->sadb_alg_reserved = 0; + KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, + "klips_debug:pfkey_register_reply: " + "adding encrypt=0p%p\n", + alg_ep); + alg_ep++; + } + } + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_register_reply: " + "found satype=%d(%s) exttype=%d id=%d ivlen=%d minbits=%d maxbits=%d.\n", + satype, + satype2name(satype), + pfkey_supported_listp->supportedp->supported_alg_exttype, + pfkey_supported_listp->supportedp->supported_alg_id, + pfkey_supported_listp->supportedp->supported_alg_ivlen, + pfkey_supported_listp->supportedp->supported_alg_minbits, + pfkey_supported_listp->supportedp->supported_alg_maxbits); + pfkey_supported_listp = pfkey_supported_listp->next; + } + if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0], + SADB_REGISTER, + satype, + 0, + sadb_msg? sadb_msg->sadb_msg_seq : ++pfkey_msg_seq, + sadb_msg? sadb_msg->sadb_msg_pid: current->pid), + extensions_reply) && + (alg_num_a ? pfkey_safe_build(error = pfkey_supported_build(&extensions_reply[SADB_EXT_SUPPORTED_AUTH], + SADB_EXT_SUPPORTED_AUTH, + alg_num_a, + alg_a), + extensions_reply) : 1) && + (alg_num_e ? pfkey_safe_build(error = pfkey_supported_build(&extensions_reply[SADB_EXT_SUPPORTED_ENCRYPT], + SADB_EXT_SUPPORTED_ENCRYPT, + alg_num_e, + alg_e), + extensions_reply) : 1))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_register_reply: " + "failed to build the register message extensions_reply\n"); + SENDERR(-error); + } + + if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_register_reply: " + "failed to build the register message\n"); + SENDERR(-error); + } + /* this should go to all registered sockets for that satype only */ + for(pfkey_socketsp = pfkey_registered_sockets[satype]; + pfkey_socketsp; + pfkey_socketsp = pfkey_socketsp->next) { + if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_register_reply: " + "sending up acquire message for satype=%d(%s) to socket=0p%p failed with error=%d.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp, + error); + SENDERR(-error); + } + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_register_reply: " + "sending up register message for satype=%d(%s) to socket=0p%p succeeded.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp); + } + + errlab: + if(alg_a) { + kfree(alg_a); + } + if(alg_e) { + kfree(alg_e); + } + if (pfkey_reply) { + pfkey_msg_free(&pfkey_reply); + } + pfkey_extensions_free(extensions_reply); + return error; +} + +DEBUG_NO_STATIC int +pfkey_expire_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) +{ + int error = 0; + struct socket_list *pfkey_socketsp; +#ifdef CONFIG_IPSEC_DEBUG + uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype; +#endif /* CONFIG_IPSEC_DEBUG */ + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_expire_parse: .\n"); + + if(pfkey_open_sockets) { + for(pfkey_socketsp = pfkey_open_sockets; + pfkey_socketsp; + pfkey_socketsp = pfkey_socketsp->next) { + if((error = pfkey_upmsg(pfkey_socketsp->socketp, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire_parse: " + "sending up expire reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp, + error); + SENDERR(-error); + } + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire_parse: " + "sending up expire reply message for satype=%d(%s) to socket=0p%p succeeded.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp); + } + } + + errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_flush_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) +{ + int error = 0; + struct socket_list *pfkey_socketsp; + uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype; + uint8_t proto = 0; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_flush_parse: " + "flushing type %d SAs\n", + satype); + + if(satype && !(proto = satype2proto(satype))) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_flush_parse: " + "satype %d lookup failed.\n", + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype); + SENDERR(EINVAL); + } + + if ((error = ipsec_sadb_cleanup(proto))) { + SENDERR(-error); + } + + if(pfkey_open_sockets) { + for(pfkey_socketsp = pfkey_open_sockets; + pfkey_socketsp; + pfkey_socketsp = pfkey_socketsp->next) { + if((error = pfkey_upmsg(pfkey_socketsp->socketp, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_flush_parse: " + "sending up flush reply message for satype=%d(%s) (proto=%d) to socket=0p%p failed with error=%d.\n", + satype, + satype2name(satype), + proto, + pfkey_socketsp->socketp, + error); + SENDERR(-error); + } + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_flush_parse: " + "sending up flush reply message for satype=%d(%s) to socket=0p%p succeeded.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp); + } + } + + errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_dump_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) +{ + int error = 0; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_dump_parse: .\n"); + + SENDERR(ENOSYS); + errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_x_promisc_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) +{ + int error = 0; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_promisc_parse: .\n"); + + SENDERR(ENOSYS); + errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_x_pchange_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) +{ + int error = 0; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_pchange_parse: .\n"); + + SENDERR(ENOSYS); + errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_x_grpsa_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) +{ + struct ipsec_sa *ips1p, *ips2p, *ipsp; + struct sadb_ext *extensions_reply[SADB_EXT_MAX+1]; + struct sadb_msg *pfkey_reply = NULL; + struct socket_list *pfkey_socketsp; + uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype; + char sa1[SATOA_BUF], sa2[SATOA_BUF]; + size_t sa_len1, sa_len2 = 0; + int error = 0; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_grpsa_parse: .\n"); + + pfkey_extensions_init(extensions_reply); + + if(extr == NULL || extr->ips == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_grpsa_parse: " + "extr or extr->ips is NULL, fatal.\n"); + SENDERR(EINVAL); + } + + sa_len1 = satoa(extr->ips->ips_said, 0, sa1, SATOA_BUF); + if(extr->ips2 != NULL) { + sa_len2 = satoa(extr->ips2->ips_said, 0, sa2, SATOA_BUF); + } + + spin_lock_bh(&tdb_lock); + + ips1p = ipsec_sa_getbyid(&(extr->ips->ips_said)); + if(ips1p == NULL) { + spin_unlock_bh(&tdb_lock); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_grpsa_parse: " + "reserved ipsec_sa for SA1: %s not found. Call SADB_ADD/UPDATE first.\n", + sa_len1 ? sa1 : " (error)"); + SENDERR(ENOENT); + } + if(extr->ips2) { /* GRPSA */ + ips2p = ipsec_sa_getbyid(&(extr->ips2->ips_said)); + if(ips2p == NULL) { + ipsec_sa_put(ips1p); + spin_unlock_bh(&tdb_lock); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_grpsa_parse: " + "reserved ipsec_sa for SA2: %s not found. Call SADB_ADD/UPDATE first.\n", + sa_len2 ? sa2 : " (error)"); + SENDERR(ENOENT); + } + + /* Is either one already linked? */ + if(ips1p->ips_onext) { + ipsec_sa_put(ips1p); + ipsec_sa_put(ips2p); + spin_unlock_bh(&tdb_lock); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_grpsa_parse: " + "ipsec_sa for SA: %s is already linked.\n", + sa_len1 ? sa1 : " (error)"); + SENDERR(EEXIST); + } + if(ips2p->ips_inext) { + ipsec_sa_put(ips1p); + ipsec_sa_put(ips2p); + spin_unlock_bh(&tdb_lock); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_grpsa_parse: " + "ipsec_sa for SA: %s is already linked.\n", + sa_len2 ? sa2 : " (error)"); + SENDERR(EEXIST); + } + + /* Is extr->ips already linked to extr->ips2? */ + ipsp = ips2p; + while(ipsp) { + if(ipsp == ips1p) { + ipsec_sa_put(ips1p); + ipsec_sa_put(ips2p); + spin_unlock_bh(&tdb_lock); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_grpsa_parse: " + "ipsec_sa for SA: %s is already linked to %s.\n", + sa_len1 ? sa1 : " (error)", + sa_len2 ? sa2 : " (error)"); + SENDERR(EEXIST); + } + ipsp = ipsp->ips_onext; + } + + /* link 'em */ + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_grpsa_parse: " + "linking ipsec_sa SA: %s with %s.\n", + sa_len1 ? sa1 : " (error)", + sa_len2 ? sa2 : " (error)"); + ips1p->ips_onext = ips2p; + ips2p->ips_inext = ips1p; + } else { /* UNGRPSA */ + ipsec_sa_put(ips1p); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_grpsa_parse: " + "unlinking ipsec_sa SA: %s.\n", + sa_len1 ? sa1 : " (error)"); + while(ips1p->ips_onext) { + ips1p = ips1p->ips_onext; + } + while(ips1p->ips_inext) { + ipsp = ips1p; + ips1p = ips1p->ips_inext; + ipsec_sa_put(ips1p); + ipsp->ips_inext = NULL; + ipsec_sa_put(ipsp); + ips1p->ips_onext = NULL; + } + } + + spin_unlock_bh(&tdb_lock); + + if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0], + SADB_X_GRPSA, + satype, + 0, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid), + extensions_reply) + && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA], + SADB_EXT_SA, + extr->ips->ips_said.spi, + extr->ips->ips_replaywin, + extr->ips->ips_state, + extr->ips->ips_authalg, + extr->ips->ips_encalg, + extr->ips->ips_flags, + extr->ips->ips_ref), + extensions_reply) + && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST], + SADB_EXT_ADDRESS_DST, + 0, /*extr->ips->ips_said.proto,*/ + 0, + extr->ips->ips_addr_d), + extensions_reply) + && (extr->ips2 + ? (pfkey_safe_build(error = pfkey_x_satype_build(&extensions_reply[SADB_X_EXT_SATYPE2], + ((struct sadb_x_satype*)extensions[SADB_X_EXT_SATYPE2])->sadb_x_satype_satype + /* proto2satype(extr->ips2->ips_said.proto) */), + extensions_reply) + && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_X_EXT_SA2], + SADB_X_EXT_SA2, + extr->ips2->ips_said.spi, + extr->ips2->ips_replaywin, + extr->ips2->ips_state, + extr->ips2->ips_authalg, + extr->ips2->ips_encalg, + extr->ips2->ips_flags, + extr->ips2->ips_ref), + extensions_reply) + && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_DST2], + SADB_X_EXT_ADDRESS_DST2, + 0, /*extr->ips->ips_said.proto,*/ + 0, + extr->ips2->ips_addr_d), + extensions_reply) ) : 1 ) + )) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_grpsa_parse: " + "failed to build the x_grpsa reply message extensions\n"); + SENDERR(-error); + } + + if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_grpsa_parse: " + "failed to build the x_grpsa reply message\n"); + SENDERR(-error); + } + + for(pfkey_socketsp = pfkey_open_sockets; + pfkey_socketsp; + pfkey_socketsp = pfkey_socketsp->next) { + if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_grpsa_parse: " + "sending up x_grpsa reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp, + error); + SENDERR(-error); + } + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_grpsa_parse: " + "sending up x_grpsa reply message for satype=%d(%s) to socket=0p%p succeeded.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp); + } + + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_grpsa_parse: " + "succeeded in sending x_grpsa reply message.\n"); + + errlab: + if (pfkey_reply) { + pfkey_msg_free(&pfkey_reply); + } + pfkey_extensions_free(extensions_reply); + return error; +} + +DEBUG_NO_STATIC int +pfkey_x_addflow_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) +{ + int error = 0; +#ifdef CONFIG_IPSEC_DEBUG + char buf1[64], buf2[64]; +#endif /* CONFIG_IPSEC_DEBUG */ + struct sadb_ext *extensions_reply[SADB_EXT_MAX+1]; + struct sadb_msg *pfkey_reply = NULL; + struct socket_list *pfkey_socketsp; + uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype; + ip_address srcflow, dstflow, srcmask, dstmask; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_addflow_parse: .\n"); + + pfkey_extensions_init(extensions_reply); + + memset((caddr_t)&srcflow, 0, sizeof(srcflow)); + memset((caddr_t)&dstflow, 0, sizeof(dstflow)); + memset((caddr_t)&srcmask, 0, sizeof(srcmask)); + memset((caddr_t)&dstmask, 0, sizeof(dstmask)); + + if(!extr || !(extr->ips) || !(extr->eroute)) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_addflow_parse: " + "missing extr, ipsec_sa or eroute data.\n"); + SENDERR(EINVAL); + } + + srcflow.u.v4.sin_family = AF_INET; + dstflow.u.v4.sin_family = AF_INET; + srcmask.u.v4.sin_family = AF_INET; + dstmask.u.v4.sin_family = AF_INET; + srcflow.u.v4.sin_addr = extr->eroute->er_eaddr.sen_ip_src; + dstflow.u.v4.sin_addr = extr->eroute->er_eaddr.sen_ip_dst; + srcmask.u.v4.sin_addr = extr->eroute->er_emask.sen_ip_src; + dstmask.u.v4.sin_addr = extr->eroute->er_emask.sen_ip_dst; + +#ifdef CONFIG_IPSEC_DEBUG + if (debug_pfkey) { + subnettoa(extr->eroute->er_eaddr.sen_ip_src, + extr->eroute->er_emask.sen_ip_src, 0, buf1, sizeof(buf1)); + subnettoa(extr->eroute->er_eaddr.sen_ip_dst, + extr->eroute->er_emask.sen_ip_dst, 0, buf2, sizeof(buf2)); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_addflow_parse: " + "calling breakeroute and/or makeroute for %s->%s\n", + buf1, buf2); + } +#endif /* CONFIG_IPSEC_DEBUG */ + if(extr->ips->ips_flags & SADB_X_SAFLAGS_INFLOW) { +/* if(ip_chk_addr((unsigned long)extr->ips->ips_said.dst.s_addr) == IS_MYADDR) */ + struct ipsec_sa *ipsp, *ipsq; + char sa[SATOA_BUF]; + size_t sa_len; + + ipsq = ipsec_sa_getbyid(&(extr->ips->ips_said)); + if(ipsq == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_addflow_parse: " + "ipsec_sa not found, cannot set incoming policy.\n"); + SENDERR(ENOENT); + } + + ipsp = ipsq; + while(ipsp && ipsp->ips_said.proto != IPPROTO_IPIP) { + ipsp = ipsp->ips_inext; + } + + if(ipsp == NULL) { + ipsec_sa_put(ipsq); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_addflow_parse: " + "SA chain does not have an IPIP SA, cannot set incoming policy.\n"); + SENDERR(ENOENT); + } + + sa_len = satoa(extr->ips->ips_said, 0, sa, SATOA_BUF); + + ipsp->ips_flags |= SADB_X_SAFLAGS_INFLOW; + ipsp->ips_flow_s = srcflow; + ipsp->ips_flow_d = dstflow; + ipsp->ips_mask_s = srcmask; + ipsp->ips_mask_d = dstmask; + + ipsec_sa_put(ipsq); + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_addflow_parse: " + "inbound eroute, setting incoming policy information in IPIP ipsec_sa for SA: %s.\n", + sa_len ? sa : " (error)"); + } else { + struct sk_buff *first = NULL, *last = NULL; + + if(extr->ips->ips_flags & SADB_X_SAFLAGS_REPLACEFLOW) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_addflow_parse: " + "REPLACEFLOW flag set, calling breakeroute.\n"); + if ((error = ipsec_breakroute(&(extr->eroute->er_eaddr), + &(extr->eroute->er_emask), + &first, &last))) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_addflow_parse: " + "breakeroute returned %d. first=0p%p, last=0p%p\n", + error, + first, + last); + if(first != NULL) { + ipsec_kfree_skb(first); + } + if(last != NULL) { + ipsec_kfree_skb(last); + } + SENDERR(-error); + } + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_addflow_parse: " + "calling makeroute.\n"); + + if ((error = ipsec_makeroute(&(extr->eroute->er_eaddr), + &(extr->eroute->er_emask), + extr->ips->ips_said, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid, + NULL, + &(extr->ips->ips_ident_s), + &(extr->ips->ips_ident_d)))) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_addflow_parse: " + "makeroute returned %d.\n", error); + SENDERR(-error); + } + if(first != NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_addflow_parse: " + "first=0p%p HOLD packet re-injected.\n", + first); + DEV_QUEUE_XMIT(first, first->dev, SOPRI_NORMAL); + } + if(last != NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_addflow_parse: " + "last=0p%p HOLD packet re-injected.\n", + last); + DEV_QUEUE_XMIT(last, last->dev, SOPRI_NORMAL); + } + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_addflow_parse: " + "makeroute call successful.\n"); + + if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0], + SADB_X_ADDFLOW, + satype, + 0, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid), + extensions_reply) + && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA], + SADB_EXT_SA, + extr->ips->ips_said.spi, + extr->ips->ips_replaywin, + extr->ips->ips_state, + extr->ips->ips_authalg, + extr->ips->ips_encalg, + extr->ips->ips_flags, + extr->ips->ips_ref), + extensions_reply) + && (extensions[SADB_EXT_ADDRESS_SRC] + ? pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_SRC], + SADB_EXT_ADDRESS_SRC, + 0, /*extr->ips->ips_said.proto,*/ + 0, + extr->ips->ips_addr_s), + extensions_reply) : 1) + && (extensions[SADB_EXT_ADDRESS_DST] + ? pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST], + SADB_EXT_ADDRESS_DST, + 0, /*extr->ips->ips_said.proto,*/ + 0, + extr->ips->ips_addr_d), + extensions_reply) : 1) + && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_SRC_FLOW], + SADB_X_EXT_ADDRESS_SRC_FLOW, + 0, /*extr->ips->ips_said.proto,*/ + 0, + (struct sockaddr*)&srcflow), + extensions_reply) + && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_DST_FLOW], + SADB_X_EXT_ADDRESS_DST_FLOW, + 0, /*extr->ips->ips_said.proto,*/ + 0, + (struct sockaddr*)&dstflow), + extensions_reply) + && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_SRC_MASK], + SADB_X_EXT_ADDRESS_SRC_MASK, + 0, /*extr->ips->ips_said.proto,*/ + 0, + (struct sockaddr*)&srcmask), + extensions_reply) + && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_DST_MASK], + SADB_X_EXT_ADDRESS_DST_MASK, + 0, /*extr->ips->ips_said.proto,*/ + 0, + (struct sockaddr*)&dstmask), + extensions_reply) + )) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_addflow_parse: " + "failed to build the x_addflow reply message extensions\n"); + SENDERR(-error); + } + + if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_addflow_parse: " + "failed to build the x_addflow reply message\n"); + SENDERR(-error); + } + + for(pfkey_socketsp = pfkey_open_sockets; + pfkey_socketsp; + pfkey_socketsp = pfkey_socketsp->next) { + if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_addflow_parse: " + "sending up x_addflow reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp, + error); + SENDERR(-error); + } + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_addflow_parse: " + "sending up x_addflow reply message for satype=%d(%s) (proto=%d) to socket=0p%p succeeded.\n", + satype, + satype2name(satype), + extr->ips->ips_said.proto, + pfkey_socketsp->socketp); + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_addflow_parse: " + "extr->ips cleaned up and freed.\n"); + + errlab: + if (pfkey_reply) { + pfkey_msg_free(&pfkey_reply); + } + pfkey_extensions_free(extensions_reply); + return error; +} + +DEBUG_NO_STATIC int +pfkey_x_delflow_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) +{ + int error = 0; +#ifdef CONFIG_IPSEC_DEBUG + char buf1[64], buf2[64]; +#endif /* CONFIG_IPSEC_DEBUG */ + struct sadb_ext *extensions_reply[SADB_EXT_MAX+1]; + struct sadb_msg *pfkey_reply = NULL; + struct socket_list *pfkey_socketsp; + uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype; + ip_address srcflow, dstflow, srcmask, dstmask; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_delflow_parse: .\n"); + + pfkey_extensions_init(extensions_reply); + + memset((caddr_t)&srcflow, 0, sizeof(srcflow)); + memset((caddr_t)&dstflow, 0, sizeof(dstflow)); + memset((caddr_t)&srcmask, 0, sizeof(srcmask)); + memset((caddr_t)&dstmask, 0, sizeof(dstmask)); + + if(!extr || !(extr->ips)) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_delflow_parse: " + "extr, or extr->ips is NULL, fatal\n"); + SENDERR(EINVAL); + } + + if(extr->ips->ips_flags & SADB_X_SAFLAGS_CLEARFLOW) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_delflow_parse: " + "CLEARFLOW flag set, calling cleareroutes.\n"); + if ((error = ipsec_cleareroutes())) + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_delflow_parse: " + "cleareroutes returned %d.\n", error); + SENDERR(-error); + } else { + struct sk_buff *first = NULL, *last = NULL; + + if(!(extr->eroute)) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_delflow_parse: " + "extr->eroute is NULL, fatal.\n"); + SENDERR(EINVAL); + } + + srcflow.u.v4.sin_family = AF_INET; + dstflow.u.v4.sin_family = AF_INET; + srcmask.u.v4.sin_family = AF_INET; + dstmask.u.v4.sin_family = AF_INET; + srcflow.u.v4.sin_addr = extr->eroute->er_eaddr.sen_ip_src; + dstflow.u.v4.sin_addr = extr->eroute->er_eaddr.sen_ip_dst; + srcmask.u.v4.sin_addr = extr->eroute->er_emask.sen_ip_src; + dstmask.u.v4.sin_addr = extr->eroute->er_emask.sen_ip_dst; + +#ifdef CONFIG_IPSEC_DEBUG + if (debug_pfkey) { + subnettoa(extr->eroute->er_eaddr.sen_ip_src, + extr->eroute->er_emask.sen_ip_src, 0, buf1, sizeof(buf1)); + subnettoa(extr->eroute->er_eaddr.sen_ip_dst, + extr->eroute->er_emask.sen_ip_dst, 0, buf2, sizeof(buf2)); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_delflow_parse: " + "calling breakeroute for %s->%s\n", + buf1, buf2); + } +#endif /* CONFIG_IPSEC_DEBUG */ + error = ipsec_breakroute(&(extr->eroute->er_eaddr), + &(extr->eroute->er_emask), + &first, &last); + if(error) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_delflow_parse: " + "breakeroute returned %d. first=0p%p, last=0p%p\n", + error, + first, + last); + } + if(first != NULL) { + ipsec_kfree_skb(first); + } + if(last != NULL) { + ipsec_kfree_skb(last); + } + if(error) { + SENDERR(-error); + } + } + + if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0], + SADB_X_DELFLOW, + satype, + 0, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid), + extensions_reply) + && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA], + SADB_EXT_SA, + extr->ips->ips_said.spi, + extr->ips->ips_replaywin, + extr->ips->ips_state, + extr->ips->ips_authalg, + extr->ips->ips_encalg, + extr->ips->ips_flags, + extr->ips->ips_ref), + extensions_reply) + && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_SRC_FLOW], + SADB_X_EXT_ADDRESS_SRC_FLOW, + 0, /*extr->ips->ips_said.proto,*/ + 0, + (struct sockaddr*)&srcflow), + extensions_reply) + && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_DST_FLOW], + SADB_X_EXT_ADDRESS_DST_FLOW, + 0, /*extr->ips->ips_said.proto,*/ + 0, + (struct sockaddr*)&dstflow), + extensions_reply) + && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_SRC_MASK], + SADB_X_EXT_ADDRESS_SRC_MASK, + 0, /*extr->ips->ips_said.proto,*/ + 0, + (struct sockaddr*)&srcmask), + extensions_reply) + && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_DST_MASK], + SADB_X_EXT_ADDRESS_DST_MASK, + 0, /*extr->ips->ips_said.proto,*/ + 0, + (struct sockaddr*)&dstmask), + extensions_reply) + )) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_delflow_parse: " + "failed to build the x_delflow reply message extensions\n"); + SENDERR(-error); + } + + if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_delflow_parse: " + "failed to build the x_delflow reply message\n"); + SENDERR(-error); + } + + for(pfkey_socketsp = pfkey_open_sockets; + pfkey_socketsp; + pfkey_socketsp = pfkey_socketsp->next) { + if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_delflow_parse: " + "sending up x_delflow reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp, + error); + SENDERR(-error); + } + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_delflow_parse: " + "sending up x_delflow reply message for satype=%d(%s) to socket=0p%p succeeded.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp); + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_delflow_parse: " + "extr->ips cleaned up and freed.\n"); + + errlab: + if (pfkey_reply) { + pfkey_msg_free(&pfkey_reply); + } + pfkey_extensions_free(extensions_reply); + return error; +} + +DEBUG_NO_STATIC int +pfkey_x_msg_debug_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) +{ + int error = 0; + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_x_msg_debug_parse: .\n"); + +/* errlab:*/ + return error; +} + +/* pfkey_expire expects the ipsec_sa table to be locked before being called. */ +int +pfkey_expire(struct ipsec_sa *ipsp, int hard) +{ + struct sadb_ext *extensions[SADB_EXT_MAX+1]; + struct sadb_msg *pfkey_msg = NULL; + struct socket_list *pfkey_socketsp; + int error = 0; + uint8_t satype; + + pfkey_extensions_init(extensions); + + if(!(satype = proto2satype(ipsp->ips_said.proto))) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_expire: " + "satype lookup for protocol %d lookup failed.\n", + ipsp->ips_said.proto); + SENDERR(EINVAL); + } + + if(!pfkey_open_sockets) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire: " + "no sockets listening.\n"); + SENDERR(EPROTONOSUPPORT); + } + + if (!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions[0], + SADB_EXPIRE, + satype, + 0, + ++pfkey_msg_seq, + 0), + extensions) + && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions[SADB_EXT_SA], + SADB_EXT_SA, + ipsp->ips_said.spi, + ipsp->ips_replaywin, + ipsp->ips_state, + ipsp->ips_authalg, + ipsp->ips_encalg, + ipsp->ips_flags, + ipsp->ips_ref), + extensions) + && pfkey_safe_build(error = pfkey_lifetime_build(&extensions[SADB_EXT_LIFETIME_CURRENT], + SADB_EXT_LIFETIME_CURRENT, + ipsp->ips_life.ipl_allocations.ipl_count, + ipsp->ips_life.ipl_bytes.ipl_count, + ipsp->ips_life.ipl_addtime.ipl_count, + ipsp->ips_life.ipl_usetime.ipl_count, + ipsp->ips_life.ipl_packets.ipl_count), + extensions) + && (hard ? + pfkey_safe_build(error = pfkey_lifetime_build(&extensions[SADB_EXT_LIFETIME_HARD], + SADB_EXT_LIFETIME_HARD, + ipsp->ips_life.ipl_allocations.ipl_hard, + ipsp->ips_life.ipl_bytes.ipl_hard, + ipsp->ips_life.ipl_addtime.ipl_hard, + ipsp->ips_life.ipl_usetime.ipl_hard, + ipsp->ips_life.ipl_packets.ipl_hard), + extensions) + : pfkey_safe_build(error = pfkey_lifetime_build(&extensions[SADB_EXT_LIFETIME_SOFT], + SADB_EXT_LIFETIME_SOFT, + ipsp->ips_life.ipl_allocations.ipl_soft, + ipsp->ips_life.ipl_bytes.ipl_soft, + ipsp->ips_life.ipl_addtime.ipl_soft, + ipsp->ips_life.ipl_usetime.ipl_soft, + ipsp->ips_life.ipl_packets.ipl_soft), + extensions)) + && pfkey_safe_build(error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_SRC], + SADB_EXT_ADDRESS_SRC, + 0, /* ipsp->ips_said.proto, */ + 0, + ipsp->ips_addr_s), + extensions) + && pfkey_safe_build(error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_DST], + SADB_EXT_ADDRESS_DST, + 0, /* ipsp->ips_said.proto, */ + 0, + ipsp->ips_addr_d), + extensions))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire: " + "failed to build the expire message extensions\n"); + spin_unlock(&tdb_lock); + goto errlab; + } + + if ((error = pfkey_msg_build(&pfkey_msg, extensions, EXT_BITS_OUT))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire: " + "failed to build the expire message\n"); + SENDERR(-error); + } + + for(pfkey_socketsp = pfkey_open_sockets; + pfkey_socketsp; + pfkey_socketsp = pfkey_socketsp->next) { + if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_msg))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire: " + "sending up expire message for satype=%d(%s) to socket=0p%p failed with error=%d.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp, + error); + SENDERR(-error); + } + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire: " + "sending up expire message for satype=%d(%s) (proto=%d) to socket=0p%p succeeded.\n", + satype, + satype2name(satype), + ipsp->ips_said.proto, + pfkey_socketsp->socketp); + } + + errlab: + if (pfkey_msg) { + pfkey_msg_free(&pfkey_msg); + } + pfkey_extensions_free(extensions); + return error; +} + +int +pfkey_acquire(struct ipsec_sa *ipsp) +{ + struct sadb_ext *extensions[SADB_EXT_MAX+1]; + struct sadb_msg *pfkey_msg = NULL; + struct socket_list *pfkey_socketsp; + int error = 0; + struct sadb_comb comb[] = { + /* auth; encrypt; flags; */ + /* auth_minbits; auth_maxbits; encrypt_minbits; encrypt_maxbits; */ + /* reserved; soft_allocations; hard_allocations; soft_bytes; hard_bytes; */ + /* soft_addtime; hard_addtime; soft_usetime; hard_usetime; */ + /* soft_packets; hard_packets; */ + { SADB_AALG_MD5_HMAC, SADB_EALG_3DES_CBC, SADB_SAFLAGS_PFS, + 128, 128, 168, 168, + 0, 0, 0, 0, 0, + 57600, 86400, 57600, 86400, + 0, 0 }, + { SADB_AALG_SHA1_HMAC, SADB_EALG_3DES_CBC, SADB_SAFLAGS_PFS, + 160, 160, 168, 168, + 0, 0, 0, 0, 0, + 57600, 86400, 57600, 86400, + 0, 0 } + }; + + /* XXX This should not be hard-coded. It should be taken from the spdb */ + uint8_t satype = SADB_SATYPE_ESP; + + pfkey_extensions_init(extensions); + + if((satype == 0) || (satype > SADB_SATYPE_MAX)) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_acquire: " + "SAtype=%d unspecified or unknown.\n", + satype); + SENDERR(EINVAL); + } + + if(!(pfkey_registered_sockets[satype])) { + KLIPS_PRINT(1|debug_pfkey, "klips_debug:pfkey_acquire: " + "no sockets registered for SAtype=%d(%s).\n", + satype, + satype2name(satype)); + SENDERR(EPROTONOSUPPORT); + } + + if (!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions[0], + SADB_ACQUIRE, + satype, + 0, + ++pfkey_msg_seq, + 0), + extensions) + && pfkey_safe_build(error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_SRC], + SADB_EXT_ADDRESS_SRC, + ipsp->ips_said.proto, + 0, + ipsp->ips_addr_s), + extensions) + && pfkey_safe_build(error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_DST], + SADB_EXT_ADDRESS_DST, + ipsp->ips_said.proto, + 0, + ipsp->ips_addr_d), + extensions) +#if 0 + && (ipsp->ips_addr_p + ? pfkey_safe_build(error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_PROXY], + SADB_EXT_ADDRESS_PROXY, + ipsp->ips_said.proto, + 0, + ipsp->ips_addr_p), + extensions) : 1) +#endif + && (ipsp->ips_ident_s.type != SADB_IDENTTYPE_RESERVED + ? pfkey_safe_build(error = pfkey_ident_build(&extensions[SADB_EXT_IDENTITY_SRC], + SADB_EXT_IDENTITY_SRC, + ipsp->ips_ident_s.type, + ipsp->ips_ident_s.id, + ipsp->ips_ident_s.len, + ipsp->ips_ident_s.data), + extensions) : 1) + + && (ipsp->ips_ident_d.type != SADB_IDENTTYPE_RESERVED + ? pfkey_safe_build(error = pfkey_ident_build(&extensions[SADB_EXT_IDENTITY_DST], + SADB_EXT_IDENTITY_DST, + ipsp->ips_ident_d.type, + ipsp->ips_ident_d.id, + ipsp->ips_ident_d.len, + ipsp->ips_ident_d.data), + extensions) : 1) +#if 0 + /* FIXME: This won't work yet because I have not finished + it. */ + && (ipsp->ips_sens_ + ? pfkey_safe_build(error = pfkey_sens_build(&extensions[SADB_EXT_SENSITIVITY], + ipsp->ips_sens_dpd, + ipsp->ips_sens_sens_level, + ipsp->ips_sens_sens_len, + ipsp->ips_sens_sens_bitmap, + ipsp->ips_sens_integ_level, + ipsp->ips_sens_integ_len, + ipsp->ips_sens_integ_bitmap), + extensions) : 1) +#endif + && pfkey_safe_build(error = pfkey_prop_build(&extensions[SADB_EXT_PROPOSAL], + 64, /* replay */ + sizeof(comb)/sizeof(struct sadb_comb), + &(comb[0])), + extensions) + )) { + KLIPS_PRINT(1|debug_pfkey, "klips_debug:pfkey_acquire: " + "failed to build the acquire message extensions\n"); + SENDERR(-error); + } + + if ((error = pfkey_msg_build(&pfkey_msg, extensions, EXT_BITS_OUT))) { + KLIPS_PRINT(1|debug_pfkey, "klips_debug:pfkey_acquire: " + "failed to build the acquire message\n"); + SENDERR(-error); + } + +#if KLIPS_PFKEY_ACQUIRE_LOSSAGE > 0 + if(sysctl_ipsec_regress_pfkey_lossage) { + return(0); + } +#endif + + /* this should go to all registered sockets for that satype only */ + for(pfkey_socketsp = pfkey_registered_sockets[satype]; + pfkey_socketsp; + pfkey_socketsp = pfkey_socketsp->next) { + if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_msg))) { + KLIPS_PRINT(1|debug_pfkey, "klips_debug:pfkey_acquire: " + "sending up acquire message for satype=%d(%s) to socket=0p%p failed with error=%d.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp, + error); + SENDERR(-error); + } + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_acquire: " + "sending up acquire message for satype=%d(%s) to socket=0p%p succeeded.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp); + } + + errlab: + if (pfkey_msg) { + pfkey_msg_free(&pfkey_msg); + } + pfkey_extensions_free(extensions); + return error; +} + +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL +int +pfkey_nat_t_new_mapping(struct ipsec_sa *ipsp, struct sockaddr *ipaddr, + __u16 sport) +{ + struct sadb_ext *extensions[SADB_EXT_MAX+1]; + struct sadb_msg *pfkey_msg = NULL; + struct socket_list *pfkey_socketsp; + int error = 0; + uint8_t satype = (ipsp->ips_said.proto==IPPROTO_ESP) ? SADB_SATYPE_ESP : 0; + + /* Construct SADB_X_NAT_T_NEW_MAPPING message */ + + pfkey_extensions_init(extensions); + + if((satype == 0) || (satype > SADB_SATYPE_MAX)) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_nat_t_new_mapping: " + "SAtype=%d unspecified or unknown.\n", + satype); + SENDERR(EINVAL); + } + + if(!(pfkey_registered_sockets[satype])) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_nat_t_new_mapping: " + "no sockets registered for SAtype=%d(%s).\n", + satype, + satype2name(satype)); + SENDERR(EPROTONOSUPPORT); + } + + if (!(pfkey_safe_build + (error = pfkey_msg_hdr_build(&extensions[0], SADB_X_NAT_T_NEW_MAPPING, + satype, 0, ++pfkey_msg_seq, 0), extensions) + /* SA */ + && pfkey_safe_build + (error = pfkey_sa_build(&extensions[SADB_EXT_SA], + SADB_EXT_SA, ipsp->ips_said.spi, 0, 0, 0, 0, 0), extensions) + /* ADDRESS_SRC = old addr */ + && pfkey_safe_build + (error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_SRC], + SADB_EXT_ADDRESS_SRC, ipsp->ips_said.proto, 0, ipsp->ips_addr_s), + extensions) + /* NAT_T_SPORT = old port */ + && pfkey_safe_build + (error = pfkey_x_nat_t_port_build(&extensions[SADB_X_EXT_NAT_T_SPORT], + SADB_X_EXT_NAT_T_SPORT, ipsp->ips_natt_sport), extensions) + /* ADDRESS_DST = new addr */ + && pfkey_safe_build + (error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_DST], + SADB_EXT_ADDRESS_DST, ipsp->ips_said.proto, 0, ipaddr), extensions) + /* NAT_T_DPORT = new port */ + && pfkey_safe_build + (error = pfkey_x_nat_t_port_build(&extensions[SADB_X_EXT_NAT_T_DPORT], + SADB_X_EXT_NAT_T_DPORT, sport), extensions) + )) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_nat_t_new_mapping: " + "failed to build the nat_t_new_mapping message extensions\n"); + SENDERR(-error); + } + + if ((error = pfkey_msg_build(&pfkey_msg, extensions, EXT_BITS_OUT))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_nat_t_new_mapping: " + "failed to build the nat_t_new_mapping message\n"); + SENDERR(-error); + } + + /* this should go to all registered sockets for that satype only */ + for(pfkey_socketsp = pfkey_registered_sockets[satype]; + pfkey_socketsp; + pfkey_socketsp = pfkey_socketsp->next) { + if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_msg))) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_nat_t_new_mapping: " + "sending up nat_t_new_mapping message for satype=%d(%s) to socket=%p failed with error=%d.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp, + error); + SENDERR(-error); + } + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_nat_t_new_mapping: " + "sending up nat_t_new_mapping message for satype=%d(%s) to socket=%p succeeded.\n", + satype, + satype2name(satype), + pfkey_socketsp->socketp); + } + + errlab: + if (pfkey_msg) { + pfkey_msg_free(&pfkey_msg); + } + pfkey_extensions_free(extensions); + return error; +} + +DEBUG_NO_STATIC int +pfkey_x_nat_t_new_mapping_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) +{ + /* SADB_X_NAT_T_NEW_MAPPING not used in kernel */ + return -EINVAL; +} +#endif + +DEBUG_NO_STATIC int (*ext_processors[SADB_EXT_MAX+1])(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) = +{ + NULL, /* pfkey_msg_process, */ + pfkey_sa_process, + pfkey_lifetime_process, + pfkey_lifetime_process, + pfkey_lifetime_process, + pfkey_address_process, + pfkey_address_process, + pfkey_address_process, + pfkey_key_process, + pfkey_key_process, + pfkey_ident_process, + pfkey_ident_process, + pfkey_sens_process, + pfkey_prop_process, + pfkey_supported_process, + pfkey_supported_process, + pfkey_spirange_process, + pfkey_x_kmprivate_process, + pfkey_x_satype_process, + pfkey_sa_process, + pfkey_address_process, + pfkey_address_process, + pfkey_address_process, + pfkey_address_process, + pfkey_address_process, + pfkey_x_debug_process, + pfkey_x_protocol_process +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL + , + pfkey_x_nat_t_type_process, + pfkey_x_nat_t_port_process, + pfkey_x_nat_t_port_process, + pfkey_address_process +#endif +}; + + +DEBUG_NO_STATIC int (*msg_parsers[SADB_MAX +1])(struct sock *sk, struct sadb_ext *extensions[], struct pfkey_extracted_data* extr) + = +{ + NULL, /* RESERVED */ + pfkey_getspi_parse, + pfkey_update_parse, + pfkey_add_parse, + pfkey_delete_parse, + pfkey_get_parse, + pfkey_acquire_parse, + pfkey_register_parse, + pfkey_expire_parse, + pfkey_flush_parse, + pfkey_dump_parse, + pfkey_x_promisc_parse, + pfkey_x_pchange_parse, + pfkey_x_grpsa_parse, + pfkey_x_addflow_parse, + pfkey_x_delflow_parse, + pfkey_x_msg_debug_parse +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL + , pfkey_x_nat_t_new_mapping_parse +#endif +}; + +int +pfkey_build_reply(struct sadb_msg *pfkey_msg, struct pfkey_extracted_data *extr, + struct sadb_msg **pfkey_reply) +{ + struct sadb_ext *extensions[SADB_EXT_MAX+1]; + int error = 0; + int msg_type = pfkey_msg->sadb_msg_type; + int seq = pfkey_msg->sadb_msg_seq; + + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_build_reply: " + "building reply with type: %d\n", + msg_type); + pfkey_extensions_init(extensions); + if (!extr || !extr->ips) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_build_reply: " + "bad ipsec_sa passed\n"); + return EINVAL; + } + error = pfkey_safe_build(pfkey_msg_hdr_build(&extensions[0], + msg_type, + proto2satype(extr->ips->ips_said.proto), + 0, + seq, + pfkey_msg->sadb_msg_pid), + extensions) && + (!(extensions_bitmaps[EXT_BITS_OUT][EXT_BITS_REQ][msg_type] & + 1 << SADB_EXT_SA) + || pfkey_safe_build(pfkey_sa_ref_build(&extensions[SADB_EXT_SA], + SADB_EXT_SA, + extr->ips->ips_said.spi, + extr->ips->ips_replaywin, + extr->ips->ips_state, + extr->ips->ips_authalg, + extr->ips->ips_encalg, + extr->ips->ips_flags, + extr->ips->ips_ref), + extensions)) && + (!(extensions_bitmaps[EXT_BITS_OUT][EXT_BITS_REQ][msg_type] & + 1 << SADB_EXT_LIFETIME_CURRENT) + || pfkey_safe_build(pfkey_lifetime_build(&extensions + [SADB_EXT_LIFETIME_CURRENT], + SADB_EXT_LIFETIME_CURRENT, + extr->ips->ips_life.ipl_allocations.ipl_count, + extr->ips->ips_life.ipl_bytes.ipl_count, + extr->ips->ips_life.ipl_addtime.ipl_count, + extr->ips->ips_life.ipl_usetime.ipl_count, + extr->ips->ips_life.ipl_packets.ipl_count), + extensions)) && + (!(extensions_bitmaps[EXT_BITS_OUT][EXT_BITS_REQ][msg_type] & + 1 << SADB_EXT_ADDRESS_SRC) + || pfkey_safe_build(pfkey_address_build(&extensions[SADB_EXT_ADDRESS_SRC], + SADB_EXT_ADDRESS_SRC, + extr->ips->ips_said.proto, + 0, + extr->ips->ips_addr_s), + extensions)) && + (!(extensions_bitmaps[EXT_BITS_OUT][EXT_BITS_REQ][msg_type] & + 1 << SADB_EXT_ADDRESS_DST) + || pfkey_safe_build(pfkey_address_build(&extensions[SADB_EXT_ADDRESS_DST], + SADB_EXT_ADDRESS_DST, + extr->ips->ips_said.proto, + 0, + extr->ips->ips_addr_d), + extensions)); + + if (error == 0) { + KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_build_reply: " + "building extensions failed\n"); + return EINVAL; + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_build_reply: " + "built extensions, proceed to build the message\n"); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_build_reply: " + "extensions[1]=0p%p\n", + extensions[1]); + error = pfkey_msg_build(pfkey_reply, extensions, EXT_BITS_OUT); + pfkey_extensions_free(extensions); + + return error; +} + +int +pfkey_msg_interp(struct sock *sk, struct sadb_msg *pfkey_msg, + struct sadb_msg **pfkey_reply) +{ + int error = 0; + int i; + struct sadb_ext *extensions[SADB_EXT_MAX+1]; + struct pfkey_extracted_data extr = {NULL, NULL, NULL}; + + pfkey_extensions_init(extensions); + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_msg_interp: " + "parsing message ver=%d, type=%d, errno=%d, satype=%d(%s), len=%d, res=%d, seq=%d, pid=%d.\n", + pfkey_msg->sadb_msg_version, + pfkey_msg->sadb_msg_type, + pfkey_msg->sadb_msg_errno, + pfkey_msg->sadb_msg_satype, + satype2name(pfkey_msg->sadb_msg_satype), + pfkey_msg->sadb_msg_len, + pfkey_msg->sadb_msg_reserved, + pfkey_msg->sadb_msg_seq, + pfkey_msg->sadb_msg_pid); + + extr.ips = ipsec_sa_alloc(&error); /* pass in error var by pointer */ + if(extr.ips == NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_msg_interp: " + "memory allocation error.\n"); + SENDERR(-error); + } + + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_msg_interp: " + "allocated extr->ips=0p%p.\n", + extr.ips); + + if(pfkey_msg->sadb_msg_satype > SADB_SATYPE_MAX) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_msg_interp: " + "satype %d > max %d\n", + pfkey_msg->sadb_msg_satype, + SADB_SATYPE_MAX); + SENDERR(EINVAL); + } + + switch(pfkey_msg->sadb_msg_type) { + case SADB_GETSPI: + case SADB_UPDATE: + case SADB_ADD: + case SADB_DELETE: + case SADB_X_GRPSA: + case SADB_X_ADDFLOW: + if(!(extr.ips->ips_said.proto = satype2proto(pfkey_msg->sadb_msg_satype))) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_msg_interp: " + "satype %d lookup failed.\n", + pfkey_msg->sadb_msg_satype); + SENDERR(EINVAL); + } else { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_msg_interp: " + "satype %d lookups to proto=%d.\n", + pfkey_msg->sadb_msg_satype, + extr.ips->ips_said.proto); + } + break; + default: + break; + } + + /* The NULL below causes the default extension parsers to be used */ + /* Parse the extensions */ + if((error = pfkey_msg_parse(pfkey_msg, NULL, extensions, EXT_BITS_IN))) + { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_msg_interp: " + "message parsing failed with error %d.\n", + error); + SENDERR(-error); + } + + /* Process the extensions */ + for(i=1; i <= SADB_EXT_MAX;i++) { + if(extensions[i] != NULL) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_msg_interp: " + "processing ext %d 0p%p with processor 0p%p.\n", + i, extensions[i], ext_processors[i]); + if((error = ext_processors[i](extensions[i], &extr))) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_msg_interp: " + "extension processing for type %d failed with error %d.\n", + i, + error); + SENDERR(-error); + } + + } + + } + + /* Parse the message types */ + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_msg_interp: " + "parsing message type %d(%s) with msg_parser 0p%p.\n", + pfkey_msg->sadb_msg_type, + pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type), + msg_parsers[pfkey_msg->sadb_msg_type]); + if((error = msg_parsers[pfkey_msg->sadb_msg_type](sk, extensions, &extr))) { + KLIPS_PRINT(debug_pfkey, + "klips_debug:pfkey_msg_interp: " + "message parsing failed with error %d.\n", + error); + SENDERR(-error); + } + +#if 0 + error = pfkey_build_reply(pfkey_msg, &extr, pfkey_reply); + if (error) { + *pfkey_reply = NULL; + } +#endif + errlab: + if(extr.ips != NULL) { + ipsec_sa_wipe(extr.ips); + } + if(extr.ips2 != NULL) { + ipsec_sa_wipe(extr.ips2); + } + if (extr.eroute != NULL) { + kfree(extr.eroute); + } + return(error); +} + diff --git a/linux/net/ipsec/radij.c b/linux/net/ipsec/radij.c new file mode 100644 index 000000000..7dbec8d37 --- /dev/null +++ b/linux/net/ipsec/radij.c @@ -0,0 +1,992 @@ +char radij_c_version[] = "RCSID $Id: radij.c,v 1.2 2004/06/13 19:57:50 as Exp $"; + +/* + * This file is defived from ${SRC}/sys/net/radix.c of BSD 4.4lite + * + * Variable and procedure names have been modified so that they don't + * conflict with the original BSD code, as a small number of modifications + * have been introduced and we may want to reuse this code in BSD. + * + * The `j' in `radij' is pronounced as a voiceless guttural (like a Greek + * chi or a German ch sound (as `doch', not as in `milch'), or even a + * spanish j as in Juan. It is not as far back in the throat like + * the corresponding Hebrew sound, nor is it a soft breath like the English h. + * It has nothing to do with the Dutch ij sound. + * + * Here is the appropriate copyright notice: + */ + +/* + * Copyright (c) 1988, 1989, 1993 + * The Regents of the University of California. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. + * + * @(#)radix.c 8.2 (Berkeley) 1/4/94 + */ + +/* + * Routines to build and maintain radix trees for routing lookups. + */ + +#include <linux/config.h> +#include <linux/version.h> +#include <linux/kernel.h> /* printk() */ + +#include "freeswan/ipsec_param.h" + +#ifdef MALLOC_SLAB +# include <linux/slab.h> /* kmalloc() */ +#else /* MALLOC_SLAB */ +# include <linux/malloc.h> /* kmalloc() */ +#endif /* MALLOC_SLAB */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/interrupt.h> /* mark_bh */ + +#include <linux/netdevice.h> /* struct device, and other headers */ +#include <linux/etherdevice.h> /* eth_type_trans */ +#include <linux/ip.h> /* struct iphdr */ +#include <linux/skbuff.h> +#ifdef NET_21 +# include <asm/uaccess.h> +# include <linux/in6.h> +#endif /* NET_21 */ +#include <asm/checksum.h> +#include <net/ip.h> + +#include <freeswan.h> + +#include "freeswan/radij.h" +#include "freeswan/ipsec_encap.h" +#include "freeswan/ipsec_radij.h" + +int maj_keylen; +struct radij_mask *rj_mkfreelist; +struct radij_node_head *mask_rjhead; +static int gotOddMasks; +static char *maskedKey; +static char *rj_zeroes, *rj_ones; + +#define rj_masktop (mask_rjhead->rnh_treetop) +#ifdef Bcmp +# undef Bcmp +#endif /* Bcmp */ +#define Bcmp(a, b, l) (l == 0 ? 0 : memcmp((caddr_t)(b), (caddr_t)(a), (size_t)l)) +/* + * The data structure for the keys is a radix tree with one way + * branching removed. The index rj_b at an internal node n represents a bit + * position to be tested. The tree is arranged so that all descendants + * of a node n have keys whose bits all agree up to position rj_b - 1. + * (We say the index of n is rj_b.) + * + * There is at least one descendant which has a one bit at position rj_b, + * and at least one with a zero there. + * + * A route is determined by a pair of key and mask. We require that the + * bit-wise logical and of the key and mask to be the key. + * We define the index of a route to associated with the mask to be + * the first bit number in the mask where 0 occurs (with bit number 0 + * representing the highest order bit). + * + * We say a mask is normal if every bit is 0, past the index of the mask. + * If a node n has a descendant (k, m) with index(m) == index(n) == rj_b, + * and m is a normal mask, then the route applies to every descendant of n. + * If the index(m) < rj_b, this implies the trailing last few bits of k + * before bit b are all 0, (and hence consequently true of every descendant + * of n), so the route applies to all descendants of the node as well. + * + * The present version of the code makes no use of normal routes, + * but similar logic shows that a non-normal mask m such that + * index(m) <= index(n) could potentially apply to many children of n. + * Thus, for each non-host route, we attach its mask to a list at an internal + * node as high in the tree as we can go. + */ + +struct radij_node * +rj_search(v_arg, head) + void *v_arg; + struct radij_node *head; +{ + register struct radij_node *x; + register caddr_t v; + + for (x = head, v = v_arg; x->rj_b >= 0;) { + if (x->rj_bmask & v[x->rj_off]) + x = x->rj_r; + else + x = x->rj_l; + } + return (x); +}; + +struct radij_node * +rj_search_m(v_arg, head, m_arg) + struct radij_node *head; + void *v_arg, *m_arg; +{ + register struct radij_node *x; + register caddr_t v = v_arg, m = m_arg; + + for (x = head; x->rj_b >= 0;) { + if ((x->rj_bmask & m[x->rj_off]) && + (x->rj_bmask & v[x->rj_off])) + x = x->rj_r; + else + x = x->rj_l; + } + return x; +}; + +int +rj_refines(m_arg, n_arg) + void *m_arg, *n_arg; +{ + register caddr_t m = m_arg, n = n_arg; + register caddr_t lim, lim2 = lim = n + *(u_char *)n; + int longer = (*(u_char *)n++) - (int)(*(u_char *)m++); + int masks_are_equal = 1; + + if (longer > 0) + lim -= longer; + while (n < lim) { + if (*n & ~(*m)) + return 0; + if (*n++ != *m++) + masks_are_equal = 0; + + } + while (n < lim2) + if (*n++) + return 0; + if (masks_are_equal && (longer < 0)) + for (lim2 = m - longer; m < lim2; ) + if (*m++) + return 1; + return (!masks_are_equal); +} + + +struct radij_node * +rj_match(v_arg, head) + void *v_arg; + struct radij_node_head *head; +{ + caddr_t v = v_arg; + register struct radij_node *t = head->rnh_treetop, *x; + register caddr_t cp = v, cp2, cp3; + caddr_t cplim, mstart; + struct radij_node *saved_t, *top = t; + int off = t->rj_off, vlen = *(u_char *)cp, matched_off; + + /* + * Open code rj_search(v, top) to avoid overhead of extra + * subroutine call. + */ + for (; t->rj_b >= 0; ) { + if (t->rj_bmask & cp[t->rj_off]) + t = t->rj_r; + else + t = t->rj_l; + } + /* + * See if we match exactly as a host destination + */ + KLIPS_PRINT(debug_radij, + "klips_debug:rj_match: " + "* See if we match exactly as a host destination\n"); + + cp += off; cp2 = t->rj_key + off; cplim = v + vlen; + for (; cp < cplim; cp++, cp2++) + if (*cp != *cp2) + goto on1; + /* + * This extra grot is in case we are explicitly asked + * to look up the default. Ugh! + */ + if ((t->rj_flags & RJF_ROOT) && t->rj_dupedkey) + t = t->rj_dupedkey; + return t; +on1: + matched_off = cp - v; + saved_t = t; + KLIPS_PRINT(debug_radij, + "klips_debug:rj_match: " + "** try to match a leaf, t=0p%p\n", t); + do { + if (t->rj_mask) { + /* + * Even if we don't match exactly as a hosts; + * we may match if the leaf we wound up at is + * a route to a net. + */ + cp3 = matched_off + t->rj_mask; + cp2 = matched_off + t->rj_key; + for (; cp < cplim; cp++) + if ((*cp2++ ^ *cp) & *cp3++) + break; + if (cp == cplim) + return t; + cp = matched_off + v; + } + } while ((t = t->rj_dupedkey)); + t = saved_t; + /* start searching up the tree */ + KLIPS_PRINT(debug_radij, + "klips_debug:rj_match: " + "*** start searching up the tree, t=0p%p\n", + t); + do { + register struct radij_mask *m; + + t = t->rj_p; + KLIPS_PRINT(debug_radij, + "klips_debug:rj_match: " + "**** t=0p%p\n", + t); + if ((m = t->rj_mklist)) { + /* + * After doing measurements here, it may + * turn out to be faster to open code + * rj_search_m here instead of always + * copying and masking. + */ + /* off = min(t->rj_off, matched_off); */ + off = t->rj_off; + if (matched_off < off) + off = matched_off; + mstart = maskedKey + off; + do { + cp2 = mstart; + cp3 = m->rm_mask + off; + KLIPS_PRINT(debug_radij, + "klips_debug:rj_match: " + "***** cp2=0p%p cp3=0p%p\n", + cp2, cp3); + for (cp = v + off; cp < cplim;) + *cp2++ = *cp++ & *cp3++; + x = rj_search(maskedKey, t); + while (x && x->rj_mask != m->rm_mask) + x = x->rj_dupedkey; + if (x && + (Bcmp(mstart, x->rj_key + off, + vlen - off) == 0)) + return x; + } while ((m = m->rm_mklist)); + } + } while (t != top); + KLIPS_PRINT(debug_radij, + "klips_debug:rj_match: " + "***** not found.\n"); + return 0; +}; + +#ifdef RJ_DEBUG +int rj_nodenum; +struct radij_node *rj_clist; +int rj_saveinfo; +DEBUG_NO_STATIC void traverse(struct radij_node *); +#ifdef RJ_DEBUG2 +int rj_debug = 1; +#else +int rj_debug = 0; +#endif /* RJ_DEBUG2 */ +#endif /* RJ_DEBUG */ + +struct radij_node * +rj_newpair(v, b, nodes) + void *v; + int b; + struct radij_node nodes[2]; +{ + register struct radij_node *tt = nodes, *t = tt + 1; + t->rj_b = b; t->rj_bmask = 0x80 >> (b & 7); + t->rj_l = tt; t->rj_off = b >> 3; + tt->rj_b = -1; tt->rj_key = (caddr_t)v; tt->rj_p = t; + tt->rj_flags = t->rj_flags = RJF_ACTIVE; +#ifdef RJ_DEBUG + tt->rj_info = rj_nodenum++; t->rj_info = rj_nodenum++; + tt->rj_twin = t; tt->rj_ybro = rj_clist; rj_clist = tt; +#endif /* RJ_DEBUG */ + return t; +} + +struct radij_node * +rj_insert(v_arg, head, dupentry, nodes) + void *v_arg; + struct radij_node_head *head; + int *dupentry; + struct radij_node nodes[2]; +{ + caddr_t v = v_arg; + struct radij_node *top = head->rnh_treetop; + int head_off = top->rj_off, vlen = (int)*((u_char *)v); + register struct radij_node *t = rj_search(v_arg, top); + register caddr_t cp = v + head_off; + register int b; + struct radij_node *tt; + /* + *find first bit at which v and t->rj_key differ + */ + { + register caddr_t cp2 = t->rj_key + head_off; + register int cmp_res; + caddr_t cplim = v + vlen; + + while (cp < cplim) + if (*cp2++ != *cp++) + goto on1; + *dupentry = 1; + return t; +on1: + *dupentry = 0; + cmp_res = (cp[-1] ^ cp2[-1]) & 0xff; + for (b = (cp - v) << 3; cmp_res; b--) + cmp_res >>= 1; + } + { + register struct radij_node *p, *x = top; + cp = v; + do { + p = x; + if (cp[x->rj_off] & x->rj_bmask) + x = x->rj_r; + else x = x->rj_l; + } while (b > (unsigned) x->rj_b); /* x->rj_b < b && x->rj_b >= 0 */ +#ifdef RJ_DEBUG + if (rj_debug) + printk("klips_debug:rj_insert: Going In:\n"), traverse(p); +#endif /* RJ_DEBUG */ + t = rj_newpair(v_arg, b, nodes); tt = t->rj_l; + if ((cp[p->rj_off] & p->rj_bmask) == 0) + p->rj_l = t; + else + p->rj_r = t; + x->rj_p = t; t->rj_p = p; /* frees x, p as temp vars below */ + if ((cp[t->rj_off] & t->rj_bmask) == 0) { + t->rj_r = x; + } else { + t->rj_r = tt; t->rj_l = x; + } +#ifdef RJ_DEBUG + if (rj_debug) + printk("klips_debug:rj_insert: Coming out:\n"), traverse(p); +#endif /* RJ_DEBUG */ + } + return (tt); +} + +struct radij_node * +rj_addmask(n_arg, search, skip) + int search, skip; + void *n_arg; +{ + caddr_t netmask = (caddr_t)n_arg; + register struct radij_node *x; + register caddr_t cp, cplim; + register int b, mlen, j; + int maskduplicated; + + mlen = *(u_char *)netmask; + if (search) { + x = rj_search(netmask, rj_masktop); + mlen = *(u_char *)netmask; + if (Bcmp(netmask, x->rj_key, mlen) == 0) + return (x); + } + R_Malloc(x, struct radij_node *, maj_keylen + 2 * sizeof (*x)); + if (x == 0) + return (0); + Bzero(x, maj_keylen + 2 * sizeof (*x)); + cp = (caddr_t)(x + 2); + Bcopy(netmask, cp, mlen); + netmask = cp; + x = rj_insert(netmask, mask_rjhead, &maskduplicated, x); + /* + * Calculate index of mask. + */ + cplim = netmask + mlen; + for (cp = netmask + skip; cp < cplim; cp++) + if (*(u_char *)cp != 0xff) + break; + b = (cp - netmask) << 3; + if (cp != cplim) { + if (*cp != 0) { + gotOddMasks = 1; + for (j = 0x80; j; b++, j >>= 1) + if ((j & *cp) == 0) + break; + } + } + x->rj_b = -1 - b; + return (x); +} + +#if 0 +struct radij_node * +#endif +int +rj_addroute(v_arg, n_arg, head, treenodes) + void *v_arg, *n_arg; + struct radij_node_head *head; + struct radij_node treenodes[2]; +{ + caddr_t v = (caddr_t)v_arg, netmask = (caddr_t)n_arg; + register struct radij_node *t, *x=NULL, *tt; + struct radij_node *saved_tt, *top = head->rnh_treetop; + short b = 0, b_leaf; + int mlen, keyduplicated; + caddr_t cplim; + struct radij_mask *m, **mp; + + /* + * In dealing with non-contiguous masks, there may be + * many different routes which have the same mask. + * We will find it useful to have a unique pointer to + * the mask to speed avoiding duplicate references at + * nodes and possibly save time in calculating indices. + */ + if (netmask) { + x = rj_search(netmask, rj_masktop); + mlen = *(u_char *)netmask; + if (Bcmp(netmask, x->rj_key, mlen) != 0) { + x = rj_addmask(netmask, 0, top->rj_off); + if (x == 0) + return -ENOMEM; /* (0) rgb */ + } + netmask = x->rj_key; + b = -1 - x->rj_b; + } + /* + * Deal with duplicated keys: attach node to previous instance + */ + saved_tt = tt = rj_insert(v, head, &keyduplicated, treenodes); + if (keyduplicated) { + do { + if (tt->rj_mask == netmask) + return -EEXIST; /* -ENXIO; (0) rgb */ + t = tt; + if (netmask == 0 || + (tt->rj_mask && rj_refines(netmask, tt->rj_mask))) + break; + } while ((tt = tt->rj_dupedkey)); + /* + * If the mask is not duplicated, we wouldn't + * find it among possible duplicate key entries + * anyway, so the above test doesn't hurt. + * + * We sort the masks for a duplicated key the same way as + * in a masklist -- most specific to least specific. + * This may require the unfortunate nuisance of relocating + * the head of the list. + */ + if (tt && t == saved_tt) { + struct radij_node *xx = x; + /* link in at head of list */ + (tt = treenodes)->rj_dupedkey = t; + tt->rj_flags = t->rj_flags; + tt->rj_p = x = t->rj_p; + if (x->rj_l == t) x->rj_l = tt; else x->rj_r = tt; + saved_tt = tt; x = xx; + } else { + (tt = treenodes)->rj_dupedkey = t->rj_dupedkey; + t->rj_dupedkey = tt; + } +#ifdef RJ_DEBUG + t=tt+1; tt->rj_info = rj_nodenum++; t->rj_info = rj_nodenum++; + tt->rj_twin = t; tt->rj_ybro = rj_clist; rj_clist = tt; +#endif /* RJ_DEBUG */ + t = saved_tt; + tt->rj_key = (caddr_t) v; + tt->rj_b = -1; + tt->rj_flags = t->rj_flags & ~RJF_ROOT; + } + /* + * Put mask in tree. + */ + if (netmask) { + tt->rj_mask = netmask; + tt->rj_b = x->rj_b; + } + t = saved_tt->rj_p; + b_leaf = -1 - t->rj_b; + if (t->rj_r == saved_tt) x = t->rj_l; else x = t->rj_r; + /* Promote general routes from below */ + if (x->rj_b < 0) { + if (x->rj_mask && (x->rj_b >= b_leaf) && x->rj_mklist == 0) { + MKGet(m); + if (m) { + Bzero(m, sizeof *m); + m->rm_b = x->rj_b; + m->rm_mask = x->rj_mask; + x->rj_mklist = t->rj_mklist = m; + } + } + } else if (x->rj_mklist) { + /* + * Skip over masks whose index is > that of new node + */ + for (mp = &x->rj_mklist; (m = *mp); mp = &m->rm_mklist) + if (m->rm_b >= b_leaf) + break; + t->rj_mklist = m; *mp = 0; + } + /* Add new route to highest possible ancestor's list */ + if ((netmask == 0) || (b > t->rj_b )) + return 0; /* tt rgb */ /* can't lift at all */ + b_leaf = tt->rj_b; + do { + x = t; + t = t->rj_p; + } while (b <= t->rj_b && x != top); + /* + * Search through routes associated with node to + * insert new route according to index. + * For nodes of equal index, place more specific + * masks first. + */ + cplim = netmask + mlen; + for (mp = &x->rj_mklist; (m = *mp); mp = &m->rm_mklist) { + if (m->rm_b < b_leaf) + continue; + if (m->rm_b > b_leaf) + break; + if (m->rm_mask == netmask) { + m->rm_refs++; + tt->rj_mklist = m; + return 0; /* tt rgb */ + } + if (rj_refines(netmask, m->rm_mask)) + break; + } + MKGet(m); + if (m == 0) { + printk("klips_debug:rj_addroute: " + "Mask for route not entered\n"); + return 0; /* (tt) rgb */ + } + Bzero(m, sizeof *m); + m->rm_b = b_leaf; + m->rm_mask = netmask; + m->rm_mklist = *mp; + *mp = m; + tt->rj_mklist = m; + return 0; /* tt rgb */ +} + +int +rj_delete(v_arg, netmask_arg, head, node) + void *v_arg, *netmask_arg; + struct radij_node_head *head; + struct radij_node **node; +{ + register struct radij_node *t, *p, *x, *tt; + struct radij_mask *m, *saved_m, **mp; + struct radij_node *dupedkey, *saved_tt, *top; + caddr_t v, netmask; + int b, head_off, vlen; + + v = v_arg; + netmask = netmask_arg; + x = head->rnh_treetop; + tt = rj_search(v, x); + head_off = x->rj_off; + vlen = *(u_char *)v; + saved_tt = tt; + top = x; + if (tt == 0 || + Bcmp(v + head_off, tt->rj_key + head_off, vlen - head_off)) + return -EFAULT; /* (0) rgb */ + /* + * Delete our route from mask lists. + */ + if ((dupedkey = tt->rj_dupedkey)) { + if (netmask) + netmask = rj_search(netmask, rj_masktop)->rj_key; + while (tt->rj_mask != netmask) + if ((tt = tt->rj_dupedkey) == 0) + return -ENOENT; /* -ENXIO; (0) rgb */ + } + if (tt->rj_mask == 0 || (saved_m = m = tt->rj_mklist) == 0) + goto on1; + if (m->rm_mask != tt->rj_mask) { + printk("klips_debug:rj_delete: " + "inconsistent annotation\n"); + goto on1; + } + if (--m->rm_refs >= 0) + goto on1; + b = -1 - tt->rj_b; + t = saved_tt->rj_p; + if (b > t->rj_b) + goto on1; /* Wasn't lifted at all */ + do { + x = t; + t = t->rj_p; + } while (b <= t->rj_b && x != top); + for (mp = &x->rj_mklist; (m = *mp); mp = &m->rm_mklist) + if (m == saved_m) { + *mp = m->rm_mklist; + MKFree(m); + break; + } + if (m == 0) + printk("klips_debug:rj_delete: " + "couldn't find our annotation\n"); +on1: + /* + * Eliminate us from tree + */ + if (tt->rj_flags & RJF_ROOT) + return -EFAULT; /* (0) rgb */ +#ifdef RJ_DEBUG + /* Get us out of the creation list */ + for (t = rj_clist; t && t->rj_ybro != tt; t = t->rj_ybro) {} + if (t) t->rj_ybro = tt->rj_ybro; +#endif /* RJ_DEBUG */ + t = tt->rj_p; + if (dupedkey) { + if (tt == saved_tt) { + x = dupedkey; x->rj_p = t; + if (t->rj_l == tt) t->rj_l = x; else t->rj_r = x; + } else { + for (x = p = saved_tt; p && p->rj_dupedkey != tt;) + p = p->rj_dupedkey; + if (p) p->rj_dupedkey = tt->rj_dupedkey; + else printk("klips_debug:rj_delete: " + "couldn't find us\n"); + } + t = tt + 1; + if (t->rj_flags & RJF_ACTIVE) { +#ifndef RJ_DEBUG + *++x = *t; p = t->rj_p; +#else + b = t->rj_info; *++x = *t; t->rj_info = b; p = t->rj_p; +#endif /* RJ_DEBUG */ + if (p->rj_l == t) p->rj_l = x; else p->rj_r = x; + x->rj_l->rj_p = x; x->rj_r->rj_p = x; + } + goto out; + } + if (t->rj_l == tt) x = t->rj_r; else x = t->rj_l; + p = t->rj_p; + if (p->rj_r == t) p->rj_r = x; else p->rj_l = x; + x->rj_p = p; + /* + * Demote routes attached to us. + */ + if (t->rj_mklist) { + if (x->rj_b >= 0) { + for (mp = &x->rj_mklist; (m = *mp);) + mp = &m->rm_mklist; + *mp = t->rj_mklist; + } else { + for (m = t->rj_mklist; m;) { + struct radij_mask *mm = m->rm_mklist; + if (m == x->rj_mklist && (--(m->rm_refs) < 0)) { + x->rj_mklist = 0; + MKFree(m); + } else + printk("klips_debug:rj_delete: " + "Orphaned Mask 0p%p at 0p%p\n", m, x); + m = mm; + } + } + } + /* + * We may be holding an active internal node in the tree. + */ + x = tt + 1; + if (t != x) { +#ifndef RJ_DEBUG + *t = *x; +#else + b = t->rj_info; *t = *x; t->rj_info = b; +#endif /* RJ_DEBUG */ + t->rj_l->rj_p = t; t->rj_r->rj_p = t; + p = x->rj_p; + if (p->rj_l == x) p->rj_l = t; else p->rj_r = t; + } +out: + tt->rj_flags &= ~RJF_ACTIVE; + tt[1].rj_flags &= ~RJF_ACTIVE; + *node = tt; + return 0; /* (tt) rgb */ +} + +int +rj_walktree(h, f, w) + struct radij_node_head *h; + register int (*f)(struct radij_node *,void *); + void *w; +{ + int error; + struct radij_node *base, *next; + register struct radij_node *rn; + + if(!h || !f /* || !w */) { + return -ENODATA; + } + + rn = h->rnh_treetop; + /* + * This gets complicated because we may delete the node + * while applying the function f to it, so we need to calculate + * the successor node in advance. + */ + /* First time through node, go left */ + while (rn->rj_b >= 0) + rn = rn->rj_l; + for (;;) { +#ifdef CONFIG_IPSEC_DEBUG + if(debug_radij) { + printk("klips_debug:rj_walktree: " + "for: rn=0p%p rj_b=%d rj_flags=%x", + rn, + rn->rj_b, + rn->rj_flags); + rn->rj_b >= 0 ? + printk(" node off=%x\n", + rn->rj_off) : + printk(" leaf key = %08x->%08x\n", + (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_src.s_addr), + (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_dst.s_addr)) + ; + } +#endif /* CONFIG_IPSEC_DEBUG */ + base = rn; + /* If at right child go back up, otherwise, go right */ + while (rn->rj_p->rj_r == rn && (rn->rj_flags & RJF_ROOT) == 0) + rn = rn->rj_p; + /* Find the next *leaf* since next node might vanish, too */ + for (rn = rn->rj_p->rj_r; rn->rj_b >= 0;) + rn = rn->rj_l; + next = rn; +#ifdef CONFIG_IPSEC_DEBUG + if(debug_radij) { + printk("klips_debug:rj_walktree: " + "processing leaves, rn=0p%p rj_b=%d rj_flags=%x", + rn, + rn->rj_b, + rn->rj_flags); + rn->rj_b >= 0 ? + printk(" node off=%x\n", + rn->rj_off) : + printk(" leaf key = %08x->%08x\n", + (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_src.s_addr), + (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_dst.s_addr)) + ; + } +#endif /* CONFIG_IPSEC_DEBUG */ + /* Process leaves */ + while ((rn = base)) { + base = rn->rj_dupedkey; +#ifdef CONFIG_IPSEC_DEBUG + if(debug_radij) { + printk("klips_debug:rj_walktree: " + "while: base=0p%p rn=0p%p rj_b=%d rj_flags=%x", + base, + rn, + rn->rj_b, + rn->rj_flags); + rn->rj_b >= 0 ? + printk(" node off=%x\n", + rn->rj_off) : + printk(" leaf key = %08x->%08x\n", + (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_src.s_addr), + (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_dst.s_addr)) + ; + } +#endif /* CONFIG_IPSEC_DEBUG */ + if (!(rn->rj_flags & RJF_ROOT) && (error = (*f)(rn, w))) + return (-error); + } + rn = next; + if (rn->rj_flags & RJF_ROOT) + return (0); + } + /* NOTREACHED */ +} + +int +rj_inithead(head, off) + void **head; + int off; +{ + register struct radij_node_head *rnh; + register struct radij_node *t, *tt, *ttt; + if (*head) + return (1); + R_Malloc(rnh, struct radij_node_head *, sizeof (*rnh)); + if (rnh == NULL) + return (0); + Bzero(rnh, sizeof (*rnh)); + *head = rnh; + t = rj_newpair(rj_zeroes, off, rnh->rnh_nodes); + ttt = rnh->rnh_nodes + 2; + t->rj_r = ttt; + t->rj_p = t; + tt = t->rj_l; + tt->rj_flags = t->rj_flags = RJF_ROOT | RJF_ACTIVE; + tt->rj_b = -1 - off; + *ttt = *tt; + ttt->rj_key = rj_ones; + rnh->rnh_addaddr = rj_addroute; + rnh->rnh_deladdr = rj_delete; + rnh->rnh_matchaddr = rj_match; + rnh->rnh_walktree = rj_walktree; + rnh->rnh_treetop = t; + return (1); +} + +void +rj_init() +{ + char *cp, *cplim; + + if (maj_keylen == 0) { + printk("klips_debug:rj_init: " + "radij functions require maj_keylen be set\n"); + return; + } + R_Malloc(rj_zeroes, char *, 3 * maj_keylen); + if (rj_zeroes == NULL) + panic("rj_init"); + Bzero(rj_zeroes, 3 * maj_keylen); + rj_ones = cp = rj_zeroes + maj_keylen; + maskedKey = cplim = rj_ones + maj_keylen; + while (cp < cplim) + *cp++ = -1; + if (rj_inithead((void **)&mask_rjhead, 0) == 0) + panic("rj_init 2"); +} + +void +rj_preorder(struct radij_node *rn, int l) +{ + int i; + + if (rn == NULL){ + printk("klips_debug:rj_preorder: " + "NULL pointer\n"); + return; + } + + if (rn->rj_b >= 0){ + rj_preorder(rn->rj_l, l+1); + rj_preorder(rn->rj_r, l+1); + printk("klips_debug:"); + for (i=0; i<l; i++) + printk("*"); + printk(" off = %d\n", + rn->rj_off); + } else { + printk("klips_debug:"); + for (i=0; i<l; i++) + printk("@"); + printk(" flags = %x", + (u_int)rn->rj_flags); + if (rn->rj_flags & RJF_ACTIVE) { + printk(" @key=0p%p", + rn->rj_key); + printk(" key = %08x->%08x", + (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_src.s_addr), + (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_dst.s_addr)); + printk(" @mask=0p%p", + rn->rj_mask); + if (rn->rj_mask) + printk(" mask = %08x->%08x", + (u_int)ntohl(((struct sockaddr_encap *)rn->rj_mask)->sen_ip_src.s_addr), + (u_int)ntohl(((struct sockaddr_encap *)rn->rj_mask)->sen_ip_dst.s_addr)); + if (rn->rj_dupedkey) + printk(" dupedkey = 0p%p", + rn->rj_dupedkey); + } + printk("\n"); + } +} + +#ifdef RJ_DEBUG +DEBUG_NO_STATIC void traverse(struct radij_node *p) +{ + rj_preorder(p, 0); +} +#endif /* RJ_DEBUG */ + +void +rj_dumptrees(void) +{ + rj_preorder(rnh->rnh_treetop, 0); +} + +void +rj_free_mkfreelist(void) +{ + struct radij_mask *mknp, *mknp2; + + mknp = rj_mkfreelist; + while(mknp) + { + mknp2 = mknp; + mknp = mknp->rm_mklist; + kfree(mknp2); + } +} + +int +radijcleartree(void) +{ + return rj_walktree(rnh, ipsec_rj_walker_delete, NULL); +} + +int +radijcleanup(void) +{ + int error = 0; + + error = radijcleartree(); + + rj_free_mkfreelist(); + +/* rj_walktree(mask_rjhead, ipsec_rj_walker_delete, NULL); */ + if(mask_rjhead) { + kfree(mask_rjhead); + } + + if(rj_zeroes) { + kfree(rj_zeroes); + } + + if(rnh) { + kfree(rnh); + } + + return error; +} + diff --git a/linux/net/ipsec/sysctl_net_ipsec.c b/linux/net/ipsec/sysctl_net_ipsec.c new file mode 100644 index 000000000..b494329f6 --- /dev/null +++ b/linux/net/ipsec/sysctl_net_ipsec.c @@ -0,0 +1,196 @@ +/* + * sysctl interface to net IPSEC subsystem. + * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + * + * RCSID $Id: sysctl_net_ipsec.c,v 1.1 2004/03/15 20:35:27 as Exp $ + */ + +/* -*- linux-c -*- + * + * Initiated April 3, 1998, Richard Guy Briggs <rgb@conscoop.ottawa.on.ca> + */ + +#include <linux/mm.h> +#include <linux/sysctl.h> + +#include "freeswan/ipsec_param.h" + +#ifdef CONFIG_SYSCTL + +#define NET_IPSEC 2112 /* Random number */ +#ifdef CONFIG_IPSEC_DEBUG +extern int debug_ah; +extern int debug_esp; +extern int debug_tunnel; +extern int debug_eroute; +extern int debug_spi; +extern int debug_radij; +extern int debug_netlink; +extern int debug_xform; +extern int debug_rcv; +extern int debug_pfkey; +extern int sysctl_ipsec_debug_verbose; +#ifdef CONFIG_IPSEC_IPCOMP +extern int sysctl_ipsec_debug_ipcomp; +#endif /* CONFIG_IPSEC_IPCOMP */ +#endif /* CONFIG_IPSEC_DEBUG */ + +extern int sysctl_ipsec_icmp; +extern int sysctl_ipsec_inbound_policy_check; +extern int sysctl_ipsec_tos; +int sysctl_ipsec_regress_pfkey_lossage; + +enum { +#ifdef CONFIG_IPSEC_DEBUG + NET_IPSEC_DEBUG_AH=1, + NET_IPSEC_DEBUG_ESP=2, + NET_IPSEC_DEBUG_TUNNEL=3, + NET_IPSEC_DEBUG_EROUTE=4, + NET_IPSEC_DEBUG_SPI=5, + NET_IPSEC_DEBUG_RADIJ=6, + NET_IPSEC_DEBUG_NETLINK=7, + NET_IPSEC_DEBUG_XFORM=8, + NET_IPSEC_DEBUG_RCV=9, + NET_IPSEC_DEBUG_PFKEY=10, + NET_IPSEC_DEBUG_VERBOSE=11, + NET_IPSEC_DEBUG_IPCOMP=12, +#endif /* CONFIG_IPSEC_DEBUG */ + NET_IPSEC_ICMP=13, + NET_IPSEC_INBOUND_POLICY_CHECK=14, + NET_IPSEC_TOS=15, + NET_IPSEC_REGRESS_PFKEY_LOSSAGE=16, +}; + +static ctl_table ipsec_table[] = { +#ifdef CONFIG_IPSEC_DEBUG + { NET_IPSEC_DEBUG_AH, "debug_ah", &debug_ah, + sizeof(int), 0644, NULL, &proc_dointvec}, + { NET_IPSEC_DEBUG_ESP, "debug_esp", &debug_esp, + sizeof(int), 0644, NULL, &proc_dointvec}, + { NET_IPSEC_DEBUG_TUNNEL, "debug_tunnel", &debug_tunnel, + sizeof(int), 0644, NULL, &proc_dointvec}, + { NET_IPSEC_DEBUG_EROUTE, "debug_eroute", &debug_eroute, + sizeof(int), 0644, NULL, &proc_dointvec}, + { NET_IPSEC_DEBUG_SPI, "debug_spi", &debug_spi, + sizeof(int), 0644, NULL, &proc_dointvec}, + { NET_IPSEC_DEBUG_RADIJ, "debug_radij", &debug_radij, + sizeof(int), 0644, NULL, &proc_dointvec}, + { NET_IPSEC_DEBUG_NETLINK, "debug_netlink", &debug_netlink, + sizeof(int), 0644, NULL, &proc_dointvec}, + { NET_IPSEC_DEBUG_XFORM, "debug_xform", &debug_xform, + sizeof(int), 0644, NULL, &proc_dointvec}, + { NET_IPSEC_DEBUG_RCV, "debug_rcv", &debug_rcv, + sizeof(int), 0644, NULL, &proc_dointvec}, + { NET_IPSEC_DEBUG_PFKEY, "debug_pfkey", &debug_pfkey, + sizeof(int), 0644, NULL, &proc_dointvec}, + { NET_IPSEC_DEBUG_VERBOSE, "debug_verbose",&sysctl_ipsec_debug_verbose, + sizeof(int), 0644, NULL, &proc_dointvec}, +#ifdef CONFIG_IPSEC_IPCOMP + { NET_IPSEC_DEBUG_IPCOMP, "debug_ipcomp", &sysctl_ipsec_debug_ipcomp, + sizeof(int), 0644, NULL, &proc_dointvec}, +#endif /* CONFIG_IPSEC_IPCOMP */ + +#ifdef CONFIG_IPSEC_REGRESS + { NET_IPSEC_REGRESS_PFKEY_LOSSAGE, "pfkey_lossage", + &sysctl_ipsec_regress_pfkey_lossage, + sizeof(int), 0644, NULL, &proc_dointvec}, +#endif /* CONFIG_IPSEC_REGRESS */ + +#endif /* CONFIG_IPSEC_DEBUG */ + { NET_IPSEC_ICMP, "icmp", &sysctl_ipsec_icmp, + sizeof(int), 0644, NULL, &proc_dointvec}, + { NET_IPSEC_INBOUND_POLICY_CHECK, "inbound_policy_check", &sysctl_ipsec_inbound_policy_check, + sizeof(int), 0644, NULL, &proc_dointvec}, + { NET_IPSEC_TOS, "tos", &sysctl_ipsec_tos, + sizeof(int), 0644, NULL, &proc_dointvec}, + {0} +}; + +static ctl_table ipsec_net_table[] = { + { NET_IPSEC, "ipsec", NULL, 0, 0555, ipsec_table }, + { 0 } +}; + +static ctl_table ipsec_root_table[] = { + { CTL_NET, "net", NULL, 0, 0555, ipsec_net_table }, + { 0 } +}; + +static struct ctl_table_header *ipsec_table_header; + +int ipsec_sysctl_register(void) +{ + ipsec_table_header = register_sysctl_table(ipsec_root_table, 0); + if (!ipsec_table_header) { + return -ENOMEM; + } + return 0; +} + +void ipsec_sysctl_unregister(void) +{ + unregister_sysctl_table(ipsec_table_header); +} + +#endif /* CONFIG_SYSCTL */ + +/* + * $Log: sysctl_net_ipsec.c,v $ + * Revision 1.1 2004/03/15 20:35:27 as + * added files from freeswan-2.04-x509-1.5.3 + * + * Revision 1.15 2002/04/24 07:55:32 mcr + * #include patches and Makefiles for post-reorg compilation. + * + * Revision 1.14 2002/04/24 07:36:35 mcr + * Moved from ./klips/net/ipsec/sysctl_net_ipsec.c,v + * + * Revision 1.13 2002/01/12 02:58:32 mcr + * first regression test causes acquire messages to be lost + * 100% of the time. This is to help testing of pluto. + * + * Revision 1.12 2001/06/14 19:35:13 rgb + * Update copyright date. + * + * Revision 1.11 2001/02/26 19:58:13 rgb + * Drop sysctl_ipsec_{no_eroute_pass,opportunistic}, replaced by magic SAs. + * + * Revision 1.10 2000/09/16 01:50:15 rgb + * Protect sysctl_ipsec_debug_ipcomp with compiler defines too so that the + * linker won't blame rj_delete() for missing symbols. ;-> Damn statics... + * + * Revision 1.9 2000/09/15 23:17:51 rgb + * Moved stuff around to compile with debug off. + * + * Revision 1.8 2000/09/15 11:37:02 rgb + * Merge in heavily modified Svenning Soerensen's <svenning@post5.tele.dk> + * IPCOMP zlib deflate code. + * + * Revision 1.7 2000/09/15 07:37:15 rgb + * Munged silly log comment that was causing a warning. + * + * Revision 1.6 2000/09/15 04:58:23 rgb + * Added tos runtime switch. + * Removed 'sysctl_ipsec_' prefix from /proc/sys/net/ipsec/ filenames. + * + * Revision 1.5 2000/09/12 03:25:28 rgb + * Filled in and implemented sysctl. + * + * Revision 1.4 1999/04/11 00:29:03 henry + * GPL boilerplate + * + * Revision 1.3 1999/04/06 04:54:29 rgb + * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes + * patch shell fixes. + * + */ diff --git a/linux/net/ipsec/tagsfile.mak b/linux/net/ipsec/tagsfile.mak new file mode 100644 index 000000000..b2a5126a2 --- /dev/null +++ b/linux/net/ipsec/tagsfile.mak @@ -0,0 +1,6 @@ +TAGS: + etags *.c ../../include/*.h ../../include/freeswan/*.h + ctags *.c ../../include/*.h ../../include/freeswan/*.h + + + |