summaryrefslogtreecommitdiff
path: root/src/libcharon/plugins/kernel_wfp
diff options
context:
space:
mode:
authorYves-Alexis Perez <corsac@debian.org>2014-07-11 07:23:31 +0200
committerYves-Alexis Perez <corsac@debian.org>2014-07-11 07:23:31 +0200
commit113920a63557a9497e6fb8d7efd60e2fcde1df09 (patch)
tree7ea97040bf40a91ae0523ca9d580edad42c77917 /src/libcharon/plugins/kernel_wfp
parentb6869973db5f522dc9e2c20155ffd6e32152f197 (diff)
parent81c63b0eed39432878f78727f60a1e7499645199 (diff)
downloadvyos-strongswan-113920a63557a9497e6fb8d7efd60e2fcde1df09.tar.gz
vyos-strongswan-113920a63557a9497e6fb8d7efd60e2fcde1df09.zip
Merge tag 'upstream/5.2.0'
Upstream version 5.2.0
Diffstat (limited to 'src/libcharon/plugins/kernel_wfp')
-rw-r--r--src/libcharon/plugins/kernel_wfp/Makefile.am33
-rw-r--r--src/libcharon/plugins/kernel_wfp/Makefile.in801
-rw-r--r--src/libcharon/plugins/kernel_wfp/ipsecdump.c666
-rw-r--r--src/libcharon/plugins/kernel_wfp/kernel_wfp_compat.c157
-rw-r--r--src/libcharon/plugins/kernel_wfp/kernel_wfp_compat.h205
-rw-r--r--src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c2551
-rw-r--r--src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.h47
-rw-r--r--src/libcharon/plugins/kernel_wfp/kernel_wfp_plugin.c77
-rw-r--r--src/libcharon/plugins/kernel_wfp/kernel_wfp_plugin.h43
-rw-r--r--src/libcharon/plugins/kernel_wfp/mingw-w64-4.8.1.diff26
10 files changed, 4606 insertions, 0 deletions
diff --git a/src/libcharon/plugins/kernel_wfp/Makefile.am b/src/libcharon/plugins/kernel_wfp/Makefile.am
new file mode 100644
index 000000000..85e5089a3
--- /dev/null
+++ b/src/libcharon/plugins/kernel_wfp/Makefile.am
@@ -0,0 +1,33 @@
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/src/libstrongswan \
+ -I$(top_srcdir)/src/libhydra \
+ -I$(top_srcdir)/src/libcharon
+
+AM_CFLAGS = \
+ $(PLUGIN_CFLAGS)
+
+if MONOLITHIC
+noinst_LTLIBRARIES = libstrongswan-kernel-wfp.la
+else
+plugin_LTLIBRARIES = libstrongswan-kernel-wfp.la
+endif
+
+libstrongswan_kernel_wfp_la_SOURCES = \
+ kernel_wfp_plugin.h kernel_wfp_plugin.c \
+ kernel_wfp_compat.c kernel_wfp_compat.h \
+ kernel_wfp_ipsec.h kernel_wfp_ipsec.c
+
+libstrongswan_kernel_wfp_la_LDFLAGS = -module -avoid-version
+libstrongswan_kernel_wfp_la_LIBADD = -lfwpuclnt
+
+
+noinst_PROGRAMS = ipsecdump
+
+ipsecdump_SOURCES = \
+ ipsecdump.c
+ipsecdump_LDADD = \
+ libstrongswan-kernel-wfp.la \
+ $(top_builddir)/src/libstrongswan/libstrongswan.la
+
+
+EXTRA_DIST = mingw-w64-4.8.1.diff
diff --git a/src/libcharon/plugins/kernel_wfp/Makefile.in b/src/libcharon/plugins/kernel_wfp/Makefile.in
new file mode 100644
index 000000000..ff987f8d4
--- /dev/null
+++ b/src/libcharon/plugins/kernel_wfp/Makefile.in
@@ -0,0 +1,801 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+noinst_PROGRAMS = ipsecdump$(EXEEXT)
+subdir = src/libcharon/plugins/kernel_wfp
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+ $(top_srcdir)/depcomp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/config/libtool.m4 \
+ $(top_srcdir)/m4/config/ltoptions.m4 \
+ $(top_srcdir)/m4/config/ltsugar.m4 \
+ $(top_srcdir)/m4/config/ltversion.m4 \
+ $(top_srcdir)/m4/config/lt~obsolete.m4 \
+ $(top_srcdir)/m4/macros/split-package-version.m4 \
+ $(top_srcdir)/m4/macros/with.m4 \
+ $(top_srcdir)/m4/macros/enable-disable.m4 \
+ $(top_srcdir)/m4/macros/add-plugin.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(plugindir)"
+LTLIBRARIES = $(noinst_LTLIBRARIES) $(plugin_LTLIBRARIES)
+libstrongswan_kernel_wfp_la_DEPENDENCIES =
+am_libstrongswan_kernel_wfp_la_OBJECTS = kernel_wfp_plugin.lo \
+ kernel_wfp_compat.lo kernel_wfp_ipsec.lo
+libstrongswan_kernel_wfp_la_OBJECTS = \
+ $(am_libstrongswan_kernel_wfp_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libstrongswan_kernel_wfp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libstrongswan_kernel_wfp_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+@MONOLITHIC_FALSE@am_libstrongswan_kernel_wfp_la_rpath = -rpath \
+@MONOLITHIC_FALSE@ $(plugindir)
+@MONOLITHIC_TRUE@am_libstrongswan_kernel_wfp_la_rpath =
+PROGRAMS = $(noinst_PROGRAMS)
+am_ipsecdump_OBJECTS = ipsecdump.$(OBJEXT)
+ipsecdump_OBJECTS = $(am_ipsecdump_OBJECTS)
+ipsecdump_DEPENDENCIES = libstrongswan-kernel-wfp.la \
+ $(top_builddir)/src/libstrongswan/libstrongswan.la
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libstrongswan_kernel_wfp_la_SOURCES) $(ipsecdump_SOURCES)
+DIST_SOURCES = $(libstrongswan_kernel_wfp_la_SOURCES) \
+ $(ipsecdump_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BFDLIB = @BFDLIB@
+BTLIB = @BTLIB@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+COVERAGE_CFLAGS = @COVERAGE_CFLAGS@
+COVERAGE_LDFLAGS = @COVERAGE_LDFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLIB = @DLLIB@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GPERF = @GPERF@
+GPRBUILD = @GPRBUILD@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MYSQLCFLAG = @MYSQLCFLAG@
+MYSQLCONFIG = @MYSQLCONFIG@
+MYSQLLIB = @MYSQLLIB@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENSSL_LIB = @OPENSSL_LIB@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_BUILD = @PACKAGE_VERSION_BUILD@
+PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@
+PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@
+PACKAGE_VERSION_REVIEW = @PACKAGE_VERSION_REVIEW@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PLUGIN_CFLAGS = @PLUGIN_CFLAGS@
+PTHREADLIB = @PTHREADLIB@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RTLIB = @RTLIB@
+RUBY = @RUBY@
+RUBYINCLUDE = @RUBYINCLUDE@
+RUBYLIB = @RUBYLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKLIB = @SOCKLIB@
+STRIP = @STRIP@
+UNWINDLIB = @UNWINDLIB@
+VERSION = @VERSION@
+YACC = @YACC@
+YFLAGS = @YFLAGS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+aikgen_plugins = @aikgen_plugins@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+attest_plugins = @attest_plugins@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+c_plugins = @c_plugins@
+charon_natt_port = @charon_natt_port@
+charon_plugins = @charon_plugins@
+charon_udp_port = @charon_udp_port@
+clearsilver_LIBS = @clearsilver_LIBS@
+cmd_plugins = @cmd_plugins@
+datadir = @datadir@
+datarootdir = @datarootdir@
+dbusservicedir = @dbusservicedir@
+dev_headers = @dev_headers@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+fips_mode = @fips_mode@
+gtk_CFLAGS = @gtk_CFLAGS@
+gtk_LIBS = @gtk_LIBS@
+h_plugins = @h_plugins@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+imcvdir = @imcvdir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+ipsec_script = @ipsec_script@
+ipsec_script_upper = @ipsec_script_upper@
+ipsecdir = @ipsecdir@
+ipsecgroup = @ipsecgroup@
+ipseclibdir = @ipseclibdir@
+ipsecuser = @ipsecuser@
+libdir = @libdir@
+libexecdir = @libexecdir@
+linux_headers = @linux_headers@
+localedir = @localedir@
+localstatedir = @localstatedir@
+maemo_CFLAGS = @maemo_CFLAGS@
+maemo_LIBS = @maemo_LIBS@
+manager_plugins = @manager_plugins@
+mandir = @mandir@
+medsrv_plugins = @medsrv_plugins@
+mkdir_p = @mkdir_p@
+nm_CFLAGS = @nm_CFLAGS@
+nm_LIBS = @nm_LIBS@
+nm_ca_dir = @nm_ca_dir@
+nm_plugins = @nm_plugins@
+oldincludedir = @oldincludedir@
+pcsclite_CFLAGS = @pcsclite_CFLAGS@
+pcsclite_LIBS = @pcsclite_LIBS@
+pdfdir = @pdfdir@
+piddir = @piddir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+pki_plugins = @pki_plugins@
+plugindir = @plugindir@
+pool_plugins = @pool_plugins@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+random_device = @random_device@
+resolv_conf = @resolv_conf@
+routing_table = @routing_table@
+routing_table_prio = @routing_table_prio@
+s_plugins = @s_plugins@
+sbindir = @sbindir@
+scepclient_plugins = @scepclient_plugins@
+scripts_plugins = @scripts_plugins@
+sharedstatedir = @sharedstatedir@
+soup_CFLAGS = @soup_CFLAGS@
+soup_LIBS = @soup_LIBS@
+srcdir = @srcdir@
+starter_plugins = @starter_plugins@
+strongswan_conf = @strongswan_conf@
+strongswan_options = @strongswan_options@
+swanctldir = @swanctldir@
+sysconfdir = @sysconfdir@
+systemdsystemunitdir = @systemdsystemunitdir@
+t_plugins = @t_plugins@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+urandom_device = @urandom_device@
+xml_CFLAGS = @xml_CFLAGS@
+xml_LIBS = @xml_LIBS@
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/src/libstrongswan \
+ -I$(top_srcdir)/src/libhydra \
+ -I$(top_srcdir)/src/libcharon
+
+AM_CFLAGS = \
+ $(PLUGIN_CFLAGS)
+
+@MONOLITHIC_TRUE@noinst_LTLIBRARIES = libstrongswan-kernel-wfp.la
+@MONOLITHIC_FALSE@plugin_LTLIBRARIES = libstrongswan-kernel-wfp.la
+libstrongswan_kernel_wfp_la_SOURCES = \
+ kernel_wfp_plugin.h kernel_wfp_plugin.c \
+ kernel_wfp_compat.c kernel_wfp_compat.h \
+ kernel_wfp_ipsec.h kernel_wfp_ipsec.c
+
+libstrongswan_kernel_wfp_la_LDFLAGS = -module -avoid-version
+libstrongswan_kernel_wfp_la_LIBADD = -lfwpuclnt
+ipsecdump_SOURCES = \
+ ipsecdump.c
+
+ipsecdump_LDADD = \
+ libstrongswan-kernel-wfp.la \
+ $(top_builddir)/src/libstrongswan/libstrongswan.la
+
+EXTRA_DIST = mingw-w64-4.8.1.diff
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/libcharon/plugins/kernel_wfp/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/libcharon/plugins/kernel_wfp/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(plugindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(plugindir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(plugindir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(plugindir)"; \
+ }
+
+uninstall-pluginLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(plugindir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(plugindir)/$$f"; \
+ done
+
+clean-pluginLTLIBRARIES:
+ -test -z "$(plugin_LTLIBRARIES)" || rm -f $(plugin_LTLIBRARIES)
+ @list='$(plugin_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libstrongswan-kernel-wfp.la: $(libstrongswan_kernel_wfp_la_OBJECTS) $(libstrongswan_kernel_wfp_la_DEPENDENCIES) $(EXTRA_libstrongswan_kernel_wfp_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libstrongswan_kernel_wfp_la_LINK) $(am_libstrongswan_kernel_wfp_la_rpath) $(libstrongswan_kernel_wfp_la_OBJECTS) $(libstrongswan_kernel_wfp_la_LIBADD) $(LIBS)
+
+clean-noinstPROGRAMS:
+ @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+ipsecdump$(EXEEXT): $(ipsecdump_OBJECTS) $(ipsecdump_DEPENDENCIES) $(EXTRA_ipsecdump_DEPENDENCIES)
+ @rm -f ipsecdump$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(ipsecdump_OBJECTS) $(ipsecdump_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipsecdump.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel_wfp_compat.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel_wfp_ipsec.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel_wfp_plugin.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(plugindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ clean-noinstPROGRAMS clean-pluginLTLIBRARIES mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-pluginLTLIBRARIES
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-pluginLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \
+ clean-pluginLTLIBRARIES cscopelist-am ctags ctags-am distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-pluginLTLIBRARIES \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am \
+ uninstall-pluginLTLIBRARIES
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/libcharon/plugins/kernel_wfp/ipsecdump.c b/src/libcharon/plugins/kernel_wfp/ipsecdump.c
new file mode 100644
index 000000000..7ca7df5a1
--- /dev/null
+++ b/src/libcharon/plugins/kernel_wfp/ipsecdump.c
@@ -0,0 +1,666 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 revosec AG
+ *
+ * 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.
+ */
+
+/* Windows 7, for some fwpmu.h functionality */
+#define _WIN32_WINNT 0x0601
+
+#include "kernel_wfp_compat.h"
+
+#include <library.h>
+
+ENUM(auth_type_names, IPSEC_AUTH_MD5, IPSEC_AUTH_AES_256,
+ "MD5",
+ "SHA1",
+ "SHA256",
+ "AES128",
+ "AES192",
+ "AES256",
+);
+
+ENUM(auth_config_names, 0, 5,
+ "HMAC96",
+ "HMAC96",
+ "HMAC128",
+ "GMAC",
+ "GMAC",
+ "GMAC",
+);
+
+ENUM(cipher_type_names, IPSEC_CIPHER_TYPE_DES, IPSEC_CIPHER_TYPE_AES_256,
+ "DES",
+ "3DES",
+ "AES128",
+ "AES192",
+ "AES256",
+);
+
+ENUM(cipher_config_names, 1, 8,
+ "CBC",
+ "CBC",
+ "CBC",
+ "CBC",
+ "CBC",
+ "GCM",
+ "GCM",
+ "GCM",
+);
+
+ENUM(match_type_names, FWP_MATCH_EQUAL, FWP_MATCH_NOT_EQUAL,
+ "equals",
+ "greater",
+ "less than",
+ "greater or equal than",
+ "less or equal than",
+ "in range",
+ "has all flags set",
+ "has any flags set",
+ "has none flags set",
+ "equals case insensitive",
+ "not equal",
+);
+
+ENUM(traffic_type_names, IPSEC_TRAFFIC_TYPE_TRANSPORT, IPSEC_TRAFFIC_TYPE_TUNNEL,
+ "Transport",
+ "Tunnel",
+);
+
+/**
+ * Print a GUID to a static buffer
+ */
+static char *guid2string(GUID *guid)
+{
+ static char buf[64];
+
+ snprintf(buf, sizeof(buf),
+ "%08x,%04x,%04x%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x",
+ guid->Data1, guid->Data2, guid->Data3,
+ guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
+ guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
+
+ return buf;
+}
+
+/**
+ * Convert filter condition key GUID to some known strings
+ */
+static char* cond2name(GUID *guid, bool *address)
+{
+ struct {
+ GUID guid;
+ char *name;
+ bool address;
+ } map[] = {
+ { FWPM_CONDITION_IP_LOCAL_ADDRESS, "local address", TRUE},
+ { FWPM_CONDITION_IP_REMOTE_ADDRESS, "remote address", TRUE},
+ { FWPM_CONDITION_IP_SOURCE_ADDRESS, "source address", TRUE},
+ { FWPM_CONDITION_IP_DESTINATION_ADDRESS, "destination address", TRUE},
+ { FWPM_CONDITION_IP_LOCAL_PORT, "local port", FALSE},
+ { FWPM_CONDITION_IP_REMOTE_PORT, "remote port", FALSE},
+ { FWPM_CONDITION_IP_PROTOCOL, "protocol", FALSE},
+ { FWPM_CONDITION_ICMP_CODE, "icmp code", FALSE},
+ { FWPM_CONDITION_ICMP_TYPE, "icmp type", FALSE},
+ };
+ int i;
+
+ for (i = 0; i < countof(map); i++)
+ {
+ if (memeq(&map[i].guid, guid, sizeof(GUID)))
+ {
+ *address = map[i].address;
+ return map[i].name;
+ }
+ }
+ *address = FALSE;
+ return guid2string(guid);
+}
+
+/**
+ * Print a host from raw data and IP version
+ */
+static void print_host(FWP_IP_VERSION version, void *data)
+{
+ host_t *host = NULL;
+ UINT32 ints[4];
+
+ switch (version)
+ {
+ case FWP_IP_VERSION_V4:
+ ints[0] = untoh32(data);
+ host = host_create_from_chunk(AF_INET, chunk_from_thing(ints[0]), 0);
+ break;
+ case FWP_IP_VERSION_V6:
+ ints[3] = untoh32(data);
+ ints[2] = untoh32(data + 4);
+ ints[1] = untoh32(data + 8);
+ ints[0] = untoh32(data + 12);
+ host = host_create_from_chunk(AF_INET6, chunk_from_thing(ints), 0);
+ break;
+ default:
+ break;
+ }
+ if (host)
+ {
+ printf("%H", host);
+ host->destroy(host);
+ }
+}
+
+/**
+ * Print IPSEC_SA_AUTH_INFORMATION0
+ */
+static void print_auth(IPSEC_SA_AUTH_INFORMATION0 *a)
+{
+ printf("%N-%N",
+ auth_type_names, a->authTransform.authTransformId.authType,
+ auth_config_names, a->authTransform.authTransformId.authConfig);
+}
+
+/**
+ * Print IPSEC_SA_CIPHER_INFORMATION0
+ */
+static void print_cipher(IPSEC_SA_CIPHER_INFORMATION0 *c)
+{
+ printf("%N-%N",
+ cipher_type_names, c->cipherTransform.cipherTransformId.cipherType,
+ cipher_config_names, c->cipherTransform.cipherTransformId.cipherConfig);
+}
+
+/**
+ * Print IPsec SA transform
+ */
+static void list_sa(HANDLE engine, IPSEC_SA0 *sa)
+{
+ printf(" SPI 0x%08x\n", sa->spi);
+ switch (sa->saTransformType)
+ {
+ case IPSEC_TRANSFORM_AH:
+ printf(" AH: ");
+ print_auth(sa->ahInformation);
+ break;
+ case IPSEC_TRANSFORM_ESP_AUTH:
+ printf(" ESP: ");
+ print_auth(sa->espAuthInformation);
+ break;
+ case IPSEC_TRANSFORM_ESP_CIPHER:
+ printf(" ESP: ");
+ print_cipher(sa->espCipherInformation);
+ break;
+ case IPSEC_TRANSFORM_ESP_AUTH_AND_CIPHER:
+ printf(" ESP: ");
+ print_auth(&sa->espAuthAndCipherInformation->saAuthInformation);
+ printf(", ");
+ print_cipher(&sa->espAuthAndCipherInformation->saCipherInformation);
+ break;
+ default:
+ printf(" (Transform %d)", sa->saTransformType);
+ break;
+ }
+ printf("\n");
+}
+
+/**
+ * List a filter condition value, optionally as IP address
+ */
+static void print_value(FWP_CONDITION_VALUE0 *value, bool address)
+{
+ chunk_t chunk;
+
+ switch (value->type)
+ {
+ case FWP_EMPTY:
+ printf("empty");
+ break;
+ case FWP_UINT8:
+ printf("%u", value->uint8);
+ break;
+ case FWP_UINT16:
+ printf("%u", value->uint16);
+ break;
+ case FWP_UINT32:
+ if (address)
+ {
+ print_host(FWP_IP_VERSION_V4, &value->uint32);
+ }
+ else
+ {
+ printf("%u", value->uint32);
+ }
+ break;
+ case FWP_UINT64:
+ printf("%llu", value->uint64);
+ break;
+ case FWP_INT8:
+ printf("%d", value->int8);
+ break;
+ case FWP_INT16:
+ printf("%d", value->int16);
+ break;
+ case FWP_INT32:
+ printf("%d", value->int32);
+ break;
+ case FWP_INT64:
+ printf("%lld", value->int64);
+ break;
+ case FWP_FLOAT:
+ printf("%f", value->float32);
+ break;
+ case FWP_DOUBLE:
+ printf("%lf", value->double64);
+ break;
+ case FWP_BYTE_ARRAY16_TYPE:
+ if (address)
+ {
+ print_host(FWP_IP_VERSION_V6, value->byteArray16);
+ }
+ else
+ {
+ chunk = chunk_create((u_char*)value->byteArray16, 16);
+ printf("%#B", &chunk);
+ }
+ break;
+ case FWP_BYTE_BLOB_TYPE:
+ chunk = chunk_create(value->byteBlob->data, value->byteBlob->size);
+ printf("%#B", &chunk);
+ break;
+ case FWP_V4_ADDR_MASK:
+ print_host(FWP_IP_VERSION_V4, &value->v4AddrMask->addr);
+ printf("/");
+ print_host(FWP_IP_VERSION_V4, &value->v4AddrMask->mask);
+ break;
+ case FWP_V6_ADDR_MASK:
+ print_host(FWP_IP_VERSION_V6, &value->v6AddrMask->addr);
+ printf("/%u", &value->v6AddrMask->prefixLength);
+ break;
+ case FWP_RANGE_TYPE:
+ print_value((FWP_CONDITION_VALUE0*)&value->rangeValue->valueLow,
+ address);
+ printf(" - ");
+ print_value((FWP_CONDITION_VALUE0*)&value->rangeValue->valueHigh,
+ address);
+ break;
+ default:
+ printf("(unsupported)");
+ break;
+ }
+}
+
+/**
+ * List a filter condition
+ */
+static void list_cond(HANDLE engine, FWPM_FILTER_CONDITION0 *cond)
+{
+ bool address;
+
+ printf(" '%s' %N '", cond2name(&cond->fieldKey, &address),
+ match_type_names, cond->matchType);
+ print_value(&cond->conditionValue, address);
+ printf("'\n");
+}
+
+/**
+ * Print IPsec SA details
+ */
+static void list_details(HANDLE engine, IPSEC_SA_DETAILS1 *details)
+{
+ int i;
+
+ printf(" %sbound SA: ",
+ details->saDirection == FWP_DIRECTION_INBOUND ? "In" : "Out");
+ print_host(details->traffic.ipVersion, &details->traffic.localV4Address);
+ printf(" %s ", details->saDirection == FWP_DIRECTION_INBOUND ? "<-" : "->");
+ print_host(details->traffic.ipVersion, &details->traffic.remoteV4Address);
+ printf("\n %N, flags: 0x%06x, lifetime: %us\n",
+ traffic_type_names, details->traffic.trafficType,
+ details->saBundle.flags, details->saBundle.lifetime.lifetimeSeconds);
+ if (details->udpEncapsulation)
+ {
+ printf(" UDP encap ports %u - %u\n",
+ details->udpEncapsulation->localUdpEncapPort,
+ details->udpEncapsulation->remoteUdpEncapPort);
+ }
+ for (i = 0; i < details->saBundle.numSAs; i++)
+ {
+ list_sa(engine, &details->saBundle.saList[i]);
+ }
+ printf(" Filter ID %llu\n", details->transportFilter->filterId);
+ for (i = 0; i < details->transportFilter->numFilterConditions; i++)
+ {
+ list_cond(engine, &details->transportFilter->filterCondition[i]);
+ }
+}
+
+/**
+ * List installed SA contexts
+ */
+static bool list_contexts(HANDLE engine)
+{
+ HANDLE handle;
+ UINT32 returned;
+ DWORD res;
+ IPSEC_SA_CONTEXT1 **entries;
+
+ res = IPsecSaContextCreateEnumHandle0(engine, NULL, &handle);
+ if (res != ERROR_SUCCESS)
+ {
+ fprintf(stderr, "IPsecSaContextCreateEnumHandle0(): 0x%08x\n", res);
+ return FALSE;
+ }
+
+ while (TRUE)
+ {
+ res = IPsecSaContextEnum1(engine, handle, 1, &entries, &returned);
+ if (res != ERROR_SUCCESS)
+ {
+ fprintf(stderr, "IPsecSaContextEnum1(): 0x%08x\n", res);
+ IPsecSaContextDestroyEnumHandle0(engine, handle);
+ return FALSE;
+ }
+ if (returned == 0)
+ {
+ break;
+ }
+
+ printf("SA context %llu:\n", entries[0]->saContextId);
+ list_details(engine, entries[0]->inboundSa);
+ list_details(engine, entries[0]->outboundSa);
+
+ FwpmFreeMemory0((void**)&entries);
+ }
+ IPsecSaContextDestroyEnumHandle0(engine, handle);
+ return TRUE;
+}
+
+const GUID FWPM_LAYER_IPSEC_KM_DEMUX_V4 = {
+ 0xf02b1526, 0xa459, 0x4a51, { 0xb9, 0xe3, 0x75, 0x9d, 0xe5, 0x2b, 0x9d, 0x2c }
+};
+const GUID FWPM_LAYER_IPSEC_KM_DEMUX_V6 = {
+ 0x2f755cf6, 0x2fd4, 0x4e88, { 0xb3, 0xe4, 0xa9, 0x1b, 0xca, 0x49, 0x52, 0x35 }
+};
+const GUID FWPM_LAYER_IPSEC_V4 = {
+ 0xeda65c74, 0x610d, 0x4bc5, { 0x94, 0x8f, 0x3c, 0x4f, 0x89, 0x55, 0x68, 0x67 }
+};
+const GUID FWPM_LAYER_IPSEC_V6 = {
+ 0x13c48442, 0x8d87, 0x4261, { 0x9a, 0x29, 0x59, 0xd2, 0xab, 0xc3, 0x48, 0xb4 }
+};
+const GUID FWPM_LAYER_IKEEXT_V4 = {
+ 0xb14b7bdb, 0xdbbd, 0x473e, { 0xbe, 0xd4, 0x8b, 0x47, 0x08, 0xd4, 0xf2, 0x70 }
+};
+const GUID FWPM_LAYER_IKEEXT_V6 = {
+ 0xb64786b3, 0xf687, 0x4eb9, { 0x89, 0xd2, 0x8e, 0xf3, 0x2a, 0xcd, 0xab, 0xe2 }
+};
+const GUID FWPM_LAYER_INBOUND_IPPACKET_V4 = {
+ 0xc86fd1bf, 0x21cd, 0x497e, { 0xa0, 0xbb, 0x17, 0x42, 0x5c, 0x88, 0x5c, 0x58 }
+};
+const GUID FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD = {
+ 0xb5a230d0, 0xa8c0, 0x44f2, { 0x91, 0x6e, 0x99, 0x1b, 0x53, 0xde, 0xd1, 0xf7 }
+};
+const GUID FWPM_LAYER_INBOUND_IPPACKET_V6 = {
+ 0xf52032cb, 0x991c, 0x46e7, { 0x97, 0x1d, 0x26, 0x01, 0x45, 0x9a, 0x91, 0xca }
+};
+const GUID FWPM_LAYER_INBOUND_IPPACKET_V6_DISCARD = {
+ 0xbb24c279, 0x93b4, 0x47a2, { 0x83, 0xad, 0xae, 0x16, 0x98, 0xb5, 0x08, 0x85 }
+};
+const GUID FWPM_LAYER_OUTBOUND_IPPACKET_V4 = {
+ 0x1e5c9fae, 0x8a84, 0x4135, { 0xa3, 0x31, 0x95, 0x0b, 0x54, 0x22, 0x9e, 0xcd }
+};
+const GUID FWPM_LAYER_OUTBOUND_IPPACKET_V4_DISCARD = {
+ 0x08e4bcb5, 0xb647, 0x48f3, { 0x95, 0x3c, 0xe5, 0xdd, 0xbd, 0x03, 0x93, 0x7e }
+};
+const GUID FWPM_LAYER_OUTBOUND_IPPACKET_V6 = {
+ 0xa3b3ab6b, 0x3564, 0x488c, { 0x91, 0x17, 0xf3, 0x4e, 0x82, 0x14, 0x27, 0x63 }
+};
+const GUID FWPM_LAYER_OUTBOUND_IPPACKET_V6_DISCARD = {
+ 0x9513d7c4, 0xa934, 0x49dc, { 0x91, 0xa7, 0x6c, 0xcb, 0x80, 0xcc, 0x02, 0xe3 }
+};
+const GUID FWPM_LAYER_IPFORWARD_V4_DISCARD = {
+ 0x9e9ea773, 0x2fae, 0x4210, { 0x8f, 0x17, 0x34, 0x12, 0x9e, 0xf3, 0x69, 0xeb }
+};
+const GUID FWPM_LAYER_IPFORWARD_V6_DISCARD = {
+ 0x31524a5d, 0x1dfe, 0x472f, { 0xbb, 0x93, 0x51, 0x8e, 0xe9, 0x45, 0xd8, 0xa2 }
+};
+const GUID FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD = {
+ 0xac4a9833, 0xf69d, 0x4648, { 0xb2, 0x61, 0x6d, 0xc8, 0x48, 0x35, 0xef, 0x39 }
+};
+const GUID FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD = {
+ 0x2a6ff955, 0x3b2b, 0x49d2, { 0x98, 0x48, 0xad, 0x9d, 0x72, 0xdc, 0xaa, 0xb7 }
+};
+const GUID FWPM_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD = {
+ 0xc5f10551, 0xbdb0, 0x43d7, { 0xa3, 0x13, 0x50, 0xe2, 0x11, 0xf4, 0xd6, 0x8a }
+};
+const GUID FWPM_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD = {
+ 0xf433df69, 0xccbd, 0x482e, { 0xb9, 0xb2, 0x57, 0x16, 0x56, 0x58, 0xc3, 0xb3 }
+};
+
+/**
+ * Convert filter layer GUID to name
+ */
+static char* layer2name(GUID *guid)
+{
+ struct {
+ GUID guid;
+ char *name;
+ } map[] = {
+ { FWPM_LAYER_IPSEC_KM_DEMUX_V4, "IPsec KM demux v4" },
+ { FWPM_LAYER_IPSEC_KM_DEMUX_V6, "IPsec KM demux v6" },
+ { FWPM_LAYER_IPSEC_V4, "IPsec v4" },
+ { FWPM_LAYER_IPSEC_V6, "IPsec v6" },
+ { FWPM_LAYER_IKEEXT_V4, "IKE ext v4" },
+ { FWPM_LAYER_IKEEXT_V6, "IKE ext v6" },
+ { FWPM_LAYER_INBOUND_IPPACKET_V4, "inbound v4" },
+ { FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD, "inbound v4 dsc" },
+ { FWPM_LAYER_INBOUND_IPPACKET_V6, "inbound v6" },
+ { FWPM_LAYER_INBOUND_IPPACKET_V6_DISCARD, "inbound v6 dsc" },
+ { FWPM_LAYER_OUTBOUND_IPPACKET_V4, "outbound v4" },
+ { FWPM_LAYER_OUTBOUND_IPPACKET_V4_DISCARD, "outbound v4 dsc" },
+ { FWPM_LAYER_OUTBOUND_IPPACKET_V6, "outbound v6" },
+ { FWPM_LAYER_OUTBOUND_IPPACKET_V6_DISCARD, "outbound v6 dsc" },
+ { FWPM_LAYER_IPFORWARD_V4, "forward v4" },
+ { FWPM_LAYER_IPFORWARD_V4_DISCARD, "forward v4 dsc" },
+ { FWPM_LAYER_IPFORWARD_V6, "forward v6" },
+ { FWPM_LAYER_IPFORWARD_V6_DISCARD, "forward v6 discard" },
+ { FWPM_LAYER_INBOUND_TRANSPORT_V4, "inbound transport v4" },
+ { FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD, "inbound transport v4 dsc" },
+ { FWPM_LAYER_INBOUND_TRANSPORT_V6, "inbound transport v6" },
+ { FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD, "inbound v6 transport dsc" },
+ { FWPM_LAYER_OUTBOUND_TRANSPORT_V4, "outbound transport v4" },
+ { FWPM_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD, "outbound transport v4 dsc" },
+ { FWPM_LAYER_OUTBOUND_TRANSPORT_V6, "outbound transport v6" },
+ { FWPM_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD, "outbound transport v6 dsc" },
+ };
+ int i;
+
+ for (i = 0; i < countof(map); i++)
+ {
+ if (memeq(&map[i].guid, guid, sizeof(GUID)))
+ {
+ return map[i].name;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Convert filter callout GUID to name
+ */
+static char* callout2name(GUID *guid)
+{
+ struct {
+ GUID guid;
+ char *name;
+ } map[] = {
+ { FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4, "inbound transport v4" },
+ { FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V6, "inbound transport v6" },
+ { FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4, "outbound transport v4" },
+ { FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V6, "outbound transport v6" },
+ { FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V4, "inbound tunnel v4" },
+ { FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V6, "inbound tunnel v6" },
+ { FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V4, "outbound tunnel v4" },
+ { FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V6, "outbound tunnel v6" },
+ { FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V4, "forward in tunnel v4" },
+ { FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V6, "forward in tunnel v6" },
+ { FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V4, "forward out tunnel v4" },
+ { FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V6, "forward out tunnel v6" },
+ };
+ int i;
+
+ for (i = 0; i < countof(map); i++)
+ {
+ if (memeq(&map[i].guid, guid, sizeof(GUID)))
+ {
+ return map[i].name;
+ }
+ }
+ return guid2string(guid);
+}
+
+/**
+ * Print display data with description
+ */
+static void print_display_data(FWPM_DISPLAY_DATA0 *data)
+{
+ char buf[128];
+
+ buf[0] = '\0';
+ if (data->name)
+ {
+ wcstombs(buf, data->name, sizeof(buf));
+ }
+ printf("%s", buf);
+ if (data->description)
+ {
+ buf[0] = '\0';
+ wcstombs(buf, data->description, sizeof(buf));
+ if (strlen(buf))
+ {
+ printf(" (%s)", buf);
+ }
+ }
+}
+
+/**
+ * List installed firewall filters
+ */
+static bool list_filters(HANDLE engine)
+{
+ HANDLE handle;
+ UINT32 returned;
+ DWORD res;
+ FWPM_FILTER0 **entries;
+ char *layer;
+ int i;
+
+ res = FwpmFilterCreateEnumHandle0(engine, NULL, &handle);
+ if (res != ERROR_SUCCESS)
+ {
+ fprintf(stderr, "FwpmFilterCreateEnumHandle0(): 0x%08x\n", res);
+ return FALSE;
+ }
+
+ while (TRUE)
+ {
+ res = FwpmFilterEnum0(engine, handle, 1, &entries, &returned);
+ if (res != ERROR_SUCCESS)
+ {
+ fprintf(stderr, "FwpmFilterEnum0(): 0x%08x\n", res);
+ FwpmFilterDestroyEnumHandle0(engine, handle);
+ return FALSE;
+ }
+ if (returned == 0)
+ {
+ break;
+ }
+
+ layer = layer2name(&entries[0]->layerKey);
+ if (layer)
+ {
+ printf("Filter ID %llu, '", entries[0]->filterId);
+ print_display_data(&entries[0]->displayData);
+ printf("'\n");
+ printf(" %s, ", layer);
+ if (entries[0]->effectiveWeight.type == FWP_UINT64)
+ {
+ printf("weight %016llx, ", *entries[0]->effectiveWeight.uint64);
+ }
+
+ switch (entries[0]->action.type)
+ {
+ case FWP_ACTION_BLOCK:
+ printf("block\n");
+ break;
+ case FWP_ACTION_PERMIT:
+ printf("permit\n");
+ break;
+ case FWP_ACTION_CALLOUT_TERMINATING:
+ printf("callout terminating: %s\n",
+ callout2name(&entries[0]->action.calloutKey));
+ break;
+ case FWP_ACTION_CALLOUT_INSPECTION:
+ printf("callout inspection: %s\n",
+ callout2name(&entries[0]->action.calloutKey));
+ break;
+ case FWP_ACTION_CALLOUT_UNKNOWN:
+ printf("callout unknown: %s\n",
+ callout2name(&entries[0]->action.calloutKey));
+ break;
+ default:
+ printf("(unknown action)\n");
+ break;
+ }
+ for (i = 0; i < entries[0]->numFilterConditions; i++)
+ {
+ list_cond(engine, &entries[0]->filterCondition[i]);
+ }
+ }
+ FwpmFreeMemory0((void**)&entries);
+ }
+ FwpmFilterDestroyEnumHandle0(engine, handle);
+ return TRUE;
+}
+
+/**
+ * ipsecdump main()
+ */
+int main(int argc, char *argv[])
+{
+ FWPM_SESSION0 session = {
+ .displayData = {
+ .name = L"ipsecdump",
+ .description = L"strongSwan SAD/SPD dumper",
+ },
+ };
+ HANDLE engine;
+ DWORD res;
+ int code;
+
+ library_init(NULL, "ipsecdump");
+ atexit(library_deinit);
+
+ res = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &engine);
+ if (res != ERROR_SUCCESS)
+ {
+ fprintf(stderr, "FwpmEngineOpen(): 0x%08x\n", res);
+ return 2;
+ }
+ if (argc > 1 && streq(argv[1], "filters"))
+ {
+ code = list_filters(engine) ? 0 : 1;
+ }
+ else
+ {
+ code = list_contexts(engine) ? 0 : 1;
+ }
+ FwpmEngineClose0(engine);
+ return code;
+}
diff --git a/src/libcharon/plugins/kernel_wfp/kernel_wfp_compat.c b/src/libcharon/plugins/kernel_wfp/kernel_wfp_compat.c
new file mode 100644
index 000000000..41f85ba5c
--- /dev/null
+++ b/src/libcharon/plugins/kernel_wfp/kernel_wfp_compat.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 revosec AG
+ *
+ * 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 <library.h>
+
+const GUID FWPM_CONDITION_IP_REMOTE_ADDRESS = {
+ 0xb235ae9a, 0x1d64, 0x49b8, { 0xa4,0x4c,0x5f,0xf3,0xd9,0x09,0x50,0x45 }
+};
+const GUID FWPM_CONDITION_IP_LOCAL_ADDRESS = {
+ 0xd9ee00de, 0xc1ef, 0x4617, { 0xbf,0xe3,0xff,0xd8,0xf5,0xa0,0x89,0x57 }
+};
+const GUID FWPM_CONDITION_IP_SOURCE_ADDRESS = {
+ 0xae96897e, 0x2e94, 0x4bc9, { 0xb3,0x13,0xb2,0x7e,0xe8,0x0e,0x57,0x4d }
+};
+const GUID FWPM_CONDITION_IP_DESTINATION_ADDRESS = {
+ 0x2d79133b, 0xb390, 0x45c6, { 0x86,0x99,0xac,0xac,0xea,0xaf,0xed,0x33 }
+};
+const GUID FWPM_CONDITION_IP_LOCAL_PORT = {
+ 0x0c1ba1af, 0x5765, 0x453f, { 0xaf,0x22,0xa8,0xf7,0x91,0xac,0x77,0x5b }
+};
+const GUID FWPM_CONDITION_IP_REMOTE_PORT = {
+ 0xc35a604d, 0xd22b, 0x4e1a, { 0x91,0xb4,0x68,0xf6,0x74,0xee,0x67,0x4b }
+};
+const GUID FWPM_CONDITION_IP_PROTOCOL = {
+ 0x3971ef2b, 0x623e, 0x4f9a, { 0x8c,0xb1,0x6e,0x79,0xb8,0x06,0xb9,0xa7 }
+};
+const GUID FWPM_LAYER_INBOUND_TRANSPORT_V4 = {
+ 0x5926dfc8, 0xe3cf, 0x4426, { 0xa2,0x83,0xdc,0x39,0x3f,0x5d,0x0f,0x9d }
+};
+const GUID FWPM_LAYER_INBOUND_TRANSPORT_V6 = {
+ 0x634a869f, 0xfc23, 0x4b90, { 0xb0,0xc1,0xbf,0x62,0x0a,0x36,0xae,0x6f }
+};
+const GUID FWPM_LAYER_OUTBOUND_TRANSPORT_V4 = {
+ 0x09e61aea, 0xd214, 0x46e2, { 0x9b,0x21,0xb2,0x6b,0x0b,0x2f,0x28,0xc8 }
+};
+const GUID FWPM_LAYER_OUTBOUND_TRANSPORT_V6 = {
+ 0xe1735bde, 0x013f, 0x4655, { 0xb3,0x51,0xa4,0x9e,0x15,0x76,0x2d,0xf0 }
+};
+const GUID FWPM_LAYER_IPFORWARD_V4 = {
+ 0xa82acc24, 0x4ee1, 0x4ee1, { 0xb4,0x65,0xfd,0x1d,0x25,0xcb,0x10,0xa4}
+};
+const GUID FWPM_LAYER_IPFORWARD_V6 = {
+ 0x7b964818, 0x19c7, 0x493a, { 0xb7,0x1f,0x83,0x2c,0x36,0x84,0xd2,0x8c }
+};
+const GUID FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4 = {
+ 0x5132900d, 0x5e84, 0x4b5f, { 0x80,0xe4,0x01,0x74,0x1e,0x81,0xff,0x10 }
+};
+const GUID FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V6 = {
+ 0x49d3ac92, 0x2a6c, 0x4dcf, { 0x95,0x5f,0x1c,0x3b,0xe0,0x09,0xdd,0x99 }
+};
+const GUID FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4 = {
+ 0x4b46bf0a, 0x4523, 0x4e57, { 0xaa,0x38,0xa8,0x79,0x87,0xc9,0x10,0xd9 }
+};
+const GUID FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V6 = {
+ 0x38d87722, 0xad83, 0x4f11, { 0xa9,0x1f,0xdf,0x0f,0xb0,0x77,0x22,0x5b }
+};
+const GUID FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V4 = {
+ 0x191a8a46, 0x0bf8, 0x46cf, { 0xb0,0x45,0x4b,0x45,0xdf,0xa6,0xa3,0x24 }
+};
+const GUID FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V6 = {
+ 0x80c342e3, 0x1e53, 0x4d6f, { 0x9b,0x44,0x03,0xdf,0x5a,0xee,0xe1,0x54 }
+};
+const GUID FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V4 = {
+ 0x70a4196c, 0x835b, 0x4fb0, { 0x98,0xe8,0x07,0x5f,0x4d,0x97,0x7d,0x46 }
+};
+const GUID FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V6 = {
+ 0xf1835363, 0xa6a5, 0x4e62, { 0xb1,0x80,0x23,0xdb,0x78,0x9d,0x8d,0xa6 }
+};
+const GUID FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V4 = {
+ 0x28829633, 0xc4f0, 0x4e66, { 0x87,0x3f,0x84,0x4d,0xb2,0xa8,0x99,0xc7 }
+};
+const GUID FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V6 = {
+ 0xaf50bec2, 0xc686, 0x429a, { 0x88,0x4d,0xb7,0x44,0x43,0xe7,0xb0,0xb4 }
+};
+const GUID FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V4 = {
+ 0xfb532136, 0x15cb, 0x440b, { 0x93,0x7c,0x17,0x17,0xca,0x32,0x0c,0x40 }
+};
+const GUID FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V6 = {
+ 0xdae640cc, 0xe021, 0x4bee, { 0x9e,0xb6,0xa4,0x8b,0x27,0x5c,0x8c,0x1d }
+};
+
+/**
+ * Load a function symbol from a loaded dll
+ */
+static inline void *load_function(char *dll, char *name)
+{
+ HANDLE handle;
+ void *sym = NULL;
+
+ handle = GetModuleHandle(dll);
+ if (!handle)
+ {
+ return NULL;
+ }
+ sym = GetProcAddress(handle, name);
+ return sym;
+}
+
+/**
+ * Macro that defines a stub for a function that calls the same DLL function
+ *
+ * @param dll DLL to find function in
+ * @param ret return type of function
+ * @param name function name
+ * @param size size of all arguments on stack
+ * @param ... arguments of function
+ */
+#define STUB(dll, ret, name, size, ...) \
+ret WINAPI name(__VA_ARGS__) \
+{ \
+ static void (*fun)() = NULL; \
+ if (!fun) \
+ { \
+ fun = load_function(#dll, #name); \
+ } \
+ if (fun) \
+ { \
+ __builtin_return(__builtin_apply(fun, __builtin_apply_args(), size)); \
+ } \
+ return ERROR_NOT_SUPPORTED; \
+}
+
+STUB(fwpuclnt, DWORD, IPsecSaContextCreate1, 40,
+ HANDLE engineHandle, const void *outboundTraffic,
+ const void *virtualIfTunnelInfo, UINT64 *inboundFilterId, UINT64 *id)
+
+STUB(fwpuclnt, DWORD, IPsecSaContextSetSpi0, 32,
+ HANDLE engineHandle, UINT64 id, const void *getSpi, UINT32 inboundSpi)
+
+STUB(fwpuclnt, DWORD, IPsecSaContextGetById1, 24,
+ HANDLE engineHandle, UINT64 id, void **saContext)
+
+STUB(fwpuclnt, DWORD, IPsecSaContextUpdate0, 24,
+ HANDLE engineHandle, UINT32 flags, const void *newValues)
+
+STUB(fwpuclnt, DWORD, IPsecSaContextEnum1, 40,
+ HANDLE engineHandle, HANDLE enumHandle, UINT32 numEntriesRequested,
+ void ***entries, UINT32 *numEntriesReturned)
+
+STUB(fwpuclnt, DWORD, FwpmNetEventSubscribe0, 40,
+ HANDLE engineHandle, const void *subscription, void(*callback)(),
+ void *context, HANDLE *eventsHandle)
+
+STUB(fwpuclnt, DWORD, FwpmNetEventUnsubscribe0, 16,
+ HANDLE engineHandle, HANDLE eventsHandle)
diff --git a/src/libcharon/plugins/kernel_wfp/kernel_wfp_compat.h b/src/libcharon/plugins/kernel_wfp/kernel_wfp_compat.h
new file mode 100644
index 000000000..50a89a007
--- /dev/null
+++ b/src/libcharon/plugins/kernel_wfp/kernel_wfp_compat.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 revosec AG
+ *
+ * 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.
+ */
+
+/**
+ * @defgroup kernel_wfp_compat kernel_wfp_compat
+ * @{ @ingroup kernel_wfp
+ */
+
+#ifndef KERNEL_WFP_COMPAT_H_
+#define KERNEL_WFP_COMPAT_H_
+
+#include <winsock2.h>
+#include <windows.h>
+#include <ipsectypes.h>
+
+/* MinGW defines CIPHERs incorrectly starting at 0 */
+#define IPSEC_CIPHER_TYPE_DES 1
+#define IPSEC_CIPHER_TYPE_3DES 2
+#define IPSEC_CIPHER_TYPE_AES_128 3
+#define IPSEC_CIPHER_TYPE_AES_192 4
+#define IPSEC_CIPHER_TYPE_AES_256 5
+#define IPSEC_CIPHER_TYPE_MAX 6
+
+#include <fwpmtypes.h>
+#include <fwpmu.h>
+#undef interface
+
+/* MinGW defines TRANSFORMs incorrectly starting at 0 */
+#define IPSEC_TRANSFORM_AH 1
+#define IPSEC_TRANSFORM_ESP_AUTH 2
+#define IPSEC_TRANSFORM_ESP_CIPHER 3
+#define IPSEC_TRANSFORM_ESP_AUTH_AND_CIPHER 4
+#define IPSEC_TRANSFORM_ESP_AUTH_FW 5
+#define IPSEC_TRANSFORM_TYPE_MAX 6
+
+/* missing in MinGW */
+enum {
+ FWPM_TUNNEL_FLAG_POINT_TO_POINT = (1<<0),
+ FWPM_TUNNEL_FLAG_ENABLE_VIRTUAL_IF_TUNNELING = (1<<1),
+};
+
+/* missing in MinGW */
+enum {
+ IPSEC_SA_DETAILS_UPDATE_TRAFFIC = (1<<0),
+ IPSEC_SA_DETAILS_UPDATE_UDP_ENCAPSULATION = (1<<1),
+ IPSEC_SA_BUNDLE_UPDATE_FLAGS = (1<<2),
+ IPSEC_SA_BUNDLE_UPDATE_NAP_CONTEXT = (1<<3),
+ IPSEC_SA_BUNDLE_UPDATE_KEY_MODULE_STATE = (1<<4),
+ IPSEC_SA_BUNDLE_UPDATE_PEER_V4_PRIVATE_ADDRESS = (1<<5),
+ IPSEC_SA_BUNDLE_UPDATE_MM_SA_ID = (1<<6),
+};
+
+/* missing in MinGW */
+enum {
+ FWPM_NET_EVENT_FLAG_IP_PROTOCOL_SET = (1<<0),
+ FWPM_NET_EVENT_FLAG_LOCAL_ADDR_SET = (1<<1),
+ FWPM_NET_EVENT_FLAG_REMOTE_ADDR_SET = (1<<2),
+ FWPM_NET_EVENT_FLAG_LOCAL_PORT_SET = (1<<3),
+ FWPM_NET_EVENT_FLAG_REMOTE_PORT_SET = (1<<4),
+ FWPM_NET_EVENT_FLAG_APP_ID_SET = (1<<5),
+ FWPM_NET_EVENT_FLAG_USER_ID_SET = (1<<6),
+ FWPM_NET_EVENT_FLAG_SCOPE_ID_SET = (1<<7),
+ FWPM_NET_EVENT_FLAG_IP_VERSION_SET = (1<<8),
+ FWPM_NET_EVENT_FLAG_REAUTH_REASON_SET = (1<<9),
+};
+
+/* missing in MinGW */
+enum {
+ FWPM_FILTER_FLAG_PERSISTENT = (1<<0),
+ FWPM_FILTER_FLAG_BOOTTIME = (1<<1),
+ FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT = (1<<2),
+ FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT = (1<<3),
+ FWPM_FILTER_FLAG_PERMIT_IF_CALLOUT_UNREGISTERED = (1<<4),
+ FWPM_FILTER_FLAG_DISABLED = (1<<5),
+};
+
+/* missing in MinGW */
+enum {
+ IPSEC_SA_BUNDLE_FLAG_ND_SECURE = (1<< 0),
+ IPSEC_SA_BUNDLE_FLAG_ND_BOUNDARY = (1<< 1),
+ IPSEC_SA_BUNDLE_FLAG_ND_PEER_NAT_BOUNDARY = (1<< 2),
+ IPSEC_SA_BUNDLE_FLAG_GUARANTEE_ENCRYPTION = (1<< 3),
+ IPSEC_SA_BUNDLE_FLAG_NLB = (1<< 4),
+ IPSEC_SA_BUNDLE_FLAG_NO_MACHINE_LUID_VERIFY = (1<< 5),
+ IPSEC_SA_BUNDLE_FLAG_NO_IMPERSONATION_LUID_VERIFY = (1<< 6),
+ IPSEC_SA_BUNDLE_FLAG_NO_EXPLICIT_CRED_MATCH = (1<< 7),
+ IPSEC_SA_BUNDLE_FLAG_ALLOW_NULL_TARGET_NAME_MATCH = (1<< 9),
+ IPSEC_SA_BUNDLE_FLAG_CLEAR_DF_ON_TUNNEL = (1<<10),
+ IPSEC_SA_BUNDLE_FLAG_ASSUME_UDP_CONTEXT_OUTBOUND = (1<<11),
+ IPSEC_SA_BUNDLE_FLAG_ND_PEER_BOUNDARY = (1<<12),
+ IPSEC_SA_BUNDLE_FLAG_SUPPRESS_DUPLICATE_DELETION = (1<<13),
+ IPSEC_SA_BUNDLE_FLAG_PEER_SUPPORTS_GUARANTEE_ENCRYPTION = (1<<14),
+ IPSEC_SA_BUNDLE_FLAG_FORCE_INBOUND_CONNECTIONS = (1<<15),
+ IPSEC_SA_BUNDLE_FLAG_FORCE_OUTBOUND_CONNECTIONS = (1<<16),
+ IPSEC_SA_BUNDLE_FLAG_FORWARD_PATH_INITIATOR = (1<<17),
+};
+
+/* missing in some MinGW versions */
+const GUID FWPM_CONDITION_IP_REMOTE_ADDRESS;
+const GUID FWPM_CONDITION_IP_LOCAL_ADDRESS;
+const GUID FWPM_CONDITION_IP_SOURCE_ADDRESS;
+const GUID FWPM_CONDITION_IP_DESTINATION_ADDRESS;
+const GUID FWPM_CONDITION_IP_LOCAL_PORT;
+const GUID FWPM_CONDITION_IP_REMOTE_PORT;
+const GUID FWPM_CONDITION_IP_PROTOCOL;
+#ifndef FWPM_CONDITION_ICMP_TYPE
+# define FWPM_CONDITION_ICMP_TYPE FWPM_CONDITION_IP_LOCAL_PORT
+#endif
+#ifndef FWPM_CONDITION_ICMP_CODE
+# define FWPM_CONDITION_ICMP_CODE FWPM_CONDITION_IP_REMOTE_PORT
+#endif
+const GUID FWPM_LAYER_INBOUND_TRANSPORT_V4;
+const GUID FWPM_LAYER_INBOUND_TRANSPORT_V6;
+const GUID FWPM_LAYER_OUTBOUND_TRANSPORT_V4;
+const GUID FWPM_LAYER_OUTBOUND_TRANSPORT_V6;
+const GUID FWPM_LAYER_IPFORWARD_V4;
+const GUID FWPM_LAYER_IPFORWARD_V6;
+const GUID FWPM_SUBLAYER_IPSEC_TUNNEL;
+const GUID FWPM_SUBLAYER_IPSEC_FORWARD_OUTBOUND_TUNNEL;
+const GUID FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4;
+const GUID FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V6;
+const GUID FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4;
+const GUID FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V6;
+const GUID FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V4;
+const GUID FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V6;
+const GUID FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V4;
+const GUID FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V6;
+const GUID FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V4;
+const GUID FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V6;
+const GUID FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V4;
+const GUID FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V6;
+
+/* integrity config, missing in some MinGW versions */
+#ifndef IPSEC_AUTH_CONFIG_HMAC_MD5_96
+enum {
+ IPSEC_AUTH_CONFIG_HMAC_MD5_96 = 0,
+ IPSEC_AUTH_CONFIG_HMAC_SHA_1_96,
+ IPSEC_AUTH_CONFIG_HMAC_SHA_256_128,
+ IPSEC_AUTH_CONFIG_GCM_AES_128,
+ IPSEC_AUTH_CONFIG_GCM_AES_192,
+ IPSEC_AUTH_CONFIG_GCM_AES_256,
+ IPSEC_AUTH_CONFIG_MAX
+};
+#define IPSEC_AUTH_TRANSFORM_ID_HMAC_MD5_96 { \
+ IPSEC_AUTH_MD5, IPSEC_AUTH_CONFIG_HMAC_MD5_96 }
+#define IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_1_96 { \
+ IPSEC_AUTH_SHA_1, IPSEC_AUTH_CONFIG_HMAC_SHA_1_96 }
+#define IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_256_128 { \
+ IPSEC_AUTH_SHA_256, IPSEC_AUTH_CONFIG_HMAC_SHA_256_128 }
+#define IPSEC_AUTH_TRANSFORM_ID_GCM_AES_128 { \
+ IPSEC_AUTH_AES_128, IPSEC_AUTH_CONFIG_GCM_AES_128 }
+#define IPSEC_AUTH_TRANSFORM_ID_GCM_AES_192 { \
+ IPSEC_AUTH_AES_192, IPSEC_AUTH_CONFIG_GCM_AES_192 }
+#define IPSEC_AUTH_TRANSFORM_ID_GCM_AES_256 { \
+ IPSEC_AUTH_AES_256, IPSEC_AUTH_CONFIG_GCM_AES_256 }
+#endif
+
+/* encryption config, missing in some MinGW versions */
+#ifndef IPSEC_CIPHER_CONFIG_CBC_DES
+enum {
+ IPSEC_CIPHER_CONFIG_CBC_DES = 1,
+ IPSEC_CIPHER_CONFIG_CBC_3DES,
+ IPSEC_CIPHER_CONFIG_CBC_AES_128,
+ IPSEC_CIPHER_CONFIG_CBC_AES_192,
+ IPSEC_CIPHER_CONFIG_CBC_AES_256,
+ IPSEC_CIPHER_CONFIG_GCM_AES_128,
+ IPSEC_CIPHER_CONFIG_GCM_AES_192,
+ IPSEC_CIPHER_CONFIG_GCM_AES_256,
+ IPSEC_CIPHER_CONFIG_MAX
+};
+#define IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_128 { \
+ IPSEC_CIPHER_TYPE_AES_128, IPSEC_CIPHER_CONFIG_GCM_AES_128 }
+#define IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_192 { \
+ IPSEC_CIPHER_TYPE_AES_192, IPSEC_CIPHER_CONFIG_GCM_AES_192 }
+#define IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_256 { \
+ IPSEC_CIPHER_TYPE_AES_256, IPSEC_CIPHER_CONFIG_GCM_AES_256 }
+#define IPSEC_CIPHER_TRANSFORM_ID_CBC_DES { \
+ IPSEC_CIPHER_TYPE_DES, IPSEC_CIPHER_CONFIG_CBC_DES }
+#define IPSEC_CIPHER_TRANSFORM_ID_CBC_3DES { \
+ IPSEC_CIPHER_TYPE_3DES, IPSEC_CIPHER_CONFIG_CBC_3DES }
+#define IPSEC_CIPHER_TRANSFORM_ID_AES_128 { \
+ IPSEC_CIPHER_TYPE_AES_128, IPSEC_CIPHER_CONFIG_CBC_AES_128 }
+#define IPSEC_CIPHER_TRANSFORM_ID_AES_192 { \
+ IPSEC_CIPHER_TYPE_AES_192, IPSEC_CIPHER_CONFIG_CBC_AES_192 }
+#define IPSEC_CIPHER_TRANSFORM_ID_AES_256 { \
+ IPSEC_CIPHER_TYPE_AES_256, IPSEC_CIPHER_CONFIG_CBC_AES_256 }
+#endif
+
+DWORD WINAPI FwpmIPsecTunnelAdd0(HANDLE, UINT32,
+ const FWPM_PROVIDER_CONTEXT0*, const FWPM_PROVIDER_CONTEXT0*, UINT32,
+ const FWPM_FILTER_CONDITION0*, PSECURITY_DESCRIPTOR);
+
+#endif /** KERNEL_WFP_COMPAT_H_ @}*/
diff --git a/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c b/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c
new file mode 100644
index 000000000..c788bfb10
--- /dev/null
+++ b/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c
@@ -0,0 +1,2551 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 revosec AG
+ *
+ * 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.
+ */
+
+/* Windows 7, for some fwpmu.h functionality */
+#define _WIN32_WINNT 0x0601
+
+#include "kernel_wfp_compat.h"
+#include "kernel_wfp_ipsec.h"
+
+#include <daemon.h>
+#include <hydra.h>
+#include <threading/mutex.h>
+#include <collections/array.h>
+#include <collections/hashtable.h>
+#include <processing/jobs/callback_job.h>
+
+
+typedef struct private_kernel_wfp_ipsec_t private_kernel_wfp_ipsec_t;
+
+struct private_kernel_wfp_ipsec_t {
+
+ /**
+ * Public interface
+ */
+ kernel_wfp_ipsec_t public;
+
+ /**
+ * Next SPI to allocate
+ */
+ refcount_t nextspi;
+
+ /**
+ * Mix value to distribute SPI allocation randomly
+ */
+ u_int32_t mixspi;
+
+ /**
+ * IKE bypass filters, as UINT64 filter LUID
+ */
+ array_t *bypass;
+
+ /**
+ * Temporary SAD/SPD entries referenced reqid, as uintptr_t => entry_t
+ */
+ hashtable_t *tsas;
+
+ /**
+ * SAD/SPD entries referenced by inbound SA, as sa_entry_t => entry_t
+ */
+ hashtable_t *isas;
+
+ /**
+ * SAD/SPD entries referenced by outbound SA, as sa_entry_t => entry_t
+ */
+ hashtable_t *osas;
+
+ /**
+ * Installed routes, as route_t => route_t
+ */
+ hashtable_t *routes;
+
+ /**
+ * Installed traps, as trap_t => trap_t
+ */
+ hashtable_t *traps;
+
+ /**
+ * Mutex for accessing entries
+ */
+ mutex_t *mutex;
+
+ /**
+ * WFP session handle
+ */
+ HANDLE handle;
+
+ /**
+ * Provider charon registers as
+ */
+ FWPM_PROVIDER0 provider;
+
+ /**
+ * Event handle
+ */
+ HANDLE event;
+};
+
+/**
+ * Security association entry
+ */
+typedef struct {
+ /** SPI for this SA */
+ u_int32_t spi;
+ /** protocol, IPPROTO_ESP/IPPROTO_AH */
+ u_int8_t protocol;
+ /** hard lifetime of SA */
+ u_int32_t lifetime;
+ /** destination host address for this SPI */
+ host_t *dst;
+ struct {
+ /** algorithm */
+ u_int16_t alg;
+ /** key */
+ chunk_t key;
+ } integ, encr;
+} sa_entry_t;
+
+/**
+ * Hash function for sas lookup table
+ */
+static u_int hash_sa(sa_entry_t *key)
+{
+ return chunk_hash_inc(chunk_from_thing(key->spi),
+ chunk_hash(key->dst->get_address(key->dst)));
+}
+
+/**
+ * equals function for sas lookup table
+ */
+static bool equals_sa(sa_entry_t *a, sa_entry_t *b)
+{
+ return a->spi == b->spi && a->dst->ip_equals(a->dst, b->dst);
+}
+
+/**
+ * Security policy entry
+ */
+typedef struct {
+ /** policy source addresses */
+ traffic_selector_t *src;
+ /** policy destinaiton addresses */
+ traffic_selector_t *dst;
+ /** WFP allocated LUID for inbound filter ID */
+ u_int64_t policy_in;
+ /** WFP allocated LUID for outbound filter ID */
+ u_int64_t policy_out;
+ /** WFP allocated LUID for forward inbound filter ID, tunnel mode only */
+ u_int64_t policy_fwd_in;
+ /** WFP allocated LUID for forward outbound filter ID, tunnel mode only */
+ u_int64_t policy_fwd_out;
+ /** have installed a route for it? */
+ bool route;
+} sp_entry_t;
+
+/**
+ * Destroy an SP entry
+ */
+static void sp_entry_destroy(sp_entry_t *sp)
+{
+ sp->src->destroy(sp->src);
+ sp->dst->destroy(sp->dst);
+ free(sp);
+}
+
+/**
+ * Collection of SA/SP database entries for a reqid
+ */
+typedef struct {
+ /** reqid of entry */
+ u_int32_t reqid;
+ /** outer address on local host */
+ host_t *local;
+ /** outer address on remote host */
+ host_t *remote;
+ /** inbound SA entry */
+ sa_entry_t isa;
+ /** outbound SA entry */
+ sa_entry_t osa;
+ /** associated (outbound) policies, as sp_entry_t* */
+ array_t *sps;
+ /** IPsec mode, tunnel|transport */
+ ipsec_mode_t mode;
+ /** UDP encapsulation */
+ bool encap;
+ /** provider context, for tunnel mode only */
+ u_int64_t provider;
+ /** WFP allocated LUID for SA context */
+ u_int64_t sa_id;
+} entry_t;
+
+/**
+ * Installed route
+ */
+typedef struct {
+ /** destination net of route */
+ host_t *dst;
+ /** prefix length of dst */
+ u_int8_t mask;
+ /** source address for route */
+ host_t *src;
+ /** gateway of route, NULL if directly attached */
+ host_t *gtw;
+ /** references for route */
+ u_int refs;
+} route_t;
+
+/**
+ * Destroy a route_t
+ */
+static void destroy_route(route_t *this)
+{
+ this->dst->destroy(this->dst);
+ this->src->destroy(this->src);
+ DESTROY_IF(this->gtw);
+ free(this);
+}
+
+/**
+ * Hashtable equals function for routes
+ */
+static bool equals_route(route_t *a, route_t *b)
+{
+ return a->mask == b->mask &&
+ a->dst->ip_equals(a->dst, b->dst) &&
+ a->src->ip_equals(a->src, b->src);
+}
+
+/**
+ * Hashtable hash function for routes
+ */
+static u_int hash_route(route_t *route)
+{
+ return chunk_hash_inc(route->src->get_address(route->src),
+ chunk_hash_inc(route->dst->get_address(route->dst), route->mask));
+}
+
+/** forward declaration */
+static bool manage_routes(private_kernel_wfp_ipsec_t *this, entry_t *entry,
+ bool add);
+
+/**
+ * Remove policies associated to an entry from kernel
+ */
+static void cleanup_policies(private_kernel_wfp_ipsec_t *this, entry_t *entry)
+{
+ enumerator_t *enumerator;
+ sp_entry_t *sp;
+
+ if (entry->mode == MODE_TUNNEL)
+ {
+ manage_routes(this, entry, FALSE);
+ }
+
+ enumerator = array_create_enumerator(entry->sps);
+ while (enumerator->enumerate(enumerator, &sp))
+ {
+ if (sp->policy_in)
+ {
+ FwpmFilterDeleteById0(this->handle, sp->policy_in);
+ sp->policy_in = 0;
+ }
+ if (sp->policy_out)
+ {
+ FwpmFilterDeleteById0(this->handle, sp->policy_out);
+ sp->policy_out = 0;
+ }
+ if (sp->policy_fwd_in)
+ {
+ FwpmFilterDeleteById0(this->handle, sp->policy_fwd_in);
+ sp->policy_fwd_in = 0;
+ }
+ if (sp->policy_fwd_out)
+ {
+ FwpmFilterDeleteById0(this->handle, sp->policy_fwd_out);
+ sp->policy_fwd_out = 0;
+ }
+ }
+ enumerator->destroy(enumerator);
+}
+
+/**
+ * Destroy a SA/SP entry set
+ */
+static void entry_destroy(private_kernel_wfp_ipsec_t *this, entry_t *entry)
+{
+ if (entry->sa_id)
+ {
+ IPsecSaContextDeleteById0(this->handle, entry->sa_id);
+ }
+ if (entry->provider)
+ {
+ FwpmProviderContextDeleteById0(this->handle, entry->provider);
+ }
+ cleanup_policies(this, entry);
+ array_destroy_function(entry->sps, (void*)sp_entry_destroy, NULL);
+ entry->local->destroy(entry->local);
+ entry->remote->destroy(entry->remote);
+ chunk_clear(&entry->isa.integ.key);
+ chunk_clear(&entry->isa.encr.key);
+ chunk_clear(&entry->osa.integ.key);
+ chunk_clear(&entry->osa.encr.key);
+ free(entry);
+}
+
+/**
+ * Append/Realloc a filter condition to an existing condition set
+ */
+static FWPM_FILTER_CONDITION0 *append_condition(FWPM_FILTER_CONDITION0 *conds[],
+ int *count)
+{
+ FWPM_FILTER_CONDITION0 *cond;
+
+ (*count)++;
+ *conds = realloc(*conds, *count * sizeof(*cond));
+ cond = *conds + *count - 1;
+ memset(cond, 0, sizeof(*cond));
+
+ return cond;
+}
+
+/**
+ * Convert an IPv4 prefix to a host order subnet mask
+ */
+static u_int32_t prefix2mask(u_int8_t prefix)
+{
+ u_int8_t netmask[4] = {};
+ int i;
+
+ for (i = 0; i < sizeof(netmask); i++)
+ {
+ if (prefix < 8)
+ {
+ netmask[i] = 0xFF << (8 - prefix);
+ break;
+ }
+ netmask[i] = 0xFF;
+ prefix -= 8;
+ }
+ return untoh32(netmask);
+}
+
+/**
+ * Convert a 16-bit range to a WFP condition
+ */
+static void range2cond(FWPM_FILTER_CONDITION0 *cond,
+ u_int16_t from, u_int16_t to)
+{
+ if (from == to)
+ {
+ cond->matchType = FWP_MATCH_EQUAL;
+ cond->conditionValue.type = FWP_UINT16;
+ cond->conditionValue.uint16 = from;
+ }
+ else
+ {
+ cond->matchType = FWP_MATCH_RANGE;
+ cond->conditionValue.type = FWP_RANGE_TYPE;
+ cond->conditionValue.rangeValue = calloc(1, sizeof(FWP_RANGE0));
+ cond->conditionValue.rangeValue->valueLow.type = FWP_UINT16;
+ cond->conditionValue.rangeValue->valueLow.uint16 = from;
+ cond->conditionValue.rangeValue->valueHigh.type = FWP_UINT16;
+ cond->conditionValue.rangeValue->valueHigh.uint16 = to;
+ }
+}
+
+/**
+ * (Re-)allocate filter conditions for given local or remote traffic selector
+ */
+static bool ts2condition(traffic_selector_t *ts, const GUID *target,
+ FWPM_FILTER_CONDITION0 *conds[], int *count)
+{
+ FWPM_FILTER_CONDITION0 *cond;
+ FWP_BYTE_ARRAY16 *addr;
+ FWP_RANGE0 *range;
+ u_int16_t from_port, to_port;
+ void *from, *to;
+ u_int8_t proto;
+ host_t *net;
+ u_int8_t prefix;
+
+ from = ts->get_from_address(ts).ptr;
+ to = ts->get_to_address(ts).ptr;
+ from_port = ts->get_from_port(ts);
+ to_port = ts->get_to_port(ts);
+
+ cond = append_condition(conds, count);
+ cond->fieldKey = *target;
+ if (ts->is_host(ts, NULL))
+ {
+ cond->matchType = FWP_MATCH_EQUAL;
+ switch (ts->get_type(ts))
+ {
+ case TS_IPV4_ADDR_RANGE:
+ cond->conditionValue.type = FWP_UINT32;
+ cond->conditionValue.uint32 = untoh32(from);
+ break;
+ case TS_IPV6_ADDR_RANGE:
+ cond->conditionValue.type = FWP_BYTE_ARRAY16_TYPE;
+ cond->conditionValue.byteArray16 = addr = malloc(sizeof(*addr));
+ memcpy(addr, from, sizeof(*addr));
+ break;
+ default:
+ return FALSE;
+ }
+ }
+ else if (ts->to_subnet(ts, &net, &prefix))
+ {
+ FWP_V6_ADDR_AND_MASK *m6;
+ FWP_V4_ADDR_AND_MASK *m4;
+
+ cond->matchType = FWP_MATCH_EQUAL;
+ switch (net->get_family(net))
+ {
+ case AF_INET:
+ cond->conditionValue.type = FWP_V4_ADDR_MASK;
+ cond->conditionValue.v4AddrMask = m4 = calloc(1, sizeof(*m4));
+ m4->addr = untoh32(from);
+ m4->mask = prefix2mask(prefix);
+ break;
+ case AF_INET6:
+ cond->conditionValue.type = FWP_V6_ADDR_MASK;
+ cond->conditionValue.v6AddrMask = m6 = calloc(1, sizeof(*m6));
+ memcpy(m6->addr, from, sizeof(m6->addr));
+ m6->prefixLength = prefix;
+ break;
+ default:
+ net->destroy(net);
+ return FALSE;
+ }
+ net->destroy(net);
+ }
+ else
+ {
+ cond->matchType = FWP_MATCH_RANGE;
+ cond->conditionValue.type = FWP_RANGE_TYPE;
+ cond->conditionValue.rangeValue = range = calloc(1, sizeof(*range));
+ switch (ts->get_type(ts))
+ {
+ case TS_IPV4_ADDR_RANGE:
+ range->valueLow.type = FWP_UINT32;
+ range->valueLow.uint32 = untoh32(from);
+ range->valueHigh.type = FWP_UINT32;
+ range->valueHigh.uint32 = untoh32(to);
+ break;
+ case TS_IPV6_ADDR_RANGE:
+ range->valueLow.type = FWP_BYTE_ARRAY16_TYPE;
+ range->valueLow.byteArray16 = addr = malloc(sizeof(*addr));
+ memcpy(addr, from, sizeof(*addr));
+ range->valueHigh.type = FWP_BYTE_ARRAY16_TYPE;
+ range->valueHigh.byteArray16 = addr = malloc(sizeof(*addr));
+ memcpy(addr, to, sizeof(*addr));
+ break;
+ default:
+ return FALSE;
+ }
+ }
+
+ proto = ts->get_protocol(ts);
+ if (proto && target == &FWPM_CONDITION_IP_LOCAL_ADDRESS)
+ {
+ cond = append_condition(conds, count);
+ cond->fieldKey = FWPM_CONDITION_IP_PROTOCOL;
+ cond->matchType = FWP_MATCH_EQUAL;
+ cond->conditionValue.type = FWP_UINT8;
+ cond->conditionValue.uint8 = proto;
+ }
+
+ if (proto == IPPROTO_ICMP)
+ {
+ if (target == &FWPM_CONDITION_IP_LOCAL_ADDRESS)
+ {
+ u_int8_t from_type, to_type, from_code, to_code;
+
+ from_type = traffic_selector_icmp_type(from_port);
+ to_type = traffic_selector_icmp_type(to_port);
+ from_code = traffic_selector_icmp_code(from_port);
+ to_code = traffic_selector_icmp_code(to_port);
+
+ if (from_type != 0 || to_type != 0xFF)
+ {
+ cond = append_condition(conds, count);
+ cond->fieldKey = FWPM_CONDITION_ICMP_TYPE;
+ range2cond(cond, from_type, to_type);
+ }
+ if (from_code != 0 || to_code != 0xFF)
+ {
+ cond = append_condition(conds, count);
+ cond->fieldKey = FWPM_CONDITION_ICMP_CODE;
+ range2cond(cond, from_code, to_code);
+ }
+ }
+ }
+ else if (from_port != 0 || to_port != 0xFFFF)
+ {
+ if (target == &FWPM_CONDITION_IP_LOCAL_ADDRESS)
+ {
+ cond = append_condition(conds, count);
+ cond->fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
+ range2cond(cond, from_port, to_port);
+ }
+ if (target == &FWPM_CONDITION_IP_REMOTE_ADDRESS)
+ {
+ cond = append_condition(conds, count);
+ cond->fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
+ range2cond(cond, from_port, to_port);
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * Free memory associated to a single condition
+ */
+static void free_condition(FWP_DATA_TYPE type, void *value)
+{
+ FWP_RANGE0 *range;
+
+ switch (type)
+ {
+ case FWP_BYTE_ARRAY16_TYPE:
+ case FWP_V4_ADDR_MASK:
+ case FWP_V6_ADDR_MASK:
+ free(value);
+ break;
+ case FWP_RANGE_TYPE:
+ range = value;
+ free_condition(range->valueLow.type, range->valueLow.sd);
+ free_condition(range->valueHigh.type, range->valueHigh.sd);
+ free(range);
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * Free memory used by a set of conditions
+ */
+static void free_conditions(FWPM_FILTER_CONDITION0 *conds, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ {
+ free_condition(conds[i].conditionValue.type, conds[i].conditionValue.sd);
+ }
+ free(conds);
+}
+
+/**
+ * Find the callout GUID for given parameters
+ */
+static bool find_callout(bool tunnel, bool v6, bool inbound, bool forward,
+ GUID *layer, GUID *sublayer, GUID *callout)
+{
+ struct {
+ bool tunnel;
+ bool v6;
+ bool inbound;
+ bool forward;
+ const GUID *layer;
+ const GUID *sublayer;
+ const GUID *callout;
+ } map[] = {
+ { 0, 0, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V4, NULL,
+ &FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4 },
+ { 0, 0, 1, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V4, NULL,
+ &FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4 },
+ { 0, 1, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V6, NULL,
+ &FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V6 },
+ { 0, 1, 1, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V6, NULL,
+ &FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V6 },
+ { 1, 0, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V4,
+ &FWPM_SUBLAYER_IPSEC_TUNNEL,
+ &FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V4 },
+ { 1, 0, 0, 1, &FWPM_LAYER_IPFORWARD_V4,
+ &FWPM_SUBLAYER_IPSEC_FORWARD_OUTBOUND_TUNNEL,
+ &FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V4 },
+ { 1, 0, 1, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V4,
+ &FWPM_SUBLAYER_IPSEC_TUNNEL,
+ &FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V4 },
+ { 1, 0, 1, 1, &FWPM_LAYER_IPFORWARD_V4,
+ &FWPM_SUBLAYER_IPSEC_TUNNEL,
+ &FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V4 },
+ { 1, 1, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V6,
+ &FWPM_SUBLAYER_IPSEC_TUNNEL,
+ &FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V6 },
+ { 1, 1, 0, 1, &FWPM_LAYER_IPFORWARD_V6,
+ &FWPM_SUBLAYER_IPSEC_TUNNEL,
+ &FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V6 },
+ { 1, 1, 1, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V6,
+ &FWPM_SUBLAYER_IPSEC_TUNNEL,
+ &FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V6 },
+ { 1, 1, 1, 1, &FWPM_LAYER_IPFORWARD_V6,
+ &FWPM_SUBLAYER_IPSEC_TUNNEL,
+ &FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V6 },
+ };
+ int i;
+
+ for (i = 0; i < countof(map); i++)
+ {
+ if (tunnel == map[i].tunnel &&
+ v6 == map[i].v6 &&
+ inbound == map[i].inbound &&
+ forward == map[i].forward)
+ {
+ *callout = *map[i].callout;
+ *layer = *map[i].layer;
+ if (map[i].sublayer)
+ {
+ *sublayer = *map[i].sublayer;
+ }
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * Install a single policy in to the kernel
+ */
+static bool install_sp(private_kernel_wfp_ipsec_t *this, sp_entry_t *sp,
+ GUID *context, bool inbound, bool fwd, UINT64 *filter_id)
+{
+ FWPM_FILTER_CONDITION0 *conds = NULL;
+ traffic_selector_t *local, *remote;
+ const GUID *ltarget, *rtarget;
+ int count = 0;
+ bool v6;
+ DWORD res;
+ FWPM_FILTER0 filter = {
+ .displayData = {
+ .name = L"charon IPsec policy",
+ },
+ .action = {
+ .type = FWP_ACTION_CALLOUT_TERMINATING,
+ },
+ };
+
+ if (context)
+ {
+ filter.flags |= FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT;
+ filter.providerKey = (GUID*)&this->provider.providerKey;
+ filter.providerContextKey = *context;
+ }
+
+ v6 = sp->src->get_type(sp->src) == TS_IPV6_ADDR_RANGE;
+ if (!find_callout(context != NULL, v6, inbound, fwd,
+ &filter.layerKey, &filter.subLayerKey,
+ &filter.action.calloutKey))
+ {
+ return FALSE;
+ }
+
+ if (inbound && fwd)
+ {
+ local = sp->dst;
+ remote = sp->src;
+ }
+ else
+ {
+ local = sp->src;
+ remote = sp->dst;
+ }
+ if (fwd)
+ {
+ ltarget = &FWPM_CONDITION_IP_SOURCE_ADDRESS;
+ rtarget = &FWPM_CONDITION_IP_DESTINATION_ADDRESS;
+ }
+ else
+ {
+ ltarget = &FWPM_CONDITION_IP_LOCAL_ADDRESS;
+ rtarget = &FWPM_CONDITION_IP_REMOTE_ADDRESS;
+ }
+ if (!ts2condition(local, ltarget, &conds, &count) ||
+ !ts2condition(remote, rtarget, &conds, &count))
+ {
+ free_conditions(conds, count);
+ return FALSE;
+ }
+
+ filter.numFilterConditions = count;
+ filter.filterCondition = conds;
+
+ res = FwpmFilterAdd0(this->handle, &filter, NULL, filter_id);
+ free_conditions(conds, count);
+ if (res != ERROR_SUCCESS)
+ {
+ DBG1(DBG_KNL, "installing %s%sbound WFP filter failed: 0x%08x",
+ fwd ? "forward " : "", inbound ? "in" : "out", res);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * Install a set of policies in to the kernel
+ */
+static bool install_sps(private_kernel_wfp_ipsec_t *this,
+ entry_t *entry, GUID *context)
+{
+ enumerator_t *enumerator;
+ sp_entry_t *sp;
+
+ enumerator = array_create_enumerator(entry->sps);
+ while (enumerator->enumerate(enumerator, &sp))
+ {
+ /* inbound policy */
+ if (!install_sp(this, sp, context, TRUE, FALSE, &sp->policy_in))
+ {
+ enumerator->destroy(enumerator);
+ return FALSE;
+ }
+ /* outbound policy */
+ if (!install_sp(this, sp, context, FALSE, FALSE, &sp->policy_out))
+ {
+ enumerator->destroy(enumerator);
+ return FALSE;
+ }
+ if (context)
+ {
+ if (!sp->src->is_host(sp->src, entry->local) ||
+ !sp->dst->is_host(sp->dst, entry->remote))
+ {
+ /* inbound forward policy, from decapsulation */
+ if (!install_sp(this, sp, context,
+ TRUE, TRUE, &sp->policy_fwd_in))
+ {
+ enumerator->destroy(enumerator);
+ return FALSE;
+ }
+ /* outbound forward policy, to encapsulate */
+ if (!install_sp(this, sp, context,
+ FALSE, TRUE, &sp->policy_fwd_out))
+ {
+ enumerator->destroy(enumerator);
+ return FALSE;
+ }
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ return TRUE;
+}
+
+/**
+ * Convert a chunk_t to a WFP FWP_BYTE_BLOB
+ */
+static inline FWP_BYTE_BLOB chunk2blob(chunk_t chunk)
+{
+ return (FWP_BYTE_BLOB){
+ .size = chunk.len,
+ .data = chunk.ptr,
+ };
+}
+
+/**
+ * Convert an integrity_algorithm_t to a WFP IPSEC_AUTH_TRANFORM_ID0
+ */
+static bool alg2auth(integrity_algorithm_t alg,
+ IPSEC_SA_AUTH_INFORMATION0 *info)
+{
+ struct {
+ integrity_algorithm_t alg;
+ IPSEC_AUTH_TRANSFORM_ID0 transform;
+ } map[] = {
+ { AUTH_HMAC_MD5_96, IPSEC_AUTH_TRANSFORM_ID_HMAC_MD5_96 },
+ { AUTH_HMAC_SHA1_96, IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_1_96 },
+ { AUTH_HMAC_SHA2_256_128, IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_256_128},
+ { AUTH_AES_128_GMAC, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_128 },
+ { AUTH_AES_192_GMAC, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_192 },
+ { AUTH_AES_256_GMAC, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_256 },
+ };
+ int i;
+
+ for (i = 0; i < countof(map); i++)
+ {
+ if (map[i].alg == alg)
+ {
+ info->authTransform.authTransformId = map[i].transform;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * Convert an encryption_algorithm_t to a WFP IPSEC_CIPHER_TRANFORM_ID0
+ */
+static bool alg2cipher(encryption_algorithm_t alg, int keylen,
+ IPSEC_SA_CIPHER_INFORMATION0 *info)
+{
+ struct {
+ encryption_algorithm_t alg;
+ int keylen;
+ IPSEC_CIPHER_TRANSFORM_ID0 transform;
+ } map[] = {
+ { ENCR_DES, 8, IPSEC_CIPHER_TRANSFORM_ID_CBC_DES },
+ { ENCR_3DES, 24, IPSEC_CIPHER_TRANSFORM_ID_CBC_3DES },
+ { ENCR_AES_CBC, 16, IPSEC_CIPHER_TRANSFORM_ID_AES_128 },
+ { ENCR_AES_CBC, 24, IPSEC_CIPHER_TRANSFORM_ID_AES_192 },
+ { ENCR_AES_CBC, 32, IPSEC_CIPHER_TRANSFORM_ID_AES_256 },
+ { ENCR_AES_GCM_ICV16, 20, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_128 },
+ { ENCR_AES_GCM_ICV16, 28, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_192 },
+ { ENCR_AES_GCM_ICV16, 36, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_256 },
+ };
+ int i;
+
+ for (i = 0; i < countof(map); i++)
+ {
+ if (map[i].alg == alg && map[i].keylen == keylen)
+ {
+ info->cipherTransform.cipherTransformId = map[i].transform;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * Get the integrity algorithm used for an AEAD transform
+ */
+static integrity_algorithm_t encr2integ(encryption_algorithm_t encr, int keylen)
+{
+ struct {
+ encryption_algorithm_t encr;
+ int keylen;
+ integrity_algorithm_t integ;
+ } map[] = {
+ { ENCR_NULL_AUTH_AES_GMAC, 20, AUTH_AES_128_GMAC },
+ { ENCR_NULL_AUTH_AES_GMAC, 28, AUTH_AES_192_GMAC },
+ { ENCR_NULL_AUTH_AES_GMAC, 36, AUTH_AES_256_GMAC },
+ { ENCR_AES_GCM_ICV16, 20, AUTH_AES_128_GMAC },
+ { ENCR_AES_GCM_ICV16, 28, AUTH_AES_192_GMAC },
+ { ENCR_AES_GCM_ICV16, 36, AUTH_AES_256_GMAC },
+ };
+ int i;
+
+ for (i = 0; i < countof(map); i++)
+ {
+ if (map[i].encr == encr && map[i].keylen == keylen)
+ {
+ return map[i].integ;
+ }
+ }
+ return AUTH_UNDEFINED;
+}
+
+/**
+ * Install a single SA
+ */
+static bool install_sa(private_kernel_wfp_ipsec_t *this, entry_t *entry,
+ bool inbound, sa_entry_t *sa, FWP_IP_VERSION version)
+{
+ IPSEC_SA_AUTH_AND_CIPHER_INFORMATION0 info = {};
+ IPSEC_SA0 ipsec = {
+ .spi = ntohl(sa->spi),
+ };
+ IPSEC_SA_BUNDLE0 bundle = {
+ .lifetime = {
+ .lifetimeSeconds = inbound ? entry->isa.lifetime
+ : entry->osa.lifetime,
+ },
+ .saList = &ipsec,
+ .numSAs = 1,
+ .ipVersion = version,
+ };
+ struct {
+ u_int16_t alg;
+ chunk_t key;
+ } integ = {}, encr = {};
+ DWORD res;
+
+ switch (sa->protocol)
+ {
+ case IPPROTO_AH:
+ ipsec.saTransformType = IPSEC_TRANSFORM_AH;
+ ipsec.ahInformation = &info.saAuthInformation;
+ integ.key = sa->integ.key;
+ integ.alg = sa->integ.alg;
+ break;
+ case IPPROTO_ESP:
+ if (sa->encr.alg == ENCR_NULL ||
+ sa->encr.alg == ENCR_NULL_AUTH_AES_GMAC)
+ {
+ ipsec.saTransformType = IPSEC_TRANSFORM_ESP_AUTH;
+ ipsec.espAuthInformation = &info.saAuthInformation;
+ }
+ else
+ {
+ ipsec.saTransformType = IPSEC_TRANSFORM_ESP_AUTH_AND_CIPHER;
+ ipsec.espAuthAndCipherInformation = &info;
+ encr.key = sa->encr.key;
+ encr.alg = sa->encr.alg;
+ }
+ if (encryption_algorithm_is_aead(sa->encr.alg))
+ {
+ integ.alg = encr2integ(sa->encr.alg, sa->encr.key.len);
+ integ.key = sa->encr.key;
+ }
+ else
+ {
+ integ.alg = sa->integ.alg;
+ integ.key = sa->integ.key;
+ }
+ break;
+ default:
+ return FALSE;
+ }
+
+ if (integ.alg)
+ {
+ info.saAuthInformation.authKey = chunk2blob(integ.key);
+ if (!alg2auth(integ.alg, &info.saAuthInformation))
+ {
+ DBG1(DBG_KNL, "integrity algorithm %N not supported by WFP",
+ integrity_algorithm_names, integ.alg);
+ return FALSE;
+ }
+ }
+ if (encr.alg)
+ {
+ info.saCipherInformation.cipherKey = chunk2blob(encr.key);
+ if (!alg2cipher(encr.alg, encr.key.len, &info.saCipherInformation))
+ {
+ DBG1(DBG_KNL, "encryption algorithm %N not supported by WFP",
+ encryption_algorithm_names, encr.alg);
+ return FALSE;
+ }
+ }
+
+ if (inbound)
+ {
+ res = IPsecSaContextAddInbound0(this->handle, entry->sa_id, &bundle);
+ }
+ else
+ {
+ bundle.flags |= IPSEC_SA_BUNDLE_FLAG_ASSUME_UDP_CONTEXT_OUTBOUND;
+ res = IPsecSaContextAddOutbound0(this->handle, entry->sa_id, &bundle);
+ }
+ if (res != ERROR_SUCCESS)
+ {
+ DBG1(DBG_KNL, "adding %sbound WFP SA failed: 0x%08x",
+ inbound ? "in" : "out", res);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * Convert an IPv6 host address to WFP representation
+ */
+static void host2address6(host_t *host, void *out)
+{
+ u_int32_t *src, *dst = out;
+
+ src = (u_int32_t*)host->get_address(host).ptr;
+
+ dst[0] = untoh32(&src[3]);
+ dst[1] = untoh32(&src[2]);
+ dst[2] = untoh32(&src[1]);
+ dst[3] = untoh32(&src[0]);
+}
+
+/**
+ * Fill in traffic structure from entry addresses
+ */
+static bool hosts2traffic(private_kernel_wfp_ipsec_t *this,
+ host_t *l, host_t *r, IPSEC_TRAFFIC1 *traffic)
+{
+ if (l->get_family(l) != r->get_family(r))
+ {
+ return FALSE;
+ }
+ switch (l->get_family(l))
+ {
+ case AF_INET:
+ traffic->ipVersion = FWP_IP_VERSION_V4;
+ traffic->localV4Address = untoh32(l->get_address(l).ptr);
+ traffic->remoteV4Address = untoh32(r->get_address(r).ptr);
+ return TRUE;
+ case AF_INET6:
+ traffic->ipVersion = FWP_IP_VERSION_V6;
+ host2address6(l, &traffic->localV6Address);
+ host2address6(r, &traffic->remoteV6Address);
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+/**
+ * Install SAs to the kernel
+ */
+static bool install_sas(private_kernel_wfp_ipsec_t *this, entry_t *entry,
+ IPSEC_TRAFFIC_TYPE type)
+{
+ IPSEC_TRAFFIC1 traffic = {
+ .trafficType = type,
+ };
+ IPSEC_GETSPI1 spi = {
+ .inboundIpsecTraffic = {
+ .trafficType = type,
+ },
+ };
+ enumerator_t *enumerator;
+ sp_entry_t *sp;
+ DWORD res;
+
+ if (type == IPSEC_TRAFFIC_TYPE_TRANSPORT)
+ {
+ enumerator = array_create_enumerator(entry->sps);
+ if (enumerator->enumerate(enumerator, &sp))
+ {
+ traffic.ipsecFilterId = sp->policy_out;
+ spi.inboundIpsecTraffic.ipsecFilterId = sp->policy_in;
+ }
+ else
+ {
+ enumerator->destroy(enumerator);
+ return FALSE;
+ }
+ enumerator->destroy(enumerator);
+ }
+ else
+ {
+ traffic.tunnelPolicyId = entry->provider;
+ spi.inboundIpsecTraffic.tunnelPolicyId = entry->provider;
+ }
+
+ if (!hosts2traffic(this, entry->local, entry->remote, &traffic))
+ {
+ return FALSE;
+ }
+
+ res = IPsecSaContextCreate1(this->handle, &traffic, NULL, NULL,
+ &entry->sa_id);
+ if (res != ERROR_SUCCESS)
+ {
+ DBG1(DBG_KNL, "creating WFP SA context failed: 0x%08x", res);
+ return FALSE;
+ }
+
+ memcpy(spi.inboundIpsecTraffic.localV6Address, traffic.localV6Address,
+ sizeof(traffic.localV6Address));
+ memcpy(spi.inboundIpsecTraffic.remoteV6Address, traffic.remoteV6Address,
+ sizeof(traffic.remoteV6Address));
+ spi.ipVersion = traffic.ipVersion;
+
+ res = IPsecSaContextSetSpi0(this->handle, entry->sa_id, &spi,
+ ntohl(entry->isa.spi));
+ if (res != ERROR_SUCCESS)
+ {
+ DBG1(DBG_KNL, "setting WFP SA SPI failed: 0x%08x", res);
+ IPsecSaContextDeleteById0(this->handle, entry->sa_id);
+ entry->sa_id = 0;
+ return FALSE;
+ }
+
+ if (!install_sa(this, entry, TRUE, &entry->isa, spi.ipVersion) ||
+ !install_sa(this, entry, FALSE, &entry->osa, spi.ipVersion))
+ {
+ IPsecSaContextDeleteById0(this->handle, entry->sa_id);
+ entry->sa_id = 0;
+ return FALSE;
+ }
+
+ if (entry->encap)
+ {
+ IPSEC_V4_UDP_ENCAPSULATION0 encap = {
+ .localUdpEncapPort = entry->local->get_port(entry->local),
+ .remoteUdpEncapPort = entry->remote->get_port(entry->remote),
+ };
+ IPSEC_SA_CONTEXT1 *ctx;
+
+ res = IPsecSaContextGetById1(this->handle, entry->sa_id, &ctx);
+ if (res != ERROR_SUCCESS)
+ {
+ DBG1(DBG_KNL, "getting WFP SA for UDP encap failed: 0x%08x", res);
+ IPsecSaContextDeleteById0(this->handle, entry->sa_id);
+ entry->sa_id = 0;
+ return FALSE;
+ }
+ ctx->inboundSa->udpEncapsulation = &encap;
+ ctx->outboundSa->udpEncapsulation = &encap;
+
+ res = IPsecSaContextUpdate0(this->handle,
+ IPSEC_SA_DETAILS_UPDATE_UDP_ENCAPSULATION, ctx);
+ FwpmFreeMemory0((void**)&ctx);
+ if (res != ERROR_SUCCESS)
+ {
+ DBG1(DBG_KNL, "enable WFP UDP encap failed: 0x%08x", res);
+ IPsecSaContextDeleteById0(this->handle, entry->sa_id);
+ entry->sa_id = 0;
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * Install a transport mode SA/SP set to the kernel
+ */
+static bool install_transport(private_kernel_wfp_ipsec_t *this, entry_t *entry)
+{
+ if (install_sps(this, entry, NULL) &&
+ install_sas(this, entry, IPSEC_TRAFFIC_TYPE_TRANSPORT))
+ {
+ return TRUE;
+ }
+ cleanup_policies(this, entry);
+ return FALSE;
+}
+
+/**
+ * Generate a new GUID, random
+ */
+static bool generate_guid(private_kernel_wfp_ipsec_t *this, GUID *guid)
+{
+ bool ok;
+ rng_t *rng;
+
+ rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
+ if (!rng)
+ {
+ return FALSE;
+ }
+ ok = rng->get_bytes(rng, sizeof(GUID), (u_int8_t*)guid);
+ rng->destroy(rng);
+ return ok;
+}
+
+/**
+ * Register a dummy tunnel provider to associate tunnel filters to
+ */
+static bool add_tunnel_provider(private_kernel_wfp_ipsec_t *this,
+ entry_t *entry, GUID *guid, UINT64 *luid)
+{
+ DWORD res;
+
+ IPSEC_AUTH_TRANSFORM0 transform = {
+ /* Create any valid proposal. This is actually not used, as we
+ * don't create an SA from this information. */
+ .authTransformId = IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_1_96,
+ };
+ IPSEC_SA_TRANSFORM0 transforms = {
+ .ipsecTransformType = IPSEC_TRANSFORM_ESP_AUTH,
+ .espAuthTransform = &transform,
+ };
+ IPSEC_PROPOSAL0 proposal = {
+ .lifetime = {
+ /* We need a valid lifetime, even if we don't create any SA
+ * from these values. Pick some values accepted. */
+ .lifetimeSeconds = 0xFFFF,
+ .lifetimeKilobytes = 0xFFFFFFFF,
+ .lifetimePackets = 0xFFFFFFFF,
+ },
+ .numSaTransforms = 1,
+ .saTransforms = &transforms,
+ };
+ IPSEC_TUNNEL_POLICY0 policy = {
+ .numIpsecProposals = 1,
+ .ipsecProposals = &proposal,
+ .saIdleTimeout = {
+ /* not used, set to lifetime for maximum */
+ .idleTimeoutSeconds = proposal.lifetime.lifetimeSeconds,
+ .idleTimeoutSecondsFailOver = proposal.lifetime.lifetimeSeconds,
+ },
+ };
+ FWPM_PROVIDER_CONTEXT0 qm = {
+ .displayData = {
+ .name = L"charon tunnel provider",
+ },
+ .providerKey = (GUID*)&this->provider.providerKey,
+ .type = FWPM_IPSEC_IKE_QM_TUNNEL_CONTEXT,
+ .ikeQmTunnelPolicy = &policy,
+ };
+
+ switch (entry->local->get_family(entry->local))
+ {
+ case AF_INET:
+ policy.tunnelEndpoints.ipVersion = FWP_IP_VERSION_V4;
+ policy.tunnelEndpoints.localV4Address =
+ untoh32(entry->local->get_address(entry->local).ptr);
+ policy.tunnelEndpoints.remoteV4Address =
+ untoh32(entry->remote->get_address(entry->remote).ptr);
+ break;
+ case AF_INET6:
+ policy.tunnelEndpoints.ipVersion = FWP_IP_VERSION_V6;
+ host2address6(entry->local, &policy.tunnelEndpoints.localV6Address);
+ host2address6(entry->remote, &policy.tunnelEndpoints.remoteV6Address);
+ break;
+ default:
+ return FALSE;
+ }
+
+ if (!generate_guid(this, &qm.providerContextKey))
+ {
+ return FALSE;
+ }
+
+ res = FwpmProviderContextAdd0(this->handle, &qm, NULL, luid);
+ if (res != ERROR_SUCCESS)
+ {
+ DBG1(DBG_KNL, "adding provider context failed: 0x%08x", res);
+ return FALSE;
+ }
+ *guid = qm.providerContextKey;
+ return TRUE;
+}
+
+/**
+ * Install tunnel mode SPs to the kernel
+ */
+static bool install_tunnel_sps(private_kernel_wfp_ipsec_t *this, entry_t *entry)
+{
+ GUID guid;
+
+ if (!add_tunnel_provider(this, entry, &guid, &entry->provider))
+ {
+ return FALSE;
+ }
+ if (!install_sps(this, entry, &guid))
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * Reduce refcount, or uninstall a route if all refs gone
+ */
+static bool uninstall_route(private_kernel_wfp_ipsec_t *this,
+ host_t *dst, u_int8_t mask, host_t *src, host_t *gtw)
+{
+ route_t *route, key = {
+ .dst = dst,
+ .mask = mask,
+ .src = src,
+ };
+ char *name;
+ bool res = FALSE;
+
+ this->mutex->lock(this->mutex);
+ route = this->routes->get(this->routes, &key);
+ if (route)
+ {
+ if (--route->refs == 0)
+ {
+ if (hydra->kernel_interface->get_interface(hydra->kernel_interface,
+ src, &name))
+ {
+ res = hydra->kernel_interface->del_route(hydra->kernel_interface,
+ dst->get_address(dst), mask, gtw, src, name) == SUCCESS;
+ free(name);
+ }
+ route = this->routes->remove(this->routes, route);
+ if (route)
+ {
+ destroy_route(route);
+ }
+ }
+ else
+ {
+ res = TRUE;
+ }
+ }
+ this->mutex->unlock(this->mutex);
+
+ return res;
+}
+
+/**
+ * Install a single route, or refcount if exists
+ */
+static bool install_route(private_kernel_wfp_ipsec_t *this,
+ host_t *dst, u_int8_t mask, host_t *src, host_t *gtw)
+{
+ route_t *route, key = {
+ .dst = dst,
+ .mask = mask,
+ .src = src,
+ };
+ char *name;
+ bool res = FALSE;
+
+ this->mutex->lock(this->mutex);
+ route = this->routes->get(this->routes, &key);
+ if (route)
+ {
+ route->refs++;
+ res = TRUE;
+ }
+ else
+ {
+ if (hydra->kernel_interface->get_interface(hydra->kernel_interface,
+ src, &name))
+ {
+ if (hydra->kernel_interface->add_route(hydra->kernel_interface,
+ dst->get_address(dst), mask, gtw, src, name) == SUCCESS)
+ {
+ INIT(route,
+ .dst = dst->clone(dst),
+ .mask = mask,
+ .src = src->clone(src),
+ .gtw = gtw ? gtw->clone(gtw) : NULL,
+ .refs = 1,
+ );
+ route = this->routes->put(this->routes, route, route);
+ if (route)
+ {
+ destroy_route(route);
+ }
+ res = TRUE;
+ }
+ free(name);
+ }
+ }
+ this->mutex->unlock(this->mutex);
+
+ return res;
+}
+
+/**
+ * (Un)-install a single route
+ */
+static bool manage_route(private_kernel_wfp_ipsec_t *this,
+ host_t *local, host_t *remote,
+ traffic_selector_t *src_ts, traffic_selector_t *dst_ts,
+ bool add)
+{
+ host_t *src, *dst, *gtw;
+ u_int8_t mask;
+ bool done;
+
+ if (!dst_ts->to_subnet(dst_ts, &dst, &mask))
+ {
+ return FALSE;
+ }
+ if (hydra->kernel_interface->get_address_by_ts(hydra->kernel_interface,
+ src_ts, &src, NULL) != SUCCESS)
+ {
+ dst->destroy(dst);
+ return FALSE;
+ }
+ gtw = hydra->kernel_interface->get_nexthop(hydra->kernel_interface,
+ remote, -1, local);
+ if (add)
+ {
+ done = install_route(this, dst, mask, src, gtw);
+ }
+ else
+ {
+ done = uninstall_route(this, dst, mask, src, gtw);
+ }
+ dst->destroy(dst);
+ src->destroy(src);
+ DESTROY_IF(gtw);
+
+ if (!done)
+ {
+ DBG1(DBG_KNL, "%sinstalling route for policy %R === %R failed",
+ add ? "" : "un", src_ts, dst_ts);
+ }
+ return done;
+}
+
+/**
+ * (Un)-install routes for IPsec policies
+ */
+static bool manage_routes(private_kernel_wfp_ipsec_t *this, entry_t *entry,
+ bool add)
+{
+ enumerator_t *enumerator;
+ sp_entry_t *sp;
+
+ enumerator = array_create_enumerator(entry->sps);
+ while (enumerator->enumerate(enumerator, &sp))
+ {
+ if (add && sp->route)
+ {
+ continue;
+ }
+ if (!add && !sp->route)
+ {
+ continue;
+ }
+ if (manage_route(this, entry->local, entry->remote,
+ sp->src, sp->dst, add))
+ {
+ sp->route = add;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ return TRUE;
+}
+
+/**
+ * Install a tunnel mode SA/SP set to the kernel
+ */
+static bool install_tunnel(private_kernel_wfp_ipsec_t *this, entry_t *entry)
+{
+ if (install_tunnel_sps(this, entry) &&
+ manage_routes(this, entry, TRUE) &&
+ install_sas(this, entry, IPSEC_TRAFFIC_TYPE_TUNNEL))
+ {
+ return TRUE;
+ }
+ cleanup_policies(this, entry);
+ return FALSE;
+}
+
+/**
+ * Install a SA/SP set to the kernel
+ */
+static bool install(private_kernel_wfp_ipsec_t *this, entry_t *entry)
+{
+ switch (entry->mode)
+ {
+ case MODE_TRANSPORT:
+ return install_transport(this, entry);
+ case MODE_TUNNEL:
+ return install_tunnel(this, entry);
+ case MODE_BEET:
+ default:
+ return FALSE;
+ }
+}
+
+/**
+ * Installed trap entry
+ */
+typedef struct {
+ /** reqid this trap is installed for */
+ u_int32_t reqid;
+ /** is this a forward policy trap for tunnel mode? */
+ bool fwd;
+ /** do we have installed a route for this trap policy? */
+ bool route;
+ /** local address of associated route */
+ host_t *local;
+ /** remote address of associated route */
+ host_t *remote;
+ /** src traffic selector */
+ traffic_selector_t *src;
+ /** dst traffic selector */
+ traffic_selector_t *dst;
+ /** LUID of installed tunnel policy filter */
+ UINT64 filter_id;
+} trap_t;
+
+/**
+ * Destroy a trap entry
+ */
+static void destroy_trap(trap_t *this)
+{
+ this->local->destroy(this->local);
+ this->remote->destroy(this->remote);
+ this->src->destroy(this->src);
+ this->dst->destroy(this->dst);
+ free(this);
+}
+
+/**
+ * Hashtable equals function for traps
+ */
+static bool equals_trap(trap_t *a, trap_t *b)
+{
+ return a->filter_id == b->filter_id;
+}
+
+/**
+ * Hashtable hash function for traps
+ */
+static u_int hash_trap(trap_t *trap)
+{
+ return chunk_hash(chunk_from_thing(trap->filter_id));
+}
+
+/**
+ * Send an acquire for an installed trap filter
+ */
+static void acquire(private_kernel_wfp_ipsec_t *this, UINT64 filter_id,
+ traffic_selector_t *src, traffic_selector_t *dst)
+{
+ u_int32_t reqid = 0;
+ trap_t *trap, key = {
+ .filter_id = filter_id,
+ };
+
+ this->mutex->lock(this->mutex);
+ trap = this->traps->get(this->traps, &key);
+ if (trap)
+ {
+ reqid = trap->reqid;
+ }
+ this->mutex->unlock(this->mutex);
+
+ if (reqid)
+ {
+ src = src ? src->clone(src) : NULL;
+ dst = dst ? dst->clone(dst) : NULL;
+ hydra->kernel_interface->acquire(hydra->kernel_interface, reqid,
+ src, dst);
+ }
+}
+
+/**
+ * Create a single host traffic selector from an FWP address definition
+ */
+static traffic_selector_t *addr2ts(FWP_IP_VERSION version, void *data,
+ u_int8_t protocol, u_int16_t from_port, u_int16_t to_port)
+{
+ ts_type_t type;
+ UINT32 ints[4];
+ chunk_t addr;
+
+ switch (version)
+ {
+ case FWP_IP_VERSION_V4:
+ ints[0] = untoh32(data);
+ addr = chunk_from_thing(ints[0]);
+ type = TS_IPV4_ADDR_RANGE;
+ break;
+ case FWP_IP_VERSION_V6:
+ ints[3] = untoh32(data);
+ ints[2] = untoh32(data + 4);
+ ints[1] = untoh32(data + 8);
+ ints[0] = untoh32(data + 12);
+ addr = chunk_from_thing(ints);
+ type = TS_IPV6_ADDR_RANGE;
+ break;
+ default:
+ return NULL;
+ }
+ return traffic_selector_create_from_bytes(protocol, type, addr, from_port,
+ addr, to_port);
+}
+
+/**
+ * FwpmNetEventSubscribe0() callback
+ */
+static void WINAPI event_callback(void *user, const FWPM_NET_EVENT1 *event)
+{
+ private_kernel_wfp_ipsec_t *this = user;
+ traffic_selector_t *local = NULL, *remote = NULL;
+ u_int8_t protocol = 0;
+ u_int16_t from_local = 0, to_local = 65535;
+ u_int16_t from_remote = 0, to_remote = 65535;
+
+ if ((event->header.flags & FWPM_NET_EVENT_FLAG_LOCAL_ADDR_SET) &&
+ (event->header.flags & FWPM_NET_EVENT_FLAG_REMOTE_ADDR_SET))
+ {
+ if (event->header.flags & FWPM_NET_EVENT_FLAG_LOCAL_PORT_SET)
+ {
+ from_local = to_local = event->header.localPort;
+ }
+ if (event->header.flags & FWPM_NET_EVENT_FLAG_LOCAL_PORT_SET)
+ {
+ from_remote = to_remote = event->header.remotePort;
+ }
+ if (event->header.flags & FWPM_NET_EVENT_FLAG_IP_PROTOCOL_SET)
+ {
+ protocol = event->header.ipProtocol;
+ }
+
+ local = addr2ts(event->header.ipVersion,
+ (void*)&event->header.localAddrV6,
+ protocol, from_local, to_local);
+ remote = addr2ts(event->header.ipVersion,
+ (void*)&event->header.remoteAddrV6,
+ protocol, from_remote, to_remote);
+ }
+
+ switch (event->type)
+ {
+ case FWPM_NET_EVENT_TYPE_CLASSIFY_DROP:
+ acquire(this, event->classifyDrop->filterId, local, remote);
+ break;
+ case FWPM_NET_EVENT_TYPE_IKEEXT_MM_FAILURE:
+ case FWPM_NET_EVENT_TYPE_IKEEXT_QM_FAILURE:
+ case FWPM_NET_EVENT_TYPE_IKEEXT_EM_FAILURE:
+ case FWPM_NET_EVENT_TYPE_IPSEC_KERNEL_DROP:
+ DBG1(DBG_KNL, "IPsec kernel drop: %R === %R, error 0x%08x, "
+ "SPI 0x%08x, %s filterId %llu", local, remote,
+ event->ipsecDrop->failureStatus, event->ipsecDrop->spi,
+ event->ipsecDrop->direction ? "in" : "out",
+ event->ipsecDrop->filterId);
+ break;
+ case FWPM_NET_EVENT_TYPE_IPSEC_DOSP_DROP:
+ default:
+ break;
+ }
+
+ DESTROY_IF(local);
+ DESTROY_IF(remote);
+}
+
+/**
+ * Register for net events
+ */
+static bool register_events(private_kernel_wfp_ipsec_t *this)
+{
+ FWPM_NET_EVENT_SUBSCRIPTION0 subscription = {};
+ DWORD res;
+
+ res = FwpmNetEventSubscribe0(this->handle, &subscription,
+ event_callback, this, &this->event);
+ if (res != ERROR_SUCCESS)
+ {
+ DBG1(DBG_KNL, "registering for WFP events failed: 0x%08x", res);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * Install a trap policy to kernel
+ */
+static bool install_trap(private_kernel_wfp_ipsec_t *this, trap_t *trap)
+{
+ FWPM_FILTER_CONDITION0 *conds = NULL;
+ int count = 0;
+ DWORD res;
+ const GUID *starget, *dtarget;
+ UINT64 weight = 0x000000000000ff00;
+ FWPM_FILTER0 filter = {
+ .displayData = {
+ .name = L"charon IPsec trap",
+ },
+ .action = {
+ .type = FWP_ACTION_BLOCK,
+ },
+ .weight = {
+ .type = FWP_UINT64,
+ .uint64 = &weight,
+ },
+ };
+
+ if (trap->fwd)
+ {
+ if (trap->src->get_type(trap->src) == TS_IPV4_ADDR_RANGE)
+ {
+ filter.layerKey = FWPM_LAYER_IPFORWARD_V4;
+ }
+ else
+ {
+ filter.layerKey = FWPM_LAYER_IPFORWARD_V6;
+ }
+ starget = &FWPM_CONDITION_IP_SOURCE_ADDRESS;
+ dtarget = &FWPM_CONDITION_IP_DESTINATION_ADDRESS;
+ }
+ else
+ {
+ if (trap->src->get_type(trap->src) == TS_IPV4_ADDR_RANGE)
+ {
+ filter.layerKey = FWPM_LAYER_OUTBOUND_TRANSPORT_V4;
+ }
+ else
+ {
+ filter.layerKey = FWPM_LAYER_OUTBOUND_TRANSPORT_V6;
+ }
+ starget = &FWPM_CONDITION_IP_LOCAL_ADDRESS;
+ dtarget = &FWPM_CONDITION_IP_REMOTE_ADDRESS;
+ }
+
+ if (!ts2condition(trap->src, starget, &conds, &count) ||
+ !ts2condition(trap->dst, dtarget, &conds, &count))
+ {
+ free_conditions(conds, count);
+ return FALSE;
+ }
+
+ filter.numFilterConditions = count;
+ filter.filterCondition = conds;
+
+ res = FwpmFilterAdd0(this->handle, &filter, NULL, &trap->filter_id);
+ free_conditions(conds, count);
+ if (res != ERROR_SUCCESS)
+ {
+ DBG1(DBG_KNL, "installing WFP trap filter failed: 0x%08x", res);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * Uninstall a trap policy from kernel
+ */
+static bool uninstall_trap(private_kernel_wfp_ipsec_t *this, trap_t *trap)
+{
+ DWORD res;
+
+ res = FwpmFilterDeleteById0(this->handle, trap->filter_id);
+ if (res != ERROR_SUCCESS)
+ {
+ DBG1(DBG_KNL, "uninstalling WFP trap filter failed: 0x%08x", res);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * Create and install a new trap entry
+ */
+static bool add_trap(private_kernel_wfp_ipsec_t *this,
+ u_int32_t reqid, bool fwd, host_t *local, host_t *remote,
+ traffic_selector_t *src, traffic_selector_t *dst)
+{
+ trap_t *trap;
+
+ INIT(trap,
+ .reqid = reqid,
+ .fwd = fwd,
+ .src = src->clone(src),
+ .dst = dst->clone(dst),
+ .local = local->clone(local),
+ .remote = remote->clone(remote),
+ );
+
+ if (!install_trap(this, trap))
+ {
+ destroy_trap(trap);
+ return FALSE;
+ }
+
+ trap->route = manage_route(this, local, remote, src, dst, TRUE);
+
+ this->mutex->lock(this->mutex);
+ this->traps->put(this->traps, trap, trap);
+ this->mutex->unlock(this->mutex);
+ return TRUE;
+}
+
+/**
+ * Uninstall and remove a new trap entry
+ */
+static bool remove_trap(private_kernel_wfp_ipsec_t *this,
+ u_int32_t reqid, bool fwd,
+ traffic_selector_t *src, traffic_selector_t *dst)
+{
+ enumerator_t *enumerator;
+ trap_t *trap, *found = NULL;
+
+ this->mutex->lock(this->mutex);
+ enumerator = this->traps->create_enumerator(this->traps);
+ while (enumerator->enumerate(enumerator, NULL, &trap))
+ {
+ if (reqid == trap->reqid &&
+ fwd == trap->fwd &&
+ src->equals(src, trap->src) &&
+ dst->equals(dst, trap->dst))
+ {
+ this->traps->remove_at(this->traps, enumerator);
+ found = trap;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ this->mutex->unlock(this->mutex);
+
+ if (found)
+ {
+ if (trap->route)
+ {
+ trap->route = !manage_route(this, trap->local, trap->remote,
+ src, dst, FALSE);
+ }
+ uninstall_trap(this, found);
+ destroy_trap(found);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+METHOD(kernel_ipsec_t, get_features, kernel_feature_t,
+ private_kernel_wfp_ipsec_t *this)
+{
+ return KERNEL_ESP_V3_TFC | KERNEL_NO_POLICY_UPDATES;
+}
+
+/**
+ * Initialize seeds for SPI generation
+ */
+static bool init_spi(private_kernel_wfp_ipsec_t *this)
+{
+ bool ok = TRUE;
+ rng_t *rng;
+
+ rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
+ if (!rng)
+ {
+ return FALSE;
+ }
+ ok = rng->get_bytes(rng, sizeof(this->nextspi), (u_int8_t*)&this->nextspi);
+ if (ok)
+ {
+ ok = rng->get_bytes(rng, sizeof(this->mixspi), (u_int8_t*)&this->mixspi);
+ }
+ rng->destroy(rng);
+ return ok;
+}
+
+/**
+ * Map an integer x with a one-to-one function using quadratic residues.
+ */
+static u_int permute(u_int x, u_int p)
+{
+ u_int qr;
+
+ x = x % p;
+ qr = ((u_int64_t)x * x) % p;
+ if (x <= p / 2)
+ {
+ return qr;
+ }
+ return p - qr;
+}
+
+METHOD(kernel_ipsec_t, get_spi, status_t,
+ private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
+ u_int8_t protocol, u_int32_t reqid, u_int32_t *spi)
+{
+ /* To avoid sequencial SPIs, we use a one-to-one permuation function on
+ * an incrementing counter, that is a full period PRNG for the range we
+ * allocate SPIs in. We add some randomness using a fixed XOR and start
+ * the counter at random position. This is not cryptographically safe,
+ * but that is actually not required.
+ * The selected prime should be smaller than the range we allocate SPIs
+ * in, and it must satisfy p % 4 == 3 to map x > p/2 using p - qr. */
+ static const u_int p = 268435399, offset = 0xc0000000;
+
+ *spi = htonl(offset + permute(ref_get(&this->nextspi) ^ this->mixspi, p));
+ return SUCCESS;
+}
+
+METHOD(kernel_ipsec_t, get_cpi, status_t,
+ private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
+ u_int32_t reqid, u_int16_t *cpi)
+{
+ return NOT_SUPPORTED;
+}
+
+/**
+ * Data for an expire callback job
+ */
+typedef struct {
+ /* backref to kernel backend */
+ private_kernel_wfp_ipsec_t *this;
+ /* SPI of expiring SA */
+ u_int32_t spi;
+ /* destination address of expiring SA */
+ host_t *dst;
+ /* is this a hard expire, or a rekey request? */
+ bool hard;
+} expire_data_t;
+
+/**
+ * Clean up expire data
+ */
+static void expire_data_destroy(expire_data_t *data)
+{
+ data->dst->destroy(data->dst);
+ free(data);
+}
+
+/**
+ * Callback job for SA expiration
+ */
+static job_requeue_t expire_job(expire_data_t *data)
+{
+ private_kernel_wfp_ipsec_t *this = data->this;
+ u_int32_t reqid = 0;
+ u_int8_t protocol;
+ entry_t *entry;
+ sa_entry_t key = {
+ .spi = data->spi,
+ .dst = data->dst,
+ };
+
+ if (data->hard)
+ {
+ this->mutex->lock(this->mutex);
+ entry = this->isas->remove(this->isas, &key);
+ this->mutex->unlock(this->mutex);
+ if (entry)
+ {
+ protocol = entry->isa.protocol;
+ reqid = entry->reqid;
+ if (entry->osa.dst)
+ {
+ key.dst = entry->osa.dst;
+ key.spi = entry->osa.spi;
+ this->osas->remove(this->osas, &key);
+ }
+ entry_destroy(this, entry);
+ }
+ }
+ else
+ {
+ this->mutex->lock(this->mutex);
+ entry = this->isas->get(this->isas, &key);
+ if (entry)
+ {
+ protocol = entry->isa.protocol;
+ reqid = entry->reqid;
+ }
+ this->mutex->unlock(this->mutex);
+ }
+
+ if (reqid)
+ {
+ hydra->kernel_interface->expire(hydra->kernel_interface,
+ reqid, protocol, data->spi, data->hard);
+ }
+
+ return JOB_REQUEUE_NONE;
+}
+
+/**
+ * Schedule an expire event for an SA
+ */
+static void schedule_expire(private_kernel_wfp_ipsec_t *this, u_int32_t spi,
+ host_t *dst, u_int32_t lifetime, bool hard)
+{
+ expire_data_t *data;
+
+ INIT(data,
+ .this = this,
+ .spi = spi,
+ .dst = dst->clone(dst),
+ .hard = hard,
+ );
+
+ lib->scheduler->schedule_job(lib->scheduler, (job_t*)
+ callback_job_create((void*)expire_job, data,
+ (void*)expire_data_destroy, NULL),
+ lifetime);
+}
+
+METHOD(kernel_ipsec_t, add_sa, status_t,
+ private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
+ u_int32_t spi, u_int8_t protocol, u_int32_t reqid, mark_t mark,
+ u_int32_t tfc, lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key,
+ u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode,
+ u_int16_t ipcomp, u_int16_t cpi, u_int32_t replay_window,
+ bool initiator, bool encap, bool esn, bool inbound,
+ traffic_selector_t *src_ts, traffic_selector_t *dst_ts)
+{
+ host_t *local, *remote;
+ entry_t *entry;
+
+ if (inbound)
+ {
+ /* comes first, create new entry */
+ local = dst->clone(dst);
+ remote = src->clone(src);
+
+ INIT(entry,
+ .reqid = reqid,
+ .isa = {
+ .spi = spi,
+ .dst = local,
+ .protocol = protocol,
+ .lifetime = lifetime->time.life,
+ .encr = {
+ .alg = enc_alg,
+ .key = chunk_clone(enc_key),
+ },
+ .integ = {
+ .alg = int_alg,
+ .key = chunk_clone(int_key),
+ },
+ },
+ .sps = array_create(0, 0),
+ .local = local,
+ .remote = remote,
+ .mode = mode,
+ .encap = encap,
+ );
+
+ if (lifetime->time.life)
+ {
+ schedule_expire(this, spi, local, lifetime->time.life, TRUE);
+ }
+ if (lifetime->time.rekey && lifetime->time.rekey != lifetime->time.life)
+ {
+ schedule_expire(this, spi, local, lifetime->time.rekey, FALSE);
+ }
+
+ this->mutex->lock(this->mutex);
+ this->tsas->put(this->tsas, (void*)(uintptr_t)reqid, entry);
+ this->isas->put(this->isas, &entry->isa, entry);
+ this->mutex->unlock(this->mutex);
+ }
+ else
+ {
+ /* comes after inbound, update entry */
+ this->mutex->lock(this->mutex);
+ entry = this->tsas->remove(this->tsas, (void*)(uintptr_t)reqid);
+ this->mutex->unlock(this->mutex);
+
+ if (!entry)
+ {
+ DBG1(DBG_KNL, "adding outbound SA failed, no inbound SA found "
+ "for reqid %u ", reqid);
+ return NOT_FOUND;
+ }
+ /* TODO: should we check for local/remote, mode etc.? */
+
+ entry->osa = (sa_entry_t){
+ .spi = spi,
+ .dst = entry->remote,
+ .protocol = protocol,
+ .lifetime = lifetime->time.life,
+ .encr = {
+ .alg = enc_alg,
+ .key = chunk_clone(enc_key),
+ },
+ .integ = {
+ .alg = int_alg,
+ .key = chunk_clone(int_key),
+ },
+ };
+
+ this->mutex->lock(this->mutex);
+ this->osas->put(this->osas, &entry->osa, entry);
+ this->mutex->unlock(this->mutex);
+ }
+
+ return SUCCESS;
+}
+
+METHOD(kernel_ipsec_t, update_sa, status_t,
+ private_kernel_wfp_ipsec_t *this, u_int32_t spi, u_int8_t protocol,
+ u_int16_t cpi, host_t *src, host_t *dst, host_t *new_src, host_t *new_dst,
+ bool encap, bool new_encap, mark_t mark)
+{
+ entry_t *entry;
+ sa_entry_t key = {
+ .dst = dst,
+ .spi = spi,
+ };
+ UINT64 sa_id = 0;
+ IPSEC_SA_CONTEXT1 *ctx;
+ IPSEC_V4_UDP_ENCAPSULATION0 ports;
+ UINT32 flags = IPSEC_SA_DETAILS_UPDATE_TRAFFIC;
+ DWORD res;
+
+ this->mutex->lock(this->mutex);
+ entry = this->osas->get(this->osas, &key);
+ this->mutex->unlock(this->mutex);
+
+ if (entry)
+ {
+ /* outbound entry, nothing to do */
+ return SUCCESS;
+ }
+
+ this->mutex->lock(this->mutex);
+ entry = this->isas->get(this->isas, &key);
+ if (entry)
+ {
+ /* inbound entry, do update */
+ sa_id = entry->sa_id;
+ ports.localUdpEncapPort = entry->local->get_port(entry->local);
+ ports.remoteUdpEncapPort = entry->remote->get_port(entry->remote);
+ }
+ this->mutex->unlock(this->mutex);
+
+ if (!sa_id)
+ {
+ return NOT_FOUND;
+ }
+
+ res = IPsecSaContextGetById1(this->handle, sa_id, &ctx);
+ if (res != ERROR_SUCCESS)
+ {
+ DBG1(DBG_KNL, "getting WFP SA context for updated failed: 0x%08x", res);
+ return FAILED;
+ }
+ if (!hosts2traffic(this, new_dst, new_src, &ctx->inboundSa->traffic) ||
+ !hosts2traffic(this, new_dst, new_src, &ctx->outboundSa->traffic))
+ {
+ FwpmFreeMemory0((void**)&ctx);
+ return FAILED;
+ }
+
+ if (new_encap != encap)
+ {
+ if (new_encap)
+ {
+ ctx->inboundSa->udpEncapsulation = &ports;
+ ctx->outboundSa->udpEncapsulation = &ports;
+ }
+ else
+ {
+ ctx->inboundSa->udpEncapsulation = NULL;
+ ctx->outboundSa->udpEncapsulation = NULL;
+ }
+ flags |= IPSEC_SA_DETAILS_UPDATE_UDP_ENCAPSULATION;
+ }
+
+ res = IPsecSaContextUpdate0(this->handle, flags, ctx);
+ FwpmFreeMemory0((void**)&ctx);
+ if (res != ERROR_SUCCESS)
+ {
+ DBG1(DBG_KNL, "updating WFP SA context failed: 0x%08x", res);
+ return FAILED;
+ }
+
+ this->mutex->lock(this->mutex);
+ entry = this->isas->remove(this->isas, &key);
+ if (entry)
+ {
+ key.spi = entry->osa.spi;
+ key.dst = entry->osa.dst;
+ this->osas->remove(this->osas, &key);
+
+ entry->local->destroy(entry->local);
+ entry->remote->destroy(entry->remote);
+ entry->local = new_dst->clone(new_dst);
+ entry->remote = new_src->clone(new_src);
+ entry->isa.dst = entry->local;
+ entry->osa.dst = entry->remote;
+
+ this->isas->put(this->isas, &entry->isa, entry);
+ this->osas->put(this->osas, &entry->osa, entry);
+
+ manage_routes(this, entry, FALSE);
+ manage_routes(this, entry, TRUE);
+ }
+ this->mutex->unlock(this->mutex);
+
+ return SUCCESS;
+}
+
+METHOD(kernel_ipsec_t, query_sa, status_t,
+ private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
+ u_int32_t spi, u_int8_t protocol, mark_t mark, u_int64_t *bytes,
+ u_int64_t *packets, time_t *time)
+{
+ /* It does not seem that WFP provides any means of getting per-SA traffic
+ * statistics. IPsecGetStatistics0/1() provides global stats, and
+ * IPsecSaContextEnum0/1() and IPsecSaEnum0/1() return the configured
+ * values only. */
+ return NOT_SUPPORTED;
+}
+
+METHOD(kernel_ipsec_t, del_sa, status_t,
+ private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
+ u_int32_t spi, u_int8_t protocol, u_int16_t cpi, mark_t mark)
+{
+ entry_t *entry;
+ sa_entry_t key = {
+ .dst = dst,
+ .spi = spi,
+ };
+
+ this->mutex->lock(this->mutex);
+ entry = this->isas->remove(this->isas, &key);
+ this->mutex->unlock(this->mutex);
+
+ if (entry)
+ {
+ /* keep entry until removal of outbound */
+ return SUCCESS;
+ }
+
+ this->mutex->lock(this->mutex);
+ entry = this->osas->remove(this->osas, &key);
+ this->mutex->unlock(this->mutex);
+
+ if (entry)
+ {
+ entry_destroy(this, entry);
+ return SUCCESS;
+ }
+
+ return NOT_FOUND;
+}
+
+METHOD(kernel_ipsec_t, flush_sas, status_t,
+ private_kernel_wfp_ipsec_t *this)
+{
+ return NOT_SUPPORTED;
+}
+
+METHOD(kernel_ipsec_t, add_policy, status_t,
+ private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
+ traffic_selector_t *src_ts, traffic_selector_t *dst_ts,
+ policy_dir_t direction, policy_type_t type, ipsec_sa_cfg_t *sa, mark_t mark,
+ policy_priority_t priority)
+{
+ status_t status = SUCCESS;
+ entry_t *entry;
+ sp_entry_t *sp;
+ sa_entry_t key = {
+ .spi = sa->esp.use ? sa->esp.spi : sa->ah.spi,
+ .dst = dst,
+ };
+
+ if (sa->esp.use && sa->ah.use)
+ {
+ return NOT_SUPPORTED;
+ }
+
+ switch (type)
+ {
+ case POLICY_IPSEC:
+ break;
+ case POLICY_PASS:
+ case POLICY_DROP:
+ return NOT_SUPPORTED;
+ }
+
+ switch (direction)
+ {
+ case POLICY_OUT:
+ break;
+ case POLICY_IN:
+ case POLICY_FWD:
+ /* not required */
+ return SUCCESS;
+ default:
+ return NOT_SUPPORTED;
+ }
+
+ switch (priority)
+ {
+ case POLICY_PRIORITY_DEFAULT:
+ break;
+ case POLICY_PRIORITY_ROUTED:
+ if (!add_trap(this, sa->reqid, FALSE, src, dst, src_ts, dst_ts))
+ {
+ return FAILED;
+ }
+ if (sa->mode == MODE_TUNNEL)
+ {
+ if (!add_trap(this, sa->reqid, TRUE, src, dst, src_ts, dst_ts))
+ {
+ return FAILED;
+ }
+ }
+ return SUCCESS;
+ case POLICY_PRIORITY_FALLBACK:
+ default:
+ return NOT_SUPPORTED;
+ }
+
+ this->mutex->lock(this->mutex);
+ entry = this->osas->get(this->osas, &key);
+ if (entry)
+ {
+ if (sa->mode == MODE_TUNNEL || array_count(entry->sps) == 0)
+ {
+ INIT(sp,
+ .src = src_ts->clone(src_ts),
+ .dst = dst_ts->clone(dst_ts),
+ );
+ array_insert(entry->sps, -1, sp);
+ if (array_count(entry->sps) == sa->policy_count)
+ {
+ if (!install(this, entry))
+ {
+ status = FAILED;
+ }
+ }
+ }
+ else
+ {
+ /* TODO: reinstall with a filter using multiple TS?
+ * Filters are ANDed for a match, but we could install a filter
+ * with the inverse TS set using NOT-matches... */
+ DBG1(DBG_KNL, "multiple transport mode traffic selectors not "
+ "supported by WFP");
+ status = NOT_SUPPORTED;
+ }
+ }
+ else
+ {
+ DBG1(DBG_KNL, "adding SP failed, no SA found for SPI 0x%08x", key.spi);
+ status = FAILED;
+ }
+ this->mutex->unlock(this->mutex);
+
+ return status;
+}
+
+METHOD(kernel_ipsec_t, query_policy, status_t,
+ private_kernel_wfp_ipsec_t *this, traffic_selector_t *src_ts,
+ traffic_selector_t *dst_ts, policy_dir_t direction, mark_t mark,
+ time_t *use_time)
+{
+ /* see query_sa() for some notes */
+ return NOT_SUPPORTED;
+}
+
+METHOD(kernel_ipsec_t, del_policy, status_t,
+ private_kernel_wfp_ipsec_t *this, traffic_selector_t *src_ts,
+ traffic_selector_t *dst_ts, policy_dir_t direction, u_int32_t reqid,
+ mark_t mark, policy_priority_t priority)
+{
+ if (direction == POLICY_OUT && priority == POLICY_PRIORITY_ROUTED)
+ {
+ if (remove_trap(this, reqid, FALSE, src_ts, dst_ts))
+ {
+ remove_trap(this, reqid, TRUE, src_ts, dst_ts);
+ return SUCCESS;
+ }
+ return NOT_FOUND;
+ }
+ /* not required, as we delete the whole SA/SP set during del_sa() */
+ return SUCCESS;
+}
+
+METHOD(kernel_ipsec_t, flush_policies, status_t,
+ private_kernel_wfp_ipsec_t *this)
+{
+ return NOT_SUPPORTED;
+}
+
+/**
+ * Add a bypass policy for a specific UDP port
+ */
+static bool add_bypass(private_kernel_wfp_ipsec_t *this,
+ int family, u_int16_t port, bool inbound, UINT64 *luid)
+{
+ FWPM_FILTER_CONDITION0 *cond, *conds = NULL;
+ int count = 0;
+ DWORD res;
+ UINT64 weight = 0xff00000000000000;
+ FWPM_FILTER0 filter = {
+ .displayData = {
+ .name = L"charon IKE bypass",
+ },
+ .action = {
+ .type = FWP_ACTION_PERMIT,
+ },
+ .weight = {
+ .type = FWP_UINT64,
+ .uint64 = &weight,
+ },
+ };
+
+ switch (family)
+ {
+ case AF_INET:
+ filter.layerKey = inbound ? FWPM_LAYER_INBOUND_TRANSPORT_V4
+ : FWPM_LAYER_OUTBOUND_TRANSPORT_V4;
+ break;
+ case AF_INET6:
+ filter.layerKey = inbound ? FWPM_LAYER_INBOUND_TRANSPORT_V6
+ : FWPM_LAYER_OUTBOUND_TRANSPORT_V6;
+ break;
+ default:
+ return FALSE;
+ }
+
+ cond = append_condition(&conds, &count);
+ cond->fieldKey = FWPM_CONDITION_IP_PROTOCOL;
+ cond->matchType = FWP_MATCH_EQUAL;
+ cond->conditionValue.type = FWP_UINT8;
+ cond->conditionValue.uint8 = IPPROTO_UDP;
+
+ cond = append_condition(&conds, &count);
+ cond->fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
+ cond->matchType = FWP_MATCH_EQUAL;
+ cond->conditionValue.type = FWP_UINT16;
+ cond->conditionValue.uint16 = port;
+
+ filter.numFilterConditions = count;
+ filter.filterCondition = conds;
+
+ res = FwpmFilterAdd0(this->handle, &filter, NULL, luid);
+ free_conditions(conds, count);
+ if (res != ERROR_SUCCESS)
+ {
+ DBG1(DBG_KNL, "installing WFP bypass filter failed: 0x%08x", res);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+METHOD(kernel_ipsec_t, bypass_socket, bool,
+ private_kernel_wfp_ipsec_t *this, int fd, int family)
+{
+ union {
+ struct sockaddr sa;
+ SOCKADDR_IN in;
+ SOCKADDR_IN6 in6;
+ } saddr;
+ int addrlen = sizeof(saddr);
+ UINT64 filter_out, filter_in = 0;
+ u_int16_t port;
+
+ if (getsockname(fd, &saddr.sa, &addrlen) == SOCKET_ERROR)
+ {
+ return FALSE;
+ }
+ switch (family)
+ {
+ case AF_INET:
+ port = ntohs(saddr.in.sin_port);
+ break;
+ case AF_INET6:
+ port = ntohs(saddr.in6.sin6_port);
+ break;
+ default:
+ return FALSE;
+ }
+
+ if (!add_bypass(this, family, port, TRUE, &filter_in) ||
+ !add_bypass(this, family, port, FALSE, &filter_out))
+ {
+ if (filter_in)
+ {
+ FwpmFilterDeleteById0(this->handle, filter_in);
+ }
+ return FALSE;
+ }
+
+ this->mutex->lock(this->mutex);
+ array_insert(this->bypass, ARRAY_TAIL, &filter_in);
+ array_insert(this->bypass, ARRAY_TAIL, &filter_out);
+ this->mutex->unlock(this->mutex);
+
+ return TRUE;
+}
+
+METHOD(kernel_ipsec_t, enable_udp_decap, bool,
+ private_kernel_wfp_ipsec_t *this, int fd, int family, u_int16_t port)
+{
+ return FALSE;
+}
+
+METHOD(kernel_ipsec_t, destroy, void,
+ private_kernel_wfp_ipsec_t *this)
+{
+ UINT64 filter;
+
+ while (array_remove(this->bypass, ARRAY_TAIL, &filter))
+ {
+ FwpmFilterDeleteById0(this->handle, filter);
+ }
+ if (this->handle)
+ {
+ if (this->event)
+ {
+ FwpmNetEventUnsubscribe0(this->handle, this->event);
+ }
+ FwpmProviderDeleteByKey0(this->handle, &this->provider.providerKey);
+ FwpmEngineClose0(this->handle);
+ }
+ array_destroy(this->bypass);
+ this->tsas->destroy(this->tsas);
+ this->isas->destroy(this->isas);
+ this->osas->destroy(this->osas);
+ this->routes->destroy(this->routes);
+ this->traps->destroy(this->traps);
+ this->mutex->destroy(this->mutex);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+kernel_wfp_ipsec_t *kernel_wfp_ipsec_create()
+{
+ private_kernel_wfp_ipsec_t *this;
+ DWORD res;
+ FWPM_SESSION0 session = {
+ .displayData = {
+ .name = L"charon",
+ .description = L"strongSwan IKE kernel-wfp backend",
+ },
+ };
+
+ INIT(this,
+ .public = {
+ .interface = {
+ .get_features = _get_features,
+ .get_spi = _get_spi,
+ .get_cpi = _get_cpi,
+ .add_sa = _add_sa,
+ .update_sa = _update_sa,
+ .query_sa = _query_sa,
+ .del_sa = _del_sa,
+ .flush_sas = _flush_sas,
+ .add_policy = _add_policy,
+ .query_policy = _query_policy,
+ .del_policy = _del_policy,
+ .flush_policies = _flush_policies,
+ .bypass_socket = _bypass_socket,
+ .enable_udp_decap = _enable_udp_decap,
+ .destroy = _destroy,
+ },
+ },
+ .provider = {
+ .displayData = {
+ .name = L"charon",
+ .description = L"strongSwan IKE kernel-wfp backend",
+ },
+ .providerKey = { 0x59cdae2e, 0xf6bb, 0x4c09,
+ { 0xa9,0x59,0x9d,0x91,0xac,0xaf,0xf9,0x19 }},
+ },
+ .mutex = mutex_create(MUTEX_TYPE_RECURSIVE),
+ .bypass = array_create(sizeof(UINT64), 2),
+ .tsas = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 4),
+ .isas = hashtable_create((void*)hash_sa, (void*)equals_sa, 4),
+ .osas = hashtable_create((void*)hash_sa, (void*)equals_sa, 4),
+ .routes = hashtable_create((void*)hash_route, (void*)equals_route, 4),
+ .traps = hashtable_create((void*)hash_trap, (void*)equals_trap, 4),
+ );
+
+ if (!init_spi(this))
+ {
+ destroy(this);
+ return NULL;
+ }
+
+ res = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session,
+ &this->handle);
+ if (res != ERROR_SUCCESS)
+ {
+ DBG1(DBG_KNL, "opening WFP engine failed: 0x%08x", res);
+ destroy(this);
+ return NULL;
+ }
+
+ res = FwpmProviderAdd0(this->handle, &this->provider, NULL);
+ if (res != ERROR_SUCCESS && res != FWP_E_ALREADY_EXISTS)
+ {
+ DBG1(DBG_KNL, "registering WFP provider failed: 0x%08x", res);
+ destroy(this);
+ return NULL;
+ }
+
+ if (!register_events(this))
+ {
+ destroy(this);
+ return NULL;
+ }
+
+ return &this->public;
+}
diff --git a/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.h b/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.h
new file mode 100644
index 000000000..d61c230e4
--- /dev/null
+++ b/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 revosec AG
+ *
+ * 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.
+ */
+
+/**
+ * @defgroup kernel_wfp_ipsec kernel_wfp_ipsec
+ * @{ @ingroup kernel_wfp
+ */
+
+#ifndef KERNEL_WFP_IPSEC_H_
+#define KERNEL_WFP_IPSEC_H_
+
+#include <library.h>
+#include <kernel/kernel_ipsec.h>
+
+typedef struct kernel_wfp_ipsec_t kernel_wfp_ipsec_t;
+
+/**
+ * Windows Filter Platform based IPsec kernel backend.
+ */
+struct kernel_wfp_ipsec_t {
+
+ /**
+ * Implements kernel_ipsec_t interface
+ */
+ kernel_ipsec_t interface;
+};
+
+/**
+ * Create WFP kernel interface instance.
+ *
+ * @return kernel_wfp_ipsec_t instance
+ */
+kernel_wfp_ipsec_t *kernel_wfp_ipsec_create();
+
+#endif /** KERNEL_WFP_IPSEC_H_ @}*/
diff --git a/src/libcharon/plugins/kernel_wfp/kernel_wfp_plugin.c b/src/libcharon/plugins/kernel_wfp/kernel_wfp_plugin.c
new file mode 100644
index 000000000..e465b0a76
--- /dev/null
+++ b/src/libcharon/plugins/kernel_wfp/kernel_wfp_plugin.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 revosec AG
+ *
+ * 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 "kernel_wfp_plugin.h"
+#include "kernel_wfp_ipsec.h"
+
+#include <daemon.h>
+
+typedef struct private_kernel_wfp_plugin_t private_kernel_wfp_plugin_t;
+
+/**
+ * Private data of kernel-wfp plugin
+ */
+struct private_kernel_wfp_plugin_t {
+
+ /**
+ * Implements plugin interface
+ */
+ kernel_wfp_plugin_t public;
+};
+
+METHOD(plugin_t, get_name, char*,
+ private_kernel_wfp_plugin_t *this)
+{
+ return "kernel-wfp";
+}
+
+METHOD(plugin_t, get_features, int,
+ private_kernel_wfp_plugin_t *this, plugin_feature_t *features[])
+{
+ static plugin_feature_t f[] = {
+ PLUGIN_CALLBACK(kernel_ipsec_register, kernel_wfp_ipsec_create),
+ PLUGIN_PROVIDE(CUSTOM, "kernel-ipsec"),
+ PLUGIN_DEPENDS(RNG, RNG_WEAK),
+ PLUGIN_DEPENDS(RNG, RNG_STRONG),
+ };
+ *features = f;
+ return countof(f);
+}
+
+METHOD(plugin_t, destroy, void,
+ private_kernel_wfp_plugin_t *this)
+{
+ free(this);
+}
+
+/*
+ * see header file
+ */
+plugin_t *kernel_wfp_plugin_create()
+{
+ private_kernel_wfp_plugin_t *this;
+
+ INIT(this,
+ .public = {
+ .plugin = {
+ .get_name = _get_name,
+ .get_features = _get_features,
+ .destroy = _destroy,
+ },
+ },
+ );
+
+ return &this->public.plugin;
+}
diff --git a/src/libcharon/plugins/kernel_wfp/kernel_wfp_plugin.h b/src/libcharon/plugins/kernel_wfp/kernel_wfp_plugin.h
new file mode 100644
index 000000000..a538e34a1
--- /dev/null
+++ b/src/libcharon/plugins/kernel_wfp/kernel_wfp_plugin.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 revosec AG
+ *
+ * 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.
+ */
+
+/**
+ * @defgroup kernel_wfp kernel_wfp
+ * @ingroup cplugins
+ *
+ * @defgroup kernel_wfp_plugin kernel_wfp_plugin
+ * @{ @ingroup kernel_wfp
+ */
+
+#ifndef KERNEL_WFP_PLUGIN_H_
+#define KERNEL_WFP_PLUGIN_H_
+
+#include <library.h>
+#include <plugins/plugin.h>
+
+typedef struct kernel_wfp_plugin_t kernel_wfp_plugin_t;
+
+/**
+ * Windows Filter Platform based IPsec backend plugin.
+ */
+struct kernel_wfp_plugin_t {
+
+ /**
+ * implements plugin interface
+ */
+ plugin_t plugin;
+};
+
+#endif /** KERNEL_WFP_PLUGIN_H_ @}*/
diff --git a/src/libcharon/plugins/kernel_wfp/mingw-w64-4.8.1.diff b/src/libcharon/plugins/kernel_wfp/mingw-w64-4.8.1.diff
new file mode 100644
index 000000000..c72b94c07
--- /dev/null
+++ b/src/libcharon/plugins/kernel_wfp/mingw-w64-4.8.1.diff
@@ -0,0 +1,26 @@
+diff -Naur /mingw-orig/x86_64-w64-mingw32/include/fwptypes.h /mingw/x86_64-w64-mingw32/include/fwptypes.h
+--- /mingw-orig/x86_64-w64-mingw32/include/fwptypes.h 2013-08-30 07:15:40 +0200
++++ /mingw/x86_64-w64-mingw32/include/fwptypes.h 2014-01-02 16:32:26 +0100
+@@ -333,11 +333,6 @@
+ } __C89_NAMELESSUNIONNAME;
+ } FWP_CONDITION_VALUE0;
+
+-typedef struct FWPM_DISPLAY_DATA0_ {
+- wchar_t *name;
+- wchar_t *description;
+-} FWPM_DISPLAY_DATA0;
+-
+ #endif /* WINAPI_PARTITION_DESKTOP. */
+ /* Begin additional prototypes for all interfaces */
+
+diff -Naur /mingw-orig/x86_64-w64-mingw32/include/iketypes.h /mingw/x86_64-w64-mingw32/include/iketypes.h
+--- /mingw-orig/x86_64-w64-mingw32/include/iketypes.h 2013-08-30 07:15:40 +0200
++++ /mingw/x86_64-w64-mingw32/include/iketypes.h 2014-01-02 16:31:12 +0100
+@@ -212,7 +212,6 @@
+ FWP_BYTE_BLOB presharedKey;
+ UINT32 flags;
+ } IKEEXT_PRESHARED_KEY_AUTHENTICATION1;
+-#endif
+
+ typedef struct IKEEXT_CERTIFICATE_AUTHENTICATION0_ {
+ IKEEXT_CERT_CONFIG_TYPE inboundConfigType;