diff options
author | Yves-Alexis Perez <corsac@debian.org> | 2016-03-24 11:59:32 +0100 |
---|---|---|
committer | Yves-Alexis Perez <corsac@debian.org> | 2016-03-24 11:59:32 +0100 |
commit | 518dd33c94e041db0444c7d1f33da363bb8e3faf (patch) | |
tree | e8d1665ffadff7ec40228dda47e81f8f4691cd07 /src/libcharon | |
parent | f42f239a632306ed082f6fde878977248eea85cf (diff) | |
download | vyos-strongswan-518dd33c94e041db0444c7d1f33da363bb8e3faf.tar.gz vyos-strongswan-518dd33c94e041db0444c7d1f33da363bb8e3faf.zip |
Imported Upstream version 5.4.0
Diffstat (limited to 'src/libcharon')
282 files changed, 25432 insertions, 2106 deletions
diff --git a/src/libcharon/Android.mk b/src/libcharon/Android.mk index 10085794b..55e6bc58b 100644 --- a/src/libcharon/Android.mk +++ b/src/libcharon/Android.mk @@ -47,7 +47,10 @@ encoding/payloads/unknown_payload.c encoding/payloads/unknown_payload.h \ encoding/payloads/vendor_id_payload.c encoding/payloads/vendor_id_payload.h \ encoding/payloads/hash_payload.c encoding/payloads/hash_payload.h \ encoding/payloads/fragment_payload.c encoding/payloads/fragment_payload.h \ -kernel/kernel_handler.c kernel/kernel_handler.h \ +kernel/kernel_interface.c kernel/kernel_interface.h \ +kernel/kernel_ipsec.c kernel/kernel_ipsec.h \ +kernel/kernel_net.c kernel/kernel_net.h \ +kernel/kernel_listener.h kernel/kernel_handler.c kernel/kernel_handler.h \ network/receiver.c network/receiver.h network/sender.c network/sender.h \ network/socket.c network/socket.h \ network/socket_manager.c network/socket_manager.h \ @@ -56,6 +59,7 @@ processing/jobs/delete_child_sa_job.c processing/jobs/delete_child_sa_job.h \ processing/jobs/delete_ike_sa_job.c processing/jobs/delete_ike_sa_job.h \ processing/jobs/migrate_job.c processing/jobs/migrate_job.h \ processing/jobs/process_message_job.c processing/jobs/process_message_job.h \ +processing/jobs/redirect_job.c processing/jobs/redirect_job.h \ processing/jobs/rekey_child_sa_job.c processing/jobs/rekey_child_sa_job.h \ processing/jobs/rekey_ike_sa_job.c processing/jobs/rekey_ike_sa_job.h \ processing/jobs/retransmit_job.c processing/jobs/retransmit_job.h \ @@ -81,6 +85,7 @@ sa/child_sa_manager.c sa/child_sa_manager.h \ sa/task_manager.h sa/task_manager.c \ sa/shunt_manager.c sa/shunt_manager.h \ sa/trap_manager.c sa/trap_manager.h \ +sa/redirect_provider.h sa/redirect_manager.c sa/redirect_manager.h \ sa/task.c sa/task.h libcharon_la_SOURCES += \ @@ -104,8 +109,10 @@ sa/ikev2/tasks/ike_mobike.c sa/ikev2/tasks/ike_mobike.h \ sa/ikev2/tasks/ike_rekey.c sa/ikev2/tasks/ike_rekey.h \ sa/ikev2/tasks/ike_reauth.c sa/ikev2/tasks/ike_reauth.h \ sa/ikev2/tasks/ike_reauth_complete.c sa/ikev2/tasks/ike_reauth_complete.h \ +sa/ikev2/tasks/ike_redirect.c sa/ikev2/tasks/ike_redirect.h \ sa/ikev2/tasks/ike_auth_lifetime.c sa/ikev2/tasks/ike_auth_lifetime.h \ -sa/ikev2/tasks/ike_vendor.c sa/ikev2/tasks/ike_vendor.h +sa/ikev2/tasks/ike_vendor.c sa/ikev2/tasks/ike_vendor.h \ +sa/ikev2/tasks/ike_verify_peer_cert.c sa/ikev2/tasks/ike_verify_peer_cert.h libcharon_la_SOURCES += \ sa/ikev1/keymat_v1.c sa/ikev1/keymat_v1.h \ @@ -149,6 +156,8 @@ endif LOCAL_SRC_FILES += $(call add_plugin, attr) +LOCAL_SRC_FILES += $(call add_plugin, p-cscf) + LOCAL_SRC_FILES += $(call add_plugin, eap-aka) LOCAL_SRC_FILES += $(call add_plugin, eap-aka-3gpp2) @@ -216,6 +225,10 @@ endif LOCAL_SRC_FILES += $(call add_plugin, load-tester) +LOCAL_SRC_FILES += $(call add_plugin, kernel-pfkey) + +LOCAL_SRC_FILES += $(call add_plugin, kernel-netlink) + LOCAL_SRC_FILES += $(call add_plugin, socket-default) LOCAL_SRC_FILES += $(call add_plugin, socket-dynamic) @@ -228,7 +241,6 @@ endif # build libcharon -------------------------------------------------------------- LOCAL_C_INCLUDES += \ - $(strongswan_PATH)/src/libhydra \ $(strongswan_PATH)/src/libstrongswan LOCAL_CFLAGS := $(strongswan_CFLAGS) @@ -241,6 +253,6 @@ LOCAL_ARM_MODE := arm LOCAL_PRELINK_MODULE := false -LOCAL_SHARED_LIBRARIES += libstrongswan libhydra +LOCAL_SHARED_LIBRARIES += libstrongswan include $(BUILD_SHARED_LIBRARY) diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am index cd81a5eee..9f0707813 100644 --- a/src/libcharon/Makefile.am +++ b/src/libcharon/Makefile.am @@ -8,6 +8,7 @@ attributes/mem_pool.c attributes/mem_pool.h \ bus/bus.c bus/bus.h \ bus/listeners/listener.h \ bus/listeners/logger.h \ +bus/listeners/custom_logger.h \ bus/listeners/file_logger.c bus/listeners/file_logger.h \ config/backend_manager.c config/backend_manager.h config/backend.h \ config/child_cfg.c config/child_cfg.h \ @@ -45,7 +46,10 @@ encoding/payloads/unknown_payload.c encoding/payloads/unknown_payload.h \ encoding/payloads/vendor_id_payload.c encoding/payloads/vendor_id_payload.h \ encoding/payloads/hash_payload.c encoding/payloads/hash_payload.h \ encoding/payloads/fragment_payload.c encoding/payloads/fragment_payload.h \ -kernel/kernel_handler.c kernel/kernel_handler.h \ +kernel/kernel_interface.c kernel/kernel_interface.h \ +kernel/kernel_ipsec.c kernel/kernel_ipsec.h \ +kernel/kernel_net.c kernel/kernel_net.h \ +kernel/kernel_listener.h kernel/kernel_handler.c kernel/kernel_handler.h \ network/receiver.c network/receiver.h network/sender.c network/sender.h \ network/socket.c network/socket.h \ network/socket_manager.c network/socket_manager.h \ @@ -54,6 +58,7 @@ processing/jobs/delete_child_sa_job.c processing/jobs/delete_child_sa_job.h \ processing/jobs/delete_ike_sa_job.c processing/jobs/delete_ike_sa_job.h \ processing/jobs/migrate_job.c processing/jobs/migrate_job.h \ processing/jobs/process_message_job.c processing/jobs/process_message_job.h \ +processing/jobs/redirect_job.c processing/jobs/redirect_job.h \ processing/jobs/rekey_child_sa_job.c processing/jobs/rekey_child_sa_job.h \ processing/jobs/rekey_ike_sa_job.c processing/jobs/rekey_ike_sa_job.h \ processing/jobs/retransmit_job.c processing/jobs/retransmit_job.h \ @@ -79,6 +84,7 @@ sa/child_sa_manager.c sa/child_sa_manager.h \ sa/task_manager.h sa/task_manager.c \ sa/shunt_manager.c sa/shunt_manager.h \ sa/trap_manager.c sa/trap_manager.h \ +sa/redirect_provider.h sa/redirect_manager.c sa/redirect_manager.h \ sa/task.c sa/task.h if USE_IKEV2 @@ -103,8 +109,10 @@ sa/ikev2/tasks/ike_mobike.c sa/ikev2/tasks/ike_mobike.h \ sa/ikev2/tasks/ike_rekey.c sa/ikev2/tasks/ike_rekey.h \ sa/ikev2/tasks/ike_reauth.c sa/ikev2/tasks/ike_reauth.h \ sa/ikev2/tasks/ike_reauth_complete.c sa/ikev2/tasks/ike_reauth_complete.h \ +sa/ikev2/tasks/ike_redirect.c sa/ikev2/tasks/ike_redirect.h \ sa/ikev2/tasks/ike_auth_lifetime.c sa/ikev2/tasks/ike_auth_lifetime.h \ -sa/ikev2/tasks/ike_vendor.c sa/ikev2/tasks/ike_vendor.h +sa/ikev2/tasks/ike_vendor.c sa/ikev2/tasks/ike_vendor.h \ +sa/ikev2/tasks/ike_verify_peer_cert.c sa/ikev2/tasks/ike_verify_peer_cert.h endif if USE_IKEV1 @@ -142,7 +150,6 @@ daemon.lo : $(top_builddir)/config.status AM_CPPFLAGS = \ -I${linux_headers} \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -DIPSEC_DIR=\"${ipsecdir}\" \ -DIPSEC_PIDDIR=\"${piddir}\" @@ -152,7 +159,6 @@ AM_LDFLAGS = \ libcharon_la_LIBADD = \ $(top_builddir)/src/libstrongswan/libstrongswan.la \ - $(top_builddir)/src/libhydra/libhydra.la \ -lm $(PTHREADLIB) $(DLLIB) $(SOCKLIB) if USE_WINDOWS @@ -483,6 +489,13 @@ if MONOLITHIC endif endif +if USE_P_CSCF + SUBDIRS += plugins/p_cscf +if MONOLITHIC + libcharon_la_LIBADD += plugins/p_cscf/libstrongswan-p-cscf.la +endif +endif + if USE_ANDROID_DNS SUBDIRS += plugins/android_dns if MONOLITHIC @@ -511,6 +524,27 @@ if MONOLITHIC endif endif +if USE_KERNEL_PFKEY + SUBDIRS += plugins/kernel_pfkey +if MONOLITHIC + libcharon_la_LIBADD += plugins/kernel_pfkey/libstrongswan-kernel-pfkey.la +endif +endif + +if USE_KERNEL_PFROUTE + SUBDIRS += plugins/kernel_pfroute +if MONOLITHIC + libcharon_la_LIBADD += plugins/kernel_pfroute/libstrongswan-kernel-pfroute.la +endif +endif + +if USE_KERNEL_NETLINK + SUBDIRS += plugins/kernel_netlink +if MONOLITHIC + libcharon_la_LIBADD += plugins/kernel_netlink/libstrongswan-kernel-netlink.la +endif +endif + if USE_KERNEL_LIBIPSEC SUBDIRS += plugins/kernel_libipsec if MONOLITHIC diff --git a/src/libcharon/Makefile.in b/src/libcharon/Makefile.in index 3d425e0b4..2ccae216e 100644 --- a/src/libcharon/Makefile.in +++ b/src/libcharon/Makefile.in @@ -99,8 +99,10 @@ host_triplet = @host@ @USE_IKEV2_TRUE@sa/ikev2/tasks/ike_rekey.c sa/ikev2/tasks/ike_rekey.h \ @USE_IKEV2_TRUE@sa/ikev2/tasks/ike_reauth.c sa/ikev2/tasks/ike_reauth.h \ @USE_IKEV2_TRUE@sa/ikev2/tasks/ike_reauth_complete.c sa/ikev2/tasks/ike_reauth_complete.h \ +@USE_IKEV2_TRUE@sa/ikev2/tasks/ike_redirect.c sa/ikev2/tasks/ike_redirect.h \ @USE_IKEV2_TRUE@sa/ikev2/tasks/ike_auth_lifetime.c sa/ikev2/tasks/ike_auth_lifetime.h \ -@USE_IKEV2_TRUE@sa/ikev2/tasks/ike_vendor.c sa/ikev2/tasks/ike_vendor.h +@USE_IKEV2_TRUE@sa/ikev2/tasks/ike_vendor.c sa/ikev2/tasks/ike_vendor.h \ +@USE_IKEV2_TRUE@sa/ikev2/tasks/ike_verify_peer_cert.c sa/ikev2/tasks/ike_verify_peer_cert.h @USE_IKEV1_TRUE@am__append_2 = \ @USE_IKEV1_TRUE@sa/ikev1/keymat_v1.c sa/ikev1/keymat_v1.h \ @@ -221,58 +223,66 @@ host_triplet = @host@ @MONOLITHIC_TRUE@@USE_DHCP_TRUE@am__append_85 = plugins/dhcp/libstrongswan-dhcp.la @USE_OSX_ATTR_TRUE@am__append_86 = plugins/osx_attr @MONOLITHIC_TRUE@@USE_OSX_ATTR_TRUE@am__append_87 = plugins/osx_attr/libstrongswan-osx-attr.la -@USE_ANDROID_DNS_TRUE@am__append_88 = plugins/android_dns -@MONOLITHIC_TRUE@@USE_ANDROID_DNS_TRUE@am__append_89 = plugins/android_dns/libstrongswan-android-dns.la -@USE_ANDROID_LOG_TRUE@am__append_90 = plugins/android_log -@MONOLITHIC_TRUE@@USE_ANDROID_LOG_TRUE@am__append_91 = plugins/android_log/libstrongswan-android-log.la -@USE_MAEMO_TRUE@am__append_92 = plugins/maemo -@MONOLITHIC_TRUE@@USE_MAEMO_TRUE@am__append_93 = plugins/maemo/libstrongswan-maemo.la -@USE_HA_TRUE@am__append_94 = plugins/ha -@MONOLITHIC_TRUE@@USE_HA_TRUE@am__append_95 = plugins/ha/libstrongswan-ha.la -@USE_KERNEL_LIBIPSEC_TRUE@am__append_96 = plugins/kernel_libipsec -@MONOLITHIC_TRUE@@USE_KERNEL_LIBIPSEC_TRUE@am__append_97 = plugins/kernel_libipsec/libstrongswan-kernel-libipsec.la -@USE_KERNEL_WFP_TRUE@am__append_98 = plugins/kernel_wfp -@MONOLITHIC_TRUE@@USE_KERNEL_WFP_TRUE@am__append_99 = plugins/kernel_wfp/libstrongswan-kernel-wfp.la -@USE_KERNEL_IPH_TRUE@am__append_100 = plugins/kernel_iph -@MONOLITHIC_TRUE@@USE_KERNEL_IPH_TRUE@am__append_101 = plugins/kernel_iph/libstrongswan-kernel-iph.la -@USE_WHITELIST_TRUE@am__append_102 = plugins/whitelist -@MONOLITHIC_TRUE@@USE_WHITELIST_TRUE@am__append_103 = plugins/whitelist/libstrongswan-whitelist.la -@USE_LOOKIP_TRUE@am__append_104 = plugins/lookip -@MONOLITHIC_TRUE@@USE_LOOKIP_TRUE@am__append_105 = plugins/lookip/libstrongswan-lookip.la -@USE_ERROR_NOTIFY_TRUE@am__append_106 = plugins/error_notify -@MONOLITHIC_TRUE@@USE_ERROR_NOTIFY_TRUE@am__append_107 = plugins/error_notify/libstrongswan-error-notify.la -@USE_CERTEXPIRE_TRUE@am__append_108 = plugins/certexpire -@MONOLITHIC_TRUE@@USE_CERTEXPIRE_TRUE@am__append_109 = plugins/certexpire/libstrongswan-certexpire.la -@USE_SYSTIME_FIX_TRUE@am__append_110 = plugins/systime_fix -@MONOLITHIC_TRUE@@USE_SYSTIME_FIX_TRUE@am__append_111 = plugins/systime_fix/libstrongswan-systime-fix.la -@USE_LED_TRUE@am__append_112 = plugins/led -@MONOLITHIC_TRUE@@USE_LED_TRUE@am__append_113 = plugins/led/libstrongswan-led.la -@USE_DUPLICHECK_TRUE@am__append_114 = plugins/duplicheck -@MONOLITHIC_TRUE@@USE_DUPLICHECK_TRUE@am__append_115 = plugins/duplicheck/libstrongswan-duplicheck.la -@USE_COUPLING_TRUE@am__append_116 = plugins/coupling -@MONOLITHIC_TRUE@@USE_COUPLING_TRUE@am__append_117 = plugins/coupling/libstrongswan-coupling.la -@USE_RADATTR_TRUE@am__append_118 = plugins/radattr -@MONOLITHIC_TRUE@@USE_RADATTR_TRUE@am__append_119 = plugins/radattr/libstrongswan-radattr.la -@USE_UCI_TRUE@am__append_120 = plugins/uci -@MONOLITHIC_TRUE@@USE_UCI_TRUE@am__append_121 = plugins/uci/libstrongswan-uci.la -@USE_ADDRBLOCK_TRUE@am__append_122 = plugins/addrblock -@MONOLITHIC_TRUE@@USE_ADDRBLOCK_TRUE@am__append_123 = plugins/addrblock/libstrongswan-addrblock.la -@USE_UNITY_TRUE@am__append_124 = plugins/unity -@MONOLITHIC_TRUE@@USE_UNITY_TRUE@am__append_125 = plugins/unity/libstrongswan-unity.la -@USE_XAUTH_GENERIC_TRUE@am__append_126 = plugins/xauth_generic -@MONOLITHIC_TRUE@@USE_XAUTH_GENERIC_TRUE@am__append_127 = plugins/xauth_generic/libstrongswan-xauth-generic.la -@USE_XAUTH_EAP_TRUE@am__append_128 = plugins/xauth_eap -@MONOLITHIC_TRUE@@USE_XAUTH_EAP_TRUE@am__append_129 = plugins/xauth_eap/libstrongswan-xauth-eap.la -@USE_XAUTH_PAM_TRUE@am__append_130 = plugins/xauth_pam -@MONOLITHIC_TRUE@@USE_XAUTH_PAM_TRUE@am__append_131 = plugins/xauth_pam/libstrongswan-xauth-pam.la -@USE_XAUTH_NOAUTH_TRUE@am__append_132 = plugins/xauth_noauth -@MONOLITHIC_TRUE@@USE_XAUTH_NOAUTH_TRUE@am__append_133 = plugins/xauth_noauth/libstrongswan-xauth-noauth.la -@USE_RESOLVE_TRUE@am__append_134 = plugins/resolve -@MONOLITHIC_TRUE@@USE_RESOLVE_TRUE@am__append_135 = plugins/resolve/libstrongswan-resolve.la -@USE_ATTR_TRUE@am__append_136 = plugins/attr -@MONOLITHIC_TRUE@@USE_ATTR_TRUE@am__append_137 = plugins/attr/libstrongswan-attr.la -@USE_ATTR_SQL_TRUE@am__append_138 = plugins/attr_sql -@MONOLITHIC_TRUE@@USE_ATTR_SQL_TRUE@am__append_139 = plugins/attr_sql/libstrongswan-attr-sql.la +@USE_P_CSCF_TRUE@am__append_88 = plugins/p_cscf +@MONOLITHIC_TRUE@@USE_P_CSCF_TRUE@am__append_89 = plugins/p_cscf/libstrongswan-p-cscf.la +@USE_ANDROID_DNS_TRUE@am__append_90 = plugins/android_dns +@MONOLITHIC_TRUE@@USE_ANDROID_DNS_TRUE@am__append_91 = plugins/android_dns/libstrongswan-android-dns.la +@USE_ANDROID_LOG_TRUE@am__append_92 = plugins/android_log +@MONOLITHIC_TRUE@@USE_ANDROID_LOG_TRUE@am__append_93 = plugins/android_log/libstrongswan-android-log.la +@USE_MAEMO_TRUE@am__append_94 = plugins/maemo +@MONOLITHIC_TRUE@@USE_MAEMO_TRUE@am__append_95 = plugins/maemo/libstrongswan-maemo.la +@USE_HA_TRUE@am__append_96 = plugins/ha +@MONOLITHIC_TRUE@@USE_HA_TRUE@am__append_97 = plugins/ha/libstrongswan-ha.la +@USE_KERNEL_PFKEY_TRUE@am__append_98 = plugins/kernel_pfkey +@MONOLITHIC_TRUE@@USE_KERNEL_PFKEY_TRUE@am__append_99 = plugins/kernel_pfkey/libstrongswan-kernel-pfkey.la +@USE_KERNEL_PFROUTE_TRUE@am__append_100 = plugins/kernel_pfroute +@MONOLITHIC_TRUE@@USE_KERNEL_PFROUTE_TRUE@am__append_101 = plugins/kernel_pfroute/libstrongswan-kernel-pfroute.la +@USE_KERNEL_NETLINK_TRUE@am__append_102 = plugins/kernel_netlink +@MONOLITHIC_TRUE@@USE_KERNEL_NETLINK_TRUE@am__append_103 = plugins/kernel_netlink/libstrongswan-kernel-netlink.la +@USE_KERNEL_LIBIPSEC_TRUE@am__append_104 = plugins/kernel_libipsec +@MONOLITHIC_TRUE@@USE_KERNEL_LIBIPSEC_TRUE@am__append_105 = plugins/kernel_libipsec/libstrongswan-kernel-libipsec.la +@USE_KERNEL_WFP_TRUE@am__append_106 = plugins/kernel_wfp +@MONOLITHIC_TRUE@@USE_KERNEL_WFP_TRUE@am__append_107 = plugins/kernel_wfp/libstrongswan-kernel-wfp.la +@USE_KERNEL_IPH_TRUE@am__append_108 = plugins/kernel_iph +@MONOLITHIC_TRUE@@USE_KERNEL_IPH_TRUE@am__append_109 = plugins/kernel_iph/libstrongswan-kernel-iph.la +@USE_WHITELIST_TRUE@am__append_110 = plugins/whitelist +@MONOLITHIC_TRUE@@USE_WHITELIST_TRUE@am__append_111 = plugins/whitelist/libstrongswan-whitelist.la +@USE_LOOKIP_TRUE@am__append_112 = plugins/lookip +@MONOLITHIC_TRUE@@USE_LOOKIP_TRUE@am__append_113 = plugins/lookip/libstrongswan-lookip.la +@USE_ERROR_NOTIFY_TRUE@am__append_114 = plugins/error_notify +@MONOLITHIC_TRUE@@USE_ERROR_NOTIFY_TRUE@am__append_115 = plugins/error_notify/libstrongswan-error-notify.la +@USE_CERTEXPIRE_TRUE@am__append_116 = plugins/certexpire +@MONOLITHIC_TRUE@@USE_CERTEXPIRE_TRUE@am__append_117 = plugins/certexpire/libstrongswan-certexpire.la +@USE_SYSTIME_FIX_TRUE@am__append_118 = plugins/systime_fix +@MONOLITHIC_TRUE@@USE_SYSTIME_FIX_TRUE@am__append_119 = plugins/systime_fix/libstrongswan-systime-fix.la +@USE_LED_TRUE@am__append_120 = plugins/led +@MONOLITHIC_TRUE@@USE_LED_TRUE@am__append_121 = plugins/led/libstrongswan-led.la +@USE_DUPLICHECK_TRUE@am__append_122 = plugins/duplicheck +@MONOLITHIC_TRUE@@USE_DUPLICHECK_TRUE@am__append_123 = plugins/duplicheck/libstrongswan-duplicheck.la +@USE_COUPLING_TRUE@am__append_124 = plugins/coupling +@MONOLITHIC_TRUE@@USE_COUPLING_TRUE@am__append_125 = plugins/coupling/libstrongswan-coupling.la +@USE_RADATTR_TRUE@am__append_126 = plugins/radattr +@MONOLITHIC_TRUE@@USE_RADATTR_TRUE@am__append_127 = plugins/radattr/libstrongswan-radattr.la +@USE_UCI_TRUE@am__append_128 = plugins/uci +@MONOLITHIC_TRUE@@USE_UCI_TRUE@am__append_129 = plugins/uci/libstrongswan-uci.la +@USE_ADDRBLOCK_TRUE@am__append_130 = plugins/addrblock +@MONOLITHIC_TRUE@@USE_ADDRBLOCK_TRUE@am__append_131 = plugins/addrblock/libstrongswan-addrblock.la +@USE_UNITY_TRUE@am__append_132 = plugins/unity +@MONOLITHIC_TRUE@@USE_UNITY_TRUE@am__append_133 = plugins/unity/libstrongswan-unity.la +@USE_XAUTH_GENERIC_TRUE@am__append_134 = plugins/xauth_generic +@MONOLITHIC_TRUE@@USE_XAUTH_GENERIC_TRUE@am__append_135 = plugins/xauth_generic/libstrongswan-xauth-generic.la +@USE_XAUTH_EAP_TRUE@am__append_136 = plugins/xauth_eap +@MONOLITHIC_TRUE@@USE_XAUTH_EAP_TRUE@am__append_137 = plugins/xauth_eap/libstrongswan-xauth-eap.la +@USE_XAUTH_PAM_TRUE@am__append_138 = plugins/xauth_pam +@MONOLITHIC_TRUE@@USE_XAUTH_PAM_TRUE@am__append_139 = plugins/xauth_pam/libstrongswan-xauth-pam.la +@USE_XAUTH_NOAUTH_TRUE@am__append_140 = plugins/xauth_noauth +@MONOLITHIC_TRUE@@USE_XAUTH_NOAUTH_TRUE@am__append_141 = plugins/xauth_noauth/libstrongswan-xauth-noauth.la +@USE_RESOLVE_TRUE@am__append_142 = plugins/resolve +@MONOLITHIC_TRUE@@USE_RESOLVE_TRUE@am__append_143 = plugins/resolve/libstrongswan-resolve.la +@USE_ATTR_TRUE@am__append_144 = plugins/attr +@MONOLITHIC_TRUE@@USE_ATTR_TRUE@am__append_145 = plugins/attr/libstrongswan-attr.la +@USE_ATTR_SQL_TRUE@am__append_146 = plugins/attr_sql +@MONOLITHIC_TRUE@@USE_ATTR_SQL_TRUE@am__append_147 = plugins/attr_sql/libstrongswan-attr-sql.la subdir = src/libcharon DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/depcomp @@ -325,47 +335,47 @@ LTLIBRARIES = $(ipseclib_LTLIBRARIES) am__DEPENDENCIES_1 = libcharon_la_DEPENDENCIES = \ $(top_builddir)/src/libstrongswan/libstrongswan.la \ - $(top_builddir)/src/libhydra/libhydra.la $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__append_7) $(am__append_9) \ - $(am__append_11) $(am__append_13) $(am__append_15) \ - $(am__append_17) $(am__append_19) $(am__append_21) \ - $(am__append_23) $(am__append_25) $(am__append_27) \ - $(am__append_29) $(am__append_31) $(am__append_33) \ - $(am__append_35) $(am__append_37) $(am__append_39) \ - $(am__append_41) $(am__append_43) $(am__append_45) \ - $(am__append_47) $(am__append_49) $(am__append_51) \ - $(am__append_53) $(am__append_54) $(am__append_56) \ - $(am__append_58) $(am__append_60) $(am__append_62) \ - $(am__append_64) $(am__append_66) $(am__append_68) \ - $(am__append_70) $(am__append_72) $(am__append_73) \ - $(am__append_74) $(am__append_76) $(am__append_78) \ - $(am__append_79) $(am__append_81) $(am__append_83) \ - $(am__append_85) $(am__append_87) $(am__append_89) \ - $(am__append_91) $(am__append_93) $(am__append_95) \ - $(am__append_97) $(am__append_99) $(am__append_101) \ - $(am__append_103) $(am__append_105) $(am__append_107) \ - $(am__append_109) $(am__append_111) $(am__append_113) \ - $(am__append_115) $(am__append_117) $(am__append_119) \ - $(am__append_121) $(am__append_123) $(am__append_125) \ - $(am__append_127) $(am__append_129) $(am__append_131) \ - $(am__append_133) $(am__append_135) $(am__append_137) \ - $(am__append_139) + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(am__append_7) \ + $(am__append_9) $(am__append_11) $(am__append_13) \ + $(am__append_15) $(am__append_17) $(am__append_19) \ + $(am__append_21) $(am__append_23) $(am__append_25) \ + $(am__append_27) $(am__append_29) $(am__append_31) \ + $(am__append_33) $(am__append_35) $(am__append_37) \ + $(am__append_39) $(am__append_41) $(am__append_43) \ + $(am__append_45) $(am__append_47) $(am__append_49) \ + $(am__append_51) $(am__append_53) $(am__append_54) \ + $(am__append_56) $(am__append_58) $(am__append_60) \ + $(am__append_62) $(am__append_64) $(am__append_66) \ + $(am__append_68) $(am__append_70) $(am__append_72) \ + $(am__append_73) $(am__append_74) $(am__append_76) \ + $(am__append_78) $(am__append_79) $(am__append_81) \ + $(am__append_83) $(am__append_85) $(am__append_87) \ + $(am__append_89) $(am__append_91) $(am__append_93) \ + $(am__append_95) $(am__append_97) $(am__append_99) \ + $(am__append_101) $(am__append_103) $(am__append_105) \ + $(am__append_107) $(am__append_109) $(am__append_111) \ + $(am__append_113) $(am__append_115) $(am__append_117) \ + $(am__append_119) $(am__append_121) $(am__append_123) \ + $(am__append_125) $(am__append_127) $(am__append_129) \ + $(am__append_131) $(am__append_133) $(am__append_135) \ + $(am__append_137) $(am__append_139) $(am__append_141) \ + $(am__append_143) $(am__append_145) $(am__append_147) am__libcharon_la_SOURCES_DIST = attributes/attributes.c \ attributes/attributes.h attributes/attribute_provider.h \ attributes/attribute_handler.h attributes/attribute_manager.c \ attributes/attribute_manager.h attributes/mem_pool.c \ attributes/mem_pool.h bus/bus.c bus/bus.h \ bus/listeners/listener.h bus/listeners/logger.h \ - bus/listeners/file_logger.c bus/listeners/file_logger.h \ - config/backend_manager.c config/backend_manager.h \ - config/backend.h config/child_cfg.c config/child_cfg.h \ - config/ike_cfg.c config/ike_cfg.h config/peer_cfg.c \ - config/peer_cfg.h config/proposal.c config/proposal.h \ - control/controller.c control/controller.h daemon.c daemon.h \ - encoding/generator.c encoding/generator.h encoding/message.c \ - encoding/message.h encoding/parser.c encoding/parser.h \ - encoding/payloads/auth_payload.c \ + bus/listeners/custom_logger.h bus/listeners/file_logger.c \ + bus/listeners/file_logger.h config/backend_manager.c \ + config/backend_manager.h config/backend.h config/child_cfg.c \ + config/child_cfg.h config/ike_cfg.c config/ike_cfg.h \ + config/peer_cfg.c config/peer_cfg.h config/proposal.c \ + config/proposal.h control/controller.c control/controller.h \ + daemon.c daemon.h encoding/generator.c encoding/generator.h \ + encoding/message.c encoding/message.h encoding/parser.c \ + encoding/parser.h encoding/payloads/auth_payload.c \ encoding/payloads/auth_payload.h \ encoding/payloads/cert_payload.c \ encoding/payloads/cert_payload.h \ @@ -407,7 +417,10 @@ am__libcharon_la_SOURCES_DIST = attributes/attributes.c \ encoding/payloads/hash_payload.c \ encoding/payloads/hash_payload.h \ encoding/payloads/fragment_payload.c \ - encoding/payloads/fragment_payload.h kernel/kernel_handler.c \ + encoding/payloads/fragment_payload.h kernel/kernel_interface.c \ + kernel/kernel_interface.h kernel/kernel_ipsec.c \ + kernel/kernel_ipsec.h kernel/kernel_net.c kernel/kernel_net.h \ + kernel/kernel_listener.h kernel/kernel_handler.c \ kernel/kernel_handler.h network/receiver.c network/receiver.h \ network/sender.c network/sender.h network/socket.c \ network/socket.h network/socket_manager.c \ @@ -420,6 +433,7 @@ am__libcharon_la_SOURCES_DIST = attributes/attributes.c \ processing/jobs/migrate_job.c processing/jobs/migrate_job.h \ processing/jobs/process_message_job.c \ processing/jobs/process_message_job.h \ + processing/jobs/redirect_job.c processing/jobs/redirect_job.h \ processing/jobs/rekey_child_sa_job.c \ processing/jobs/rekey_child_sa_job.h \ processing/jobs/rekey_ike_sa_job.c \ @@ -449,7 +463,8 @@ am__libcharon_la_SOURCES_DIST = attributes/attributes.c \ sa/ike_sa_manager.h sa/child_sa_manager.c \ sa/child_sa_manager.h sa/task_manager.h sa/task_manager.c \ sa/shunt_manager.c sa/shunt_manager.h sa/trap_manager.c \ - sa/trap_manager.h sa/task.c sa/task.h sa/ikev2/keymat_v2.c \ + sa/trap_manager.h sa/redirect_provider.h sa/redirect_manager.c \ + sa/redirect_manager.h sa/task.c sa/task.h sa/ikev2/keymat_v2.c \ sa/ikev2/keymat_v2.h sa/ikev2/task_manager_v2.c \ sa/ikev2/task_manager_v2.h \ sa/ikev2/authenticators/eap_authenticator.c \ @@ -474,9 +489,12 @@ am__libcharon_la_SOURCES_DIST = attributes/attributes.c \ sa/ikev2/tasks/ike_reauth.c sa/ikev2/tasks/ike_reauth.h \ sa/ikev2/tasks/ike_reauth_complete.c \ sa/ikev2/tasks/ike_reauth_complete.h \ + sa/ikev2/tasks/ike_redirect.c sa/ikev2/tasks/ike_redirect.h \ sa/ikev2/tasks/ike_auth_lifetime.c \ sa/ikev2/tasks/ike_auth_lifetime.h sa/ikev2/tasks/ike_vendor.c \ - sa/ikev2/tasks/ike_vendor.h sa/ikev1/keymat_v1.c \ + sa/ikev2/tasks/ike_vendor.h \ + sa/ikev2/tasks/ike_verify_peer_cert.c \ + sa/ikev2/tasks/ike_verify_peer_cert.h sa/ikev1/keymat_v1.c \ sa/ikev1/keymat_v1.h sa/ikev1/task_manager_v1.c \ sa/ikev1/task_manager_v1.h \ sa/ikev1/authenticators/psk_v1_authenticator.c \ @@ -535,8 +553,10 @@ am__dirstamp = $(am__leading_dot)dirstamp @USE_IKEV2_TRUE@ sa/ikev2/tasks/ike_rekey.lo \ @USE_IKEV2_TRUE@ sa/ikev2/tasks/ike_reauth.lo \ @USE_IKEV2_TRUE@ sa/ikev2/tasks/ike_reauth_complete.lo \ +@USE_IKEV2_TRUE@ sa/ikev2/tasks/ike_redirect.lo \ @USE_IKEV2_TRUE@ sa/ikev2/tasks/ike_auth_lifetime.lo \ -@USE_IKEV2_TRUE@ sa/ikev2/tasks/ike_vendor.lo +@USE_IKEV2_TRUE@ sa/ikev2/tasks/ike_vendor.lo \ +@USE_IKEV2_TRUE@ sa/ikev2/tasks/ike_verify_peer_cert.lo @USE_IKEV1_TRUE@am__objects_2 = sa/ikev1/keymat_v1.lo \ @USE_IKEV1_TRUE@ sa/ikev1/task_manager_v1.lo \ @USE_IKEV1_TRUE@ sa/ikev1/authenticators/psk_v1_authenticator.lo \ @@ -595,13 +615,16 @@ am_libcharon_la_OBJECTS = attributes/attributes.lo \ encoding/payloads/unknown_payload.lo \ encoding/payloads/vendor_id_payload.lo \ encoding/payloads/hash_payload.lo \ - encoding/payloads/fragment_payload.lo kernel/kernel_handler.lo \ + encoding/payloads/fragment_payload.lo \ + kernel/kernel_interface.lo kernel/kernel_ipsec.lo \ + kernel/kernel_net.lo kernel/kernel_handler.lo \ network/receiver.lo network/sender.lo network/socket.lo \ network/socket_manager.lo processing/jobs/acquire_job.lo \ processing/jobs/delete_child_sa_job.lo \ processing/jobs/delete_ike_sa_job.lo \ processing/jobs/migrate_job.lo \ processing/jobs/process_message_job.lo \ + processing/jobs/redirect_job.lo \ processing/jobs/rekey_child_sa_job.lo \ processing/jobs/rekey_ike_sa_job.lo \ processing/jobs/retransmit_job.lo \ @@ -616,8 +639,9 @@ am_libcharon_la_OBJECTS = attributes/attributes.lo \ sa/xauth/xauth_manager.lo sa/authenticator.lo sa/child_sa.lo \ sa/ike_sa.lo sa/ike_sa_id.lo sa/keymat.lo sa/ike_sa_manager.lo \ sa/child_sa_manager.lo sa/task_manager.lo sa/shunt_manager.lo \ - sa/trap_manager.lo sa/task.lo $(am__objects_1) \ - $(am__objects_2) $(am__objects_3) $(am__objects_4) + sa/trap_manager.lo sa/redirect_manager.lo sa/task.lo \ + $(am__objects_1) $(am__objects_2) $(am__objects_3) \ + $(am__objects_4) libcharon_la_OBJECTS = $(am_libcharon_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -712,12 +736,14 @@ DIST_SUBDIRS = . plugins/load_tester plugins/socket_default \ plugins/eap_tls plugins/eap_ttls plugins/eap_peap \ plugins/eap_tnc plugins/tnc_ifmap plugins/tnc_pdp \ plugins/medsrv plugins/medcli plugins/dhcp plugins/osx_attr \ - plugins/android_dns plugins/android_log plugins/maemo \ - plugins/ha plugins/kernel_libipsec plugins/kernel_wfp \ - plugins/kernel_iph plugins/whitelist plugins/lookip \ - plugins/error_notify plugins/certexpire plugins/systime_fix \ - plugins/led plugins/duplicheck plugins/coupling \ - plugins/radattr plugins/uci plugins/addrblock plugins/unity \ + plugins/p_cscf plugins/android_dns plugins/android_log \ + plugins/maemo plugins/ha plugins/kernel_pfkey \ + plugins/kernel_pfroute plugins/kernel_netlink \ + plugins/kernel_libipsec plugins/kernel_wfp plugins/kernel_iph \ + plugins/whitelist plugins/lookip plugins/error_notify \ + plugins/certexpire plugins/systime_fix plugins/led \ + plugins/duplicheck plugins/coupling plugins/radattr \ + plugins/uci plugins/addrblock plugins/unity \ plugins/xauth_generic plugins/xauth_eap plugins/xauth_pam \ plugins/xauth_noauth plugins/resolve plugins/attr \ plugins/attr_sql tests @@ -959,6 +985,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -978,15 +1006,15 @@ libcharon_la_SOURCES = attributes/attributes.c attributes/attributes.h \ attributes/attribute_manager.c attributes/attribute_manager.h \ attributes/mem_pool.c attributes/mem_pool.h bus/bus.c \ bus/bus.h bus/listeners/listener.h bus/listeners/logger.h \ - bus/listeners/file_logger.c bus/listeners/file_logger.h \ - config/backend_manager.c config/backend_manager.h \ - config/backend.h config/child_cfg.c config/child_cfg.h \ - config/ike_cfg.c config/ike_cfg.h config/peer_cfg.c \ - config/peer_cfg.h config/proposal.c config/proposal.h \ - control/controller.c control/controller.h daemon.c daemon.h \ - encoding/generator.c encoding/generator.h encoding/message.c \ - encoding/message.h encoding/parser.c encoding/parser.h \ - encoding/payloads/auth_payload.c \ + bus/listeners/custom_logger.h bus/listeners/file_logger.c \ + bus/listeners/file_logger.h config/backend_manager.c \ + config/backend_manager.h config/backend.h config/child_cfg.c \ + config/child_cfg.h config/ike_cfg.c config/ike_cfg.h \ + config/peer_cfg.c config/peer_cfg.h config/proposal.c \ + config/proposal.h control/controller.c control/controller.h \ + daemon.c daemon.h encoding/generator.c encoding/generator.h \ + encoding/message.c encoding/message.h encoding/parser.c \ + encoding/parser.h encoding/payloads/auth_payload.c \ encoding/payloads/auth_payload.h \ encoding/payloads/cert_payload.c \ encoding/payloads/cert_payload.h \ @@ -1028,7 +1056,10 @@ libcharon_la_SOURCES = attributes/attributes.c attributes/attributes.h \ encoding/payloads/hash_payload.c \ encoding/payloads/hash_payload.h \ encoding/payloads/fragment_payload.c \ - encoding/payloads/fragment_payload.h kernel/kernel_handler.c \ + encoding/payloads/fragment_payload.h kernel/kernel_interface.c \ + kernel/kernel_interface.h kernel/kernel_ipsec.c \ + kernel/kernel_ipsec.h kernel/kernel_net.c kernel/kernel_net.h \ + kernel/kernel_listener.h kernel/kernel_handler.c \ kernel/kernel_handler.h network/receiver.c network/receiver.h \ network/sender.c network/sender.h network/socket.c \ network/socket.h network/socket_manager.c \ @@ -1041,6 +1072,7 @@ libcharon_la_SOURCES = attributes/attributes.c attributes/attributes.h \ processing/jobs/migrate_job.c processing/jobs/migrate_job.h \ processing/jobs/process_message_job.c \ processing/jobs/process_message_job.h \ + processing/jobs/redirect_job.c processing/jobs/redirect_job.h \ processing/jobs/rekey_child_sa_job.c \ processing/jobs/rekey_child_sa_job.h \ processing/jobs/rekey_ike_sa_job.c \ @@ -1070,12 +1102,12 @@ libcharon_la_SOURCES = attributes/attributes.c attributes/attributes.h \ sa/ike_sa_manager.h sa/child_sa_manager.c \ sa/child_sa_manager.h sa/task_manager.h sa/task_manager.c \ sa/shunt_manager.c sa/shunt_manager.h sa/trap_manager.c \ - sa/trap_manager.h sa/task.c sa/task.h $(am__append_1) \ + sa/trap_manager.h sa/redirect_provider.h sa/redirect_manager.c \ + sa/redirect_manager.h sa/task.c sa/task.h $(am__append_1) \ $(am__append_2) $(am__append_3) $(am__append_5) AM_CPPFLAGS = \ -I${linux_headers} \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -DIPSEC_DIR=\"${ipsecdir}\" \ -DIPSEC_PIDDIR=\"${piddir}\" @@ -1084,32 +1116,33 @@ AM_LDFLAGS = \ -no-undefined libcharon_la_LIBADD = \ - $(top_builddir)/src/libstrongswan/libstrongswan.la \ - $(top_builddir)/src/libhydra/libhydra.la -lm $(PTHREADLIB) \ - $(DLLIB) $(SOCKLIB) $(am__append_4) $(am__append_7) \ - $(am__append_9) $(am__append_11) $(am__append_13) \ - $(am__append_15) $(am__append_17) $(am__append_19) \ - $(am__append_21) $(am__append_23) $(am__append_25) \ - $(am__append_27) $(am__append_29) $(am__append_31) \ - $(am__append_33) $(am__append_35) $(am__append_37) \ - $(am__append_39) $(am__append_41) $(am__append_43) \ - $(am__append_45) $(am__append_47) $(am__append_49) \ - $(am__append_51) $(am__append_53) $(am__append_54) \ - $(am__append_56) $(am__append_58) $(am__append_60) \ - $(am__append_62) $(am__append_64) $(am__append_66) \ - $(am__append_68) $(am__append_70) $(am__append_72) \ - $(am__append_73) $(am__append_74) $(am__append_76) \ - $(am__append_78) $(am__append_79) $(am__append_81) \ - $(am__append_83) $(am__append_85) $(am__append_87) \ - $(am__append_89) $(am__append_91) $(am__append_93) \ - $(am__append_95) $(am__append_97) $(am__append_99) \ - $(am__append_101) $(am__append_103) $(am__append_105) \ - $(am__append_107) $(am__append_109) $(am__append_111) \ - $(am__append_113) $(am__append_115) $(am__append_117) \ - $(am__append_119) $(am__append_121) $(am__append_123) \ - $(am__append_125) $(am__append_127) $(am__append_129) \ - $(am__append_131) $(am__append_133) $(am__append_135) \ - $(am__append_137) $(am__append_139) + $(top_builddir)/src/libstrongswan/libstrongswan.la -lm \ + $(PTHREADLIB) $(DLLIB) $(SOCKLIB) $(am__append_4) \ + $(am__append_7) $(am__append_9) $(am__append_11) \ + $(am__append_13) $(am__append_15) $(am__append_17) \ + $(am__append_19) $(am__append_21) $(am__append_23) \ + $(am__append_25) $(am__append_27) $(am__append_29) \ + $(am__append_31) $(am__append_33) $(am__append_35) \ + $(am__append_37) $(am__append_39) $(am__append_41) \ + $(am__append_43) $(am__append_45) $(am__append_47) \ + $(am__append_49) $(am__append_51) $(am__append_53) \ + $(am__append_54) $(am__append_56) $(am__append_58) \ + $(am__append_60) $(am__append_62) $(am__append_64) \ + $(am__append_66) $(am__append_68) $(am__append_70) \ + $(am__append_72) $(am__append_73) $(am__append_74) \ + $(am__append_76) $(am__append_78) $(am__append_79) \ + $(am__append_81) $(am__append_83) $(am__append_85) \ + $(am__append_87) $(am__append_89) $(am__append_91) \ + $(am__append_93) $(am__append_95) $(am__append_97) \ + $(am__append_99) $(am__append_101) $(am__append_103) \ + $(am__append_105) $(am__append_107) $(am__append_109) \ + $(am__append_111) $(am__append_113) $(am__append_115) \ + $(am__append_117) $(am__append_119) $(am__append_121) \ + $(am__append_123) $(am__append_125) $(am__append_127) \ + $(am__append_129) $(am__append_131) $(am__append_133) \ + $(am__append_135) $(am__append_137) $(am__append_139) \ + $(am__append_141) $(am__append_143) $(am__append_145) \ + $(am__append_147) EXTRA_DIST = Android.mk @MONOLITHIC_FALSE@SUBDIRS = . $(am__append_6) $(am__append_8) \ @MONOLITHIC_FALSE@ $(am__append_10) $(am__append_12) \ @@ -1143,7 +1176,9 @@ EXTRA_DIST = Android.mk @MONOLITHIC_FALSE@ $(am__append_126) $(am__append_128) \ @MONOLITHIC_FALSE@ $(am__append_130) $(am__append_132) \ @MONOLITHIC_FALSE@ $(am__append_134) $(am__append_136) \ -@MONOLITHIC_FALSE@ $(am__append_138) tests +@MONOLITHIC_FALSE@ $(am__append_138) $(am__append_140) \ +@MONOLITHIC_FALSE@ $(am__append_142) $(am__append_144) \ +@MONOLITHIC_FALSE@ $(am__append_146) tests # build optional plugins ######################## @@ -1179,7 +1214,9 @@ EXTRA_DIST = Android.mk @MONOLITHIC_TRUE@ $(am__append_126) $(am__append_128) \ @MONOLITHIC_TRUE@ $(am__append_130) $(am__append_132) \ @MONOLITHIC_TRUE@ $(am__append_134) $(am__append_136) \ -@MONOLITHIC_TRUE@ $(am__append_138) . tests +@MONOLITHIC_TRUE@ $(am__append_138) $(am__append_140) \ +@MONOLITHIC_TRUE@ $(am__append_142) $(am__append_144) \ +@MONOLITHIC_TRUE@ $(am__append_146) . tests all: all-recursive .SUFFIXES: @@ -1386,6 +1423,12 @@ kernel/$(am__dirstamp): kernel/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) kernel/$(DEPDIR) @: > kernel/$(DEPDIR)/$(am__dirstamp) +kernel/kernel_interface.lo: kernel/$(am__dirstamp) \ + kernel/$(DEPDIR)/$(am__dirstamp) +kernel/kernel_ipsec.lo: kernel/$(am__dirstamp) \ + kernel/$(DEPDIR)/$(am__dirstamp) +kernel/kernel_net.lo: kernel/$(am__dirstamp) \ + kernel/$(DEPDIR)/$(am__dirstamp) kernel/kernel_handler.lo: kernel/$(am__dirstamp) \ kernel/$(DEPDIR)/$(am__dirstamp) network/$(am__dirstamp): @@ -1420,6 +1463,8 @@ processing/jobs/migrate_job.lo: processing/jobs/$(am__dirstamp) \ processing/jobs/process_message_job.lo: \ processing/jobs/$(am__dirstamp) \ processing/jobs/$(DEPDIR)/$(am__dirstamp) +processing/jobs/redirect_job.lo: processing/jobs/$(am__dirstamp) \ + processing/jobs/$(DEPDIR)/$(am__dirstamp) processing/jobs/rekey_child_sa_job.lo: \ processing/jobs/$(am__dirstamp) \ processing/jobs/$(DEPDIR)/$(am__dirstamp) @@ -1483,6 +1528,8 @@ sa/child_sa_manager.lo: sa/$(am__dirstamp) \ sa/task_manager.lo: sa/$(am__dirstamp) sa/$(DEPDIR)/$(am__dirstamp) sa/shunt_manager.lo: sa/$(am__dirstamp) sa/$(DEPDIR)/$(am__dirstamp) sa/trap_manager.lo: sa/$(am__dirstamp) sa/$(DEPDIR)/$(am__dirstamp) +sa/redirect_manager.lo: sa/$(am__dirstamp) \ + sa/$(DEPDIR)/$(am__dirstamp) sa/task.lo: sa/$(am__dirstamp) sa/$(DEPDIR)/$(am__dirstamp) sa/ikev2/$(am__dirstamp): @$(MKDIR_P) sa/ikev2 @@ -1545,10 +1592,15 @@ sa/ikev2/tasks/ike_reauth.lo: sa/ikev2/tasks/$(am__dirstamp) \ sa/ikev2/tasks/$(DEPDIR)/$(am__dirstamp) sa/ikev2/tasks/ike_reauth_complete.lo: sa/ikev2/tasks/$(am__dirstamp) \ sa/ikev2/tasks/$(DEPDIR)/$(am__dirstamp) +sa/ikev2/tasks/ike_redirect.lo: sa/ikev2/tasks/$(am__dirstamp) \ + sa/ikev2/tasks/$(DEPDIR)/$(am__dirstamp) sa/ikev2/tasks/ike_auth_lifetime.lo: sa/ikev2/tasks/$(am__dirstamp) \ sa/ikev2/tasks/$(DEPDIR)/$(am__dirstamp) sa/ikev2/tasks/ike_vendor.lo: sa/ikev2/tasks/$(am__dirstamp) \ sa/ikev2/tasks/$(DEPDIR)/$(am__dirstamp) +sa/ikev2/tasks/ike_verify_peer_cert.lo: \ + sa/ikev2/tasks/$(am__dirstamp) \ + sa/ikev2/tasks/$(DEPDIR)/$(am__dirstamp) sa/ikev1/$(am__dirstamp): @$(MKDIR_P) sa/ikev1 @: > sa/ikev1/$(am__dirstamp) @@ -1720,6 +1772,9 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@encoding/payloads/$(DEPDIR)/unknown_payload.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@encoding/payloads/$(DEPDIR)/vendor_id_payload.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@kernel/$(DEPDIR)/kernel_handler.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kernel/$(DEPDIR)/kernel_interface.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kernel/$(DEPDIR)/kernel_ipsec.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kernel/$(DEPDIR)/kernel_net.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@network/$(DEPDIR)/receiver.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@network/$(DEPDIR)/sender.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@network/$(DEPDIR)/socket.Plo@am__quote@ @@ -1735,6 +1790,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@processing/jobs/$(DEPDIR)/mediation_job.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@processing/jobs/$(DEPDIR)/migrate_job.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@processing/jobs/$(DEPDIR)/process_message_job.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@processing/jobs/$(DEPDIR)/redirect_job.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@processing/jobs/$(DEPDIR)/rekey_child_sa_job.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@processing/jobs/$(DEPDIR)/rekey_ike_sa_job.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@processing/jobs/$(DEPDIR)/retransmit_job.Plo@am__quote@ @@ -1751,6 +1807,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@sa/$(DEPDIR)/ike_sa_id.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@sa/$(DEPDIR)/ike_sa_manager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@sa/$(DEPDIR)/keymat.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@sa/$(DEPDIR)/redirect_manager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@sa/$(DEPDIR)/shunt_manager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@sa/$(DEPDIR)/task.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@sa/$(DEPDIR)/task_manager.Plo@am__quote@ @@ -1799,8 +1856,10 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@sa/ikev2/tasks/$(DEPDIR)/ike_natd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@sa/ikev2/tasks/$(DEPDIR)/ike_reauth.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@sa/ikev2/tasks/$(DEPDIR)/ike_reauth_complete.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@sa/ikev2/tasks/$(DEPDIR)/ike_redirect.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@sa/ikev2/tasks/$(DEPDIR)/ike_rekey.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@sa/ikev2/tasks/$(DEPDIR)/ike_vendor.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@sa/ikev2/tasks/$(DEPDIR)/ike_verify_peer_cert.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@sa/xauth/$(DEPDIR)/xauth_manager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@sa/xauth/$(DEPDIR)/xauth_method.Plo@am__quote@ diff --git a/src/libcharon/attributes/attributes.c b/src/libcharon/attributes/attributes.c index 9fabcf4e4..0f28d55fa 100644 --- a/src/libcharon/attributes/attributes.c +++ b/src/libcharon/attributes/attributes.c @@ -17,7 +17,7 @@ #include "attributes.h" -ENUM_BEGIN(configuration_attribute_type_names, INTERNAL_IP4_ADDRESS, HOME_AGENT_ADDRESS, +ENUM_BEGIN(configuration_attribute_type_names, INTERNAL_IP4_ADDRESS, P_CSCF_IP6_ADDRESS, "INTERNAL_IP4_ADDRESS", "INTERNAL_IP4_NETMASK", "INTERNAL_IP4_DNS", @@ -36,8 +36,10 @@ ENUM_BEGIN(configuration_attribute_type_names, INTERNAL_IP4_ADDRESS, HOME_AGENT_ "MIP6_HOME_PREFIX", "INTERNAL_IP6_LINK", "INTERNAL_IP6_PREFIX", - "HOME_AGENT_ADDRESS"); -ENUM_NEXT(configuration_attribute_type_names, XAUTH_TYPE, XAUTH_ANSWER, HOME_AGENT_ADDRESS, + "HOME_AGENT_ADDRESS", + "P_CSCF_IP4_ADDRESS", + "P_CSCF_IP6_ADDRESS"); +ENUM_NEXT(configuration_attribute_type_names, XAUTH_TYPE, XAUTH_ANSWER, P_CSCF_IP6_ADDRESS, "XAUTH_TYPE", "XAUTH_USER_NAME", "XAUTH_USER_PASSWORD", @@ -65,7 +67,7 @@ ENUM_NEXT(configuration_attribute_type_names, UNITY_BANNER, UNITY_DDNS_HOSTNAME, "UNITY_DDNS_HOSTNAME"); ENUM_END(configuration_attribute_type_names, UNITY_DDNS_HOSTNAME); -ENUM_BEGIN(configuration_attribute_type_short_names, INTERNAL_IP4_ADDRESS, HOME_AGENT_ADDRESS, +ENUM_BEGIN(configuration_attribute_type_short_names, INTERNAL_IP4_ADDRESS, P_CSCF_IP6_ADDRESS, "ADDR", "MASK", "DNS", @@ -84,8 +86,10 @@ ENUM_BEGIN(configuration_attribute_type_short_names, INTERNAL_IP4_ADDRESS, HOME_ "MIP6HPFX", "LINK6", "PFX6", - "HOA"); -ENUM_NEXT(configuration_attribute_type_short_names, XAUTH_TYPE, XAUTH_ANSWER, HOME_AGENT_ADDRESS, + "HOA", + "PCSCF4", + "PCSCF6"); +ENUM_NEXT(configuration_attribute_type_short_names, XAUTH_TYPE, XAUTH_ANSWER, P_CSCF_IP6_ADDRESS, "X_TYPE", "X_USER", "X_PWD", diff --git a/src/libcharon/attributes/attributes.h b/src/libcharon/attributes/attributes.h index 5d1e9f9ba..dd1db4fc3 100644 --- a/src/libcharon/attributes/attributes.h +++ b/src/libcharon/attributes/attributes.h @@ -49,6 +49,9 @@ enum configuration_attribute_type_t { INTERNAL_IP6_LINK = 17, INTERNAL_IP6_PREFIX = 18, HOME_AGENT_ADDRESS = 19, + /* RFC 7651 */ + P_CSCF_IP4_ADDRESS = 20, + P_CSCF_IP6_ADDRESS = 21, /* XAUTH attributes */ XAUTH_TYPE = 16520, XAUTH_USER_NAME = 16521, diff --git a/src/libcharon/attributes/mem_pool.c b/src/libcharon/attributes/mem_pool.c index 279668249..833c3e950 100644 --- a/src/libcharon/attributes/mem_pool.c +++ b/src/libcharon/attributes/mem_pool.c @@ -17,7 +17,6 @@ #include "mem_pool.h" #include <library.h> -#include <hydra.h> #include <utils/debug.h> #include <collections/hashtable.h> #include <collections/array.h> diff --git a/src/libcharon/bus/listeners/custom_logger.h b/src/libcharon/bus/listeners/custom_logger.h new file mode 100644 index 000000000..a256ad1ec --- /dev/null +++ b/src/libcharon/bus/listeners/custom_logger.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2016 secunet Security Networks AG + * Copyright (C) 2016 Thomas Egerer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @defgroup custom_logger custom_logger + * @{ @ingroup listeners + */ + +#ifndef CUSTOM_LOGGER_H_ +#define CUSTOM_LOGGER_H_ + +#include <bus/listeners/logger.h> + +typedef struct custom_logger_t custom_logger_t; + +/** + * Custom logger which implements listener_t. + */ +struct custom_logger_t { + + /** + * Implements the logger_t interface. + */ + logger_t logger; + + /** + * Set the loglevel for a debug group. + * + * @param group debug group to set + * @param level max level to log (0..4) + */ + void (*set_level) (custom_logger_t *this, debug_t group, level_t level); + + /** + * Destroy the custom_logger_t object. + */ + void (*destroy) (custom_logger_t *this); +}; + +/** + * Prototype for custom logger construction function pointer. + */ +typedef custom_logger_t *(*custom_logger_constructor_t)(const char *name); + +#endif /** CUSTOM_LOGGER_H_ @}*/ diff --git a/src/libcharon/config/child_cfg.c b/src/libcharon/config/child_cfg.c index ed7c0d406..3d3c7419b 100644 --- a/src/libcharon/config/child_cfg.c +++ b/src/libcharon/config/child_cfg.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2009 Tobias Brunner + * Copyright (C) 2008-2015 Tobias Brunner * Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -531,6 +531,57 @@ METHOD(child_cfg_t, install_policy, bool, return this->install_policy; } +#define LT_PART_EQUALS(a, b) ({ a.life == b.life && a.rekey == b.rekey && a.jitter == b.jitter; }) +#define LIFETIME_EQUALS(a, b) ({ LT_PART_EQUALS(a.time, b.time) && LT_PART_EQUALS(a.bytes, b.bytes) && LT_PART_EQUALS(a.packets, b.packets); }) + +METHOD(child_cfg_t, equals, bool, + private_child_cfg_t *this, child_cfg_t *other_pub) +{ + private_child_cfg_t *other = (private_child_cfg_t*)other_pub; + + if (this == other) + { + return TRUE; + } + if (this->public.equals != other->public.equals) + { + return FALSE; + } + if (!this->proposals->equals_offset(this->proposals, other->proposals, + offsetof(proposal_t, equals))) + { + return FALSE; + } + if (!this->my_ts->equals_offset(this->my_ts, other->my_ts, + offsetof(traffic_selector_t, equals))) + { + return FALSE; + } + if (!this->other_ts->equals_offset(this->other_ts, other->other_ts, + offsetof(traffic_selector_t, equals))) + { + return FALSE; + } + return this->hostaccess == other->hostaccess && + this->mode == other->mode && + this->start_action == other->start_action && + this->dpd_action == other->dpd_action && + this->close_action == other->close_action && + LIFETIME_EQUALS(this->lifetime, other->lifetime) && + this->use_ipcomp == other->use_ipcomp && + this->inactivity == other->inactivity && + this->reqid == other->reqid && + this->mark_in.value == other->mark_in.value && + this->mark_in.mask == other->mark_in.mask && + this->mark_out.value == other->mark_out.value && + this->mark_out.mask == other->mark_out.mask && + this->tfc == other->tfc && + this->replay_window == other->replay_window && + this->proxy_mode == other->proxy_mode && + this->install_policy == other->install_policy && + streq(this->updown, other->updown); +} + METHOD(child_cfg_t, get_ref, child_cfg_t*, private_child_cfg_t *this) { @@ -593,6 +644,7 @@ child_cfg_t *child_cfg_create(char *name, lifetime_cfg_t *lifetime, .set_replay_window = _set_replay_window, .use_proxy_mode = _use_proxy_mode, .install_policy = _install_policy, + .equals = _equals, .get_ref = _get_ref, .destroy = _destroy, }, diff --git a/src/libcharon/config/child_cfg.h b/src/libcharon/config/child_cfg.h index 9f7a92b70..22641f77e 100644 --- a/src/libcharon/config/child_cfg.h +++ b/src/libcharon/config/child_cfg.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2009 Tobias Brunner + * Copyright (C) 2008-2015 Tobias Brunner * Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -274,6 +274,14 @@ struct child_cfg_t { bool (*install_policy)(child_cfg_t *this); /** + * Check if two child_cfg objects are equal. + * + * @param other candidate to check for equality against this + * @return TRUE if equal + */ + bool (*equals)(child_cfg_t *this, child_cfg_t *other); + + /** * Increase the reference count. * * @return reference to this diff --git a/src/libcharon/config/ike_cfg.c b/src/libcharon/config/ike_cfg.c index dee9e4c29..a720e1493 100644 --- a/src/libcharon/config/ike_cfg.c +++ b/src/libcharon/config/ike_cfg.c @@ -371,9 +371,6 @@ METHOD(ike_cfg_t, equals, bool, private_ike_cfg_t *this, ike_cfg_t *other_public) { private_ike_cfg_t *other = (private_ike_cfg_t*)other_public; - enumerator_t *e1, *e2; - proposal_t *p1, *p2; - bool eq = TRUE; if (this == other) { @@ -383,25 +380,12 @@ METHOD(ike_cfg_t, equals, bool, { return FALSE; } - if (this->proposals->get_count(this->proposals) != - other->proposals->get_count(other->proposals)) + if (!this->proposals->equals_offset(this->proposals, other->proposals, + offsetof(proposal_t, equals))) { return FALSE; } - e1 = this->proposals->create_enumerator(this->proposals); - e2 = other->proposals->create_enumerator(other->proposals); - while (e1->enumerate(e1, &p1) && e2->enumerate(e2, &p2)) - { - if (!p1->equals(p1, p2)) - { - eq = FALSE; - break; - } - } - e1->destroy(e1); - e2->destroy(e2); - - return (eq && + return this->version == other->version && this->certreq == other->certreq && this->force_encap == other->force_encap && @@ -409,7 +393,7 @@ METHOD(ike_cfg_t, equals, bool, streq(this->me, other->me) && streq(this->other, other->other) && this->my_port == other->my_port && - this->other_port == other->other_port); + this->other_port == other->other_port; } METHOD(ike_cfg_t, get_ref, ike_cfg_t*, diff --git a/src/libcharon/config/peer_cfg.c b/src/libcharon/config/peer_cfg.c index aa2a39ce5..d28a79507 100644 --- a/src/libcharon/config/peer_cfg.c +++ b/src/libcharon/config/peer_cfg.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2008 Tobias Brunner + * Copyright (C) 2007-2015 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -200,6 +200,117 @@ METHOD(peer_cfg_t, add_child_cfg, void, this->mutex->unlock(this->mutex); } +typedef struct { + enumerator_t public; + linked_list_t *removed; + linked_list_t *added; + enumerator_t *wrapped; + bool add; +} child_cfgs_replace_enumerator_t; + +METHOD(enumerator_t, child_cfgs_replace_enumerate, bool, + child_cfgs_replace_enumerator_t *this, child_cfg_t **chd, bool *added) +{ + child_cfg_t *child_cfg; + + if (!this->wrapped) + { + this->wrapped = this->removed->create_enumerator(this->removed); + } + while (TRUE) + { + if (this->wrapped->enumerate(this->wrapped, &child_cfg)) + { + if (chd) + { + *chd = child_cfg; + } + if (added) + { + *added = this->add; + } + return TRUE; + } + if (this->add) + { + break; + } + this->wrapped = this->added->create_enumerator(this->added); + this->add = TRUE; + } + return FALSE; +} + +METHOD(enumerator_t, child_cfgs_replace_enumerator_destroy, void, + child_cfgs_replace_enumerator_t *this) +{ + DESTROY_IF(this->wrapped); + this->removed->destroy_offset(this->removed, offsetof(child_cfg_t, destroy)); + this->added->destroy_offset(this->added, offsetof(child_cfg_t, destroy)); + free(this); +} + +METHOD(peer_cfg_t, replace_child_cfgs, enumerator_t*, + private_peer_cfg_t *this, peer_cfg_t *other_pub) +{ + private_peer_cfg_t *other = (private_peer_cfg_t*)other_pub; + linked_list_t *removed, *added; + enumerator_t *mine, *others; + child_cfg_t *my_cfg, *other_cfg; + child_cfgs_replace_enumerator_t *enumerator; + bool found; + + removed = linked_list_create(); + + other->mutex->lock(other->mutex); + added = linked_list_create_from_enumerator( + other->child_cfgs->create_enumerator(other->child_cfgs)); + added->invoke_offset(added, offsetof(child_cfg_t, get_ref)); + other->mutex->unlock(other->mutex); + + this->mutex->lock(this->mutex); + others = added->create_enumerator(added); + mine = this->child_cfgs->create_enumerator(this->child_cfgs); + while (mine->enumerate(mine, &my_cfg)) + { + found = FALSE; + while (others->enumerate(others, &other_cfg)) + { + if (my_cfg->equals(my_cfg, other_cfg)) + { + added->remove_at(added, others); + other_cfg->destroy(other_cfg); + found = TRUE; + break; + } + } + added->reset_enumerator(added, others); + if (!found) + { + this->child_cfgs->remove_at(this->child_cfgs, mine); + removed->insert_last(removed, my_cfg); + } + } + while (others->enumerate(others, &other_cfg)) + { + this->child_cfgs->insert_last(this->child_cfgs, + other_cfg->get_ref(other_cfg)); + } + others->destroy(others); + mine->destroy(mine); + this->mutex->unlock(this->mutex); + + INIT(enumerator, + .public = { + .enumerate = (void*)_child_cfgs_replace_enumerate, + .destroy = (void*)_child_cfgs_replace_enumerator_destroy, + }, + .removed = removed, + .added = added, + ); + return &enumerator->public; +} + /** * child_cfg enumerator */ @@ -538,10 +649,6 @@ static bool auth_cfg_equal(private_peer_cfg_t *this, private_peer_cfg_t *other) METHOD(peer_cfg_t, equals, bool, private_peer_cfg_t *this, private_peer_cfg_t *other) { - enumerator_t *e1, *e2; - host_t *vip1, *vip2; - char *pool1, *pool2; - if (this == other) { return TRUE; @@ -550,44 +657,15 @@ METHOD(peer_cfg_t, equals, bool, { return FALSE; } - - if (this->vips->get_count(this->vips) != other->vips->get_count(other->vips)) + if (!this->vips->equals_offset(this->vips, other->vips, + offsetof(host_t, ip_equals))) { return FALSE; } - e1 = create_virtual_ip_enumerator(this); - e2 = create_virtual_ip_enumerator(other); - if (e1->enumerate(e1, &vip1) && e2->enumerate(e2, &vip2)) - { - if (!vip1->ip_equals(vip1, vip2)) - { - e1->destroy(e1); - e2->destroy(e2); - return FALSE; - } - } - e1->destroy(e1); - e2->destroy(e2); - - if (this->pools->get_count(this->pools) != - other->pools->get_count(other->pools)) + if (!this->pools->equals_function(this->pools, other->pools, (void*)streq)) { return FALSE; } - e1 = create_pool_enumerator(this); - e2 = create_pool_enumerator(other); - if (e1->enumerate(e1, &pool1) && e2->enumerate(e2, &pool2)) - { - if (!streq(pool1, pool2)) - { - e1->destroy(e1); - e2->destroy(e2); - return FALSE; - } - } - e1->destroy(e1); - e2->destroy(e2); - return ( get_ike_version(this) == get_ike_version(other) && this->cert_policy == other->cert_policy && @@ -666,6 +744,10 @@ peer_cfg_t *peer_cfg_create(char *name, { jitter_time = reauth_time; } + if (dpd && dpd_timeout && dpd > dpd_timeout) + { + dpd_timeout = dpd; + } INIT(this, .public = { @@ -674,6 +756,7 @@ peer_cfg_t *peer_cfg_create(char *name, .get_ike_cfg = _get_ike_cfg, .add_child_cfg = _add_child_cfg, .remove_child_cfg = (void*)_remove_child_cfg, + .replace_child_cfgs = _replace_child_cfgs, .create_child_cfg_enumerator = _create_child_cfg_enumerator, .select_child_cfg = _select_child_cfg, .get_cert_policy = _get_cert_policy, diff --git a/src/libcharon/config/peer_cfg.h b/src/libcharon/config/peer_cfg.h index 3e780394a..b612a2ef1 100644 --- a/src/libcharon/config/peer_cfg.h +++ b/src/libcharon/config/peer_cfg.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2008 Tobias Brunner + * Copyright (C) 2007-2015 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -154,6 +154,20 @@ struct peer_cfg_t { void (*remove_child_cfg)(peer_cfg_t *this, enumerator_t *enumerator); /** + * Replace the CHILD configs with those in the given PEER config. + * + * Configs that are equal are not replaced. + * + * The enumerator enumerates the removed and added CHILD configs + * (child_cfg_t*, bool), where the flag is FALSE for removed configs and + * TRUE for added configs. + * + * @param other other config to get CHILD configs from + * @return an enumerator over removed/added CHILD configs + */ + enumerator_t* (*replace_child_cfgs)(peer_cfg_t *this, peer_cfg_t *other); + + /** * Create an enumerator for all attached CHILD configs. * * @return an enumerator over all CHILD configs. diff --git a/src/libcharon/config/proposal.c b/src/libcharon/config/proposal.c index e59dcd9ec..95b6a00ea 100644 --- a/src/libcharon/config/proposal.c +++ b/src/libcharon/config/proposal.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2008-2014 Tobias Brunner * Copyright (C) 2006-2010 Martin Willi + * Copyright (C) 2013-2015 Andreas Steffen * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -640,20 +641,41 @@ static bool proposal_add_supported_ike(private_proposal_t *this, bool aead) if (aead) { + /* Round 1 adds algorithms with at least 128 bit security strength */ enumerator = lib->crypto->create_aead_enumerator(lib->crypto); while (enumerator->enumerate(enumerator, &encryption, &plugin_name)) { switch (encryption) { - case ENCR_AES_CCM_ICV8: - case ENCR_AES_CCM_ICV12: + case ENCR_AES_GCM_ICV16: case ENCR_AES_CCM_ICV16: - case ENCR_AES_GCM_ICV8: + case ENCR_CAMELLIA_CCM_ICV16: + /* we assume that we support all AES/Camellia sizes */ + add_algorithm(this, ENCRYPTION_ALGORITHM, encryption, 128); + add_algorithm(this, ENCRYPTION_ALGORITHM, encryption, 192); + add_algorithm(this, ENCRYPTION_ALGORITHM, encryption, 256); + break; + case ENCR_CHACHA20_POLY1305: + add_algorithm(this, ENCRYPTION_ALGORITHM, encryption, 256); + break; + default: + break; + } + } + enumerator->destroy(enumerator); + + /* Round 2 adds algorithms with less than 128 bit security strength */ + enumerator = lib->crypto->create_aead_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &encryption, &plugin_name)) + { + switch (encryption) + { case ENCR_AES_GCM_ICV12: - case ENCR_AES_GCM_ICV16: - case ENCR_CAMELLIA_CCM_ICV8: + case ENCR_AES_GCM_ICV8: + case ENCR_AES_CCM_ICV12: + case ENCR_AES_CCM_ICV8: case ENCR_CAMELLIA_CCM_ICV12: - case ENCR_CAMELLIA_CCM_ICV16: + case ENCR_CAMELLIA_CCM_ICV8: /* we assume that we support all AES/Camellia sizes */ add_algorithm(this, ENCRYPTION_ALGORITHM, encryption, 128); add_algorithm(this, ENCRYPTION_ALGORITHM, encryption, 192); @@ -672,6 +694,7 @@ static bool proposal_add_supported_ike(private_proposal_t *this, bool aead) } else { + /* Round 1 adds algorithms with at least 128 bit security strength */ enumerator = lib->crypto->create_crypter_enumerator(lib->crypto); while (enumerator->enumerate(enumerator, &encryption, &plugin_name)) { @@ -686,6 +709,18 @@ static bool proposal_add_supported_ike(private_proposal_t *this, bool aead) add_algorithm(this, ENCRYPTION_ALGORITHM, encryption, 192); add_algorithm(this, ENCRYPTION_ALGORITHM, encryption, 256); break; + default: + break; + } + } + enumerator->destroy(enumerator); + + /* Round 2 adds algorithms with less than 128 bit security strength */ + enumerator = lib->crypto->create_crypter_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &encryption, &plugin_name)) + { + switch (encryption) + { case ENCR_3DES: add_algorithm(this, ENCRYPTION_ALGORITHM, encryption, 0); break; @@ -703,18 +738,33 @@ static bool proposal_add_supported_ike(private_proposal_t *this, bool aead) return FALSE; } + /* Round 1 adds algorithms with at least 128 bit security strength */ enumerator = lib->crypto->create_signer_enumerator(lib->crypto); while (enumerator->enumerate(enumerator, &integrity, &plugin_name)) { switch (integrity) { - case AUTH_HMAC_SHA1_96: case AUTH_HMAC_SHA2_256_128: case AUTH_HMAC_SHA2_384_192: case AUTH_HMAC_SHA2_512_256: - case AUTH_HMAC_MD5_96: + add_algorithm(this, INTEGRITY_ALGORITHM, integrity, 0); + break; + default: + break; + } + } + enumerator->destroy(enumerator); + + /* Round 2 adds algorithms with less than 128 bit security strength */ + enumerator = lib->crypto->create_signer_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &integrity, &plugin_name)) + { + switch (integrity) + { case AUTH_AES_XCBC_96: case AUTH_AES_CMAC_96: + case AUTH_HMAC_SHA1_96: + case AUTH_HMAC_MD5_96: add_algorithm(this, INTEGRITY_ALGORITHM, integrity, 0); break; default: @@ -724,16 +774,15 @@ static bool proposal_add_supported_ike(private_proposal_t *this, bool aead) enumerator->destroy(enumerator); } + /* Round 1 adds algorithms with at least 128 bit security strength */ enumerator = lib->crypto->create_prf_enumerator(lib->crypto); while (enumerator->enumerate(enumerator, &prf, &plugin_name)) { switch (prf) { - case PRF_HMAC_SHA1: case PRF_HMAC_SHA2_256: case PRF_HMAC_SHA2_384: case PRF_HMAC_SHA2_512: - case PRF_HMAC_MD5: case PRF_AES128_XCBC: case PRF_AES128_CMAC: add_algorithm(this, PSEUDO_RANDOM_FUNCTION, prf, 0); @@ -744,6 +793,63 @@ static bool proposal_add_supported_ike(private_proposal_t *this, bool aead) } enumerator->destroy(enumerator); + /* Round 2 adds algorithms with less than 128 bit security strength */ + enumerator = lib->crypto->create_prf_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &prf, &plugin_name)) + { + switch (prf) + { + case PRF_HMAC_SHA1: + case PRF_HMAC_MD5: + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, prf, 0); + break; + default: + break; + } + } + enumerator->destroy(enumerator); + + /* Round 1 adds ECC and NTRU algorithms with at least 128 bit security strength */ + enumerator = lib->crypto->create_dh_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &group, &plugin_name)) + { + switch (group) + { + case ECP_256_BIT: + case ECP_384_BIT: + case ECP_521_BIT: + case ECP_256_BP: + case ECP_384_BP: + case ECP_512_BP: + case NTRU_128_BIT: + case NTRU_192_BIT: + case NTRU_256_BIT: + add_algorithm(this, DIFFIE_HELLMAN_GROUP, group, 0); + break; + default: + break; + } + } + enumerator->destroy(enumerator); + + /* Round 2 adds other algorithms with at least 128 bit security strength */ + enumerator = lib->crypto->create_dh_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &group, &plugin_name)) + { + switch (group) + { + case MODP_3072_BIT: + case MODP_4096_BIT: + case MODP_8192_BIT: + add_algorithm(this, DIFFIE_HELLMAN_GROUP, group, 0); + break; + default: + break; + } + } + enumerator->destroy(enumerator); + + /* Round 3 adds algorithms with less than 128 bit security strength */ enumerator = lib->crypto->create_dh_enumerator(lib->crypto); while (enumerator->enumerate(enumerator, &group, &plugin_name)) { @@ -755,28 +861,16 @@ static bool proposal_add_supported_ike(private_proposal_t *this, bool aead) case MODP_768_BIT: /* weak */ break; - case MODP_1024_BIT: - case MODP_1536_BIT: case MODP_2048_BIT: - case MODP_3072_BIT: - case MODP_4096_BIT: - case MODP_8192_BIT: - case ECP_256_BIT: - case ECP_384_BIT: - case ECP_521_BIT: - case MODP_1024_160: - case MODP_2048_224: case MODP_2048_256: - case ECP_192_BIT: + case MODP_2048_224: + case MODP_1536_BIT: + case MODP_1024_BIT: + case MODP_1024_160: case ECP_224_BIT: case ECP_224_BP: - case ECP_256_BP: - case ECP_384_BP: - case ECP_512_BP: + case ECP_192_BIT: case NTRU_112_BIT: - case NTRU_128_BIT: - case NTRU_192_BIT: - case NTRU_256_BIT: add_algorithm(this, DIFFIE_HELLMAN_GROUP, group, 0); break; default: @@ -805,21 +899,27 @@ proposal_t *proposal_create_default(protocol_id_t protocol) } break; case PROTO_ESP: - add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 128); - add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 192); - add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 256); - add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_3DES, 0); - add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_BLOWFISH, 256); - add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0); - add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_AES_XCBC_96, 0); - add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 0); - add_algorithm(this, EXTENDED_SEQUENCE_NUMBERS, NO_EXT_SEQ_NUMBERS, 0); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 128); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 192); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 256); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_3DES, 0); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_BLOWFISH, 256); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_256_128, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_384_192, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_512_256, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_AES_XCBC_96, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 0); + add_algorithm(this, EXTENDED_SEQUENCE_NUMBERS, NO_EXT_SEQ_NUMBERS, 0); break; case PROTO_AH: - add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0); - add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_AES_XCBC_96, 0); - add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 0); - add_algorithm(this, EXTENDED_SEQUENCE_NUMBERS, NO_EXT_SEQ_NUMBERS, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_256_128, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_384_192, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_512_256, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_AES_XCBC_96, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 0); + add_algorithm(this, EXTENDED_SEQUENCE_NUMBERS, NO_EXT_SEQ_NUMBERS, 0); break; default: break; diff --git a/src/libcharon/daemon.c b/src/libcharon/daemon.c index dce2a7144..cef8b8992 100644 --- a/src/libcharon/daemon.c +++ b/src/libcharon/daemon.c @@ -16,6 +16,29 @@ * for more details. */ +/* + * Copyright (C) 2016 secunet Security Networks AG + * Copyright (C) 2016 Thomas Egerer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + #include <stdio.h> #include <sys/types.h> #include <unistd.h> @@ -111,6 +134,70 @@ static void dbg_bus(debug_t group, level_t level, char *fmt, ...) } /** + * Data for registered custom loggers + */ +typedef struct { + /** + * Name of the custom logger (also used for loglevel configuration) + */ + char *name; + + /** + * Constructor to be called for custom logger creation + */ + custom_logger_constructor_t constructor; + +} custom_logger_entry_t; + +#define MAX_CUSTOM_LOGGERS 10 + +/** + * Static array for logger registration using __attribute__((constructor)) + */ +static custom_logger_entry_t custom_loggers[MAX_CUSTOM_LOGGERS]; +static int custom_logger_count; + +/** + * Described in header + */ +void register_custom_logger(char *name, + custom_logger_constructor_t constructor) +{ + if (custom_logger_count < MAX_CUSTOM_LOGGERS - 1) + { + custom_loggers[custom_logger_count].name = name; + custom_loggers[custom_logger_count].constructor = constructor; + custom_logger_count++; + } + else + { + fprintf(stderr, "failed to register custom logger, please increase " + "MAX_CUSTOM_LOGGERS"); + } +} + +/** + * Types of supported loggers + */ +typedef enum { + /** + * Syslog logger instance + */ + SYS_LOGGER, + + /** + * File logger instance + */ + FILE_LOGGER, + + /** + * Custom logger instance + */ + CUSTOM_LOGGER, + +} logger_type_t; + +/** * Some metadata about configured loggers */ typedef struct { @@ -120,9 +207,9 @@ typedef struct { char *target; /** - * TRUE if this is a file logger + * Type of logger */ - bool file; + logger_type_t type; /** * The actual logger @@ -130,6 +217,7 @@ typedef struct { union { sys_logger_t *sys; file_logger_t *file; + custom_logger_t *custom; } logger; } logger_entry_t; @@ -139,13 +227,17 @@ typedef struct { */ static void logger_entry_destroy(logger_entry_t *this) { - if (this->file) - { - DESTROY_IF(this->logger.file); - } - else + switch (this->type) { - DESTROY_IF(this->logger.sys); + case FILE_LOGGER: + DESTROY_IF(this->logger.file); + break; + case SYS_LOGGER: + DESTROY_IF(this->logger.sys); + break; + case CUSTOM_LOGGER: + DESTROY_IF(this->logger.custom); + break; } free(this->target); free(this); @@ -156,13 +248,18 @@ static void logger_entry_destroy(logger_entry_t *this) */ static void logger_entry_unregister_destroy(logger_entry_t *this) { - if (this->file) + switch (this->type) { - charon->bus->remove_logger(charon->bus, &this->logger.file->logger); - } - else - { - charon->bus->remove_logger(charon->bus, &this->logger.sys->logger); + case FILE_LOGGER: + charon->bus->remove_logger(charon->bus, &this->logger.file->logger); + break; + case SYS_LOGGER: + charon->bus->remove_logger(charon->bus, &this->logger.sys->logger); + break; + case CUSTOM_LOGGER: + charon->bus->remove_logger(charon->bus, + &this->logger.custom->logger); + break; } logger_entry_destroy(this); } @@ -170,9 +267,10 @@ static void logger_entry_unregister_destroy(logger_entry_t *this) /** * Match a logger entry by target and whether it is a file or syslog logger */ -static bool logger_entry_match(logger_entry_t *this, char *target, bool *file) +static bool logger_entry_match(logger_entry_t *this, char *target, + logger_type_t *type) { - return this->file == *file && streq(this->target, target); + return this->type == *type && streq(this->target, target); } /** @@ -228,28 +326,45 @@ static int get_syslog_facility(char *facility) * Returns an existing or newly created logger entry (if found, it is removed * from the given linked list of existing loggers) */ -static logger_entry_t *get_logger_entry(char *target, bool is_file_logger, - linked_list_t *existing) +static logger_entry_t *get_logger_entry(char *target, logger_type_t type, + linked_list_t *existing, + custom_logger_constructor_t constructor) { logger_entry_t *entry; if (existing->find_first(existing, (void*)logger_entry_match, - (void**)&entry, target, &is_file_logger) != SUCCESS) + (void**)&entry, target, &type) != SUCCESS) { INIT(entry, .target = strdup(target), - .file = is_file_logger, + .type = type, ); - if (is_file_logger) + switch (type) { - entry->logger.file = file_logger_create(target); - } + case FILE_LOGGER: + entry->logger.file = file_logger_create(target); + break; + case SYS_LOGGER: #ifdef HAVE_SYSLOG - else - { - entry->logger.sys = sys_logger_create(get_syslog_facility(target)); - } + entry->logger.sys = sys_logger_create( + get_syslog_facility(target)); + break; +#else + free(entry); + return NULL; #endif /* HAVE_SYSLOG */ + case CUSTOM_LOGGER: + if (constructor) + { + entry->logger.custom = constructor(target); + } + if (!entry->logger.custom) + { + free(entry); + return NULL; + } + break; + } } else { @@ -266,9 +381,12 @@ static sys_logger_t *add_sys_logger(private_daemon_t *this, char *facility, { logger_entry_t *entry; - entry = get_logger_entry(facility, FALSE, current_loggers); - this->loggers->insert_last(this->loggers, entry); - return entry->logger.sys; + entry = get_logger_entry(facility, SYS_LOGGER, current_loggers, NULL); + if (entry) + { + this->loggers->insert_last(this->loggers, entry); + } + return entry ? entry->logger.sys : NULL; } /** @@ -279,9 +397,30 @@ static file_logger_t *add_file_logger(private_daemon_t *this, char *filename, { logger_entry_t *entry; - entry = get_logger_entry(filename, TRUE, current_loggers); - this->loggers->insert_last(this->loggers, entry); - return entry->logger.file; + entry = get_logger_entry(filename, FILE_LOGGER, current_loggers, NULL); + if (entry) + { + this->loggers->insert_last(this->loggers, entry); + } + return entry ? entry->logger.file : NULL; +} + + /** + * Create or reuse a custom logger + */ +static custom_logger_t *add_custom_logger(private_daemon_t *this, + custom_logger_entry_t *custom, + linked_list_t *current_loggers) +{ + logger_entry_t *entry; + + entry = get_logger_entry(custom->name, CUSTOM_LOGGER, current_loggers, + custom->constructor); + if (entry) + { + this->loggers->insert_last(this->loggers, entry); + } + return entry ? entry->logger.custom : NULL; } /** @@ -300,6 +439,11 @@ static void load_sys_logger(private_daemon_t *this, char *facility, } sys_logger = add_sys_logger(this, facility, current_loggers); + if (!sys_logger) + { + return; + } + sys_logger->set_options(sys_logger, lib->settings->get_bool(lib->settings, "%s.syslog.%s.ike_name", FALSE, lib->ns, facility)); @@ -339,6 +483,11 @@ static void load_file_logger(private_daemon_t *this, char *filename, "%s.filelog.%s.append", TRUE, lib->ns, filename); file_logger = add_file_logger(this, filename, current_loggers); + if (!file_logger) + { + return; + } + file_logger->set_options(file_logger, time_format, add_ms, ike_name); file_logger->open(file_logger, flush_line, append); @@ -353,12 +502,41 @@ static void load_file_logger(private_daemon_t *this, char *filename, charon->bus->add_logger(charon->bus, &file_logger->logger); } +/** + * Load the given custom logger configured in strongswan.conf + */ +static void load_custom_logger(private_daemon_t *this, + custom_logger_entry_t *entry, + linked_list_t *current_loggers) +{ + custom_logger_t *custom_logger; + debug_t group; + level_t def; + + custom_logger = add_custom_logger(this, entry, current_loggers); + if (!custom_logger) + { + return; + } + + def = lib->settings->get_int(lib->settings, "%s.customlog.%s.default", 1, + lib->ns, entry->name); + for (group = 0; group < DBG_MAX; group++) + { + custom_logger->set_level(custom_logger, group, + lib->settings->get_int(lib->settings, "%s.customlog.%s.%N", def, + lib->ns, entry->name, debug_lower_names, group)); + } + charon->bus->add_logger(charon->bus, &custom_logger->logger); +} + METHOD(daemon_t, load_loggers, void, private_daemon_t *this, level_t levels[DBG_MAX], bool to_stderr) { enumerator_t *enumerator; linked_list_t *current_loggers; char *target; + int i; this->mutex->lock(this->mutex); handle_syslog_identifier(this); @@ -380,6 +558,11 @@ METHOD(daemon_t, load_loggers, void, } enumerator->destroy(enumerator); + for (i = 0; i < custom_logger_count; ++i) + { + load_custom_logger(this, &custom_loggers[i], current_loggers); + } + if (!this->loggers->get_count(this->loggers) && levels) { /* setup legacy style default loggers configured via command-line */ file_logger_t *file_logger; @@ -431,15 +614,24 @@ METHOD(daemon_t, set_level, void, enumerator = this->loggers->create_enumerator(this->loggers); while (enumerator->enumerate(enumerator, &entry)) { - if (entry->file) - { - entry->logger.file->set_level(entry->logger.file, group, level); - charon->bus->add_logger(charon->bus, &entry->logger.file->logger); - } - else + switch (entry->type) { - entry->logger.sys->set_level(entry->logger.sys, group, level); - charon->bus->add_logger(charon->bus, &entry->logger.sys->logger); + case FILE_LOGGER: + entry->logger.file->set_level(entry->logger.file, group, level); + charon->bus->add_logger(charon->bus, + &entry->logger.file->logger); + break; + case SYS_LOGGER: + entry->logger.sys->set_level(entry->logger.sys, group, level); + charon->bus->add_logger(charon->bus, + &entry->logger.sys->logger); + break; + case CUSTOM_LOGGER: + entry->logger.custom->set_level(entry->logger.custom, group, + level); + charon->bus->add_logger(charon->bus, + &entry->logger.sys->logger); + break; } } enumerator->destroy(enumerator); @@ -488,11 +680,13 @@ static void destroy(private_daemon_t *this) DESTROY_IF(this->kernel_handler); DESTROY_IF(this->public.traps); DESTROY_IF(this->public.shunts); + DESTROY_IF(this->public.redirect); DESTROY_IF(this->public.controller); DESTROY_IF(this->public.eap); DESTROY_IF(this->public.xauth); DESTROY_IF(this->public.backends); DESTROY_IF(this->public.socket); + DESTROY_IF(this->public.kernel); /* rehook library logging, shutdown logging */ dbg = dbg_old; @@ -670,6 +864,7 @@ private_daemon_t *daemon_create() .ref = 1, ); charon = &this->public; + this->public.kernel = kernel_interface_create(); this->public.attributes = attribute_manager_create(); this->public.controller = controller_create(); this->public.eap = eap_manager_create(); @@ -678,6 +873,7 @@ private_daemon_t *daemon_create() this->public.socket = socket_manager_create(); this->public.traps = trap_manager_create(); this->public.shunts = shunt_manager_create(); + this->public.redirect = redirect_manager_create(); this->kernel_handler = kernel_handler_create(); return this; diff --git a/src/libcharon/daemon.h b/src/libcharon/daemon.h index d16bf1ddb..48b9c7ec3 100644 --- a/src/libcharon/daemon.h +++ b/src/libcharon/daemon.h @@ -16,6 +16,29 @@ * for more details. */ +/* + * Copyright (C) 2016 secunet Security Networks AG + * Copyright (C) 2016 Thomas Egerer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + /** * @defgroup libcharon libcharon * @@ -40,7 +63,7 @@ * @defgroup payloads payloads * @ingroup encoding * - * @defgroup ckernel kernel + * @defgroup kernel kernel * @ingroup libcharon * * @defgroup network network @@ -156,15 +179,18 @@ typedef struct daemon_t daemon_t; #include <attributes/attribute_manager.h> +#include <kernel/kernel_interface.h> #include <network/sender.h> #include <network/receiver.h> #include <network/socket_manager.h> #include <control/controller.h> #include <bus/bus.h> +#include <bus/listeners/custom_logger.h> #include <sa/ike_sa_manager.h> #include <sa/child_sa_manager.h> #include <sa/trap_manager.h> #include <sa/shunt_manager.h> +#include <sa/redirect_manager.h> #include <config/backend_manager.h> #include <sa/eap/eap_manager.h> #include <sa/xauth/xauth_manager.h> @@ -215,6 +241,11 @@ struct daemon_t { socket_manager_t *socket; /** + * Kernel interface to communicate with kernel + */ + kernel_interface_t *kernel; + + /** * A ike_sa_manager_t instance. */ ike_sa_manager_t *ike_sa_manager; @@ -235,6 +266,11 @@ struct daemon_t { shunt_manager_t *shunts; /** + * Manager for IKE redirect providers + */ + redirect_manager_t *redirect; + + /** * Manager for the different configuration backends. */ backend_manager_t *backends; @@ -311,8 +347,8 @@ struct daemon_t { bool to_stderr); /** - * Set the log level for the given log group for all configured file- and - * syslog-loggers. + * Set the log level for the given log group for all configured file-, + * syslog and custom-loggers. * * @param group log group * @param level log level @@ -345,4 +381,15 @@ bool libcharon_init(); */ void libcharon_deinit(); +/** + * Register a custom logger constructor. + * + * To be called from __attribute__((constructor)) functions. + * + * @param name name of the logger (also used for loglevel config) + * @param constructor constructor to create custom logger + */ +void register_custom_logger(char *name, + custom_logger_constructor_t constructor); + #endif /** DAEMON_H_ @}*/ diff --git a/src/libcharon/encoding/message.c b/src/libcharon/encoding/message.c index 3303024cd..bbdc4629d 100644 --- a/src/libcharon/encoding/message.c +++ b/src/libcharon/encoding/message.c @@ -551,13 +551,13 @@ static payload_order_t aggressive_i_order[] = { {PLV1_NONCE, 0}, {PLV1_ID, 0}, {PLV1_CERTIFICATE, 0}, + {PLV1_CERTREQ, 0}, + {PLV1_NOTIFY, 0}, + {PLV1_VENDOR_ID, 0}, {PLV1_NAT_D, 0}, {PLV1_NAT_D_DRAFT_00_03, 0}, {PLV1_SIGNATURE, 0}, {PLV1_HASH, 0}, - {PLV1_CERTREQ, 0}, - {PLV1_NOTIFY, 0}, - {PLV1_VENDOR_ID, 0}, {PLV1_FRAGMENT, 0}, }; @@ -591,13 +591,13 @@ static payload_order_t aggressive_r_order[] = { {PLV1_NONCE, 0}, {PLV1_ID, 0}, {PLV1_CERTIFICATE, 0}, + {PLV1_CERTREQ, 0}, + {PLV1_NOTIFY, 0}, + {PLV1_VENDOR_ID, 0}, {PLV1_NAT_D, 0}, {PLV1_NAT_D_DRAFT_00_03, 0}, {PLV1_SIGNATURE, 0}, {PLV1_HASH, 0}, - {PLV1_CERTREQ, 0}, - {PLV1_NOTIFY, 0}, - {PLV1_VENDOR_ID, 0}, {PLV1_FRAGMENT, 0}, }; diff --git a/src/libcharon/encoding/payloads/configuration_attribute.c b/src/libcharon/encoding/payloads/configuration_attribute.c index 481bb7bc6..4ecdf569d 100644 --- a/src/libcharon/encoding/payloads/configuration_attribute.c +++ b/src/libcharon/encoding/payloads/configuration_attribute.c @@ -132,6 +132,7 @@ METHOD(payload_t, verify, status_t, case INTERNAL_IP4_NBNS: case INTERNAL_ADDRESS_EXPIRY: case INTERNAL_IP4_DHCP: + case P_CSCF_IP4_ADDRESS: if (this->length_or_value != 0 && this->length_or_value != 4) { failed = TRUE; @@ -144,6 +145,13 @@ METHOD(payload_t, verify, status_t, } break; case INTERNAL_IP6_ADDRESS: + if (this->type == PLV1_CONFIGURATION_ATTRIBUTE && + this->length_or_value == 16) + { /* 16 bytes are correct for IKEv1, but older releases sent a + * prefix byte so we still accept 0 or 17 as in IKEv2 */ + break; + } + /* fall-through */ case INTERNAL_IP6_SUBNET: if (this->length_or_value != 0 && this->length_or_value != 17) { @@ -153,6 +161,7 @@ METHOD(payload_t, verify, status_t, case INTERNAL_IP6_DNS: case INTERNAL_IP6_NBNS: case INTERNAL_IP6_DHCP: + case P_CSCF_IP6_ADDRESS: if (this->length_or_value != 0 && this->length_or_value != 16) { failed = TRUE; diff --git a/src/libcharon/kernel/kernel_handler.c b/src/libcharon/kernel/kernel_handler.c index 9c0e2602b..be37d30e5 100644 --- a/src/libcharon/kernel/kernel_handler.c +++ b/src/libcharon/kernel/kernel_handler.c @@ -15,7 +15,6 @@ #include "kernel_handler.h" -#include <hydra.h> #include <daemon.h> #include <processing/jobs/acquire_job.h> #include <processing/jobs/delete_child_sa_job.h> @@ -135,8 +134,7 @@ METHOD(kernel_listener_t, roam, bool, METHOD(kernel_handler_t, destroy, void, private_kernel_handler_t *this) { - hydra->kernel_interface->remove_listener(hydra->kernel_interface, - &this->public.listener); + charon->kernel->remove_listener(charon->kernel, &this->public.listener); free(this); } @@ -157,8 +155,7 @@ kernel_handler_t *kernel_handler_create() }, ); - hydra->kernel_interface->add_listener(hydra->kernel_interface, - &this->public.listener); + charon->kernel->add_listener(charon->kernel, &this->public.listener); return &this->public; } diff --git a/src/libcharon/kernel/kernel_handler.h b/src/libcharon/kernel/kernel_handler.h index 48ad6889c..f1fa0bdfc 100644 --- a/src/libcharon/kernel/kernel_handler.h +++ b/src/libcharon/kernel/kernel_handler.h @@ -15,7 +15,7 @@ /** * @defgroup kernel_handler kernel_handler - * @{ @ingroup ckernel + * @{ @ingroup kernel */ #ifndef KERNEL_HANDLER_H_ diff --git a/src/libcharon/kernel/kernel_interface.c b/src/libcharon/kernel/kernel_interface.c new file mode 100644 index 000000000..40c4ee589 --- /dev/null +++ b/src/libcharon/kernel/kernel_interface.c @@ -0,0 +1,1085 @@ +/* + * Copyright (C) 2008-2015 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 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. + */ + +/* + * Copyright (c) 2012 Nanoteq Pty Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "kernel_interface.h" + +#include <utils/debug.h> +#include <threading/mutex.h> +#include <collections/linked_list.h> +#include <collections/hashtable.h> +#include <collections/array.h> + +typedef struct private_kernel_interface_t private_kernel_interface_t; + +typedef struct kernel_algorithm_t kernel_algorithm_t; + +/** + * Mapping of IKE algorithms to kernel-specific algorithm identifiers + */ +struct kernel_algorithm_t { + + /** + * Transform type of the algorithm + */ + transform_type_t type; + + /** + * Identifier specified in IKE + */ + u_int16_t ike; + + /** + * Identifier as defined in pfkeyv2.h + */ + u_int16_t kernel; + + /** + * Name of the algorithm in linux crypto API + */ + char *name; +}; + +/** + * Private data of a kernel_interface_t object. + */ +struct private_kernel_interface_t { + + /** + * Public part of kernel_interface_t object. + */ + kernel_interface_t public; + + /** + * Registered IPsec constructor + */ + kernel_ipsec_constructor_t ipsec_constructor; + + /** + * Registered net constructor + */ + kernel_net_constructor_t net_constructor; + + /** + * ipsec interface + */ + kernel_ipsec_t *ipsec; + + /** + * network interface + */ + kernel_net_t *net; + + /** + * mutex for listeners + */ + mutex_t *mutex; + + /** + * list of registered listeners + */ + linked_list_t *listeners; + + /** + * Reqid entries indexed by reqids + */ + hashtable_t *reqids; + + /** + * Reqid entries indexed by traffic selectors + */ + hashtable_t *reqids_by_ts; + + /** + * mutex for algorithm mappings + */ + mutex_t *mutex_algs; + + /** + * List of algorithm mappings (kernel_algorithm_t*) + */ + linked_list_t *algorithms; + + /** + * List of interface names to include or exclude (char*), NULL if interfaces + * are not filtered + */ + linked_list_t *ifaces_filter; + + /** + * TRUE to exclude interfaces listed in ifaces_filter, FALSE to consider + * only those listed there + */ + bool ifaces_exclude; +}; + +METHOD(kernel_interface_t, get_features, kernel_feature_t, + private_kernel_interface_t *this) +{ + kernel_feature_t features = 0; + + if (this->ipsec && this->ipsec->get_features) + { + features |= this->ipsec->get_features(this->ipsec); + } + if (this->net && this->net->get_features) + { + features |= this->net->get_features(this->net); + } + return features; +} + +METHOD(kernel_interface_t, get_spi, status_t, + private_kernel_interface_t *this, host_t *src, host_t *dst, + u_int8_t protocol, u_int32_t *spi) +{ + if (!this->ipsec) + { + return NOT_SUPPORTED; + } + return this->ipsec->get_spi(this->ipsec, src, dst, protocol, spi); +} + +METHOD(kernel_interface_t, get_cpi, status_t, + private_kernel_interface_t *this, host_t *src, host_t *dst, + u_int16_t *cpi) +{ + if (!this->ipsec) + { + return NOT_SUPPORTED; + } + return this->ipsec->get_cpi(this->ipsec, src, dst, cpi); +} + +/** + * Reqid mapping entry + */ +typedef struct { + /** allocated reqid */ + u_int32_t reqid; + /** references to this entry */ + u_int refs; + /** inbound mark used for SA */ + mark_t mark_in; + /** outbound mark used for SA */ + mark_t mark_out; + /** local traffic selectors */ + array_t *local; + /** remote traffic selectors */ + array_t *remote; +} reqid_entry_t; + +/** + * Destroy a reqid mapping entry + */ +static void reqid_entry_destroy(reqid_entry_t *entry) +{ + array_destroy_offset(entry->local, offsetof(traffic_selector_t, destroy)); + array_destroy_offset(entry->remote, offsetof(traffic_selector_t, destroy)); + free(entry); +} + +/** + * Hashtable hash function for reqid entries using reqid as key + */ +static u_int hash_reqid(reqid_entry_t *entry) +{ + return chunk_hash_inc(chunk_from_thing(entry->reqid), + chunk_hash_inc(chunk_from_thing(entry->mark_in), + chunk_hash(chunk_from_thing(entry->mark_out)))); +} + +/** + * Hashtable equals function for reqid entries using reqid as key + */ +static bool equals_reqid(reqid_entry_t *a, reqid_entry_t *b) +{ + return a->reqid == b->reqid && + a->mark_in.value == b->mark_in.value && + a->mark_in.mask == b->mark_in.mask && + a->mark_out.value == b->mark_out.value && + a->mark_out.mask == b->mark_out.mask; +} + +/** + * Hash an array of traffic selectors + */ +static u_int hash_ts_array(array_t *array, u_int hash) +{ + enumerator_t *enumerator; + traffic_selector_t *ts; + + enumerator = array_create_enumerator(array); + while (enumerator->enumerate(enumerator, &ts)) + { + hash = ts->hash(ts, hash); + } + enumerator->destroy(enumerator); + + return hash; +} + +/** + * Hashtable hash function for reqid entries using traffic selectors as key + */ +static u_int hash_reqid_by_ts(reqid_entry_t *entry) +{ + return hash_ts_array(entry->local, hash_ts_array(entry->remote, + chunk_hash_inc(chunk_from_thing(entry->mark_in), + chunk_hash(chunk_from_thing(entry->mark_out))))); +} + +/** + * Compare two array with traffic selectors for equality + */ +static bool ts_array_equals(array_t *a, array_t *b) +{ + traffic_selector_t *tsa, *tsb; + enumerator_t *ae, *be; + bool equal = TRUE; + + if (array_count(a) != array_count(b)) + { + return FALSE; + } + + ae = array_create_enumerator(a); + be = array_create_enumerator(b); + while (equal && ae->enumerate(ae, &tsa) && be->enumerate(be, &tsb)) + { + equal = tsa->equals(tsa, tsb); + } + ae->destroy(ae); + be->destroy(be); + + return equal; +} + +/** + * Hashtable equals function for reqid entries using traffic selectors as key + */ +static bool equals_reqid_by_ts(reqid_entry_t *a, reqid_entry_t *b) +{ + return ts_array_equals(a->local, b->local) && + ts_array_equals(a->remote, b->remote) && + a->mark_in.value == b->mark_in.value && + a->mark_in.mask == b->mark_in.mask && + a->mark_out.value == b->mark_out.value && + a->mark_out.mask == b->mark_out.mask; +} + +/** + * Create an array from copied traffic selector list items + */ +static array_t *array_from_ts_list(linked_list_t *list) +{ + enumerator_t *enumerator; + traffic_selector_t *ts; + array_t *array; + + array = array_create(0, 0); + + enumerator = list->create_enumerator(list); + while (enumerator->enumerate(enumerator, &ts)) + { + array_insert(array, ARRAY_TAIL, ts->clone(ts)); + } + enumerator->destroy(enumerator); + + return array; +} + +METHOD(kernel_interface_t, alloc_reqid, status_t, + private_kernel_interface_t *this, + linked_list_t *local_ts, linked_list_t *remote_ts, + mark_t mark_in, mark_t mark_out, u_int32_t *reqid) +{ + static u_int32_t counter = 0; + reqid_entry_t *entry = NULL, *tmpl; + status_t status = SUCCESS; + + INIT(tmpl, + .local = array_from_ts_list(local_ts), + .remote = array_from_ts_list(remote_ts), + .mark_in = mark_in, + .mark_out = mark_out, + .reqid = *reqid, + ); + + this->mutex->lock(this->mutex); + if (tmpl->reqid) + { + /* search by reqid if given */ + entry = this->reqids->get(this->reqids, tmpl); + } + if (entry) + { + /* we don't require a traffic selector match for explicit reqids, + * as we wan't to reuse a reqid for trap-triggered policies that + * got narrowed during negotiation. */ + reqid_entry_destroy(tmpl); + } + else + { + /* search by traffic selectors */ + entry = this->reqids_by_ts->get(this->reqids_by_ts, tmpl); + if (entry) + { + reqid_entry_destroy(tmpl); + } + else + { + /* none found, create a new entry, allocating a reqid */ + entry = tmpl; + entry->reqid = ++counter; + this->reqids_by_ts->put(this->reqids_by_ts, entry, entry); + this->reqids->put(this->reqids, entry, entry); + } + *reqid = entry->reqid; + } + entry->refs++; + this->mutex->unlock(this->mutex); + + return status; +} + +METHOD(kernel_interface_t, release_reqid, status_t, + private_kernel_interface_t *this, u_int32_t reqid, + mark_t mark_in, mark_t mark_out) +{ + reqid_entry_t *entry, tmpl = { + .reqid = reqid, + .mark_in = mark_in, + .mark_out = mark_out, + }; + + this->mutex->lock(this->mutex); + entry = this->reqids->remove(this->reqids, &tmpl); + if (entry) + { + if (--entry->refs == 0) + { + entry = this->reqids_by_ts->remove(this->reqids_by_ts, entry); + if (entry) + { + reqid_entry_destroy(entry); + } + } + else + { + this->reqids->put(this->reqids, entry, entry); + } + } + this->mutex->unlock(this->mutex); + + if (entry) + { + return SUCCESS; + } + return NOT_FOUND; +} + +METHOD(kernel_interface_t, add_sa, status_t, + private_kernel_interface_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, bool update, + linked_list_t *src_ts, linked_list_t *dst_ts) +{ + if (!this->ipsec) + { + return NOT_SUPPORTED; + } + return this->ipsec->add_sa(this->ipsec, src, dst, spi, protocol, reqid, + mark, tfc, lifetime, enc_alg, enc_key, int_alg, int_key, mode, + ipcomp, cpi, replay_window, initiator, encap, esn, inbound, + update, src_ts, dst_ts); +} + +METHOD(kernel_interface_t, update_sa, status_t, + private_kernel_interface_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) +{ + if (!this->ipsec) + { + return NOT_SUPPORTED; + } + return this->ipsec->update_sa(this->ipsec, spi, protocol, cpi, src, dst, + new_src, new_dst, encap, new_encap, mark); +} + +METHOD(kernel_interface_t, query_sa, status_t, + private_kernel_interface_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) +{ + if (!this->ipsec) + { + return NOT_SUPPORTED; + } + return this->ipsec->query_sa(this->ipsec, src, dst, spi, protocol, mark, + bytes, packets, time); +} + +METHOD(kernel_interface_t, del_sa, status_t, + private_kernel_interface_t *this, host_t *src, host_t *dst, u_int32_t spi, + u_int8_t protocol, u_int16_t cpi, mark_t mark) +{ + if (!this->ipsec) + { + return NOT_SUPPORTED; + } + return this->ipsec->del_sa(this->ipsec, src, dst, spi, protocol, cpi, mark); +} + +METHOD(kernel_interface_t, flush_sas, status_t, + private_kernel_interface_t *this) +{ + if (!this->ipsec) + { + return NOT_SUPPORTED; + } + return this->ipsec->flush_sas(this->ipsec); +} + +METHOD(kernel_interface_t, add_policy, status_t, + private_kernel_interface_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) +{ + if (!this->ipsec) + { + return NOT_SUPPORTED; + } + return this->ipsec->add_policy(this->ipsec, src, dst, src_ts, dst_ts, + direction, type, sa, mark, priority); +} + +METHOD(kernel_interface_t, query_policy, status_t, + private_kernel_interface_t *this, traffic_selector_t *src_ts, + traffic_selector_t *dst_ts, policy_dir_t direction, mark_t mark, + time_t *use_time) +{ + if (!this->ipsec) + { + return NOT_SUPPORTED; + } + return this->ipsec->query_policy(this->ipsec, src_ts, dst_ts, + direction, mark, use_time); +} + +METHOD(kernel_interface_t, del_policy, status_t, + private_kernel_interface_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) +{ + if (!this->ipsec) + { + return NOT_SUPPORTED; + } + return this->ipsec->del_policy(this->ipsec, src, dst, src_ts, dst_ts, + direction, type, sa, mark, priority); +} + +METHOD(kernel_interface_t, flush_policies, status_t, + private_kernel_interface_t *this) +{ + if (!this->ipsec) + { + return NOT_SUPPORTED; + } + return this->ipsec->flush_policies(this->ipsec); +} + +METHOD(kernel_interface_t, get_source_addr, host_t*, + private_kernel_interface_t *this, host_t *dest, host_t *src) +{ + if (!this->net) + { + return NULL; + } + return this->net->get_source_addr(this->net, dest, src); +} + +METHOD(kernel_interface_t, get_nexthop, host_t*, + private_kernel_interface_t *this, host_t *dest, int prefix, host_t *src) +{ + if (!this->net) + { + return NULL; + } + return this->net->get_nexthop(this->net, dest, prefix, src); +} + +METHOD(kernel_interface_t, get_interface, bool, + private_kernel_interface_t *this, host_t *host, char **name) +{ + if (!this->net) + { + return NULL; + } + return this->net->get_interface(this->net, host, name); +} + +METHOD(kernel_interface_t, create_address_enumerator, enumerator_t*, + private_kernel_interface_t *this, kernel_address_type_t which) +{ + if (!this->net) + { + return enumerator_create_empty(); + } + return this->net->create_address_enumerator(this->net, which); +} + +METHOD(kernel_interface_t, add_ip, status_t, + private_kernel_interface_t *this, host_t *virtual_ip, int prefix, + char *iface) +{ + if (!this->net) + { + return NOT_SUPPORTED; + } + return this->net->add_ip(this->net, virtual_ip, prefix, iface); +} + +METHOD(kernel_interface_t, del_ip, status_t, + private_kernel_interface_t *this, host_t *virtual_ip, int prefix, bool wait) +{ + if (!this->net) + { + return NOT_SUPPORTED; + } + return this->net->del_ip(this->net, virtual_ip, prefix, wait); +} + +METHOD(kernel_interface_t, add_route, status_t, + private_kernel_interface_t *this, chunk_t dst_net, + u_int8_t prefixlen, host_t *gateway, host_t *src_ip, char *if_name) +{ + if (!this->net) + { + return NOT_SUPPORTED; + } + return this->net->add_route(this->net, dst_net, prefixlen, gateway, + src_ip, if_name); +} + +METHOD(kernel_interface_t, del_route, status_t, + private_kernel_interface_t *this, chunk_t dst_net, + u_int8_t prefixlen, host_t *gateway, host_t *src_ip, char *if_name) +{ + if (!this->net) + { + return NOT_SUPPORTED; + } + return this->net->del_route(this->net, dst_net, prefixlen, gateway, + src_ip, if_name); +} + +METHOD(kernel_interface_t, bypass_socket, bool, + private_kernel_interface_t *this, int fd, int family) +{ + if (!this->ipsec) + { + return FALSE; + } + return this->ipsec->bypass_socket(this->ipsec, fd, family); +} + +METHOD(kernel_interface_t, enable_udp_decap, bool, + private_kernel_interface_t *this, int fd, int family, u_int16_t port) +{ + if (!this->ipsec) + { + return FALSE; + } + return this->ipsec->enable_udp_decap(this->ipsec, fd, family, port); +} + +METHOD(kernel_interface_t, is_interface_usable, bool, + private_kernel_interface_t *this, const char *iface) +{ + status_t expected; + + if (!this->ifaces_filter) + { + return TRUE; + } + expected = this->ifaces_exclude ? NOT_FOUND : SUCCESS; + return this->ifaces_filter->find_first(this->ifaces_filter, (void*)streq, + NULL, iface) == expected; +} + +METHOD(kernel_interface_t, all_interfaces_usable, bool, + private_kernel_interface_t *this) +{ + return this->ifaces_filter == NULL; +} + +METHOD(kernel_interface_t, get_address_by_ts, status_t, + private_kernel_interface_t *this, traffic_selector_t *ts, + host_t **ip, bool *vip) +{ + enumerator_t *addrs; + host_t *host; + int family; + bool found = FALSE; + + DBG2(DBG_KNL, "getting a local address in traffic selector %R", ts); + + /* if we have a family which includes localhost, we do not + * search for an IP, we use the default */ + family = ts->get_type(ts) == TS_IPV4_ADDR_RANGE ? AF_INET : AF_INET6; + + if (family == AF_INET) + { + host = host_create_from_string("127.0.0.1", 0); + } + else + { + host = host_create_from_string("::1", 0); + } + + if (ts->includes(ts, host)) + { + *ip = host_create_any(family); + host->destroy(host); + DBG2(DBG_KNL, "using host %H", *ip); + return SUCCESS; + } + host->destroy(host); + + /* try virtual IPs only first (on all interfaces) */ + addrs = create_address_enumerator(this, + ADDR_TYPE_ALL ^ ADDR_TYPE_REGULAR); + while (addrs->enumerate(addrs, (void**)&host)) + { + if (ts->includes(ts, host)) + { + found = TRUE; + *ip = host->clone(host); + if (vip) + { + *vip = TRUE; + } + break; + } + } + addrs->destroy(addrs); + + if (!found) + { /* then try the regular addresses (on all interfaces) */ + addrs = create_address_enumerator(this, + ADDR_TYPE_ALL ^ ADDR_TYPE_VIRTUAL); + while (addrs->enumerate(addrs, (void**)&host)) + { + if (ts->includes(ts, host)) + { + found = TRUE; + *ip = host->clone(host); + if (vip) + { + *vip = FALSE; + } + break; + } + } + addrs->destroy(addrs); + } + + if (!found) + { + DBG2(DBG_KNL, "no local address found in traffic selector %R", ts); + return FAILED; + } + + DBG2(DBG_KNL, "using host %H", *ip); + return SUCCESS; +} + + +METHOD(kernel_interface_t, add_ipsec_interface, bool, + private_kernel_interface_t *this, kernel_ipsec_constructor_t constructor) +{ + if (!this->ipsec) + { + this->ipsec_constructor = constructor; + this->ipsec = constructor(); + return this->ipsec != NULL; + } + return FALSE; +} + +METHOD(kernel_interface_t, remove_ipsec_interface, bool, + private_kernel_interface_t *this, kernel_ipsec_constructor_t constructor) +{ + if (constructor == this->ipsec_constructor && this->ipsec) + { + this->ipsec->destroy(this->ipsec); + this->ipsec = NULL; + return TRUE; + } + return FALSE; +} + +METHOD(kernel_interface_t, add_net_interface, bool, + private_kernel_interface_t *this, kernel_net_constructor_t constructor) +{ + if (!this->net) + { + this->net_constructor = constructor; + this->net = constructor(); + return this->net != NULL; + } + return FALSE; +} + +METHOD(kernel_interface_t, remove_net_interface, bool, + private_kernel_interface_t *this, kernel_net_constructor_t constructor) +{ + if (constructor == this->net_constructor && this->net) + { + this->net->destroy(this->net); + this->net = NULL; + return TRUE; + } + return FALSE; +} + +METHOD(kernel_interface_t, add_listener, void, + private_kernel_interface_t *this, kernel_listener_t *listener) +{ + this->mutex->lock(this->mutex); + this->listeners->insert_last(this->listeners, listener); + this->mutex->unlock(this->mutex); +} + +METHOD(kernel_interface_t, remove_listener, void, + private_kernel_interface_t *this, kernel_listener_t *listener) +{ + this->mutex->lock(this->mutex); + this->listeners->remove(this->listeners, listener, NULL); + this->mutex->unlock(this->mutex); +} + +METHOD(kernel_interface_t, acquire, void, + private_kernel_interface_t *this, u_int32_t reqid, + traffic_selector_t *src_ts, traffic_selector_t *dst_ts) +{ + kernel_listener_t *listener; + enumerator_t *enumerator; + this->mutex->lock(this->mutex); + enumerator = this->listeners->create_enumerator(this->listeners); + while (enumerator->enumerate(enumerator, &listener)) + { + if (listener->acquire && + !listener->acquire(listener, reqid, src_ts, dst_ts)) + { + this->listeners->remove_at(this->listeners, enumerator); + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); +} + +METHOD(kernel_interface_t, expire, void, + private_kernel_interface_t *this, u_int8_t protocol, u_int32_t spi, + host_t *dst, bool hard) +{ + kernel_listener_t *listener; + enumerator_t *enumerator; + + this->mutex->lock(this->mutex); + enumerator = this->listeners->create_enumerator(this->listeners); + while (enumerator->enumerate(enumerator, &listener)) + { + if (listener->expire && + !listener->expire(listener, protocol, spi, dst, hard)) + { + this->listeners->remove_at(this->listeners, enumerator); + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); +} + +METHOD(kernel_interface_t, mapping, void, + private_kernel_interface_t *this, u_int8_t protocol, u_int32_t spi, + host_t *dst, host_t *remote) +{ + kernel_listener_t *listener; + enumerator_t *enumerator; + + this->mutex->lock(this->mutex); + enumerator = this->listeners->create_enumerator(this->listeners); + while (enumerator->enumerate(enumerator, &listener)) + { + if (listener->mapping && + !listener->mapping(listener, protocol, spi, dst, remote)) + { + this->listeners->remove_at(this->listeners, enumerator); + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); +} + +METHOD(kernel_interface_t, migrate, void, + private_kernel_interface_t *this, u_int32_t reqid, + traffic_selector_t *src_ts, traffic_selector_t *dst_ts, + policy_dir_t direction, host_t *local, host_t *remote) +{ + kernel_listener_t *listener; + enumerator_t *enumerator; + this->mutex->lock(this->mutex); + enumerator = this->listeners->create_enumerator(this->listeners); + while (enumerator->enumerate(enumerator, &listener)) + { + if (listener->migrate && + !listener->migrate(listener, reqid, src_ts, dst_ts, direction, + local, remote)) + { + this->listeners->remove_at(this->listeners, enumerator); + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); +} + +static bool call_roam(kernel_listener_t *listener, bool *roam) +{ + return listener->roam && !listener->roam(listener, *roam); +} + +METHOD(kernel_interface_t, roam, void, + private_kernel_interface_t *this, bool address) +{ + this->mutex->lock(this->mutex); + this->listeners->remove(this->listeners, &address, (void*)call_roam); + this->mutex->unlock(this->mutex); +} + +METHOD(kernel_interface_t, tun, void, + private_kernel_interface_t *this, tun_device_t *tun, bool created) +{ + kernel_listener_t *listener; + enumerator_t *enumerator; + this->mutex->lock(this->mutex); + enumerator = this->listeners->create_enumerator(this->listeners); + while (enumerator->enumerate(enumerator, &listener)) + { + if (listener->tun && + !listener->tun(listener, tun, created)) + { + this->listeners->remove_at(this->listeners, enumerator); + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); +} + +METHOD(kernel_interface_t, register_algorithm, void, + private_kernel_interface_t *this, u_int16_t alg_id, transform_type_t type, + u_int16_t kernel_id, char *kernel_name) +{ + kernel_algorithm_t *algorithm; + + INIT(algorithm, + .type = type, + .ike = alg_id, + .kernel = kernel_id, + .name = strdup(kernel_name), + ); + + this->mutex_algs->lock(this->mutex_algs); + this->algorithms->insert_first(this->algorithms, algorithm); + this->mutex_algs->unlock(this->mutex_algs); +} + +METHOD(kernel_interface_t, lookup_algorithm, bool, + private_kernel_interface_t *this, u_int16_t alg_id, transform_type_t type, + u_int16_t *kernel_id, char **kernel_name) +{ + kernel_algorithm_t *algorithm; + enumerator_t *enumerator; + bool found = FALSE; + + this->mutex_algs->lock(this->mutex_algs); + enumerator = this->algorithms->create_enumerator(this->algorithms); + while (enumerator->enumerate(enumerator, &algorithm)) + { + if (algorithm->type == type && algorithm->ike == alg_id) + { + if (kernel_id) + { + *kernel_id = algorithm->kernel; + } + if (kernel_name) + { + *kernel_name = algorithm->name; + } + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + this->mutex_algs->unlock(this->mutex_algs); + return found; +} + +METHOD(kernel_interface_t, destroy, void, + private_kernel_interface_t *this) +{ + kernel_algorithm_t *algorithm; + + while (this->algorithms->remove_first(this->algorithms, + (void**)&algorithm) == SUCCESS) + { + free(algorithm->name); + free(algorithm); + } + this->algorithms->destroy(this->algorithms); + this->mutex_algs->destroy(this->mutex_algs); + DESTROY_IF(this->ipsec); + DESTROY_IF(this->net); + DESTROY_FUNCTION_IF(this->ifaces_filter, (void*)free); + this->reqids->destroy(this->reqids); + this->reqids_by_ts->destroy(this->reqids_by_ts); + this->listeners->destroy(this->listeners); + this->mutex->destroy(this->mutex); + free(this); +} + +/* + * Described in header-file + */ +kernel_interface_t *kernel_interface_create() +{ + private_kernel_interface_t *this; + char *ifaces; + + INIT(this, + .public = { + .get_features = _get_features, + .get_spi = _get_spi, + .get_cpi = _get_cpi, + .alloc_reqid = _alloc_reqid, + .release_reqid = _release_reqid, + .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, + .get_source_addr = _get_source_addr, + .get_nexthop = _get_nexthop, + .get_interface = _get_interface, + .create_address_enumerator = _create_address_enumerator, + .add_ip = _add_ip, + .del_ip = _del_ip, + .add_route = _add_route, + .del_route = _del_route, + .bypass_socket = _bypass_socket, + .enable_udp_decap = _enable_udp_decap, + + .is_interface_usable = _is_interface_usable, + .all_interfaces_usable = _all_interfaces_usable, + .get_address_by_ts = _get_address_by_ts, + .add_ipsec_interface = _add_ipsec_interface, + .remove_ipsec_interface = _remove_ipsec_interface, + .add_net_interface = _add_net_interface, + .remove_net_interface = _remove_net_interface, + + .add_listener = _add_listener, + .remove_listener = _remove_listener, + .register_algorithm = _register_algorithm, + .lookup_algorithm = _lookup_algorithm, + .acquire = _acquire, + .expire = _expire, + .mapping = _mapping, + .migrate = _migrate, + .roam = _roam, + .tun = _tun, + .destroy = _destroy, + }, + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + .listeners = linked_list_create(), + .mutex_algs = mutex_create(MUTEX_TYPE_DEFAULT), + .algorithms = linked_list_create(), + .reqids = hashtable_create((hashtable_hash_t)hash_reqid, + (hashtable_equals_t)equals_reqid, 8), + .reqids_by_ts = hashtable_create((hashtable_hash_t)hash_reqid_by_ts, + (hashtable_equals_t)equals_reqid_by_ts, 8), + ); + + ifaces = lib->settings->get_str(lib->settings, + "%s.interfaces_use", NULL, lib->ns); + if (!ifaces) + { + this->ifaces_exclude = TRUE; + ifaces = lib->settings->get_str(lib->settings, + "%s.interfaces_ignore", NULL, lib->ns); + } + if (ifaces) + { + enumerator_t *enumerator; + char *iface; + + enumerator = enumerator_create_token(ifaces, ",", " "); + while (enumerator->enumerate(enumerator, &iface)) + { + if (!this->ifaces_filter) + { + this->ifaces_filter = linked_list_create(); + } + this->ifaces_filter->insert_last(this->ifaces_filter, + strdup(iface)); + } + enumerator->destroy(enumerator); + } + + return &this->public; +} diff --git a/src/libcharon/kernel/kernel_interface.h b/src/libcharon/kernel/kernel_interface.h new file mode 100644 index 000000000..6793c6cc6 --- /dev/null +++ b/src/libcharon/kernel/kernel_interface.h @@ -0,0 +1,655 @@ +/* + * Copyright (C) 2006-2015 Tobias Brunner + * Copyright (C) 2006 Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * 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. + */ + +/* + * Copyright (c) 2012 Nanoteq Pty Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @defgroup kernel_interface kernel_interface + * @{ @ingroup kernel + */ + +#ifndef KERNEL_INTERFACE_H_ +#define KERNEL_INTERFACE_H_ + +typedef struct kernel_interface_t kernel_interface_t; +typedef enum kernel_feature_t kernel_feature_t; + +#include <networking/host.h> +#include <crypto/prf_plus.h> + +#include <kernel/kernel_listener.h> +#include <kernel/kernel_ipsec.h> +#include <kernel/kernel_net.h> + +/** + * Bitfield of optional features a kernel backend supports. + * + * This feature-set is for both, kernel_ipsec_t and kernel_net_t. Each + * backend returns a subset of these features. + */ +enum kernel_feature_t { + /** IPsec can process ESPv3 (RFC 4303) TFC padded packets */ + KERNEL_ESP_V3_TFC = (1<<0), + /** Networking requires an "exclude" route for IKE/ESP packets */ + KERNEL_REQUIRE_EXCLUDE_ROUTE = (1<<1), + /** IPsec implementation requires UDP encapsulation of ESP packets */ + KERNEL_REQUIRE_UDP_ENCAPSULATION = (1<<2), + /** IPsec backend does not require a policy reinstall on SA updates */ + KERNEL_NO_POLICY_UPDATES = (1<<3), +}; + +/** + * Constructor function for ipsec kernel interface + */ +typedef kernel_ipsec_t* (*kernel_ipsec_constructor_t)(void); + +/** + * Constructor function for network kernel interface + */ +typedef kernel_net_t* (*kernel_net_constructor_t)(void); + +/** + * Manager and wrapper for different kernel interfaces. + * + * The kernel interface handles the communication with the kernel + * for SA and policy management and interface and IP address management. + */ +struct kernel_interface_t { + + /** + * Get the feature set supported by the net and ipsec kernel backends. + * + * @return ORed feature-set of backends + */ + kernel_feature_t (*get_features)(kernel_interface_t *this); + + /** + * Get a SPI from the kernel. + * + * @param src source address of SA + * @param dst destination address of SA + * @param protocol protocol for SA (ESP/AH) + * @param spi allocated spi + * @return SUCCESS if operation completed + */ + status_t (*get_spi)(kernel_interface_t *this, host_t *src, host_t *dst, + u_int8_t protocol, u_int32_t *spi); + + /** + * Get a Compression Parameter Index (CPI) from the kernel. + * + * @param src source address of SA + * @param dst destination address of SA + * @param cpi allocated cpi + * @return SUCCESS if operation completed + */ + status_t (*get_cpi)(kernel_interface_t *this, host_t *src, host_t *dst, + u_int16_t *cpi); + + /** + * Allocate or confirm a reqid to use for a given SA pair. + * + * Each returned reqid by a successful call to alloc_reqid() must be + * released using release_reqid(). + * + * The reqid parameter is an in/out parameter. If it points to non-zero, + * the reqid is confirmed and registered for use. If it points to zero, + * a reqid is allocated for the given selectors, and returned to reqid. + * + * @param local_ts traffic selectors of local side for SA + * @param remote_ts traffic selectors of remote side for SA + * @param mark_in inbound mark on SA + * @param mark_out outbound mark on SA + * @param reqid allocated reqid + * @return SUCCESS if reqid allocated + */ + status_t (*alloc_reqid)(kernel_interface_t *this, + linked_list_t *local_ts, linked_list_t *remote_ts, + mark_t mark_in, mark_t mark_out, + u_int32_t *reqid); + + /** + * Release a previously allocated reqid. + * + * @param reqid reqid to release + * @param mark_in inbound mark on SA + * @param mark_out outbound mark on SA + * @return SUCCESS if reqid released + */ + status_t (*release_reqid)(kernel_interface_t *this, u_int32_t reqid, + mark_t mark_in, mark_t mark_out); + + /** + * Add an SA to the SAD. + * + * This function does install a single SA for a single protocol in one + * direction. + * + * @param src source address for this SA + * @param dst destination address for this SA + * @param spi SPI allocated by us or remote peer + * @param protocol protocol for this SA (ESP/AH) + * @param reqid reqid for this SA + * @param mark optional mark for this SA + * @param tfc Traffic Flow Confidentiality padding for this SA + * @param lifetime lifetime_cfg_t for this SA + * @param enc_alg Algorithm to use for encryption (ESP only) + * @param enc_key key to use for encryption + * @param int_alg Algorithm to use for integrity protection + * @param int_key key to use for integrity protection + * @param mode mode of the SA (tunnel, transport) + * @param ipcomp IPComp transform to use + * @param cpi CPI for IPComp + * @param replay_window anti-replay window size + * @param initiator TRUE if initiator of the exchange creating this SA + * @param encap enable UDP encapsulation for NAT traversal + * @param esn TRUE to use Extended Sequence Numbers + * @param inbound TRUE if this is an inbound SA + * @param update TRUE if an SPI has already been allocated for SA + * @param src_ts list of source traffic selectors + * @param dst_ts list of destination traffic selectors + * @return SUCCESS if operation completed + */ + status_t (*add_sa) (kernel_interface_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, bool update, + linked_list_t *src_ts, linked_list_t *dst_ts); + + /** + * Update the hosts on an installed SA. + * + * We cannot directly update the destination address as the kernel + * requires the spi, the protocol AND the destination address (and family) + * to identify SAs. Therefore if the destination address changed we + * create a new SA and delete the old one. + * + * @param spi SPI of the SA + * @param protocol protocol for this SA (ESP/AH) + * @param cpi CPI for IPComp, 0 if no IPComp is used + * @param src current source address + * @param dst current destination address + * @param new_src new source address + * @param new_dst new destination address + * @param encap current use of UDP encapsulation + * @param new_encap new use of UDP encapsulation + * @param mark optional mark for this SA + * @return SUCCESS if operation completed, NOT_SUPPORTED if + * the kernel interface can't update the SA + */ + status_t (*update_sa)(kernel_interface_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); + + /** + * Query the number of bytes processed by an SA from the SAD. + * + * @param src source address for this SA + * @param dst destination address for this SA + * @param spi SPI allocated by us or remote peer + * @param protocol protocol for this SA (ESP/AH) + * @param mark optional mark for this SA + * @param[out] bytes the number of bytes processed by SA + * @param[out] packets number of packets processed by SA + * @param[out] time last (monotonic) time of SA use + * @return SUCCESS if operation completed + */ + status_t (*query_sa) (kernel_interface_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); + + /** + * Delete a previously installed SA from the SAD. + * + * @param src source address for this SA + * @param dst destination address for this SA + * @param spi SPI allocated by us or remote peer + * @param protocol protocol for this SA (ESP/AH) + * @param cpi CPI for IPComp or 0 + * @param mark optional mark for this SA + * @return SUCCESS if operation completed + */ + status_t (*del_sa) (kernel_interface_t *this, host_t *src, host_t *dst, + u_int32_t spi, u_int8_t protocol, u_int16_t cpi, + mark_t mark); + + /** + * Flush all SAs from the SAD. + * + * @return SUCCESS if operation completed + */ + status_t (*flush_sas) (kernel_interface_t *this); + + /** + * Add a policy to the SPD. + * + * @param src source address of SA + * @param dst dest address of SA + * @param src_ts traffic selector to match traffic source + * @param dst_ts traffic selector to match traffic dest + * @param direction direction of traffic, POLICY_(IN|OUT|FWD) + * @param type type of policy, POLICY_(IPSEC|PASS|DROP) + * @param sa details about the SA(s) tied to this policy + * @param mark mark for this policy + * @param priority priority of this policy + * @return SUCCESS if operation completed + */ + status_t (*add_policy) (kernel_interface_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); + + /** + * Query the use time of a policy. + * + * The use time of a policy is the time the policy was used + * for the last time. + * + * @param src_ts traffic selector to match traffic source + * @param dst_ts traffic selector to match traffic dest + * @param direction direction of traffic, POLICY_(IN|OUT|FWD) + * @param mark optional mark + * @param[out] use_time the (monotonic) time of this SA's last use + * @return SUCCESS if operation completed + */ + status_t (*query_policy) (kernel_interface_t *this, + traffic_selector_t *src_ts, + traffic_selector_t *dst_ts, + policy_dir_t direction, mark_t mark, + time_t *use_time); + + /** + * Remove a policy from the SPD. + * + * @param src source address of SA + * @param dst dest address of SA + * @param src_ts traffic selector to match traffic source + * @param dst_ts traffic selector to match traffic dest + * @param direction direction of traffic, POLICY_(IN|OUT|FWD) + * @param type type of policy, POLICY_(IPSEC|PASS|DROP) + * @param sa details about the SA(s) tied to this policy + * @param mark mark for this policy + * @param priority priority of the policy + * @return SUCCESS if operation completed + */ + status_t (*del_policy) (kernel_interface_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); + + /** + * Flush all policies from the SPD. + * + * @return SUCCESS if operation completed + */ + status_t (*flush_policies) (kernel_interface_t *this); + + /** + * Get our outgoing source address for a destination. + * + * Does a route lookup to get the source address used to reach dest. + * The returned host is allocated and must be destroyed. + * An optional src address can be used to check if a route is available + * for the given source to dest. + * + * @param dest target destination address + * @param src source address to check, or NULL + * @return outgoing source address, NULL if unreachable + */ + host_t* (*get_source_addr)(kernel_interface_t *this, + host_t *dest, host_t *src); + + /** + * Get the next hop for a destination. + * + * Does a route lookup to get the next hop used to reach dest. + * The returned host is allocated and must be destroyed. + * An optional src address can be used to check if a route is available + * for the given source to dest. + * + * @param dest target destination address + * @param prefix prefix length if dest is a subnet, -1 for auto + * @param src source address to check, or NULL + * @return next hop address, NULL if unreachable + */ + host_t* (*get_nexthop)(kernel_interface_t *this, host_t *dest, + int prefix, host_t *src); + + /** + * Get the interface name of a local address. Interfaces that are down or + * ignored by config are not considered. + * + * @param host address to get interface name from + * @param name allocated interface name (optional) + * @return TRUE if interface found and usable + */ + bool (*get_interface)(kernel_interface_t *this, host_t *host, char **name); + + /** + * Creates an enumerator over all local addresses. + * + * This function blocks an internal cached address list until the + * enumerator gets destroyed. + * The hosts are read-only, do not modify of free. + * + * @param which a combination of address types to enumerate + * @return enumerator over host_t's + */ + enumerator_t *(*create_address_enumerator) (kernel_interface_t *this, + kernel_address_type_t which); + + /** + * Add a virtual IP to an interface. + * + * Virtual IPs are attached to an interface. If an IP is added multiple + * times, the IP is refcounted and not removed until del_ip() was called + * as many times as add_ip(). + * + * @param virtual_ip virtual ip address to assign + * @param prefix prefix length to install IP with, -1 for auto + * @param iface interface to install virtual IP on + * @return SUCCESS if operation completed + */ + status_t (*add_ip) (kernel_interface_t *this, host_t *virtual_ip, int prefix, + char *iface); + + /** + * Remove a virtual IP from an interface. + * + * The kernel interface uses refcounting, see add_ip(). + * + * @param virtual_ip virtual ip address to remove + * @param prefix prefix length of the IP to uninstall, -1 for auto + * @param wait TRUE to wait untily IP is gone + * @return SUCCESS if operation completed + */ + status_t (*del_ip) (kernel_interface_t *this, host_t *virtual_ip, + int prefix, bool wait); + + /** + * Add a route. + * + * @param dst_net destination net + * @param prefixlen destination net prefix length + * @param gateway gateway for this route + * @param src_ip source ip of the route + * @param if_name name of the interface the route is bound to + * @return SUCCESS if operation completed + * ALREADY_DONE if the route already exists + */ + status_t (*add_route) (kernel_interface_t *this, chunk_t dst_net, + u_int8_t prefixlen, host_t *gateway, host_t *src_ip, + char *if_name); + + /** + * Delete a route. + * + * @param dst_net destination net + * @param prefixlen destination net prefix length + * @param gateway gateway for this route + * @param src_ip source ip of the route + * @param if_name name of the interface the route is bound to + * @return SUCCESS if operation completed + */ + status_t (*del_route) (kernel_interface_t *this, chunk_t dst_net, + u_int8_t prefixlen, host_t *gateway, host_t *src_ip, + char *if_name); + + /** + * Set up a bypass policy for a given socket. + * + * @param fd socket file descriptor to setup policy for + * @param family protocol family of the socket + * @return TRUE if policy set up successfully + */ + bool (*bypass_socket)(kernel_interface_t *this, int fd, int family); + + /** + * Enable decapsulation of ESP-in-UDP packets for the given port/socket. + * + * @param fd socket file descriptor + * @param family protocol family of the socket + * @param port the UDP port + * @return TRUE if UDP decapsulation was enabled successfully + */ + bool (*enable_udp_decap)(kernel_interface_t *this, int fd, int family, + u_int16_t port); + + + /** + * manager methods + */ + + /** + * Verifies that the given interface is usable and not excluded by + * configuration. + * + * @param iface interface name + * @return TRUE if usable + */ + bool (*is_interface_usable)(kernel_interface_t *this, const char *iface); + + /** + * Check if interfaces are excluded by config. + * + * @return TRUE if no interfaces are exclued by config + */ + bool (*all_interfaces_usable)(kernel_interface_t *this); + + /** + * Tries to find an IP address of a local interface that is included in the + * supplied traffic selector. + * + * @param ts traffic selector + * @param ip returned IP address (has to be destroyed) + * @param vip set to TRUE if returned address is a virtual IP + * @return SUCCESS if address found + */ + status_t (*get_address_by_ts)(kernel_interface_t *this, + traffic_selector_t *ts, host_t **ip, bool *vip); + + /** + * Register an ipsec kernel interface constructor on the manager. + * + * @param create constructor to register + * @return TRUE if the ipsec kernel interface was registered + * successfully, FALSE if an interface was already + * registered or the registration failed + */ + bool (*add_ipsec_interface)(kernel_interface_t *this, + kernel_ipsec_constructor_t create); + + /** + * Unregister an ipsec kernel interface constructor. + * + * @param create constructor to unregister + * @return TRUE if the ipsec kernel interface was unregistered + * successfully, FALSE otherwise + */ + bool (*remove_ipsec_interface)(kernel_interface_t *this, + kernel_ipsec_constructor_t create); + + /** + * Register a network kernel interface constructor on the manager. + * + * @param create constructor to register + * @return TRUE if the kernel net interface was registered + * successfully, FALSE if an interface was already + * registered or the registration failed + */ + bool (*add_net_interface)(kernel_interface_t *this, + kernel_net_constructor_t create); + + /** + * Unregister a network kernel interface constructor. + * + * @param create constructor to unregister + * @return TRUE if the kernel net interface was unregistered + * successfully, FALSE otherwise + */ + bool (*remove_net_interface)(kernel_interface_t *this, + kernel_net_constructor_t create); + + /** + * Add a listener to the kernel interface. + * + * @param listener listener to add + */ + void (*add_listener)(kernel_interface_t *this, + kernel_listener_t *listener); + + /** + * Remove a listener from the kernel interface. + * + * @param listener listener to remove + */ + void (*remove_listener)(kernel_interface_t *this, + kernel_listener_t *listener); + + /** + * Raise an acquire event. + * + * @param reqid reqid of the policy to acquire + * @param src_ts source traffic selector + * @param dst_ts destination traffic selector + */ + void (*acquire)(kernel_interface_t *this, u_int32_t reqid, + traffic_selector_t *src_ts, traffic_selector_t *dst_ts); + + /** + * Raise an expire event. + * + * @param protocol protocol of the expired SA + * @param spi spi of the expired SA + * @param dst destination address of expired SA + * @param hard TRUE if it is a hard expire, FALSE otherwise + */ + void (*expire)(kernel_interface_t *this, u_int8_t protocol, u_int32_t spi, + host_t *dst, bool hard); + + /** + * Raise a mapping event. + * + * @param protocol protocol of affected SA + * @param spi spi of the SA + * @param dst original destination address of SA + * @param remote new remote host + */ + void (*mapping)(kernel_interface_t *this, u_int8_t protocol, u_int32_t spi, + host_t *dst, host_t *remote); + + /** + * Raise a migrate event. + * + * @param reqid reqid of the policy + * @param src_ts source traffic selector + * @param dst_ts destination traffic selector + * @param direction direction of the policy (in|out) + * @param local local host address to be used in the IKE_SA + * @param remote remote host address to be used in the IKE_SA + */ + void (*migrate)(kernel_interface_t *this, u_int32_t reqid, + traffic_selector_t *src_ts, traffic_selector_t *dst_ts, + policy_dir_t direction, host_t *local, host_t *remote); + + /** + * Raise a roam event. + * + * @param address TRUE if address list, FALSE if routing changed + */ + void (*roam)(kernel_interface_t *this, bool address); + + /** + * Raise a tun event. + * + * @param tun TUN device + * @param created TRUE if created, FALSE if going to be destroyed + */ + void (*tun)(kernel_interface_t *this, tun_device_t *tun, bool created); + + /** + * Register a new algorithm with the kernel interface. + * + * @param alg_id the IKE id of the algorithm + * @param type the transform type of the algorithm + * @param kernel_id the kernel id of the algorithm + * @param kernel_name the kernel name of the algorithm + */ + void (*register_algorithm)(kernel_interface_t *this, u_int16_t alg_id, + transform_type_t type, u_int16_t kernel_id, + char *kernel_name); + + /** + * Return the kernel-specific id and/or name for an algorithms depending on + * the arguments specified. + * + * @param alg_id the IKE id of the algorithm + * @param type the transform type of the algorithm + * @param kernel_id the kernel id of the algorithm (optional) + * @param kernel_name the kernel name of the algorithm (optional) + * @return TRUE if algorithm was found + */ + bool (*lookup_algorithm)(kernel_interface_t *this, u_int16_t alg_id, + transform_type_t type, u_int16_t *kernel_id, + char **kernel_name); + + /** + * Destroys a kernel_interface_t object. + */ + void (*destroy) (kernel_interface_t *this); +}; + +/** + * Creates an object of type kernel_interface_t. + */ +kernel_interface_t *kernel_interface_create(void); + +#endif /** KERNEL_INTERFACE_H_ @}*/ diff --git a/src/libcharon/kernel/kernel_ipsec.c b/src/libcharon/kernel/kernel_ipsec.c new file mode 100644 index 000000000..0440f11bb --- /dev/null +++ b/src/libcharon/kernel/kernel_ipsec.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2008 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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_ipsec.h" + +#include <daemon.h> + +/** + * See header + */ +bool kernel_ipsec_register(plugin_t *plugin, plugin_feature_t *feature, + bool reg, void *data) +{ + if (reg) + { + return charon->kernel->add_ipsec_interface(charon->kernel, + (kernel_ipsec_constructor_t)data); + } + else + { + return charon->kernel->remove_ipsec_interface(charon->kernel, + (kernel_ipsec_constructor_t)data); + } +} diff --git a/src/libcharon/kernel/kernel_ipsec.h b/src/libcharon/kernel/kernel_ipsec.h new file mode 100644 index 000000000..31e06308e --- /dev/null +++ b/src/libcharon/kernel/kernel_ipsec.h @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2006-2015 Tobias Brunner + * Copyright (C) 2006 Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * 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_ipsec kernel_ipsec + * @{ @ingroup kernel + */ + +#ifndef KERNEL_IPSEC_H_ +#define KERNEL_IPSEC_H_ + +typedef struct kernel_ipsec_t kernel_ipsec_t; + +#include <networking/host.h> +#include <ipsec/ipsec_types.h> +#include <selectors/traffic_selector.h> +#include <plugins/plugin.h> +#include <kernel/kernel_interface.h> + +/** + * Interface to the ipsec subsystem of the kernel. + * + * The kernel ipsec interface handles the communication with the kernel + * for SA and policy management. It allows setup of these, and provides + * further the handling of kernel events. + * Policy information are cached in the interface. This is necessary to do + * reference counting. The Linux kernel does not allow the same policy + * installed twice, but we need this as CHILD_SA exist multiple times + * when rekeying. Thats why we do reference counting of policies. + */ +struct kernel_ipsec_t { + + /** + * Get the feature set supported by this kernel backend. + * + * @return ORed feature-set of backend + */ + kernel_feature_t (*get_features)(kernel_ipsec_t *this); + + /** + * Get a SPI from the kernel. + * + * @param src source address of SA + * @param dst destination address of SA + * @param protocol protocol for SA (ESP/AH) + * @param spi allocated spi + * @return SUCCESS if operation completed + */ + status_t (*get_spi)(kernel_ipsec_t *this, host_t *src, host_t *dst, + u_int8_t protocol, u_int32_t *spi); + + /** + * Get a Compression Parameter Index (CPI) from the kernel. + * + * @param src source address of SA + * @param dst destination address of SA + * @param cpi allocated cpi + * @return SUCCESS if operation completed + */ + status_t (*get_cpi)(kernel_ipsec_t *this, host_t *src, host_t *dst, + u_int16_t *cpi); + + /** + * Add an SA to the SAD. + * + * This function does install a single SA for a single protocol in one + * direction. + * + * @param src source address for this SA + * @param dst destination address for this SA + * @param spi SPI allocated by us or remote peer + * @param protocol protocol for this SA (ESP/AH) + * @param reqid unique ID for this SA + * @param mark mark for this SA + * @param tfc Traffic Flow Confidentiality padding for this SA + * @param lifetime lifetime_cfg_t for this SA + * @param enc_alg Algorithm to use for encryption (ESP only) + * @param enc_key key to use for encryption + * @param int_alg Algorithm to use for integrity protection + * @param int_key key to use for integrity protection + * @param mode mode of the SA (tunnel, transport) + * @param ipcomp IPComp transform to use + * @param cpi CPI for IPComp + * @param replay_window anti-replay window size + * @param initiator TRUE if initiator of the exchange creating this SA + * @param encap enable UDP encapsulation for NAT traversal + * @param esn TRUE to use Extended Sequence Numbers + * @param inbound TRUE if this is an inbound SA + * @param update TRUE if an SPI has already been allocated for SA + * @param src_ts list of source traffic selectors + * @param dst_ts list of destination traffic selectors + * @return SUCCESS if operation completed + */ + status_t (*add_sa) (kernel_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, bool update, + linked_list_t *src_ts, linked_list_t *dst_ts); + + /** + * Update the hosts on an installed SA. + * + * We cannot directly update the destination address as the kernel + * requires the spi, the protocol AND the destination address (and family) + * to identify SAs. Therefore if the destination address changed we + * create a new SA and delete the old one. + * + * @param spi SPI of the SA + * @param protocol protocol for this SA (ESP/AH) + * @param cpi CPI for IPComp, 0 if no IPComp is used + * @param src current source address + * @param dst current destination address + * @param new_src new source address + * @param new_dst new destination address + * @param encap current use of UDP encapsulation + * @param new_encap new use of UDP encapsulation + * @param mark optional mark for this SA + * @return SUCCESS if operation completed, NOT_SUPPORTED if + * the kernel interface can't update the SA + */ + status_t (*update_sa)(kernel_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); + + /** + * Query the number of bytes processed by an SA from the SAD. + * + * @param src source address for this SA + * @param dst destination address for this SA + * @param spi SPI allocated by us or remote peer + * @param protocol protocol for this SA (ESP/AH) + * @param mark optional mark for this SA + * @param[out] bytes the number of bytes processed by SA + * @param[out] packets number of packets processed by SA + * @param[out] time last (monotonic) time of SA use + * @return SUCCESS if operation completed + */ + status_t (*query_sa) (kernel_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); + + /** + * Delete a previusly installed SA from the SAD. + * + * @param src source address for this SA + * @param dst destination address for this SA + * @param spi SPI allocated by us or remote peer + * @param protocol protocol for this SA (ESP/AH) + * @param cpi CPI for IPComp or 0 + * @param mark optional mark for this SA + * @return SUCCESS if operation completed + */ + status_t (*del_sa) (kernel_ipsec_t *this, host_t *src, host_t *dst, + u_int32_t spi, u_int8_t protocol, u_int16_t cpi, + mark_t mark); + + /** + * Flush all SAs from the SAD. + * + * @return SUCCESS if operation completed + */ + status_t (*flush_sas) (kernel_ipsec_t *this); + + /** + * Add a policy to the SPD. + * + * @param src source address of SA + * @param dst dest address of SA + * @param src_ts traffic selector to match traffic source + * @param dst_ts traffic selector to match traffic dest + * @param direction direction of traffic, POLICY_(IN|OUT|FWD) + * @param type type of policy, POLICY_(IPSEC|PASS|DROP) + * @param sa details about the SA(s) tied to this policy + * @param mark mark for this policy + * @param priority priority of this policy + * @return SUCCESS if operation completed + */ + status_t (*add_policy) (kernel_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); + + /** + * Query the use time of a policy. + * + * The use time of a policy is the time the policy was used for the last + * time. It is not the system time, but a monotonic timestamp as returned + * by time_monotonic. + * + * @param src_ts traffic selector to match traffic source + * @param dst_ts traffic selector to match traffic dest + * @param direction direction of traffic, POLICY_(IN|OUT|FWD) + * @param mark optional mark + * @param[out] use_time the monotonic timestamp of this SA's last use + * @return SUCCESS if operation completed + */ + status_t (*query_policy) (kernel_ipsec_t *this, + traffic_selector_t *src_ts, + traffic_selector_t *dst_ts, + policy_dir_t direction, mark_t mark, + time_t *use_time); + + /** + * Remove a policy from the SPD. + * + * @param src source address of SA + * @param dst dest address of SA + * @param src_ts traffic selector to match traffic source + * @param dst_ts traffic selector to match traffic dest + * @param direction direction of traffic, POLICY_(IN|OUT|FWD) + * @param type type of policy, POLICY_(IPSEC|PASS|DROP) + * @param sa details about the SA(s) tied to this policy + * @param mark mark for this policy + * @param priority priority of the policy + * @return SUCCESS if operation completed + */ + status_t (*del_policy) (kernel_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); + + /** + * Flush all policies from the SPD. + * + * @return SUCCESS if operation completed + */ + status_t (*flush_policies) (kernel_ipsec_t *this); + + /** + * Install a bypass policy for the given socket. + * + * @param fd socket file descriptor to setup policy for + * @param family protocol family of the socket + * @return TRUE of policy set up successfully + */ + bool (*bypass_socket)(kernel_ipsec_t *this, int fd, int family); + + /** + * Enable decapsulation of ESP-in-UDP packets for the given port/socket. + * + * @param fd socket file descriptor + * @param family protocol family of the socket + * @param port the UDP port + * @return TRUE if UDP decapsulation was enabled successfully + */ + bool (*enable_udp_decap)(kernel_ipsec_t *this, int fd, int family, + u_int16_t port); + + /** + * Destroy the implementation. + */ + void (*destroy) (kernel_ipsec_t *this); +}; + +/** + * Helper function to (un-)register IPsec kernel interfaces from plugin features. + * + * This function is a plugin_feature_callback_t and can be used with the + * PLUGIN_CALLBACK macro to register an IPsec kernel interface constructor. + * + * @param plugin plugin registering the kernel interface + * @param feature associated plugin feature + * @param reg TRUE to register, FALSE to unregister + * @param data data passed to callback, an kernel_ipsec_constructor_t + */ +bool kernel_ipsec_register(plugin_t *plugin, plugin_feature_t *feature, + bool reg, void *data); + +#endif /** KERNEL_IPSEC_H_ @}*/ diff --git a/src/libcharon/kernel/kernel_listener.h b/src/libcharon/kernel/kernel_listener.h new file mode 100644 index 000000000..6426fae2a --- /dev/null +++ b/src/libcharon/kernel/kernel_listener.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2010-2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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_listener kernel_listener + * @{ @ingroup kernel + */ + +#ifndef KERNEL_LISTENER_H_ +#define KERNEL_LISTENER_H_ + +typedef struct kernel_listener_t kernel_listener_t; + +#include <networking/host.h> +#include <networking/tun_device.h> +#include <selectors/traffic_selector.h> +#include <kernel/kernel_ipsec.h> + +/** + * Interface for components interested in kernel events. + * + * All hooks are optional. + */ +struct kernel_listener_t { + + /** + * Hook called if an acquire event for a policy is received. + * + * @param reqid reqid of the policy to acquire + * @param src_ts source traffic selector + * @param dst_ts destination traffic selector + * @return TRUE to remain registered, FALSE to unregister + */ + bool (*acquire)(kernel_listener_t *this, u_int32_t reqid, + traffic_selector_t *src_ts, traffic_selector_t *dst_ts); + + /** + * Hook called if an exire event for an IPsec SA is received. + * + * @param protocol protocol of the expired SA + * @param spi spi of the expired SA + * @param dst destination address of expired SA + * @param hard TRUE if it is a hard expire, FALSE otherwise + * @return TRUE to remain registered, FALSE to unregister + */ + bool (*expire)(kernel_listener_t *this, u_int8_t protocol, u_int32_t spi, + host_t *dst, bool hard); + + /** + * Hook called if the NAT mappings of an IPsec SA changed. + * + * @param protocol IPsec protocol of affected SA + * @param spi spi of the SA + * @param dst old destinatino address of SA + * @param remote new remote host + * @return TRUE to remain registered, FALSE to unregister + */ + bool (*mapping)(kernel_listener_t *this, u_int8_t protocol, u_int32_t spi, + host_t *dst, host_t *remote); + + /** + * Hook called if a migrate event for a policy is received. + * + * @param reqid reqid of the policy + * @param src_ts source traffic selector + * @param dst_ts destination traffic selector + * @param direction direction of the policy (in|out) + * @param local local host address to be used in the IKE_SA + * @param remote remote host address to be used in the IKE_SA + * @return TRUE to remain registered, FALSE to unregister + */ + bool (*migrate)(kernel_listener_t *this, u_int32_t reqid, + traffic_selector_t *src_ts, traffic_selector_t *dst_ts, + policy_dir_t direction, host_t *local, host_t *remote); + + /** + * Hook called if changes in the networking layer occurred (interfaces + * up/down, routes added/deleted etc.). + * + * @param address TRUE if address list, FALSE if routing changed + * @return TRUE to remain registered, FALSE to unregister + */ + bool (*roam)(kernel_listener_t *this, bool address); + + /** + * Hook called after a TUN device was created for a virtual IP address, or + * before such a device gets destroyed. + * + * @param tun TUN device + * @param created TRUE if created, FALSE if going to be destroyed + */ + bool (*tun)(kernel_listener_t *this, tun_device_t *tun, bool created); +}; + +#endif /** KERNEL_LISTENER_H_ @}*/ diff --git a/src/libcharon/kernel/kernel_net.c b/src/libcharon/kernel/kernel_net.c new file mode 100644 index 000000000..f169cad14 --- /dev/null +++ b/src/libcharon/kernel/kernel_net.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2011 Martin Willi + * Copyright (C) 2011 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_net.h" + +#include <daemon.h> + +/** + * See header + */ +bool kernel_net_register(plugin_t *plugin, plugin_feature_t *feature, + bool reg, void *data) +{ + if (reg) + { + return charon->kernel->add_net_interface(charon->kernel, + (kernel_net_constructor_t)data); + } + else + { + return charon->kernel->remove_net_interface(charon->kernel, + (kernel_net_constructor_t)data); + } +} diff --git a/src/libcharon/kernel/kernel_net.h b/src/libcharon/kernel/kernel_net.h new file mode 100644 index 000000000..7fc644a7e --- /dev/null +++ b/src/libcharon/kernel/kernel_net.h @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2008-2012 Tobias Brunner + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * 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_net kernel_net + * @{ @ingroup kernel + */ + +#ifndef KERNEL_NET_H_ +#define KERNEL_NET_H_ + +typedef struct kernel_net_t kernel_net_t; +typedef enum kernel_address_type_t kernel_address_type_t; + +#include <collections/enumerator.h> +#include <networking/host.h> +#include <plugins/plugin.h> +#include <kernel/kernel_interface.h> + +/** + * Type of addresses (e.g. when enumerating them) + */ +enum kernel_address_type_t { + /** normal addresses (on regular, up, non-ignored) interfaces */ + ADDR_TYPE_REGULAR = (1 << 0), + /** addresses on down interfaces */ + ADDR_TYPE_DOWN = (1 << 1), + /** addresses on ignored interfaces */ + ADDR_TYPE_IGNORED = (1 << 2), + /** addresses on loopback interfaces */ + ADDR_TYPE_LOOPBACK = (1 << 3), + /** virtual IP addresses */ + ADDR_TYPE_VIRTUAL = (1 << 4), + /** to enumerate all available addresses */ + ADDR_TYPE_ALL = (1 << 5) - 1, +}; + +/** + * Interface to the network subsystem of the kernel. + * + * The kernel network interface handles the communication with the kernel + * for interface and IP address management. + */ +struct kernel_net_t { + + /** + * Get the feature set supported by this kernel backend. + * + * @return ORed feature-set of backend + */ + kernel_feature_t (*get_features)(kernel_net_t *this); + + /** + * Get our outgoing source address for a destination. + * + * Does a route lookup to get the source address used to reach dest. + * The returned host is allocated and must be destroyed. + * An optional src address can be used to check if a route is available + * for the given source to dest. + * + * @param dest target destination address + * @param src source address to check, or NULL + * @return outgoing source address, NULL if unreachable + */ + host_t* (*get_source_addr)(kernel_net_t *this, host_t *dest, host_t *src); + + /** + * Get the next hop for a destination. + * + * Does a route lookup to get the next hop used to reach dest. + * The returned host is allocated and must be destroyed. + * An optional src address can be used to check if a route is available + * for the given source to dest. + * + * @param dest target destination address + * @param prefix prefix length if dest is a subnet, -1 for auto + * @param src source address to check, or NULL + * @return next hop address, NULL if unreachable + */ + host_t* (*get_nexthop)(kernel_net_t *this, host_t *dest, int prefix, + host_t *src); + + /** + * Get the interface name of a local address. Interfaces that are down or + * ignored by config are not considered. + * + * @param host address to get interface name from + * @param name allocated interface name (optional) + * @return TRUE if interface found and usable + */ + bool (*get_interface) (kernel_net_t *this, host_t *host, char **name); + + /** + * Creates an enumerator over all local addresses. + * + * This function blocks an internal cached address list until the + * enumerator gets destroyed. + * The hosts are read-only, do not modify of free. + * + * @param which a combination of address types to enumerate + * @return enumerator over host_t's + */ + enumerator_t *(*create_address_enumerator) (kernel_net_t *this, + kernel_address_type_t which); + + /** + * Add a virtual IP to an interface. + * + * Virtual IPs are attached to an interface. If an IP is added multiple + * times, the IP is refcounted and not removed until del_ip() was called + * as many times as add_ip(). + * + * @param virtual_ip virtual ip address to assign + * @param prefix prefix length to install with IP address, -1 for auto + * @param iface interface to install virtual IP on + * @return SUCCESS if operation completed + */ + status_t (*add_ip) (kernel_net_t *this, host_t *virtual_ip, int prefix, + char *iface); + + /** + * Remove a virtual IP from an interface. + * + * The kernel interface uses refcounting, see add_ip(). + * + * @param virtual_ip virtual ip address to remove + * @param prefix prefix length of the IP to uninstall, -1 for auto + * @param wait TRUE to wait until IP is gone + * @return SUCCESS if operation completed + */ + status_t (*del_ip) (kernel_net_t *this, host_t *virtual_ip, int prefix, + bool wait); + + /** + * Add a route. + * + * @param dst_net destination net + * @param prefixlen destination net prefix length + * @param gateway gateway for this route + * @param src_ip source ip of the route + * @param if_name name of the interface the route is bound to + * @return SUCCESS if operation completed + * ALREADY_DONE if the route already exists + */ + status_t (*add_route) (kernel_net_t *this, chunk_t dst_net, + u_int8_t prefixlen, host_t *gateway, host_t *src_ip, + char *if_name); + + /** + * Delete a route. + * + * @param dst_net destination net + * @param prefixlen destination net prefix length + * @param gateway gateway for this route + * @param src_ip source ip of the route + * @param if_name name of the interface the route is bound to + * @return SUCCESS if operation completed + */ + status_t (*del_route) (kernel_net_t *this, chunk_t dst_net, + u_int8_t prefixlen, host_t *gateway, host_t *src_ip, + char *if_name); + + /** + * Destroy the implementation. + */ + void (*destroy) (kernel_net_t *this); +}; + +/** + * Helper function to (un-)register net kernel interfaces from plugin features. + * + * This function is a plugin_feature_callback_t and can be used with the + * PLUGIN_CALLBACK macro to register an net kernel interface constructor. + * + * @param plugin plugin registering the kernel interface + * @param feature associated plugin feature + * @param reg TRUE to register, FALSE to unregister + * @param data data passed to callback, an kernel_net_constructor_t + */ +bool kernel_net_register(plugin_t *plugin, plugin_feature_t *feature, + bool reg, void *data); + +#endif /** KERNEL_NET_H_ @}*/ diff --git a/src/libcharon/network/receiver.c b/src/libcharon/network/receiver.c index a2f2016ff..ee357ca4d 100644 --- a/src/libcharon/network/receiver.c +++ b/src/libcharon/network/receiver.c @@ -20,7 +20,6 @@ #include "receiver.h" -#include <hydra.h> #include <daemon.h> #include <network/socket.h> #include <processing/jobs/job.h> @@ -451,9 +450,8 @@ static job_requeue_t receive_packets(private_receiver_t *this) dst = packet->get_destination(packet); src = packet->get_source(packet); - if (!hydra->kernel_interface->all_interfaces_usable(hydra->kernel_interface) - && !hydra->kernel_interface->get_interface(hydra->kernel_interface, - dst, NULL)) + if (!charon->kernel->all_interfaces_usable(charon->kernel) + && !charon->kernel->get_interface(charon->kernel, dst, NULL)) { DBG3(DBG_NET, "received packet from %#H to %#H on ignored interface", src, dst); diff --git a/src/libcharon/plugins/addrblock/Makefile.am b/src/libcharon/plugins/addrblock/Makefile.am index 33ee60d86..ddb2706c8 100644 --- a/src/libcharon/plugins/addrblock/Makefile.am +++ b/src/libcharon/plugins/addrblock/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/addrblock/Makefile.in b/src/libcharon/plugins/addrblock/Makefile.in index 0554465b9..b4ae6fa3e 100644 --- a/src/libcharon/plugins/addrblock/Makefile.in +++ b/src/libcharon/plugins/addrblock/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 = \ diff --git a/src/libcharon/plugins/android_dns/Makefile.am b/src/libcharon/plugins/android_dns/Makefile.am index 1a0d6e6f2..e606a832c 100644 --- a/src/libcharon/plugins/android_dns/Makefile.am +++ b/src/libcharon/plugins/android_dns/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/android_dns/Makefile.in b/src/libcharon/plugins/android_dns/Makefile.in index 58cf97b6e..d90149827 100644 --- a/src/libcharon/plugins/android_dns/Makefile.in +++ b/src/libcharon/plugins/android_dns/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 = \ diff --git a/src/libcharon/plugins/android_log/Makefile.am b/src/libcharon/plugins/android_log/Makefile.am index 79c61b51e..9f82f6e60 100644 --- a/src/libcharon/plugins/android_log/Makefile.am +++ b/src/libcharon/plugins/android_log/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/android_log/Makefile.in b/src/libcharon/plugins/android_log/Makefile.in index 8ce92e577..64fecd9e3 100644 --- a/src/libcharon/plugins/android_log/Makefile.in +++ b/src/libcharon/plugins/android_log/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 = \ diff --git a/src/libcharon/plugins/attr/Makefile.am b/src/libcharon/plugins/attr/Makefile.am index 6bc7e77d8..ecbb76d1a 100644 --- a/src/libcharon/plugins/attr/Makefile.am +++ b/src/libcharon/plugins/attr/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/attr/Makefile.in b/src/libcharon/plugins/attr/Makefile.in index 486b3c0b0..acb7d07c0 100644 --- a/src/libcharon/plugins/attr/Makefile.in +++ b/src/libcharon/plugins/attr/Makefile.in @@ -413,6 +413,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -428,7 +430,6 @@ 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 = \ diff --git a/src/libcharon/plugins/attr/attr_provider.c b/src/libcharon/plugins/attr/attr_provider.c index cac0ae4bf..1de571c3f 100644 --- a/src/libcharon/plugins/attr/attr_provider.c +++ b/src/libcharon/plugins/attr/attr_provider.c @@ -54,6 +54,8 @@ struct attribute_entry_t { configuration_attribute_type_t type; /** attribute value */ chunk_t value; + /** associated IKE version */ + ike_version_t ike; }; /** @@ -66,26 +68,51 @@ static void attribute_destroy(attribute_entry_t *this) } /** + * Data for attribute enumerator + */ +typedef struct { + rwlock_t *lock; + ike_version_t ike; +} enumerator_data_t; + +/** * convert enumerator value from attribute_entry */ -static bool attr_enum_filter(void *null, attribute_entry_t **in, +static bool attr_enum_filter(enumerator_data_t *data, attribute_entry_t **in, configuration_attribute_type_t *type, void* none, chunk_t *value) { - *type = (*in)->type; - *value = (*in)->value; - return TRUE; + if ((*in)->ike == IKE_ANY || (*in)->ike == data->ike) + { + *type = (*in)->type; + *value = (*in)->value; + return TRUE; + } + return FALSE; +} + +CALLBACK(attr_enum_destroy, void, + enumerator_data_t *data) +{ + data->lock->unlock(data->lock); + free(data); } METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*, private_attr_provider_t *this, linked_list_t *pools, ike_sa_t *ike_sa, linked_list_t *vips) { + enumerator_data_t *data; + if (vips->get_count(vips)) { + INIT(data, + .lock = this->lock, + .ike = ike_sa->get_version(ike_sa), + ); this->lock->read_lock(this->lock); return enumerator_create_filter( this->attributes->create_enumerator(this->attributes), - (void*)attr_enum_filter, this->lock, (void*)this->lock->unlock); + (void*)attr_enum_filter, data, attr_enum_destroy); } return enumerator_create_empty(); } @@ -116,8 +143,6 @@ static void add_legacy_entry(private_attr_provider_t *this, char *key, int nr, host = host_create_from_string(str, 0); if (host) { - entry = malloc_thing(attribute_entry_t); - if (host->get_family(host) == AF_INET6) { switch (type) @@ -132,8 +157,11 @@ static void add_legacy_entry(private_attr_provider_t *this, char *key, int nr, break; } } - entry->type = type; - entry->value = chunk_clone(host->get_address(host)); + INIT(entry, + .type = type, + .value = chunk_clone(host->get_address(host)), + .ike = IKE_ANY, + ); host->destroy(host); DBG2(DBG_CFG, "loaded legacy entry attribute %N: %#B", configuration_attribute_type_names, entry->type, &entry->value); @@ -149,18 +177,20 @@ typedef struct { char *name; configuration_attribute_type_t v4; configuration_attribute_type_t v6; + ike_version_t ike; } attribute_type_key_t; static attribute_type_key_t keys[] = { - {"address", INTERNAL_IP4_ADDRESS, INTERNAL_IP6_ADDRESS}, - {"dns", INTERNAL_IP4_DNS, INTERNAL_IP6_DNS}, - {"nbns", INTERNAL_IP4_NBNS, INTERNAL_IP6_NBNS}, - {"dhcp", INTERNAL_IP4_DHCP, INTERNAL_IP6_DHCP}, - {"netmask", INTERNAL_IP4_NETMASK, INTERNAL_IP6_NETMASK}, - {"server", INTERNAL_IP4_SERVER, INTERNAL_IP6_SERVER}, - {"subnet", INTERNAL_IP4_SUBNET, INTERNAL_IP6_SUBNET}, - {"split-include", UNITY_SPLIT_INCLUDE, UNITY_SPLIT_INCLUDE}, - {"split-exclude", UNITY_LOCAL_LAN, UNITY_LOCAL_LAN}, + {"address", INTERNAL_IP4_ADDRESS, INTERNAL_IP6_ADDRESS, IKE_ANY}, + {"dns", INTERNAL_IP4_DNS, INTERNAL_IP6_DNS, IKE_ANY}, + {"nbns", INTERNAL_IP4_NBNS, INTERNAL_IP6_NBNS, IKE_ANY}, + {"dhcp", INTERNAL_IP4_DHCP, INTERNAL_IP6_DHCP, IKE_ANY}, + {"netmask", INTERNAL_IP4_NETMASK, INTERNAL_IP6_NETMASK, IKE_ANY}, + {"server", INTERNAL_IP4_SERVER, INTERNAL_IP6_SERVER, IKE_ANY}, + {"subnet", INTERNAL_IP4_SUBNET, INTERNAL_IP6_SUBNET, IKE_ANY}, + {"p-cscf", P_CSCF_IP4_ADDRESS, P_CSCF_IP6_ADDRESS, IKEV2}, + {"split-include", UNITY_SPLIT_INCLUDE, UNITY_SPLIT_INCLUDE, IKEV1}, + {"split-exclude", UNITY_LOCAL_LAN, UNITY_LOCAL_LAN, IKEV1}, }; /** @@ -275,6 +305,7 @@ static void load_entries(private_attr_provider_t *this) INIT(entry, .type = type, .value = data, + .ike = mapped ? mapped->ike : IKE_ANY, ); DBG2(DBG_CFG, "loaded attribute %N: %#B", configuration_attribute_type_names, entry->type, &entry->value); diff --git a/src/libcharon/plugins/attr_sql/Makefile.am b/src/libcharon/plugins/attr_sql/Makefile.am index 366c902f7..e65ef36a1 100644 --- a/src/libcharon/plugins/attr_sql/Makefile.am +++ b/src/libcharon/plugins/attr_sql/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/attr_sql/Makefile.in b/src/libcharon/plugins/attr_sql/Makefile.in index 8f1b3c0ff..8ee9f3f92 100644 --- a/src/libcharon/plugins/attr_sql/Makefile.in +++ b/src/libcharon/plugins/attr_sql/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 = \ diff --git a/src/libcharon/plugins/certexpire/Makefile.am b/src/libcharon/plugins/certexpire/Makefile.am index b8c241dfb..d2d38efea 100644 --- a/src/libcharon/plugins/certexpire/Makefile.am +++ b/src/libcharon/plugins/certexpire/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -DIPSEC_PIDDIR=\"${piddir}\" diff --git a/src/libcharon/plugins/certexpire/Makefile.in b/src/libcharon/plugins/certexpire/Makefile.in index f946d73c1..be19d615e 100644 --- a/src/libcharon/plugins/certexpire/Makefile.in +++ b/src/libcharon/plugins/certexpire/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 \ -DIPSEC_PIDDIR=\"${piddir}\" diff --git a/src/libcharon/plugins/connmark/Makefile.am b/src/libcharon/plugins/connmark/Makefile.am index cc4d0ec8d..561efa0af 100644 --- a/src/libcharon/plugins/connmark/Makefile.am +++ b/src/libcharon/plugins/connmark/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/connmark/Makefile.in b/src/libcharon/plugins/connmark/Makefile.in index 65f53fde9..eaf4f1ec9 100644 --- a/src/libcharon/plugins/connmark/Makefile.in +++ b/src/libcharon/plugins/connmark/Makefile.in @@ -417,6 +417,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -432,7 +434,6 @@ 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 = \ diff --git a/src/libcharon/plugins/connmark/connmark_listener.c b/src/libcharon/plugins/connmark/connmark_listener.c index 23df690e8..607316f7b 100644 --- a/src/libcharon/plugins/connmark/connmark_listener.c +++ b/src/libcharon/plugins/connmark/connmark_listener.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2015 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * * Copyright (C) 2014 Martin Willi * Copyright (C) 2014 revosec AG * @@ -25,6 +28,14 @@ #include <linux/netfilter/xt_policy.h> #include <linux/netfilter/xt_CONNMARK.h> +/** + * Add a struct at the current position in the buffer + */ +#define ADD_STRUCT(pos, st, ...) ({\ + typeof(pos) _cur = pos; pos += XT_ALIGN(sizeof(st));\ + *(st*)_cur = (st){ __VA_ARGS__ };\ + (st*)_cur;\ +}) typedef struct private_connmark_listener_t private_connmark_listener_t; @@ -90,7 +101,10 @@ static bool manage_rule(struct iptc_handle *ipth, const char *chain, } else { - if (!iptc_delete_entry(chain, e, "", ipth)) + u_char matchmask[e->next_offset]; + + memset(matchmask, 255, sizeof(matchmask)); + if (!iptc_delete_entry(chain, e, matchmask, ipth)) { DBG1(DBG_CFG, "deleting %s rule failed: %s", chain, iptc_strerror(errno)); @@ -108,54 +122,54 @@ static bool manage_pre_esp_in_udp(private_connmark_listener_t *this, u_int mark, u_int32_t spi, host_t *dst, host_t *src) { - struct { - struct ipt_entry e; - struct ipt_entry_match m; - struct xt_udp udp; - struct ipt_entry_target t; - struct xt_mark_tginfo2 tm; - } ipt = { - .e = { - .target_offset = XT_ALIGN(sizeof(ipt.e) + sizeof(ipt.m) + - sizeof(ipt.udp)), - .next_offset = sizeof(ipt), - .ip = { - .proto = IPPROTO_UDP, - }, + u_int16_t match_size = XT_ALIGN(sizeof(struct ipt_entry_match)) + + XT_ALIGN(sizeof(struct xt_udp)); + u_int16_t target_offset = XT_ALIGN(sizeof(struct ipt_entry)) + match_size; + u_int16_t target_size = XT_ALIGN(sizeof(struct ipt_entry_target)) + + XT_ALIGN(sizeof(struct xt_mark_tginfo2)); + u_int16_t entry_size = target_offset + target_size; + u_char ipt[entry_size], *pos = ipt; + struct ipt_entry *e; + + memset(ipt, 0, sizeof(ipt)); + e = ADD_STRUCT(pos, struct ipt_entry, + .target_offset = target_offset, + .next_offset = entry_size, + .ip = { + .proto = IPPROTO_UDP, }, - .m = { - .u = { - .user = { - .match_size = XT_ALIGN(sizeof(ipt.m) + sizeof(ipt.udp)), - .name = "udp", - }, + ); + if (!host2in(dst, &e->ip.dst, &e->ip.dmsk) || + !host2in(src, &e->ip.src, &e->ip.smsk)) + { + return FALSE; + } + ADD_STRUCT(pos, struct ipt_entry_match, + .u = { + .user = { + .match_size = match_size, + .name = "udp", }, }, - .udp = { - .spts = { src->get_port(src), src->get_port(src) }, - .dpts = { dst->get_port(dst), dst->get_port(dst) }, - }, - .t = { - .u = { - .user = { - .target_size = XT_ALIGN(sizeof(ipt.t) + sizeof(ipt.tm)), - .name = "MARK", - .revision = 2, - }, + ); + ADD_STRUCT(pos, struct xt_udp, + .spts = { src->get_port(src), src->get_port(src) }, + .dpts = { dst->get_port(dst), dst->get_port(dst) }, + ); + ADD_STRUCT(pos, struct ipt_entry_target, + .u = { + .user = { + .target_size = target_size, + .name = "MARK", + .revision = 2, }, }, - .tm = { - .mark = mark, - .mask = ~0, - }, - }; - - if (!host2in(dst, &ipt.e.ip.dst, &ipt.e.ip.dmsk) || - !host2in(src, &ipt.e.ip.src, &ipt.e.ip.smsk)) - { - return FALSE; - } - return manage_rule(ipth, "PREROUTING", add, &ipt.e); + ); + ADD_STRUCT(pos, struct xt_mark_tginfo2, + .mark = mark, + .mask = ~0, + ); + return manage_rule(ipth, "PREROUTING", add, e); } /** @@ -166,53 +180,53 @@ static bool manage_pre_esp(private_connmark_listener_t *this, u_int mark, u_int32_t spi, host_t *dst, host_t *src) { - struct { - struct ipt_entry e; - struct ipt_entry_match m; - struct xt_esp esp; - struct ipt_entry_target t; - struct xt_mark_tginfo2 tm; - } ipt = { - .e = { - .target_offset = XT_ALIGN(sizeof(ipt.e) + sizeof(ipt.m) + - sizeof(ipt.esp)), - .next_offset = sizeof(ipt), - .ip = { - .proto = IPPROTO_ESP, - }, + u_int16_t match_size = XT_ALIGN(sizeof(struct ipt_entry_match)) + + XT_ALIGN(sizeof(struct xt_esp)); + u_int16_t target_offset = XT_ALIGN(sizeof(struct ipt_entry)) + match_size; + u_int16_t target_size = XT_ALIGN(sizeof(struct ipt_entry_target)) + + XT_ALIGN(sizeof(struct xt_mark_tginfo2)); + u_int16_t entry_size = target_offset + target_size; + u_char ipt[entry_size], *pos = ipt; + struct ipt_entry *e; + + memset(ipt, 0, sizeof(ipt)); + e = ADD_STRUCT(pos, struct ipt_entry, + .target_offset = target_offset, + .next_offset = entry_size, + .ip = { + .proto = IPPROTO_ESP, }, - .m = { - .u = { - .user = { - .match_size = XT_ALIGN(sizeof(ipt.m) + sizeof(ipt.esp)), - .name = "esp", - }, + ); + if (!host2in(dst, &e->ip.dst, &e->ip.dmsk) || + !host2in(src, &e->ip.src, &e->ip.smsk)) + { + return FALSE; + } + ADD_STRUCT(pos, struct ipt_entry_match, + .u = { + .user = { + .match_size = match_size, + .name = "esp", }, }, - .esp = { - .spis = { htonl(spi), htonl(spi) }, - }, - .t = { - .u = { - .user = { - .target_size = XT_ALIGN(sizeof(ipt.t) + sizeof(ipt.tm)), - .name = "MARK", - .revision = 2, - }, + ); + ADD_STRUCT(pos, struct xt_esp, + .spis = { htonl(spi), htonl(spi) }, + ); + ADD_STRUCT(pos, struct ipt_entry_target, + .u = { + .user = { + .target_size = target_size, + .name = "MARK", + .revision = 2, }, }, - .tm = { - .mark = mark, - .mask = ~0, - }, - }; - - if (!host2in(dst, &ipt.e.ip.dst, &ipt.e.ip.dmsk) || - !host2in(src, &ipt.e.ip.src, &ipt.e.ip.smsk)) - { - return FALSE; - } - return manage_rule(ipth, "PREROUTING", add, &ipt.e); + ); + ADD_STRUCT(pos, struct xt_mark_tginfo2, + .mark = mark, + .mask = ~0, + ); + return manage_rule(ipth, "PREROUTING", add, e); } /** @@ -238,99 +252,115 @@ static bool manage_in(private_connmark_listener_t *this, u_int mark, u_int32_t spi, traffic_selector_t *dst, traffic_selector_t *src) { - struct { - struct ipt_entry e; - struct ipt_entry_match m; - struct xt_policy_info p; - struct ipt_entry_target t; - struct xt_connmark_tginfo1 cm; - } ipt = { - .e = { - .target_offset = XT_ALIGN(sizeof(ipt.e) + sizeof(ipt.m) + - sizeof(ipt.p)), - .next_offset = sizeof(ipt), - }, - .m = { - .u = { - .user = { - .match_size = XT_ALIGN(sizeof(ipt.m) + sizeof(ipt.p)), - .name = "policy", - }, + u_int16_t match_size = XT_ALIGN(sizeof(struct ipt_entry_match)) + + XT_ALIGN(sizeof(struct xt_policy_info)); + u_int16_t target_offset = XT_ALIGN(sizeof(struct ipt_entry)) + match_size; + u_int16_t target_size = XT_ALIGN(sizeof(struct ipt_entry_target)) + + XT_ALIGN(sizeof(struct xt_connmark_tginfo1)); + u_int16_t entry_size = target_offset + target_size; + u_char ipt[entry_size], *pos = ipt; + struct ipt_entry *e; + + memset(ipt, 0, sizeof(ipt)); + e = ADD_STRUCT(pos, struct ipt_entry, + .target_offset = target_offset, + .next_offset = entry_size, + ); + if (!ts2in(dst, &e->ip.dst, &e->ip.dmsk) || + !ts2in(src, &e->ip.src, &e->ip.smsk)) + { + return FALSE; + } + ADD_STRUCT(pos, struct ipt_entry_match, + .u = { + .user = { + .match_size = match_size, + .name = "policy", }, }, - .p = { - .pol = { - { - .spi = spi, - .match.spi = 1, - }, + ); + ADD_STRUCT(pos, struct xt_policy_info, + .pol = { + { + .spi = spi, + .match.spi = 1, }, - .len = 1, - .flags = XT_POLICY_MATCH_IN, }, - .t = { - .u = { - .user = { - .target_size = XT_ALIGN(sizeof(ipt.t) + sizeof(ipt.cm)), - .name = "CONNMARK", - .revision = 1, - }, + .len = 1, + .flags = XT_POLICY_MATCH_IN, + ); + ADD_STRUCT(pos, struct ipt_entry_target, + .u = { + .user = { + .target_size = target_size, + .name = "CONNMARK", + .revision = 1, }, }, - .cm = { - .ctmark = mark, - .ctmask = ~0, - .nfmask = ~0, - .mode = XT_CONNMARK_SET, - }, - }; - - if (!ts2in(dst, &ipt.e.ip.dst, &ipt.e.ip.dmsk) || - !ts2in(src, &ipt.e.ip.src, &ipt.e.ip.smsk)) - { - return FALSE; - } - return manage_rule(ipth, "INPUT", add, &ipt.e); + ); + ADD_STRUCT(pos, struct xt_connmark_tginfo1, + .ctmark = mark, + .ctmask = ~0, + .nfmask = ~0, + .mode = XT_CONNMARK_SET, + ); + return manage_rule(ipth, "INPUT", add, e); } /** - * Add outbund rule restoring CONNMARK on matching traffic + * Add outbund rule restoring CONNMARK on matching traffic unless the packet + * already has a mark set */ static bool manage_out(private_connmark_listener_t *this, struct iptc_handle *ipth, bool add, traffic_selector_t *dst, traffic_selector_t *src) { - struct { - struct ipt_entry e; - struct ipt_entry_target t; - struct xt_connmark_tginfo1 cm; - } ipt = { - .e = { - .target_offset = XT_ALIGN(sizeof(ipt.e)), - .next_offset = sizeof(ipt), - }, - .t = { - .u = { - .user = { - .target_size = XT_ALIGN(sizeof(ipt.t) + sizeof(ipt.cm)), - .name = "CONNMARK", - .revision = 1, - }, - }, - }, - .cm = { - .ctmask = ~0, - .nfmask = ~0, - .mode = XT_CONNMARK_RESTORE, - }, - }; - - if (!ts2in(dst, &ipt.e.ip.dst, &ipt.e.ip.dmsk) || - !ts2in(src, &ipt.e.ip.src, &ipt.e.ip.smsk)) + u_int16_t match_size = XT_ALIGN(sizeof(struct ipt_entry_match)) + + XT_ALIGN(sizeof(struct xt_mark_mtinfo1)); + u_int16_t target_offset = XT_ALIGN(sizeof(struct ipt_entry)) + match_size; + u_int16_t target_size = XT_ALIGN(sizeof(struct ipt_entry_target)) + + XT_ALIGN(sizeof(struct xt_connmark_tginfo1)); + u_int16_t entry_size = target_offset + target_size; + u_char ipt[entry_size], *pos = ipt; + struct ipt_entry *e; + + memset(ipt, 0, sizeof(ipt)); + e = ADD_STRUCT(pos, struct ipt_entry, + .target_offset = target_offset, + .next_offset = entry_size, + ); + if (!ts2in(dst, &e->ip.dst, &e->ip.dmsk) || + !ts2in(src, &e->ip.src, &e->ip.smsk)) { return FALSE; } - return manage_rule(ipth, "OUTPUT", add, &ipt.e); + ADD_STRUCT(pos, struct ipt_entry_match, + .u = { + .user = { + .match_size = match_size, + .name = "mark", + .revision = 1, + }, + }, + ); + ADD_STRUCT(pos, struct xt_mark_mtinfo1, + .mask = ~0, + ); + ADD_STRUCT(pos, struct ipt_entry_target, + .u = { + .user = { + .target_size = target_size, + .name = "CONNMARK", + .revision = 1, + }, + }, + ); + ADD_STRUCT(pos, struct xt_connmark_tginfo1, + .ctmask = ~0, + .nfmask = ~0, + .mode = XT_CONNMARK_RESTORE, + ); + return manage_rule(ipth, "OUTPUT", add, e); } /** diff --git a/src/libcharon/plugins/coupling/Makefile.am b/src/libcharon/plugins/coupling/Makefile.am index badc7b7b2..62695aabe 100644 --- a/src/libcharon/plugins/coupling/Makefile.am +++ b/src/libcharon/plugins/coupling/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/coupling/Makefile.in b/src/libcharon/plugins/coupling/Makefile.in index dff80c37f..44598c3ea 100644 --- a/src/libcharon/plugins/coupling/Makefile.in +++ b/src/libcharon/plugins/coupling/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 = \ diff --git a/src/libcharon/plugins/dhcp/Makefile.am b/src/libcharon/plugins/dhcp/Makefile.am index 3c09db016..9ae68be35 100644 --- a/src/libcharon/plugins/dhcp/Makefile.am +++ b/src/libcharon/plugins/dhcp/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/dhcp/Makefile.in b/src/libcharon/plugins/dhcp/Makefile.in index 1e84f04e2..3d39fda29 100644 --- a/src/libcharon/plugins/dhcp/Makefile.in +++ b/src/libcharon/plugins/dhcp/Makefile.in @@ -414,6 +414,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -429,7 +431,6 @@ 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 = \ diff --git a/src/libcharon/plugins/dhcp/dhcp_socket.c b/src/libcharon/plugins/dhcp/dhcp_socket.c index b8c1b4059..0fd1d33fd 100644 --- a/src/libcharon/plugins/dhcp/dhcp_socket.c +++ b/src/libcharon/plugins/dhcp/dhcp_socket.c @@ -31,7 +31,6 @@ #include <threading/condvar.h> #include <threading/thread.h> -#include <hydra.h> #include <daemon.h> #include <processing/jobs/callback_job.h> @@ -209,8 +208,7 @@ static int prepare_dhcp(private_dhcp_socket_t *this, else { /* act as relay agent */ - src = hydra->kernel_interface->get_source_addr(hydra->kernel_interface, - this->dst, NULL); + src = charon->kernel->get_source_addr(charon->kernel, this->dst, NULL); if (src) { memcpy(&dhcp->gateway_address, src->get_address(src).ptr, diff --git a/src/libcharon/plugins/dnscert/Makefile.am b/src/libcharon/plugins/dnscert/Makefile.am index 145562522..8181bfc9e 100644 --- a/src/libcharon/plugins/dnscert/Makefile.am +++ b/src/libcharon/plugins/dnscert/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/dnscert/Makefile.in b/src/libcharon/plugins/dnscert/Makefile.in index ed873b316..04fc31a3a 100644 --- a/src/libcharon/plugins/dnscert/Makefile.in +++ b/src/libcharon/plugins/dnscert/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 = \ diff --git a/src/libcharon/plugins/duplicheck/Makefile.am b/src/libcharon/plugins/duplicheck/Makefile.am index 338a114fe..32b850ccb 100644 --- a/src/libcharon/plugins/duplicheck/Makefile.am +++ b/src/libcharon/plugins/duplicheck/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -DIPSEC_PIDDIR=\"${piddir}\" diff --git a/src/libcharon/plugins/duplicheck/Makefile.in b/src/libcharon/plugins/duplicheck/Makefile.in index 41862cb2a..da4534c21 100644 --- a/src/libcharon/plugins/duplicheck/Makefile.in +++ b/src/libcharon/plugins/duplicheck/Makefile.in @@ -423,6 +423,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -438,7 +440,6 @@ 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 \ -DIPSEC_PIDDIR=\"${piddir}\" diff --git a/src/libcharon/plugins/duplicheck/duplicheck.c b/src/libcharon/plugins/duplicheck/duplicheck.c index 508e8e386..7c4cd5ce1 100644 --- a/src/libcharon/plugins/duplicheck/duplicheck.c +++ b/src/libcharon/plugins/duplicheck/duplicheck.c @@ -19,8 +19,10 @@ #include <stdlib.h> #include <stddef.h> #include <stdio.h> +#include <string.h> #include <errno.h> #include <arpa/inet.h> +#include <netinet/in.h> #include "duplicheck_msg.h" diff --git a/src/libcharon/plugins/eap_aka/Makefile.am b/src/libcharon/plugins/eap_aka/Makefile.am index 75e8eafb2..5d7ab8485 100644 --- a/src/libcharon/plugins/eap_aka/Makefile.am +++ b/src/libcharon/plugins/eap_aka/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -I$(top_srcdir)/src/libsimaka diff --git a/src/libcharon/plugins/eap_aka/Makefile.in b/src/libcharon/plugins/eap_aka/Makefile.in index dacddfb87..b5ffd8c24 100644 --- a/src/libcharon/plugins/eap_aka/Makefile.in +++ b/src/libcharon/plugins/eap_aka/Makefile.in @@ -417,6 +417,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -432,7 +434,6 @@ 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 \ -I$(top_srcdir)/src/libsimaka diff --git a/src/libcharon/plugins/eap_aka_3gpp2/Makefile.am b/src/libcharon/plugins/eap_aka_3gpp2/Makefile.am index ec145a39e..d68bfc4c4 100644 --- a/src/libcharon/plugins/eap_aka_3gpp2/Makefile.am +++ b/src/libcharon/plugins/eap_aka_3gpp2/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -I$(top_srcdir)/src/libsimaka diff --git a/src/libcharon/plugins/eap_aka_3gpp2/Makefile.in b/src/libcharon/plugins/eap_aka_3gpp2/Makefile.in index 3c26b8511..e0ad6fe2e 100644 --- a/src/libcharon/plugins/eap_aka_3gpp2/Makefile.in +++ b/src/libcharon/plugins/eap_aka_3gpp2/Makefile.in @@ -418,6 +418,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -433,7 +435,6 @@ 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 \ -I$(top_srcdir)/src/libsimaka diff --git a/src/libcharon/plugins/eap_dynamic/Makefile.am b/src/libcharon/plugins/eap_dynamic/Makefile.am index 58b827a78..fd08846a9 100644 --- a/src/libcharon/plugins/eap_dynamic/Makefile.am +++ b/src/libcharon/plugins/eap_dynamic/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/eap_dynamic/Makefile.in b/src/libcharon/plugins/eap_dynamic/Makefile.in index 402c7cadc..821f6de6c 100644 --- a/src/libcharon/plugins/eap_dynamic/Makefile.in +++ b/src/libcharon/plugins/eap_dynamic/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 = \ diff --git a/src/libcharon/plugins/eap_gtc/Makefile.am b/src/libcharon/plugins/eap_gtc/Makefile.am index c3a12ba3e..a7d1f6275 100644 --- a/src/libcharon/plugins/eap_gtc/Makefile.am +++ b/src/libcharon/plugins/eap_gtc/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/eap_gtc/Makefile.in b/src/libcharon/plugins/eap_gtc/Makefile.in index 2279b2514..cfd7c4e24 100644 --- a/src/libcharon/plugins/eap_gtc/Makefile.in +++ b/src/libcharon/plugins/eap_gtc/Makefile.in @@ -415,6 +415,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -430,7 +432,6 @@ 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 = \ diff --git a/src/libcharon/plugins/eap_identity/Makefile.am b/src/libcharon/plugins/eap_identity/Makefile.am index 6c5b43f00..4c44962bd 100644 --- a/src/libcharon/plugins/eap_identity/Makefile.am +++ b/src/libcharon/plugins/eap_identity/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/eap_identity/Makefile.in b/src/libcharon/plugins/eap_identity/Makefile.in index 30d2c88d1..1c544f360 100644 --- a/src/libcharon/plugins/eap_identity/Makefile.in +++ b/src/libcharon/plugins/eap_identity/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 = \ diff --git a/src/libcharon/plugins/eap_md5/Makefile.am b/src/libcharon/plugins/eap_md5/Makefile.am index 16aa1919b..b27e8cc54 100644 --- a/src/libcharon/plugins/eap_md5/Makefile.am +++ b/src/libcharon/plugins/eap_md5/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/eap_md5/Makefile.in b/src/libcharon/plugins/eap_md5/Makefile.in index 14616c214..e967262b6 100644 --- a/src/libcharon/plugins/eap_md5/Makefile.in +++ b/src/libcharon/plugins/eap_md5/Makefile.in @@ -415,6 +415,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -430,7 +432,6 @@ 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 = \ diff --git a/src/libcharon/plugins/eap_mschapv2/Makefile.am b/src/libcharon/plugins/eap_mschapv2/Makefile.am index 4276a082d..ded9bbe3f 100644 --- a/src/libcharon/plugins/eap_mschapv2/Makefile.am +++ b/src/libcharon/plugins/eap_mschapv2/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/eap_mschapv2/Makefile.in b/src/libcharon/plugins/eap_mschapv2/Makefile.in index 78dfd29e3..d96343a5c 100644 --- a/src/libcharon/plugins/eap_mschapv2/Makefile.in +++ b/src/libcharon/plugins/eap_mschapv2/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 = \ diff --git a/src/libcharon/plugins/eap_peap/Makefile.am b/src/libcharon/plugins/eap_peap/Makefile.am index 8960b84bd..ef226169d 100644 --- a/src/libcharon/plugins/eap_peap/Makefile.am +++ b/src/libcharon/plugins/eap_peap/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -I$(top_srcdir)/src/libtls diff --git a/src/libcharon/plugins/eap_peap/Makefile.in b/src/libcharon/plugins/eap_peap/Makefile.in index 2f0d65d6d..0f920fef8 100644 --- a/src/libcharon/plugins/eap_peap/Makefile.in +++ b/src/libcharon/plugins/eap_peap/Makefile.in @@ -417,6 +417,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -432,7 +434,6 @@ 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 \ -I$(top_srcdir)/src/libtls diff --git a/src/libcharon/plugins/eap_radius/Makefile.am b/src/libcharon/plugins/eap_radius/Makefile.am index bc7a7765d..78cf99184 100644 --- a/src/libcharon/plugins/eap_radius/Makefile.am +++ b/src/libcharon/plugins/eap_radius/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -I$(top_srcdir)/src/libradius diff --git a/src/libcharon/plugins/eap_radius/Makefile.in b/src/libcharon/plugins/eap_radius/Makefile.in index 47534372b..881a5b7e3 100644 --- a/src/libcharon/plugins/eap_radius/Makefile.in +++ b/src/libcharon/plugins/eap_radius/Makefile.in @@ -418,6 +418,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -433,7 +435,6 @@ 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 \ -I$(top_srcdir)/src/libradius diff --git a/src/libcharon/plugins/eap_sim/Makefile.am b/src/libcharon/plugins/eap_sim/Makefile.am index f68138579..8d93077e2 100644 --- a/src/libcharon/plugins/eap_sim/Makefile.am +++ b/src/libcharon/plugins/eap_sim/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -I$(top_srcdir)/src/libsimaka diff --git a/src/libcharon/plugins/eap_sim/Makefile.in b/src/libcharon/plugins/eap_sim/Makefile.in index 251eeeeba..aaa24bb17 100644 --- a/src/libcharon/plugins/eap_sim/Makefile.in +++ b/src/libcharon/plugins/eap_sim/Makefile.in @@ -417,6 +417,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -432,7 +434,6 @@ 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 \ -I$(top_srcdir)/src/libsimaka diff --git a/src/libcharon/plugins/eap_sim_file/Makefile.am b/src/libcharon/plugins/eap_sim_file/Makefile.am index c38e55e2c..5c5694c18 100644 --- a/src/libcharon/plugins/eap_sim_file/Makefile.am +++ b/src/libcharon/plugins/eap_sim_file/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -I$(top_srcdir)/src/libsimaka \ -DIPSEC_CONFDIR=\"${sysconfdir}\" diff --git a/src/libcharon/plugins/eap_sim_file/Makefile.in b/src/libcharon/plugins/eap_sim_file/Makefile.in index bffcbc0df..6e61f99de 100644 --- a/src/libcharon/plugins/eap_sim_file/Makefile.in +++ b/src/libcharon/plugins/eap_sim_file/Makefile.in @@ -418,6 +418,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -433,7 +435,6 @@ 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 \ -I$(top_srcdir)/src/libsimaka \ -DIPSEC_CONFDIR=\"${sysconfdir}\" diff --git a/src/libcharon/plugins/eap_sim_pcsc/Makefile.am b/src/libcharon/plugins/eap_sim_pcsc/Makefile.am index 22922049d..5e235e7ea 100644 --- a/src/libcharon/plugins/eap_sim_pcsc/Makefile.am +++ b/src/libcharon/plugins/eap_sim_pcsc/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -I$(top_srcdir)/src/libsimaka diff --git a/src/libcharon/plugins/eap_sim_pcsc/Makefile.in b/src/libcharon/plugins/eap_sim_pcsc/Makefile.in index 78682ce37..e821e3ee2 100644 --- a/src/libcharon/plugins/eap_sim_pcsc/Makefile.in +++ b/src/libcharon/plugins/eap_sim_pcsc/Makefile.in @@ -419,6 +419,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -434,7 +436,6 @@ 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 \ -I$(top_srcdir)/src/libsimaka diff --git a/src/libcharon/plugins/eap_simaka_pseudonym/Makefile.am b/src/libcharon/plugins/eap_simaka_pseudonym/Makefile.am index f40efbd6f..c0d7b914c 100644 --- a/src/libcharon/plugins/eap_simaka_pseudonym/Makefile.am +++ b/src/libcharon/plugins/eap_simaka_pseudonym/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -I$(top_srcdir)/src/libsimaka diff --git a/src/libcharon/plugins/eap_simaka_pseudonym/Makefile.in b/src/libcharon/plugins/eap_simaka_pseudonym/Makefile.in index 2a6be5fd9..b883f0abd 100644 --- a/src/libcharon/plugins/eap_simaka_pseudonym/Makefile.in +++ b/src/libcharon/plugins/eap_simaka_pseudonym/Makefile.in @@ -419,6 +419,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -434,7 +436,6 @@ 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 \ -I$(top_srcdir)/src/libsimaka diff --git a/src/libcharon/plugins/eap_simaka_reauth/Makefile.am b/src/libcharon/plugins/eap_simaka_reauth/Makefile.am index 0fb622220..9e55bb188 100644 --- a/src/libcharon/plugins/eap_simaka_reauth/Makefile.am +++ b/src/libcharon/plugins/eap_simaka_reauth/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -I$(top_srcdir)/src/libsimaka diff --git a/src/libcharon/plugins/eap_simaka_reauth/Makefile.in b/src/libcharon/plugins/eap_simaka_reauth/Makefile.in index de504d4cd..5417f9639 100644 --- a/src/libcharon/plugins/eap_simaka_reauth/Makefile.in +++ b/src/libcharon/plugins/eap_simaka_reauth/Makefile.in @@ -418,6 +418,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -433,7 +435,6 @@ 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 \ -I$(top_srcdir)/src/libsimaka diff --git a/src/libcharon/plugins/eap_simaka_sql/Makefile.am b/src/libcharon/plugins/eap_simaka_sql/Makefile.am index b7d6fd43e..f4c478dba 100644 --- a/src/libcharon/plugins/eap_simaka_sql/Makefile.am +++ b/src/libcharon/plugins/eap_simaka_sql/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -I$(top_srcdir)/src/libsimaka \ -DIPSEC_CONFDIR=\"${sysconfdir}\" diff --git a/src/libcharon/plugins/eap_simaka_sql/Makefile.in b/src/libcharon/plugins/eap_simaka_sql/Makefile.in index de3508a07..c858e467c 100644 --- a/src/libcharon/plugins/eap_simaka_sql/Makefile.in +++ b/src/libcharon/plugins/eap_simaka_sql/Makefile.in @@ -417,6 +417,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -432,7 +434,6 @@ 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 \ -I$(top_srcdir)/src/libsimaka \ -DIPSEC_CONFDIR=\"${sysconfdir}\" diff --git a/src/libcharon/plugins/eap_tls/Makefile.am b/src/libcharon/plugins/eap_tls/Makefile.am index 825beb841..551ecb380 100644 --- a/src/libcharon/plugins/eap_tls/Makefile.am +++ b/src/libcharon/plugins/eap_tls/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -I$(top_srcdir)/src/libtls diff --git a/src/libcharon/plugins/eap_tls/Makefile.in b/src/libcharon/plugins/eap_tls/Makefile.in index d4219b876..c953d0e9c 100644 --- a/src/libcharon/plugins/eap_tls/Makefile.in +++ b/src/libcharon/plugins/eap_tls/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 \ -I$(top_srcdir)/src/libtls diff --git a/src/libcharon/plugins/eap_tnc/Makefile.am b/src/libcharon/plugins/eap_tnc/Makefile.am index 6fc78bc9a..186ae45e2 100644 --- a/src/libcharon/plugins/eap_tnc/Makefile.am +++ b/src/libcharon/plugins/eap_tnc/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -I$(top_srcdir)/src/libtls \ -I$(top_srcdir)/src/libtncif \ diff --git a/src/libcharon/plugins/eap_tnc/Makefile.in b/src/libcharon/plugins/eap_tnc/Makefile.in index 6c34ed098..2f197ed33 100644 --- a/src/libcharon/plugins/eap_tnc/Makefile.in +++ b/src/libcharon/plugins/eap_tnc/Makefile.in @@ -417,6 +417,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -432,7 +434,6 @@ 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 \ -I$(top_srcdir)/src/libtls \ -I$(top_srcdir)/src/libtncif \ diff --git a/src/libcharon/plugins/eap_tnc/eap_tnc.c b/src/libcharon/plugins/eap_tnc/eap_tnc.c index 350001bb4..621caffee 100644 --- a/src/libcharon/plugins/eap_tnc/eap_tnc.c +++ b/src/libcharon/plugins/eap_tnc/eap_tnc.c @@ -328,7 +328,7 @@ static eap_tnc_t *eap_tnc_create(identification_t *server, tnccs = tnc->tnccs->create_instance(tnc->tnccs, tnccs_type, is_server, server, peer, server_ip, peer_ip, (type == EAP_TNC) ? TNC_IFT_EAP_1_1 : TNC_IFT_EAP_2_0, - is_server ? enforce_recommendation : NULL); + enforce_recommendation); if (!tnccs) { DBG1(DBG_TNC, "TNCCS protocol '%s' not enabled", protocol); diff --git a/src/libcharon/plugins/eap_ttls/Makefile.am b/src/libcharon/plugins/eap_ttls/Makefile.am index 3a7a8cda3..3db20e348 100644 --- a/src/libcharon/plugins/eap_ttls/Makefile.am +++ b/src/libcharon/plugins/eap_ttls/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -I$(top_srcdir)/src/libtls \ -I$(top_srcdir)/src/libradius diff --git a/src/libcharon/plugins/eap_ttls/Makefile.in b/src/libcharon/plugins/eap_ttls/Makefile.in index 0babf1766..b563acdda 100644 --- a/src/libcharon/plugins/eap_ttls/Makefile.in +++ b/src/libcharon/plugins/eap_ttls/Makefile.in @@ -418,6 +418,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -433,7 +435,6 @@ 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 \ -I$(top_srcdir)/src/libtls \ -I$(top_srcdir)/src/libradius diff --git a/src/libcharon/plugins/error_notify/Makefile.am b/src/libcharon/plugins/error_notify/Makefile.am index 1c64bd2cc..766bb4c51 100644 --- a/src/libcharon/plugins/error_notify/Makefile.am +++ b/src/libcharon/plugins/error_notify/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -DIPSEC_PIDDIR=\"${piddir}\" diff --git a/src/libcharon/plugins/error_notify/Makefile.in b/src/libcharon/plugins/error_notify/Makefile.in index 0a07aa7a3..03dfe3d60 100644 --- a/src/libcharon/plugins/error_notify/Makefile.in +++ b/src/libcharon/plugins/error_notify/Makefile.in @@ -424,6 +424,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -439,7 +441,6 @@ 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 \ -DIPSEC_PIDDIR=\"${piddir}\" diff --git a/src/libcharon/plugins/ext_auth/Makefile.am b/src/libcharon/plugins/ext_auth/Makefile.am index d51ea8881..7028819aa 100644 --- a/src/libcharon/plugins/ext_auth/Makefile.am +++ b/src/libcharon/plugins/ext_auth/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/ext_auth/Makefile.in b/src/libcharon/plugins/ext_auth/Makefile.in index d23e680aa..fce2e8e63 100644 --- a/src/libcharon/plugins/ext_auth/Makefile.in +++ b/src/libcharon/plugins/ext_auth/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 = \ diff --git a/src/libcharon/plugins/farp/Makefile.am b/src/libcharon/plugins/farp/Makefile.am index 0d862b0a9..6d96f3abb 100644 --- a/src/libcharon/plugins/farp/Makefile.am +++ b/src/libcharon/plugins/farp/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/farp/Makefile.in b/src/libcharon/plugins/farp/Makefile.in index 318400fc9..2afc5ad76 100644 --- a/src/libcharon/plugins/farp/Makefile.in +++ b/src/libcharon/plugins/farp/Makefile.in @@ -414,6 +414,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -429,7 +431,6 @@ 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 = \ diff --git a/src/libcharon/plugins/forecast/Makefile.am b/src/libcharon/plugins/forecast/Makefile.am index ce573135d..77535294e 100644 --- a/src/libcharon/plugins/forecast/Makefile.am +++ b/src/libcharon/plugins/forecast/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/forecast/Makefile.in b/src/libcharon/plugins/forecast/Makefile.in index 7b190ca25..4f2a407b4 100644 --- a/src/libcharon/plugins/forecast/Makefile.in +++ b/src/libcharon/plugins/forecast/Makefile.in @@ -417,6 +417,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -432,7 +434,6 @@ 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 = \ diff --git a/src/libcharon/plugins/forecast/forecast_forwarder.c b/src/libcharon/plugins/forecast/forecast_forwarder.c index 07a3d4953..40aaa7f25 100644 --- a/src/libcharon/plugins/forecast/forecast_forwarder.c +++ b/src/libcharon/plugins/forecast/forecast_forwarder.c @@ -27,7 +27,6 @@ #include <ifaddrs.h> #include <net/if.h> -#include <hydra.h> #include <daemon.h> #include <threading/thread.h> #include <processing/jobs/callback_job.h> @@ -428,8 +427,7 @@ METHOD(forecast_forwarder_t, destroy, void, lib->watcher->remove(lib->watcher, this->kernel.pkt); close(this->kernel.pkt); } - hydra->kernel_interface->remove_listener(hydra->kernel_interface, - &this->kernel.listener); + charon->kernel->remove_listener(charon->kernel, &this->kernel.listener); free(this); } @@ -486,8 +484,8 @@ forecast_forwarder_t *forecast_forwarder_create(forecast_listener_t *listener) setup_interface(&this->kernel); - hydra->kernel_interface->add_listener(hydra->kernel_interface, - &this->kernel.listener); + charon->kernel->add_listener(charon->kernel, + &this->kernel.listener); lib->watcher->add(lib->watcher, this->kernel.pkt, WATCHER_READ, (watcher_cb_t)receive_casts, this); diff --git a/src/libcharon/plugins/forecast/forecast_listener.c b/src/libcharon/plugins/forecast/forecast_listener.c index 63a8cb15b..8f7f2600c 100644 --- a/src/libcharon/plugins/forecast/forecast_listener.c +++ b/src/libcharon/plugins/forecast/forecast_listener.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2015 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * * Copyright (C) 2010-2014 Martin Willi * Copyright (C) 2010-2014 revosec AG * @@ -25,6 +28,15 @@ #include <collections/hashtable.h> #include <threading/rwlock.h> +/** + * Add a struct at the current position in the buffer + */ +#define ADD_STRUCT(pos, st, ...) ({\ + typeof(pos) _cur = pos; pos += XT_ALIGN(sizeof(st));\ + *(st*)_cur = (st){ __VA_ARGS__ };\ + (st*)_cur;\ +}) + typedef struct private_forecast_listener_t private_forecast_listener_t; /** @@ -148,7 +160,10 @@ static bool manage_rule(struct iptc_handle *ipth, const char *chain, } else { - if (!iptc_delete_entry(chain, e, "", ipth)) + u_char matchmask[e->next_offset]; + + memset(matchmask, 255, sizeof(matchmask)); + if (!iptc_delete_entry(chain, e, matchmask, ipth)) { DBG1(DBG_CFG, "deleting %s rule failed: %s", chain, iptc_strerror(errno)); @@ -164,60 +179,60 @@ static bool manage_rule(struct iptc_handle *ipth, const char *chain, static bool manage_pre_esp_in_udp(struct iptc_handle *ipth, entry_t *entry, bool add) { - struct { - struct ipt_entry e; - struct ipt_entry_match m; - struct xt_udp udp; - struct ipt_entry_target t; - struct xt_mark_tginfo2 tm; - } ipt = { - .e = { - .target_offset = XT_ALIGN(sizeof(ipt.e) + sizeof(ipt.m) + - sizeof(ipt.udp)), - .next_offset = sizeof(ipt), - .ip = { - .proto = IPPROTO_UDP, - }, + u_int16_t match_size = XT_ALIGN(sizeof(struct ipt_entry_match)) + + XT_ALIGN(sizeof(struct xt_udp)); + u_int16_t target_offset = XT_ALIGN(sizeof(struct ipt_entry)) + match_size; + u_int16_t target_size = XT_ALIGN(sizeof(struct ipt_entry_target)) + + XT_ALIGN(sizeof(struct xt_mark_tginfo2)); + u_int16_t entry_size = target_offset + target_size; + u_char ipt[entry_size], *pos = ipt; + struct ipt_entry *e; + + memset(ipt, 0, sizeof(ipt)); + e = ADD_STRUCT(pos, struct ipt_entry, + .target_offset = target_offset, + .next_offset = entry_size, + .ip = { + .proto = IPPROTO_UDP, }, - .m = { - .u = { - .user = { - .match_size = XT_ALIGN(sizeof(ipt.m) + sizeof(ipt.udp)), - .name = "udp", - }, + ); + if (!host2in(entry->lhost, &e->ip.dst, &e->ip.dmsk) || + !host2in(entry->rhost, &e->ip.src, &e->ip.smsk)) + { + return FALSE; + } + ADD_STRUCT(pos, struct ipt_entry_match, + .u = { + .user = { + .match_size = match_size, + .name = "udp", }, }, - .udp = { - .spts = { - entry->rhost->get_port(entry->rhost), - entry->rhost->get_port(entry->lhost) - }, - .dpts = { - entry->lhost->get_port(entry->lhost), - entry->lhost->get_port(entry->lhost) - }, + ); + ADD_STRUCT(pos, struct xt_udp, + .spts = { + entry->rhost->get_port(entry->rhost), + entry->rhost->get_port(entry->lhost) }, - .t = { - .u = { - .user = { - .target_size = XT_ALIGN(sizeof(ipt.t) + sizeof(ipt.tm)), - .name = "MARK", - .revision = 2, - }, - }, + .dpts = { + entry->lhost->get_port(entry->lhost), + entry->lhost->get_port(entry->lhost) }, - .tm = { - .mark = entry->mark, - .mask = ~0, + ); + ADD_STRUCT(pos, struct ipt_entry_target, + .u = { + .user = { + .target_size = target_size, + .name = "MARK", + .revision = 2, + }, }, - }; - - if (!host2in(entry->lhost, &ipt.e.ip.dst, &ipt.e.ip.dmsk) || - !host2in(entry->rhost, &ipt.e.ip.src, &ipt.e.ip.smsk)) - { - return FALSE; - } - return manage_rule(ipth, "PREROUTING", add, &ipt.e); + ); + ADD_STRUCT(pos, struct xt_mark_tginfo2, + .mark = entry->mark, + .mask = ~0, + ); + return manage_rule(ipth, "PREROUTING", add, e); } /** @@ -225,53 +240,53 @@ static bool manage_pre_esp_in_udp(struct iptc_handle *ipth, */ static bool manage_pre_esp(struct iptc_handle *ipth, entry_t *entry, bool add) { - struct { - struct ipt_entry e; - struct ipt_entry_match m; - struct xt_esp esp; - struct ipt_entry_target t; - struct xt_mark_tginfo2 tm; - } ipt = { - .e = { - .target_offset = XT_ALIGN(sizeof(ipt.e) + sizeof(ipt.m) + - sizeof(ipt.esp)), - .next_offset = sizeof(ipt), - .ip = { - .proto = IPPROTO_ESP, - }, + u_int16_t match_size = XT_ALIGN(sizeof(struct ipt_entry_match)) + + XT_ALIGN(sizeof(struct xt_esp)); + u_int16_t target_offset = XT_ALIGN(sizeof(struct ipt_entry)) + match_size; + u_int16_t target_size = XT_ALIGN(sizeof(struct ipt_entry_target)) + + XT_ALIGN(sizeof(struct xt_mark_tginfo2)); + u_int16_t entry_size = target_offset + target_size; + u_char ipt[entry_size], *pos = ipt; + struct ipt_entry *e; + + memset(ipt, 0, sizeof(ipt)); + e = ADD_STRUCT(pos, struct ipt_entry, + .target_offset = target_offset, + .next_offset = entry_size, + .ip = { + .proto = IPPROTO_ESP, }, - .m = { - .u = { - .user = { - .match_size = XT_ALIGN(sizeof(ipt.m) + sizeof(ipt.esp)), - .name = "esp", - }, + ); + if (!host2in(entry->lhost, &e->ip.dst, &e->ip.dmsk) || + !host2in(entry->rhost, &e->ip.src, &e->ip.smsk)) + { + return FALSE; + } + ADD_STRUCT(pos, struct ipt_entry_match, + .u = { + .user = { + .match_size = match_size, + .name = "esp", }, }, - .esp = { - .spis = { htonl(entry->spi), htonl(entry->spi) }, - }, - .t = { - .u = { - .user = { - .target_size = XT_ALIGN(sizeof(ipt.t) + sizeof(ipt.tm)), - .name = "MARK", - .revision = 2, - }, + ); + ADD_STRUCT(pos, struct xt_esp, + .spis = { htonl(entry->spi), htonl(entry->spi) }, + ); + ADD_STRUCT(pos, struct ipt_entry_target, + .u = { + .user = { + .target_size = target_size, + .name = "MARK", + .revision = 2, }, }, - .tm = { - .mark = entry->mark, - .mask = ~0, - }, - }; - - if (!host2in(entry->lhost, &ipt.e.ip.dst, &ipt.e.ip.dmsk) || - !host2in(entry->rhost, &ipt.e.ip.src, &ipt.e.ip.smsk)) - { - return FALSE; - } - return manage_rule(ipth, "PREROUTING", add, &ipt.e); + ); + ADD_STRUCT(pos, struct xt_mark_tginfo2, + .mark = entry->mark, + .mask = ~0, + ); + return manage_rule(ipth, "PREROUTING", add, e); } /** @@ -291,45 +306,52 @@ static bool manage_pre(struct iptc_handle *ipth, entry_t *entry, bool add) */ static bool manage_out(struct iptc_handle *ipth, entry_t *entry, bool add) { - struct { - struct ipt_entry e; - struct ipt_entry_target t; - struct xt_mark_tginfo2 m; - } ipt = { - .e = { - .target_offset = XT_ALIGN(sizeof(ipt.e)), - .next_offset = sizeof(ipt), - }, - .t = { - .u.user.target_size = XT_ALIGN(sizeof(ipt.t) + sizeof(ipt.m)), - .u.user.name = "MARK", - .u.user.revision = 2, - }, - .m = { - .mark = entry->mark, - .mask = ~0, + u_int16_t target_offset = XT_ALIGN(sizeof(struct ipt_entry)); + u_int16_t target_size = XT_ALIGN(sizeof(struct ipt_entry_target)) + + XT_ALIGN(sizeof(struct xt_mark_tginfo2)); + u_int16_t entry_size = target_offset + target_size; + u_char ipt[entry_size], *pos = ipt; + struct ipt_entry *e; + + memset(ipt, 0, sizeof(ipt)); + e = ADD_STRUCT(pos, struct ipt_entry, + .target_offset = target_offset, + .next_offset = entry_size, + ); + ADD_STRUCT(pos, struct ipt_entry_target, + .u = { + .user = { + .target_size = target_size, + .name = "MARK", + .revision = 2, + }, }, - }; + ); + ADD_STRUCT(pos, struct xt_mark_tginfo2, + .mark = entry->mark, + .mask = ~0, + ); + enumerator_t *enumerator; traffic_selector_t *ts; enumerator = array_create_enumerator(entry->rts); while (enumerator->enumerate(enumerator, &ts)) { - if (!ts2in(ts, &ipt.e.ip.dst, &ipt.e.ip.dmsk)) + if (!ts2in(ts, &e->ip.dst, &e->ip.dmsk)) { continue; } - if (ipt.e.ip.dst.s_addr == 0xffffffff || - ipt.e.ip.dst.s_addr == entry->broadcast || - memeq(&ipt.e.ip.dst.s_addr, "\xe0", 1)) + if (e->ip.dst.s_addr == 0xffffffff || + e->ip.dst.s_addr == entry->broadcast || + memeq(&e->ip.dst.s_addr, "\xe0", 1)) { /* skip broadcast/multicast selectors, they are shared and the mark * is set by the socket we use for reinjection */ continue; } - if (!manage_rule(ipth, "PREROUTING", add, &ipt.e) || - !manage_rule(ipth, "OUTPUT", add, &ipt.e)) + if (!manage_rule(ipth, "PREROUTING", add, e) || + !manage_rule(ipth, "OUTPUT", add, e)) { enumerator->destroy(enumerator); return FALSE; diff --git a/src/libcharon/plugins/ha/Makefile.am b/src/libcharon/plugins/ha/Makefile.am index 50d342389..d501834d7 100644 --- a/src/libcharon/plugins/ha/Makefile.am +++ b/src/libcharon/plugins/ha/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -DIPSEC_PIDDIR=\"${piddir}\" diff --git a/src/libcharon/plugins/ha/Makefile.in b/src/libcharon/plugins/ha/Makefile.in index de74f88cc..677c36afe 100644 --- a/src/libcharon/plugins/ha/Makefile.in +++ b/src/libcharon/plugins/ha/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 \ -DIPSEC_PIDDIR=\"${piddir}\" diff --git a/src/libcharon/plugins/ha/ha_child.c b/src/libcharon/plugins/ha/ha_child.c index dbb6adc8f..7dafb1693 100644 --- a/src/libcharon/plugins/ha/ha_child.c +++ b/src/libcharon/plugins/ha/ha_child.c @@ -91,6 +91,10 @@ METHOD(listener_t, child_keys, bool, { m->add_attribute(m, HA_ALG_INTEG, alg); } + if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, &alg, NULL)) + { + m->add_attribute(m, HA_ALG_DH, alg); + } if (proposal->get_algorithm(proposal, EXTENDED_SEQUENCE_NUMBERS, &alg, NULL)) { m->add_attribute(m, HA_ESN, alg); diff --git a/src/libcharon/plugins/ha/ha_dispatcher.c b/src/libcharon/plugins/ha/ha_dispatcher.c index 07ef607c6..ce90f5bfe 100644 --- a/src/libcharon/plugins/ha/ha_dispatcher.c +++ b/src/libcharon/plugins/ha/ha_dispatcher.c @@ -132,6 +132,7 @@ static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message ike_sa_t *ike_sa = NULL, *old_sa = NULL; ike_version_t version = IKEV2; u_int16_t encr = 0, len = 0, integ = 0, prf = 0, old_prf = PRF_UNDEFINED; + u_int16_t dh_grp = 0; chunk_t nonce_i = chunk_empty, nonce_r = chunk_empty; chunk_t secret = chunk_empty, old_skd = chunk_empty; chunk_t dh_local = chunk_empty, dh_remote = chunk_empty, psk = chunk_empty; @@ -193,6 +194,9 @@ static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message case HA_ALG_OLD_PRF: old_prf = value.u16; break; + case HA_ALG_DH: + dh_grp = value.u16; + break; default: break; } @@ -217,6 +221,10 @@ static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message { proposal->add_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, prf, 0); } + if (dh_grp) + { + proposal->add_algorithm(proposal, DIFFIE_HELLMAN_GROUP, dh_grp, 0); + } charon->bus->set_sa(charon->bus, ike_sa); dh = ha_diffie_hellman_create(secret, dh_local); if (ike_sa->get_version(ike_sa) == IKEV2) @@ -647,7 +655,7 @@ static void process_child_add(private_ha_dispatcher_t *this, u_int32_t inbound_spi = 0, outbound_spi = 0; u_int16_t inbound_cpi = 0, outbound_cpi = 0; u_int8_t mode = MODE_TUNNEL, ipcomp = 0; - u_int16_t encr = 0, integ = 0, len = 0; + u_int16_t encr = 0, integ = 0, len = 0, dh_grp = 0; u_int16_t esn = NO_EXT_SEQ_NUMBERS; u_int seg_i, seg_o; chunk_t nonce_i = chunk_empty, nonce_r = chunk_empty, secret = chunk_empty; @@ -697,6 +705,9 @@ static void process_child_add(private_ha_dispatcher_t *this, case HA_ALG_INTEG: integ = value.u16; break; + case HA_ALG_DH: + dh_grp = value.u16; + break; case HA_ESN: esn = value.u16; break; @@ -747,6 +758,10 @@ static void process_child_add(private_ha_dispatcher_t *this, { proposal->add_algorithm(proposal, ENCRYPTION_ALGORITHM, encr, len); } + if (dh_grp) + { + proposal->add_algorithm(proposal, DIFFIE_HELLMAN_GROUP, dh_grp, 0); + } proposal->add_algorithm(proposal, EXTENDED_SEQUENCE_NUMBERS, esn, 0); if (secret.len) { diff --git a/src/libcharon/plugins/ha/ha_ike.c b/src/libcharon/plugins/ha/ha_ike.c index 7492dd06e..3ffcaee6b 100644 --- a/src/libcharon/plugins/ha/ha_ike.c +++ b/src/libcharon/plugins/ha/ha_ike.c @@ -121,6 +121,10 @@ METHOD(listener_t, ike_keys, bool, { m->add_attribute(m, HA_ALG_PRF, alg); } + if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, &alg, NULL)) + { + m->add_attribute(m, HA_ALG_DH, alg); + } m->add_attribute(m, HA_NONCE_I, nonce_i); m->add_attribute(m, HA_NONCE_R, nonce_r); m->add_attribute(m, HA_SECRET, secret); @@ -310,27 +314,31 @@ METHOD(listener_t, message_hook, bool, sync_vips(this, ike_sa); } } - if (!plain && ike_sa->get_version(ike_sa) == IKEV1) + if (ike_sa->get_version(ike_sa) == IKEV1) { ha_message_t *m; keymat_v1_t *keymat; - u_int32_t mid; chunk_t iv; - mid = message->get_message_id(message); - if (mid == 0) + /* we need the last block (or expected next IV) of Phase 1, which gets + * upated after successful en-/decryption depending on direction */ + if (incoming == plain) { - keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa); - if (keymat->get_iv(keymat, mid, &iv)) + if (message->get_message_id(message) == 0) { - m = ha_message_create(HA_IKE_IV); - m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa)); - m->add_attribute(m, HA_IV, iv); - this->socket->push(this->socket, m); - this->cache->cache(this->cache, ike_sa, m); + keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa); + if (keymat->get_iv(keymat, 0, &iv)) + { + m = ha_message_create(HA_IKE_IV); + m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa)); + m->add_attribute(m, HA_IV, iv); + this->socket->push(this->socket, m); + this->cache->cache(this->cache, ike_sa, m); + } } } - if (!incoming && message->get_exchange_type(message) == TRANSACTION) + if (!plain && !incoming && + message->get_exchange_type(message) == TRANSACTION) { sync_vips(this, ike_sa); } diff --git a/src/libcharon/plugins/ha/ha_message.c b/src/libcharon/plugins/ha/ha_message.c index 6b00ed83f..b40219ce1 100644 --- a/src/libcharon/plugins/ha/ha_message.c +++ b/src/libcharon/plugins/ha/ha_message.c @@ -230,6 +230,7 @@ METHOD(ha_message_t, add_attribute, void, break; } /* u_int16_t */ + case HA_ALG_DH: case HA_ALG_PRF: case HA_ALG_OLD_PRF: case HA_ALG_ENCR: @@ -450,6 +451,7 @@ METHOD(enumerator_t, attribute_enumerate, bool, return TRUE; } /** u_int16_t */ + case HA_ALG_DH: case HA_ALG_PRF: case HA_ALG_OLD_PRF: case HA_ALG_ENCR: diff --git a/src/libcharon/plugins/ha/ha_message.h b/src/libcharon/plugins/ha/ha_message.h index 2ccb1fc55..fe1786edf 100644 --- a/src/libcharon/plugins/ha/ha_message.h +++ b/src/libcharon/plugins/ha/ha_message.h @@ -122,6 +122,8 @@ enum ha_message_attribute_t { HA_ALG_ENCR_LEN, /** u_int16_t, integrity protection algorithm */ HA_ALG_INTEG, + /** u_int16_t, DH group */ + HA_ALG_DH, /** u_int8_t, IPsec mode, TUNNEL|TRANSPORT|... */ HA_IPSEC_MODE, /** u_int8_t, IPComp protocol */ diff --git a/src/libcharon/plugins/ipseckey/Makefile.am b/src/libcharon/plugins/ipseckey/Makefile.am index aed63c122..b8933008c 100644 --- a/src/libcharon/plugins/ipseckey/Makefile.am +++ b/src/libcharon/plugins/ipseckey/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/ipseckey/Makefile.in b/src/libcharon/plugins/ipseckey/Makefile.in index f98e78ffc..0b7a29194 100644 --- a/src/libcharon/plugins/ipseckey/Makefile.in +++ b/src/libcharon/plugins/ipseckey/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 = \ diff --git a/src/libcharon/plugins/kernel_iph/Makefile.am b/src/libcharon/plugins/kernel_iph/Makefile.am index 56946ae1f..707570195 100644 --- a/src/libcharon/plugins/kernel_iph/Makefile.am +++ b/src/libcharon/plugins/kernel_iph/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/kernel_iph/Makefile.in b/src/libcharon/plugins/kernel_iph/Makefile.in index 7a2583d06..de5bfd517 100644 --- a/src/libcharon/plugins/kernel_iph/Makefile.in +++ b/src/libcharon/plugins/kernel_iph/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 = \ diff --git a/src/libcharon/plugins/kernel_iph/kernel_iph_net.c b/src/libcharon/plugins/kernel_iph/kernel_iph_net.c index a4be4041e..6a8a96821 100644 --- a/src/libcharon/plugins/kernel_iph/kernel_iph_net.c +++ b/src/libcharon/plugins/kernel_iph/kernel_iph_net.c @@ -24,7 +24,7 @@ #include "kernel_iph_net.h" -#include <hydra.h> +#include <daemon.h> #include <threading/mutex.h> #include <collections/linked_list.h> #include <processing/jobs/callback_job.h> @@ -130,7 +130,7 @@ static job_requeue_t roam_event(private_kernel_iph_net_t *this) this->roam_address = FALSE; this->mutex->unlock(this->mutex); - hydra->kernel_interface->roam(hydra->kernel_interface, address); + charon->kernel->roam(charon->kernel, address); return JOB_REQUEUE_NONE; } diff --git a/src/libcharon/plugins/kernel_iph/kernel_iph_plugin.c b/src/libcharon/plugins/kernel_iph/kernel_iph_plugin.c index c5475e30b..c16381440 100644 --- a/src/libcharon/plugins/kernel_iph/kernel_iph_plugin.c +++ b/src/libcharon/plugins/kernel_iph/kernel_iph_plugin.c @@ -17,8 +17,6 @@ #include "kernel_iph_plugin.h" #include "kernel_iph_net.h" -#include <hydra.h> - typedef struct private_kernel_iph_plugin_t private_kernel_iph_plugin_t; /** diff --git a/src/libcharon/plugins/kernel_libipsec/Makefile.am b/src/libcharon/plugins/kernel_libipsec/Makefile.am index eca2b2325..4757280b4 100644 --- a/src/libcharon/plugins/kernel_libipsec/Makefile.am +++ b/src/libcharon/plugins/kernel_libipsec/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -I$(top_srcdir)/src/libipsec diff --git a/src/libcharon/plugins/kernel_libipsec/Makefile.in b/src/libcharon/plugins/kernel_libipsec/Makefile.in index 6b6c95688..018a25a62 100644 --- a/src/libcharon/plugins/kernel_libipsec/Makefile.in +++ b/src/libcharon/plugins/kernel_libipsec/Makefile.in @@ -418,6 +418,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -433,7 +435,6 @@ 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 \ -I$(top_srcdir)/src/libipsec diff --git a/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c b/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c index d738e6d13..4c8771e96 100644 --- a/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c +++ b/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c @@ -17,7 +17,7 @@ #include <library.h> #include <ipsec.h> -#include <hydra.h> +#include <daemon.h> #include <networking/tun_device.h> #include <threading/mutex.h> #include <utils/debug.h> @@ -224,8 +224,7 @@ static inline bool policy_entry_equals(policy_entry_t *a, */ static void expire(u_int8_t protocol, u_int32_t spi, host_t *dst, bool hard) { - hydra->kernel_interface->expire(hydra->kernel_interface, protocol, - spi, dst, hard); + charon->kernel->expire(charon->kernel, protocol, spi, dst, hard); } METHOD(kernel_ipsec_t, get_features, kernel_feature_t, @@ -313,16 +312,13 @@ static void add_exclude_route(private_kernel_libipsec_ipsec_t *this, if (!route->exclude) { DBG2(DBG_KNL, "installing new exclude route for %H src %H", dst, src); - gtw = hydra->kernel_interface->get_nexthop(hydra->kernel_interface, - dst, -1, NULL); + gtw = charon->kernel->get_nexthop(charon->kernel, dst, -1, NULL); if (gtw) { char *if_name = NULL; - if (hydra->kernel_interface->get_interface( - hydra->kernel_interface, src, &if_name) && - hydra->kernel_interface->add_route(hydra->kernel_interface, - dst->get_address(dst), + if (charon->kernel->get_interface(charon->kernel, src, &if_name) && + charon->kernel->add_route(charon->kernel, dst->get_address(dst), dst->get_family(dst) == AF_INET ? 32 : 128, gtw, src, if_name) == SUCCESS) { @@ -367,14 +363,12 @@ static void remove_exclude_route(private_kernel_libipsec_ipsec_t *this, dst = route->exclude->dst; DBG2(DBG_KNL, "uninstalling exclude route for %H src %H", dst, route->exclude->src); - if (hydra->kernel_interface->get_interface( - hydra->kernel_interface, - route->exclude->src, &if_name) && - hydra->kernel_interface->del_route(hydra->kernel_interface, - dst->get_address(dst), - dst->get_family(dst) == AF_INET ? 32 : 128, - route->exclude->gtw, route->exclude->src, - if_name) != SUCCESS) + if (charon->kernel->get_interface(charon->kernel, route->exclude->src, + &if_name) && + charon->kernel->del_route(charon->kernel, dst->get_address(dst), + dst->get_family(dst) == AF_INET ? 32 : 128, + route->exclude->gtw, route->exclude->src, + if_name) != SUCCESS) { DBG1(DBG_KNL, "uninstalling exclude route for %H failed", dst); } @@ -402,8 +396,8 @@ static bool install_route(private_kernel_libipsec_ipsec_t *this, return TRUE; } - if (hydra->kernel_interface->get_address_by_ts(hydra->kernel_interface, - src_ts, &src_ip, &is_virtual) != SUCCESS) + if (charon->kernel->get_address_by_ts(charon->kernel, src_ts, &src_ip, + &is_virtual) != SUCCESS) { traffic_selector_t *multicast, *broadcast = NULL; bool ignore = FALSE; @@ -444,8 +438,7 @@ static bool install_route(private_kernel_libipsec_ipsec_t *this, ); #ifndef __linux__ /* on Linux we cant't install a gateway */ - route->gateway = hydra->kernel_interface->get_nexthop( - hydra->kernel_interface, dst, -1, src); + route->gateway = charon->kernel->get_nexthop(charon->kernel, dst, -1, src); #endif if (policy->route) @@ -459,9 +452,9 @@ static bool install_route(private_kernel_libipsec_ipsec_t *this, return TRUE; } /* uninstall previously installed route */ - if (hydra->kernel_interface->del_route(hydra->kernel_interface, - old->dst_net, old->prefixlen, old->gateway, - old->src_ip, old->if_name) != SUCCESS) + if (charon->kernel->del_route(charon->kernel, old->dst_net, + old->prefixlen, old->gateway, + old->src_ip, old->if_name) != SUCCESS) { DBG1(DBG_KNL, "error uninstalling route installed with policy " "%R === %R %N", src_ts, dst_ts, policy_dir_names, @@ -490,9 +483,9 @@ static bool install_route(private_kernel_libipsec_ipsec_t *this, DBG2(DBG_KNL, "installing route: %R src %H dev %s", dst_ts, route->src_ip, route->if_name); - switch (hydra->kernel_interface->add_route(hydra->kernel_interface, - route->dst_net, route->prefixlen, route->gateway, - route->src_ip, route->if_name)) + switch (charon->kernel->add_route(charon->kernel, route->dst_net, + route->prefixlen, route->gateway, + route->src_ip, route->if_name)) { case ALREADY_DONE: /* route exists, do not uninstall */ @@ -571,8 +564,8 @@ METHOD(kernel_ipsec_t, del_policy, status_t, policy_entry_t *policy, *found = NULL; status_t status; - status = ipsec->policies->del_policy(ipsec->policies, src_ts, dst_ts, - direction, sa->reqid, mark, priority); + status = ipsec->policies->del_policy(ipsec->policies, src, dst, src_ts, + dst_ts, direction, type, sa, mark, priority); policy = create_policy_entry(src_ts, dst_ts, direction); @@ -598,9 +591,9 @@ METHOD(kernel_ipsec_t, del_policy, status_t, { route_entry_t *route = policy->route; - if (hydra->kernel_interface->del_route(hydra->kernel_interface, - route->dst_net, route->prefixlen, route->gateway, route->src_ip, - route->if_name) != SUCCESS) + if (charon->kernel->del_route(charon->kernel, route->dst_net, + route->prefixlen, route->gateway, + route->src_ip, route->if_name) != SUCCESS) { DBG1(DBG_KNL, "error uninstalling route installed with " "policy %R === %R %N", src_ts, dst_ts, @@ -629,9 +622,9 @@ METHOD(kernel_ipsec_t, flush_policies, status_t, { route_entry_t *route = pol->route; - hydra->kernel_interface->del_route(hydra->kernel_interface, - route->dst_net, route->prefixlen, route->gateway, - route->src_ip, route->if_name); + charon->kernel->del_route(charon->kernel, route->dst_net, + route->prefixlen, route->gateway, + route->src_ip, route->if_name); remove_exclude_route(this, route); } policy_entry_destroy(pol); diff --git a/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_router.c b/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_router.c index 830954e11..66141ad56 100644 --- a/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_router.c +++ b/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_router.c @@ -19,7 +19,6 @@ #include "kernel_libipsec_router.h" #include <daemon.h> -#include <hydra.h> #include <ipsec.h> #include <collections/hashtable.h> #include <networking/tun_device.h> @@ -298,8 +297,7 @@ METHOD(kernel_libipsec_router_t, destroy, void, (ipsec_outbound_cb_t)send_esp); ipsec->processor->unregister_inbound(ipsec->processor, (ipsec_inbound_cb_t)deliver_plain); - hydra->kernel_interface->remove_listener(hydra->kernel_interface, - &this->public.listener); + charon->kernel->remove_listener(charon->kernel, &this->public.listener); this->lock->destroy(this->lock); this->tuns->destroy(this->tuns); close(this->notify[0]); @@ -351,8 +349,7 @@ kernel_libipsec_router_t *kernel_libipsec_router_create() (hashtable_equals_t)tun_entry_equals, 4); this->lock = rwlock_create(RWLOCK_TYPE_DEFAULT); - hydra->kernel_interface->add_listener(hydra->kernel_interface, - &this->public.listener); + charon->kernel->add_listener(charon->kernel, &this->public.listener); ipsec->processor->register_outbound(ipsec->processor, send_esp, NULL); ipsec->processor->register_inbound(ipsec->processor, (ipsec_inbound_cb_t)deliver_plain, this); diff --git a/src/libcharon/plugins/kernel_netlink/Makefile.am b/src/libcharon/plugins/kernel_netlink/Makefile.am new file mode 100644 index 000000000..973e2c2f4 --- /dev/null +++ b/src/libcharon/plugins/kernel_netlink/Makefile.am @@ -0,0 +1,44 @@ +AM_CPPFLAGS = \ + -I${linux_headers} \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libcharon \ + -DROUTING_TABLE=${routing_table} \ + -DROUTING_TABLE_PRIO=${routing_table_prio} + +AM_CFLAGS = \ + $(PLUGIN_CFLAGS) + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-kernel-netlink.la +else +plugin_LTLIBRARIES = libstrongswan-kernel-netlink.la +endif + +libstrongswan_kernel_netlink_la_SOURCES = \ + kernel_netlink_plugin.h kernel_netlink_plugin.c \ + kernel_netlink_ipsec.h kernel_netlink_ipsec.c \ + kernel_netlink_net.h kernel_netlink_net.c \ + kernel_netlink_shared.h kernel_netlink_shared.c + +libstrongswan_kernel_netlink_la_LDFLAGS = -module -avoid-version + + +TESTS = tests + +check_PROGRAMS = $(TESTS) + +tests_SOURCES = \ + tests.h tests.c \ + suites/test_socket.c \ + kernel_netlink_shared.c + +tests_CFLAGS = \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libstrongswan/tests \ + -DNETLINK_MSG_LOSS_HOOK=netlink_msg_loss \ + @COVERAGE_CFLAGS@ + +tests_LDFLAGS = @COVERAGE_LDFLAGS@ +tests_LDADD = \ + $(top_builddir)/src/libstrongswan/libstrongswan.la \ + $(top_builddir)/src/libstrongswan/tests/libtest.la diff --git a/src/libcharon/plugins/kernel_netlink/Makefile.in b/src/libcharon/plugins/kernel_netlink/Makefile.in new file mode 100644 index 000000000..55dcabf6f --- /dev/null +++ b/src/libcharon/plugins/kernel_netlink/Makefile.in @@ -0,0 +1,1007 @@ +# 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@ +TESTS = tests$(EXEEXT) +check_PROGRAMS = $(am__EXEEXT_1) +subdir = src/libcharon/plugins/kernel_netlink +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_netlink_la_LIBADD = +am_libstrongswan_kernel_netlink_la_OBJECTS = kernel_netlink_plugin.lo \ + kernel_netlink_ipsec.lo kernel_netlink_net.lo \ + kernel_netlink_shared.lo +libstrongswan_kernel_netlink_la_OBJECTS = \ + $(am_libstrongswan_kernel_netlink_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_netlink_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) \ + $(libstrongswan_kernel_netlink_la_LDFLAGS) $(LDFLAGS) -o $@ +@MONOLITHIC_FALSE@am_libstrongswan_kernel_netlink_la_rpath = -rpath \ +@MONOLITHIC_FALSE@ $(plugindir) +@MONOLITHIC_TRUE@am_libstrongswan_kernel_netlink_la_rpath = +am__EXEEXT_1 = tests$(EXEEXT) +am__dirstamp = $(am__leading_dot)dirstamp +am_tests_OBJECTS = tests-tests.$(OBJEXT) \ + suites/tests-test_socket.$(OBJEXT) \ + tests-kernel_netlink_shared.$(OBJEXT) +tests_OBJECTS = $(am_tests_OBJECTS) +tests_DEPENDENCIES = \ + $(top_builddir)/src/libstrongswan/libstrongswan.la \ + $(top_builddir)/src/libstrongswan/tests/libtest.la +tests_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(tests_CFLAGS) $(CFLAGS) \ + $(tests_LDFLAGS) $(LDFLAGS) -o $@ +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_netlink_la_SOURCES) $(tests_SOURCES) +DIST_SOURCES = $(libstrongswan_kernel_netlink_la_SOURCES) \ + $(tests_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 +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + fi; \ +} +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@ +EASY_INSTALL = @EASY_INSTALL@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GEM = @GEM@ +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@ +PYTHONEGGINSTALLDIR = @PYTHONEGGINSTALLDIR@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +PY_TEST = @PY_TEST@ +RANLIB = @RANLIB@ +RTLIB = @RTLIB@ +RUBY = @RUBY@ +RUBYGEMDIR = @RUBYGEMDIR@ +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@ +json_CFLAGS = @json_CFLAGS@ +json_LIBS = @json_LIBS@ +libdir = @libdir@ +libexecdir = @libexecdir@ +libiptc_CFLAGS = @libiptc_CFLAGS@ +libiptc_LIBS = @libiptc_LIBS@ +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@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ +systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ +systemd_daemon_LIBS = @systemd_daemon_LIBS@ +systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ +systemd_journal_LIBS = @systemd_journal_LIBS@ +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${linux_headers} \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libcharon \ + -DROUTING_TABLE=${routing_table} \ + -DROUTING_TABLE_PRIO=${routing_table_prio} + +AM_CFLAGS = \ + $(PLUGIN_CFLAGS) + +@MONOLITHIC_TRUE@noinst_LTLIBRARIES = libstrongswan-kernel-netlink.la +@MONOLITHIC_FALSE@plugin_LTLIBRARIES = libstrongswan-kernel-netlink.la +libstrongswan_kernel_netlink_la_SOURCES = \ + kernel_netlink_plugin.h kernel_netlink_plugin.c \ + kernel_netlink_ipsec.h kernel_netlink_ipsec.c \ + kernel_netlink_net.h kernel_netlink_net.c \ + kernel_netlink_shared.h kernel_netlink_shared.c + +libstrongswan_kernel_netlink_la_LDFLAGS = -module -avoid-version +tests_SOURCES = \ + tests.h tests.c \ + suites/test_socket.c \ + kernel_netlink_shared.c + +tests_CFLAGS = \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libstrongswan/tests \ + -DNETLINK_MSG_LOSS_HOOK=netlink_msg_loss \ + @COVERAGE_CFLAGS@ + +tests_LDFLAGS = @COVERAGE_LDFLAGS@ +tests_LDADD = \ + $(top_builddir)/src/libstrongswan/libstrongswan.la \ + $(top_builddir)/src/libstrongswan/tests/libtest.la + +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_netlink/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/libcharon/plugins/kernel_netlink/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-netlink.la: $(libstrongswan_kernel_netlink_la_OBJECTS) $(libstrongswan_kernel_netlink_la_DEPENDENCIES) $(EXTRA_libstrongswan_kernel_netlink_la_DEPENDENCIES) + $(AM_V_CCLD)$(libstrongswan_kernel_netlink_la_LINK) $(am_libstrongswan_kernel_netlink_la_rpath) $(libstrongswan_kernel_netlink_la_OBJECTS) $(libstrongswan_kernel_netlink_la_LIBADD) $(LIBS) + +clean-checkPROGRAMS: + @list='$(check_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 +suites/$(am__dirstamp): + @$(MKDIR_P) suites + @: > suites/$(am__dirstamp) +suites/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) suites/$(DEPDIR) + @: > suites/$(DEPDIR)/$(am__dirstamp) +suites/tests-test_socket.$(OBJEXT): suites/$(am__dirstamp) \ + suites/$(DEPDIR)/$(am__dirstamp) + +tests$(EXEEXT): $(tests_OBJECTS) $(tests_DEPENDENCIES) $(EXTRA_tests_DEPENDENCIES) + @rm -f tests$(EXEEXT) + $(AM_V_CCLD)$(tests_LINK) $(tests_OBJECTS) $(tests_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f suites/*.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel_netlink_ipsec.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel_netlink_net.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel_netlink_plugin.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel_netlink_shared.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tests-kernel_netlink_shared.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tests-tests.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@suites/$(DEPDIR)/tests-test_socket.Po@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 $@ $< + +tests-tests.o: tests.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_CFLAGS) $(CFLAGS) -MT tests-tests.o -MD -MP -MF $(DEPDIR)/tests-tests.Tpo -c -o tests-tests.o `test -f 'tests.c' || echo '$(srcdir)/'`tests.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tests-tests.Tpo $(DEPDIR)/tests-tests.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tests.c' object='tests-tests.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_CFLAGS) $(CFLAGS) -c -o tests-tests.o `test -f 'tests.c' || echo '$(srcdir)/'`tests.c + +tests-tests.obj: tests.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_CFLAGS) $(CFLAGS) -MT tests-tests.obj -MD -MP -MF $(DEPDIR)/tests-tests.Tpo -c -o tests-tests.obj `if test -f 'tests.c'; then $(CYGPATH_W) 'tests.c'; else $(CYGPATH_W) '$(srcdir)/tests.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tests-tests.Tpo $(DEPDIR)/tests-tests.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tests.c' object='tests-tests.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_CFLAGS) $(CFLAGS) -c -o tests-tests.obj `if test -f 'tests.c'; then $(CYGPATH_W) 'tests.c'; else $(CYGPATH_W) '$(srcdir)/tests.c'; fi` + +suites/tests-test_socket.o: suites/test_socket.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_CFLAGS) $(CFLAGS) -MT suites/tests-test_socket.o -MD -MP -MF suites/$(DEPDIR)/tests-test_socket.Tpo -c -o suites/tests-test_socket.o `test -f 'suites/test_socket.c' || echo '$(srcdir)/'`suites/test_socket.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/tests-test_socket.Tpo suites/$(DEPDIR)/tests-test_socket.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_socket.c' object='suites/tests-test_socket.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_CFLAGS) $(CFLAGS) -c -o suites/tests-test_socket.o `test -f 'suites/test_socket.c' || echo '$(srcdir)/'`suites/test_socket.c + +suites/tests-test_socket.obj: suites/test_socket.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_CFLAGS) $(CFLAGS) -MT suites/tests-test_socket.obj -MD -MP -MF suites/$(DEPDIR)/tests-test_socket.Tpo -c -o suites/tests-test_socket.obj `if test -f 'suites/test_socket.c'; then $(CYGPATH_W) 'suites/test_socket.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_socket.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/tests-test_socket.Tpo suites/$(DEPDIR)/tests-test_socket.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_socket.c' object='suites/tests-test_socket.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_CFLAGS) $(CFLAGS) -c -o suites/tests-test_socket.obj `if test -f 'suites/test_socket.c'; then $(CYGPATH_W) 'suites/test_socket.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_socket.c'; fi` + +tests-kernel_netlink_shared.o: kernel_netlink_shared.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_CFLAGS) $(CFLAGS) -MT tests-kernel_netlink_shared.o -MD -MP -MF $(DEPDIR)/tests-kernel_netlink_shared.Tpo -c -o tests-kernel_netlink_shared.o `test -f 'kernel_netlink_shared.c' || echo '$(srcdir)/'`kernel_netlink_shared.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tests-kernel_netlink_shared.Tpo $(DEPDIR)/tests-kernel_netlink_shared.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kernel_netlink_shared.c' object='tests-kernel_netlink_shared.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_CFLAGS) $(CFLAGS) -c -o tests-kernel_netlink_shared.o `test -f 'kernel_netlink_shared.c' || echo '$(srcdir)/'`kernel_netlink_shared.c + +tests-kernel_netlink_shared.obj: kernel_netlink_shared.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_CFLAGS) $(CFLAGS) -MT tests-kernel_netlink_shared.obj -MD -MP -MF $(DEPDIR)/tests-kernel_netlink_shared.Tpo -c -o tests-kernel_netlink_shared.obj `if test -f 'kernel_netlink_shared.c'; then $(CYGPATH_W) 'kernel_netlink_shared.c'; else $(CYGPATH_W) '$(srcdir)/kernel_netlink_shared.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tests-kernel_netlink_shared.Tpo $(DEPDIR)/tests-kernel_netlink_shared.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kernel_netlink_shared.c' object='tests-kernel_netlink_shared.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_CFLAGS) $(CFLAGS) -c -o tests-kernel_netlink_shared.obj `if test -f 'kernel_netlink_shared.c'; then $(CYGPATH_W) 'kernel_netlink_shared.c'; else $(CYGPATH_W) '$(srcdir)/kernel_netlink_shared.c'; fi` + +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 + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + fi; \ + echo "$${col}$$dashes$${std}"; \ + echo "$${col}$$banner$${std}"; \ + test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ + test -z "$$report" || echo "$${col}$$report$${std}"; \ + echo "$${col}$$dashes$${std}"; \ + test "$$failed" -eq 0; \ + else :; fi + +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 + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-am +all-am: Makefile $(LTLIBRARIES) +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) + -rm -f suites/$(DEPDIR)/$(am__dirstamp) + -rm -f suites/$(am__dirstamp) + +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-checkPROGRAMS clean-generic clean-libtool \ + clean-noinstLTLIBRARIES clean-pluginLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) suites/$(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) suites/$(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: check-am install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-TESTS check-am clean \ + clean-checkPROGRAMS clean-generic clean-libtool \ + clean-noinstLTLIBRARIES 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_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c new file mode 100644 index 000000000..6d9d63a98 --- /dev/null +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -0,0 +1,2975 @@ +/* + * Copyright (C) 2006-2015 Tobias Brunner + * Copyright (C) 2005-2009 Martin Willi + * Copyright (C) 2008-2016 Andreas Steffen + * Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser + * Copyright (C) 2006 Daniel Roethlisberger + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * 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 <sys/types.h> +#include <sys/socket.h> +#include <stdint.h> +#include <linux/ipsec.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <linux/xfrm.h> +#include <linux/udp.h> +#include <unistd.h> +#include <time.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> + +#include "kernel_netlink_ipsec.h" +#include "kernel_netlink_shared.h" + +#include <daemon.h> +#include <utils/debug.h> +#include <threading/mutex.h> +#include <collections/array.h> +#include <collections/hashtable.h> +#include <collections/linked_list.h> + +/** Required for Linux 2.6.26 kernel and later */ +#ifndef XFRM_STATE_AF_UNSPEC +#define XFRM_STATE_AF_UNSPEC 32 +#endif + +/** From linux/in.h */ +#ifndef IP_XFRM_POLICY +#define IP_XFRM_POLICY 17 +#endif + +/** Missing on uclibc */ +#ifndef IPV6_XFRM_POLICY +#define IPV6_XFRM_POLICY 34 +#endif /*IPV6_XFRM_POLICY*/ + +/* from linux/udp.h */ +#ifndef UDP_ENCAP +#define UDP_ENCAP 100 +#endif + +#ifndef UDP_ENCAP_ESPINUDP +#define UDP_ENCAP_ESPINUDP 2 +#endif + +/* this is not defined on some platforms */ +#ifndef SOL_UDP +#define SOL_UDP IPPROTO_UDP +#endif + +/** Base priority for installed policies */ +#define PRIO_BASE 384 + +/** Default lifetime of an acquire XFRM state (in seconds) */ +#define DEFAULT_ACQUIRE_LIFETIME 165 + +/** + * Map the limit for bytes and packets to XFRM_INF by default + */ +#define XFRM_LIMIT(x) ((x) == 0 ? XFRM_INF : (x)) + +/** + * Create ORable bitfield of XFRM NL groups + */ +#define XFRMNLGRP(x) (1<<(XFRMNLGRP_##x-1)) + +/** + * Returns a pointer to the first rtattr following the nlmsghdr *nlh and the + * 'usual' netlink data x like 'struct xfrm_usersa_info' + */ +#define XFRM_RTA(nlh, x) ((struct rtattr*)(NLMSG_DATA(nlh) + \ + NLMSG_ALIGN(sizeof(x)))) +/** + * Returns the total size of attached rta data + * (after 'usual' netlink data x like 'struct xfrm_usersa_info') + */ +#define XFRM_PAYLOAD(nlh, x) NLMSG_PAYLOAD(nlh, sizeof(x)) + +typedef struct kernel_algorithm_t kernel_algorithm_t; + +/** + * Mapping of IKEv2 kernel identifier to linux crypto API names + */ +struct kernel_algorithm_t { + /** + * Identifier specified in IKEv2 + */ + int ikev2; + + /** + * Name of the algorithm in linux crypto API + */ + char *name; +}; + +ENUM(xfrm_msg_names, XFRM_MSG_NEWSA, XFRM_MSG_MAPPING, + "XFRM_MSG_NEWSA", + "XFRM_MSG_DELSA", + "XFRM_MSG_GETSA", + "XFRM_MSG_NEWPOLICY", + "XFRM_MSG_DELPOLICY", + "XFRM_MSG_GETPOLICY", + "XFRM_MSG_ALLOCSPI", + "XFRM_MSG_ACQUIRE", + "XFRM_MSG_EXPIRE", + "XFRM_MSG_UPDPOLICY", + "XFRM_MSG_UPDSA", + "XFRM_MSG_POLEXPIRE", + "XFRM_MSG_FLUSHSA", + "XFRM_MSG_FLUSHPOLICY", + "XFRM_MSG_NEWAE", + "XFRM_MSG_GETAE", + "XFRM_MSG_REPORT", + "XFRM_MSG_MIGRATE", + "XFRM_MSG_NEWSADINFO", + "XFRM_MSG_GETSADINFO", + "XFRM_MSG_NEWSPDINFO", + "XFRM_MSG_GETSPDINFO", + "XFRM_MSG_MAPPING" +); + +ENUM(xfrm_attr_type_names, XFRMA_UNSPEC, XFRMA_REPLAY_ESN_VAL, + "XFRMA_UNSPEC", + "XFRMA_ALG_AUTH", + "XFRMA_ALG_CRYPT", + "XFRMA_ALG_COMP", + "XFRMA_ENCAP", + "XFRMA_TMPL", + "XFRMA_SA", + "XFRMA_POLICY", + "XFRMA_SEC_CTX", + "XFRMA_LTIME_VAL", + "XFRMA_REPLAY_VAL", + "XFRMA_REPLAY_THRESH", + "XFRMA_ETIMER_THRESH", + "XFRMA_SRCADDR", + "XFRMA_COADDR", + "XFRMA_LASTUSED", + "XFRMA_POLICY_TYPE", + "XFRMA_MIGRATE", + "XFRMA_ALG_AEAD", + "XFRMA_KMADDRESS", + "XFRMA_ALG_AUTH_TRUNC", + "XFRMA_MARK", + "XFRMA_TFCPAD", + "XFRMA_REPLAY_ESN_VAL", +); + +/** + * Algorithms for encryption + */ +static kernel_algorithm_t encryption_algs[] = { +/* {ENCR_DES_IV64, "***" }, */ + {ENCR_DES, "des" }, + {ENCR_3DES, "des3_ede" }, +/* {ENCR_RC5, "***" }, */ +/* {ENCR_IDEA, "***" }, */ + {ENCR_CAST, "cast5" }, + {ENCR_BLOWFISH, "blowfish" }, +/* {ENCR_3IDEA, "***" }, */ +/* {ENCR_DES_IV32, "***" }, */ + {ENCR_NULL, "cipher_null" }, + {ENCR_AES_CBC, "aes" }, + {ENCR_AES_CTR, "rfc3686(ctr(aes))" }, + {ENCR_AES_CCM_ICV8, "rfc4309(ccm(aes))" }, + {ENCR_AES_CCM_ICV12, "rfc4309(ccm(aes))" }, + {ENCR_AES_CCM_ICV16, "rfc4309(ccm(aes))" }, + {ENCR_AES_GCM_ICV8, "rfc4106(gcm(aes))" }, + {ENCR_AES_GCM_ICV12, "rfc4106(gcm(aes))" }, + {ENCR_AES_GCM_ICV16, "rfc4106(gcm(aes))" }, + {ENCR_NULL_AUTH_AES_GMAC, "rfc4543(gcm(aes))" }, + {ENCR_CAMELLIA_CBC, "cbc(camellia)" }, +/* {ENCR_CAMELLIA_CTR, "***" }, */ +/* {ENCR_CAMELLIA_CCM_ICV8, "***" }, */ +/* {ENCR_CAMELLIA_CCM_ICV12, "***" }, */ +/* {ENCR_CAMELLIA_CCM_ICV16, "***" }, */ + {ENCR_SERPENT_CBC, "serpent" }, + {ENCR_TWOFISH_CBC, "twofish" }, + {ENCR_CHACHA20_POLY1305, "rfc7539esp(chacha20,poly1305)"}, +}; + +/** + * Algorithms for integrity protection + */ +static kernel_algorithm_t integrity_algs[] = { + {AUTH_HMAC_MD5_96, "md5" }, + {AUTH_HMAC_MD5_128, "hmac(md5)" }, + {AUTH_HMAC_SHA1_96, "sha1" }, + {AUTH_HMAC_SHA1_160, "hmac(sha1)" }, + {AUTH_HMAC_SHA2_256_96, "sha256" }, + {AUTH_HMAC_SHA2_256_128, "hmac(sha256)" }, + {AUTH_HMAC_SHA2_384_192, "hmac(sha384)" }, + {AUTH_HMAC_SHA2_512_256, "hmac(sha512)" }, +/* {AUTH_DES_MAC, "***" }, */ +/* {AUTH_KPDK_MD5, "***" }, */ + {AUTH_AES_XCBC_96, "xcbc(aes)" }, +}; + +/** + * Algorithms for IPComp + */ +static kernel_algorithm_t compression_algs[] = { +/* {IPCOMP_OUI, "***" }, */ + {IPCOMP_DEFLATE, "deflate" }, + {IPCOMP_LZS, "lzs" }, + {IPCOMP_LZJH, "lzjh" }, +}; + +/** + * Look up a kernel algorithm name and its key size + */ +static char* lookup_algorithm(transform_type_t type, int ikev2) +{ + kernel_algorithm_t *list; + int i, count; + char *name; + + switch (type) + { + case ENCRYPTION_ALGORITHM: + list = encryption_algs; + count = countof(encryption_algs); + break; + case INTEGRITY_ALGORITHM: + list = integrity_algs; + count = countof(integrity_algs); + break; + case COMPRESSION_ALGORITHM: + list = compression_algs; + count = countof(compression_algs); + break; + default: + return NULL; + } + for (i = 0; i < count; i++) + { + if (list[i].ikev2 == ikev2) + { + return list[i].name; + } + } + if (charon->kernel->lookup_algorithm(charon->kernel, ikev2, type, NULL, + &name)) + { + return name; + } + return NULL; +} + +typedef struct private_kernel_netlink_ipsec_t private_kernel_netlink_ipsec_t; + +/** + * Private variables and functions of kernel_netlink class. + */ +struct private_kernel_netlink_ipsec_t { + /** + * Public part of the kernel_netlink_t object + */ + kernel_netlink_ipsec_t public; + + /** + * Mutex to lock access to installed policies + */ + mutex_t *mutex; + + /** + * Hash table of installed policies (policy_entry_t) + */ + hashtable_t *policies; + + /** + * Hash table of IPsec SAs using policies (ipsec_sa_t) + */ + hashtable_t *sas; + + /** + * Netlink xfrm socket (IPsec) + */ + netlink_socket_t *socket_xfrm; + + /** + * Netlink xfrm socket to receive acquire and expire events + */ + int socket_xfrm_events; + + /** + * Whether to install routes along policies + */ + bool install_routes; + + /** + * Whether to set protocol and ports on selector installed with transport + * mode IPsec SAs + */ + bool proto_port_transport; + + /** + * Whether to always use UPDATE to install policies + */ + bool policy_update; + + /** + * Installed port based IKE bypass policies, as bypass_t + */ + array_t *bypass; +}; + +typedef struct route_entry_t route_entry_t; + +/** + * Installed routing entry + */ +struct route_entry_t { + /** Name of the interface the route is bound to */ + char *if_name; + + /** Source ip of the route */ + host_t *src_ip; + + /** Gateway for this route */ + host_t *gateway; + + /** Destination net */ + chunk_t dst_net; + + /** Destination net prefixlen */ + u_int8_t prefixlen; +}; + +/** + * Destroy a route_entry_t object + */ +static void route_entry_destroy(route_entry_t *this) +{ + free(this->if_name); + this->src_ip->destroy(this->src_ip); + DESTROY_IF(this->gateway); + chunk_free(&this->dst_net); + free(this); +} + +/** + * Compare two route_entry_t objects + */ +static bool route_entry_equals(route_entry_t *a, route_entry_t *b) +{ + return a->if_name && b->if_name && streq(a->if_name, b->if_name) && + a->src_ip->ip_equals(a->src_ip, b->src_ip) && + a->gateway->ip_equals(a->gateway, b->gateway) && + chunk_equals(a->dst_net, b->dst_net) && a->prefixlen == b->prefixlen; +} + +typedef struct ipsec_sa_t ipsec_sa_t; + +/** + * IPsec SA assigned to a policy. + */ +struct ipsec_sa_t { + /** Source address of this SA */ + host_t *src; + + /** Destination address of this SA */ + host_t *dst; + + /** Optional mark */ + mark_t mark; + + /** Description of this SA */ + ipsec_sa_cfg_t cfg; + + /** Reference count for this SA */ + refcount_t refcount; +}; + +/** + * Hash function for ipsec_sa_t objects + */ +static u_int ipsec_sa_hash(ipsec_sa_t *sa) +{ + return chunk_hash_inc(sa->src->get_address(sa->src), + chunk_hash_inc(sa->dst->get_address(sa->dst), + chunk_hash_inc(chunk_from_thing(sa->mark), + chunk_hash(chunk_from_thing(sa->cfg))))); +} + +/** + * Equality function for ipsec_sa_t objects + */ +static bool ipsec_sa_equals(ipsec_sa_t *sa, ipsec_sa_t *other_sa) +{ + return sa->src->ip_equals(sa->src, other_sa->src) && + sa->dst->ip_equals(sa->dst, other_sa->dst) && + memeq(&sa->mark, &other_sa->mark, sizeof(mark_t)) && + memeq(&sa->cfg, &other_sa->cfg, sizeof(ipsec_sa_cfg_t)); +} + +/** + * Allocate or reference an IPsec SA object + */ +static ipsec_sa_t *ipsec_sa_create(private_kernel_netlink_ipsec_t *this, + host_t *src, host_t *dst, mark_t mark, + ipsec_sa_cfg_t *cfg) +{ + ipsec_sa_t *sa, *found; + INIT(sa, + .src = src, + .dst = dst, + .mark = mark, + .cfg = *cfg, + ); + found = this->sas->get(this->sas, sa); + if (!found) + { + sa->src = src->clone(src); + sa->dst = dst->clone(dst); + this->sas->put(this->sas, sa, sa); + } + else + { + free(sa); + sa = found; + } + ref_get(&sa->refcount); + return sa; +} + +/** + * Release and destroy an IPsec SA object + */ +static void ipsec_sa_destroy(private_kernel_netlink_ipsec_t *this, + ipsec_sa_t *sa) +{ + if (ref_put(&sa->refcount)) + { + this->sas->remove(this->sas, sa); + DESTROY_IF(sa->src); + DESTROY_IF(sa->dst); + free(sa); + } +} + +typedef struct policy_sa_t policy_sa_t; +typedef struct policy_sa_fwd_t policy_sa_fwd_t; + +/** + * Mapping between a policy and an IPsec SA. + */ +struct policy_sa_t { + /** Priority assigned to the policy when installed with this SA */ + u_int32_t priority; + + /** Type of the policy */ + policy_type_t type; + + /** Assigned SA */ + ipsec_sa_t *sa; +}; + +/** + * For forward policies we also cache the traffic selectors in order to install + * the route. + */ +struct policy_sa_fwd_t { + /** Generic interface */ + policy_sa_t generic; + + /** Source traffic selector of this policy */ + traffic_selector_t *src_ts; + + /** Destination traffic selector of this policy */ + traffic_selector_t *dst_ts; +}; + +/** + * Create a policy_sa(_fwd)_t object + */ +static policy_sa_t *policy_sa_create(private_kernel_netlink_ipsec_t *this, + policy_dir_t dir, policy_type_t type, host_t *src, host_t *dst, + traffic_selector_t *src_ts, traffic_selector_t *dst_ts, mark_t mark, + ipsec_sa_cfg_t *cfg) +{ + policy_sa_t *policy; + + if (dir == POLICY_FWD) + { + policy_sa_fwd_t *fwd; + INIT(fwd, + .src_ts = src_ts->clone(src_ts), + .dst_ts = dst_ts->clone(dst_ts), + ); + policy = &fwd->generic; + } + else + { + INIT(policy, .priority = 0); + } + policy->type = type; + policy->sa = ipsec_sa_create(this, src, dst, mark, cfg); + return policy; +} + +/** + * Destroy a policy_sa(_fwd)_t object + */ +static void policy_sa_destroy(policy_sa_t *policy, policy_dir_t *dir, + private_kernel_netlink_ipsec_t *this) +{ + if (*dir == POLICY_FWD) + { + policy_sa_fwd_t *fwd = (policy_sa_fwd_t*)policy; + fwd->src_ts->destroy(fwd->src_ts); + fwd->dst_ts->destroy(fwd->dst_ts); + } + ipsec_sa_destroy(this, policy->sa); + free(policy); +} + +typedef struct policy_entry_t policy_entry_t; + +/** + * Installed kernel policy. + */ +struct policy_entry_t { + + /** Direction of this policy: in, out, forward */ + u_int8_t direction; + + /** Parameters of installed policy */ + struct xfrm_selector sel; + + /** Optional mark */ + u_int32_t mark; + + /** Associated route installed for this policy */ + route_entry_t *route; + + /** List of SAs this policy is used by, ordered by priority */ + linked_list_t *used_by; + + /** reqid for this policy */ + u_int32_t reqid; +}; + +/** + * Destroy a policy_entry_t object + */ +static void policy_entry_destroy(private_kernel_netlink_ipsec_t *this, + policy_entry_t *policy) +{ + if (policy->route) + { + route_entry_destroy(policy->route); + } + if (policy->used_by) + { + policy->used_by->invoke_function(policy->used_by, + (linked_list_invoke_t)policy_sa_destroy, + &policy->direction, this); + policy->used_by->destroy(policy->used_by); + } + free(policy); +} + +/** + * Hash function for policy_entry_t objects + */ +static u_int policy_hash(policy_entry_t *key) +{ + chunk_t chunk = chunk_from_thing(key->sel); + return chunk_hash_inc(chunk, chunk_hash(chunk_from_thing(key->mark))); +} + +/** + * Equality function for policy_entry_t objects + */ +static bool policy_equals(policy_entry_t *key, policy_entry_t *other_key) +{ + return memeq(&key->sel, &other_key->sel, sizeof(struct xfrm_selector)) && + key->mark == other_key->mark && + key->direction == other_key->direction; +} + +/** + * Calculate the priority of a policy + */ +static inline u_int32_t get_priority(policy_entry_t *policy, + policy_priority_t prio) +{ + u_int32_t priority = PRIO_BASE; + switch (prio) + { + case POLICY_PRIORITY_FALLBACK: + priority <<= 1; + /* fall-through */ + case POLICY_PRIORITY_ROUTED: + priority <<= 1; + /* fall-through */ + case POLICY_PRIORITY_DEFAULT: + priority <<= 1; + /* fall-through */ + case POLICY_PRIORITY_PASS: + break; + } + /* calculate priority based on selector size, small size = high prio */ + priority -= policy->sel.prefixlen_s; + priority -= policy->sel.prefixlen_d; + priority <<= 2; /* make some room for the two flags */ + priority += policy->sel.sport_mask || policy->sel.dport_mask ? 0 : 2; + priority += policy->sel.proto ? 0 : 1; + return priority; +} + +/** + * Convert the general ipsec mode to the one defined in xfrm.h + */ +static u_int8_t mode2kernel(ipsec_mode_t mode) +{ + switch (mode) + { + case MODE_TRANSPORT: + return XFRM_MODE_TRANSPORT; + case MODE_TUNNEL: + return XFRM_MODE_TUNNEL; + case MODE_BEET: + return XFRM_MODE_BEET; + default: + return mode; + } +} + +/** + * Convert a host_t to a struct xfrm_address + */ +static void host2xfrm(host_t *host, xfrm_address_t *xfrm) +{ + chunk_t chunk = host->get_address(host); + memcpy(xfrm, chunk.ptr, min(chunk.len, sizeof(xfrm_address_t))); +} + +/** + * Convert a struct xfrm_address to a host_t + */ +static host_t* xfrm2host(int family, xfrm_address_t *xfrm, u_int16_t port) +{ + chunk_t chunk; + + switch (family) + { + case AF_INET: + chunk = chunk_create((u_char*)&xfrm->a4, sizeof(xfrm->a4)); + break; + case AF_INET6: + chunk = chunk_create((u_char*)&xfrm->a6, sizeof(xfrm->a6)); + break; + default: + return NULL; + } + return host_create_from_chunk(family, chunk, ntohs(port)); +} + +/** + * Convert a traffic selector address range to subnet and its mask. + */ +static void ts2subnet(traffic_selector_t* ts, + xfrm_address_t *net, u_int8_t *mask) +{ + host_t *net_host; + chunk_t net_chunk; + + ts->to_subnet(ts, &net_host, mask); + net_chunk = net_host->get_address(net_host); + memcpy(net, net_chunk.ptr, net_chunk.len); + net_host->destroy(net_host); +} + +/** + * Convert a traffic selector port range to port/portmask + */ +static void ts2ports(traffic_selector_t* ts, + u_int16_t *port, u_int16_t *mask) +{ + uint16_t from, to, bitmask; + int bit; + + from = ts->get_from_port(ts); + to = ts->get_to_port(ts); + + /* Quick check for a single port */ + if (from == to) + { + *port = htons(from); + *mask = ~0; + } + else + { + /* Compute the port mask for port ranges */ + *mask = 0; + + for (bit = 15; bit >= 0; bit--) + { + bitmask = 1 << bit; + + if ((bitmask & from) != (bitmask & to)) + { + *port = htons(from & *mask); + *mask = htons(*mask); + return; + } + *mask |= bitmask; + } + } + return; +} + +/** + * Convert a pair of traffic_selectors to an xfrm_selector + */ +static struct xfrm_selector ts2selector(traffic_selector_t *src, + traffic_selector_t *dst) +{ + struct xfrm_selector sel; + u_int16_t port; + + memset(&sel, 0, sizeof(sel)); + sel.family = (src->get_type(src) == TS_IPV4_ADDR_RANGE) ? AF_INET : AF_INET6; + /* src or dest proto may be "any" (0), use more restrictive one */ + sel.proto = max(src->get_protocol(src), dst->get_protocol(dst)); + ts2subnet(dst, &sel.daddr, &sel.prefixlen_d); + ts2subnet(src, &sel.saddr, &sel.prefixlen_s); + ts2ports(dst, &sel.dport, &sel.dport_mask); + ts2ports(src, &sel.sport, &sel.sport_mask); + if ((sel.proto == IPPROTO_ICMP || sel.proto == IPPROTO_ICMPV6) && + (sel.dport || sel.sport)) + { + /* the kernel expects the ICMP type and code in the source and + * destination port fields, respectively. */ + port = ntohs(max(sel.dport, sel.sport)); + sel.sport = htons(traffic_selector_icmp_type(port)); + sel.sport_mask = sel.sport ? ~0 : 0; + sel.dport = htons(traffic_selector_icmp_code(port)); + sel.dport_mask = sel.dport ? ~0 : 0; + } + sel.ifindex = 0; + sel.user = 0; + + return sel; +} + +/** + * Convert an xfrm_selector to a src|dst traffic_selector + */ +static traffic_selector_t* selector2ts(struct xfrm_selector *sel, bool src) +{ + u_char *addr; + u_int8_t prefixlen; + u_int16_t port = 0; + host_t *host = NULL; + + if (src) + { + addr = (u_char*)&sel->saddr; + prefixlen = sel->prefixlen_s; + if (sel->sport_mask) + { + port = ntohs(sel->sport); + } + } + else + { + addr = (u_char*)&sel->daddr; + prefixlen = sel->prefixlen_d; + if (sel->dport_mask) + { + port = ntohs(sel->dport); + } + } + if (sel->proto == IPPROTO_ICMP || sel->proto == IPPROTO_ICMPV6) + { /* convert ICMP[v6] message type and code as supplied by the kernel in + * source and destination ports (both in network order) */ + port = (sel->sport >> 8) | (sel->dport & 0xff00); + port = ntohs(port); + } + /* The Linux 2.6 kernel does not set the selector's family field, + * so as a kludge we additionally test the prefix length. + */ + if (sel->family == AF_INET || sel->prefixlen_s == 32) + { + host = host_create_from_chunk(AF_INET, chunk_create(addr, 4), 0); + } + else if (sel->family == AF_INET6 || sel->prefixlen_s == 128) + { + host = host_create_from_chunk(AF_INET6, chunk_create(addr, 16), 0); + } + + if (host) + { + return traffic_selector_create_from_subnet(host, prefixlen, + sel->proto, port, port ?: 65535); + } + return NULL; +} + +/** + * Process a XFRM_MSG_ACQUIRE from kernel + */ +static void process_acquire(private_kernel_netlink_ipsec_t *this, + struct nlmsghdr *hdr) +{ + struct xfrm_user_acquire *acquire; + struct rtattr *rta; + size_t rtasize; + traffic_selector_t *src_ts, *dst_ts; + u_int32_t reqid = 0; + int proto = 0; + + acquire = NLMSG_DATA(hdr); + rta = XFRM_RTA(hdr, struct xfrm_user_acquire); + rtasize = XFRM_PAYLOAD(hdr, struct xfrm_user_acquire); + + DBG2(DBG_KNL, "received a XFRM_MSG_ACQUIRE"); + + while (RTA_OK(rta, rtasize)) + { + DBG2(DBG_KNL, " %N", xfrm_attr_type_names, rta->rta_type); + + if (rta->rta_type == XFRMA_TMPL) + { + struct xfrm_user_tmpl* tmpl; + tmpl = (struct xfrm_user_tmpl*)RTA_DATA(rta); + reqid = tmpl->reqid; + proto = tmpl->id.proto; + } + rta = RTA_NEXT(rta, rtasize); + } + switch (proto) + { + case 0: + case IPPROTO_ESP: + case IPPROTO_AH: + break; + default: + /* acquire for AH/ESP only, not for IPCOMP */ + return; + } + src_ts = selector2ts(&acquire->sel, TRUE); + dst_ts = selector2ts(&acquire->sel, FALSE); + + charon->kernel->acquire(charon->kernel, reqid, src_ts, dst_ts); +} + +/** + * Process a XFRM_MSG_EXPIRE from kernel + */ +static void process_expire(private_kernel_netlink_ipsec_t *this, + struct nlmsghdr *hdr) +{ + struct xfrm_user_expire *expire; + u_int32_t spi; + u_int8_t protocol; + host_t *dst; + + expire = NLMSG_DATA(hdr); + protocol = expire->state.id.proto; + spi = expire->state.id.spi; + + DBG2(DBG_KNL, "received a XFRM_MSG_EXPIRE"); + + if (protocol == IPPROTO_ESP || protocol == IPPROTO_AH) + { + dst = xfrm2host(expire->state.family, &expire->state.id.daddr, 0); + if (dst) + { + charon->kernel->expire(charon->kernel, protocol, spi, dst, + expire->hard != 0); + dst->destroy(dst); + } + } +} + +/** + * Process a XFRM_MSG_MIGRATE from kernel + */ +static void process_migrate(private_kernel_netlink_ipsec_t *this, + struct nlmsghdr *hdr) +{ + struct xfrm_userpolicy_id *policy_id; + struct rtattr *rta; + size_t rtasize; + traffic_selector_t *src_ts, *dst_ts; + host_t *local = NULL, *remote = NULL; + host_t *old_src = NULL, *old_dst = NULL; + host_t *new_src = NULL, *new_dst = NULL; + u_int32_t reqid = 0; + policy_dir_t dir; + + policy_id = NLMSG_DATA(hdr); + rta = XFRM_RTA(hdr, struct xfrm_userpolicy_id); + rtasize = XFRM_PAYLOAD(hdr, struct xfrm_userpolicy_id); + + DBG2(DBG_KNL, "received a XFRM_MSG_MIGRATE"); + + src_ts = selector2ts(&policy_id->sel, TRUE); + dst_ts = selector2ts(&policy_id->sel, FALSE); + dir = (policy_dir_t)policy_id->dir; + + DBG2(DBG_KNL, " policy: %R === %R %N", src_ts, dst_ts, policy_dir_names); + + while (RTA_OK(rta, rtasize)) + { + DBG2(DBG_KNL, " %N", xfrm_attr_type_names, rta->rta_type); + if (rta->rta_type == XFRMA_KMADDRESS) + { + struct xfrm_user_kmaddress *kmaddress; + + kmaddress = (struct xfrm_user_kmaddress*)RTA_DATA(rta); + local = xfrm2host(kmaddress->family, &kmaddress->local, 0); + remote = xfrm2host(kmaddress->family, &kmaddress->remote, 0); + DBG2(DBG_KNL, " kmaddress: %H...%H", local, remote); + } + else if (rta->rta_type == XFRMA_MIGRATE) + { + struct xfrm_user_migrate *migrate; + + migrate = (struct xfrm_user_migrate*)RTA_DATA(rta); + old_src = xfrm2host(migrate->old_family, &migrate->old_saddr, 0); + old_dst = xfrm2host(migrate->old_family, &migrate->old_daddr, 0); + new_src = xfrm2host(migrate->new_family, &migrate->new_saddr, 0); + new_dst = xfrm2host(migrate->new_family, &migrate->new_daddr, 0); + reqid = migrate->reqid; + DBG2(DBG_KNL, " migrate %H...%H to %H...%H, reqid {%u}", + old_src, old_dst, new_src, new_dst, reqid); + DESTROY_IF(old_src); + DESTROY_IF(old_dst); + DESTROY_IF(new_src); + DESTROY_IF(new_dst); + } + rta = RTA_NEXT(rta, rtasize); + } + + if (src_ts && dst_ts && local && remote) + { + charon->kernel->migrate(charon->kernel, reqid, src_ts, dst_ts, dir, + local, remote); + } + else + { + DESTROY_IF(src_ts); + DESTROY_IF(dst_ts); + DESTROY_IF(local); + DESTROY_IF(remote); + } +} + +/** + * Process a XFRM_MSG_MAPPING from kernel + */ +static void process_mapping(private_kernel_netlink_ipsec_t *this, + struct nlmsghdr *hdr) +{ + struct xfrm_user_mapping *mapping; + u_int32_t spi; + + mapping = NLMSG_DATA(hdr); + spi = mapping->id.spi; + + DBG2(DBG_KNL, "received a XFRM_MSG_MAPPING"); + + if (mapping->id.proto == IPPROTO_ESP) + { + host_t *dst, *new; + + dst = xfrm2host(mapping->id.family, &mapping->id.daddr, 0); + if (dst) + { + new = xfrm2host(mapping->id.family, &mapping->new_saddr, + mapping->new_sport); + if (new) + { + charon->kernel->mapping(charon->kernel, IPPROTO_ESP, spi, dst, + new); + new->destroy(new); + } + dst->destroy(dst); + } + } +} + +/** + * Receives events from kernel + */ +static bool receive_events(private_kernel_netlink_ipsec_t *this, int fd, + watcher_event_t event) +{ + char response[1024]; + struct nlmsghdr *hdr = (struct nlmsghdr*)response; + struct sockaddr_nl addr; + socklen_t addr_len = sizeof(addr); + int len; + + len = recvfrom(this->socket_xfrm_events, response, sizeof(response), + MSG_DONTWAIT, (struct sockaddr*)&addr, &addr_len); + if (len < 0) + { + switch (errno) + { + case EINTR: + /* interrupted, try again */ + return TRUE; + case EAGAIN: + /* no data ready, select again */ + return TRUE; + default: + DBG1(DBG_KNL, "unable to receive from xfrm event socket"); + sleep(1); + return TRUE; + } + } + + if (addr.nl_pid != 0) + { /* not from kernel. not interested, try another one */ + return TRUE; + } + + while (NLMSG_OK(hdr, len)) + { + switch (hdr->nlmsg_type) + { + case XFRM_MSG_ACQUIRE: + process_acquire(this, hdr); + break; + case XFRM_MSG_EXPIRE: + process_expire(this, hdr); + break; + case XFRM_MSG_MIGRATE: + process_migrate(this, hdr); + break; + case XFRM_MSG_MAPPING: + process_mapping(this, hdr); + break; + default: + DBG1(DBG_KNL, "received unknown event from xfrm event " + "socket: %d", hdr->nlmsg_type); + break; + } + hdr = NLMSG_NEXT(hdr, len); + } + return TRUE; +} + +METHOD(kernel_ipsec_t, get_features, kernel_feature_t, + private_kernel_netlink_ipsec_t *this) +{ + return KERNEL_ESP_V3_TFC; +} + +/** + * Get an SPI for a specific protocol from the kernel. + */ +static status_t get_spi_internal(private_kernel_netlink_ipsec_t *this, + host_t *src, host_t *dst, u_int8_t proto, u_int32_t min, u_int32_t max, + u_int32_t *spi) +{ + netlink_buf_t request; + struct nlmsghdr *hdr, *out; + struct xfrm_userspi_info *userspi; + u_int32_t received_spi = 0; + size_t len; + + memset(&request, 0, sizeof(request)); + + hdr = &request.hdr; + hdr->nlmsg_flags = NLM_F_REQUEST; + hdr->nlmsg_type = XFRM_MSG_ALLOCSPI; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userspi_info)); + + userspi = NLMSG_DATA(hdr); + host2xfrm(src, &userspi->info.saddr); + host2xfrm(dst, &userspi->info.id.daddr); + userspi->info.id.proto = proto; + userspi->info.mode = XFRM_MODE_TUNNEL; + userspi->info.family = src->get_family(src); + userspi->min = min; + userspi->max = max; + + if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) + { + hdr = out; + while (NLMSG_OK(hdr, len)) + { + switch (hdr->nlmsg_type) + { + case XFRM_MSG_NEWSA: + { + struct xfrm_usersa_info* usersa = NLMSG_DATA(hdr); + received_spi = usersa->id.spi; + break; + } + case NLMSG_ERROR: + { + struct nlmsgerr *err = NLMSG_DATA(hdr); + DBG1(DBG_KNL, "allocating SPI failed: %s (%d)", + strerror(-err->error), -err->error); + break; + } + default: + hdr = NLMSG_NEXT(hdr, len); + continue; + case NLMSG_DONE: + break; + } + break; + } + free(out); + } + + if (received_spi == 0) + { + return FAILED; + } + + *spi = received_spi; + return SUCCESS; +} + +METHOD(kernel_ipsec_t, get_spi, status_t, + private_kernel_netlink_ipsec_t *this, host_t *src, host_t *dst, + u_int8_t protocol, u_int32_t *spi) +{ + if (get_spi_internal(this, src, dst, protocol, + 0xc0000000, 0xcFFFFFFF, spi) != SUCCESS) + { + DBG1(DBG_KNL, "unable to get SPI"); + return FAILED; + } + + DBG2(DBG_KNL, "got SPI %.8x", ntohl(*spi)); + return SUCCESS; +} + +METHOD(kernel_ipsec_t, get_cpi, status_t, + private_kernel_netlink_ipsec_t *this, host_t *src, host_t *dst, + u_int16_t *cpi) +{ + u_int32_t received_spi = 0; + + if (get_spi_internal(this, src, dst, IPPROTO_COMP, + 0x100, 0xEFFF, &received_spi) != SUCCESS) + { + DBG1(DBG_KNL, "unable to get CPI"); + return FAILED; + } + + *cpi = htons((u_int16_t)ntohl(received_spi)); + + DBG2(DBG_KNL, "got CPI %.4x", ntohs(*cpi)); + return SUCCESS; +} + +/** + * Add a XFRM mark to message if required + */ +static bool add_mark(struct nlmsghdr *hdr, int buflen, mark_t mark) +{ + if (mark.value) + { + struct xfrm_mark *xmrk; + + xmrk = netlink_reserve(hdr, buflen, XFRMA_MARK, sizeof(*xmrk)); + if (!xmrk) + { + return FALSE; + } + xmrk->v = mark.value; + xmrk->m = mark.mask; + } + return TRUE; +} + +METHOD(kernel_ipsec_t, add_sa, status_t, + private_kernel_netlink_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, bool update, + linked_list_t* src_ts, linked_list_t* dst_ts) +{ + netlink_buf_t request; + char *alg_name; + struct nlmsghdr *hdr; + struct xfrm_usersa_info *sa; + u_int16_t icv_size = 64; + ipsec_mode_t original_mode = mode; + traffic_selector_t *first_src_ts, *first_dst_ts; + status_t status = FAILED; + + /* if IPComp is used, we install an additional IPComp SA. if the cpi is 0 + * we are in the recursive call below */ + if (ipcomp != IPCOMP_NONE && cpi != 0) + { + lifetime_cfg_t lft = {{0,0,0},{0,0,0},{0,0,0}}; + add_sa(this, src, dst, htonl(ntohs(cpi)), IPPROTO_COMP, reqid, mark, + tfc, &lft, ENCR_UNDEFINED, chunk_empty, AUTH_UNDEFINED, + chunk_empty, mode, ipcomp, 0, 0, initiator, FALSE, FALSE, + inbound, update, src_ts, dst_ts); + ipcomp = IPCOMP_NONE; + /* use transport mode ESP SA, IPComp uses tunnel mode */ + mode = MODE_TRANSPORT; + } + + memset(&request, 0, sizeof(request)); + + DBG2(DBG_KNL, "adding SAD entry with SPI %.8x and reqid {%u} (mark " + "%u/0x%08x)", ntohl(spi), reqid, mark.value, mark.mask); + + hdr = &request.hdr; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = update ? XFRM_MSG_UPDSA : XFRM_MSG_NEWSA; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_info)); + + sa = NLMSG_DATA(hdr); + host2xfrm(src, &sa->saddr); + host2xfrm(dst, &sa->id.daddr); + sa->id.spi = spi; + sa->id.proto = protocol; + sa->family = src->get_family(src); + sa->mode = mode2kernel(mode); + switch (mode) + { + case MODE_TUNNEL: + sa->flags |= XFRM_STATE_AF_UNSPEC; + break; + case MODE_BEET: + case MODE_TRANSPORT: + if (original_mode == MODE_TUNNEL) + { /* don't install selectors for switched SAs. because only one + * selector can be installed other traffic would get dropped */ + break; + } + if (src_ts->get_first(src_ts, (void**)&first_src_ts) == SUCCESS && + dst_ts->get_first(dst_ts, (void**)&first_dst_ts) == SUCCESS) + { + sa->sel = ts2selector(first_src_ts, first_dst_ts); + if (!this->proto_port_transport) + { + /* don't install proto/port on SA. This would break + * potential secondary SAs for the same address using a + * different prot/port. */ + sa->sel.proto = 0; + sa->sel.dport = sa->sel.dport_mask = 0; + sa->sel.sport = sa->sel.sport_mask = 0; + } + } + break; + default: + break; + } + + sa->reqid = reqid; + sa->lft.soft_byte_limit = XFRM_LIMIT(lifetime->bytes.rekey); + sa->lft.hard_byte_limit = XFRM_LIMIT(lifetime->bytes.life); + sa->lft.soft_packet_limit = XFRM_LIMIT(lifetime->packets.rekey); + sa->lft.hard_packet_limit = XFRM_LIMIT(lifetime->packets.life); + /* we use lifetimes since added, not since used */ + sa->lft.soft_add_expires_seconds = lifetime->time.rekey; + sa->lft.hard_add_expires_seconds = lifetime->time.life; + sa->lft.soft_use_expires_seconds = 0; + sa->lft.hard_use_expires_seconds = 0; + + switch (enc_alg) + { + case ENCR_UNDEFINED: + /* no encryption */ + break; + case ENCR_AES_CCM_ICV16: + case ENCR_AES_GCM_ICV16: + case ENCR_NULL_AUTH_AES_GMAC: + case ENCR_CAMELLIA_CCM_ICV16: + case ENCR_CHACHA20_POLY1305: + icv_size += 32; + /* FALL */ + case ENCR_AES_CCM_ICV12: + case ENCR_AES_GCM_ICV12: + case ENCR_CAMELLIA_CCM_ICV12: + icv_size += 32; + /* FALL */ + case ENCR_AES_CCM_ICV8: + case ENCR_AES_GCM_ICV8: + case ENCR_CAMELLIA_CCM_ICV8: + { + struct xfrm_algo_aead *algo; + + alg_name = lookup_algorithm(ENCRYPTION_ALGORITHM, enc_alg); + if (alg_name == NULL) + { + DBG1(DBG_KNL, "algorithm %N not supported by kernel!", + encryption_algorithm_names, enc_alg); + goto failed; + } + DBG2(DBG_KNL, " using encryption algorithm %N with key size %d", + encryption_algorithm_names, enc_alg, enc_key.len * 8); + + algo = netlink_reserve(hdr, sizeof(request), XFRMA_ALG_AEAD, + sizeof(*algo) + enc_key.len); + if (!algo) + { + goto failed; + } + algo->alg_key_len = enc_key.len * 8; + algo->alg_icv_len = icv_size; + strncpy(algo->alg_name, alg_name, sizeof(algo->alg_name)); + algo->alg_name[sizeof(algo->alg_name) - 1] = '\0'; + memcpy(algo->alg_key, enc_key.ptr, enc_key.len); + break; + } + default: + { + struct xfrm_algo *algo; + + alg_name = lookup_algorithm(ENCRYPTION_ALGORITHM, enc_alg); + if (alg_name == NULL) + { + DBG1(DBG_KNL, "algorithm %N not supported by kernel!", + encryption_algorithm_names, enc_alg); + goto failed; + } + DBG2(DBG_KNL, " using encryption algorithm %N with key size %d", + encryption_algorithm_names, enc_alg, enc_key.len * 8); + + algo = netlink_reserve(hdr, sizeof(request), XFRMA_ALG_CRYPT, + sizeof(*algo) + enc_key.len); + if (!algo) + { + goto failed; + } + algo->alg_key_len = enc_key.len * 8; + strncpy(algo->alg_name, alg_name, sizeof(algo->alg_name)); + algo->alg_name[sizeof(algo->alg_name) - 1] = '\0'; + memcpy(algo->alg_key, enc_key.ptr, enc_key.len); + } + } + + if (int_alg != AUTH_UNDEFINED) + { + u_int trunc_len = 0; + + alg_name = lookup_algorithm(INTEGRITY_ALGORITHM, int_alg); + if (alg_name == NULL) + { + DBG1(DBG_KNL, "algorithm %N not supported by kernel!", + integrity_algorithm_names, int_alg); + goto failed; + } + DBG2(DBG_KNL, " using integrity algorithm %N with key size %d", + integrity_algorithm_names, int_alg, int_key.len * 8); + + switch (int_alg) + { + case AUTH_HMAC_MD5_128: + case AUTH_HMAC_SHA2_256_128: + trunc_len = 128; + break; + case AUTH_HMAC_SHA1_160: + trunc_len = 160; + break; + default: + break; + } + + if (trunc_len) + { + struct xfrm_algo_auth* algo; + + /* the kernel uses SHA256 with 96 bit truncation by default, + * use specified truncation size supported by newer kernels. + * also use this for untruncated MD5 and SHA1. */ + algo = netlink_reserve(hdr, sizeof(request), XFRMA_ALG_AUTH_TRUNC, + sizeof(*algo) + int_key.len); + if (!algo) + { + goto failed; + } + algo->alg_key_len = int_key.len * 8; + algo->alg_trunc_len = trunc_len; + strncpy(algo->alg_name, alg_name, sizeof(algo->alg_name)); + algo->alg_name[sizeof(algo->alg_name) - 1] = '\0'; + memcpy(algo->alg_key, int_key.ptr, int_key.len); + } + else + { + struct xfrm_algo* algo; + + algo = netlink_reserve(hdr, sizeof(request), XFRMA_ALG_AUTH, + sizeof(*algo) + int_key.len); + if (!algo) + { + goto failed; + } + algo->alg_key_len = int_key.len * 8; + strncpy(algo->alg_name, alg_name, sizeof(algo->alg_name)); + algo->alg_name[sizeof(algo->alg_name) - 1] = '\0'; + memcpy(algo->alg_key, int_key.ptr, int_key.len); + } + } + + if (ipcomp != IPCOMP_NONE) + { + struct xfrm_algo* algo; + + alg_name = lookup_algorithm(COMPRESSION_ALGORITHM, ipcomp); + if (alg_name == NULL) + { + DBG1(DBG_KNL, "algorithm %N not supported by kernel!", + ipcomp_transform_names, ipcomp); + goto failed; + } + DBG2(DBG_KNL, " using compression algorithm %N", + ipcomp_transform_names, ipcomp); + + algo = netlink_reserve(hdr, sizeof(request), XFRMA_ALG_COMP, + sizeof(*algo)); + if (!algo) + { + goto failed; + } + algo->alg_key_len = 0; + strncpy(algo->alg_name, alg_name, sizeof(algo->alg_name)); + algo->alg_name[sizeof(algo->alg_name) - 1] = '\0'; + } + + if (encap) + { + struct xfrm_encap_tmpl *tmpl; + + tmpl = netlink_reserve(hdr, sizeof(request), XFRMA_ENCAP, sizeof(*tmpl)); + if (!tmpl) + { + goto failed; + } + tmpl->encap_type = UDP_ENCAP_ESPINUDP; + tmpl->encap_sport = htons(src->get_port(src)); + tmpl->encap_dport = htons(dst->get_port(dst)); + memset(&tmpl->encap_oa, 0, sizeof (xfrm_address_t)); + /* encap_oa could probably be derived from the + * traffic selectors [rfc4306, p39]. In the netlink kernel + * implementation pluto does the same as we do here but it uses + * encap_oa in the pfkey implementation. + * BUT as /usr/src/linux/net/key/af_key.c indicates the kernel ignores + * it anyway + * -> does that mean that NAT-T encap doesn't work in transport mode? + * No. The reason the kernel ignores NAT-OA is that it recomputes + * (or, rather, just ignores) the checksum. If packets pass the IPsec + * checks it marks them "checksum ok" so OA isn't needed. */ + } + + if (!add_mark(hdr, sizeof(request), mark)) + { + goto failed; + } + + if (tfc && protocol == IPPROTO_ESP && mode == MODE_TUNNEL) + { /* the kernel supports TFC padding only for tunnel mode ESP SAs */ + u_int32_t *tfcpad; + + tfcpad = netlink_reserve(hdr, sizeof(request), XFRMA_TFCPAD, + sizeof(*tfcpad)); + if (!tfcpad) + { + goto failed; + } + *tfcpad = tfc; + } + + if (protocol != IPPROTO_COMP) + { + if (replay_window != 0 && (esn || replay_window > 32)) + { + /* for ESN or larger replay windows we need the new + * XFRMA_REPLAY_ESN_VAL attribute to configure a bitmap */ + struct xfrm_replay_state_esn *replay; + u_int32_t bmp_size; + + bmp_size = round_up(replay_window, sizeof(u_int32_t) * 8) / 8; + replay = netlink_reserve(hdr, sizeof(request), XFRMA_REPLAY_ESN_VAL, + sizeof(*replay) + bmp_size); + if (!replay) + { + goto failed; + } + /* bmp_len contains number uf __u32's */ + replay->bmp_len = bmp_size / sizeof(u_int32_t); + replay->replay_window = replay_window; + DBG2(DBG_KNL, " using replay window of %u packets", replay_window); + + if (esn) + { + DBG2(DBG_KNL, " using extended sequence numbers (ESN)"); + sa->flags |= XFRM_STATE_ESN; + } + } + else + { + DBG2(DBG_KNL, " using replay window of %u packets", replay_window); + sa->replay_window = replay_window; + } + } + + if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS) + { + if (mark.value) + { + DBG1(DBG_KNL, "unable to add SAD entry with SPI %.8x " + "(mark %u/0x%08x)", ntohl(spi), mark.value, mark.mask); + } + else + { + DBG1(DBG_KNL, "unable to add SAD entry with SPI %.8x", ntohl(spi)); + } + goto failed; + } + + status = SUCCESS; + +failed: + memwipe(&request, sizeof(request)); + return status; +} + +/** + * Get the ESN replay state (i.e. sequence numbers) of an SA. + * + * Allocates into one the replay state structure we get from the kernel. + */ +static void get_replay_state(private_kernel_netlink_ipsec_t *this, + u_int32_t spi, u_int8_t protocol, + host_t *dst, mark_t mark, + struct xfrm_replay_state_esn **replay_esn, + u_int32_t *replay_esn_len, + struct xfrm_replay_state **replay, + struct xfrm_lifetime_cur **lifetime) +{ + netlink_buf_t request; + struct nlmsghdr *hdr, *out = NULL; + struct xfrm_aevent_id *out_aevent = NULL, *aevent_id; + size_t len; + struct rtattr *rta; + size_t rtasize; + + memset(&request, 0, sizeof(request)); + + DBG2(DBG_KNL, "querying replay state from SAD entry with SPI %.8x", + ntohl(spi)); + + hdr = &request.hdr; + hdr->nlmsg_flags = NLM_F_REQUEST; + hdr->nlmsg_type = XFRM_MSG_GETAE; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_aevent_id)); + + aevent_id = NLMSG_DATA(hdr); + aevent_id->flags = XFRM_AE_RVAL; + + host2xfrm(dst, &aevent_id->sa_id.daddr); + aevent_id->sa_id.spi = spi; + aevent_id->sa_id.proto = protocol; + aevent_id->sa_id.family = dst->get_family(dst); + + if (!add_mark(hdr, sizeof(request), mark)) + { + return; + } + + if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) + { + hdr = out; + while (NLMSG_OK(hdr, len)) + { + switch (hdr->nlmsg_type) + { + case XFRM_MSG_NEWAE: + { + out_aevent = NLMSG_DATA(hdr); + break; + } + case NLMSG_ERROR: + { + struct nlmsgerr *err = NLMSG_DATA(hdr); + DBG1(DBG_KNL, "querying replay state from SAD entry " + "failed: %s (%d)", strerror(-err->error), + -err->error); + break; + } + default: + hdr = NLMSG_NEXT(hdr, len); + continue; + case NLMSG_DONE: + break; + } + break; + } + } + + if (out_aevent) + { + rta = XFRM_RTA(out, struct xfrm_aevent_id); + rtasize = XFRM_PAYLOAD(out, struct xfrm_aevent_id); + while (RTA_OK(rta, rtasize)) + { + if (rta->rta_type == XFRMA_LTIME_VAL && + RTA_PAYLOAD(rta) == sizeof(**lifetime)) + { + free(*lifetime); + *lifetime = malloc(RTA_PAYLOAD(rta)); + memcpy(*lifetime, RTA_DATA(rta), RTA_PAYLOAD(rta)); + } + if (rta->rta_type == XFRMA_REPLAY_VAL && + RTA_PAYLOAD(rta) == sizeof(**replay)) + { + free(*replay); + *replay = malloc(RTA_PAYLOAD(rta)); + memcpy(*replay, RTA_DATA(rta), RTA_PAYLOAD(rta)); + } + if (rta->rta_type == XFRMA_REPLAY_ESN_VAL && + RTA_PAYLOAD(rta) >= sizeof(**replay_esn)) + { + free(*replay_esn); + *replay_esn = malloc(RTA_PAYLOAD(rta)); + *replay_esn_len = RTA_PAYLOAD(rta); + memcpy(*replay_esn, RTA_DATA(rta), RTA_PAYLOAD(rta)); + } + rta = RTA_NEXT(rta, rtasize); + } + } + free(out); +} + +METHOD(kernel_ipsec_t, query_sa, status_t, + private_kernel_netlink_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) +{ + netlink_buf_t request; + struct nlmsghdr *out = NULL, *hdr; + struct xfrm_usersa_id *sa_id; + struct xfrm_usersa_info *sa = NULL; + status_t status = FAILED; + size_t len; + + memset(&request, 0, sizeof(request)); + + DBG2(DBG_KNL, "querying SAD entry with SPI %.8x (mark %u/0x%08x)", + ntohl(spi), mark.value, mark.mask); + + hdr = &request.hdr; + hdr->nlmsg_flags = NLM_F_REQUEST; + hdr->nlmsg_type = XFRM_MSG_GETSA; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_id)); + + sa_id = NLMSG_DATA(hdr); + host2xfrm(dst, &sa_id->daddr); + sa_id->spi = spi; + sa_id->proto = protocol; + sa_id->family = dst->get_family(dst); + + if (!add_mark(hdr, sizeof(request), mark)) + { + return FAILED; + } + + if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) + { + hdr = out; + while (NLMSG_OK(hdr, len)) + { + switch (hdr->nlmsg_type) + { + case XFRM_MSG_NEWSA: + { + sa = NLMSG_DATA(hdr); + break; + } + case NLMSG_ERROR: + { + struct nlmsgerr *err = NLMSG_DATA(hdr); + + if (mark.value) + { + DBG1(DBG_KNL, "querying SAD entry with SPI %.8x " + "(mark %u/0x%08x) failed: %s (%d)", + ntohl(spi), mark.value, mark.mask, + strerror(-err->error), -err->error); + } + else + { + DBG1(DBG_KNL, "querying SAD entry with SPI %.8x " + "failed: %s (%d)", ntohl(spi), + strerror(-err->error), -err->error); + } + break; + } + default: + hdr = NLMSG_NEXT(hdr, len); + continue; + case NLMSG_DONE: + break; + } + break; + } + } + + if (sa == NULL) + { + DBG2(DBG_KNL, "unable to query SAD entry with SPI %.8x", ntohl(spi)); + } + else + { + if (bytes) + { + *bytes = sa->curlft.bytes; + } + if (packets) + { + *packets = sa->curlft.packets; + } + if (time) + { /* curlft contains an "use" time, but that contains a timestamp + * of the first use, not the last. Last use time must be queried + * on the policy on Linux */ + *time = 0; + } + status = SUCCESS; + } + memwipe(out, len); + free(out); + return status; +} + +METHOD(kernel_ipsec_t, del_sa, status_t, + private_kernel_netlink_ipsec_t *this, host_t *src, host_t *dst, + u_int32_t spi, u_int8_t protocol, u_int16_t cpi, mark_t mark) +{ + netlink_buf_t request; + struct nlmsghdr *hdr; + struct xfrm_usersa_id *sa_id; + + /* if IPComp was used, we first delete the additional IPComp SA */ + if (cpi) + { + del_sa(this, src, dst, htonl(ntohs(cpi)), IPPROTO_COMP, 0, mark); + } + + memset(&request, 0, sizeof(request)); + + DBG2(DBG_KNL, "deleting SAD entry with SPI %.8x (mark %u/0x%08x)", + ntohl(spi), mark.value, mark.mask); + + hdr = &request.hdr; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = XFRM_MSG_DELSA; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_id)); + + sa_id = NLMSG_DATA(hdr); + host2xfrm(dst, &sa_id->daddr); + sa_id->spi = spi; + sa_id->proto = protocol; + sa_id->family = dst->get_family(dst); + + if (!add_mark(hdr, sizeof(request), mark)) + { + return FAILED; + } + + switch (this->socket_xfrm->send_ack(this->socket_xfrm, hdr)) + { + case SUCCESS: + DBG2(DBG_KNL, "deleted SAD entry with SPI %.8x (mark %u/0x%08x)", + ntohl(spi), mark.value, mark.mask); + return SUCCESS; + case NOT_FOUND: + return NOT_FOUND; + default: + if (mark.value) + { + DBG1(DBG_KNL, "unable to delete SAD entry with SPI %.8x " + "(mark %u/0x%08x)", ntohl(spi), mark.value, mark.mask); + } + else + { + DBG1(DBG_KNL, "unable to delete SAD entry with SPI %.8x", + ntohl(spi)); + } + return FAILED; + } +} + +METHOD(kernel_ipsec_t, update_sa, status_t, + private_kernel_netlink_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 old_encap, bool new_encap, mark_t mark) +{ + netlink_buf_t request; + struct nlmsghdr *hdr, *out = NULL; + struct xfrm_usersa_id *sa_id; + struct xfrm_usersa_info *out_sa = NULL, *sa; + size_t len; + struct rtattr *rta; + size_t rtasize; + struct xfrm_encap_tmpl* tmpl = NULL; + struct xfrm_replay_state *replay = NULL; + struct xfrm_replay_state_esn *replay_esn = NULL; + struct xfrm_lifetime_cur *lifetime = NULL; + u_int32_t replay_esn_len = 0; + status_t status = FAILED; + + /* if IPComp is used, we first update the IPComp SA */ + if (cpi) + { + update_sa(this, htonl(ntohs(cpi)), IPPROTO_COMP, 0, + src, dst, new_src, new_dst, FALSE, FALSE, mark); + } + + memset(&request, 0, sizeof(request)); + + DBG2(DBG_KNL, "querying SAD entry with SPI %.8x for update", ntohl(spi)); + + /* query the existing SA first */ + hdr = &request.hdr; + hdr->nlmsg_flags = NLM_F_REQUEST; + hdr->nlmsg_type = XFRM_MSG_GETSA; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_id)); + + sa_id = NLMSG_DATA(hdr); + host2xfrm(dst, &sa_id->daddr); + sa_id->spi = spi; + sa_id->proto = protocol; + sa_id->family = dst->get_family(dst); + + if (!add_mark(hdr, sizeof(request), mark)) + { + return FAILED; + } + + if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) + { + hdr = out; + while (NLMSG_OK(hdr, len)) + { + switch (hdr->nlmsg_type) + { + case XFRM_MSG_NEWSA: + { + out_sa = NLMSG_DATA(hdr); + break; + } + case NLMSG_ERROR: + { + struct nlmsgerr *err = NLMSG_DATA(hdr); + DBG1(DBG_KNL, "querying SAD entry failed: %s (%d)", + strerror(-err->error), -err->error); + break; + } + default: + hdr = NLMSG_NEXT(hdr, len); + continue; + case NLMSG_DONE: + break; + } + break; + } + } + if (out_sa == NULL) + { + DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x", ntohl(spi)); + goto failed; + } + + get_replay_state(this, spi, protocol, dst, mark, &replay_esn, + &replay_esn_len, &replay, &lifetime); + + /* delete the old SA (without affecting the IPComp SA) */ + if (del_sa(this, src, dst, spi, protocol, 0, mark) != SUCCESS) + { + DBG1(DBG_KNL, "unable to delete old SAD entry with SPI %.8x", + ntohl(spi)); + goto failed; + } + + DBG2(DBG_KNL, "updating SAD entry with SPI %.8x from %#H..%#H to %#H..%#H", + ntohl(spi), src, dst, new_src, new_dst); + /* copy over the SA from out to request */ + hdr = &request.hdr; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = XFRM_MSG_NEWSA; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_info)); + sa = NLMSG_DATA(hdr); + memcpy(sa, NLMSG_DATA(out), sizeof(struct xfrm_usersa_info)); + sa->family = new_dst->get_family(new_dst); + + if (!src->ip_equals(src, new_src)) + { + host2xfrm(new_src, &sa->saddr); + } + if (!dst->ip_equals(dst, new_dst)) + { + host2xfrm(new_dst, &sa->id.daddr); + } + + rta = XFRM_RTA(out, struct xfrm_usersa_info); + rtasize = XFRM_PAYLOAD(out, struct xfrm_usersa_info); + while (RTA_OK(rta, rtasize)) + { + /* copy all attributes, but not XFRMA_ENCAP if we are disabling it */ + if (rta->rta_type != XFRMA_ENCAP || new_encap) + { + if (rta->rta_type == XFRMA_ENCAP) + { /* update encap tmpl */ + tmpl = RTA_DATA(rta); + tmpl->encap_sport = ntohs(new_src->get_port(new_src)); + tmpl->encap_dport = ntohs(new_dst->get_port(new_dst)); + } + netlink_add_attribute(hdr, rta->rta_type, + chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta)), + sizeof(request)); + } + rta = RTA_NEXT(rta, rtasize); + } + + if (tmpl == NULL && new_encap) + { /* add tmpl if we are enabling it */ + tmpl = netlink_reserve(hdr, sizeof(request), XFRMA_ENCAP, sizeof(*tmpl)); + if (!tmpl) + { + goto failed; + } + tmpl->encap_type = UDP_ENCAP_ESPINUDP; + tmpl->encap_sport = ntohs(new_src->get_port(new_src)); + tmpl->encap_dport = ntohs(new_dst->get_port(new_dst)); + memset(&tmpl->encap_oa, 0, sizeof (xfrm_address_t)); + } + + if (replay_esn) + { + struct xfrm_replay_state_esn *state; + + state = netlink_reserve(hdr, sizeof(request), XFRMA_REPLAY_ESN_VAL, + replay_esn_len); + if (!state) + { + goto failed; + } + memcpy(state, replay_esn, replay_esn_len); + } + else if (replay) + { + struct xfrm_replay_state *state; + + state = netlink_reserve(hdr, sizeof(request), XFRMA_REPLAY_VAL, + sizeof(*state)); + if (!state) + { + goto failed; + } + memcpy(state, replay, sizeof(*state)); + } + else + { + DBG1(DBG_KNL, "unable to copy replay state from old SAD entry with " + "SPI %.8x", ntohl(spi)); + } + if (lifetime) + { + struct xfrm_lifetime_cur *state; + + state = netlink_reserve(hdr, sizeof(request), XFRMA_LTIME_VAL, + sizeof(*state)); + if (!state) + { + goto failed; + } + memcpy(state, lifetime, sizeof(*state)); + } + else + { + DBG1(DBG_KNL, "unable to copy usage stats from old SAD entry with " + "SPI %.8x", ntohl(spi)); + } + + if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS) + { + DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x", ntohl(spi)); + goto failed; + } + + status = SUCCESS; +failed: + free(replay); + free(replay_esn); + free(lifetime); + memwipe(out, len); + memwipe(&request, sizeof(request)); + free(out); + + return status; +} + +METHOD(kernel_ipsec_t, flush_sas, status_t, + private_kernel_netlink_ipsec_t *this) +{ + netlink_buf_t request; + struct nlmsghdr *hdr; + struct xfrm_usersa_flush *flush; + struct { + u_int8_t proto; + char *name; + } protos[] = { + { IPPROTO_AH, "AH" }, + { IPPROTO_ESP, "ESP" }, + { IPPROTO_COMP, "IPComp" }, + }; + int i; + + memset(&request, 0, sizeof(request)); + + hdr = &request.hdr; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = XFRM_MSG_FLUSHSA; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_flush)); + + flush = NLMSG_DATA(hdr); + + for (i = 0; i < countof(protos); i++) + { + DBG2(DBG_KNL, "flushing all %s SAD entries", protos[i].name); + + flush->proto = protos[i].proto; + + if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS) + { + DBG1(DBG_KNL, "unable to flush %s SAD entries", protos[i].name); + return FAILED; + } + } + return SUCCESS; +} + +/** + * Add or update a policy in the kernel. + * + * Note: The mutex has to be locked when entering this function + * and is unlocked here in any case. + */ +static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, + policy_entry_t *policy, policy_sa_t *mapping, bool update) +{ + netlink_buf_t request; + policy_entry_t clone; + ipsec_sa_t *ipsec = mapping->sa; + struct xfrm_userpolicy_info *policy_info; + struct nlmsghdr *hdr; + status_t status; + int i; + + /* clone the policy so we are able to check it out again later */ + memcpy(&clone, policy, sizeof(policy_entry_t)); + + memset(&request, 0, sizeof(request)); + hdr = &request.hdr; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = update ? XFRM_MSG_UPDPOLICY : XFRM_MSG_NEWPOLICY; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_info)); + + policy_info = NLMSG_DATA(hdr); + policy_info->sel = policy->sel; + policy_info->dir = policy->direction; + + /* calculate priority based on selector size, small size = high prio */ + policy_info->priority = mapping->priority; + policy_info->action = mapping->type != POLICY_DROP ? XFRM_POLICY_ALLOW + : XFRM_POLICY_BLOCK; + policy_info->share = XFRM_SHARE_ANY; + + /* policies don't expire */ + policy_info->lft.soft_byte_limit = XFRM_INF; + policy_info->lft.soft_packet_limit = XFRM_INF; + policy_info->lft.hard_byte_limit = XFRM_INF; + policy_info->lft.hard_packet_limit = XFRM_INF; + policy_info->lft.soft_add_expires_seconds = 0; + policy_info->lft.hard_add_expires_seconds = 0; + policy_info->lft.soft_use_expires_seconds = 0; + policy_info->lft.hard_use_expires_seconds = 0; + + if (mapping->type == POLICY_IPSEC) + { + struct xfrm_user_tmpl *tmpl; + struct { + u_int8_t proto; + bool use; + } protos[] = { + { IPPROTO_COMP, ipsec->cfg.ipcomp.transform != IPCOMP_NONE }, + { IPPROTO_ESP, ipsec->cfg.esp.use }, + { IPPROTO_AH, ipsec->cfg.ah.use }, + }; + ipsec_mode_t proto_mode = ipsec->cfg.mode; + int count = 0; + + for (i = 0; i < countof(protos); i++) + { + if (protos[i].use) + { + count++; + } + } + tmpl = netlink_reserve(hdr, sizeof(request), XFRMA_TMPL, + count * sizeof(*tmpl)); + if (!tmpl) + { + this->mutex->unlock(this->mutex); + return FAILED; + } + + for (i = 0; i < countof(protos); i++) + { + if (!protos[i].use) + { + continue; + } + tmpl->reqid = ipsec->cfg.reqid; + tmpl->id.proto = protos[i].proto; + tmpl->aalgos = tmpl->ealgos = tmpl->calgos = ~0; + tmpl->mode = mode2kernel(proto_mode); + tmpl->optional = protos[i].proto == IPPROTO_COMP && + policy->direction != POLICY_OUT; + tmpl->family = ipsec->src->get_family(ipsec->src); + + if (proto_mode == MODE_TUNNEL || proto_mode == MODE_BEET) + { /* only for tunnel mode */ + host2xfrm(ipsec->src, &tmpl->saddr); + host2xfrm(ipsec->dst, &tmpl->id.daddr); + } + + tmpl++; + + /* use transport mode for other SAs */ + proto_mode = MODE_TRANSPORT; + } + } + + if (!add_mark(hdr, sizeof(request), ipsec->mark)) + { + this->mutex->unlock(this->mutex); + return FAILED; + } + this->mutex->unlock(this->mutex); + + status = this->socket_xfrm->send_ack(this->socket_xfrm, hdr); + if (status == ALREADY_DONE && !update) + { + DBG1(DBG_KNL, "policy already exists, try to update it"); + hdr->nlmsg_type = XFRM_MSG_UPDPOLICY; + status = this->socket_xfrm->send_ack(this->socket_xfrm, hdr); + } + if (status != SUCCESS) + { + return FAILED; + } + + /* find the policy again */ + this->mutex->lock(this->mutex); + policy = this->policies->get(this->policies, &clone); + if (!policy || + policy->used_by->find_first(policy->used_by, + NULL, (void**)&mapping) != SUCCESS) + { /* policy or mapping is already gone, ignore */ + this->mutex->unlock(this->mutex); + return SUCCESS; + } + + /* install a route, if: + * - this is a forward policy (to just get one for each child) + * - we are in tunnel/BEET mode or install a bypass policy + * - routing is not disabled via strongswan.conf + */ + if (policy->direction == POLICY_FWD && this->install_routes && + (mapping->type != POLICY_IPSEC || ipsec->cfg.mode != MODE_TRANSPORT)) + { + policy_sa_fwd_t *fwd = (policy_sa_fwd_t*)mapping; + route_entry_t *route; + host_t *iface; + + INIT(route, + .prefixlen = policy->sel.prefixlen_s, + ); + + if (charon->kernel->get_address_by_ts(charon->kernel, fwd->dst_ts, + &route->src_ip, NULL) == SUCCESS) + { + /* get the nexthop to src (src as we are in POLICY_FWD) */ + if (!ipsec->src->is_anyaddr(ipsec->src)) + { + route->gateway = charon->kernel->get_nexthop(charon->kernel, + ipsec->src, -1, ipsec->dst); + } + else + { /* for shunt policies */ + iface = xfrm2host(policy->sel.family, &policy->sel.saddr, 0); + route->gateway = charon->kernel->get_nexthop(charon->kernel, + iface, policy->sel.prefixlen_s, + route->src_ip); + iface->destroy(iface); + } + route->dst_net = chunk_alloc(policy->sel.family == AF_INET ? 4 : 16); + memcpy(route->dst_net.ptr, &policy->sel.saddr, route->dst_net.len); + + /* get the interface to install the route for. If we have a local + * address, use it. Otherwise (for shunt policies) use the + * routes source address. */ + iface = ipsec->dst; + if (iface->is_anyaddr(iface)) + { + iface = route->src_ip; + } + /* install route via outgoing interface */ + if (!charon->kernel->get_interface(charon->kernel, iface, + &route->if_name)) + { + this->mutex->unlock(this->mutex); + route_entry_destroy(route); + return SUCCESS; + } + + if (policy->route) + { + route_entry_t *old = policy->route; + if (route_entry_equals(old, route)) + { + this->mutex->unlock(this->mutex); + route_entry_destroy(route); + return SUCCESS; + } + /* uninstall previously installed route */ + if (charon->kernel->del_route(charon->kernel, old->dst_net, + old->prefixlen, old->gateway, + old->src_ip, old->if_name) != SUCCESS) + { + DBG1(DBG_KNL, "error uninstalling route installed with " + "policy %R === %R %N", fwd->src_ts, + fwd->dst_ts, policy_dir_names, + policy->direction); + } + route_entry_destroy(old); + policy->route = NULL; + } + + DBG2(DBG_KNL, "installing route: %R via %H src %H dev %s", + fwd->src_ts, route->gateway, route->src_ip, route->if_name); + switch (charon->kernel->add_route(charon->kernel, route->dst_net, + route->prefixlen, route->gateway, + route->src_ip, route->if_name)) + { + default: + DBG1(DBG_KNL, "unable to install source route for %H", + route->src_ip); + /* FALL */ + case ALREADY_DONE: + /* route exists, do not uninstall */ + route_entry_destroy(route); + break; + case SUCCESS: + /* cache the installed route */ + policy->route = route; + break; + } + } + else + { + free(route); + } + } + this->mutex->unlock(this->mutex); + return SUCCESS; +} + +METHOD(kernel_ipsec_t, add_policy, status_t, + private_kernel_netlink_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) +{ + policy_entry_t *policy, *current; + policy_sa_t *assigned_sa, *current_sa; + enumerator_t *enumerator; + bool found = FALSE, update = TRUE; + + /* create a policy */ + INIT(policy, + .sel = ts2selector(src_ts, dst_ts), + .mark = mark.value & mark.mask, + .direction = direction, + .reqid = sa->reqid, + ); + + /* find the policy, which matches EXACTLY */ + this->mutex->lock(this->mutex); + current = this->policies->get(this->policies, policy); + if (current) + { + if (current->reqid && sa->reqid && current->reqid != sa->reqid) + { + DBG1(DBG_CFG, "unable to install policy %R === %R %N (mark " + "%u/0x%08x) for reqid %u, the same policy for reqid %u exists", + src_ts, dst_ts, policy_dir_names, direction, + mark.value, mark.mask, sa->reqid, current->reqid); + policy_entry_destroy(this, policy); + this->mutex->unlock(this->mutex); + return INVALID_STATE; + } + /* use existing policy */ + DBG2(DBG_KNL, "policy %R === %R %N (mark %u/0x%08x) " + "already exists, increasing refcount", + src_ts, dst_ts, policy_dir_names, direction, + mark.value, mark.mask); + policy_entry_destroy(this, policy); + policy = current; + found = TRUE; + } + else + { /* use the new one, if we have no such policy */ + policy->used_by = linked_list_create(); + this->policies->put(this->policies, policy, policy); + } + + /* cache the assigned IPsec SA */ + assigned_sa = policy_sa_create(this, direction, type, src, dst, src_ts, + dst_ts, mark, sa); + assigned_sa->priority = get_priority(policy, priority); + + /* insert the SA according to its priority */ + enumerator = policy->used_by->create_enumerator(policy->used_by); + while (enumerator->enumerate(enumerator, (void**)¤t_sa)) + { + if (current_sa->priority >= assigned_sa->priority) + { + break; + } + update = FALSE; + } + policy->used_by->insert_before(policy->used_by, enumerator, + assigned_sa); + enumerator->destroy(enumerator); + + if (!update) + { /* we don't update the policy if the priority is lower than that of + * the currently installed one */ + this->mutex->unlock(this->mutex); + return SUCCESS; + } + + if (this->policy_update) + { + found = TRUE; + } + + DBG2(DBG_KNL, "%s policy %R === %R %N (mark %u/0x%08x)", + found ? "updating" : "adding", src_ts, dst_ts, + policy_dir_names, direction, mark.value, mark.mask); + + if (add_policy_internal(this, policy, assigned_sa, found) != SUCCESS) + { + DBG1(DBG_KNL, "unable to %s policy %R === %R %N", + found ? "update" : "add", src_ts, dst_ts, + policy_dir_names, direction); + return FAILED; + } + return SUCCESS; +} + +METHOD(kernel_ipsec_t, query_policy, status_t, + private_kernel_netlink_ipsec_t *this, traffic_selector_t *src_ts, + traffic_selector_t *dst_ts, policy_dir_t direction, mark_t mark, + time_t *use_time) +{ + netlink_buf_t request; + struct nlmsghdr *out = NULL, *hdr; + struct xfrm_userpolicy_id *policy_id; + struct xfrm_userpolicy_info *policy = NULL; + size_t len; + + memset(&request, 0, sizeof(request)); + + DBG2(DBG_KNL, "querying policy %R === %R %N (mark %u/0x%08x)", + src_ts, dst_ts, policy_dir_names, direction, + mark.value, mark.mask); + + hdr = &request.hdr; + hdr->nlmsg_flags = NLM_F_REQUEST; + hdr->nlmsg_type = XFRM_MSG_GETPOLICY; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_id)); + + policy_id = NLMSG_DATA(hdr); + policy_id->sel = ts2selector(src_ts, dst_ts); + policy_id->dir = direction; + + if (!add_mark(hdr, sizeof(request), mark)) + { + return FAILED; + } + + if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) + { + hdr = out; + while (NLMSG_OK(hdr, len)) + { + switch (hdr->nlmsg_type) + { + case XFRM_MSG_NEWPOLICY: + { + policy = NLMSG_DATA(hdr); + break; + } + case NLMSG_ERROR: + { + struct nlmsgerr *err = NLMSG_DATA(hdr); + DBG1(DBG_KNL, "querying policy failed: %s (%d)", + strerror(-err->error), -err->error); + break; + } + default: + hdr = NLMSG_NEXT(hdr, len); + continue; + case NLMSG_DONE: + break; + } + break; + } + } + + if (policy == NULL) + { + DBG2(DBG_KNL, "unable to query policy %R === %R %N", src_ts, dst_ts, + policy_dir_names, direction); + free(out); + return FAILED; + } + + if (policy->curlft.use_time) + { + /* we need the monotonic time, but the kernel returns system time. */ + *use_time = time_monotonic(NULL) - (time(NULL) - policy->curlft.use_time); + } + else + { + *use_time = 0; + } + + free(out); + return SUCCESS; +} + +METHOD(kernel_ipsec_t, del_policy, status_t, + private_kernel_netlink_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 prio) +{ + policy_entry_t *current, policy; + enumerator_t *enumerator; + policy_sa_t *mapping; + netlink_buf_t request; + struct nlmsghdr *hdr; + struct xfrm_userpolicy_id *policy_id; + bool is_installed = TRUE; + u_int32_t priority; + ipsec_sa_t assigned_sa = { + .src = src, + .dst = dst, + .mark = mark, + .cfg = *sa, + }; + + DBG2(DBG_KNL, "deleting policy %R === %R %N (mark %u/0x%08x)", + src_ts, dst_ts, policy_dir_names, direction, + mark.value, mark.mask); + + /* create a policy */ + memset(&policy, 0, sizeof(policy_entry_t)); + policy.sel = ts2selector(src_ts, dst_ts); + policy.mark = mark.value & mark.mask; + policy.direction = direction; + + /* find the policy */ + this->mutex->lock(this->mutex); + current = this->policies->get(this->policies, &policy); + if (!current) + { + if (mark.value) + { + DBG1(DBG_KNL, "deleting policy %R === %R %N (mark %u/0x%08x) " + "failed, not found", src_ts, dst_ts, policy_dir_names, + direction, mark.value, mark.mask); + } + else + { + DBG1(DBG_KNL, "deleting policy %R === %R %N failed, not found", + src_ts, dst_ts, policy_dir_names, direction); + } + this->mutex->unlock(this->mutex); + return NOT_FOUND; + } + + /* remove mapping to SA by reqid and priority */ + priority = get_priority(current, prio); + enumerator = current->used_by->create_enumerator(current->used_by); + while (enumerator->enumerate(enumerator, (void**)&mapping)) + { + if (priority == mapping->priority && type == mapping->type && + ipsec_sa_equals(mapping->sa, &assigned_sa)) + { + current->used_by->remove_at(current->used_by, enumerator); + policy_sa_destroy(mapping, &direction, this); + break; + } + is_installed = FALSE; + } + enumerator->destroy(enumerator); + + if (current->used_by->get_count(current->used_by) > 0) + { /* policy is used by more SAs, keep in kernel */ + DBG2(DBG_KNL, "policy still used by another CHILD_SA, not removed"); + if (!is_installed) + { /* no need to update as the policy was not installed for this SA */ + this->mutex->unlock(this->mutex); + return SUCCESS; + } + + DBG2(DBG_KNL, "updating policy %R === %R %N (mark %u/0x%08x)", + src_ts, dst_ts, policy_dir_names, direction, + mark.value, mark.mask); + + current->used_by->get_first(current->used_by, (void**)&mapping); + if (add_policy_internal(this, current, mapping, TRUE) != SUCCESS) + { + DBG1(DBG_KNL, "unable to update policy %R === %R %N", + src_ts, dst_ts, policy_dir_names, direction); + return FAILED; + } + return SUCCESS; + } + + memset(&request, 0, sizeof(request)); + + hdr = &request.hdr; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = XFRM_MSG_DELPOLICY; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_id)); + + policy_id = NLMSG_DATA(hdr); + policy_id->sel = current->sel; + policy_id->dir = direction; + + if (!add_mark(hdr, sizeof(request), mark)) + { + this->mutex->unlock(this->mutex); + return FAILED; + } + + if (current->route) + { + route_entry_t *route = current->route; + if (charon->kernel->del_route(charon->kernel, route->dst_net, + route->prefixlen, route->gateway, + route->src_ip, route->if_name) != SUCCESS) + { + DBG1(DBG_KNL, "error uninstalling route installed with " + "policy %R === %R %N", src_ts, dst_ts, + policy_dir_names, direction); + } + } + + this->policies->remove(this->policies, current); + policy_entry_destroy(this, current); + this->mutex->unlock(this->mutex); + + if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS) + { + if (mark.value) + { + DBG1(DBG_KNL, "unable to delete policy %R === %R %N " + "(mark %u/0x%08x)", src_ts, dst_ts, policy_dir_names, + direction, mark.value, mark.mask); + } + else + { + DBG1(DBG_KNL, "unable to delete policy %R === %R %N", + src_ts, dst_ts, policy_dir_names, direction); + } + return FAILED; + } + return SUCCESS; +} + +METHOD(kernel_ipsec_t, flush_policies, status_t, + private_kernel_netlink_ipsec_t *this) +{ + netlink_buf_t request; + struct nlmsghdr *hdr; + + memset(&request, 0, sizeof(request)); + + DBG2(DBG_KNL, "flushing all policies from SPD"); + + hdr = &request.hdr; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = XFRM_MSG_FLUSHPOLICY; + hdr->nlmsg_len = NLMSG_LENGTH(0); /* no data associated */ + + /* by adding an rtattr of type XFRMA_POLICY_TYPE we could restrict this + * to main or sub policies (default is main) */ + + if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS) + { + DBG1(DBG_KNL, "unable to flush SPD entries"); + return FAILED; + } + return SUCCESS; +} + +/** + * Bypass socket using a per-socket policy + */ +static bool add_socket_bypass(private_kernel_netlink_ipsec_t *this, + int fd, int family) +{ + struct xfrm_userpolicy_info policy; + u_int sol, ipsec_policy; + + switch (family) + { + case AF_INET: + sol = SOL_IP; + ipsec_policy = IP_XFRM_POLICY; + break; + case AF_INET6: + sol = SOL_IPV6; + ipsec_policy = IPV6_XFRM_POLICY; + break; + default: + return FALSE; + } + + memset(&policy, 0, sizeof(policy)); + policy.action = XFRM_POLICY_ALLOW; + policy.sel.family = family; + + policy.dir = XFRM_POLICY_OUT; + if (setsockopt(fd, sol, ipsec_policy, &policy, sizeof(policy)) < 0) + { + DBG1(DBG_KNL, "unable to set IPSEC_POLICY on socket: %s", + strerror(errno)); + return FALSE; + } + policy.dir = XFRM_POLICY_IN; + if (setsockopt(fd, sol, ipsec_policy, &policy, sizeof(policy)) < 0) + { + DBG1(DBG_KNL, "unable to set IPSEC_POLICY on socket: %s", + strerror(errno)); + return FALSE; + } + return TRUE; +} + +/** + * Port based IKE bypass policy + */ +typedef struct { + /** address family */ + int family; + /** layer 4 protocol */ + int proto; + /** port number, network order */ + u_int16_t port; +} bypass_t; + +/** + * Add or remove a bypass policy from/to kernel + */ +static bool manage_bypass(private_kernel_netlink_ipsec_t *this, + int type, policy_dir_t dir, bypass_t *bypass) +{ + netlink_buf_t request; + struct xfrm_selector *sel; + struct nlmsghdr *hdr; + + memset(&request, 0, sizeof(request)); + hdr = &request.hdr; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = type; + + if (type == XFRM_MSG_NEWPOLICY) + { + struct xfrm_userpolicy_info *policy; + + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_info)); + + policy = NLMSG_DATA(hdr); + policy->dir = dir; + policy->priority = 32; + policy->action = XFRM_POLICY_ALLOW; + policy->share = XFRM_SHARE_ANY; + + policy->lft.soft_byte_limit = XFRM_INF; + policy->lft.soft_packet_limit = XFRM_INF; + policy->lft.hard_byte_limit = XFRM_INF; + policy->lft.hard_packet_limit = XFRM_INF; + + sel = &policy->sel; + } + else /* XFRM_MSG_DELPOLICY */ + { + struct xfrm_userpolicy_id *policy; + + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_id)); + + policy = NLMSG_DATA(hdr); + policy->dir = dir; + + sel = &policy->sel; + } + + sel->family = bypass->family; + sel->proto = bypass->proto; + if (dir == POLICY_IN) + { + sel->dport = bypass->port; + sel->dport_mask = 0xffff; + } + else + { + sel->sport = bypass->port; + sel->sport_mask = 0xffff; + } + return this->socket_xfrm->send_ack(this->socket_xfrm, hdr) == SUCCESS; +} + +/** + * Bypass socket using a port-based bypass policy + */ +static bool add_port_bypass(private_kernel_netlink_ipsec_t *this, + int fd, int family) +{ + union { + struct sockaddr sa; + struct sockaddr_in in; + struct sockaddr_in6 in6; + } saddr; + socklen_t len; + bypass_t bypass = { + .family = family, + }; + + len = sizeof(saddr); + if (getsockname(fd, &saddr.sa, &len) != 0) + { + return FALSE; + } +#ifdef SO_PROTOCOL /* since 2.6.32 */ + len = sizeof(bypass.proto); + if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &bypass.proto, &len) != 0) +#endif + { /* assume UDP if SO_PROTOCOL not supported */ + bypass.proto = IPPROTO_UDP; + } + switch (family) + { + case AF_INET: + bypass.port = saddr.in.sin_port; + break; + case AF_INET6: + bypass.port = saddr.in6.sin6_port; + break; + default: + return FALSE; + } + + if (!manage_bypass(this, XFRM_MSG_NEWPOLICY, POLICY_IN, &bypass)) + { + return FALSE; + } + if (!manage_bypass(this, XFRM_MSG_NEWPOLICY, POLICY_OUT, &bypass)) + { + manage_bypass(this, XFRM_MSG_DELPOLICY, POLICY_IN, &bypass); + return FALSE; + } + array_insert(this->bypass, ARRAY_TAIL, &bypass); + + return TRUE; +} + +/** + * Remove installed port based bypass policy + */ +static void remove_port_bypass(bypass_t *bypass, int idx, + private_kernel_netlink_ipsec_t *this) +{ + manage_bypass(this, XFRM_MSG_DELPOLICY, POLICY_OUT, bypass); + manage_bypass(this, XFRM_MSG_DELPOLICY, POLICY_IN, bypass); +} + +METHOD(kernel_ipsec_t, bypass_socket, bool, + private_kernel_netlink_ipsec_t *this, int fd, int family) +{ + if (lib->settings->get_bool(lib->settings, + "%s.plugins.kernel-netlink.port_bypass", FALSE, lib->ns)) + { + return add_port_bypass(this, fd, family); + } + return add_socket_bypass(this, fd, family); +} + +METHOD(kernel_ipsec_t, enable_udp_decap, bool, + private_kernel_netlink_ipsec_t *this, int fd, int family, u_int16_t port) +{ + int type = UDP_ENCAP_ESPINUDP; + + if (setsockopt(fd, SOL_UDP, UDP_ENCAP, &type, sizeof(type)) < 0) + { + DBG1(DBG_KNL, "unable to set UDP_ENCAP: %s", strerror(errno)); + return FALSE; + } + return TRUE; +} + +METHOD(kernel_ipsec_t, destroy, void, + private_kernel_netlink_ipsec_t *this) +{ + enumerator_t *enumerator; + policy_entry_t *policy; + + array_destroy_function(this->bypass, + (array_callback_t)remove_port_bypass, this); + if (this->socket_xfrm_events > 0) + { + lib->watcher->remove(lib->watcher, this->socket_xfrm_events); + close(this->socket_xfrm_events); + } + DESTROY_IF(this->socket_xfrm); + enumerator = this->policies->create_enumerator(this->policies); + while (enumerator->enumerate(enumerator, &policy, &policy)) + { + policy_entry_destroy(this, policy); + } + enumerator->destroy(enumerator); + this->policies->destroy(this->policies); + this->sas->destroy(this->sas); + this->mutex->destroy(this->mutex); + free(this); +} + +/* + * Described in header. + */ +kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() +{ + private_kernel_netlink_ipsec_t *this; + bool register_for_events = TRUE; + FILE *f; + + 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, + }, + }, + .policies = hashtable_create((hashtable_hash_t)policy_hash, + (hashtable_equals_t)policy_equals, 32), + .sas = hashtable_create((hashtable_hash_t)ipsec_sa_hash, + (hashtable_equals_t)ipsec_sa_equals, 32), + .bypass = array_create(sizeof(bypass_t), 0), + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + .policy_update = lib->settings->get_bool(lib->settings, + "%s.plugins.kernel-netlink.policy_update", FALSE, lib->ns), + .install_routes = lib->settings->get_bool(lib->settings, + "%s.install_routes", TRUE, lib->ns), + .proto_port_transport = lib->settings->get_bool(lib->settings, + "%s.plugins.kernel-netlink.set_proto_port_transport_sa", + FALSE, lib->ns), + ); + + if (streq(lib->ns, "starter")) + { /* starter has no threads, so we do not register for kernel events */ + register_for_events = FALSE; + } + + f = fopen("/proc/sys/net/core/xfrm_acq_expires", "w"); + if (f) + { + fprintf(f, "%u", lib->settings->get_int(lib->settings, + "%s.plugins.kernel-netlink.xfrm_acq_expires", + DEFAULT_ACQUIRE_LIFETIME, lib->ns)); + fclose(f); + } + + this->socket_xfrm = netlink_socket_create(NETLINK_XFRM, xfrm_msg_names, + lib->settings->get_bool(lib->settings, + "%s.plugins.kernel-netlink.parallel_xfrm", FALSE, lib->ns)); + if (!this->socket_xfrm) + { + destroy(this); + return NULL; + } + + if (register_for_events) + { + struct sockaddr_nl addr; + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + + /* create and bind XFRM socket for ACQUIRE, EXPIRE, MIGRATE & MAPPING */ + this->socket_xfrm_events = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM); + if (this->socket_xfrm_events <= 0) + { + DBG1(DBG_KNL, "unable to create XFRM event socket"); + destroy(this); + return NULL; + } + addr.nl_groups = XFRMNLGRP(ACQUIRE) | XFRMNLGRP(EXPIRE) | + XFRMNLGRP(MIGRATE) | XFRMNLGRP(MAPPING); + if (bind(this->socket_xfrm_events, (struct sockaddr*)&addr, sizeof(addr))) + { + DBG1(DBG_KNL, "unable to bind XFRM event socket"); + destroy(this); + return NULL; + } + lib->watcher->add(lib->watcher, this->socket_xfrm_events, WATCHER_READ, + (watcher_cb_t)receive_events, this); + } + + return &this->public; +} diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.h b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.h new file mode 100644 index 000000000..3a45cce06 --- /dev/null +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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_netlink_ipsec_i kernel_netlink_ipsec + * @{ @ingroup kernel_netlink + */ + +#ifndef KERNEL_NETLINK_IPSEC_H_ +#define KERNEL_NETLINK_IPSEC_H_ + +#include <kernel/kernel_ipsec.h> + +typedef struct kernel_netlink_ipsec_t kernel_netlink_ipsec_t; + +/** + * Implementation of the kernel ipsec interface using Netlink. + */ +struct kernel_netlink_ipsec_t { + + /** + * Implements kernel_ipsec_t interface + */ + kernel_ipsec_t interface; +}; + +/** + * Create a netlink kernel ipsec interface instance. + * + * @return kernel_netlink_ipsec_t instance + */ +kernel_netlink_ipsec_t *kernel_netlink_ipsec_create(); + +#endif /** KERNEL_NETLINK_IPSEC_H_ @}*/ diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c new file mode 100644 index 000000000..f4394a14f --- /dev/null +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c @@ -0,0 +1,2686 @@ +/* + * Copyright (C) 2008-2014 Tobias Brunner + * Copyright (C) 2005-2008 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * 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. + */ + +/* + * Copyright (C) 2010 secunet Security Networks AG + * Copyright (C) 2010 Thomas Egerer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <sys/socket.h> +#include <sys/utsname.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <unistd.h> +#include <errno.h> +#include <net/if.h> +#ifdef HAVE_LINUX_FIB_RULES_H +#include <linux/fib_rules.h> +#endif + +#include "kernel_netlink_net.h" +#include "kernel_netlink_shared.h" + +#include <daemon.h> +#include <utils/debug.h> +#include <threading/mutex.h> +#include <threading/rwlock.h> +#include <threading/rwlock_condvar.h> +#include <threading/spinlock.h> +#include <collections/hashtable.h> +#include <collections/linked_list.h> +#include <processing/jobs/callback_job.h> + +/** delay before firing roam events (ms) */ +#define ROAM_DELAY 100 + +/** delay before reinstalling routes (ms) */ +#define ROUTE_DELAY 100 + +/** maximum recursion when searching for addresses in get_route() */ +#define MAX_ROUTE_RECURSION 2 + +#ifndef ROUTING_TABLE +#define ROUTING_TABLE 0 +#endif + +#ifndef ROUTING_TABLE_PRIO +#define ROUTING_TABLE_PRIO 0 +#endif + +ENUM(rt_msg_names, RTM_NEWLINK, RTM_GETRULE, + "RTM_NEWLINK", + "RTM_DELLINK", + "RTM_GETLINK", + "RTM_SETLINK", + "RTM_NEWADDR", + "RTM_DELADDR", + "RTM_GETADDR", + "31", + "RTM_NEWROUTE", + "RTM_DELROUTE", + "RTM_GETROUTE", + "35", + "RTM_NEWNEIGH", + "RTM_DELNEIGH", + "RTM_GETNEIGH", + "RTM_NEWRULE", + "RTM_DELRULE", + "RTM_GETRULE", +); + +typedef struct addr_entry_t addr_entry_t; + +/** + * IP address in an iface_entry_t + */ +struct addr_entry_t { + + /** the ip address */ + host_t *ip; + + /** address flags */ + u_char flags; + + /** scope of the address */ + u_char scope; + + /** number of times this IP is used, if virtual (i.e. managed by us) */ + u_int refcount; + + /** TRUE once it is installed, if virtual */ + bool installed; +}; + +/** + * destroy a addr_entry_t object + */ +static void addr_entry_destroy(addr_entry_t *this) +{ + this->ip->destroy(this->ip); + free(this); +} + +typedef struct iface_entry_t iface_entry_t; + +/** + * A network interface on this system, containing addr_entry_t's + */ +struct iface_entry_t { + + /** interface index */ + int ifindex; + + /** name of the interface */ + char ifname[IFNAMSIZ]; + + /** interface flags, as in netdevice(7) SIOCGIFFLAGS */ + u_int flags; + + /** list of addresses as host_t */ + linked_list_t *addrs; + + /** TRUE if usable by config */ + bool usable; +}; + +/** + * destroy an interface entry + */ +static void iface_entry_destroy(iface_entry_t *this) +{ + this->addrs->destroy_function(this->addrs, (void*)addr_entry_destroy); + free(this); +} + +/** + * find an interface entry by index + */ +static bool iface_entry_by_index(iface_entry_t *this, int *ifindex) +{ + return this->ifindex == *ifindex; +} + +/** + * find an interface entry by name + */ +static bool iface_entry_by_name(iface_entry_t *this, char *ifname) +{ + return streq(this->ifname, ifname); +} + +/** + * check if an interface is up + */ +static inline bool iface_entry_up(iface_entry_t *iface) +{ + return (iface->flags & IFF_UP) == IFF_UP; +} + +/** + * check if an interface is up and usable + */ +static inline bool iface_entry_up_and_usable(iface_entry_t *iface) +{ + return iface->usable && iface_entry_up(iface); +} + +typedef struct addr_map_entry_t addr_map_entry_t; + +/** + * Entry that maps an IP address to an interface entry + */ +struct addr_map_entry_t { + /** The IP address */ + host_t *ip; + + /** The address entry for this IP address */ + addr_entry_t *addr; + + /** The interface this address is installed on */ + iface_entry_t *iface; +}; + +/** + * Hash a addr_map_entry_t object, all entries with the same IP address + * are stored in the same bucket + */ +static u_int addr_map_entry_hash(addr_map_entry_t *this) +{ + return chunk_hash(this->ip->get_address(this->ip)); +} + +/** + * Compare two addr_map_entry_t objects, two entries are equal if they are + * installed on the same interface + */ +static bool addr_map_entry_equals(addr_map_entry_t *a, addr_map_entry_t *b) +{ + return a->iface->ifindex == b->iface->ifindex && + a->ip->ip_equals(a->ip, b->ip); +} + +/** + * Used with get_match this finds an address entry if it is installed on + * an up and usable interface + */ +static bool addr_map_entry_match_up_and_usable(addr_map_entry_t *a, + addr_map_entry_t *b) +{ + return iface_entry_up_and_usable(b->iface) && + a->ip->ip_equals(a->ip, b->ip); +} + +/** + * Used with get_match this finds an address entry if it is installed on + * any active local interface + */ +static bool addr_map_entry_match_up(addr_map_entry_t *a, addr_map_entry_t *b) +{ + return iface_entry_up(b->iface) && a->ip->ip_equals(a->ip, b->ip); +} + +/** + * Used with get_match this finds an address entry if it is installed on + * any local interface + */ +static bool addr_map_entry_match(addr_map_entry_t *a, addr_map_entry_t *b) +{ + return a->ip->ip_equals(a->ip, b->ip); +} + +typedef struct route_entry_t route_entry_t; + +/** + * Installed routing entry + */ +struct route_entry_t { + /** Name of the interface the route is bound to */ + char *if_name; + + /** Source ip of the route */ + host_t *src_ip; + + /** Gateway for this route */ + host_t *gateway; + + /** Destination net */ + chunk_t dst_net; + + /** Destination net prefixlen */ + u_int8_t prefixlen; +}; + +/** + * Clone a route_entry_t object. + */ +static route_entry_t *route_entry_clone(route_entry_t *this) +{ + route_entry_t *route; + + INIT(route, + .if_name = strdup(this->if_name), + .src_ip = this->src_ip->clone(this->src_ip), + .gateway = this->gateway ? this->gateway->clone(this->gateway) : NULL, + .dst_net = chunk_clone(this->dst_net), + .prefixlen = this->prefixlen, + ); + return route; +} + +/** + * Destroy a route_entry_t object + */ +static void route_entry_destroy(route_entry_t *this) +{ + free(this->if_name); + DESTROY_IF(this->src_ip); + DESTROY_IF(this->gateway); + chunk_free(&this->dst_net); + free(this); +} + +/** + * Hash a route_entry_t object + */ +static u_int route_entry_hash(route_entry_t *this) +{ + return chunk_hash_inc(chunk_from_thing(this->prefixlen), + chunk_hash(this->dst_net)); +} + +/** + * Compare two route_entry_t objects + */ +static bool route_entry_equals(route_entry_t *a, route_entry_t *b) +{ + if (a->if_name && b->if_name && streq(a->if_name, b->if_name) && + a->src_ip->ip_equals(a->src_ip, b->src_ip) && + chunk_equals(a->dst_net, b->dst_net) && a->prefixlen == b->prefixlen) + { + return (!a->gateway && !b->gateway) || (a->gateway && b->gateway && + a->gateway->ip_equals(a->gateway, b->gateway)); + } + return FALSE; +} + +typedef struct net_change_t net_change_t; + +/** + * Queued network changes + */ +struct net_change_t { + /** Name of the interface that got activated (or an IP appeared on) */ + char *if_name; +}; + +/** + * Destroy a net_change_t object + */ +static void net_change_destroy(net_change_t *this) +{ + free(this->if_name); + free(this); +} + +/** + * Hash a net_change_t object + */ +static u_int net_change_hash(net_change_t *this) +{ + return chunk_hash(chunk_create(this->if_name, strlen(this->if_name))); +} + +/** + * Compare two net_change_t objects + */ +static bool net_change_equals(net_change_t *a, net_change_t *b) +{ + return streq(a->if_name, b->if_name); +} + +typedef struct private_kernel_netlink_net_t private_kernel_netlink_net_t; + +/** + * Private variables and functions of kernel_netlink_net class. + */ +struct private_kernel_netlink_net_t { + /** + * Public part of the kernel_netlink_net_t object. + */ + kernel_netlink_net_t public; + + /** + * lock to access various lists and maps + */ + rwlock_t *lock; + + /** + * condition variable to signal virtual IP add/removal + */ + rwlock_condvar_t *condvar; + + /** + * Cached list of interfaces and its addresses (iface_entry_t) + */ + linked_list_t *ifaces; + + /** + * Map for IP addresses to iface_entry_t objects (addr_map_entry_t) + */ + hashtable_t *addrs; + + /** + * Map for virtual IP addresses to iface_entry_t objects (addr_map_entry_t) + */ + hashtable_t *vips; + + /** + * netlink rt socket (routing) + */ + netlink_socket_t *socket; + + /** + * Netlink rt socket to receive address change events + */ + int socket_events; + + /** + * earliest time of the next roam event + */ + timeval_t next_roam; + + /** + * roam event due to address change + */ + bool roam_address; + + /** + * lock to check and update roam event time + */ + spinlock_t *roam_lock; + + /** + * routing table to install routes + */ + int routing_table; + + /** + * priority of used routing table + */ + int routing_table_prio; + + /** + * installed routes + */ + hashtable_t *routes; + + /** + * mutex for routes + */ + mutex_t *routes_lock; + + /** + * interface changes which may trigger route reinstallation + */ + hashtable_t *net_changes; + + /** + * mutex for route reinstallation triggers + */ + mutex_t *net_changes_lock; + + /** + * time of last route reinstallation + */ + timeval_t last_route_reinstall; + + /** + * whether to react to RTM_NEWROUTE or RTM_DELROUTE events + */ + bool process_route; + + /** + * whether to trigger roam events + */ + bool roam_events; + + /** + * whether to actually install virtual IPs + */ + bool install_virtual_ip; + + /** + * the name of the interface virtual IP addresses are installed on + */ + char *install_virtual_ip_on; + + /** + * whether preferred source addresses can be specified for IPv6 routes + */ + bool rta_prefsrc_for_ipv6; + + /** + * whether marks can be used in route lookups + */ + bool rta_mark; + + /** + * the mark excluded from the routing rule used for virtual IPs + */ + mark_t routing_mark; + + /** + * whether to prefer temporary IPv6 addresses over public ones + */ + bool prefer_temporary_addrs; + + /** + * list with routing tables to be excluded from route lookup + */ + linked_list_t *rt_exclude; + + /** + * MTU to set on installed routes + */ + u_int32_t mtu; + + /** + * MSS to set on installed routes + */ + u_int32_t mss; +}; + +/** + * Forward declaration + */ +static status_t manage_srcroute(private_kernel_netlink_net_t *this, + int nlmsg_type, int flags, chunk_t dst_net, + u_int8_t prefixlen, host_t *gateway, + host_t *src_ip, char *if_name); + +/** + * Clear the queued network changes. + */ +static void net_changes_clear(private_kernel_netlink_net_t *this) +{ + enumerator_t *enumerator; + net_change_t *change; + + enumerator = this->net_changes->create_enumerator(this->net_changes); + while (enumerator->enumerate(enumerator, NULL, (void**)&change)) + { + this->net_changes->remove_at(this->net_changes, enumerator); + net_change_destroy(change); + } + enumerator->destroy(enumerator); +} + +/** + * Act upon queued network changes. + */ +static job_requeue_t reinstall_routes(private_kernel_netlink_net_t *this) +{ + enumerator_t *enumerator; + route_entry_t *route; + + this->net_changes_lock->lock(this->net_changes_lock); + this->routes_lock->lock(this->routes_lock); + + enumerator = this->routes->create_enumerator(this->routes); + while (enumerator->enumerate(enumerator, NULL, (void**)&route)) + { + net_change_t *change, lookup = { + .if_name = route->if_name, + }; + /* check if a change for the outgoing interface is queued */ + change = this->net_changes->get(this->net_changes, &lookup); + if (!change) + { /* in case src_ip is not on the outgoing interface */ + if (this->public.interface.get_interface(&this->public.interface, + route->src_ip, &lookup.if_name)) + { + if (!streq(lookup.if_name, route->if_name)) + { + change = this->net_changes->get(this->net_changes, &lookup); + } + free(lookup.if_name); + } + } + if (change) + { + manage_srcroute(this, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, + route->dst_net, route->prefixlen, route->gateway, + route->src_ip, route->if_name); + } + } + enumerator->destroy(enumerator); + this->routes_lock->unlock(this->routes_lock); + + net_changes_clear(this); + this->net_changes_lock->unlock(this->net_changes_lock); + return JOB_REQUEUE_NONE; +} + +/** + * Queue route reinstallation caused by network changes for a given interface. + * + * The route reinstallation is delayed for a while and only done once for + * several calls during this delay, in order to avoid doing it too often. + * The interface name is freed. + */ +static void queue_route_reinstall(private_kernel_netlink_net_t *this, + char *if_name) +{ + net_change_t *update, *found; + timeval_t now; + job_t *job; + + INIT(update, + .if_name = if_name + ); + + this->net_changes_lock->lock(this->net_changes_lock); + found = this->net_changes->put(this->net_changes, update, update); + if (found) + { + net_change_destroy(found); + } + time_monotonic(&now); + if (timercmp(&now, &this->last_route_reinstall, >)) + { + timeval_add_ms(&now, ROUTE_DELAY); + this->last_route_reinstall = now; + + job = (job_t*)callback_job_create((callback_job_cb_t)reinstall_routes, + this, NULL, NULL); + lib->scheduler->schedule_job_ms(lib->scheduler, job, ROUTE_DELAY); + } + this->net_changes_lock->unlock(this->net_changes_lock); +} + +/** + * check if the given IP is known as virtual IP and currently installed + * + * this function will also return TRUE if the virtual IP entry disappeared. + * in that case the returned entry will be NULL. + * + * this->lock must be held when calling this function + */ +static bool is_vip_installed_or_gone(private_kernel_netlink_net_t *this, + host_t *ip, addr_map_entry_t **entry) +{ + addr_map_entry_t lookup = { + .ip = ip, + }; + + *entry = this->vips->get_match(this->vips, &lookup, + (void*)addr_map_entry_match); + if (*entry == NULL) + { /* the virtual IP disappeared */ + return TRUE; + } + return (*entry)->addr->installed; +} + +/** + * check if the given IP is known as virtual IP + * + * this->lock must be held when calling this function + */ +static bool is_known_vip(private_kernel_netlink_net_t *this, host_t *ip) +{ + addr_map_entry_t lookup = { + .ip = ip, + }; + + return this->vips->get_match(this->vips, &lookup, + (void*)addr_map_entry_match) != NULL; +} + +/** + * Add an address map entry + */ +static void addr_map_entry_add(hashtable_t *map, addr_entry_t *addr, + iface_entry_t *iface) +{ + addr_map_entry_t *entry; + + INIT(entry, + .ip = addr->ip, + .addr = addr, + .iface = iface, + ); + entry = map->put(map, entry, entry); + free(entry); +} + +/** + * Remove an address map entry + */ +static void addr_map_entry_remove(hashtable_t *map, addr_entry_t *addr, + iface_entry_t *iface) +{ + addr_map_entry_t *entry, lookup = { + .ip = addr->ip, + .addr = addr, + .iface = iface, + }; + + entry = map->remove(map, &lookup); + free(entry); +} + +/** + * Determine the type or scope of the given unicast IP address. This is not + * the same thing returned in rtm_scope/ifa_scope. + * + * We use return values as defined in RFC 6724 (referring to RFC 4291). + */ +static u_char get_scope(host_t *ip) +{ + chunk_t addr; + + addr = ip->get_address(ip); + switch (addr.len) + { + case 4: + /* we use the mapping defined in RFC 6724, 3.2 */ + if (addr.ptr[0] == 127) + { /* link-local, same as the IPv6 loopback address */ + return 2; + } + if (addr.ptr[0] == 169 && addr.ptr[1] == 254) + { /* link-local */ + return 2; + } + break; + case 16: + if (IN6_IS_ADDR_LOOPBACK((struct in6_addr*)addr.ptr)) + { /* link-local, according to RFC 4291, 2.5.3 */ + return 2; + } + if (IN6_IS_ADDR_LINKLOCAL((struct in6_addr*)addr.ptr)) + { + return 2; + } + if (IN6_IS_ADDR_SITELOCAL((struct in6_addr*)addr.ptr)) + { /* deprecated, according to RFC 4291, 2.5.7 */ + return 5; + } + break; + default: + break; + } + /* global */ + return 14; +} + +/** + * Returns the length of the common prefix in bits up to the length of a's + * prefix, defined by RFC 6724 as the portion of the address not including the + * interface ID, which is 64-bit for most unicast addresses (see RFC 4291). + */ +static u_char common_prefix(host_t *a, host_t *b) +{ + chunk_t aa, ba; + u_char byte, bits = 0, match; + + aa = a->get_address(a); + ba = b->get_address(b); + for (byte = 0; byte < 8; byte++) + { + if (aa.ptr[byte] != ba.ptr[byte]) + { + match = aa.ptr[byte] ^ ba.ptr[byte]; + for (bits = 8; match; match >>= 1) + { + bits--; + } + break; + } + } + return byte * 8 + bits; +} + +/** + * Compare two IP addresses and return TRUE if the second address is the better + * choice of the two to reach the destination. + * For IPv6 we approximately follow RFC 6724. + */ +static bool is_address_better(private_kernel_netlink_net_t *this, + addr_entry_t *a, addr_entry_t *b, host_t *d) +{ + u_char sa, sb, sd, pa, pb; + + /* rule 2: prefer appropriate scope */ + if (d) + { + sa = get_scope(a->ip); + sb = get_scope(b->ip); + sd = get_scope(d); + if (sa < sb) + { + return sa < sd; + } + else if (sb < sa) + { + return sb >= sd; + } + } + if (a->ip->get_family(a->ip) == AF_INET) + { /* stop here for IPv4, default to addresses found earlier */ + return FALSE; + } + /* rule 3: avoid deprecated addresses (RFC 4862) */ + if ((a->flags & IFA_F_DEPRECATED) != (b->flags & IFA_F_DEPRECATED)) + { + return a->flags & IFA_F_DEPRECATED; + } + /* rule 4 is not applicable as we don't know if an address is a home or + * care-of addresses. + * rule 5 does not apply as we only compare addresses from one interface + * rule 6 requires a policy table (optionally configurable) to match + * configurable labels + */ + /* rule 7: prefer temporary addresses (WE REVERSE THIS BY DEFAULT!) */ + if ((a->flags & IFA_F_TEMPORARY) != (b->flags & IFA_F_TEMPORARY)) + { + if (this->prefer_temporary_addrs) + { + return b->flags & IFA_F_TEMPORARY; + } + return a->flags & IFA_F_TEMPORARY; + } + /* rule 8: use longest matching prefix */ + if (d) + { + pa = common_prefix(a->ip, d); + pb = common_prefix(b->ip, d); + if (pa != pb) + { + return pb > pa; + } + } + /* default to addresses found earlier */ + return FALSE; +} + +/** + * Get a non-virtual IP address on the given interface. + * + * If a candidate address is given, we first search for that address and if not + * found return the address as above. + * Returned host is a clone, has to be freed by caller. + * + * this->lock must be held when calling this function. + */ +static host_t *get_interface_address(private_kernel_netlink_net_t *this, + int ifindex, int family, host_t *dest, + host_t *candidate) +{ + iface_entry_t *iface; + enumerator_t *addrs; + addr_entry_t *addr, *best = NULL; + + if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_index, + (void**)&iface, &ifindex) == SUCCESS) + { + if (iface->usable) + { /* only use interfaces not excluded by config */ + addrs = iface->addrs->create_enumerator(iface->addrs); + while (addrs->enumerate(addrs, &addr)) + { + if (addr->refcount || + addr->ip->get_family(addr->ip) != family) + { /* ignore virtual IP addresses and ensure family matches */ + continue; + } + if (candidate && candidate->ip_equals(candidate, addr->ip)) + { /* stop if we find the candidate */ + best = addr; + break; + } + else if (!best || is_address_better(this, best, addr, dest)) + { + best = addr; + } + } + addrs->destroy(addrs); + } + } + return best ? best->ip->clone(best->ip) : NULL; +} + +/** + * callback function that raises the delayed roam event + */ +static job_requeue_t roam_event(private_kernel_netlink_net_t *this) +{ + bool address; + + this->roam_lock->lock(this->roam_lock); + address = this->roam_address; + this->roam_address = FALSE; + this->roam_lock->unlock(this->roam_lock); + charon->kernel->roam(charon->kernel, address); + return JOB_REQUEUE_NONE; +} + +/** + * fire a roaming event. we delay it for a bit and fire only one event + * for multiple calls. otherwise we would create too many events. + */ +static void fire_roam_event(private_kernel_netlink_net_t *this, bool address) +{ + timeval_t now; + job_t *job; + + if (!this->roam_events) + { + return; + } + + time_monotonic(&now); + this->roam_lock->lock(this->roam_lock); + this->roam_address |= address; + if (!timercmp(&now, &this->next_roam, >)) + { + this->roam_lock->unlock(this->roam_lock); + return; + } + timeval_add_ms(&now, ROAM_DELAY); + this->next_roam = now; + this->roam_lock->unlock(this->roam_lock); + + job = (job_t*)callback_job_create((callback_job_cb_t)roam_event, + this, NULL, NULL); + lib->scheduler->schedule_job_ms(lib->scheduler, job, ROAM_DELAY); +} + +/** + * check if an interface with a given index is up and usable + * + * this->lock must be locked when calling this function + */ +static bool is_interface_up_and_usable(private_kernel_netlink_net_t *this, + int index) +{ + iface_entry_t *iface; + + if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_index, + (void**)&iface, &index) == SUCCESS) + { + return iface_entry_up_and_usable(iface); + } + return FALSE; +} + +/** + * unregister the current addr_entry_t from the hashtable it is stored in + * + * this->lock must be locked when calling this function + */ +static void addr_entry_unregister(addr_entry_t *addr, iface_entry_t *iface, + private_kernel_netlink_net_t *this) +{ + if (addr->refcount) + { + addr_map_entry_remove(this->vips, addr, iface); + this->condvar->broadcast(this->condvar); + return; + } + addr_map_entry_remove(this->addrs, addr, iface); +} + +/** + * process RTM_NEWLINK/RTM_DELLINK from kernel + */ +static void process_link(private_kernel_netlink_net_t *this, + struct nlmsghdr *hdr, bool event) +{ + struct ifinfomsg* msg = NLMSG_DATA(hdr); + struct rtattr *rta = IFLA_RTA(msg); + size_t rtasize = IFLA_PAYLOAD (hdr); + enumerator_t *enumerator; + iface_entry_t *current, *entry = NULL; + char *name = NULL; + bool update = FALSE, update_routes = FALSE; + + while (RTA_OK(rta, rtasize)) + { + switch (rta->rta_type) + { + case IFLA_IFNAME: + name = RTA_DATA(rta); + break; + } + rta = RTA_NEXT(rta, rtasize); + } + if (!name) + { + name = "(unknown)"; + } + + this->lock->write_lock(this->lock); + switch (hdr->nlmsg_type) + { + case RTM_NEWLINK: + { + if (this->ifaces->find_first(this->ifaces, + (void*)iface_entry_by_index, (void**)&entry, + &msg->ifi_index) != SUCCESS) + { + INIT(entry, + .ifindex = msg->ifi_index, + .addrs = linked_list_create(), + .usable = charon->kernel->is_interface_usable( + charon->kernel, name), + ); + this->ifaces->insert_last(this->ifaces, entry); + } + strncpy(entry->ifname, name, IFNAMSIZ); + entry->ifname[IFNAMSIZ-1] = '\0'; + if (event && entry->usable) + { + if (!(entry->flags & IFF_UP) && (msg->ifi_flags & IFF_UP)) + { + update = update_routes = TRUE; + DBG1(DBG_KNL, "interface %s activated", name); + } + if ((entry->flags & IFF_UP) && !(msg->ifi_flags & IFF_UP)) + { + update = TRUE; + DBG1(DBG_KNL, "interface %s deactivated", name); + } + } + entry->flags = msg->ifi_flags; + break; + } + case RTM_DELLINK: + { + enumerator = this->ifaces->create_enumerator(this->ifaces); + while (enumerator->enumerate(enumerator, ¤t)) + { + if (current->ifindex == msg->ifi_index) + { + if (event && current->usable) + { + update = TRUE; + DBG1(DBG_KNL, "interface %s deleted", current->ifname); + } + /* TODO: move virtual IPs installed on this interface to + * another interface? */ + this->ifaces->remove_at(this->ifaces, enumerator); + current->addrs->invoke_function(current->addrs, + (void*)addr_entry_unregister, current, this); + iface_entry_destroy(current); + break; + } + } + enumerator->destroy(enumerator); + break; + } + } + this->lock->unlock(this->lock); + + if (update_routes && event) + { + queue_route_reinstall(this, strdup(name)); + } + + if (update && event) + { + fire_roam_event(this, TRUE); + } +} + +/** + * process RTM_NEWADDR/RTM_DELADDR from kernel + */ +static void process_addr(private_kernel_netlink_net_t *this, + struct nlmsghdr *hdr, bool event) +{ + struct ifaddrmsg* msg = NLMSG_DATA(hdr); + struct rtattr *rta = IFA_RTA(msg); + size_t rtasize = IFA_PAYLOAD (hdr); + host_t *host = NULL; + iface_entry_t *iface; + chunk_t local = chunk_empty, address = chunk_empty; + char *route_ifname = NULL; + bool update = FALSE, found = FALSE, changed = FALSE; + + while (RTA_OK(rta, rtasize)) + { + switch (rta->rta_type) + { + case IFA_LOCAL: + local.ptr = RTA_DATA(rta); + local.len = RTA_PAYLOAD(rta); + break; + case IFA_ADDRESS: + address.ptr = RTA_DATA(rta); + address.len = RTA_PAYLOAD(rta); + break; + } + rta = RTA_NEXT(rta, rtasize); + } + + /* For PPP interfaces, we need the IFA_LOCAL address, + * IFA_ADDRESS is the peers address. But IFA_LOCAL is + * not included in all cases (IPv6?), so fallback to IFA_ADDRESS. */ + if (local.ptr) + { + host = host_create_from_chunk(msg->ifa_family, local, 0); + } + else if (address.ptr) + { + host = host_create_from_chunk(msg->ifa_family, address, 0); + } + + if (host == NULL) + { /* bad family? */ + return; + } + + this->lock->write_lock(this->lock); + if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_index, + (void**)&iface, &msg->ifa_index) == SUCCESS) + { + addr_map_entry_t *entry, lookup = { + .ip = host, + .iface = iface, + }; + addr_entry_t *addr; + + entry = this->vips->get(this->vips, &lookup); + if (entry) + { + if (hdr->nlmsg_type == RTM_NEWADDR) + { /* mark as installed and signal waiting threads */ + entry->addr->installed = TRUE; + } + else + { /* the address was already marked as uninstalled */ + addr = entry->addr; + iface->addrs->remove(iface->addrs, addr, NULL); + addr_map_entry_remove(this->vips, addr, iface); + addr_entry_destroy(addr); + } + /* no roam events etc. for virtual IPs */ + this->condvar->broadcast(this->condvar); + this->lock->unlock(this->lock); + host->destroy(host); + return; + } + entry = this->addrs->get(this->addrs, &lookup); + if (entry) + { + if (hdr->nlmsg_type == RTM_DELADDR) + { + found = TRUE; + addr = entry->addr; + iface->addrs->remove(iface->addrs, addr, NULL); + if (iface->usable) + { + changed = TRUE; + DBG1(DBG_KNL, "%H disappeared from %s", host, + iface->ifname); + } + addr_map_entry_remove(this->addrs, addr, iface); + addr_entry_destroy(addr); + } + } + else + { + if (hdr->nlmsg_type == RTM_NEWADDR) + { + found = TRUE; + changed = TRUE; + route_ifname = strdup(iface->ifname); + INIT(addr, + .ip = host->clone(host), + .flags = msg->ifa_flags, + .scope = msg->ifa_scope, + ); + iface->addrs->insert_last(iface->addrs, addr); + addr_map_entry_add(this->addrs, addr, iface); + if (event && iface->usable) + { + DBG1(DBG_KNL, "%H appeared on %s", host, iface->ifname); + } + } + } + if (found && (iface->flags & IFF_UP)) + { + update = TRUE; + } + if (!iface->usable) + { /* ignore events for interfaces excluded by config */ + update = changed = FALSE; + } + } + this->lock->unlock(this->lock); + + if (update && event && route_ifname) + { + queue_route_reinstall(this, route_ifname); + } + else + { + free(route_ifname); + } + host->destroy(host); + + /* send an update to all IKE_SAs */ + if (update && event && changed) + { + fire_roam_event(this, TRUE); + } +} + +/** + * process RTM_NEWROUTE and RTM_DELROUTE from kernel + */ +static void process_route(private_kernel_netlink_net_t *this, struct nlmsghdr *hdr) +{ + struct rtmsg* msg = NLMSG_DATA(hdr); + struct rtattr *rta = RTM_RTA(msg); + size_t rtasize = RTM_PAYLOAD(hdr); + u_int32_t rta_oif = 0; + host_t *host = NULL; + + /* ignore routes added by us or in the local routing table (local addrs) */ + if (msg->rtm_table && (msg->rtm_table == this->routing_table || + msg->rtm_table == RT_TABLE_LOCAL)) + { + return; + } + else if (msg->rtm_flags & RTM_F_CLONED) + { /* ignore cached routes, seem to be created a lot for IPv6 */ + return; + } + + while (RTA_OK(rta, rtasize)) + { + switch (rta->rta_type) + { + case RTA_PREFSRC: + DESTROY_IF(host); + host = host_create_from_chunk(msg->rtm_family, + chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta)), 0); + break; + case RTA_OIF: + if (RTA_PAYLOAD(rta) == sizeof(rta_oif)) + { + rta_oif = *(u_int32_t*)RTA_DATA(rta); + } + break; + } + rta = RTA_NEXT(rta, rtasize); + } + this->lock->read_lock(this->lock); + if (rta_oif && !is_interface_up_and_usable(this, rta_oif)) + { /* ignore route changes for interfaces that are ignored or down */ + this->lock->unlock(this->lock); + DESTROY_IF(host); + return; + } + if (!host && rta_oif) + { + host = get_interface_address(this, rta_oif, msg->rtm_family, + NULL, NULL); + } + if (!host || is_known_vip(this, host)) + { /* ignore routes added for virtual IPs */ + this->lock->unlock(this->lock); + DESTROY_IF(host); + return; + } + this->lock->unlock(this->lock); + fire_roam_event(this, FALSE); + host->destroy(host); +} + +/** + * Receives events from kernel + */ +static bool receive_events(private_kernel_netlink_net_t *this, int fd, + watcher_event_t event) +{ + char response[1536]; + struct nlmsghdr *hdr = (struct nlmsghdr*)response; + struct sockaddr_nl addr; + socklen_t addr_len = sizeof(addr); + int len; + + len = recvfrom(this->socket_events, response, sizeof(response), + MSG_DONTWAIT, (struct sockaddr*)&addr, &addr_len); + if (len < 0) + { + switch (errno) + { + case EINTR: + /* interrupted, try again */ + return TRUE; + case EAGAIN: + /* no data ready, select again */ + return TRUE; + default: + DBG1(DBG_KNL, "unable to receive from rt event socket"); + sleep(1); + return TRUE; + } + } + + if (addr.nl_pid != 0) + { /* not from kernel. not interested, try another one */ + return TRUE; + } + + while (NLMSG_OK(hdr, len)) + { + /* looks good so far, dispatch netlink message */ + switch (hdr->nlmsg_type) + { + case RTM_NEWADDR: + case RTM_DELADDR: + process_addr(this, hdr, TRUE); + break; + case RTM_NEWLINK: + case RTM_DELLINK: + process_link(this, hdr, TRUE); + break; + case RTM_NEWROUTE: + case RTM_DELROUTE: + if (this->process_route) + { + process_route(this, hdr); + } + break; + default: + break; + } + hdr = NLMSG_NEXT(hdr, len); + } + return TRUE; +} + +/** enumerator over addresses */ +typedef struct { + private_kernel_netlink_net_t* this; + /** which addresses to enumerate */ + kernel_address_type_t which; +} address_enumerator_t; + +/** + * cleanup function for address enumerator + */ +static void address_enumerator_destroy(address_enumerator_t *data) +{ + data->this->lock->unlock(data->this->lock); + free(data); +} + +/** + * filter for addresses + */ +static bool filter_addresses(address_enumerator_t *data, + addr_entry_t** in, host_t** out) +{ + if (!(data->which & ADDR_TYPE_VIRTUAL) && (*in)->refcount) + { /* skip virtual interfaces added by us */ + return FALSE; + } + if (!(data->which & ADDR_TYPE_REGULAR) && !(*in)->refcount) + { /* address is regular, but not requested */ + return FALSE; + } + if ((*in)->scope >= RT_SCOPE_LINK) + { /* skip addresses with a unusable scope */ + return FALSE; + } + *out = (*in)->ip; + return TRUE; +} + +/** + * enumerator constructor for interfaces + */ +static enumerator_t *create_iface_enumerator(iface_entry_t *iface, + address_enumerator_t *data) +{ + return enumerator_create_filter( + iface->addrs->create_enumerator(iface->addrs), + (void*)filter_addresses, data, NULL); +} + +/** + * filter for interfaces + */ +static bool filter_interfaces(address_enumerator_t *data, iface_entry_t** in, + iface_entry_t** out) +{ + if (!(data->which & ADDR_TYPE_IGNORED) && !(*in)->usable) + { /* skip interfaces excluded by config */ + return FALSE; + } + if (!(data->which & ADDR_TYPE_LOOPBACK) && ((*in)->flags & IFF_LOOPBACK)) + { /* ignore loopback devices */ + return FALSE; + } + if (!(data->which & ADDR_TYPE_DOWN) && !((*in)->flags & IFF_UP)) + { /* skip interfaces not up */ + return FALSE; + } + *out = *in; + return TRUE; +} + +METHOD(kernel_net_t, create_address_enumerator, enumerator_t*, + private_kernel_netlink_net_t *this, kernel_address_type_t which) +{ + address_enumerator_t *data; + + INIT(data, + .this = this, + .which = which, + ); + + this->lock->read_lock(this->lock); + return enumerator_create_nested( + enumerator_create_filter( + this->ifaces->create_enumerator(this->ifaces), + (void*)filter_interfaces, data, NULL), + (void*)create_iface_enumerator, data, + (void*)address_enumerator_destroy); +} + +METHOD(kernel_net_t, get_interface_name, bool, + private_kernel_netlink_net_t *this, host_t* ip, char **name) +{ + addr_map_entry_t *entry, lookup = { + .ip = ip, + }; + + if (ip->is_anyaddr(ip)) + { + return FALSE; + } + this->lock->read_lock(this->lock); + /* first try to find it on an up and usable interface */ + entry = this->addrs->get_match(this->addrs, &lookup, + (void*)addr_map_entry_match_up_and_usable); + if (entry) + { + if (name) + { + *name = strdup(entry->iface->ifname); + DBG2(DBG_KNL, "%H is on interface %s", ip, *name); + } + this->lock->unlock(this->lock); + return TRUE; + } + /* in a second step, consider virtual IPs installed by us */ + entry = this->vips->get_match(this->vips, &lookup, + (void*)addr_map_entry_match_up_and_usable); + if (entry) + { + if (name) + { + *name = strdup(entry->iface->ifname); + DBG2(DBG_KNL, "virtual IP %H is on interface %s", ip, *name); + } + this->lock->unlock(this->lock); + return TRUE; + } + /* maybe it is installed on an ignored interface */ + entry = this->addrs->get_match(this->addrs, &lookup, + (void*)addr_map_entry_match_up); + if (!entry) + { + DBG2(DBG_KNL, "%H is not a local address or the interface is down", ip); + } + this->lock->unlock(this->lock); + return FALSE; +} + +/** + * get the index of an interface by name + */ +static int get_interface_index(private_kernel_netlink_net_t *this, char* name) +{ + iface_entry_t *iface; + int ifindex = 0; + + DBG2(DBG_KNL, "getting iface index for %s", name); + + this->lock->read_lock(this->lock); + if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_name, + (void**)&iface, name) == SUCCESS) + { + ifindex = iface->ifindex; + } + this->lock->unlock(this->lock); + + if (ifindex == 0) + { + DBG1(DBG_KNL, "unable to get interface index for %s", name); + } + return ifindex; +} + +/** + * check if an address or net (addr with prefix net bits) is in + * subnet (net with net_len net bits) + */ +static bool addr_in_subnet(chunk_t addr, int prefix, chunk_t net, int net_len) +{ + static const u_char mask[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe }; + int byte = 0; + + if (net_len == 0) + { /* any address matches a /0 network */ + return TRUE; + } + if (addr.len != net.len || net_len > 8 * net.len || prefix < net_len) + { + return FALSE; + } + /* scan through all bytes in network order */ + while (net_len > 0) + { + if (net_len < 8) + { + return (mask[net_len] & addr.ptr[byte]) == (mask[net_len] & net.ptr[byte]); + } + else + { + if (addr.ptr[byte] != net.ptr[byte]) + { + return FALSE; + } + byte++; + net_len -= 8; + } + } + return TRUE; +} + +/** + * Store information about a route retrieved via RTNETLINK + */ +typedef struct { + chunk_t gtw; + chunk_t src; + chunk_t dst; + host_t *src_host; + u_int8_t dst_len; + u_int32_t table; + u_int32_t oif; + u_int32_t priority; +} rt_entry_t; + +/** + * Free a route entry + */ +static void rt_entry_destroy(rt_entry_t *this) +{ + DESTROY_IF(this->src_host); + free(this); +} + +/** + * Check if the route received with RTM_NEWROUTE is usable based on its type. + */ +static bool route_usable(struct nlmsghdr *hdr) +{ + struct rtmsg *msg; + + msg = NLMSG_DATA(hdr); + switch (msg->rtm_type) + { + case RTN_BLACKHOLE: + case RTN_UNREACHABLE: + case RTN_PROHIBIT: + case RTN_THROW: + return FALSE; + default: + return TRUE; + } +} + +/** + * Parse route received with RTM_NEWROUTE. The given rt_entry_t object will be + * reused if not NULL. + * + * Returned chunks point to internal data of the Netlink message. + */ +static rt_entry_t *parse_route(struct nlmsghdr *hdr, rt_entry_t *route) +{ + struct rtattr *rta; + struct rtmsg *msg; + size_t rtasize; + + msg = NLMSG_DATA(hdr); + rta = RTM_RTA(msg); + rtasize = RTM_PAYLOAD(hdr); + + if (route) + { + route->gtw = chunk_empty; + route->src = chunk_empty; + route->dst = chunk_empty; + route->dst_len = msg->rtm_dst_len; + route->table = msg->rtm_table; + route->oif = 0; + route->priority = 0; + } + else + { + INIT(route, + .dst_len = msg->rtm_dst_len, + .table = msg->rtm_table, + ); + } + + while (RTA_OK(rta, rtasize)) + { + switch (rta->rta_type) + { + case RTA_PREFSRC: + route->src = chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta)); + break; + case RTA_GATEWAY: + route->gtw = chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta)); + break; + case RTA_DST: + route->dst = chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta)); + break; + case RTA_OIF: + if (RTA_PAYLOAD(rta) == sizeof(route->oif)) + { + route->oif = *(u_int32_t*)RTA_DATA(rta); + } + break; + case RTA_PRIORITY: + if (RTA_PAYLOAD(rta) == sizeof(route->priority)) + { + route->priority = *(u_int32_t*)RTA_DATA(rta); + } + break; +#ifdef HAVE_RTA_TABLE + case RTA_TABLE: + if (RTA_PAYLOAD(rta) == sizeof(route->table)) + { + route->table = *(u_int32_t*)RTA_DATA(rta); + } + break; +#endif /* HAVE_RTA_TABLE*/ + } + rta = RTA_NEXT(rta, rtasize); + } + return route; +} + +/** + * Get a route: If "nexthop", the nexthop is returned. source addr otherwise. + */ +static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest, + int prefix, bool nexthop, host_t *candidate, + u_int recursion) +{ + netlink_buf_t request; + struct nlmsghdr *hdr, *out, *current; + struct rtmsg *msg; + chunk_t chunk; + size_t len; + linked_list_t *routes; + rt_entry_t *route = NULL, *best = NULL; + enumerator_t *enumerator; + host_t *addr = NULL; + bool match_net; + int family; + + if (recursion > MAX_ROUTE_RECURSION) + { + return NULL; + } + chunk = dest->get_address(dest); + len = chunk.len * 8; + prefix = prefix < 0 ? len : min(prefix, len); + match_net = prefix != len; + + memset(&request, 0, sizeof(request)); + + family = dest->get_family(dest); + hdr = &request.hdr; + hdr->nlmsg_flags = NLM_F_REQUEST; + hdr->nlmsg_type = RTM_GETROUTE; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + + msg = NLMSG_DATA(hdr); + msg->rtm_family = family; + if (!match_net && this->rta_mark && this->routing_mark.value) + { + /* if our routing rule excludes packets with a certain mark we can + * get the preferred route without having to dump all routes */ + chunk = chunk_from_thing(this->routing_mark.value); + netlink_add_attribute(hdr, RTA_MARK, chunk, sizeof(request)); + } + else if (family == AF_INET || this->rta_prefsrc_for_ipv6 || + this->routing_table || match_net) + { /* kernels prior to 3.0 do not support RTA_PREFSRC for IPv6 routes. + * as we want to ignore routes with virtual IPs we cannot use DUMP + * if these routes are not installed in a separate table */ + hdr->nlmsg_flags |= NLM_F_DUMP; + } + if (candidate) + { + chunk = candidate->get_address(candidate); + netlink_add_attribute(hdr, RTA_PREFSRC, chunk, sizeof(request)); + } + /* we use this below to match against the routes */ + chunk = dest->get_address(dest); + if (!match_net) + { + netlink_add_attribute(hdr, RTA_DST, chunk, sizeof(request)); + } + + if (this->socket->send(this->socket, hdr, &out, &len) != SUCCESS) + { + DBG2(DBG_KNL, "getting %s to reach %H/%d failed", + nexthop ? "nexthop" : "address", dest, prefix); + return NULL; + } + routes = linked_list_create(); + this->lock->read_lock(this->lock); + + for (current = out; NLMSG_OK(current, len); + current = NLMSG_NEXT(current, len)) + { + switch (current->nlmsg_type) + { + case NLMSG_DONE: + break; + case RTM_NEWROUTE: + { + rt_entry_t *other; + uintptr_t table; + + if (!route_usable(current)) + { + continue; + } + route = parse_route(current, route); + + table = (uintptr_t)route->table; + if (this->rt_exclude->find_first(this->rt_exclude, NULL, + (void**)&table) == SUCCESS) + { /* route is from an excluded routing table */ + continue; + } + if (this->routing_table != 0 && + route->table == this->routing_table) + { /* route is from our own ipsec routing table */ + continue; + } + if (route->oif && !is_interface_up_and_usable(this, route->oif)) + { /* interface is down */ + continue; + } + if (!addr_in_subnet(chunk, prefix, route->dst, route->dst_len)) + { /* route destination does not contain dest */ + continue; + } + if (route->src.ptr) + { /* verify source address, if any */ + host_t *src = host_create_from_chunk(msg->rtm_family, + route->src, 0); + if (src && is_known_vip(this, src)) + { /* ignore routes installed by us */ + src->destroy(src); + continue; + } + route->src_host = src; + } + /* insert route, sorted by priority and network prefix */ + enumerator = routes->create_enumerator(routes); + while (enumerator->enumerate(enumerator, &other)) + { + if (route->priority < other->priority) + { + break; + } + if (route->priority == other->priority && + route->dst_len > other->dst_len) + { + break; + } + } + routes->insert_before(routes, enumerator, route); + enumerator->destroy(enumerator); + route = NULL; + continue; + } + default: + continue; + } + break; + } + if (route) + { + rt_entry_destroy(route); + } + + /* now we have a list of routes matching dest, sorted by net prefix. + * we will look for source addresses for these routes and select the one + * with the preferred source address, if possible */ + enumerator = routes->create_enumerator(routes); + while (enumerator->enumerate(enumerator, &route)) + { + if (route->src_host) + { /* got a source address with the route, if no preferred source + * is given or it matches we are done, as this is the best route */ + if (!candidate || candidate->ip_equals(candidate, route->src_host)) + { + best = route; + break; + } + else if (route->oif) + { /* no match yet, maybe it is assigned to the same interface */ + host_t *src = get_interface_address(this, route->oif, + msg->rtm_family, dest, candidate); + if (src && src->ip_equals(src, candidate)) + { + route->src_host->destroy(route->src_host); + route->src_host = src; + best = route; + break; + } + DESTROY_IF(src); + } + /* no luck yet with the source address. if this is the best (first) + * route we store it as fallback in case we don't find a route with + * the preferred source */ + best = best ?: route; + continue; + } + if (route->oif) + { /* no src, but an interface - get address from it */ + route->src_host = get_interface_address(this, route->oif, + msg->rtm_family, dest, candidate); + if (route->src_host) + { /* we handle this address the same as the one above */ + if (!candidate || + candidate->ip_equals(candidate, route->src_host)) + { + best = route; + break; + } + best = best ?: route; + continue; + } + } + if (route->gtw.ptr) + { /* no src, no iface, but a gateway - lookup src to reach gtw */ + host_t *gtw; + + gtw = host_create_from_chunk(msg->rtm_family, route->gtw, 0); + if (gtw && !gtw->ip_equals(gtw, dest)) + { + route->src_host = get_route(this, gtw, -1, FALSE, candidate, + recursion + 1); + } + DESTROY_IF(gtw); + if (route->src_host) + { /* more of the same */ + if (!candidate || + candidate->ip_equals(candidate, route->src_host)) + { + best = route; + break; + } + best = best ?: route; + } + } + } + enumerator->destroy(enumerator); + + if (nexthop) + { /* nexthop lookup, return gateway if any */ + if (best || routes->get_first(routes, (void**)&best) == SUCCESS) + { + addr = host_create_from_chunk(msg->rtm_family, best->gtw, 0); + } + if (!addr && !match_net) + { /* fallback to destination address */ + addr = dest->clone(dest); + } + } + else + { + if (best) + { + addr = best->src_host->clone(best->src_host); + } + } + this->lock->unlock(this->lock); + routes->destroy_function(routes, (void*)rt_entry_destroy); + free(out); + + if (addr) + { + DBG2(DBG_KNL, "using %H as %s to reach %H/%d", addr, + nexthop ? "nexthop" : "address", dest, prefix); + } + else if (!recursion) + { + DBG2(DBG_KNL, "no %s found to reach %H/%d", + nexthop ? "nexthop" : "address", dest, prefix); + } + return addr; +} + +METHOD(kernel_net_t, get_source_addr, host_t*, + private_kernel_netlink_net_t *this, host_t *dest, host_t *src) +{ + return get_route(this, dest, -1, FALSE, src, 0); +} + +METHOD(kernel_net_t, get_nexthop, host_t*, + private_kernel_netlink_net_t *this, host_t *dest, int prefix, host_t *src) +{ + return get_route(this, dest, prefix, TRUE, src, 0); +} + +/** + * Manages the creation and deletion of ip addresses on an interface. + * By setting the appropriate nlmsg_type, the ip will be set or unset. + */ +static status_t manage_ipaddr(private_kernel_netlink_net_t *this, int nlmsg_type, + int flags, int if_index, host_t *ip, int prefix) +{ + netlink_buf_t request; + struct nlmsghdr *hdr; + struct ifaddrmsg *msg; + chunk_t chunk; + + memset(&request, 0, sizeof(request)); + + chunk = ip->get_address(ip); + + hdr = &request.hdr; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags; + hdr->nlmsg_type = nlmsg_type; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + + msg = NLMSG_DATA(hdr); + msg->ifa_family = ip->get_family(ip); + msg->ifa_flags = 0; + msg->ifa_prefixlen = prefix < 0 ? chunk.len * 8 : prefix; + msg->ifa_scope = RT_SCOPE_UNIVERSE; + msg->ifa_index = if_index; + + netlink_add_attribute(hdr, IFA_LOCAL, chunk, sizeof(request)); + + if (ip->get_family(ip) == AF_INET6 && this->rta_prefsrc_for_ipv6) + { /* if source routes are possible we let the virtual IP get deprecated + * immediately (but mark it as valid forever) so it gets only used if + * forced by our route, and not by the default IPv6 address selection */ + struct ifa_cacheinfo cache = { + .ifa_valid = 0xFFFFFFFF, + .ifa_prefered = 0, + }; + netlink_add_attribute(hdr, IFA_CACHEINFO, chunk_from_thing(cache), + sizeof(request)); + } + return this->socket->send_ack(this->socket, hdr); +} + +METHOD(kernel_net_t, add_ip, status_t, + private_kernel_netlink_net_t *this, host_t *virtual_ip, int prefix, + char *iface_name) +{ + addr_map_entry_t *entry, lookup = { + .ip = virtual_ip, + }; + iface_entry_t *iface = NULL; + + if (!this->install_virtual_ip) + { /* disabled by config */ + return SUCCESS; + } + + this->lock->write_lock(this->lock); + /* the virtual IP might actually be installed as regular IP, in which case + * we don't track it as virtual IP */ + entry = this->addrs->get_match(this->addrs, &lookup, + (void*)addr_map_entry_match); + if (!entry) + { /* otherwise it might already be installed as virtual IP */ + entry = this->vips->get_match(this->vips, &lookup, + (void*)addr_map_entry_match); + if (entry) + { /* the vip we found can be in one of three states: 1) installed and + * ready, 2) just added by another thread, but not yet confirmed to + * be installed by the kernel, 3) just deleted, but not yet gone. + * Then while we wait below, several things could happen (as we + * release the lock). For instance, the interface could disappear, + * or the IP is finally deleted, and it reappears on a different + * interface. All these cases are handled by the call below. */ + while (!is_vip_installed_or_gone(this, virtual_ip, &entry)) + { + this->condvar->wait(this->condvar, this->lock); + } + if (entry) + { + entry->addr->refcount++; + } + } + } + if (entry) + { + DBG2(DBG_KNL, "virtual IP %H is already installed on %s", virtual_ip, + entry->iface->ifname); + this->lock->unlock(this->lock); + return SUCCESS; + } + /* try to find the target interface, either by config or via src ip */ + if (!this->install_virtual_ip_on || + this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_name, + (void**)&iface, this->install_virtual_ip_on) != SUCCESS) + { + if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_name, + (void**)&iface, iface_name) != SUCCESS) + { /* if we don't find the requested interface we just use the first */ + this->ifaces->get_first(this->ifaces, (void**)&iface); + } + } + if (iface) + { + addr_entry_t *addr; + char *ifname; + int ifi; + + INIT(addr, + .ip = virtual_ip->clone(virtual_ip), + .refcount = 1, + .scope = RT_SCOPE_UNIVERSE, + ); + iface->addrs->insert_last(iface->addrs, addr); + addr_map_entry_add(this->vips, addr, iface); + ifi = iface->ifindex; + this->lock->unlock(this->lock); + if (manage_ipaddr(this, RTM_NEWADDR, NLM_F_CREATE | NLM_F_EXCL, + ifi, virtual_ip, prefix) == SUCCESS) + { + this->lock->write_lock(this->lock); + while (!is_vip_installed_or_gone(this, virtual_ip, &entry)) + { /* wait until address appears */ + this->condvar->wait(this->condvar, this->lock); + } + if (entry) + { /* we fail if the interface got deleted in the meantime */ + ifname = strdup(entry->iface->ifname); + this->lock->unlock(this->lock); + DBG2(DBG_KNL, "virtual IP %H installed on %s", + virtual_ip, ifname); + /* during IKEv1 reauthentication, children get moved from + * old the new SA before the virtual IP is available. This + * kills the route for our virtual IP, reinstall. */ + queue_route_reinstall(this, ifname); + return SUCCESS; + } + this->lock->unlock(this->lock); + } + DBG1(DBG_KNL, "adding virtual IP %H failed", virtual_ip); + return FAILED; + } + this->lock->unlock(this->lock); + DBG1(DBG_KNL, "no interface available, unable to install virtual IP %H", + virtual_ip); + return FAILED; +} + +METHOD(kernel_net_t, del_ip, status_t, + private_kernel_netlink_net_t *this, host_t *virtual_ip, int prefix, + bool wait) +{ + addr_map_entry_t *entry, lookup = { + .ip = virtual_ip, + }; + + if (!this->install_virtual_ip) + { /* disabled by config */ + return SUCCESS; + } + + DBG2(DBG_KNL, "deleting virtual IP %H", virtual_ip); + + this->lock->write_lock(this->lock); + entry = this->vips->get_match(this->vips, &lookup, + (void*)addr_map_entry_match); + if (!entry) + { /* we didn't install this IP as virtual IP */ + entry = this->addrs->get_match(this->addrs, &lookup, + (void*)addr_map_entry_match); + if (entry) + { + DBG2(DBG_KNL, "not deleting existing IP %H on %s", virtual_ip, + entry->iface->ifname); + this->lock->unlock(this->lock); + return SUCCESS; + } + DBG2(DBG_KNL, "virtual IP %H not cached, unable to delete", virtual_ip); + this->lock->unlock(this->lock); + return FAILED; + } + if (entry->addr->refcount == 1) + { + status_t status; + int ifi; + + /* we set this flag so that threads calling add_ip will block and wait + * until the entry is gone, also so we can wait below */ + entry->addr->installed = FALSE; + ifi = entry->iface->ifindex; + this->lock->unlock(this->lock); + status = manage_ipaddr(this, RTM_DELADDR, 0, ifi, virtual_ip, prefix); + if (status == SUCCESS && wait) + { /* wait until the address is really gone */ + this->lock->write_lock(this->lock); + while (is_known_vip(this, virtual_ip)) + { + this->condvar->wait(this->condvar, this->lock); + } + this->lock->unlock(this->lock); + } + return status; + } + else + { + entry->addr->refcount--; + } + DBG2(DBG_KNL, "virtual IP %H used by other SAs, not deleting", + virtual_ip); + this->lock->unlock(this->lock); + return SUCCESS; +} + +/** + * Manages source routes in the routing table. + * By setting the appropriate nlmsg_type, the route gets added or removed. + */ +static status_t manage_srcroute(private_kernel_netlink_net_t *this, + int nlmsg_type, int flags, chunk_t dst_net, + u_int8_t prefixlen, host_t *gateway, + host_t *src_ip, char *if_name) +{ + netlink_buf_t request; + struct nlmsghdr *hdr; + struct rtmsg *msg; + struct rtattr *rta; + int ifindex; + chunk_t chunk; + + /* if route is 0.0.0.0/0, we can't install it, as it would + * overwrite the default route. Instead, we add two routes: + * 0.0.0.0/1 and 128.0.0.0/1 */ + if (this->routing_table == 0 && prefixlen == 0) + { + chunk_t half_net; + u_int8_t half_prefixlen; + status_t status; + + half_net = chunk_alloca(dst_net.len); + memset(half_net.ptr, 0, half_net.len); + half_prefixlen = 1; + + status = manage_srcroute(this, nlmsg_type, flags, half_net, half_prefixlen, + gateway, src_ip, if_name); + half_net.ptr[0] |= 0x80; + status = manage_srcroute(this, nlmsg_type, flags, half_net, half_prefixlen, + gateway, src_ip, if_name); + return status; + } + + memset(&request, 0, sizeof(request)); + + hdr = &request.hdr; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags; + hdr->nlmsg_type = nlmsg_type; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + + msg = NLMSG_DATA(hdr); + msg->rtm_family = src_ip->get_family(src_ip); + msg->rtm_dst_len = prefixlen; + msg->rtm_table = this->routing_table; + msg->rtm_protocol = RTPROT_STATIC; + msg->rtm_type = RTN_UNICAST; + msg->rtm_scope = RT_SCOPE_UNIVERSE; + + netlink_add_attribute(hdr, RTA_DST, dst_net, sizeof(request)); + chunk = src_ip->get_address(src_ip); + netlink_add_attribute(hdr, RTA_PREFSRC, chunk, sizeof(request)); + if (gateway && gateway->get_family(gateway) == src_ip->get_family(src_ip)) + { + chunk = gateway->get_address(gateway); + netlink_add_attribute(hdr, RTA_GATEWAY, chunk, sizeof(request)); + } + ifindex = get_interface_index(this, if_name); + chunk.ptr = (char*)&ifindex; + chunk.len = sizeof(ifindex); + netlink_add_attribute(hdr, RTA_OIF, chunk, sizeof(request)); + + if (this->mtu || this->mss) + { + chunk = chunk_alloca(RTA_LENGTH((sizeof(struct rtattr) + + sizeof(u_int32_t)) * 2)); + chunk.len = 0; + rta = (struct rtattr*)chunk.ptr; + if (this->mtu) + { + rta->rta_type = RTAX_MTU; + rta->rta_len = RTA_LENGTH(sizeof(u_int32_t)); + memcpy(RTA_DATA(rta), &this->mtu, sizeof(u_int32_t)); + chunk.len = rta->rta_len; + } + if (this->mss) + { + rta = (struct rtattr*)(chunk.ptr + RTA_ALIGN(chunk.len)); + rta->rta_type = RTAX_ADVMSS; + rta->rta_len = RTA_LENGTH(sizeof(u_int32_t)); + memcpy(RTA_DATA(rta), &this->mss, sizeof(u_int32_t)); + chunk.len = RTA_ALIGN(chunk.len) + rta->rta_len; + } + netlink_add_attribute(hdr, RTA_METRICS, chunk, sizeof(request)); + } + + return this->socket->send_ack(this->socket, hdr); +} + +METHOD(kernel_net_t, add_route, status_t, + private_kernel_netlink_net_t *this, chunk_t dst_net, u_int8_t prefixlen, + host_t *gateway, host_t *src_ip, char *if_name) +{ + status_t status; + route_entry_t *found, route = { + .dst_net = dst_net, + .prefixlen = prefixlen, + .gateway = gateway, + .src_ip = src_ip, + .if_name = if_name, + }; + + this->routes_lock->lock(this->routes_lock); + found = this->routes->get(this->routes, &route); + if (found) + { + this->routes_lock->unlock(this->routes_lock); + return ALREADY_DONE; + } + status = manage_srcroute(this, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, + dst_net, prefixlen, gateway, src_ip, if_name); + if (status == SUCCESS) + { + found = route_entry_clone(&route); + this->routes->put(this->routes, found, found); + } + this->routes_lock->unlock(this->routes_lock); + return status; +} + +METHOD(kernel_net_t, del_route, status_t, + private_kernel_netlink_net_t *this, chunk_t dst_net, u_int8_t prefixlen, + host_t *gateway, host_t *src_ip, char *if_name) +{ + status_t status; + route_entry_t *found, route = { + .dst_net = dst_net, + .prefixlen = prefixlen, + .gateway = gateway, + .src_ip = src_ip, + .if_name = if_name, + }; + + this->routes_lock->lock(this->routes_lock); + found = this->routes->get(this->routes, &route); + if (!found) + { + this->routes_lock->unlock(this->routes_lock); + return NOT_FOUND; + } + this->routes->remove(this->routes, found); + route_entry_destroy(found); + status = manage_srcroute(this, RTM_DELROUTE, 0, dst_net, prefixlen, + gateway, src_ip, if_name); + this->routes_lock->unlock(this->routes_lock); + return status; +} + +/** + * Initialize a list of local addresses. + */ +static status_t init_address_list(private_kernel_netlink_net_t *this) +{ + netlink_buf_t request; + struct nlmsghdr *out, *current, *in; + struct rtgenmsg *msg; + size_t len; + enumerator_t *ifaces, *addrs; + iface_entry_t *iface; + addr_entry_t *addr; + + DBG2(DBG_KNL, "known interfaces and IP addresses:"); + + memset(&request, 0, sizeof(request)); + + in = &request.hdr; + in->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); + in->nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH | NLM_F_ROOT; + msg = NLMSG_DATA(in); + msg->rtgen_family = AF_UNSPEC; + + /* get all links */ + in->nlmsg_type = RTM_GETLINK; + if (this->socket->send(this->socket, in, &out, &len) != SUCCESS) + { + return FAILED; + } + current = out; + while (NLMSG_OK(current, len)) + { + switch (current->nlmsg_type) + { + case NLMSG_DONE: + break; + case RTM_NEWLINK: + process_link(this, current, FALSE); + /* fall through */ + default: + current = NLMSG_NEXT(current, len); + continue; + } + break; + } + free(out); + + /* get all interface addresses */ + in->nlmsg_type = RTM_GETADDR; + if (this->socket->send(this->socket, in, &out, &len) != SUCCESS) + { + return FAILED; + } + current = out; + while (NLMSG_OK(current, len)) + { + switch (current->nlmsg_type) + { + case NLMSG_DONE: + break; + case RTM_NEWADDR: + process_addr(this, current, FALSE); + /* fall through */ + default: + current = NLMSG_NEXT(current, len); + continue; + } + break; + } + free(out); + + this->lock->read_lock(this->lock); + ifaces = this->ifaces->create_enumerator(this->ifaces); + while (ifaces->enumerate(ifaces, &iface)) + { + if (iface_entry_up_and_usable(iface)) + { + DBG2(DBG_KNL, " %s", iface->ifname); + addrs = iface->addrs->create_enumerator(iface->addrs); + while (addrs->enumerate(addrs, (void**)&addr)) + { + DBG2(DBG_KNL, " %H", addr->ip); + } + addrs->destroy(addrs); + } + } + ifaces->destroy(ifaces); + this->lock->unlock(this->lock); + return SUCCESS; +} + +/** + * create or delete a rule to use our routing table + */ +static status_t manage_rule(private_kernel_netlink_net_t *this, int nlmsg_type, + int family, u_int32_t table, u_int32_t prio) +{ + netlink_buf_t request; + struct nlmsghdr *hdr; + struct rtmsg *msg; + chunk_t chunk; + char *fwmark; + + memset(&request, 0, sizeof(request)); + hdr = &request.hdr; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = nlmsg_type; + if (nlmsg_type == RTM_NEWRULE) + { + hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; + } + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + + msg = NLMSG_DATA(hdr); + msg->rtm_table = table; + msg->rtm_family = family; + msg->rtm_protocol = RTPROT_BOOT; + msg->rtm_scope = RT_SCOPE_UNIVERSE; + msg->rtm_type = RTN_UNICAST; + + chunk = chunk_from_thing(prio); + netlink_add_attribute(hdr, RTA_PRIORITY, chunk, sizeof(request)); + + fwmark = lib->settings->get_str(lib->settings, + "%s.plugins.kernel-netlink.fwmark", NULL, lib->ns); + if (fwmark) + { +#ifdef HAVE_LINUX_FIB_RULES_H + mark_t mark; + + if (fwmark[0] == '!') + { + msg->rtm_flags |= FIB_RULE_INVERT; + fwmark++; + } + if (mark_from_string(fwmark, &mark)) + { + chunk = chunk_from_thing(mark.value); + netlink_add_attribute(hdr, FRA_FWMARK, chunk, sizeof(request)); + chunk = chunk_from_thing(mark.mask); + netlink_add_attribute(hdr, FRA_FWMASK, chunk, sizeof(request)); + if (msg->rtm_flags & FIB_RULE_INVERT) + { + this->routing_mark = mark; + } + } +#else + DBG1(DBG_KNL, "setting firewall mark on routing rule is not supported"); +#endif + } + return this->socket->send_ack(this->socket, hdr); +} + +/** + * check for kernel features (currently only via version number) + */ +static void check_kernel_features(private_kernel_netlink_net_t *this) +{ + struct utsname utsname; + int a, b, c; + + if (uname(&utsname) == 0) + { + switch(sscanf(utsname.release, "%d.%d.%d", &a, &b, &c)) + { + case 3: + if (a == 2) + { + if (b == 6 && c >= 36) + { + this->rta_mark = TRUE; + } + DBG2(DBG_KNL, "detected Linux %d.%d.%d, no support for " + "RTA_PREFSRC for IPv6 routes", a, b, c); + break; + } + /* fall-through */ + case 2: + /* only 3.x+ uses two part version numbers */ + this->rta_prefsrc_for_ipv6 = TRUE; + this->rta_mark = TRUE; + break; + default: + break; + } + } +} + +/** + * Destroy an address to iface map + */ +static void addr_map_destroy(hashtable_t *map) +{ + enumerator_t *enumerator; + addr_map_entry_t *addr; + + enumerator = map->create_enumerator(map); + while (enumerator->enumerate(enumerator, NULL, (void**)&addr)) + { + free(addr); + } + enumerator->destroy(enumerator); + map->destroy(map); +} + +METHOD(kernel_net_t, destroy, void, + private_kernel_netlink_net_t *this) +{ + enumerator_t *enumerator; + route_entry_t *route; + + if (this->routing_table) + { + manage_rule(this, RTM_DELRULE, AF_INET, this->routing_table, + this->routing_table_prio); + manage_rule(this, RTM_DELRULE, AF_INET6, this->routing_table, + this->routing_table_prio); + } + if (this->socket_events > 0) + { + lib->watcher->remove(lib->watcher, this->socket_events); + close(this->socket_events); + } + enumerator = this->routes->create_enumerator(this->routes); + while (enumerator->enumerate(enumerator, NULL, (void**)&route)) + { + manage_srcroute(this, RTM_DELROUTE, 0, route->dst_net, route->prefixlen, + route->gateway, route->src_ip, route->if_name); + route_entry_destroy(route); + } + enumerator->destroy(enumerator); + this->routes->destroy(this->routes); + this->routes_lock->destroy(this->routes_lock); + DESTROY_IF(this->socket); + + net_changes_clear(this); + this->net_changes->destroy(this->net_changes); + this->net_changes_lock->destroy(this->net_changes_lock); + + addr_map_destroy(this->addrs); + addr_map_destroy(this->vips); + + this->ifaces->destroy_function(this->ifaces, (void*)iface_entry_destroy); + this->rt_exclude->destroy(this->rt_exclude); + this->roam_lock->destroy(this->roam_lock); + this->condvar->destroy(this->condvar); + this->lock->destroy(this->lock); + free(this); +} + +/* + * Described in header. + */ +kernel_netlink_net_t *kernel_netlink_net_create() +{ + private_kernel_netlink_net_t *this; + enumerator_t *enumerator; + bool register_for_events = TRUE; + char *exclude; + + INIT(this, + .public = { + .interface = { + .get_interface = _get_interface_name, + .create_address_enumerator = _create_address_enumerator, + .get_source_addr = _get_source_addr, + .get_nexthop = _get_nexthop, + .add_ip = _add_ip, + .del_ip = _del_ip, + .add_route = _add_route, + .del_route = _del_route, + .destroy = _destroy, + }, + }, + .socket = netlink_socket_create(NETLINK_ROUTE, rt_msg_names, + lib->settings->get_bool(lib->settings, + "%s.plugins.kernel-netlink.parallel_route", FALSE, lib->ns)), + .rt_exclude = linked_list_create(), + .routes = hashtable_create((hashtable_hash_t)route_entry_hash, + (hashtable_equals_t)route_entry_equals, 16), + .net_changes = hashtable_create( + (hashtable_hash_t)net_change_hash, + (hashtable_equals_t)net_change_equals, 16), + .addrs = hashtable_create( + (hashtable_hash_t)addr_map_entry_hash, + (hashtable_equals_t)addr_map_entry_equals, 16), + .vips = hashtable_create((hashtable_hash_t)addr_map_entry_hash, + (hashtable_equals_t)addr_map_entry_equals, 16), + .routes_lock = mutex_create(MUTEX_TYPE_DEFAULT), + .net_changes_lock = mutex_create(MUTEX_TYPE_DEFAULT), + .ifaces = linked_list_create(), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + .condvar = rwlock_condvar_create(), + .roam_lock = spinlock_create(), + .routing_table = lib->settings->get_int(lib->settings, + "%s.routing_table", ROUTING_TABLE, lib->ns), + .routing_table_prio = lib->settings->get_int(lib->settings, + "%s.routing_table_prio", ROUTING_TABLE_PRIO, lib->ns), + .process_route = lib->settings->get_bool(lib->settings, + "%s.process_route", TRUE, lib->ns), + .install_virtual_ip = lib->settings->get_bool(lib->settings, + "%s.install_virtual_ip", TRUE, lib->ns), + .install_virtual_ip_on = lib->settings->get_str(lib->settings, + "%s.install_virtual_ip_on", NULL, lib->ns), + .prefer_temporary_addrs = lib->settings->get_bool(lib->settings, + "%s.prefer_temporary_addrs", FALSE, lib->ns), + .roam_events = lib->settings->get_bool(lib->settings, + "%s.plugins.kernel-netlink.roam_events", TRUE, lib->ns), + .mtu = lib->settings->get_int(lib->settings, + "%s.plugins.kernel-netlink.mtu", 0, lib->ns), + .mss = lib->settings->get_int(lib->settings, + "%s.plugins.kernel-netlink.mss", 0, lib->ns), + ); + timerclear(&this->last_route_reinstall); + timerclear(&this->next_roam); + + check_kernel_features(this); + + if (streq(lib->ns, "starter")) + { /* starter has no threads, so we do not register for kernel events */ + register_for_events = FALSE; + } + + exclude = lib->settings->get_str(lib->settings, + "%s.ignore_routing_tables", NULL, lib->ns); + if (exclude) + { + char *token; + uintptr_t table; + + enumerator = enumerator_create_token(exclude, " ", " "); + while (enumerator->enumerate(enumerator, &token)) + { + errno = 0; + table = strtoul(token, NULL, 10); + + if (errno == 0) + { + this->rt_exclude->insert_last(this->rt_exclude, (void*)table); + } + } + enumerator->destroy(enumerator); + } + + if (register_for_events) + { + struct sockaddr_nl addr; + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + + /* create and bind RT socket for events (address/interface/route changes) */ + this->socket_events = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (this->socket_events < 0) + { + DBG1(DBG_KNL, "unable to create RT event socket"); + destroy(this); + return NULL; + } + addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | + RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_LINK; + if (bind(this->socket_events, (struct sockaddr*)&addr, sizeof(addr))) + { + DBG1(DBG_KNL, "unable to bind RT event socket"); + destroy(this); + return NULL; + } + + lib->watcher->add(lib->watcher, this->socket_events, WATCHER_READ, + (watcher_cb_t)receive_events, this); + } + + if (init_address_list(this) != SUCCESS) + { + DBG1(DBG_KNL, "unable to get interface list"); + destroy(this); + return NULL; + } + + if (this->routing_table) + { + if (manage_rule(this, RTM_NEWRULE, AF_INET, this->routing_table, + this->routing_table_prio) != SUCCESS) + { + DBG1(DBG_KNL, "unable to create IPv4 routing table rule"); + } + if (manage_rule(this, RTM_NEWRULE, AF_INET6, this->routing_table, + this->routing_table_prio) != SUCCESS) + { + DBG1(DBG_KNL, "unable to create IPv6 routing table rule"); + } + } + + return &this->public; +} diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.h b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.h new file mode 100644 index 000000000..ff9831d3c --- /dev/null +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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_netlink_net_i kernel_netlink_net + * @{ @ingroup kernel_netlink + */ + +#ifndef KERNEL_NETLINK_NET_H_ +#define KERNEL_NETLINK_NET_H_ + +#include <kernel/kernel_net.h> + +typedef struct kernel_netlink_net_t kernel_netlink_net_t; + +/** + * Implementation of the kernel network interface using Netlink. + */ +struct kernel_netlink_net_t { + + /** + * Implements kernel_net_t interface + */ + kernel_net_t interface; +}; + +/** + * Create a netlink kernel network interface instance. + * + * @return kernel_netlink_net_t instance + */ +kernel_netlink_net_t *kernel_netlink_net_create(); + +#endif /** KERNEL_NETLINK_NET_H_ @}*/ diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_plugin.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_plugin.c new file mode 100644 index 000000000..8bafc3c55 --- /dev/null +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_plugin.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2008 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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_netlink_plugin.h" + +#include "kernel_netlink_ipsec.h" +#include "kernel_netlink_net.h" + +typedef struct private_kernel_netlink_plugin_t private_kernel_netlink_plugin_t; + +/** + * private data of kernel netlink plugin + */ +struct private_kernel_netlink_plugin_t { + /** + * implements plugin interface + */ + kernel_netlink_plugin_t public; +}; + +METHOD(plugin_t, get_name, char*, + private_kernel_netlink_plugin_t *this) +{ + return "kernel-netlink"; +} + +METHOD(plugin_t, get_features, int, + private_kernel_netlink_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_CALLBACK(kernel_ipsec_register, kernel_netlink_ipsec_create), + PLUGIN_PROVIDE(CUSTOM, "kernel-ipsec"), + PLUGIN_CALLBACK(kernel_net_register, kernel_netlink_net_create), + PLUGIN_PROVIDE(CUSTOM, "kernel-net"), + }; + *features = f; + return countof(f); +} + +METHOD(plugin_t, destroy, void, + private_kernel_netlink_plugin_t *this) +{ + free(this); +} + +/* + * see header file + */ +plugin_t *kernel_netlink_plugin_create() +{ + private_kernel_netlink_plugin_t *this; + + if (!lib->caps->keep(lib->caps, CAP_NET_ADMIN)) + { /* required to bind/use XFRM sockets / create/modify routing tables, but + * not if only the read-only parts of kernel-netlink-net are used, so + * we don't fail here */ + DBG1(DBG_KNL, "kernel-netlink plugin might require CAP_NET_ADMIN " + "capability"); + } + + 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_netlink/kernel_netlink_plugin.h b/src/libcharon/plugins/kernel_netlink/kernel_netlink_plugin.h new file mode 100644 index 000000000..74c9ae24f --- /dev/null +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_plugin.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2008 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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_netlink kernel_netlink + * @ingroup cplugins + * + * @defgroup kernel_netlink_plugin kernel_netlink_plugin + * @{ @ingroup kernel_netlink + */ + +#ifndef KERNEL_NETLINK_PLUGIN_H_ +#define KERNEL_NETLINK_PLUGIN_H_ + +#include <plugins/plugin.h> + +typedef struct kernel_netlink_plugin_t kernel_netlink_plugin_t; + +/** + * netlink kernel interface plugin + */ +struct kernel_netlink_plugin_t { + + /** + * implements plugin interface + */ + plugin_t plugin; +}; + +#endif /** KERNEL_NETLINK_PLUGIN_H_ @}*/ diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c new file mode 100644 index 000000000..f7ce992a3 --- /dev/null +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c @@ -0,0 +1,655 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * Copyright (C) 2008 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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 <sys/socket.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <linux/xfrm.h> +#include <errno.h> +#include <unistd.h> + +#include "kernel_netlink_shared.h" + +#include <utils/debug.h> +#include <threading/mutex.h> +#include <threading/condvar.h> +#include <collections/array.h> +#include <collections/hashtable.h> + +typedef struct private_netlink_socket_t private_netlink_socket_t; + +/** + * Private variables and functions of netlink_socket_t class. + */ +struct private_netlink_socket_t { + + /** + * public part of the netlink_socket_t object. + */ + netlink_socket_t public; + + /** + * mutex to lock access entries + */ + mutex_t *mutex; + + /** + * Netlink request entries currently active, uintptr_t seq => entry_t + */ + hashtable_t *entries; + + /** + * Current sequence number for Netlink requests + */ + refcount_t seq; + + /** + * netlink socket + */ + int socket; + + /** + * Netlink protocol + */ + int protocol; + + /** + * Enum names for Netlink messages + */ + enum_name_t *names; + + /** + * Timeout for Netlink replies, in ms + */ + u_int timeout; + + /** + * Number of times to repeat timed out queries + */ + u_int retries; + + /** + * Buffer size for received Netlink messages + */ + u_int buflen; + + /** + * Use parallel netlink queries + */ + bool parallel; + + /** + * Ignore errors potentially resulting from a retransmission + */ + bool ignore_retransmit_errors; +}; + +/** + * #definable hook to simulate request message loss + */ +#ifdef NETLINK_MSG_LOSS_HOOK +bool NETLINK_MSG_LOSS_HOOK(struct nlmsghdr *msg); +#define msg_loss_hook(msg) NETLINK_MSG_LOSS_HOOK(msg) +#else +#define msg_loss_hook(msg) FALSE +#endif + +/** + * Request entry the answer for a waiting thread is collected in + */ +typedef struct { + /** Condition variable thread is waiting */ + condvar_t *condvar; + /** Array of hdrs in a multi-message response, as struct nlmsghdr* */ + array_t *hdrs; + /** All response messages received? */ + bool complete; +} entry_t; + +/** + * Clean up a thread waiting entry + */ +static void destroy_entry(entry_t *entry) +{ + entry->condvar->destroy(entry->condvar); + array_destroy_function(entry->hdrs, (void*)free, NULL); + free(entry); +} + +/** + * Write a Netlink message to socket + */ +static bool write_msg(private_netlink_socket_t *this, struct nlmsghdr *msg) +{ + struct sockaddr_nl addr = { + .nl_family = AF_NETLINK, + }; + int len; + + if (msg_loss_hook(msg)) + { + return TRUE; + } + + while (TRUE) + { + len = sendto(this->socket, msg, msg->nlmsg_len, 0, + (struct sockaddr*)&addr, sizeof(addr)); + if (len != msg->nlmsg_len) + { + if (errno == EINTR) + { + continue; + } + DBG1(DBG_KNL, "netlink write error: %s", strerror(errno)); + return FALSE; + } + return TRUE; + } +} + +/** + * Read a single Netlink message from socket, return 0 on error, -1 on timeout + */ +static ssize_t read_msg(private_netlink_socket_t *this, + char *buf, size_t buflen, bool block) +{ + ssize_t len; + + if (block) + { + fd_set set; + timeval_t tv = {}; + + FD_ZERO(&set); + FD_SET(this->socket, &set); + timeval_add_ms(&tv, this->timeout); + + if (select(this->socket + 1, &set, NULL, NULL, + this->timeout ? &tv : NULL) <= 0) + { + return -1; + } + } + len = recv(this->socket, buf, buflen, MSG_TRUNC|(block ? 0 : MSG_DONTWAIT)); + if (len > buflen) + { + DBG1(DBG_KNL, "netlink response exceeds buffer size"); + return 0; + } + if (len < 0) + { + if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) + { + DBG1(DBG_KNL, "netlink read error: %s", strerror(errno)); + } + return 0; + } + return len; +} + +/** + * Queue received response message + */ +static bool queue(private_netlink_socket_t *this, struct nlmsghdr *buf) +{ + struct nlmsghdr *hdr; + entry_t *entry; + uintptr_t seq; + + seq = (uintptr_t)buf->nlmsg_seq; + + this->mutex->lock(this->mutex); + entry = this->entries->get(this->entries, (void*)seq); + if (entry) + { + hdr = malloc(buf->nlmsg_len); + memcpy(hdr, buf, buf->nlmsg_len); + array_insert(entry->hdrs, ARRAY_TAIL, hdr); + if (hdr->nlmsg_type == NLMSG_DONE || !(hdr->nlmsg_flags & NLM_F_MULTI)) + { + entry->complete = TRUE; + entry->condvar->signal(entry->condvar); + } + } + else + { + DBG1(DBG_KNL, "received unknown netlink seq %u, ignored", seq); + } + this->mutex->unlock(this->mutex); + + return entry != NULL; +} + +/** + * Read and queue response message, optionally blocking, returns TRUE on timeout + */ +static bool read_and_queue(private_netlink_socket_t *this, bool block) +{ + struct nlmsghdr *hdr; + char buf[this->buflen]; + ssize_t len; + + len = read_msg(this, buf, sizeof(buf), block); + if (len == -1) + { + return TRUE; + } + if (len) + { + hdr = (struct nlmsghdr*)buf; + while (NLMSG_OK(hdr, len)) + { + if (!queue(this, hdr)) + { + break; + } + hdr = NLMSG_NEXT(hdr, len); + } + } + return FALSE; +} + +CALLBACK(watch, bool, + private_netlink_socket_t *this, int fd, watcher_event_t event) +{ + if (event == WATCHER_READ) + { + read_and_queue(this, FALSE); + } + return TRUE; +} + +/** + * Send a netlink request, try once + */ +static status_t send_once(private_netlink_socket_t *this, struct nlmsghdr *in, + uintptr_t seq, struct nlmsghdr **out, size_t *out_len) +{ + struct nlmsghdr *hdr; + chunk_t result = {}; + entry_t *entry; + + in->nlmsg_seq = seq; + in->nlmsg_pid = getpid(); + + if (this->names) + { + DBG3(DBG_KNL, "sending %N %u: %b", this->names, in->nlmsg_type, + (u_int)seq, in, in->nlmsg_len); + } + + this->mutex->lock(this->mutex); + if (!write_msg(this, in)) + { + this->mutex->unlock(this->mutex); + return FAILED; + } + + INIT(entry, + .condvar = condvar_create(CONDVAR_TYPE_DEFAULT), + .hdrs = array_create(0, 0), + ); + this->entries->put(this->entries, (void*)seq, entry); + + while (!entry->complete) + { + if (this->parallel && + lib->watcher->get_state(lib->watcher) == WATCHER_RUNNING) + { + if (this->timeout) + { + if (entry->condvar->timed_wait(entry->condvar, this->mutex, + this->timeout)) + { + break; + } + } + else + { + entry->condvar->wait(entry->condvar, this->mutex); + } + } + else + { /* During (de-)initialization, no watcher thread is active. + * collect responses ourselves. */ + if (read_and_queue(this, TRUE)) + { + break; + } + } + } + this->entries->remove(this->entries, (void*)seq); + + this->mutex->unlock(this->mutex); + + if (!entry->complete) + { /* timeout */ + destroy_entry(entry); + return OUT_OF_RES; + } + + while (array_remove(entry->hdrs, ARRAY_HEAD, &hdr)) + { + if (this->names) + { + DBG3(DBG_KNL, "received %N %u: %b", this->names, hdr->nlmsg_type, + hdr->nlmsg_seq, hdr, hdr->nlmsg_len); + } + result = chunk_cat("mm", result, + chunk_create((char*)hdr, hdr->nlmsg_len)); + } + destroy_entry(entry); + + *out_len = result.len; + *out = (struct nlmsghdr*)result.ptr; + + return SUCCESS; +} + +/** + * Ignore errors for message types that might have completed previously + */ +static void ignore_retransmit_error(private_netlink_socket_t *this, + struct nlmsgerr *err, int type) +{ + switch (err->error) + { + case -EEXIST: + switch (this->protocol) + { + case NETLINK_XFRM: + switch (type) + { + case XFRM_MSG_NEWPOLICY: + case XFRM_MSG_NEWSA: + err->error = 0; + break; + } + break; + case NETLINK_ROUTE: + switch (type) + { + case RTM_NEWADDR: + case RTM_NEWLINK: + case RTM_NEWNEIGH: + case RTM_NEWROUTE: + case RTM_NEWRULE: + err->error = 0; + break; + } + break; + } + break; + case -ENOENT: + switch (this->protocol) + { + case NETLINK_XFRM: + switch (type) + { + case XFRM_MSG_DELPOLICY: + case XFRM_MSG_DELSA: + err->error = 0; + break; + } + break; + case NETLINK_ROUTE: + switch (type) + { + case RTM_DELADDR: + case RTM_DELLINK: + case RTM_DELNEIGH: + case RTM_DELROUTE: + case RTM_DELRULE: + err->error = 0; + break; + } + break; + } + break; + } +} + +METHOD(netlink_socket_t, netlink_send, status_t, + private_netlink_socket_t *this, struct nlmsghdr *in, struct nlmsghdr **out, + size_t *out_len) +{ + uintptr_t seq; + u_int try; + + seq = ref_get(&this->seq); + + for (try = 0; try <= this->retries; ++try) + { + struct nlmsghdr *hdr; + status_t status; + size_t len; + + if (try > 0) + { + DBG1(DBG_KNL, "retransmitting Netlink request (%u/%u)", + try, this->retries); + } + status = send_once(this, in, seq, &hdr, &len); + switch (status) + { + case SUCCESS: + break; + case OUT_OF_RES: + continue; + default: + return status; + } + if (hdr->nlmsg_type == NLMSG_ERROR) + { + struct nlmsgerr* err; + + err = NLMSG_DATA(hdr); + if (err->error == -EBUSY) + { + free(hdr); + try--; + continue; + } + if (this->ignore_retransmit_errors && try > 0) + { + ignore_retransmit_error(this, err, in->nlmsg_type); + } + } + *out = hdr; + *out_len = len; + return SUCCESS; + } + DBG1(DBG_KNL, "Netlink request timed out after %u retransmits", + this->retries); + return OUT_OF_RES; +} + +METHOD(netlink_socket_t, netlink_send_ack, status_t, + private_netlink_socket_t *this, struct nlmsghdr *in) +{ + struct nlmsghdr *out, *hdr; + size_t len; + + if (netlink_send(this, in, &out, &len) != SUCCESS) + { + return FAILED; + } + hdr = out; + while (NLMSG_OK(hdr, len)) + { + switch (hdr->nlmsg_type) + { + case NLMSG_ERROR: + { + struct nlmsgerr* err = NLMSG_DATA(hdr); + + if (err->error) + { + if (-err->error == EEXIST) + { /* do not report existing routes */ + free(out); + return ALREADY_DONE; + } + if (-err->error == ESRCH) + { /* do not report missing entries */ + free(out); + return NOT_FOUND; + } + DBG1(DBG_KNL, "received netlink error: %s (%d)", + strerror(-err->error), -err->error); + free(out); + return FAILED; + } + free(out); + return SUCCESS; + } + default: + hdr = NLMSG_NEXT(hdr, len); + continue; + case NLMSG_DONE: + break; + } + break; + } + DBG1(DBG_KNL, "netlink request not acknowledged"); + free(out); + return FAILED; +} + +METHOD(netlink_socket_t, destroy, void, + private_netlink_socket_t *this) +{ + if (this->socket != -1) + { + if (this->parallel) + { + lib->watcher->remove(lib->watcher, this->socket); + } + close(this->socket); + } + this->entries->destroy(this->entries); + this->mutex->destroy(this->mutex); + free(this); +} + +/** + * Described in header. + */ +netlink_socket_t *netlink_socket_create(int protocol, enum_name_t *names, + bool parallel) +{ + private_netlink_socket_t *this; + struct sockaddr_nl addr = { + .nl_family = AF_NETLINK, + }; + + INIT(this, + .public = { + .send = _netlink_send, + .send_ack = _netlink_send_ack, + .destroy = _destroy, + }, + .seq = 200, + .mutex = mutex_create(MUTEX_TYPE_RECURSIVE), + .socket = socket(AF_NETLINK, SOCK_RAW, protocol), + .entries = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 4), + .protocol = protocol, + .names = names, + .buflen = lib->settings->get_int(lib->settings, + "%s.plugins.kernel-netlink.buflen", 0, lib->ns), + .timeout = lib->settings->get_int(lib->settings, + "%s.plugins.kernel-netlink.timeout", 0, lib->ns), + .retries = lib->settings->get_int(lib->settings, + "%s.plugins.kernel-netlink.retries", 0, lib->ns), + .ignore_retransmit_errors = lib->settings->get_bool(lib->settings, + "%s.plugins.kernel-netlink.ignore_retransmit_errors", + FALSE, lib->ns), + .parallel = parallel, + ); + + if (!this->buflen) + { + long pagesize = sysconf(_SC_PAGESIZE); + if (pagesize == -1) + { + pagesize = 4096; + } + /* base this on NLMSG_GOODSIZE */ + this->buflen = min(pagesize, 8192); + } + if (this->socket == -1) + { + DBG1(DBG_KNL, "unable to create netlink socket"); + destroy(this); + return NULL; + } + if (bind(this->socket, (struct sockaddr*)&addr, sizeof(addr))) + { + DBG1(DBG_KNL, "unable to bind netlink socket"); + destroy(this); + return NULL; + } + if (this->parallel) + { + lib->watcher->add(lib->watcher, this->socket, WATCHER_READ, watch, this); + } + + return &this->public; +} + +/** + * Described in header. + */ +void netlink_add_attribute(struct nlmsghdr *hdr, int rta_type, chunk_t data, + size_t buflen) +{ + struct rtattr *rta; + + if (NLMSG_ALIGN(hdr->nlmsg_len) + RTA_LENGTH(data.len) > buflen) + { + DBG1(DBG_KNL, "unable to add attribute, buffer too small"); + return; + } + + rta = (struct rtattr*)(((char*)hdr) + NLMSG_ALIGN(hdr->nlmsg_len)); + rta->rta_type = rta_type; + rta->rta_len = RTA_LENGTH(data.len); + memcpy(RTA_DATA(rta), data.ptr, data.len); + hdr->nlmsg_len = NLMSG_ALIGN(hdr->nlmsg_len) + rta->rta_len; +} + +/** + * Described in header. + */ +void* netlink_reserve(struct nlmsghdr *hdr, int buflen, int type, int len) +{ + struct rtattr *rta; + + if (NLMSG_ALIGN(hdr->nlmsg_len) + RTA_LENGTH(len) > buflen) + { + DBG1(DBG_KNL, "unable to add attribute, buffer too small"); + return NULL; + } + + rta = ((void*)hdr) + NLMSG_ALIGN(hdr->nlmsg_len); + rta->rta_type = type; + rta->rta_len = RTA_LENGTH(len); + hdr->nlmsg_len = NLMSG_ALIGN(hdr->nlmsg_len) + rta->rta_len; + + return RTA_DATA(rta); +} diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.h b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.h new file mode 100644 index 000000000..b034326d7 --- /dev/null +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2008 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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. + */ + +#ifndef KERNEL_NETLINK_SHARED_H_ +#define KERNEL_NETLINK_SHARED_H_ + +#include <library.h> + +#include <linux/rtnetlink.h> + +/** + * Default buffer size. + * + * 1024 byte is currently sufficient for all operations. + */ +#ifndef KERNEL_NETLINK_BUFSIZE +#define KERNEL_NETLINK_BUFSIZE 1024 +#endif + +/** + * General purpose netlink buffer. + * + * Some platforms require an enforced aligment to four bytes (e.g. ARM). + */ +typedef union { + struct nlmsghdr hdr; + u_char bytes[KERNEL_NETLINK_BUFSIZE]; +} netlink_buf_t __attribute__((aligned(RTA_ALIGNTO))); + +typedef struct netlink_socket_t netlink_socket_t; + +/** + * Wrapper around a netlink socket. + */ +struct netlink_socket_t { + + /** + * Send a netlink message and wait for a reply. + * + * @param in netlink message to send + * @param out received netlink message + * @param out_len length of the received message + */ + status_t (*send)(netlink_socket_t *this, struct nlmsghdr *in, + struct nlmsghdr **out, size_t *out_len); + + /** + * Send a netlink message and wait for its acknowledge. + * + * @param in netlink message to send + */ + status_t (*send_ack)(netlink_socket_t *this, struct nlmsghdr *in); + + /** + * Destroy the socket. + */ + void (*destroy)(netlink_socket_t *this); +}; + +/** + * Create a netlink_socket_t object. + * + * @param protocol protocol type (e.g. NETLINK_XFRM or NETLINK_ROUTE) + * @param names optional enum names for Netlink messages + * @param parallel support parallel queries on this Netlink socket + */ +netlink_socket_t *netlink_socket_create(int protocol, enum_name_t *names, + bool parallel); + +/** + * Creates an rtattr and adds it to the given netlink message. + * + * @param hdr netlink message + * @param rta_type type of the rtattr + * @param data data to add to the rtattr + * @param buflen length of the netlink message buffer + */ +void netlink_add_attribute(struct nlmsghdr *hdr, int rta_type, chunk_t data, + size_t buflen); + +/** + * Reserve space in a netlink message for given size and type, returning buffer. + * + * @param hdr netlink message + * @param buflen size of full netlink buffer + * @param type RTA type + * @param len length of RTA data + * @return buffer to len bytes of attribute data, NULL on error + */ +void* netlink_reserve(struct nlmsghdr *hdr, int buflen, int type, int len); + +#endif /* KERNEL_NETLINK_SHARED_H_ */ diff --git a/src/libcharon/plugins/kernel_netlink/suites/test_socket.c b/src/libcharon/plugins/kernel_netlink/suites/test_socket.c new file mode 100644 index 000000000..3e8facd0a --- /dev/null +++ b/src/libcharon/plugins/kernel_netlink/suites/test_socket.c @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 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 <test_suite.h> + +#include <threading/thread.h> + +#include "../kernel_netlink_shared.h" + +/** + * Netlink message drop configuration + */ +static int drop_interval = 0; + +/** + * Netlink message drop hook + */ +bool netlink_msg_loss(struct nlmsghdr *hdr) +{ + static refcount_t i; + + if (drop_interval) + { + return ref_get(&i) % drop_interval == drop_interval - 1; + } + return FALSE; +} + +START_TEST(test_echo) +{ + netlink_socket_t *s; + struct nlmsghdr *out; + struct rtmsg *msg; + char dst[] = { + 127,0,0,1 + }; + size_t len; + netlink_buf_t request = { + .hdr = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)), + .nlmsg_flags = NLM_F_REQUEST, + .nlmsg_type = RTM_GETROUTE, + }, + }; + + msg = NLMSG_DATA(&request.hdr); + msg->rtm_family = AF_INET; + netlink_add_attribute(&request.hdr, RTA_DST, + chunk_from_thing(dst), sizeof(request)); + + s = netlink_socket_create(NETLINK_ROUTE, NULL, _i != 0); + + ck_assert(s->send(s, &request.hdr, &out, &len) == SUCCESS); + ck_assert_int_eq(out->nlmsg_type, RTM_NEWROUTE); + free(out); + s->destroy(s); +} +END_TEST + +START_TEST(test_echo_dump) +{ + netlink_socket_t *s; + struct nlmsghdr *out, *current; + struct rtgenmsg *msg; + size_t len; + netlink_buf_t request = { + .hdr = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)), + .nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH | NLM_F_ROOT, + .nlmsg_type = RTM_GETLINK, + }, + }; + + s = netlink_socket_create(NETLINK_ROUTE, NULL, _i != 0); + msg = NLMSG_DATA(&request.hdr); + msg->rtgen_family = AF_UNSPEC; + + ck_assert(s->send(s, &request.hdr, &out, &len) == SUCCESS); + current = out; + while (TRUE) + { + ck_assert(NLMSG_OK(current, len)); + if (current->nlmsg_type == NLMSG_DONE) + { + break; + } + ck_assert_int_eq(current->nlmsg_type, RTM_NEWLINK); + current = NLMSG_NEXT(current, len); + } + free(out); + s->destroy(s); +} +END_TEST + +CALLBACK(stress, void*, + netlink_socket_t *s) +{ + struct nlmsghdr *out; + struct rtmsg *msg; + char dst[] = { + 127,0,0,1 + }; + size_t len; + int i; + netlink_buf_t request = { + .hdr = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)), + .nlmsg_flags = NLM_F_REQUEST, + .nlmsg_type = RTM_GETROUTE, + }, + }; + + for (i = 0; i < 10; i++) + { + msg = NLMSG_DATA(&request.hdr); + msg->rtm_family = AF_INET; + netlink_add_attribute(&request.hdr, RTA_DST, + chunk_from_thing(dst), sizeof(request)); + + ck_assert(s->send(s, &request.hdr, &out, &len) == SUCCESS); + ck_assert_int_eq(out->nlmsg_type, RTM_NEWROUTE); + free(out); + } + return NULL; +} + +CALLBACK(stress_dump, void*, + netlink_socket_t *s) +{ + struct nlmsghdr *out, *current; + struct rtgenmsg *msg; + size_t len; + int i; + netlink_buf_t request = { + .hdr = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)), + .nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH | NLM_F_ROOT, + .nlmsg_type = RTM_GETLINK, + }, + }; + + msg = NLMSG_DATA(&request.hdr); + msg->rtgen_family = AF_UNSPEC; + + for (i = 0; i < 10; i++) + { + ck_assert(s->send(s, &request.hdr, &out, &len) == SUCCESS); + current = out; + while (TRUE) + { + ck_assert(NLMSG_OK(current, len)); + if (current->nlmsg_type == NLMSG_DONE) + { + break; + } + ck_assert_int_eq(current->nlmsg_type, RTM_NEWLINK); + current = NLMSG_NEXT(current, len); + } + free(out); + } + return NULL; +} + +START_TEST(test_stress) +{ + thread_t *threads[10]; + netlink_socket_t *s; + int i; + + s = netlink_socket_create(NETLINK_ROUTE, NULL, _i != 0); + for (i = 0; i < countof(threads); i++) + { + threads[i] = thread_create(stress, s); + } + for (i = 0; i < countof(threads); i++) + { + threads[i]->join(threads[i]); + } + s->destroy(s); +} +END_TEST + +START_TEST(test_stress_dump) +{ + thread_t *threads[10]; + netlink_socket_t *s; + int i; + + s = netlink_socket_create(NETLINK_ROUTE, NULL, _i != 0); + for (i = 0; i < countof(threads); i++) + { + threads[i] = thread_create(stress_dump, s); + } + for (i = 0; i < countof(threads); i++) + { + threads[i]->join(threads[i]); + } + s->destroy(s); +} +END_TEST + +START_TEST(test_retransmit_success) +{ + netlink_socket_t *s; + struct nlmsghdr *out; + struct rtgenmsg *msg; + size_t len; + netlink_buf_t request = { + .hdr = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)), + .nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH | NLM_F_ROOT, + .nlmsg_type = RTM_GETLINK, + }, + }; + + drop_interval = 2; + + lib->settings->set_int(lib->settings, + "%s.plugins.kernel-netlink.timeout", 100, lib->ns); + lib->settings->set_int(lib->settings, + "%s.plugins.kernel-netlink.retries", 1, lib->ns); + + s = netlink_socket_create(NETLINK_ROUTE, NULL, _i != 0); + msg = NLMSG_DATA(&request.hdr); + msg->rtgen_family = AF_UNSPEC; + + ck_assert(s->send(s, &request.hdr, &out, &len) == SUCCESS); + free(out); + s->destroy(s); + + drop_interval = 0; +} +END_TEST + +START_TEST(test_retransmit_fail) +{ + netlink_socket_t *s; + struct nlmsghdr *out; + struct rtgenmsg *msg; + size_t len; + netlink_buf_t request = { + .hdr = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)), + .nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH | NLM_F_ROOT, + .nlmsg_type = RTM_GETLINK, + }, + }; + + drop_interval = 1; + + lib->settings->set_int(lib->settings, + "%s.plugins.kernel-netlink.timeout", 50, lib->ns); + lib->settings->set_int(lib->settings, + "%s.plugins.kernel-netlink.retries", 3, lib->ns); + + s = netlink_socket_create(NETLINK_ROUTE, NULL, _i != 0); + msg = NLMSG_DATA(&request.hdr); + msg->rtgen_family = AF_UNSPEC; + + ck_assert(s->send(s, &request.hdr, &out, &len) == OUT_OF_RES); + s->destroy(s); + + drop_interval = 0; +} +END_TEST + +Suite *socket_suite_create() +{ + Suite *s; + TCase *tc; + + s = suite_create("netlink socket"); + + tc = tcase_create("echo"); + tcase_add_loop_test(tc, test_echo, 0, 2); + tcase_add_loop_test(tc, test_echo_dump, 0, 2); + suite_add_tcase(s, tc); + + tc = tcase_create("stress"); + tcase_add_loop_test(tc, test_stress, 0, 2); + tcase_add_loop_test(tc, test_stress_dump, 0, 2); + suite_add_tcase(s, tc); + + tc = tcase_create("retransmit"); + tcase_add_loop_test(tc, test_retransmit_success, 0, 2); + tcase_add_loop_test(tc, test_retransmit_fail, 0, 2); + suite_add_tcase(s, tc); + + return s; +} diff --git a/src/libcharon/plugins/kernel_netlink/tests.c b/src/libcharon/plugins/kernel_netlink/tests.c new file mode 100644 index 000000000..a1799ea70 --- /dev/null +++ b/src/libcharon/plugins/kernel_netlink/tests.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 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 <test_runner.h> + +/* declare test suite constructors */ +#define TEST_SUITE(x) test_suite_t* x(); +#include "tests.h" +#undef TEST_SUITE + +static test_configuration_t tests[] = { +#define TEST_SUITE(x) \ + { .suite = x, }, +#include "tests.h" + { .suite = NULL, } +}; + +static bool test_runner_init(bool init) +{ + if (init) + { + lib->processor->set_threads(lib->processor, 8); + } + else + { + lib->processor->set_threads(lib->processor, 0); + lib->processor->cancel(lib->processor); + } + return TRUE; +} + +int main(int argc, char *argv[]) +{ + return test_runner_run("kernel-netlink", tests, test_runner_init); +} diff --git a/src/libcharon/plugins/kernel_netlink/tests.h b/src/libcharon/plugins/kernel_netlink/tests.h new file mode 100644 index 000000000..2b6715a78 --- /dev/null +++ b/src/libcharon/plugins/kernel_netlink/tests.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 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. + */ + +TEST_SUITE(socket_suite_create) diff --git a/src/libcharon/plugins/kernel_pfkey/Makefile.am b/src/libcharon/plugins/kernel_pfkey/Makefile.am new file mode 100644 index 000000000..8fdca93a5 --- /dev/null +++ b/src/libcharon/plugins/kernel_pfkey/Makefile.am @@ -0,0 +1,19 @@ +AM_CPPFLAGS = \ + -I${linux_headers} \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libcharon + +AM_CFLAGS = \ + $(PLUGIN_CFLAGS) + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-kernel-pfkey.la +else +plugin_LTLIBRARIES = libstrongswan-kernel-pfkey.la +endif + +libstrongswan_kernel_pfkey_la_SOURCES = \ + kernel_pfkey_plugin.h kernel_pfkey_plugin.c \ + kernel_pfkey_ipsec.h kernel_pfkey_ipsec.c + +libstrongswan_kernel_pfkey_la_LDFLAGS = -module -avoid-version diff --git a/src/libcharon/plugins/kernel_pfkey/Makefile.in b/src/libcharon/plugins/kernel_pfkey/Makefile.in new file mode 100644 index 000000000..f2876a272 --- /dev/null +++ b/src/libcharon/plugins/kernel_pfkey/Makefile.in @@ -0,0 +1,782 @@ +# 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@ +subdir = src/libcharon/plugins/kernel_pfkey +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_pfkey_la_LIBADD = +am_libstrongswan_kernel_pfkey_la_OBJECTS = kernel_pfkey_plugin.lo \ + kernel_pfkey_ipsec.lo +libstrongswan_kernel_pfkey_la_OBJECTS = \ + $(am_libstrongswan_kernel_pfkey_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_pfkey_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) \ + $(libstrongswan_kernel_pfkey_la_LDFLAGS) $(LDFLAGS) -o $@ +@MONOLITHIC_FALSE@am_libstrongswan_kernel_pfkey_la_rpath = -rpath \ +@MONOLITHIC_FALSE@ $(plugindir) +@MONOLITHIC_TRUE@am_libstrongswan_kernel_pfkey_la_rpath = +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_pfkey_la_SOURCES) +DIST_SOURCES = $(libstrongswan_kernel_pfkey_la_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@ +EASY_INSTALL = @EASY_INSTALL@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GEM = @GEM@ +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@ +PYTHONEGGINSTALLDIR = @PYTHONEGGINSTALLDIR@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +PY_TEST = @PY_TEST@ +RANLIB = @RANLIB@ +RTLIB = @RTLIB@ +RUBY = @RUBY@ +RUBYGEMDIR = @RUBYGEMDIR@ +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@ +json_CFLAGS = @json_CFLAGS@ +json_LIBS = @json_LIBS@ +libdir = @libdir@ +libexecdir = @libexecdir@ +libiptc_CFLAGS = @libiptc_CFLAGS@ +libiptc_LIBS = @libiptc_LIBS@ +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@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ +systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ +systemd_daemon_LIBS = @systemd_daemon_LIBS@ +systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ +systemd_journal_LIBS = @systemd_journal_LIBS@ +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${linux_headers} \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libcharon + +AM_CFLAGS = \ + $(PLUGIN_CFLAGS) + +@MONOLITHIC_TRUE@noinst_LTLIBRARIES = libstrongswan-kernel-pfkey.la +@MONOLITHIC_FALSE@plugin_LTLIBRARIES = libstrongswan-kernel-pfkey.la +libstrongswan_kernel_pfkey_la_SOURCES = \ + kernel_pfkey_plugin.h kernel_pfkey_plugin.c \ + kernel_pfkey_ipsec.h kernel_pfkey_ipsec.c + +libstrongswan_kernel_pfkey_la_LDFLAGS = -module -avoid-version +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_pfkey/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/libcharon/plugins/kernel_pfkey/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-pfkey.la: $(libstrongswan_kernel_pfkey_la_OBJECTS) $(libstrongswan_kernel_pfkey_la_DEPENDENCIES) $(EXTRA_libstrongswan_kernel_pfkey_la_DEPENDENCIES) + $(AM_V_CCLD)$(libstrongswan_kernel_pfkey_la_LINK) $(am_libstrongswan_kernel_pfkey_la_rpath) $(libstrongswan_kernel_pfkey_la_OBJECTS) $(libstrongswan_kernel_pfkey_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel_pfkey_ipsec.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel_pfkey_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) +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-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-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_pfkey/kernel_pfkey_ipsec.c b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c new file mode 100644 index 000000000..d505f1c33 --- /dev/null +++ b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -0,0 +1,3094 @@ +/* + * Copyright (C) 2008-2015 Tobias Brunner + * Copyright (C) 2008 Andreas Steffen + * Hochschule fuer Technik Rapperswil + * + * 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. + */ +/* + * Copyright (C) 2014 Nanoteq Pty Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <stdint.h> +#include <sys/types.h> +#include <sys/socket.h> + +#ifdef __FreeBSD__ +#include <limits.h> /* for LONG_MAX */ +#endif + +#ifdef HAVE_NET_PFKEYV2_H +#include <net/pfkeyv2.h> +#else +#include <linux/pfkeyv2.h> +#endif + +#ifdef SADB_X_EXT_NAT_T_TYPE +#define HAVE_NATT +#endif + +#ifdef HAVE_NETIPSEC_IPSEC_H +#include <netipsec/ipsec.h> +#elif defined(HAVE_NETINET6_IPSEC_H) +#include <netinet6/ipsec.h> +#else +#include <linux/ipsec.h> +#endif + +#ifdef HAVE_NATT +#ifdef HAVE_LINUX_UDP_H +#include <linux/udp.h> +#else +#include <netinet/udp.h> +#endif /*HAVE_LINUX_UDP_H*/ +#endif /*HAVE_NATT*/ + +#include <unistd.h> +#include <time.h> +#include <errno.h> +#ifdef __APPLE__ +#include <sys/sysctl.h> +#endif + +#include "kernel_pfkey_ipsec.h" + +#include <daemon.h> +#include <utils/debug.h> +#include <networking/host.h> +#include <collections/linked_list.h> +#include <collections/hashtable.h> +#include <threading/mutex.h> + +/** non linux specific */ +#ifndef IPPROTO_COMP +#ifdef IPPROTO_IPCOMP +#define IPPROTO_COMP IPPROTO_IPCOMP +#endif +#endif + +#ifndef SADB_X_AALG_SHA2_256HMAC +#define SADB_X_AALG_SHA2_256HMAC SADB_X_AALG_SHA2_256 +#define SADB_X_AALG_SHA2_384HMAC SADB_X_AALG_SHA2_384 +#define SADB_X_AALG_SHA2_512HMAC SADB_X_AALG_SHA2_512 +#endif + +#ifndef SADB_X_EALG_AESCBC +#define SADB_X_EALG_AESCBC SADB_X_EALG_AES +#endif + +#ifndef SADB_X_EALG_CASTCBC +#define SADB_X_EALG_CASTCBC SADB_X_EALG_CAST128CBC +#endif + +#if !defined(SADB_X_EALG_AES_GCM_ICV8) && defined(SADB_X_EALG_AESGCM8) +#define SADB_X_EALG_AES_GCM_ICV8 SADB_X_EALG_AESGCM8 +#define SADB_X_EALG_AES_GCM_ICV12 SADB_X_EALG_AESGCM12 +#define SADB_X_EALG_AES_GCM_ICV16 SADB_X_EALG_AESGCM16 +#endif + +#ifndef SOL_IP +#define SOL_IP IPPROTO_IP +#define SOL_IPV6 IPPROTO_IPV6 +#endif + +/** from linux/in.h */ +#ifndef IP_IPSEC_POLICY +#define IP_IPSEC_POLICY 16 +#endif + +/** missing on uclibc */ +#ifndef IPV6_IPSEC_POLICY +#define IPV6_IPSEC_POLICY 34 +#endif + +/* from linux/udp.h */ +#ifndef UDP_ENCAP +#define UDP_ENCAP 100 +#endif + +#ifndef UDP_ENCAP_ESPINUDP +#define UDP_ENCAP_ESPINUDP 2 +#endif + +/* this is not defined on some platforms */ +#ifndef SOL_UDP +#define SOL_UDP IPPROTO_UDP +#endif + +/** base priority for installed policies */ +#define PRIO_BASE 384 + +#ifdef __APPLE__ +/** from xnu/bsd/net/pfkeyv2.h */ +#define SADB_X_EXT_NATT 0x002 + struct sadb_sa_2 { + struct sadb_sa sa; + u_int16_t sadb_sa_natt_port; + u_int16_t sadb_reserved0; + u_int32_t sadb_reserved1; + }; +#endif + +/** buffer size for PF_KEY messages */ +#define PFKEY_BUFFER_SIZE 4096 + +/** PF_KEY messages are 64 bit aligned */ +#define PFKEY_ALIGNMENT 8 +/** aligns len to 64 bits */ +#define PFKEY_ALIGN(len) (((len) + PFKEY_ALIGNMENT - 1) & ~(PFKEY_ALIGNMENT - 1)) +/** calculates the properly padded length in 64 bit chunks */ +#define PFKEY_LEN(len) ((PFKEY_ALIGN(len) / PFKEY_ALIGNMENT)) +/** calculates user mode length i.e. in bytes */ +#define PFKEY_USER_LEN(len) ((len) * PFKEY_ALIGNMENT) + +/** given a PF_KEY message header and an extension this updates the length in the header */ +#define PFKEY_EXT_ADD(msg, ext) ((msg)->sadb_msg_len += ((struct sadb_ext*)ext)->sadb_ext_len) +/** given a PF_KEY message header this returns a pointer to the next extension */ +#define PFKEY_EXT_ADD_NEXT(msg) ((struct sadb_ext*)(((char*)(msg)) + PFKEY_USER_LEN((msg)->sadb_msg_len))) +/** copy an extension and append it to a PF_KEY message */ +#define PFKEY_EXT_COPY(msg, ext) (PFKEY_EXT_ADD(msg, memcpy(PFKEY_EXT_ADD_NEXT(msg), ext, PFKEY_USER_LEN(((struct sadb_ext*)ext)->sadb_ext_len)))) +/** given a PF_KEY extension this returns a pointer to the next extension */ +#define PFKEY_EXT_NEXT(ext) ((struct sadb_ext*)(((char*)(ext)) + PFKEY_USER_LEN(((struct sadb_ext*)ext)->sadb_ext_len))) +/** given a PF_KEY extension this returns a pointer to the next extension also updates len (len in 64 bit words) */ +#define PFKEY_EXT_NEXT_LEN(ext,len) ((len) -= (ext)->sadb_ext_len, PFKEY_EXT_NEXT(ext)) +/** true if ext has a valid length and len is large enough to contain ext (assuming len in 64 bit words) */ +#define PFKEY_EXT_OK(ext,len) ((len) >= PFKEY_LEN(sizeof(struct sadb_ext)) && \ + (ext)->sadb_ext_len >= PFKEY_LEN(sizeof(struct sadb_ext)) && \ + (ext)->sadb_ext_len <= (len)) + +typedef struct private_kernel_pfkey_ipsec_t private_kernel_pfkey_ipsec_t; + +/** + * Private variables and functions of kernel_pfkey class. + */ +struct private_kernel_pfkey_ipsec_t +{ + /** + * Public part of the kernel_pfkey_t object. + */ + kernel_pfkey_ipsec_t public; + + /** + * mutex to lock access to various lists + */ + mutex_t *mutex; + + /** + * List of installed policies (policy_entry_t) + */ + linked_list_t *policies; + + /** + * List of exclude routes (exclude_route_t) + */ + linked_list_t *excludes; + + /** + * Hash table of IPsec SAs using policies (ipsec_sa_t) + */ + hashtable_t *sas; + + /** + * whether to install routes along policies + */ + bool install_routes; + + /** + * mutex to lock access to the PF_KEY socket + */ + mutex_t *mutex_pfkey; + + /** + * PF_KEY socket to communicate with the kernel + */ + int socket; + + /** + * PF_KEY socket to receive acquire and expire events + */ + int socket_events; + + /** + * sequence number for messages sent to the kernel + */ + int seq; +}; + +typedef struct exclude_route_t exclude_route_t; + +/** + * Exclude route definition + */ +struct exclude_route_t { + /** destination address of exclude */ + host_t *dst; + /** source address for route */ + host_t *src; + /** nexthop exclude has been installed */ + host_t *gtw; + /** references to this route */ + int refs; +}; + +/** + * clean up a route exclude entry + */ +static void exclude_route_destroy(exclude_route_t *this) +{ + this->dst->destroy(this->dst); + this->src->destroy(this->src); + this->gtw->destroy(this->gtw); + free(this); +} + +typedef struct route_entry_t route_entry_t; + +/** + * installed routing entry + */ +struct route_entry_t { + /** name of the interface the route is bound to */ + char *if_name; + + /** source ip of the route */ + host_t *src_ip; + + /** gateway for this route */ + host_t *gateway; + + /** destination net */ + chunk_t dst_net; + + /** destination net prefixlen */ + u_int8_t prefixlen; + + /** reference to exclude route, if any */ + exclude_route_t *exclude; +}; + +/** + * destroy an route_entry_t object + */ +static void route_entry_destroy(route_entry_t *this) +{ + free(this->if_name); + DESTROY_IF(this->src_ip); + DESTROY_IF(this->gateway); + chunk_free(&this->dst_net); + free(this); +} + +/** + * compare two route_entry_t objects + */ +static bool route_entry_equals(route_entry_t *a, route_entry_t *b) +{ + return a->if_name && b->if_name && streq(a->if_name, b->if_name) && + a->src_ip->ip_equals(a->src_ip, b->src_ip) && + a->gateway && b->gateway && + a->gateway->ip_equals(a->gateway, b->gateway) && + chunk_equals(a->dst_net, b->dst_net) && a->prefixlen == b->prefixlen; +} + +typedef struct ipsec_sa_t ipsec_sa_t; + +/** + * IPsec SA assigned to a policy. + */ +struct ipsec_sa_t { + /** Source address of this SA */ + host_t *src; + + /** Destination address of this SA */ + host_t *dst; + + /** Description of this SA */ + ipsec_sa_cfg_t cfg; + + /** Reference count for this SA */ + refcount_t refcount; +}; + +/** + * Hash function for ipsec_sa_t objects + */ +static u_int ipsec_sa_hash(ipsec_sa_t *sa) +{ + return chunk_hash_inc(sa->src->get_address(sa->src), + chunk_hash_inc(sa->dst->get_address(sa->dst), + chunk_hash(chunk_from_thing(sa->cfg)))); +} + +/** + * Equality function for ipsec_sa_t objects + */ +static bool ipsec_sa_equals(ipsec_sa_t *sa, ipsec_sa_t *other_sa) +{ + return sa->src->ip_equals(sa->src, other_sa->src) && + sa->dst->ip_equals(sa->dst, other_sa->dst) && + memeq(&sa->cfg, &other_sa->cfg, sizeof(ipsec_sa_cfg_t)); +} + +/** + * Allocate or reference an IPsec SA object + */ +static ipsec_sa_t *ipsec_sa_create(private_kernel_pfkey_ipsec_t *this, + host_t *src, host_t *dst, + ipsec_sa_cfg_t *cfg) +{ + ipsec_sa_t *sa, *found; + INIT(sa, + .src = src, + .dst = dst, + .cfg = *cfg, + ); + found = this->sas->get(this->sas, sa); + if (!found) + { + sa->src = src->clone(src); + sa->dst = dst->clone(dst); + this->sas->put(this->sas, sa, sa); + } + else + { + free(sa); + sa = found; + } + ref_get(&sa->refcount); + return sa; +} + +/** + * Release and destroy an IPsec SA object + */ +static void ipsec_sa_destroy(private_kernel_pfkey_ipsec_t *this, + ipsec_sa_t *sa) +{ + if (ref_put(&sa->refcount)) + { + this->sas->remove(this->sas, sa); + DESTROY_IF(sa->src); + DESTROY_IF(sa->dst); + free(sa); + } +} + +typedef struct policy_sa_t policy_sa_t; +typedef struct policy_sa_in_t policy_sa_in_t; + +/** + * Mapping between a policy and an IPsec SA. + */ +struct policy_sa_t { + /** Priority assigned to the policy when installed with this SA */ + u_int32_t priority; + + /** Type of the policy */ + policy_type_t type; + + /** Assigned SA */ + ipsec_sa_t *sa; +}; + +/** + * For input policies we also cache the traffic selectors in order to install + * the route. + */ +struct policy_sa_in_t { + /** Generic interface */ + policy_sa_t generic; + + /** Source traffic selector of this policy */ + traffic_selector_t *src_ts; + + /** Destination traffic selector of this policy */ + traffic_selector_t *dst_ts; +}; + +/** + * Create a policy_sa(_in)_t object + */ +static policy_sa_t *policy_sa_create(private_kernel_pfkey_ipsec_t *this, + policy_dir_t dir, policy_type_t type, host_t *src, host_t *dst, + traffic_selector_t *src_ts, traffic_selector_t *dst_ts, ipsec_sa_cfg_t *cfg) +{ + policy_sa_t *policy; + + if (dir == POLICY_IN) + { + policy_sa_in_t *in; + INIT(in, + .src_ts = src_ts->clone(src_ts), + .dst_ts = dst_ts->clone(dst_ts), + ); + policy = &in->generic; + } + else + { + INIT(policy, .priority = 0); + } + policy->type = type; + policy->sa = ipsec_sa_create(this, src, dst, cfg); + return policy; +} + +/** + * Destroy a policy_sa(_in)_t object + */ +static void policy_sa_destroy(policy_sa_t *policy, policy_dir_t *dir, + private_kernel_pfkey_ipsec_t *this) +{ + if (*dir == POLICY_IN) + { + policy_sa_in_t *in = (policy_sa_in_t*)policy; + in->src_ts->destroy(in->src_ts); + in->dst_ts->destroy(in->dst_ts); + } + ipsec_sa_destroy(this, policy->sa); + free(policy); +} + +typedef struct policy_entry_t policy_entry_t; + +/** + * installed kernel policy. + */ +struct policy_entry_t { + /** Index assigned by the kernel */ + u_int32_t index; + + /** Direction of this policy: in, out, forward */ + u_int8_t direction; + + /** Parameters of installed policy */ + struct { + /** Subnet and port */ + host_t *net; + /** Subnet mask */ + u_int8_t mask; + /** Protocol */ + u_int8_t proto; + } src, dst; + + /** Associated route installed for this policy */ + route_entry_t *route; + + /** List of SAs this policy is used by, ordered by priority */ + linked_list_t *used_by; +}; + +/** + * Create a policy_entry_t object + */ +static policy_entry_t *create_policy_entry(traffic_selector_t *src_ts, + traffic_selector_t *dst_ts, + policy_dir_t dir) +{ + policy_entry_t *policy; + INIT(policy, + .direction = dir, + ); + u_int16_t port; + u_int8_t proto; + + src_ts->to_subnet(src_ts, &policy->src.net, &policy->src.mask); + dst_ts->to_subnet(dst_ts, &policy->dst.net, &policy->dst.mask); + + /* src or dest proto may be "any" (0), use more restrictive one */ + proto = max(src_ts->get_protocol(src_ts), dst_ts->get_protocol(dst_ts)); + /* map the ports to ICMP type/code how the Linux kernel expects them, that + * is, type in src, code in dst */ + if (proto == IPPROTO_ICMP || proto == IPPROTO_ICMPV6) + { + port = max(policy->src.net->get_port(policy->src.net), + policy->dst.net->get_port(policy->dst.net)); + policy->src.net->set_port(policy->src.net, + traffic_selector_icmp_type(port)); + policy->dst.net->set_port(policy->dst.net, + traffic_selector_icmp_code(port)); + } + else if (!proto) + { + proto = IPSEC_PROTO_ANY; + } + policy->src.proto = policy->dst.proto = proto; + + return policy; +} + +/** + * Destroy a policy_entry_t object + */ +static void policy_entry_destroy(policy_entry_t *policy, + private_kernel_pfkey_ipsec_t *this) +{ + if (policy->route) + { + route_entry_destroy(policy->route); + } + if (policy->used_by) + { + policy->used_by->invoke_function(policy->used_by, + (linked_list_invoke_t)policy_sa_destroy, + &policy->direction, this); + policy->used_by->destroy(policy->used_by); + } + DESTROY_IF(policy->src.net); + DESTROY_IF(policy->dst.net); + free(policy); +} + +/** + * compares two policy_entry_t + */ +static inline bool policy_entry_equals(policy_entry_t *current, + policy_entry_t *policy) +{ + return current->direction == policy->direction && + current->src.proto == policy->src.proto && + current->dst.proto == policy->dst.proto && + current->src.mask == policy->src.mask && + current->dst.mask == policy->dst.mask && + current->src.net->equals(current->src.net, policy->src.net) && + current->dst.net->equals(current->dst.net, policy->dst.net); +} + +/** + * compare the given kernel index with that of a policy + */ +static inline bool policy_entry_match_byindex(policy_entry_t *current, + u_int32_t *index) +{ + return current->index == *index; +} + +/** + * Calculate the priority of a policy + */ +static inline u_int32_t get_priority(policy_entry_t *policy, + policy_priority_t prio) +{ + u_int32_t priority = PRIO_BASE; + switch (prio) + { + case POLICY_PRIORITY_FALLBACK: + priority <<= 1; + /* fall-through */ + case POLICY_PRIORITY_ROUTED: + priority <<= 1; + /* fall-through */ + case POLICY_PRIORITY_DEFAULT: + priority <<= 1; + /* fall-trough */ + case POLICY_PRIORITY_PASS: + break; + } + /* calculate priority based on selector size, small size = high prio */ + priority -= policy->src.mask; + priority -= policy->dst.mask; + priority <<= 2; /* make some room for the two flags */ + priority += policy->src.net->get_port(policy->src.net) || + policy->dst.net->get_port(policy->dst.net) ? + 0 : 2; + priority += policy->src.proto != IPSEC_PROTO_ANY ? 0 : 1; + return priority; +} + +typedef struct pfkey_msg_t pfkey_msg_t; + +struct pfkey_msg_t +{ + /** + * PF_KEY message base + */ + struct sadb_msg *msg; + + /** + * PF_KEY message extensions + */ + union { + struct sadb_ext *ext[SADB_EXT_MAX + 1]; + struct { + struct sadb_ext *reserved; /* SADB_EXT_RESERVED */ + struct sadb_sa *sa; /* SADB_EXT_SA */ + struct sadb_lifetime *lft_current; /* SADB_EXT_LIFETIME_CURRENT */ + struct sadb_lifetime *lft_hard; /* SADB_EXT_LIFETIME_HARD */ + struct sadb_lifetime *lft_soft; /* SADB_EXT_LIFETIME_SOFT */ + struct sadb_address *src; /* SADB_EXT_ADDRESS_SRC */ + struct sadb_address *dst; /* SADB_EXT_ADDRESS_DST */ + struct sadb_address *proxy; /* SADB_EXT_ADDRESS_PROXY */ + struct sadb_key *key_auth; /* SADB_EXT_KEY_AUTH */ + struct sadb_key *key_encr; /* SADB_EXT_KEY_ENCRYPT */ + struct sadb_ident *id_src; /* SADB_EXT_IDENTITY_SRC */ + struct sadb_ident *id_dst; /* SADB_EXT_IDENTITY_DST */ + struct sadb_sens *sensitivity; /* SADB_EXT_SENSITIVITY */ + struct sadb_prop *proposal; /* SADB_EXT_PROPOSAL */ + struct sadb_supported *supported_auth; /* SADB_EXT_SUPPORTED_AUTH */ + struct sadb_supported *supported_encr; /* SADB_EXT_SUPPORTED_ENCRYPT */ + struct sadb_spirange *spirange; /* SADB_EXT_SPIRANGE */ + struct sadb_x_kmprivate *x_kmprivate; /* SADB_X_EXT_KMPRIVATE */ + struct sadb_x_policy *x_policy; /* SADB_X_EXT_POLICY */ + struct sadb_x_sa2 *x_sa2; /* SADB_X_EXT_SA2 */ + struct sadb_x_nat_t_type *x_natt_type; /* SADB_X_EXT_NAT_T_TYPE */ + struct sadb_x_nat_t_port *x_natt_sport; /* SADB_X_EXT_NAT_T_SPORT */ + struct sadb_x_nat_t_port *x_natt_dport; /* SADB_X_EXT_NAT_T_DPORT */ + struct sadb_address *x_natt_oa; /* SADB_X_EXT_NAT_T_OA */ + struct sadb_x_sec_ctx *x_sec_ctx; /* SADB_X_EXT_SEC_CTX */ + struct sadb_x_kmaddress *x_kmaddress; /* SADB_X_EXT_KMADDRESS */ + } __attribute__((__packed__)); + }; +}; + +ENUM(sadb_ext_type_names, SADB_EXT_RESERVED, SADB_EXT_MAX, + "SADB_EXT_RESERVED", + "SADB_EXT_SA", + "SADB_EXT_LIFETIME_CURRENT", + "SADB_EXT_LIFETIME_HARD", + "SADB_EXT_LIFETIME_SOFT", + "SADB_EXT_ADDRESS_SRC", + "SADB_EXT_ADDRESS_DST", + "SADB_EXT_ADDRESS_PROXY", + "SADB_EXT_KEY_AUTH", + "SADB_EXT_KEY_ENCRYPT", + "SADB_EXT_IDENTITY_SRC", + "SADB_EXT_IDENTITY_DST", + "SADB_EXT_SENSITIVITY", + "SADB_EXT_PROPOSAL", + "SADB_EXT_SUPPORTED_AUTH", + "SADB_EXT_SUPPORTED_ENCRYPT", + "SADB_EXT_SPIRANGE", + "SADB_X_EXT_KMPRIVATE", + "SADB_X_EXT_POLICY", + "SADB_X_EXT_SA2", + "SADB_X_EXT_NAT_T_TYPE", + "SADB_X_EXT_NAT_T_SPORT", + "SADB_X_EXT_NAT_T_DPORT", + "SADB_X_EXT_NAT_T_OA", + "SADB_X_EXT_SEC_CTX", + "SADB_X_EXT_KMADDRESS" +); + +/** + * convert a protocol identifier to the PF_KEY sa type + */ +static u_int8_t proto2satype(u_int8_t proto) +{ + switch (proto) + { + case IPPROTO_ESP: + return SADB_SATYPE_ESP; + case IPPROTO_AH: + return SADB_SATYPE_AH; + case IPPROTO_COMP: + return SADB_X_SATYPE_IPCOMP; + default: + return proto; + } +} + +/** + * convert a PF_KEY sa type to a protocol identifier + */ +static u_int8_t satype2proto(u_int8_t satype) +{ + switch (satype) + { + case SADB_SATYPE_ESP: + return IPPROTO_ESP; + case SADB_SATYPE_AH: + return IPPROTO_AH; + case SADB_X_SATYPE_IPCOMP: + return IPPROTO_COMP; + default: + return satype; + } +} + +/** + * convert the general ipsec mode to the one defined in ipsec.h + */ +static u_int8_t mode2kernel(ipsec_mode_t mode) +{ + switch (mode) + { + case MODE_TRANSPORT: + return IPSEC_MODE_TRANSPORT; + case MODE_TUNNEL: + return IPSEC_MODE_TUNNEL; +#ifdef HAVE_IPSEC_MODE_BEET + case MODE_BEET: + return IPSEC_MODE_BEET; +#endif + default: + return mode; + } +} + +/** + * convert the general policy direction to the one defined in ipsec.h + */ +static u_int8_t dir2kernel(policy_dir_t dir) +{ + switch (dir) + { + case POLICY_IN: + return IPSEC_DIR_INBOUND; + case POLICY_OUT: + return IPSEC_DIR_OUTBOUND; +#ifdef HAVE_IPSEC_DIR_FWD + case POLICY_FWD: + return IPSEC_DIR_FWD; +#endif + default: + return IPSEC_DIR_INVALID; + } +} + +/** + * convert the policy type to the one defined in ipsec.h + */ +static inline u_int16_t type2kernel(policy_type_t type) +{ + switch (type) + { + case POLICY_IPSEC: + return IPSEC_POLICY_IPSEC; + case POLICY_PASS: + return IPSEC_POLICY_NONE; + case POLICY_DROP: + return IPSEC_POLICY_DISCARD; + } + return type; +} + +#ifdef SADB_X_MIGRATE +/** + * convert the policy direction in ipsec.h to the general one. + */ +static policy_dir_t kernel2dir(u_int8_t dir) +{ + switch (dir) + { + case IPSEC_DIR_INBOUND: + return POLICY_IN; + case IPSEC_DIR_OUTBOUND: + return POLICY_OUT; +#ifdef HAVE_IPSEC_DIR_FWD + case IPSEC_DIR_FWD: + return POLICY_FWD; +#endif + default: + return dir; + } +} +#endif /*SADB_X_MIGRATE*/ + +typedef struct kernel_algorithm_t kernel_algorithm_t; + +/** + * Mapping of IKEv2 algorithms to PF_KEY algorithms + */ +struct kernel_algorithm_t { + /** + * Identifier specified in IKEv2 + */ + int ikev2; + + /** + * Identifier as defined in pfkeyv2.h + */ + int kernel; +}; + +#define END_OF_LIST -1 + +/** + * Algorithms for encryption + */ +static kernel_algorithm_t encryption_algs[] = { +/* {ENCR_DES_IV64, 0 }, */ + {ENCR_DES, SADB_EALG_DESCBC }, + {ENCR_3DES, SADB_EALG_3DESCBC }, +/* {ENCR_RC5, 0 }, */ +/* {ENCR_IDEA, 0 }, */ + {ENCR_CAST, SADB_X_EALG_CASTCBC }, + {ENCR_BLOWFISH, SADB_X_EALG_BLOWFISHCBC }, +/* {ENCR_3IDEA, 0 }, */ +/* {ENCR_DES_IV32, 0 }, */ + {ENCR_NULL, SADB_EALG_NULL }, + {ENCR_AES_CBC, SADB_X_EALG_AESCBC }, +#ifdef SADB_X_EALG_AESCTR + {ENCR_AES_CTR, SADB_X_EALG_AESCTR }, +#endif +/* {ENCR_AES_CCM_ICV8, SADB_X_EALG_AES_CCM_ICV8 }, */ +/* {ENCR_AES_CCM_ICV12, SADB_X_EALG_AES_CCM_ICV12 }, */ +/* {ENCR_AES_CCM_ICV16, SADB_X_EALG_AES_CCM_ICV16 }, */ +#ifdef SADB_X_EALG_AES_GCM_ICV8 /* assume the others are defined too */ + {ENCR_AES_GCM_ICV8, SADB_X_EALG_AES_GCM_ICV8 }, + {ENCR_AES_GCM_ICV12, SADB_X_EALG_AES_GCM_ICV12 }, + {ENCR_AES_GCM_ICV16, SADB_X_EALG_AES_GCM_ICV16 }, +#endif +#ifdef SADB_X_EALG_CAMELLIACBC + {ENCR_CAMELLIA_CBC, SADB_X_EALG_CAMELLIACBC }, +#endif + {END_OF_LIST, 0 }, +}; + +/** + * Algorithms for integrity protection + */ +static kernel_algorithm_t integrity_algs[] = { + {AUTH_HMAC_MD5_96, SADB_AALG_MD5HMAC }, + {AUTH_HMAC_SHA1_96, SADB_AALG_SHA1HMAC }, + {AUTH_HMAC_SHA2_256_128, SADB_X_AALG_SHA2_256HMAC }, + {AUTH_HMAC_SHA2_384_192, SADB_X_AALG_SHA2_384HMAC }, + {AUTH_HMAC_SHA2_512_256, SADB_X_AALG_SHA2_512HMAC }, +/* {AUTH_DES_MAC, 0, }, */ +/* {AUTH_KPDK_MD5, 0, }, */ +#ifdef SADB_X_AALG_AES_XCBC_MAC + {AUTH_AES_XCBC_96, SADB_X_AALG_AES_XCBC_MAC, }, +#endif + {END_OF_LIST, 0, }, +}; + +/** + * Algorithms for IPComp, unused yet + */ +static kernel_algorithm_t compression_algs[] = { +/* {IPCOMP_OUI, 0 }, */ + {IPCOMP_DEFLATE, SADB_X_CALG_DEFLATE }, +#ifdef SADB_X_CALG_LZS + {IPCOMP_LZS, SADB_X_CALG_LZS }, +#endif +#ifdef SADB_X_CALG_LZJH + {IPCOMP_LZJH, SADB_X_CALG_LZJH }, +#endif + {END_OF_LIST, 0 }, +}; + +/** + * Look up a kernel algorithm ID and its key size + */ +static int lookup_algorithm(transform_type_t type, int ikev2) +{ + kernel_algorithm_t *list; + u_int16_t alg = 0; + + switch (type) + { + case ENCRYPTION_ALGORITHM: + list = encryption_algs; + break; + case INTEGRITY_ALGORITHM: + list = integrity_algs; + break; + case COMPRESSION_ALGORITHM: + list = compression_algs; + break; + default: + return 0; + } + while (list->ikev2 != END_OF_LIST) + { + if (ikev2 == list->ikev2) + { + return list->kernel; + } + list++; + } + charon->kernel->lookup_algorithm(charon->kernel, ikev2, type, &alg, NULL); + return alg; +} + +/** + * Helper to set a port in a sockaddr_t, the port has to be in host order + */ +static void set_port(sockaddr_t *addr, u_int16_t port) +{ + switch (addr->sa_family) + { + case AF_INET: + { + struct sockaddr_in *sin = (struct sockaddr_in*)addr; + sin->sin_port = htons(port); + break; + } + case AF_INET6: + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)addr; + sin6->sin6_port = htons(port); + break; + } + } +} + +/** + * Copy a host_t as sockaddr_t to the given memory location. + * @return the number of bytes copied + */ +static size_t hostcpy(void *dest, host_t *host, bool include_port) +{ + sockaddr_t *addr = host->get_sockaddr(host), *dest_addr = dest; + socklen_t *len = host->get_sockaddr_len(host); + + memcpy(dest, addr, *len); +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + dest_addr->sa_len = *len; +#endif + if (!include_port) + { + set_port(dest_addr, 0); + } + return *len; +} + +/** + * add a host to the given sadb_msg + */ +static void add_addr_ext(struct sadb_msg *msg, host_t *host, u_int16_t type, + u_int8_t proto, u_int8_t prefixlen, bool include_port) +{ + struct sadb_address *addr = (struct sadb_address*)PFKEY_EXT_ADD_NEXT(msg); + size_t len; + + addr->sadb_address_exttype = type; + addr->sadb_address_proto = proto; + addr->sadb_address_prefixlen = prefixlen; + len = hostcpy(addr + 1, host, include_port); + addr->sadb_address_len = PFKEY_LEN(sizeof(*addr) + len); + PFKEY_EXT_ADD(msg, addr); +} + +/** + * adds an empty address extension to the given sadb_msg + */ +static void add_anyaddr_ext(struct sadb_msg *msg, int family, u_int8_t type) +{ + socklen_t len = (family == AF_INET) ? sizeof(struct sockaddr_in) : + sizeof(struct sockaddr_in6); + struct sadb_address *addr = (struct sadb_address*)PFKEY_EXT_ADD_NEXT(msg); + addr->sadb_address_exttype = type; + sockaddr_t *saddr = (sockaddr_t*)(addr + 1); + saddr->sa_family = family; +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + saddr->sa_len = len; +#endif + addr->sadb_address_len = PFKEY_LEN(sizeof(*addr) + len); + PFKEY_EXT_ADD(msg, addr); +} + +#ifdef HAVE_NATT +/** + * add udp encap extensions to a sadb_msg + */ +static void add_encap_ext(struct sadb_msg *msg, host_t *src, host_t *dst) +{ + struct sadb_x_nat_t_type* nat_type; + struct sadb_x_nat_t_port* nat_port; + + nat_type = (struct sadb_x_nat_t_type*)PFKEY_EXT_ADD_NEXT(msg); + nat_type->sadb_x_nat_t_type_exttype = SADB_X_EXT_NAT_T_TYPE; + nat_type->sadb_x_nat_t_type_len = PFKEY_LEN(sizeof(*nat_type)); + nat_type->sadb_x_nat_t_type_type = UDP_ENCAP_ESPINUDP; + PFKEY_EXT_ADD(msg, nat_type); + + nat_port = (struct sadb_x_nat_t_port*)PFKEY_EXT_ADD_NEXT(msg); + nat_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_SPORT; + nat_port->sadb_x_nat_t_port_len = PFKEY_LEN(sizeof(*nat_port)); + nat_port->sadb_x_nat_t_port_port = htons(src->get_port(src)); + PFKEY_EXT_ADD(msg, nat_port); + + nat_port = (struct sadb_x_nat_t_port*)PFKEY_EXT_ADD_NEXT(msg); + nat_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_DPORT; + nat_port->sadb_x_nat_t_port_len = PFKEY_LEN(sizeof(*nat_port)); + nat_port->sadb_x_nat_t_port_port = htons(dst->get_port(dst)); + PFKEY_EXT_ADD(msg, nat_port); +} +#endif /*HAVE_NATT*/ + +/** + * Convert a sadb_address to a traffic_selector + */ +static traffic_selector_t* sadb_address2ts(struct sadb_address *address) +{ + traffic_selector_t *ts; + host_t *host; + u_int8_t proto; + + proto = address->sadb_address_proto; + proto = proto == IPSEC_PROTO_ANY ? 0 : proto; + + /* The Linux 2.6 kernel does not set the protocol and port information + * in the src and dst sadb_address extensions of the SADB_ACQUIRE message. + */ + host = host_create_from_sockaddr((sockaddr_t*)&address[1]); + ts = traffic_selector_create_from_subnet(host, + address->sadb_address_prefixlen, + proto, host->get_port(host), + host->get_port(host) ?: 65535); + return ts; +} + +/** + * Parses a pfkey message received from the kernel + */ +static status_t parse_pfkey_message(struct sadb_msg *msg, pfkey_msg_t *out) +{ + struct sadb_ext* ext; + size_t len; + + memset(out, 0, sizeof(pfkey_msg_t)); + out->msg = msg; + + len = msg->sadb_msg_len; + len -= PFKEY_LEN(sizeof(struct sadb_msg)); + + ext = (struct sadb_ext*)(((char*)msg) + sizeof(struct sadb_msg)); + + while (len >= PFKEY_LEN(sizeof(struct sadb_ext))) + { + DBG3(DBG_KNL, " %N", sadb_ext_type_names, ext->sadb_ext_type); + if (ext->sadb_ext_len < PFKEY_LEN(sizeof(struct sadb_ext)) || + ext->sadb_ext_len > len) + { + DBG1(DBG_KNL, "length of %N extension is invalid", + sadb_ext_type_names, ext->sadb_ext_type); + break; + } + + if ((ext->sadb_ext_type > SADB_EXT_MAX) || (!ext->sadb_ext_type)) + { + DBG1(DBG_KNL, "type of PF_KEY extension (%d) is invalid", + ext->sadb_ext_type); + break; + } + + if (out->ext[ext->sadb_ext_type]) + { + DBG1(DBG_KNL, "duplicate %N extension", + sadb_ext_type_names, ext->sadb_ext_type); + break; + } + + out->ext[ext->sadb_ext_type] = ext; + ext = PFKEY_EXT_NEXT_LEN(ext, len); + } + + if (len) + { + DBG1(DBG_KNL, "PF_KEY message length is invalid"); + return FAILED; + } + + return SUCCESS; +} + +/** + * Send a message to a specific PF_KEY socket and handle the response. + */ +static status_t pfkey_send_socket(private_kernel_pfkey_ipsec_t *this, int socket, + struct sadb_msg *in, struct sadb_msg **out, size_t *out_len) +{ + unsigned char buf[PFKEY_BUFFER_SIZE]; + struct sadb_msg *msg; + int in_len, len; + + this->mutex_pfkey->lock(this->mutex_pfkey); + + /* FIXME: our usage of sequence numbers is probably wrong. check RFC 2367, + * in particular the behavior in response to an SADB_ACQUIRE. */ + in->sadb_msg_seq = ++this->seq; + in->sadb_msg_pid = getpid(); + + in_len = PFKEY_USER_LEN(in->sadb_msg_len); + + while (TRUE) + { + len = send(socket, in, in_len, 0); + + if (len != in_len) + { + if (errno == EINTR) + { + /* interrupted, try again */ + continue; + } + this->mutex_pfkey->unlock(this->mutex_pfkey); + DBG1(DBG_KNL, "error sending to PF_KEY socket: %s", + strerror(errno)); + return FAILED; + } + break; + } + + while (TRUE) + { + msg = (struct sadb_msg*)buf; + + len = recv(socket, buf, sizeof(buf), 0); + + if (len < 0) + { + if (errno == EINTR) + { + DBG1(DBG_KNL, "got interrupted"); + /* interrupted, try again */ + continue; + } + DBG1(DBG_KNL, "error reading from PF_KEY socket: %s", + strerror(errno)); + this->mutex_pfkey->unlock(this->mutex_pfkey); + return FAILED; + } + if (len < sizeof(struct sadb_msg) || + msg->sadb_msg_len < PFKEY_LEN(sizeof(struct sadb_msg))) + { + DBG1(DBG_KNL, "received corrupted PF_KEY message"); + this->mutex_pfkey->unlock(this->mutex_pfkey); + return FAILED; + } + if (msg->sadb_msg_len > len / PFKEY_ALIGNMENT) + { + DBG1(DBG_KNL, "buffer was too small to receive the complete PF_KEY " + "message"); + this->mutex_pfkey->unlock(this->mutex_pfkey); + return FAILED; + } + if (msg->sadb_msg_pid != in->sadb_msg_pid) + { + DBG2(DBG_KNL, "received PF_KEY message is not intended for us"); + continue; + } + if (msg->sadb_msg_seq != this->seq) + { + DBG2(DBG_KNL, "received PF_KEY message with unexpected sequence " + "number, was %d expected %d", msg->sadb_msg_seq, + this->seq); + if (msg->sadb_msg_seq == 0) + { + /* FreeBSD and Mac OS X do this for the response to + * SADB_X_SPDGET (but not for the response to SADB_GET). + * FreeBSD: 'key_spdget' in /usr/src/sys/netipsec/key.c. */ + } + else if (msg->sadb_msg_seq < this->seq) + { + continue; + } + else + { + this->mutex_pfkey->unlock(this->mutex_pfkey); + return FAILED; + } + } + if (msg->sadb_msg_type != in->sadb_msg_type) + { + DBG2(DBG_KNL, "received PF_KEY message of wrong type, " + "was %d expected %d, ignoring", msg->sadb_msg_type, + in->sadb_msg_type); + } + break; + } + + *out_len = len; + *out = (struct sadb_msg*)malloc(len); + memcpy(*out, buf, len); + + this->mutex_pfkey->unlock(this->mutex_pfkey); + return SUCCESS; +} + +/** + * Send a message to the default PF_KEY socket and handle the response. + */ +static status_t pfkey_send(private_kernel_pfkey_ipsec_t *this, + struct sadb_msg *in, struct sadb_msg **out, + size_t *out_len) +{ + return pfkey_send_socket(this, this->socket, in, out, out_len); +} + +/** + * Process a SADB_ACQUIRE message from the kernel + */ +static void process_acquire(private_kernel_pfkey_ipsec_t *this, + struct sadb_msg* msg) +{ + pfkey_msg_t response; + u_int32_t index, reqid = 0; + traffic_selector_t *src_ts, *dst_ts; + policy_entry_t *policy; + policy_sa_t *sa; + + switch (msg->sadb_msg_satype) + { + case SADB_SATYPE_UNSPEC: + case SADB_SATYPE_ESP: + case SADB_SATYPE_AH: + break; + default: + /* acquire for AH/ESP only */ + return; + } + DBG2(DBG_KNL, "received an SADB_ACQUIRE"); + + if (parse_pfkey_message(msg, &response) != SUCCESS) + { + DBG1(DBG_KNL, "parsing SADB_ACQUIRE from kernel failed"); + return; + } + + index = response.x_policy->sadb_x_policy_id; + this->mutex->lock(this->mutex); + if (this->policies->find_first(this->policies, + (linked_list_match_t)policy_entry_match_byindex, + (void**)&policy, &index) == SUCCESS && + policy->used_by->get_first(policy->used_by, (void**)&sa) == SUCCESS) + { + reqid = sa->sa->cfg.reqid; + } + else + { + DBG1(DBG_KNL, "received an SADB_ACQUIRE with policy id %d but no " + "matching policy found", index); + } + this->mutex->unlock(this->mutex); + + src_ts = sadb_address2ts(response.src); + dst_ts = sadb_address2ts(response.dst); + + charon->kernel->acquire(charon->kernel, reqid, src_ts, dst_ts); +} + +/** + * Process a SADB_EXPIRE message from the kernel + */ +static void process_expire(private_kernel_pfkey_ipsec_t *this, + struct sadb_msg* msg) +{ + pfkey_msg_t response; + u_int8_t protocol; + u_int32_t spi; + host_t *dst; + bool hard; + + DBG2(DBG_KNL, "received an SADB_EXPIRE"); + + if (parse_pfkey_message(msg, &response) != SUCCESS) + { + DBG1(DBG_KNL, "parsing SADB_EXPIRE from kernel failed"); + return; + } + + protocol = satype2proto(msg->sadb_msg_satype); + spi = response.sa->sadb_sa_spi; + hard = response.lft_hard != NULL; + + if (protocol == IPPROTO_ESP || protocol == IPPROTO_AH) + { + dst = host_create_from_sockaddr((sockaddr_t*)(response.dst + 1)); + if (dst) + { + charon->kernel->expire(charon->kernel, protocol, spi, dst, hard); + dst->destroy(dst); + } + } +} + +#ifdef SADB_X_MIGRATE +/** + * Process a SADB_X_MIGRATE message from the kernel + */ +static void process_migrate(private_kernel_pfkey_ipsec_t *this, + struct sadb_msg* msg) +{ + pfkey_msg_t response; + traffic_selector_t *src_ts, *dst_ts; + policy_dir_t dir; + u_int32_t reqid = 0; + host_t *local = NULL, *remote = NULL; + + DBG2(DBG_KNL, "received an SADB_X_MIGRATE"); + + if (parse_pfkey_message(msg, &response) != SUCCESS) + { + DBG1(DBG_KNL, "parsing SADB_X_MIGRATE from kernel failed"); + return; + } + src_ts = sadb_address2ts(response.src); + dst_ts = sadb_address2ts(response.dst); + dir = kernel2dir(response.x_policy->sadb_x_policy_dir); + DBG2(DBG_KNL, " policy %R === %R %N, id %u", src_ts, dst_ts, + policy_dir_names, dir); + + /* SADB_X_EXT_KMADDRESS is not present in unpatched kernels < 2.6.28 */ + if (response.x_kmaddress) + { + sockaddr_t *local_addr, *remote_addr; + u_int32_t local_len; + + local_addr = (sockaddr_t*)&response.x_kmaddress[1]; + local = host_create_from_sockaddr(local_addr); + local_len = (local_addr->sa_family == AF_INET6)? + sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); + remote_addr = (sockaddr_t*)((u_int8_t*)local_addr + local_len); + remote = host_create_from_sockaddr(remote_addr); + DBG2(DBG_KNL, " kmaddress: %H...%H", local, remote); + } + + if (src_ts && dst_ts && local && remote) + { + charon->kernel->migrate(charon->kernel, reqid, src_ts, dst_ts, dir, + local, remote); + } + else + { + DESTROY_IF(src_ts); + DESTROY_IF(dst_ts); + DESTROY_IF(local); + DESTROY_IF(remote); + } +} +#endif /*SADB_X_MIGRATE*/ + +#ifdef SADB_X_NAT_T_NEW_MAPPING +/** + * Process a SADB_X_NAT_T_NEW_MAPPING message from the kernel + */ +static void process_mapping(private_kernel_pfkey_ipsec_t *this, + struct sadb_msg* msg) +{ + pfkey_msg_t response; + u_int32_t spi; + sockaddr_t *sa; + host_t *dst, *new; + + DBG2(DBG_KNL, "received an SADB_X_NAT_T_NEW_MAPPING"); + + if (parse_pfkey_message(msg, &response) != SUCCESS) + { + DBG1(DBG_KNL, "parsing SADB_X_NAT_T_NEW_MAPPING from kernel failed"); + return; + } + + if (!response.x_sa2) + { + DBG1(DBG_KNL, "received SADB_X_NAT_T_NEW_MAPPING is missing required " + "information"); + return; + } + + spi = response.sa->sadb_sa_spi; + + if (satype2proto(msg->sadb_msg_satype) != IPPROTO_ESP) + { + return; + } + + sa = (sockaddr_t*)(response.dst + 1); + dst = host_create_from_sockaddr(sa); + switch (sa->sa_family) + { + case AF_INET: + { + struct sockaddr_in *sin = (struct sockaddr_in*)sa; + sin->sin_port = htons(response.x_natt_dport->sadb_x_nat_t_port_port); + break; + } + case AF_INET6: + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)sa; + sin6->sin6_port = htons(response.x_natt_dport->sadb_x_nat_t_port_port); + break; + } + default: + break; + } + if (dst) + { + new = host_create_from_sockaddr(sa); + if (new) + { + charon->kernel->mapping(charon->kernel, IPPROTO_ESP, spi, dst, new); + new->destroy(new); + } + dst->destroy(dst); + } +} +#endif /*SADB_X_NAT_T_NEW_MAPPING*/ + +/** + * Receives events from kernel + */ +static bool receive_events(private_kernel_pfkey_ipsec_t *this, int fd, + watcher_event_t event) +{ + unsigned char buf[PFKEY_BUFFER_SIZE]; + struct sadb_msg *msg = (struct sadb_msg*)buf; + int len; + + len = recvfrom(this->socket_events, buf, sizeof(buf), MSG_DONTWAIT, NULL, 0); + if (len < 0) + { + switch (errno) + { + case EINTR: + /* interrupted, try again */ + return TRUE; + case EAGAIN: + /* no data ready, select again */ + return TRUE; + default: + DBG1(DBG_KNL, "unable to receive from PF_KEY event socket"); + sleep(1); + return TRUE; + } + } + + if (len < sizeof(struct sadb_msg) || + msg->sadb_msg_len < PFKEY_LEN(sizeof(struct sadb_msg))) + { + DBG2(DBG_KNL, "received corrupted PF_KEY message"); + return TRUE; + } + if (msg->sadb_msg_pid != 0) + { /* not from kernel. not interested, try another one */ + return TRUE; + } + if (msg->sadb_msg_len > len / PFKEY_ALIGNMENT) + { + DBG1(DBG_KNL, "buffer was too small to receive the complete " + "PF_KEY message"); + return TRUE; + } + + switch (msg->sadb_msg_type) + { + case SADB_ACQUIRE: + process_acquire(this, msg); + break; + case SADB_EXPIRE: + process_expire(this, msg); + break; +#ifdef SADB_X_MIGRATE + case SADB_X_MIGRATE: + process_migrate(this, msg); + break; +#endif /*SADB_X_MIGRATE*/ +#ifdef SADB_X_NAT_T_NEW_MAPPING + case SADB_X_NAT_T_NEW_MAPPING: + process_mapping(this, msg); + break; +#endif /*SADB_X_NAT_T_NEW_MAPPING*/ + default: + break; + } + + return TRUE; +} + +/** + * Get an SPI for a specific protocol from the kernel. + */ + +static status_t get_spi_internal(private_kernel_pfkey_ipsec_t *this, + host_t *src, host_t *dst, u_int8_t proto, u_int32_t min, u_int32_t max, + u_int32_t *spi) +{ + unsigned char request[PFKEY_BUFFER_SIZE]; + struct sadb_msg *msg, *out; + struct sadb_spirange *range; + pfkey_msg_t response; + u_int32_t received_spi = 0; + size_t len; + + memset(&request, 0, sizeof(request)); + + msg = (struct sadb_msg*)request; + msg->sadb_msg_version = PF_KEY_V2; + msg->sadb_msg_type = SADB_GETSPI; + msg->sadb_msg_satype = proto2satype(proto); + msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg)); + + add_addr_ext(msg, src, SADB_EXT_ADDRESS_SRC, 0, 0, FALSE); + add_addr_ext(msg, dst, SADB_EXT_ADDRESS_DST, 0, 0, FALSE); + + range = (struct sadb_spirange*)PFKEY_EXT_ADD_NEXT(msg); + range->sadb_spirange_exttype = SADB_EXT_SPIRANGE; + range->sadb_spirange_len = PFKEY_LEN(sizeof(struct sadb_spirange)); + range->sadb_spirange_min = min; + range->sadb_spirange_max = max; + PFKEY_EXT_ADD(msg, range); + + if (pfkey_send(this, msg, &out, &len) == SUCCESS) + { + if (out->sadb_msg_errno) + { + DBG1(DBG_KNL, "allocating SPI failed: %s (%d)", + strerror(out->sadb_msg_errno), out->sadb_msg_errno); + } + else if (parse_pfkey_message(out, &response) == SUCCESS) + { + received_spi = response.sa->sadb_sa_spi; + } + free(out); + } + + if (received_spi == 0) + { + return FAILED; + } + + *spi = received_spi; + return SUCCESS; +} + +METHOD(kernel_ipsec_t, get_spi, status_t, + private_kernel_pfkey_ipsec_t *this, host_t *src, host_t *dst, + u_int8_t protocol, u_int32_t *spi) +{ + if (get_spi_internal(this, src, dst, protocol, + 0xc0000000, 0xcFFFFFFF, spi) != SUCCESS) + { + DBG1(DBG_KNL, "unable to get SPI"); + return FAILED; + } + + DBG2(DBG_KNL, "got SPI %.8x", ntohl(*spi)); + return SUCCESS; +} + +METHOD(kernel_ipsec_t, get_cpi, status_t, + private_kernel_pfkey_ipsec_t *this, host_t *src, host_t *dst, + u_int16_t *cpi) +{ + u_int32_t received_spi = 0; + + DBG2(DBG_KNL, "getting CPI"); + + if (get_spi_internal(this, src, dst, IPPROTO_COMP, + 0x100, 0xEFFF, &received_spi) != SUCCESS) + { + DBG1(DBG_KNL, "unable to get CPI"); + return FAILED; + } + + *cpi = htons((u_int16_t)ntohl(received_spi)); + + DBG2(DBG_KNL, "got CPI %.4x", ntohs(*cpi)); + return SUCCESS; +} + +METHOD(kernel_ipsec_t, add_sa, status_t, + private_kernel_pfkey_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, bool update, + linked_list_t *src_ts, linked_list_t *dst_ts) +{ + unsigned char request[PFKEY_BUFFER_SIZE]; + struct sadb_msg *msg, *out; + struct sadb_sa *sa; + struct sadb_x_sa2 *sa2; + struct sadb_lifetime *lft; + struct sadb_key *key; + size_t len; + + /* if IPComp is used, we install an additional IPComp SA. if the cpi is 0 + * we are in the recursive call below */ + if (ipcomp != IPCOMP_NONE && cpi != 0) + { + lifetime_cfg_t lft = {{0,0,0},{0,0,0},{0,0,0}}; + add_sa(this, src, dst, htonl(ntohs(cpi)), IPPROTO_COMP, reqid, mark, + tfc, &lft, ENCR_UNDEFINED, chunk_empty, AUTH_UNDEFINED, + chunk_empty, mode, ipcomp, 0, 0, FALSE, FALSE, FALSE, inbound, + update, NULL, NULL); + ipcomp = IPCOMP_NONE; + /* use transport mode ESP SA, IPComp uses tunnel mode */ + mode = MODE_TRANSPORT; + } + + if (update) + { + /* As we didn't know the reqid during SPI allocation, we used reqid + * zero. Unfortunately we can't SADB_UPDATE to the new reqid, hence we + * have to delete the SPI allocation state manually. The reqid + * selector does not count for that, therefore we have to delete + * that state before installing the new SA to avoid deleting the + * the new state after installing it. */ + mark_t zeromark = {0, 0}; + + if (this->public.interface.del_sa(&this->public.interface, + src, dst, spi, protocol, 0, zeromark) != SUCCESS) + { + DBG1(DBG_KNL, "deleting SPI allocation SA failed"); + } + } + + memset(&request, 0, sizeof(request)); + + DBG2(DBG_KNL, "adding SAD entry with SPI %.8x and reqid {%u}", + ntohl(spi), reqid); + + msg = (struct sadb_msg*)request; + msg->sadb_msg_version = PF_KEY_V2; + msg->sadb_msg_type = SADB_ADD; + msg->sadb_msg_satype = proto2satype(protocol); + msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg)); + +#ifdef __APPLE__ + if (encap) + { + struct sadb_sa_2 *sa_2; + sa_2 = (struct sadb_sa_2*)PFKEY_EXT_ADD_NEXT(msg); + sa_2->sadb_sa_natt_port = dst->get_port(dst); + sa = &sa_2->sa; + sa->sadb_sa_flags |= SADB_X_EXT_NATT; + len = sizeof(struct sadb_sa_2); + } + else +#endif + { + sa = (struct sadb_sa*)PFKEY_EXT_ADD_NEXT(msg); + len = sizeof(struct sadb_sa); + } + sa->sadb_sa_exttype = SADB_EXT_SA; + sa->sadb_sa_len = PFKEY_LEN(len); + sa->sadb_sa_spi = spi; + if (protocol == IPPROTO_COMP) + { + sa->sadb_sa_encrypt = lookup_algorithm(COMPRESSION_ALGORITHM, ipcomp); + } + else + { + /* Linux interprets sadb_sa_replay as number of packets/bits in the + * replay window, whereas on BSD it's the size of the window in bytes */ +#ifdef __linux__ + sa->sadb_sa_replay = min(replay_window, 32); +#else + sa->sadb_sa_replay = (replay_window + 7) / 8; +#endif + sa->sadb_sa_auth = lookup_algorithm(INTEGRITY_ALGORITHM, int_alg); + sa->sadb_sa_encrypt = lookup_algorithm(ENCRYPTION_ALGORITHM, enc_alg); + } + PFKEY_EXT_ADD(msg, sa); + + sa2 = (struct sadb_x_sa2*)PFKEY_EXT_ADD_NEXT(msg); + sa2->sadb_x_sa2_exttype = SADB_X_EXT_SA2; + sa2->sadb_x_sa2_len = PFKEY_LEN(sizeof(struct sadb_spirange)); + sa2->sadb_x_sa2_mode = mode2kernel(mode); + sa2->sadb_x_sa2_reqid = reqid; + PFKEY_EXT_ADD(msg, sa2); + + add_addr_ext(msg, src, SADB_EXT_ADDRESS_SRC, 0, 0, FALSE); + add_addr_ext(msg, dst, SADB_EXT_ADDRESS_DST, 0, 0, FALSE); + + lft = (struct sadb_lifetime*)PFKEY_EXT_ADD_NEXT(msg); + lft->sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT; + lft->sadb_lifetime_len = PFKEY_LEN(sizeof(struct sadb_lifetime)); + lft->sadb_lifetime_allocations = lifetime->packets.rekey; + lft->sadb_lifetime_bytes = lifetime->bytes.rekey; + lft->sadb_lifetime_addtime = lifetime->time.rekey; + lft->sadb_lifetime_usetime = 0; /* we only use addtime */ + PFKEY_EXT_ADD(msg, lft); + + lft = (struct sadb_lifetime*)PFKEY_EXT_ADD_NEXT(msg); + lft->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD; + lft->sadb_lifetime_len = PFKEY_LEN(sizeof(struct sadb_lifetime)); + lft->sadb_lifetime_allocations = lifetime->packets.life; + lft->sadb_lifetime_bytes = lifetime->bytes.life; + lft->sadb_lifetime_addtime = lifetime->time.life; + lft->sadb_lifetime_usetime = 0; /* we only use addtime */ + PFKEY_EXT_ADD(msg, lft); + + if (enc_alg != ENCR_UNDEFINED) + { + if (!sa->sadb_sa_encrypt) + { + DBG1(DBG_KNL, "algorithm %N not supported by kernel!", + encryption_algorithm_names, enc_alg); + return FAILED; + } + DBG2(DBG_KNL, " using encryption algorithm %N with key size %d", + encryption_algorithm_names, enc_alg, enc_key.len * 8); + + key = (struct sadb_key*)PFKEY_EXT_ADD_NEXT(msg); + key->sadb_key_exttype = SADB_EXT_KEY_ENCRYPT; + key->sadb_key_bits = enc_key.len * 8; + key->sadb_key_len = PFKEY_LEN(sizeof(struct sadb_key) + enc_key.len); + memcpy(key + 1, enc_key.ptr, enc_key.len); + + PFKEY_EXT_ADD(msg, key); + } + + if (int_alg != AUTH_UNDEFINED) + { + if (!sa->sadb_sa_auth) + { + DBG1(DBG_KNL, "algorithm %N not supported by kernel!", + integrity_algorithm_names, int_alg); + return FAILED; + } + DBG2(DBG_KNL, " using integrity algorithm %N with key size %d", + integrity_algorithm_names, int_alg, int_key.len * 8); + + key = (struct sadb_key*)PFKEY_EXT_ADD_NEXT(msg); + key->sadb_key_exttype = SADB_EXT_KEY_AUTH; + key->sadb_key_bits = int_key.len * 8; + key->sadb_key_len = PFKEY_LEN(sizeof(struct sadb_key) + int_key.len); + memcpy(key + 1, int_key.ptr, int_key.len); + + PFKEY_EXT_ADD(msg, key); + } + +#ifdef HAVE_NATT + if (encap) + { + add_encap_ext(msg, src, dst); + } +#endif /*HAVE_NATT*/ + + if (pfkey_send(this, msg, &out, &len) != SUCCESS) + { + DBG1(DBG_KNL, "unable to add SAD entry with SPI %.8x", ntohl(spi)); + return FAILED; + } + else if (out->sadb_msg_errno) + { + DBG1(DBG_KNL, "unable to add SAD entry with SPI %.8x: %s (%d)", + ntohl(spi), strerror(out->sadb_msg_errno), out->sadb_msg_errno); + free(out); + return FAILED; + } + + free(out); + return SUCCESS; +} + +METHOD(kernel_ipsec_t, update_sa, status_t, + private_kernel_pfkey_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) +{ + unsigned char request[PFKEY_BUFFER_SIZE]; + struct sadb_msg *msg, *out; + struct sadb_sa *sa; + pfkey_msg_t response; + size_t len; + + /* we can't update the SA if any of the ip addresses have changed. + * that's because we can't use SADB_UPDATE and by deleting and readding the + * SA the sequence numbers would get lost */ + if (!src->ip_equals(src, new_src) || + !dst->ip_equals(dst, new_dst)) + { + DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x: address " + "changes are not supported", ntohl(spi)); + return NOT_SUPPORTED; + } + + /* if IPComp is used, we first update the IPComp SA */ + if (cpi) + { + update_sa(this, htonl(ntohs(cpi)), IPPROTO_COMP, 0, + src, dst, new_src, new_dst, FALSE, FALSE, mark); + } + + memset(&request, 0, sizeof(request)); + + DBG2(DBG_KNL, "querying SAD entry with SPI %.8x", ntohl(spi)); + + msg = (struct sadb_msg*)request; + msg->sadb_msg_version = PF_KEY_V2; + msg->sadb_msg_type = SADB_GET; + msg->sadb_msg_satype = proto2satype(protocol); + msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg)); + + sa = (struct sadb_sa*)PFKEY_EXT_ADD_NEXT(msg); + sa->sadb_sa_exttype = SADB_EXT_SA; + sa->sadb_sa_len = PFKEY_LEN(sizeof(struct sadb_sa)); + sa->sadb_sa_spi = spi; + PFKEY_EXT_ADD(msg, sa); + + /* the kernel wants a SADB_EXT_ADDRESS_SRC to be present even though + * it is not used for anything. */ + add_anyaddr_ext(msg, dst->get_family(dst), SADB_EXT_ADDRESS_SRC); + add_addr_ext(msg, dst, SADB_EXT_ADDRESS_DST, 0, 0, FALSE); + + if (pfkey_send(this, msg, &out, &len) != SUCCESS) + { + DBG1(DBG_KNL, "unable to query SAD entry with SPI %.8x", ntohl(spi)); + return FAILED; + } + else if (out->sadb_msg_errno) + { + DBG1(DBG_KNL, "unable to query SAD entry with SPI %.8x: %s (%d)", + ntohl(spi), strerror(out->sadb_msg_errno), + out->sadb_msg_errno); + free(out); + return FAILED; + } + else if (parse_pfkey_message(out, &response) != SUCCESS) + { + DBG1(DBG_KNL, "unable to query SAD entry with SPI %.8x: parsing " + "response from kernel failed", ntohl(spi)); + free(out); + return FAILED; + } + + DBG2(DBG_KNL, "updating SAD entry with SPI %.8x from %#H..%#H to %#H..%#H", + ntohl(spi), src, dst, new_src, new_dst); + + memset(&request, 0, sizeof(request)); + + msg = (struct sadb_msg*)request; + msg->sadb_msg_version = PF_KEY_V2; + msg->sadb_msg_type = SADB_UPDATE; + msg->sadb_msg_satype = proto2satype(protocol); + msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg)); + +#ifdef __APPLE__ + { + struct sadb_sa_2 *sa_2; + sa_2 = (struct sadb_sa_2*)PFKEY_EXT_ADD_NEXT(msg); + sa_2->sa.sadb_sa_len = PFKEY_LEN(sizeof(struct sadb_sa_2)); + memcpy(&sa_2->sa, response.sa, sizeof(struct sadb_sa)); + if (encap) + { + sa_2->sadb_sa_natt_port = new_dst->get_port(new_dst); + sa_2->sa.sadb_sa_flags |= SADB_X_EXT_NATT; + } + } +#else + PFKEY_EXT_COPY(msg, response.sa); +#endif + PFKEY_EXT_COPY(msg, response.x_sa2); + + PFKEY_EXT_COPY(msg, response.src); + PFKEY_EXT_COPY(msg, response.dst); + + PFKEY_EXT_COPY(msg, response.lft_soft); + PFKEY_EXT_COPY(msg, response.lft_hard); + + if (response.key_encr) + { + PFKEY_EXT_COPY(msg, response.key_encr); + } + + if (response.key_auth) + { + PFKEY_EXT_COPY(msg, response.key_auth); + } + +#ifdef HAVE_NATT + if (new_encap) + { + add_encap_ext(msg, new_src, new_dst); + } +#endif /*HAVE_NATT*/ + + free(out); + + if (pfkey_send(this, msg, &out, &len) != SUCCESS) + { + DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x", ntohl(spi)); + return FAILED; + } + else if (out->sadb_msg_errno) + { + DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x: %s (%d)", + ntohl(spi), strerror(out->sadb_msg_errno), + out->sadb_msg_errno); + free(out); + return FAILED; + } + free(out); + + return SUCCESS; +} + +METHOD(kernel_ipsec_t, query_sa, status_t, + private_kernel_pfkey_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) +{ + unsigned char request[PFKEY_BUFFER_SIZE]; + struct sadb_msg *msg, *out; + struct sadb_sa *sa; + pfkey_msg_t response; + size_t len; + + memset(&request, 0, sizeof(request)); + + DBG2(DBG_KNL, "querying SAD entry with SPI %.8x", ntohl(spi)); + + msg = (struct sadb_msg*)request; + msg->sadb_msg_version = PF_KEY_V2; + msg->sadb_msg_type = SADB_GET; + msg->sadb_msg_satype = proto2satype(protocol); + msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg)); + + sa = (struct sadb_sa*)PFKEY_EXT_ADD_NEXT(msg); + sa->sadb_sa_exttype = SADB_EXT_SA; + sa->sadb_sa_len = PFKEY_LEN(sizeof(struct sadb_sa)); + sa->sadb_sa_spi = spi; + PFKEY_EXT_ADD(msg, sa); + + /* the Linux Kernel doesn't care for the src address, but other systems do + * (e.g. FreeBSD) + */ + add_addr_ext(msg, src, SADB_EXT_ADDRESS_SRC, 0, 0, FALSE); + add_addr_ext(msg, dst, SADB_EXT_ADDRESS_DST, 0, 0, FALSE); + + if (pfkey_send(this, msg, &out, &len) != SUCCESS) + { + DBG1(DBG_KNL, "unable to query SAD entry with SPI %.8x", ntohl(spi)); + return FAILED; + } + else if (out->sadb_msg_errno) + { + DBG1(DBG_KNL, "unable to query SAD entry with SPI %.8x: %s (%d)", + ntohl(spi), strerror(out->sadb_msg_errno), + out->sadb_msg_errno); + free(out); + return FAILED; + } + else if (parse_pfkey_message(out, &response) != SUCCESS) + { + DBG1(DBG_KNL, "unable to query SAD entry with SPI %.8x", ntohl(spi)); + free(out); + return FAILED; + } + if (bytes) + { + *bytes = response.lft_current->sadb_lifetime_bytes; + } + if (packets) + { + /* at least on Linux and FreeBSD this contains the number of packets */ + *packets = response.lft_current->sadb_lifetime_allocations; + } + if (time) + { +#ifdef __APPLE__ + /* OS X uses the "last" time of use in usetime */ + *time = response.lft_current->sadb_lifetime_usetime; +#else /* !__APPLE__ */ + /* on Linux, sadb_lifetime_usetime is set to the "first" time of use, + * which is actually correct according to PF_KEY. We have to query + * policies for the last usetime. */ + *time = 0; +#endif /* !__APPLE__ */ + } + + free(out); + return SUCCESS; +} + +METHOD(kernel_ipsec_t, del_sa, status_t, + private_kernel_pfkey_ipsec_t *this, host_t *src, host_t *dst, + u_int32_t spi, u_int8_t protocol, u_int16_t cpi, mark_t mark) +{ + unsigned char request[PFKEY_BUFFER_SIZE]; + struct sadb_msg *msg, *out; + struct sadb_sa *sa; + size_t len; + + /* if IPComp was used, we first delete the additional IPComp SA */ + if (cpi) + { + del_sa(this, src, dst, htonl(ntohs(cpi)), IPPROTO_COMP, 0, mark); + } + + memset(&request, 0, sizeof(request)); + + DBG2(DBG_KNL, "deleting SAD entry with SPI %.8x", ntohl(spi)); + + msg = (struct sadb_msg*)request; + msg->sadb_msg_version = PF_KEY_V2; + msg->sadb_msg_type = SADB_DELETE; + msg->sadb_msg_satype = proto2satype(protocol); + msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg)); + + sa = (struct sadb_sa*)PFKEY_EXT_ADD_NEXT(msg); + sa->sadb_sa_exttype = SADB_EXT_SA; + sa->sadb_sa_len = PFKEY_LEN(sizeof(struct sadb_sa)); + sa->sadb_sa_spi = spi; + PFKEY_EXT_ADD(msg, sa); + + /* the Linux Kernel doesn't care for the src address, but other systems do + * (e.g. FreeBSD) + */ + add_addr_ext(msg, src, SADB_EXT_ADDRESS_SRC, 0, 0, FALSE); + add_addr_ext(msg, dst, SADB_EXT_ADDRESS_DST, 0, 0, FALSE); + + if (pfkey_send(this, msg, &out, &len) != SUCCESS) + { + DBG1(DBG_KNL, "unable to delete SAD entry with SPI %.8x", ntohl(spi)); + return FAILED; + } + else if (out->sadb_msg_errno) + { + DBG1(DBG_KNL, "unable to delete SAD entry with SPI %.8x: %s (%d)", + ntohl(spi), strerror(out->sadb_msg_errno), + out->sadb_msg_errno); + free(out); + return FAILED; + } + + DBG2(DBG_KNL, "deleted SAD entry with SPI %.8x", ntohl(spi)); + free(out); + return SUCCESS; +} + +METHOD(kernel_ipsec_t, flush_sas, status_t, + private_kernel_pfkey_ipsec_t *this) +{ + unsigned char request[PFKEY_BUFFER_SIZE]; + struct sadb_msg *msg, *out; + struct { + u_int8_t proto; + char *name; + } protos[] = { + { SADB_SATYPE_AH, "AH" }, + { SADB_SATYPE_ESP, "ESP" }, + { SADB_X_SATYPE_IPCOMP, "IPComp" }, + }; + size_t len; + int i; + + memset(&request, 0, sizeof(request)); + + msg = (struct sadb_msg*)request; + msg->sadb_msg_version = PF_KEY_V2; + msg->sadb_msg_type = SADB_FLUSH; + msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg)); + + for (i = 0; i < countof(protos); i++) + { + DBG2(DBG_KNL, "flushing all %s SAD entries", protos[i].name); + + msg->sadb_msg_satype = protos[i].proto; + if (pfkey_send(this, msg, &out, &len) != SUCCESS) + { + DBG1(DBG_KNL, "unable to flush %s SAD entries", protos[i].name); + return FAILED; + } + else if (out->sadb_msg_errno) + { + DBG1(DBG_KNL, "unable to flush %s SAD entries: %s (%d)", + protos[i].name, strerror(out->sadb_msg_errno), + out->sadb_msg_errno); + free(out); + return FAILED; + } + free(out); + } + return SUCCESS; +} + +/** + * Add an explicit exclude route to a routing entry + */ +static void add_exclude_route(private_kernel_pfkey_ipsec_t *this, + route_entry_t *route, host_t *src, host_t *dst) +{ + enumerator_t *enumerator; + exclude_route_t *exclude; + host_t *gtw; + + enumerator = this->excludes->create_enumerator(this->excludes); + while (enumerator->enumerate(enumerator, &exclude)) + { + if (dst->ip_equals(dst, exclude->dst)) + { + route->exclude = exclude; + exclude->refs++; + } + } + enumerator->destroy(enumerator); + + if (!route->exclude) + { + DBG2(DBG_KNL, "installing new exclude route for %H src %H", dst, src); + gtw = charon->kernel->get_nexthop(charon->kernel, dst, -1, NULL); + if (gtw) + { + char *if_name = NULL; + + if (charon->kernel->get_interface(charon->kernel, src, &if_name) && + charon->kernel->add_route(charon->kernel, + dst->get_address(dst), + dst->get_family(dst) == AF_INET ? 32 : 128, + gtw, src, if_name) == SUCCESS) + { + INIT(exclude, + .dst = dst->clone(dst), + .src = src->clone(src), + .gtw = gtw->clone(gtw), + .refs = 1, + ); + route->exclude = exclude; + this->excludes->insert_last(this->excludes, exclude); + } + else + { + DBG1(DBG_KNL, "installing exclude route for %H failed", dst); + } + gtw->destroy(gtw); + free(if_name); + } + else + { + DBG1(DBG_KNL, "gateway lookup for for %H failed", dst); + } + } +} + +/** + * Remove an exclude route attached to a routing entry + */ +static void remove_exclude_route(private_kernel_pfkey_ipsec_t *this, + route_entry_t *route) +{ + if (route->exclude) + { + enumerator_t *enumerator; + exclude_route_t *exclude; + bool removed = FALSE; + host_t *dst; + + enumerator = this->excludes->create_enumerator(this->excludes); + while (enumerator->enumerate(enumerator, &exclude)) + { + if (route->exclude == exclude) + { + if (--exclude->refs == 0) + { + this->excludes->remove_at(this->excludes, enumerator); + removed = TRUE; + break; + } + } + } + enumerator->destroy(enumerator); + + if (removed) + { + char *if_name = NULL; + + dst = route->exclude->dst; + DBG2(DBG_KNL, "uninstalling exclude route for %H src %H", + dst, route->exclude->src); + if (charon->kernel->get_interface( + charon->kernel, + route->exclude->src, &if_name) && + charon->kernel->del_route(charon->kernel, + dst->get_address(dst), + dst->get_family(dst) == AF_INET ? 32 : 128, + route->exclude->gtw, route->exclude->src, + if_name) != SUCCESS) + { + DBG1(DBG_KNL, "uninstalling exclude route for %H failed", dst); + } + exclude_route_destroy(route->exclude); + free(if_name); + } + route->exclude = NULL; + } +} + +/** + * Try to install a route to the given inbound policy + */ +static bool install_route(private_kernel_pfkey_ipsec_t *this, + policy_entry_t *policy, policy_sa_in_t *in) +{ + route_entry_t *route, *old; + host_t *host, *src, *dst; + bool is_virtual; + + if (charon->kernel->get_address_by_ts(charon->kernel, in->dst_ts, &host, + &is_virtual) != SUCCESS) + { + return FALSE; + } + + /* switch src/dst, as we handle an IN policy */ + src = in->generic.sa->dst; + dst = in->generic.sa->src; + + INIT(route, + .prefixlen = policy->src.mask, + .src_ip = host, + .dst_net = chunk_clone(policy->src.net->get_address(policy->src.net)), + ); + + if (!dst->is_anyaddr(dst)) + { + route->gateway = charon->kernel->get_nexthop(charon->kernel, dst, -1, + src); + + /* if the IP is virtual, we install the route over the interface it has + * been installed on. Otherwise we use the interface we use for IKE, as + * this is required for example on Linux. */ + if (is_virtual) + { + src = route->src_ip; + } + } + else + { /* for shunt policies */ + route->gateway = charon->kernel->get_nexthop(charon->kernel, + policy->src.net, policy->src.mask, + route->src_ip); + + /* we don't have a source address, use the address we found */ + src = route->src_ip; + } + + /* get interface for route, using source address */ + if (!charon->kernel->get_interface(charon->kernel, src, &route->if_name)) + { + route_entry_destroy(route); + return FALSE; + } + + if (policy->route) + { + old = policy->route; + + if (route_entry_equals(old, route)) + { /* such a route already exists */ + route_entry_destroy(route); + return TRUE; + } + /* uninstall previously installed route */ + if (charon->kernel->del_route(charon->kernel, old->dst_net, + old->prefixlen, old->gateway, + old->src_ip, old->if_name) != SUCCESS) + { + DBG1(DBG_KNL, "error uninstalling route installed with policy " + "%R === %R %N", in->src_ts, in->dst_ts, + policy_dir_names, policy->direction); + } + route_entry_destroy(old); + policy->route = NULL; + } + + /* if remote traffic selector covers the IKE peer, add an exclude route */ + if (charon->kernel->get_features(charon->kernel) & KERNEL_REQUIRE_EXCLUDE_ROUTE) + { + if (in->src_ts->is_host(in->src_ts, dst)) + { + DBG1(DBG_KNL, "can't install route for %R === %R %N, conflicts " + "with IKE traffic", in->src_ts, in->dst_ts, policy_dir_names, + policy->direction); + route_entry_destroy(route); + return FALSE; + } + if (in->src_ts->includes(in->src_ts, dst)) + { + add_exclude_route(this, route, in->generic.sa->dst, dst); + } + } + + DBG2(DBG_KNL, "installing route: %R via %H src %H dev %s", + in->src_ts, route->gateway, route->src_ip, route->if_name); + + switch (charon->kernel->add_route(charon->kernel, route->dst_net, + route->prefixlen, route->gateway, + route->src_ip, route->if_name)) + { + case ALREADY_DONE: + /* route exists, do not uninstall */ + remove_exclude_route(this, route); + route_entry_destroy(route); + return TRUE; + case SUCCESS: + /* cache the installed route */ + policy->route = route; + return TRUE; + default: + DBG1(DBG_KNL, "installing route failed: %R via %H src %H dev %s", + in->src_ts, route->gateway, route->src_ip, route->if_name); + remove_exclude_route(this, route); + route_entry_destroy(route); + return FALSE; + } +} + +/** + * Add or update a policy in the kernel. + * + * Note: The mutex has to be locked when entering this function. + */ +static status_t add_policy_internal(private_kernel_pfkey_ipsec_t *this, + policy_entry_t *policy, policy_sa_t *mapping, bool update) +{ + unsigned char request[PFKEY_BUFFER_SIZE]; + struct sadb_msg *msg, *out; + struct sadb_x_policy *pol; + struct sadb_x_ipsecrequest *req; + ipsec_sa_t *ipsec = mapping->sa; + pfkey_msg_t response; + size_t len; + ipsec_mode_t proto_mode; + status_t status; + + memset(&request, 0, sizeof(request)); + + msg = (struct sadb_msg*)request; + msg->sadb_msg_version = PF_KEY_V2; + msg->sadb_msg_type = update ? SADB_X_SPDUPDATE : SADB_X_SPDADD; + msg->sadb_msg_satype = 0; + msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg)); + + pol = (struct sadb_x_policy*)PFKEY_EXT_ADD_NEXT(msg); + pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY; + pol->sadb_x_policy_len = PFKEY_LEN(sizeof(struct sadb_x_policy)); + pol->sadb_x_policy_id = 0; + pol->sadb_x_policy_dir = dir2kernel(policy->direction); + pol->sadb_x_policy_type = type2kernel(mapping->type); +#ifdef HAVE_STRUCT_SADB_X_POLICY_SADB_X_POLICY_PRIORITY + pol->sadb_x_policy_priority = mapping->priority; +#endif + + /* one or more sadb_x_ipsecrequest extensions are added to the + * sadb_x_policy extension */ + proto_mode = ipsec->cfg.mode; + + req = (struct sadb_x_ipsecrequest*)(pol + 1); + + if (ipsec->cfg.ipcomp.transform != IPCOMP_NONE) + { + req->sadb_x_ipsecrequest_proto = IPPROTO_COMP; + + /* !!! the length here MUST be in octets instead of 64 bit words */ + req->sadb_x_ipsecrequest_len = sizeof(struct sadb_x_ipsecrequest); + req->sadb_x_ipsecrequest_mode = mode2kernel(ipsec->cfg.mode); + req->sadb_x_ipsecrequest_reqid = ipsec->cfg.reqid; + req->sadb_x_ipsecrequest_level = (policy->direction == POLICY_OUT) ? + IPSEC_LEVEL_UNIQUE : IPSEC_LEVEL_USE; + if (ipsec->cfg.mode == MODE_TUNNEL) + { + len = hostcpy(req + 1, ipsec->src, FALSE); + req->sadb_x_ipsecrequest_len += len; + len = hostcpy((char*)(req + 1) + len, ipsec->dst, FALSE); + req->sadb_x_ipsecrequest_len += len; + /* use transport mode for other SAs */ + proto_mode = MODE_TRANSPORT; + } + + pol->sadb_x_policy_len += PFKEY_LEN(req->sadb_x_ipsecrequest_len); + req = (struct sadb_x_ipsecrequest*)((char*)(req) + + req->sadb_x_ipsecrequest_len); + } + + req->sadb_x_ipsecrequest_proto = ipsec->cfg.esp.use ? IPPROTO_ESP + : IPPROTO_AH; + /* !!! the length here MUST be in octets instead of 64 bit words */ + req->sadb_x_ipsecrequest_len = sizeof(struct sadb_x_ipsecrequest); + req->sadb_x_ipsecrequest_mode = mode2kernel(proto_mode); + req->sadb_x_ipsecrequest_reqid = ipsec->cfg.reqid; + req->sadb_x_ipsecrequest_level = IPSEC_LEVEL_UNIQUE; + if (proto_mode == MODE_TUNNEL) + { + len = hostcpy(req + 1, ipsec->src, FALSE); + req->sadb_x_ipsecrequest_len += len; + len = hostcpy((char*)(req + 1) + len, ipsec->dst, FALSE); + req->sadb_x_ipsecrequest_len += len; + } + + pol->sadb_x_policy_len += PFKEY_LEN(req->sadb_x_ipsecrequest_len); + PFKEY_EXT_ADD(msg, pol); + + add_addr_ext(msg, policy->src.net, SADB_EXT_ADDRESS_SRC, policy->src.proto, + policy->src.mask, TRUE); + add_addr_ext(msg, policy->dst.net, SADB_EXT_ADDRESS_DST, policy->dst.proto, + policy->dst.mask, TRUE); + +#ifdef __FreeBSD__ + { /* on FreeBSD a lifetime has to be defined to be able to later query + * the current use time. */ + struct sadb_lifetime *lft; + lft = (struct sadb_lifetime*)PFKEY_EXT_ADD_NEXT(msg); + lft->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD; + lft->sadb_lifetime_len = PFKEY_LEN(sizeof(struct sadb_lifetime)); + lft->sadb_lifetime_addtime = LONG_MAX; + PFKEY_EXT_ADD(msg, lft); + } +#endif + + this->mutex->unlock(this->mutex); + + status = pfkey_send(this, msg, &out, &len); + if (status == SUCCESS && !update && out->sadb_msg_errno == EEXIST) + { + DBG1(DBG_KNL, "policy already exists, try to update it"); + free(out); + msg->sadb_msg_type = SADB_X_SPDUPDATE; + status = pfkey_send(this, msg, &out, &len); + } + if (status != SUCCESS) + { + return FAILED; + } + else if (out->sadb_msg_errno) + { + DBG1(DBG_KNL, "unable to %s policy: %s (%d)", + update ? "update" : "add", strerror(out->sadb_msg_errno), + out->sadb_msg_errno); + free(out); + return FAILED; + } + else if (parse_pfkey_message(out, &response) != SUCCESS) + { + DBG1(DBG_KNL, "unable to %s policy: parsing response from kernel " + "failed", update ? "update" : "add"); + free(out); + return FAILED; + } + + /* we try to find the policy again and update the kernel index */ + this->mutex->lock(this->mutex); + if (this->policies->find_first(this->policies, NULL, + (void**)&policy) != SUCCESS) + { + DBG2(DBG_KNL, "unable to update index, the policy is already gone, " + "ignoring"); + this->mutex->unlock(this->mutex); + free(out); + return SUCCESS; + } + policy->index = response.x_policy->sadb_x_policy_id; + free(out); + + /* install a route, if: + * - this is an inbound policy (to just get one for each child) + * - we are in tunnel mode or install a bypass policy + * - routing is not disabled via strongswan.conf + */ + if (policy->direction == POLICY_IN && this->install_routes && + (mapping->type != POLICY_IPSEC || ipsec->cfg.mode != MODE_TRANSPORT)) + { + install_route(this, policy, (policy_sa_in_t*)mapping); + } + this->mutex->unlock(this->mutex); + return SUCCESS; +} + +METHOD(kernel_ipsec_t, add_policy, status_t, + private_kernel_pfkey_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) +{ + policy_entry_t *policy, *found = NULL; + policy_sa_t *assigned_sa, *current_sa; + enumerator_t *enumerator; + bool update = TRUE; + + if (dir2kernel(direction) == IPSEC_DIR_INVALID) + { /* FWD policies are not supported on all platforms */ + return SUCCESS; + } + + /* create a policy */ + policy = create_policy_entry(src_ts, dst_ts, direction); + + /* find a matching policy */ + this->mutex->lock(this->mutex); + if (this->policies->find_first(this->policies, + (linked_list_match_t)policy_entry_equals, + (void**)&found, policy) == SUCCESS) + { /* use existing policy */ + DBG2(DBG_KNL, "policy %R === %R %N already exists, increasing " + "refcount", src_ts, dst_ts, policy_dir_names, direction); + policy_entry_destroy(policy, this); + policy = found; + } + else + { /* use the new one, if we have no such policy */ + this->policies->insert_first(this->policies, policy); + policy->used_by = linked_list_create(); + } + + /* cache the assigned IPsec SA */ + assigned_sa = policy_sa_create(this, direction, type, src, dst, src_ts, + dst_ts, sa); + assigned_sa->priority = get_priority(policy, priority); + + /* insert the SA according to its priority */ + enumerator = policy->used_by->create_enumerator(policy->used_by); + while (enumerator->enumerate(enumerator, (void**)¤t_sa)) + { + if (current_sa->priority >= assigned_sa->priority) + { + break; + } + update = FALSE; + } + policy->used_by->insert_before(policy->used_by, enumerator, assigned_sa); + enumerator->destroy(enumerator); + + if (!update) + { /* we don't update the policy if the priority is lower than that of the + * currently installed one */ + this->mutex->unlock(this->mutex); + return SUCCESS; + } + + DBG2(DBG_KNL, "%s policy %R === %R %N", + found ? "updating" : "adding", src_ts, dst_ts, + policy_dir_names, direction); + + if (add_policy_internal(this, policy, assigned_sa, found) != SUCCESS) + { + DBG1(DBG_KNL, "unable to %s policy %R === %R %N", + found ? "update" : "add", src_ts, dst_ts, + policy_dir_names, direction); + return FAILED; + } + return SUCCESS; +} + +METHOD(kernel_ipsec_t, query_policy, status_t, + private_kernel_pfkey_ipsec_t *this, traffic_selector_t *src_ts, + traffic_selector_t *dst_ts, policy_dir_t direction, mark_t mark, + time_t *use_time) +{ + unsigned char request[PFKEY_BUFFER_SIZE]; + struct sadb_msg *msg, *out; + struct sadb_x_policy *pol; + policy_entry_t *policy, *found = NULL; + pfkey_msg_t response; + size_t len; + + if (dir2kernel(direction) == IPSEC_DIR_INVALID) + { /* FWD policies are not supported on all platforms */ + return NOT_FOUND; + } + + DBG2(DBG_KNL, "querying policy %R === %R %N", src_ts, dst_ts, + policy_dir_names, direction); + + /* create a policy */ + policy = create_policy_entry(src_ts, dst_ts, direction); + + /* find a matching policy */ + this->mutex->lock(this->mutex); + if (this->policies->find_first(this->policies, + (linked_list_match_t)policy_entry_equals, + (void**)&found, policy) != SUCCESS) + { + DBG1(DBG_KNL, "querying policy %R === %R %N failed, not found", src_ts, + dst_ts, policy_dir_names, direction); + policy_entry_destroy(policy, this); + this->mutex->unlock(this->mutex); + return NOT_FOUND; + } + policy_entry_destroy(policy, this); + policy = found; + + memset(&request, 0, sizeof(request)); + + msg = (struct sadb_msg*)request; + msg->sadb_msg_version = PF_KEY_V2; + msg->sadb_msg_type = SADB_X_SPDGET; + msg->sadb_msg_satype = 0; + msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg)); + + pol = (struct sadb_x_policy*)PFKEY_EXT_ADD_NEXT(msg); + pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY; + pol->sadb_x_policy_id = policy->index; + pol->sadb_x_policy_len = PFKEY_LEN(sizeof(struct sadb_x_policy)); + pol->sadb_x_policy_dir = dir2kernel(direction); + pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC; + PFKEY_EXT_ADD(msg, pol); + + add_addr_ext(msg, policy->src.net, SADB_EXT_ADDRESS_SRC, policy->src.proto, + policy->src.mask, TRUE); + add_addr_ext(msg, policy->dst.net, SADB_EXT_ADDRESS_DST, policy->dst.proto, + policy->dst.mask, TRUE); + + this->mutex->unlock(this->mutex); + + if (pfkey_send(this, msg, &out, &len) != SUCCESS) + { + DBG1(DBG_KNL, "unable to query policy %R === %R %N", src_ts, dst_ts, + policy_dir_names, direction); + return FAILED; + } + else if (out->sadb_msg_errno) + { + DBG1(DBG_KNL, "unable to query policy %R === %R %N: %s (%d)", src_ts, + dst_ts, policy_dir_names, direction, + strerror(out->sadb_msg_errno), out->sadb_msg_errno); + free(out); + return FAILED; + } + else if (parse_pfkey_message(out, &response) != SUCCESS) + { + DBG1(DBG_KNL, "unable to query policy %R === %R %N: parsing response " + "from kernel failed", src_ts, dst_ts, policy_dir_names, + direction); + free(out); + return FAILED; + } + else if (response.lft_current == NULL) + { + DBG2(DBG_KNL, "unable to query policy %R === %R %N: kernel reports no " + "use time", src_ts, dst_ts, policy_dir_names, direction); + free(out); + return FAILED; + } + + /* we need the monotonic time, but the kernel returns system time. */ + if (response.lft_current->sadb_lifetime_usetime) + { + *use_time = time_monotonic(NULL) - + (time(NULL) - response.lft_current->sadb_lifetime_usetime); + } + else + { + *use_time = 0; + } + free(out); + return SUCCESS; +} + +METHOD(kernel_ipsec_t, del_policy, status_t, + private_kernel_pfkey_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 prio) +{ + unsigned char request[PFKEY_BUFFER_SIZE]; + struct sadb_msg *msg, *out; + struct sadb_x_policy *pol; + policy_entry_t *policy, *found = NULL; + policy_sa_t *mapping, *to_remove = NULL; + enumerator_t *enumerator; + bool first = TRUE, is_installed = TRUE; + u_int32_t priority; + size_t len; + ipsec_sa_t assigned_sa = { + .src = src, + .dst = dst, + .cfg = *sa, + }; + + if (dir2kernel(direction) == IPSEC_DIR_INVALID) + { /* FWD policies are not supported on all platforms */ + return SUCCESS; + } + + DBG2(DBG_KNL, "deleting policy %R === %R %N", src_ts, dst_ts, + policy_dir_names, direction); + + /* create a policy */ + policy = create_policy_entry(src_ts, dst_ts, direction); + + /* find a matching policy */ + this->mutex->lock(this->mutex); + if (this->policies->find_first(this->policies, + (linked_list_match_t)policy_entry_equals, + (void**)&found, policy) != SUCCESS) + { + DBG1(DBG_KNL, "deleting policy %R === %R %N failed, not found", src_ts, + dst_ts, policy_dir_names, direction); + policy_entry_destroy(policy, this); + this->mutex->unlock(this->mutex); + return NOT_FOUND; + } + policy_entry_destroy(policy, this); + policy = found; + + /* remove mapping to SA by reqid and priority, if multiple match, which + * could happen when rekeying due to an address change, remove the oldest */ + priority = get_priority(policy, prio); + enumerator = policy->used_by->create_enumerator(policy->used_by); + while (enumerator->enumerate(enumerator, (void**)&mapping)) + { + if (priority == mapping->priority && + ipsec_sa_equals(mapping->sa, &assigned_sa)) + { + to_remove = mapping; + is_installed = first; + } + else if (priority < mapping->priority) + { + break; + } + first = FALSE; + } + enumerator->destroy(enumerator); + if (!to_remove) + { /* sanity check */ + this->mutex->unlock(this->mutex); + return SUCCESS; + } + policy->used_by->remove(policy->used_by, to_remove, NULL); + mapping = to_remove; + + if (policy->used_by->get_count(policy->used_by) > 0) + { /* policy is used by more SAs, keep in kernel */ + DBG2(DBG_KNL, "policy still used by another CHILD_SA, not removed"); + policy_sa_destroy(mapping, &direction, this); + + if (!is_installed) + { /* no need to update as the policy was not installed for this SA */ + this->mutex->unlock(this->mutex); + return SUCCESS; + } + + DBG2(DBG_KNL, "updating policy %R === %R %N", src_ts, dst_ts, + policy_dir_names, direction); + policy->used_by->get_first(policy->used_by, (void**)&mapping); + if (add_policy_internal(this, policy, mapping, TRUE) != SUCCESS) + { + DBG1(DBG_KNL, "unable to update policy %R === %R %N", + src_ts, dst_ts, policy_dir_names, direction); + return FAILED; + } + return SUCCESS; + } + + memset(&request, 0, sizeof(request)); + + msg = (struct sadb_msg*)request; + msg->sadb_msg_version = PF_KEY_V2; + msg->sadb_msg_type = SADB_X_SPDDELETE; + msg->sadb_msg_satype = 0; + msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg)); + + pol = (struct sadb_x_policy*)PFKEY_EXT_ADD_NEXT(msg); + pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY; + pol->sadb_x_policy_len = PFKEY_LEN(sizeof(struct sadb_x_policy)); + pol->sadb_x_policy_dir = dir2kernel(direction); + pol->sadb_x_policy_type = type2kernel(mapping->type); + PFKEY_EXT_ADD(msg, pol); + + add_addr_ext(msg, policy->src.net, SADB_EXT_ADDRESS_SRC, policy->src.proto, + policy->src.mask, TRUE); + add_addr_ext(msg, policy->dst.net, SADB_EXT_ADDRESS_DST, policy->dst.proto, + policy->dst.mask, TRUE); + + if (policy->route) + { + route_entry_t *route = policy->route; + if (charon->kernel->del_route(charon->kernel, route->dst_net, + route->prefixlen, route->gateway, + route->src_ip, route->if_name) != SUCCESS) + { + DBG1(DBG_KNL, "error uninstalling route installed with " + "policy %R === %R %N", src_ts, dst_ts, + policy_dir_names, direction); + } + remove_exclude_route(this, route); + } + + this->policies->remove(this->policies, found, NULL); + policy_sa_destroy(mapping, &direction, this); + policy_entry_destroy(policy, this); + this->mutex->unlock(this->mutex); + + if (pfkey_send(this, msg, &out, &len) != SUCCESS) + { + DBG1(DBG_KNL, "unable to delete policy %R === %R %N", src_ts, dst_ts, + policy_dir_names, direction); + return FAILED; + } + else if (out->sadb_msg_errno) + { + DBG1(DBG_KNL, "unable to delete policy %R === %R %N: %s (%d)", src_ts, + dst_ts, policy_dir_names, direction, + strerror(out->sadb_msg_errno), out->sadb_msg_errno); + free(out); + return FAILED; + } + free(out); + return SUCCESS; +} + +METHOD(kernel_ipsec_t, flush_policies, status_t, + private_kernel_pfkey_ipsec_t *this) +{ + unsigned char request[PFKEY_BUFFER_SIZE]; + struct sadb_msg *msg, *out; + size_t len; + + memset(&request, 0, sizeof(request)); + + DBG2(DBG_KNL, "flushing all policies from SPD"); + + msg = (struct sadb_msg*)request; + msg->sadb_msg_version = PF_KEY_V2; + msg->sadb_msg_type = SADB_X_SPDFLUSH; + msg->sadb_msg_satype = SADB_SATYPE_UNSPEC; + msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg)); + + if (pfkey_send(this, msg, &out, &len) != SUCCESS) + { + DBG1(DBG_KNL, "unable to flush SPD entries"); + return FAILED; + } + else if (out->sadb_msg_errno) + { + DBG1(DBG_KNL, "unable to flush SPD entries: %s (%d)", + strerror(out->sadb_msg_errno), out->sadb_msg_errno); + free(out); + return FAILED; + } + free(out); + return SUCCESS; +} + +/** + * Register a socket for ACQUIRE/EXPIRE messages + */ +static status_t register_pfkey_socket(private_kernel_pfkey_ipsec_t *this, + u_int8_t satype) +{ + unsigned char request[PFKEY_BUFFER_SIZE]; + struct sadb_msg *msg, *out; + size_t len; + + memset(&request, 0, sizeof(request)); + + msg = (struct sadb_msg*)request; + msg->sadb_msg_version = PF_KEY_V2; + msg->sadb_msg_type = SADB_REGISTER; + msg->sadb_msg_satype = satype; + msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg)); + + if (pfkey_send_socket(this, this->socket_events, msg, &out, &len) != SUCCESS) + { + DBG1(DBG_KNL, "unable to register PF_KEY socket"); + return FAILED; + } + else if (out->sadb_msg_errno) + { + DBG1(DBG_KNL, "unable to register PF_KEY socket: %s (%d)", + strerror(out->sadb_msg_errno), out->sadb_msg_errno); + free(out); + return FAILED; + } + free(out); + return SUCCESS; +} + +METHOD(kernel_ipsec_t, bypass_socket, bool, + private_kernel_pfkey_ipsec_t *this, int fd, int family) +{ + struct sadb_x_policy policy; + u_int sol, ipsec_policy; + + switch (family) + { + case AF_INET: + { + sol = SOL_IP; + ipsec_policy = IP_IPSEC_POLICY; + break; + } + case AF_INET6: + { + sol = SOL_IPV6; + ipsec_policy = IPV6_IPSEC_POLICY; + break; + } + default: + return FALSE; + } + + memset(&policy, 0, sizeof(policy)); + policy.sadb_x_policy_len = sizeof(policy) / sizeof(u_int64_t); + policy.sadb_x_policy_exttype = SADB_X_EXT_POLICY; + policy.sadb_x_policy_type = IPSEC_POLICY_BYPASS; + + policy.sadb_x_policy_dir = IPSEC_DIR_OUTBOUND; + if (setsockopt(fd, sol, ipsec_policy, &policy, sizeof(policy)) < 0) + { + DBG1(DBG_KNL, "unable to set IPSEC_POLICY on socket: %s", + strerror(errno)); + return FALSE; + } + policy.sadb_x_policy_dir = IPSEC_DIR_INBOUND; + if (setsockopt(fd, sol, ipsec_policy, &policy, sizeof(policy)) < 0) + { + DBG1(DBG_KNL, "unable to set IPSEC_POLICY on socket: %s", + strerror(errno)); + return FALSE; + } + return TRUE; +} + +METHOD(kernel_ipsec_t, enable_udp_decap, bool, + private_kernel_pfkey_ipsec_t *this, int fd, int family, u_int16_t port) +{ +#ifndef __APPLE__ + int type = UDP_ENCAP_ESPINUDP; + + if (setsockopt(fd, SOL_UDP, UDP_ENCAP, &type, sizeof(type)) < 0) + { + DBG1(DBG_KNL, "unable to set UDP_ENCAP: %s", strerror(errno)); + return FALSE; + } +#else /* __APPLE__ */ + int intport = port; + + if (sysctlbyname("net.inet.ipsec.esp_port", NULL, NULL, &intport, + sizeof(intport)) != 0) + { + DBG1(DBG_KNL, "could not set net.inet.ipsec.esp_port to %d: %s", + port, strerror(errno)); + return FALSE; + } +#endif /* __APPLE__ */ + + return TRUE; +} + +METHOD(kernel_ipsec_t, destroy, void, + private_kernel_pfkey_ipsec_t *this) +{ + if (this->socket > 0) + { + close(this->socket); + } + if (this->socket_events > 0) + { + lib->watcher->remove(lib->watcher, this->socket_events); + close(this->socket_events); + } + this->policies->invoke_function(this->policies, + (linked_list_invoke_t)policy_entry_destroy, + this); + this->policies->destroy(this->policies); + this->excludes->destroy(this->excludes); + this->sas->destroy(this->sas); + this->mutex->destroy(this->mutex); + this->mutex_pfkey->destroy(this->mutex_pfkey); + free(this); +} + +/* + * Described in header. + */ +kernel_pfkey_ipsec_t *kernel_pfkey_ipsec_create() +{ + private_kernel_pfkey_ipsec_t *this; + bool register_for_events = TRUE; + int rcv_buffer; + + INIT(this, + .public = { + .interface = { + .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, + }, + }, + .policies = linked_list_create(), + .excludes = linked_list_create(), + .sas = hashtable_create((hashtable_hash_t)ipsec_sa_hash, + (hashtable_equals_t)ipsec_sa_equals, 32), + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + .mutex_pfkey = mutex_create(MUTEX_TYPE_DEFAULT), + .install_routes = lib->settings->get_bool(lib->settings, + "%s.install_routes", TRUE, + lib->ns), + ); + + if (streq(lib->ns, "starter")) + { /* starter has no threads, so we do not register for kernel events */ + register_for_events = FALSE; + } + + /* create a PF_KEY socket to communicate with the kernel */ + this->socket = socket(PF_KEY, SOCK_RAW, PF_KEY_V2); + if (this->socket <= 0) + { + DBG1(DBG_KNL, "unable to create PF_KEY socket"); + destroy(this); + return NULL; + } + + if (register_for_events) + { + /* create a PF_KEY socket for ACQUIRE & EXPIRE */ + this->socket_events = socket(PF_KEY, SOCK_RAW, PF_KEY_V2); + if (this->socket_events <= 0) + { + DBG1(DBG_KNL, "unable to create PF_KEY event socket"); + destroy(this); + return NULL; + } + + rcv_buffer = lib->settings->get_int(lib->settings, + "%s.plugins.kernel-pfkey.events_buffer_size", 0, lib->ns); + if (rcv_buffer > 0) + { + if (setsockopt(this->socket_events, SOL_SOCKET, SO_RCVBUF, + &rcv_buffer, sizeof(rcv_buffer)) == -1) + { + DBG1(DBG_KNL, "unable to set receive buffer size on PF_KEY " + "event socket: %s", strerror(errno)); + } + } + + /* register the event socket */ + if (register_pfkey_socket(this, SADB_SATYPE_ESP) != SUCCESS || + register_pfkey_socket(this, SADB_SATYPE_AH) != SUCCESS) + { + DBG1(DBG_KNL, "unable to register PF_KEY event socket"); + destroy(this); + return NULL; + } + + lib->watcher->add(lib->watcher, this->socket_events, WATCHER_READ, + (watcher_cb_t)receive_events, this); + } + + return &this->public; +} diff --git a/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.h b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.h new file mode 100644 index 000000000..649f93733 --- /dev/null +++ b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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_pfkey_ipsec_i kernel_pfkey_ipsec + * @{ @ingroup kernel_pfkey + */ + +#ifndef KERNEL_PFKEY_IPSEC_H_ +#define KERNEL_PFKEY_IPSEC_H_ + +#include <kernel/kernel_ipsec.h> + +typedef struct kernel_pfkey_ipsec_t kernel_pfkey_ipsec_t; + +/** + * Implementation of the kernel ipsec interface using PF_KEY. + */ +struct kernel_pfkey_ipsec_t { + + /** + * Implements kernel_ipsec_t interface + */ + kernel_ipsec_t interface; +}; + +/** + * Create a PF_KEY kernel ipsec interface instance. + * + * @return kernel_pfkey_ipsec_t instance + */ +kernel_pfkey_ipsec_t *kernel_pfkey_ipsec_create(); + +#endif /** KERNEL_PFKEY_IPSEC_H_ @}*/ diff --git a/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_plugin.c b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_plugin.c new file mode 100644 index 000000000..d49fe2422 --- /dev/null +++ b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_plugin.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2008 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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_pfkey_plugin.h" + +#include "kernel_pfkey_ipsec.h" + +typedef struct private_kernel_pfkey_plugin_t private_kernel_pfkey_plugin_t; + +/** + * private data of kernel PF_KEY plugin + */ +struct private_kernel_pfkey_plugin_t { + /** + * implements plugin interface + */ + kernel_pfkey_plugin_t public; +}; + +METHOD(plugin_t, get_name, char*, + private_kernel_pfkey_plugin_t *this) +{ + return "kernel-pfkey"; +} + +METHOD(plugin_t, get_features, int, + private_kernel_pfkey_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_CALLBACK(kernel_ipsec_register, kernel_pfkey_ipsec_create), + PLUGIN_PROVIDE(CUSTOM, "kernel-ipsec"), + }; + *features = f; + return countof(f); +} + +METHOD(plugin_t, destroy, void, + private_kernel_pfkey_plugin_t *this) +{ + free(this); +} + +/* + * see header file + */ +plugin_t *kernel_pfkey_plugin_create() +{ + private_kernel_pfkey_plugin_t *this; + + if (!lib->caps->check(lib->caps, CAP_NET_ADMIN)) + { /* required to open PF_KEY sockets */ + DBG1(DBG_KNL, "kernel-pfkey plugin requires CAP_NET_ADMIN capability"); + return NULL; + } + + 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_pfkey/kernel_pfkey_plugin.h b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_plugin.h new file mode 100644 index 000000000..ecccc6303 --- /dev/null +++ b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_plugin.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2008 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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_pfkey kernel_pfkey + * @ingroup cplugins + * + * @defgroup kernel_pfkey_plugin kernel_pfkey_plugin + * @{ @ingroup kernel_pfkey + */ + +#ifndef KERNEL_PFKEY_PLUGIN_H_ +#define KERNEL_PFKEY_PLUGIN_H_ + +#include <plugins/plugin.h> + +typedef struct kernel_pfkey_plugin_t kernel_pfkey_plugin_t; + +/** + * PF_KEY kernel interface plugin + */ +struct kernel_pfkey_plugin_t { + + /** + * implements plugin interface + */ + plugin_t plugin; +}; + +#endif /** KERNEL_PFKEY_PLUGIN_H_ @}*/ diff --git a/src/libcharon/plugins/kernel_pfroute/Makefile.am b/src/libcharon/plugins/kernel_pfroute/Makefile.am new file mode 100644 index 000000000..51047e38a --- /dev/null +++ b/src/libcharon/plugins/kernel_pfroute/Makefile.am @@ -0,0 +1,19 @@ +AM_CPPFLAGS = \ + -I${linux_headers} \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libcharon + +AM_CFLAGS = \ + $(PLUGIN_CFLAGS) + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-kernel-pfroute.la +else +plugin_LTLIBRARIES = libstrongswan-kernel-pfroute.la +endif + +libstrongswan_kernel_pfroute_la_SOURCES = \ + kernel_pfroute_plugin.h kernel_pfroute_plugin.c \ + kernel_pfroute_net.h kernel_pfroute_net.c + +libstrongswan_kernel_pfroute_la_LDFLAGS = -module -avoid-version diff --git a/src/libcharon/plugins/kernel_pfroute/Makefile.in b/src/libcharon/plugins/kernel_pfroute/Makefile.in new file mode 100644 index 000000000..77d83cbca --- /dev/null +++ b/src/libcharon/plugins/kernel_pfroute/Makefile.in @@ -0,0 +1,782 @@ +# 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@ +subdir = src/libcharon/plugins/kernel_pfroute +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_pfroute_la_LIBADD = +am_libstrongswan_kernel_pfroute_la_OBJECTS = kernel_pfroute_plugin.lo \ + kernel_pfroute_net.lo +libstrongswan_kernel_pfroute_la_OBJECTS = \ + $(am_libstrongswan_kernel_pfroute_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_pfroute_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) \ + $(libstrongswan_kernel_pfroute_la_LDFLAGS) $(LDFLAGS) -o $@ +@MONOLITHIC_FALSE@am_libstrongswan_kernel_pfroute_la_rpath = -rpath \ +@MONOLITHIC_FALSE@ $(plugindir) +@MONOLITHIC_TRUE@am_libstrongswan_kernel_pfroute_la_rpath = +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_pfroute_la_SOURCES) +DIST_SOURCES = $(libstrongswan_kernel_pfroute_la_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@ +EASY_INSTALL = @EASY_INSTALL@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GEM = @GEM@ +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@ +PYTHONEGGINSTALLDIR = @PYTHONEGGINSTALLDIR@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +PY_TEST = @PY_TEST@ +RANLIB = @RANLIB@ +RTLIB = @RTLIB@ +RUBY = @RUBY@ +RUBYGEMDIR = @RUBYGEMDIR@ +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@ +json_CFLAGS = @json_CFLAGS@ +json_LIBS = @json_LIBS@ +libdir = @libdir@ +libexecdir = @libexecdir@ +libiptc_CFLAGS = @libiptc_CFLAGS@ +libiptc_LIBS = @libiptc_LIBS@ +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@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ +systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ +systemd_daemon_LIBS = @systemd_daemon_LIBS@ +systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ +systemd_journal_LIBS = @systemd_journal_LIBS@ +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${linux_headers} \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libcharon + +AM_CFLAGS = \ + $(PLUGIN_CFLAGS) + +@MONOLITHIC_TRUE@noinst_LTLIBRARIES = libstrongswan-kernel-pfroute.la +@MONOLITHIC_FALSE@plugin_LTLIBRARIES = libstrongswan-kernel-pfroute.la +libstrongswan_kernel_pfroute_la_SOURCES = \ + kernel_pfroute_plugin.h kernel_pfroute_plugin.c \ + kernel_pfroute_net.h kernel_pfroute_net.c + +libstrongswan_kernel_pfroute_la_LDFLAGS = -module -avoid-version +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_pfroute/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/libcharon/plugins/kernel_pfroute/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-pfroute.la: $(libstrongswan_kernel_pfroute_la_OBJECTS) $(libstrongswan_kernel_pfroute_la_DEPENDENCIES) $(EXTRA_libstrongswan_kernel_pfroute_la_DEPENDENCIES) + $(AM_V_CCLD)$(libstrongswan_kernel_pfroute_la_LINK) $(am_libstrongswan_kernel_pfroute_la_rpath) $(libstrongswan_kernel_pfroute_la_OBJECTS) $(libstrongswan_kernel_pfroute_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel_pfroute_net.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel_pfroute_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) +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-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-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_pfroute/kernel_pfroute_net.c b/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.c new file mode 100644 index 000000000..4eebdfdad --- /dev/null +++ b/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -0,0 +1,1902 @@ +/* + * Copyright (C) 2009-2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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 <sys/types.h> +#include <sys/socket.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <ifaddrs.h> +#include <net/route.h> +#include <unistd.h> +#include <errno.h> + +#include "kernel_pfroute_net.h" + +#include <daemon.h> +#include <utils/debug.h> +#include <networking/host.h> +#include <networking/tun_device.h> +#include <threading/thread.h> +#include <threading/mutex.h> +#include <threading/condvar.h> +#include <threading/rwlock.h> +#include <threading/spinlock.h> +#include <collections/hashtable.h> +#include <collections/linked_list.h> +#include <processing/jobs/callback_job.h> + +#ifndef HAVE_STRUCT_SOCKADDR_SA_LEN +#error Cannot compile this plugin on systems where 'struct sockaddr' has no sa_len member. +#endif + +/** properly align sockaddrs */ +#ifdef __APPLE__ +/* Apple always uses 4 bytes */ +#define SA_ALIGN 4 +#else +/* while on other platforms like FreeBSD it depends on the architecture */ +#define SA_ALIGN sizeof(long) +#endif +#define SA_LEN(len) ((len) > 0 ? (((len)+SA_ALIGN-1) & ~(SA_ALIGN-1)) : SA_ALIGN) + +/** delay before firing roam events (ms) */ +#define ROAM_DELAY 100 + +/** delay before reinstalling routes (ms) */ +#define ROUTE_DELAY 100 + +typedef struct addr_entry_t addr_entry_t; + +/** + * IP address in an inface_entry_t + */ +struct addr_entry_t { + + /** The ip address */ + host_t *ip; + + /** virtual IP managed by us */ + bool virtual; +}; + +/** + * destroy a addr_entry_t object + */ +static void addr_entry_destroy(addr_entry_t *this) +{ + this->ip->destroy(this->ip); + free(this); +} + +typedef struct iface_entry_t iface_entry_t; + +/** + * A network interface on this system, containing addr_entry_t's + */ +struct iface_entry_t { + + /** interface index */ + int ifindex; + + /** name of the interface */ + char ifname[IFNAMSIZ]; + + /** interface flags, as in netdevice(7) SIOCGIFFLAGS */ + u_int flags; + + /** list of addresses as host_t */ + linked_list_t *addrs; + + /** TRUE if usable by config */ + bool usable; +}; + +/** + * destroy an interface entry + */ +static void iface_entry_destroy(iface_entry_t *this) +{ + this->addrs->destroy_function(this->addrs, (void*)addr_entry_destroy); + free(this); +} + +/** + * check if an interface is up + */ +static inline bool iface_entry_up(iface_entry_t *iface) +{ + return (iface->flags & IFF_UP) == IFF_UP; +} + +/** + * check if an interface is up and usable + */ +static inline bool iface_entry_up_and_usable(iface_entry_t *iface) +{ + return iface->usable && iface_entry_up(iface); +} + +typedef struct addr_map_entry_t addr_map_entry_t; + +/** + * Entry that maps an IP address to an interface entry + */ +struct addr_map_entry_t { + /** The IP address */ + host_t *ip; + + /** The address entry for this IP address */ + addr_entry_t *addr; + + /** The interface this address is installed on */ + iface_entry_t *iface; +}; + +/** + * Hash a addr_map_entry_t object, all entries with the same IP address + * are stored in the same bucket + */ +static u_int addr_map_entry_hash(addr_map_entry_t *this) +{ + return chunk_hash(this->ip->get_address(this->ip)); +} + +/** + * Compare two addr_map_entry_t objects, two entries are equal if they are + * installed on the same interface + */ +static bool addr_map_entry_equals(addr_map_entry_t *a, addr_map_entry_t *b) +{ + return a->iface->ifindex == b->iface->ifindex && + a->ip->ip_equals(a->ip, b->ip); +} + +/** + * Used with get_match this finds an address entry if it is installed on + * an up and usable interface + */ +static bool addr_map_entry_match_up_and_usable(addr_map_entry_t *a, + addr_map_entry_t *b) +{ + return !b->addr->virtual && iface_entry_up_and_usable(b->iface) && + a->ip->ip_equals(a->ip, b->ip); +} + +/** + * Used with get_match this finds an address entry if it is installed as virtual + * IP address + */ +static bool addr_map_entry_match_virtual(addr_map_entry_t *a, addr_map_entry_t *b) +{ + return b->addr->virtual && a->ip->ip_equals(a->ip, b->ip); +} + +/** + * Used with get_match this finds an address entry if it is installed on + * any active local interface + */ +static bool addr_map_entry_match_up(addr_map_entry_t *a, addr_map_entry_t *b) +{ + return !b->addr->virtual && iface_entry_up(b->iface) && + a->ip->ip_equals(a->ip, b->ip); +} + +typedef struct route_entry_t route_entry_t; + +/** + * Installed routing entry + */ +struct route_entry_t { + /** Name of the interface the route is bound to */ + char *if_name; + + /** Gateway for this route */ + host_t *gateway; + + /** Destination net */ + chunk_t dst_net; + + /** Destination net prefixlen */ + u_int8_t prefixlen; +}; + +/** + * Clone a route_entry_t object. + */ +static route_entry_t *route_entry_clone(route_entry_t *this) +{ + route_entry_t *route; + + INIT(route, + .if_name = strdup(this->if_name), + .gateway = this->gateway ? this->gateway->clone(this->gateway) : NULL, + .dst_net = chunk_clone(this->dst_net), + .prefixlen = this->prefixlen, + ); + return route; +} + +/** + * Destroy a route_entry_t object + */ +static void route_entry_destroy(route_entry_t *this) +{ + free(this->if_name); + DESTROY_IF(this->gateway); + chunk_free(&this->dst_net); + free(this); +} + +/** + * Hash a route_entry_t object + */ +static u_int route_entry_hash(route_entry_t *this) +{ + return chunk_hash_inc(chunk_from_thing(this->prefixlen), + chunk_hash(this->dst_net)); +} + +/** + * Compare two route_entry_t objects + */ +static bool route_entry_equals(route_entry_t *a, route_entry_t *b) +{ + if (a->if_name && b->if_name && streq(a->if_name, b->if_name) && + chunk_equals(a->dst_net, b->dst_net) && a->prefixlen == b->prefixlen) + { + return (!a->gateway && !b->gateway) || (a->gateway && b->gateway && + a->gateway->ip_equals(a->gateway, b->gateway)); + } + return FALSE; +} + +typedef struct net_change_t net_change_t; + +/** + * Queued network changes + */ +struct net_change_t { + /** Name of the interface that got activated (or an IP appeared on) */ + char *if_name; +}; + +/** + * Destroy a net_change_t object + */ +static void net_change_destroy(net_change_t *this) +{ + free(this->if_name); + free(this); +} + +/** + * Hash a net_change_t object + */ +static u_int net_change_hash(net_change_t *this) +{ + return chunk_hash(chunk_create(this->if_name, strlen(this->if_name))); +} + +/** + * Compare two net_change_t objects + */ +static bool net_change_equals(net_change_t *a, net_change_t *b) +{ + return streq(a->if_name, b->if_name); +} + +typedef struct private_kernel_pfroute_net_t private_kernel_pfroute_net_t; + +/** + * Private variables and functions of kernel_pfroute class. + */ +struct private_kernel_pfroute_net_t +{ + /** + * Public part of the kernel_pfroute_t object. + */ + kernel_pfroute_net_t public; + + /** + * lock to access lists and maps + */ + rwlock_t *lock; + + /** + * Cached list of interfaces and their addresses (iface_entry_t) + */ + linked_list_t *ifaces; + + /** + * Map for IP addresses to iface_entry_t objects (addr_map_entry_t) + */ + hashtable_t *addrs; + + /** + * List of tun devices we installed for virtual IPs + */ + linked_list_t *tuns; + + /** + * mutex to communicate exclusively with PF_KEY + */ + mutex_t *mutex; + + /** + * condvar to signal if PF_KEY query got a response + */ + condvar_t *condvar; + + /** + * installed routes + */ + hashtable_t *routes; + + /** + * mutex for routes + */ + mutex_t *routes_lock; + + /** + * interface changes which may trigger route reinstallation + */ + hashtable_t *net_changes; + + /** + * mutex for route reinstallation triggers + */ + mutex_t *net_changes_lock; + + /** + * time of last route reinstallation + */ + timeval_t last_route_reinstall; + + /** + * pid to send PF_ROUTE messages with + */ + pid_t pid; + + /** + * PF_ROUTE socket to communicate with the kernel + */ + int socket; + + /** + * sequence number for messages sent to the kernel + */ + int seq; + + /** + * Sequence number a query is waiting for + */ + int waiting_seq; + + /** + * Allocated reply message from kernel + */ + struct rt_msghdr *reply; + + /** + * earliest time of the next roam event + */ + timeval_t next_roam; + + /** + * roam event due to address change + */ + bool roam_address; + + /** + * lock to check and update roam event time + */ + spinlock_t *roam_lock; + + /** + * Time in ms to wait for IP addresses to appear/disappear + */ + int vip_wait; + + /** + * whether to actually install virtual IPs + */ + bool install_virtual_ip; +}; + + +/** + * Forward declaration + */ +static status_t manage_route(private_kernel_pfroute_net_t *this, int op, + chunk_t dst_net, u_int8_t prefixlen, + host_t *gateway, char *if_name); + +/** + * Clear the queued network changes. + */ +static void net_changes_clear(private_kernel_pfroute_net_t *this) +{ + enumerator_t *enumerator; + net_change_t *change; + + enumerator = this->net_changes->create_enumerator(this->net_changes); + while (enumerator->enumerate(enumerator, NULL, (void**)&change)) + { + this->net_changes->remove_at(this->net_changes, enumerator); + net_change_destroy(change); + } + enumerator->destroy(enumerator); +} + +/** + * Act upon queued network changes. + */ +static job_requeue_t reinstall_routes(private_kernel_pfroute_net_t *this) +{ + enumerator_t *enumerator; + route_entry_t *route; + + this->net_changes_lock->lock(this->net_changes_lock); + this->routes_lock->lock(this->routes_lock); + + enumerator = this->routes->create_enumerator(this->routes); + while (enumerator->enumerate(enumerator, NULL, (void**)&route)) + { + net_change_t *change, lookup = { + .if_name = route->if_name, + }; + /* check if a change for the outgoing interface is queued */ + change = this->net_changes->get(this->net_changes, &lookup); + if (change) + { + manage_route(this, RTM_ADD, route->dst_net, route->prefixlen, + route->gateway, route->if_name); + } + } + enumerator->destroy(enumerator); + this->routes_lock->unlock(this->routes_lock); + + net_changes_clear(this); + this->net_changes_lock->unlock(this->net_changes_lock); + return JOB_REQUEUE_NONE; +} + +/** + * Queue route reinstallation caused by network changes for a given interface. + * + * The route reinstallation is delayed for a while and only done once for + * several calls during this delay, in order to avoid doing it too often. + * The interface name is freed. + */ +static void queue_route_reinstall(private_kernel_pfroute_net_t *this, + char *if_name) +{ + net_change_t *update, *found; + timeval_t now; + job_t *job; + + INIT(update, + .if_name = if_name + ); + + this->net_changes_lock->lock(this->net_changes_lock); + found = this->net_changes->put(this->net_changes, update, update); + if (found) + { + net_change_destroy(found); + } + time_monotonic(&now); + if (timercmp(&now, &this->last_route_reinstall, >)) + { + timeval_add_ms(&now, ROUTE_DELAY); + this->last_route_reinstall = now; + + job = (job_t*)callback_job_create((callback_job_cb_t)reinstall_routes, + this, NULL, NULL); + lib->scheduler->schedule_job_ms(lib->scheduler, job, ROUTE_DELAY); + } + this->net_changes_lock->unlock(this->net_changes_lock); +} + +/** + * Add an address map entry + */ +static void addr_map_entry_add(private_kernel_pfroute_net_t *this, + addr_entry_t *addr, iface_entry_t *iface) +{ + addr_map_entry_t *entry; + + INIT(entry, + .ip = addr->ip, + .addr = addr, + .iface = iface, + ); + entry = this->addrs->put(this->addrs, entry, entry); + free(entry); +} + +/** + * Remove an address map entry (the argument order is a bit strange because + * it is also used with linked_list_t.invoke_function) + */ +static void addr_map_entry_remove(addr_entry_t *addr, iface_entry_t *iface, + private_kernel_pfroute_net_t *this) +{ + addr_map_entry_t *entry, lookup = { + .ip = addr->ip, + .addr = addr, + .iface = iface, + }; + + entry = this->addrs->remove(this->addrs, &lookup); + free(entry); +} + +/** + * callback function that raises the delayed roam event + */ +static job_requeue_t roam_event(private_kernel_pfroute_net_t *this) +{ + bool address; + + this->roam_lock->lock(this->roam_lock); + address = this->roam_address; + this->roam_address = FALSE; + this->roam_lock->unlock(this->roam_lock); + charon->kernel->roam(charon->kernel, address); + return JOB_REQUEUE_NONE; +} + +/** + * fire a roaming event. we delay it for a bit and fire only one event + * for multiple calls. otherwise we would create too many events. + */ +static void fire_roam_event(private_kernel_pfroute_net_t *this, bool address) +{ + timeval_t now; + job_t *job; + + time_monotonic(&now); + this->roam_lock->lock(this->roam_lock); + this->roam_address |= address; + if (!timercmp(&now, &this->next_roam, >)) + { + this->roam_lock->unlock(this->roam_lock); + return; + } + timeval_add_ms(&now, ROAM_DELAY); + this->next_roam = now; + this->roam_lock->unlock(this->roam_lock); + + job = (job_t*)callback_job_create((callback_job_cb_t)roam_event, + this, NULL, NULL); + lib->scheduler->schedule_job_ms(lib->scheduler, job, ROAM_DELAY); +} + +/** + * Data for enumerator over rtmsg sockaddrs + */ +typedef struct { + /** implements enumerator */ + enumerator_t public; + /** copy of attribute bitfield */ + int types; + /** bytes remaining in buffer */ + int remaining; + /** next sockaddr to enumerate */ + struct sockaddr *addr; +} rt_enumerator_t; + +METHOD(enumerator_t, rt_enumerate, bool, + rt_enumerator_t *this, int *xtype, struct sockaddr **addr) +{ + int i, type; + + if (this->remaining < sizeof(this->addr->sa_len) || + this->remaining < this->addr->sa_len) + { + return FALSE; + } + for (i = 0; i < RTAX_MAX; i++) + { + type = (1 << i); + if (this->types & type) + { + this->types &= ~type; + *addr = this->addr; + *xtype = i; + this->remaining -= SA_LEN(this->addr->sa_len); + this->addr = (struct sockaddr*)((char*)this->addr + + SA_LEN(this->addr->sa_len)); + return TRUE; + } + } + return FALSE; +} + +/** + * Create an enumerator over sockaddrs in rt/if messages + */ +static enumerator_t *create_rt_enumerator(int types, int remaining, + struct sockaddr *addr) +{ + rt_enumerator_t *this; + + INIT(this, + .public = { + .enumerate = (void*)_rt_enumerate, + .destroy = (void*)free, + }, + .types = types, + .remaining = remaining, + .addr = addr, + ); + return &this->public; +} + +/** + * Create a safe enumerator over sockaddrs in rt_msghdr + */ +static enumerator_t *create_rtmsg_enumerator(struct rt_msghdr *hdr) +{ + return create_rt_enumerator(hdr->rtm_addrs, hdr->rtm_msglen - sizeof(*hdr), + (struct sockaddr *)(hdr + 1)); +} + +/** + * Create a safe enumerator over sockaddrs in ifa_msghdr + */ +static enumerator_t *create_ifamsg_enumerator(struct ifa_msghdr *hdr) +{ + return create_rt_enumerator(hdr->ifam_addrs, hdr->ifam_msglen - sizeof(*hdr), + (struct sockaddr *)(hdr + 1)); +} + +/** + * Process an RTM_*ADDR message from the kernel + */ +static void process_addr(private_kernel_pfroute_net_t *this, + struct ifa_msghdr *ifa) +{ + struct sockaddr *sockaddr; + host_t *host = NULL; + enumerator_t *ifaces, *addrs; + iface_entry_t *iface; + addr_entry_t *addr; + bool found = FALSE, changed = FALSE, roam = FALSE; + enumerator_t *enumerator; + char *ifname = NULL; + int type; + + enumerator = create_ifamsg_enumerator(ifa); + while (enumerator->enumerate(enumerator, &type, &sockaddr)) + { + if (type == RTAX_IFA) + { + host = host_create_from_sockaddr(sockaddr); + break; + } + } + enumerator->destroy(enumerator); + + if (!host || host->is_anyaddr(host)) + { + DESTROY_IF(host); + return; + } + + this->lock->write_lock(this->lock); + ifaces = this->ifaces->create_enumerator(this->ifaces); + while (ifaces->enumerate(ifaces, &iface)) + { + if (iface->ifindex == ifa->ifam_index) + { + addrs = iface->addrs->create_enumerator(iface->addrs); + while (addrs->enumerate(addrs, &addr)) + { + if (host->ip_equals(host, addr->ip)) + { + found = TRUE; + if (ifa->ifam_type == RTM_DELADDR) + { + iface->addrs->remove_at(iface->addrs, addrs); + if (!addr->virtual && iface->usable) + { + changed = TRUE; + DBG1(DBG_KNL, "%H disappeared from %s", + host, iface->ifname); + } + addr_map_entry_remove(addr, iface, this); + addr_entry_destroy(addr); + } + } + } + addrs->destroy(addrs); + + if (!found && ifa->ifam_type == RTM_NEWADDR) + { + INIT(addr, + .ip = host->clone(host), + ); + changed = TRUE; + ifname = strdup(iface->ifname); + iface->addrs->insert_last(iface->addrs, addr); + addr_map_entry_add(this, addr, iface); + if (iface->usable) + { + DBG1(DBG_KNL, "%H appeared on %s", host, iface->ifname); + } + } + + if (changed && iface_entry_up_and_usable(iface)) + { + roam = TRUE; + } + break; + } + } + ifaces->destroy(ifaces); + this->lock->unlock(this->lock); + host->destroy(host); + + if (roam && ifname) + { + queue_route_reinstall(this, ifname); + } + else + { + free(ifname); + } + + if (roam) + { + fire_roam_event(this, TRUE); + } +} + +/** + * Re-initialize address list of an interface if it changes state + */ +static void repopulate_iface(private_kernel_pfroute_net_t *this, + iface_entry_t *iface) +{ + struct ifaddrs *ifap, *ifa; + addr_entry_t *addr; + + while (iface->addrs->remove_last(iface->addrs, (void**)&addr) == SUCCESS) + { + addr_map_entry_remove(addr, iface, this); + addr_entry_destroy(addr); + } + + if (getifaddrs(&ifap) == 0) + { + for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr && streq(ifa->ifa_name, iface->ifname)) + { + switch (ifa->ifa_addr->sa_family) + { + case AF_INET: + case AF_INET6: + INIT(addr, + .ip = host_create_from_sockaddr(ifa->ifa_addr), + ); + iface->addrs->insert_last(iface->addrs, addr); + addr_map_entry_add(this, addr, iface); + break; + default: + break; + } + } + } + freeifaddrs(ifap); + } +} + +/** + * Process an RTM_IFINFO message from the kernel + */ +static void process_link(private_kernel_pfroute_net_t *this, + struct if_msghdr *msg) +{ + enumerator_t *enumerator; + iface_entry_t *iface; + bool roam = FALSE, found = FALSE, update_routes = FALSE; + + this->lock->write_lock(this->lock); + enumerator = this->ifaces->create_enumerator(this->ifaces); + while (enumerator->enumerate(enumerator, &iface)) + { + if (iface->ifindex == msg->ifm_index) + { + if (iface->usable) + { + if (!(iface->flags & IFF_UP) && (msg->ifm_flags & IFF_UP)) + { + roam = update_routes = TRUE; + DBG1(DBG_KNL, "interface %s activated", iface->ifname); + } + else if ((iface->flags & IFF_UP) && !(msg->ifm_flags & IFF_UP)) + { + roam = TRUE; + DBG1(DBG_KNL, "interface %s deactivated", iface->ifname); + } + } +#ifdef __APPLE__ + /* There seems to be a race condition on 10.10, where we get + * the RTM_IFINFO, but getifaddrs() does not return the virtual + * IP installed on a tun device, but we also don't get a + * RTM_NEWADDR. We therefore could miss the new address, letting + * virtual IP installation fail. Delaying getifaddrs() helps, + * but is obviously not a clean fix. */ + usleep(50000); +#endif + iface->flags = msg->ifm_flags; + repopulate_iface(this, iface); + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + + if (!found) + { + INIT(iface, + .ifindex = msg->ifm_index, + .flags = msg->ifm_flags, + .addrs = linked_list_create(), + ); + if (if_indextoname(iface->ifindex, iface->ifname)) + { + DBG1(DBG_KNL, "interface %s appeared", iface->ifname); + iface->usable = charon->kernel->is_interface_usable(charon->kernel, + iface->ifname); + repopulate_iface(this, iface); + this->ifaces->insert_last(this->ifaces, iface); + if (iface->usable) + { + roam = update_routes = TRUE; + } + } + else + { + free(iface); + } + } + this->lock->unlock(this->lock); + + if (update_routes) + { + queue_route_reinstall(this, strdup(iface->ifname)); + } + + if (roam) + { + fire_roam_event(this, TRUE); + } +} + +#ifdef HAVE_RTM_IFANNOUNCE + +/** + * Process an RTM_IFANNOUNCE message from the kernel + */ +static void process_announce(private_kernel_pfroute_net_t *this, + struct if_announcemsghdr *msg) +{ + enumerator_t *enumerator; + iface_entry_t *iface; + + if (msg->ifan_what != IFAN_DEPARTURE) + { + /* we handle new interfaces in process_link() */ + return; + } + + this->lock->write_lock(this->lock); + enumerator = this->ifaces->create_enumerator(this->ifaces); + while (enumerator->enumerate(enumerator, &iface)) + { + if (iface->ifindex == msg->ifan_index) + { + DBG1(DBG_KNL, "interface %s disappeared", iface->ifname); + this->ifaces->remove_at(this->ifaces, enumerator); + iface_entry_destroy(iface); + break; + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); +} + +#endif /* HAVE_RTM_IFANNOUNCE */ + +/** + * Process an RTM_*ROUTE message from the kernel + */ +static void process_route(private_kernel_pfroute_net_t *this, + struct rt_msghdr *msg) +{ + +} + +/** + * Receives PF_ROUTE messages from kernel + */ +static bool receive_events(private_kernel_pfroute_net_t *this, int fd, + watcher_event_t event) +{ + struct { + union { + struct rt_msghdr rtm; + struct if_msghdr ifm; + struct ifa_msghdr ifam; +#ifdef HAVE_RTM_IFANNOUNCE + struct if_announcemsghdr ifanm; +#endif + }; + char buf[sizeof(struct sockaddr_storage) * RTAX_MAX]; + } msg; + int len, hdrlen; + + len = recv(this->socket, &msg, sizeof(msg), MSG_DONTWAIT); + if (len < 0) + { + switch (errno) + { + case EINTR: + case EAGAIN: + return TRUE; + default: + DBG1(DBG_KNL, "unable to receive from PF_ROUTE event socket"); + sleep(1); + return TRUE; + } + } + + if (len < offsetof(struct rt_msghdr, rtm_flags) || len < msg.rtm.rtm_msglen) + { + DBG1(DBG_KNL, "received invalid PF_ROUTE message"); + return TRUE; + } + if (msg.rtm.rtm_version != RTM_VERSION) + { + DBG1(DBG_KNL, "received PF_ROUTE message with unsupported version: %d", + msg.rtm.rtm_version); + return TRUE; + } + switch (msg.rtm.rtm_type) + { + case RTM_NEWADDR: + case RTM_DELADDR: + hdrlen = sizeof(msg.ifam); + break; + case RTM_IFINFO: + hdrlen = sizeof(msg.ifm); + break; +#ifdef HAVE_RTM_IFANNOUNCE + case RTM_IFANNOUNCE: + hdrlen = sizeof(msg.ifanm); + break; +#endif /* HAVE_RTM_IFANNOUNCE */ + case RTM_ADD: + case RTM_DELETE: + case RTM_GET: + hdrlen = sizeof(msg.rtm); + break; + default: + return TRUE; + } + if (msg.rtm.rtm_msglen < hdrlen) + { + DBG1(DBG_KNL, "ignoring short PF_ROUTE message"); + return TRUE; + } + switch (msg.rtm.rtm_type) + { + case RTM_NEWADDR: + case RTM_DELADDR: + process_addr(this, &msg.ifam); + break; + case RTM_IFINFO: + process_link(this, &msg.ifm); + break; +#ifdef HAVE_RTM_IFANNOUNCE + case RTM_IFANNOUNCE: + process_announce(this, &msg.ifanm); + break; +#endif /* HAVE_RTM_IFANNOUNCE */ + case RTM_ADD: + case RTM_DELETE: + process_route(this, &msg.rtm); + break; + default: + break; + } + + this->mutex->lock(this->mutex); + if (msg.rtm.rtm_pid == this->pid && msg.rtm.rtm_seq == this->waiting_seq) + { + /* seems like the message someone is waiting for, deliver */ + this->reply = realloc(this->reply, msg.rtm.rtm_msglen); + memcpy(this->reply, &msg, msg.rtm.rtm_msglen); + } + /* signal on any event, add_ip()/del_ip() might wait for it */ + this->condvar->broadcast(this->condvar); + this->mutex->unlock(this->mutex); + + return TRUE; +} + + +/** enumerator over addresses */ +typedef struct { + private_kernel_pfroute_net_t* this; + /** which addresses to enumerate */ + kernel_address_type_t which; +} address_enumerator_t; + +/** + * cleanup function for address enumerator + */ +static void address_enumerator_destroy(address_enumerator_t *data) +{ + data->this->lock->unlock(data->this->lock); + free(data); +} + +/** + * filter for addresses + */ +static bool filter_addresses(address_enumerator_t *data, + addr_entry_t** in, host_t** out) +{ + host_t *ip; + if (!(data->which & ADDR_TYPE_VIRTUAL) && (*in)->virtual) + { /* skip virtual interfaces added by us */ + return FALSE; + } + if (!(data->which & ADDR_TYPE_REGULAR) && !(*in)->virtual) + { /* address is regular, but not requested */ + return FALSE; + } + ip = (*in)->ip; + if (ip->get_family(ip) == AF_INET6) + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ip->get_sockaddr(ip); + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) + { /* skip addresses with a unusable scope */ + return FALSE; + } + } + *out = ip; + return TRUE; +} + +/** + * enumerator constructor for interfaces + */ +static enumerator_t *create_iface_enumerator(iface_entry_t *iface, + address_enumerator_t *data) +{ + return enumerator_create_filter(iface->addrs->create_enumerator(iface->addrs), + (void*)filter_addresses, data, NULL); +} + +/** + * filter for interfaces + */ +static bool filter_interfaces(address_enumerator_t *data, iface_entry_t** in, + iface_entry_t** out) +{ + if (!(data->which & ADDR_TYPE_IGNORED) && !(*in)->usable) + { /* skip interfaces excluded by config */ + return FALSE; + } + if (!(data->which & ADDR_TYPE_LOOPBACK) && ((*in)->flags & IFF_LOOPBACK)) + { /* ignore loopback devices */ + return FALSE; + } + if (!(data->which & ADDR_TYPE_DOWN) && !((*in)->flags & IFF_UP)) + { /* skip interfaces not up */ + return FALSE; + } + *out = *in; + return TRUE; +} + +METHOD(kernel_net_t, create_address_enumerator, enumerator_t*, + private_kernel_pfroute_net_t *this, kernel_address_type_t which) +{ + address_enumerator_t *data; + + INIT(data, + .this = this, + .which = which, + ); + + this->lock->read_lock(this->lock); + return enumerator_create_nested( + enumerator_create_filter( + this->ifaces->create_enumerator(this->ifaces), + (void*)filter_interfaces, data, NULL), + (void*)create_iface_enumerator, data, + (void*)address_enumerator_destroy); +} + +METHOD(kernel_net_t, get_features, kernel_feature_t, + private_kernel_pfroute_net_t *this) +{ + return KERNEL_REQUIRE_EXCLUDE_ROUTE; +} + +METHOD(kernel_net_t, get_interface_name, bool, + private_kernel_pfroute_net_t *this, host_t* ip, char **name) +{ + addr_map_entry_t *entry, lookup = { + .ip = ip, + }; + + if (ip->is_anyaddr(ip)) + { + return FALSE; + } + this->lock->read_lock(this->lock); + /* first try to find it on an up and usable interface */ + entry = this->addrs->get_match(this->addrs, &lookup, + (void*)addr_map_entry_match_up_and_usable); + if (entry) + { + if (name) + { + *name = strdup(entry->iface->ifname); + DBG2(DBG_KNL, "%H is on interface %s", ip, *name); + } + this->lock->unlock(this->lock); + return TRUE; + } + /* check if it is a virtual IP */ + entry = this->addrs->get_match(this->addrs, &lookup, + (void*)addr_map_entry_match_virtual); + if (entry) + { + if (name) + { + *name = strdup(entry->iface->ifname); + DBG2(DBG_KNL, "virtual IP %H is on interface %s", ip, *name); + } + this->lock->unlock(this->lock); + return TRUE; + } + /* maybe it is installed on an ignored interface */ + entry = this->addrs->get_match(this->addrs, &lookup, + (void*)addr_map_entry_match_up); + if (!entry) + { /* the address does not exist, is on a down interface */ + DBG2(DBG_KNL, "%H is not a local address or the interface is down", ip); + } + this->lock->unlock(this->lock); + return FALSE; +} + +METHOD(kernel_net_t, add_ip, status_t, + private_kernel_pfroute_net_t *this, host_t *vip, int prefix, + char *ifname) +{ + enumerator_t *ifaces, *addrs; + iface_entry_t *iface; + addr_entry_t *addr; + tun_device_t *tun; + bool timeout = FALSE; + + if (!this->install_virtual_ip) + { /* disabled by config */ + return SUCCESS; + } + + tun = tun_device_create(NULL); + if (!tun) + { + return FAILED; + } + if (prefix == -1) + { + prefix = vip->get_address(vip).len * 8; + } + if (!tun->up(tun) || !tun->set_address(tun, vip, prefix)) + { + tun->destroy(tun); + return FAILED; + } + + /* wait until address appears */ + this->mutex->lock(this->mutex); + while (!timeout && !get_interface_name(this, vip, NULL)) + { + timeout = this->condvar->timed_wait(this->condvar, this->mutex, + this->vip_wait); + } + this->mutex->unlock(this->mutex); + if (timeout) + { + DBG1(DBG_KNL, "virtual IP %H did not appear on %s", + vip, tun->get_name(tun)); + tun->destroy(tun); + return FAILED; + } + + this->lock->write_lock(this->lock); + this->tuns->insert_last(this->tuns, tun); + + ifaces = this->ifaces->create_enumerator(this->ifaces); + while (ifaces->enumerate(ifaces, &iface)) + { + if (streq(iface->ifname, tun->get_name(tun))) + { + addrs = iface->addrs->create_enumerator(iface->addrs); + while (addrs->enumerate(addrs, &addr)) + { + if (addr->ip->ip_equals(addr->ip, vip)) + { + addr->virtual = TRUE; + } + } + addrs->destroy(addrs); + /* during IKEv1 reauthentication, children get moved from + * old the new SA before the virtual IP is available. This + * kills the route for our virtual IP, reinstall. */ + queue_route_reinstall(this, strdup(iface->ifname)); + break; + } + } + ifaces->destroy(ifaces); + /* lets do this while holding the lock, thus preventing another thread + * from deleting the TUN device concurrently, hopefully listeners are quick + * and cause no deadlocks */ + charon->kernel->tun(charon->kernel, tun, TRUE); + this->lock->unlock(this->lock); + + return SUCCESS; +} + +METHOD(kernel_net_t, del_ip, status_t, + private_kernel_pfroute_net_t *this, host_t *vip, int prefix, + bool wait) +{ + enumerator_t *enumerator; + tun_device_t *tun; + host_t *addr; + bool timeout = FALSE, found = FALSE; + + if (!this->install_virtual_ip) + { /* disabled by config */ + return SUCCESS; + } + + this->lock->write_lock(this->lock); + enumerator = this->tuns->create_enumerator(this->tuns); + while (enumerator->enumerate(enumerator, &tun)) + { + addr = tun->get_address(tun, NULL); + if (addr && addr->ip_equals(addr, vip)) + { + this->tuns->remove_at(this->tuns, enumerator); + charon->kernel->tun(charon->kernel, tun, FALSE); + tun->destroy(tun); + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + + if (!found) + { + return NOT_FOUND; + } + /* wait until address disappears */ + if (wait) + { + this->mutex->lock(this->mutex); + while (!timeout && get_interface_name(this, vip, NULL)) + { + timeout = this->condvar->timed_wait(this->condvar, this->mutex, + this->vip_wait); + } + this->mutex->unlock(this->mutex); + if (timeout) + { + DBG1(DBG_KNL, "virtual IP %H did not disappear from tun", vip); + return FAILED; + } + } + return SUCCESS; +} + +/** + * Append a sockaddr_in/in6 of given type to routing message + */ +static void add_rt_addr(struct rt_msghdr *hdr, int type, host_t *addr) +{ + if (addr) + { + int len; + + len = *addr->get_sockaddr_len(addr); + memcpy((char*)hdr + hdr->rtm_msglen, addr->get_sockaddr(addr), len); + hdr->rtm_msglen += SA_LEN(len); + hdr->rtm_addrs |= type; + } +} + +/** + * Append a subnet mask sockaddr using the given prefix to routing message + */ +static void add_rt_mask(struct rt_msghdr *hdr, int type, int family, int prefix) +{ + host_t *mask; + + mask = host_create_netmask(family, prefix); + if (mask) + { + add_rt_addr(hdr, type, mask); + mask->destroy(mask); + } +} + +/** + * Append an interface name sockaddr_dl to routing message + */ +static void add_rt_ifname(struct rt_msghdr *hdr, int type, char *name) +{ + struct sockaddr_dl sdl = { + .sdl_len = sizeof(struct sockaddr_dl), + .sdl_family = AF_LINK, + .sdl_nlen = strlen(name), + }; + + if (strlen(name) <= sizeof(sdl.sdl_data)) + { + memcpy(sdl.sdl_data, name, sdl.sdl_nlen); + memcpy((char*)hdr + hdr->rtm_msglen, &sdl, sdl.sdl_len); + hdr->rtm_msglen += SA_LEN(sdl.sdl_len); + hdr->rtm_addrs |= type; + } +} + +/** + * Add or remove a route + */ +static status_t manage_route(private_kernel_pfroute_net_t *this, int op, + chunk_t dst_net, u_int8_t prefixlen, + host_t *gateway, char *if_name) +{ + struct { + struct rt_msghdr hdr; + char buf[sizeof(struct sockaddr_storage) * RTAX_MAX]; + } msg = { + .hdr = { + .rtm_version = RTM_VERSION, + .rtm_type = op, + .rtm_flags = RTF_UP | RTF_STATIC, + .rtm_pid = this->pid, + .rtm_seq = ref_get(&this->seq), + }, + }; + host_t *dst; + int type; + + if (prefixlen == 0 && dst_net.len) + { + status_t status; + chunk_t half; + + half = chunk_clonea(dst_net); + half.ptr[0] |= 0x80; + prefixlen = 1; + status = manage_route(this, op, half, prefixlen, gateway, if_name); + if (status != SUCCESS) + { + return status; + } + } + + dst = host_create_from_chunk(AF_UNSPEC, dst_net, 0); + if (!dst) + { + return FAILED; + } + + if ((dst->get_family(dst) == AF_INET && prefixlen == 32) || + (dst->get_family(dst) == AF_INET6 && prefixlen == 128)) + { + msg.hdr.rtm_flags |= RTF_HOST | RTF_GATEWAY; + } + + msg.hdr.rtm_msglen = sizeof(struct rt_msghdr); + for (type = 0; type < RTAX_MAX; type++) + { + switch (type) + { + case RTAX_DST: + add_rt_addr(&msg.hdr, RTA_DST, dst); + break; + case RTAX_NETMASK: + if (!(msg.hdr.rtm_flags & RTF_HOST)) + { + add_rt_mask(&msg.hdr, RTA_NETMASK, + dst->get_family(dst), prefixlen); + } + break; + case RTAX_IFP: + if (if_name) + { + add_rt_ifname(&msg.hdr, RTA_IFP, if_name); + } + break; + case RTAX_GATEWAY: + if (gateway) + { + add_rt_addr(&msg.hdr, RTA_GATEWAY, gateway); + } + break; + default: + break; + } + } + dst->destroy(dst); + + if (send(this->socket, &msg, msg.hdr.rtm_msglen, 0) != msg.hdr.rtm_msglen) + { + if (errno == EEXIST) + { + return ALREADY_DONE; + } + DBG1(DBG_KNL, "%s PF_ROUTE route failed: %s", + op == RTM_ADD ? "adding" : "deleting", strerror(errno)); + return FAILED; + } + return SUCCESS; +} + +METHOD(kernel_net_t, add_route, status_t, + private_kernel_pfroute_net_t *this, chunk_t dst_net, u_int8_t prefixlen, + host_t *gateway, host_t *src_ip, char *if_name) +{ + status_t status; + route_entry_t *found, route = { + .dst_net = dst_net, + .prefixlen = prefixlen, + .gateway = gateway, + .if_name = if_name, + }; + + this->routes_lock->lock(this->routes_lock); + found = this->routes->get(this->routes, &route); + if (found) + { + this->routes_lock->unlock(this->routes_lock); + return ALREADY_DONE; + } + status = manage_route(this, RTM_ADD, dst_net, prefixlen, gateway, if_name); + if (status == SUCCESS) + { + found = route_entry_clone(&route); + this->routes->put(this->routes, found, found); + } + this->routes_lock->unlock(this->routes_lock); + return status; +} + +METHOD(kernel_net_t, del_route, status_t, + private_kernel_pfroute_net_t *this, chunk_t dst_net, u_int8_t prefixlen, + host_t *gateway, host_t *src_ip, char *if_name) +{ + status_t status; + route_entry_t *found, route = { + .dst_net = dst_net, + .prefixlen = prefixlen, + .gateway = gateway, + .if_name = if_name, + }; + + this->routes_lock->lock(this->routes_lock); + found = this->routes->get(this->routes, &route); + if (!found) + { + this->routes_lock->unlock(this->routes_lock); + return NOT_FOUND; + } + this->routes->remove(this->routes, found); + route_entry_destroy(found); + status = manage_route(this, RTM_DELETE, dst_net, prefixlen, gateway, + if_name); + this->routes_lock->unlock(this->routes_lock); + return status; +} + +/** + * Do a route lookup for dest and return either the nexthop or the source + * address. + */ +static host_t *get_route(private_kernel_pfroute_net_t *this, bool nexthop, + host_t *dest, host_t *src) +{ + struct { + struct rt_msghdr hdr; + char buf[sizeof(struct sockaddr_storage) * RTAX_MAX]; + } msg = { + .hdr = { + .rtm_version = RTM_VERSION, + .rtm_type = RTM_GET, + .rtm_pid = this->pid, + .rtm_seq = ref_get(&this->seq), + }, + }; + host_t *host = NULL; + enumerator_t *enumerator; + struct sockaddr *addr; + bool failed = FALSE; + int type; + +retry: + msg.hdr.rtm_msglen = sizeof(struct rt_msghdr); + for (type = 0; type < RTAX_MAX; type++) + { + switch (type) + { + case RTAX_DST: + add_rt_addr(&msg.hdr, RTA_DST, dest); + break; + case RTAX_IFA: + add_rt_addr(&msg.hdr, RTA_IFA, src); + break; + case RTAX_IFP: + if (!nexthop) + { /* add an empty IFP to ensure we get a source address */ + add_rt_ifname(&msg.hdr, RTA_IFP, ""); + } + break; + default: + break; + } + } + this->mutex->lock(this->mutex); + + while (this->waiting_seq) + { + this->condvar->wait(this->condvar, this->mutex); + } + this->waiting_seq = msg.hdr.rtm_seq; + if (send(this->socket, &msg, msg.hdr.rtm_msglen, 0) == msg.hdr.rtm_msglen) + { + while (TRUE) + { + if (this->condvar->timed_wait(this->condvar, this->mutex, 1000)) + { /* timed out? */ + break; + } + if (!this->reply) + { + continue; + } + enumerator = create_rtmsg_enumerator(this->reply); + while (enumerator->enumerate(enumerator, &type, &addr)) + { + if (nexthop) + { + if (type == RTAX_DST && this->reply->rtm_flags & RTF_HOST) + { /* probably a cloned/cached direct route, only use that + * as fallback if no gateway is found */ + host = host ?: host_create_from_sockaddr(addr); + } + if (type == RTAX_GATEWAY) + { /* could actually be a MAC address */ + host_t *gtw = host_create_from_sockaddr(addr); + if (gtw) + { + DESTROY_IF(host); + host = gtw; + } + } + } + else + { + if (type == RTAX_IFA) + { + host = host_create_from_sockaddr(addr); + } + } + } + enumerator->destroy(enumerator); + break; + } + } + else + { + failed = TRUE; + } + free(this->reply); + this->reply = NULL; + /* signal completion of query to a waiting thread */ + this->waiting_seq = 0; + this->condvar->signal(this->condvar); + this->mutex->unlock(this->mutex); + + if (failed) + { + if (src) + { /* the given source address might be gone, try again without */ + src = NULL; + msg.hdr.rtm_seq = ref_get(&this->seq); + msg.hdr.rtm_addrs = 0; + memset(msg.buf, 0, sizeof(msg.buf)); + goto retry; + } + DBG1(DBG_KNL, "PF_ROUTE lookup failed: %s", strerror(errno)); + } + if (nexthop) + { + host = host ?: dest->clone(dest); + } + else + { /* make sure the source address is not virtual and usable */ + addr_entry_t *entry, lookup = { + .ip = host, + }; + + if (!host) + { + return NULL; + } + this->lock->read_lock(this->lock); + entry = this->addrs->get_match(this->addrs, &lookup, + (void*)addr_map_entry_match_up_and_usable); + this->lock->unlock(this->lock); + if (!entry) + { + host->destroy(host); + return NULL; + } + } + DBG2(DBG_KNL, "using %H as %s to reach %H", host, + nexthop ? "nexthop" : "address", dest); + return host; +} + +METHOD(kernel_net_t, get_source_addr, host_t*, + private_kernel_pfroute_net_t *this, host_t *dest, host_t *src) +{ + return get_route(this, FALSE, dest, src); +} + +METHOD(kernel_net_t, get_nexthop, host_t*, + private_kernel_pfroute_net_t *this, host_t *dest, int prefix, host_t *src) +{ + return get_route(this, TRUE, dest, src); +} + +/** + * Initialize a list of local addresses. + */ +static status_t init_address_list(private_kernel_pfroute_net_t *this) +{ + struct ifaddrs *ifap, *ifa; + iface_entry_t *iface, *current; + addr_entry_t *addr; + enumerator_t *ifaces, *addrs; + + DBG2(DBG_KNL, "known interfaces and IP addresses:"); + + if (getifaddrs(&ifap) < 0) + { + DBG1(DBG_KNL, " failed to get interfaces!"); + return FAILED; + } + + for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr == NULL) + { + continue; + } + switch(ifa->ifa_addr->sa_family) + { + case AF_LINK: + case AF_INET: + case AF_INET6: + { + iface = NULL; + ifaces = this->ifaces->create_enumerator(this->ifaces); + while (ifaces->enumerate(ifaces, ¤t)) + { + if (streq(current->ifname, ifa->ifa_name)) + { + iface = current; + break; + } + } + ifaces->destroy(ifaces); + + if (!iface) + { + INIT(iface, + .ifindex = if_nametoindex(ifa->ifa_name), + .flags = ifa->ifa_flags, + .addrs = linked_list_create(), + .usable = charon->kernel->is_interface_usable( + charon->kernel, ifa->ifa_name), + ); + memcpy(iface->ifname, ifa->ifa_name, IFNAMSIZ); + this->ifaces->insert_last(this->ifaces, iface); + } + + if (ifa->ifa_addr->sa_family != AF_LINK) + { + INIT(addr, + .ip = host_create_from_sockaddr(ifa->ifa_addr), + ); + iface->addrs->insert_last(iface->addrs, addr); + addr_map_entry_add(this, addr, iface); + } + } + } + } + freeifaddrs(ifap); + + ifaces = this->ifaces->create_enumerator(this->ifaces); + while (ifaces->enumerate(ifaces, &iface)) + { + if (iface->usable && iface->flags & IFF_UP) + { + DBG2(DBG_KNL, " %s", iface->ifname); + addrs = iface->addrs->create_enumerator(iface->addrs); + while (addrs->enumerate(addrs, (void**)&addr)) + { + DBG2(DBG_KNL, " %H", addr->ip); + } + addrs->destroy(addrs); + } + } + ifaces->destroy(ifaces); + + return SUCCESS; +} + +METHOD(kernel_net_t, destroy, void, + private_kernel_pfroute_net_t *this) +{ + enumerator_t *enumerator; + route_entry_t *route; + addr_entry_t *addr; + + enumerator = this->routes->create_enumerator(this->routes); + while (enumerator->enumerate(enumerator, NULL, (void**)&route)) + { + manage_route(this, RTM_DELETE, route->dst_net, route->prefixlen, + route->gateway, route->if_name); + route_entry_destroy(route); + } + enumerator->destroy(enumerator); + this->routes->destroy(this->routes); + this->routes_lock->destroy(this->routes_lock); + + if (this->socket != -1) + { + lib->watcher->remove(lib->watcher, this->socket); + close(this->socket); + } + + net_changes_clear(this); + this->net_changes->destroy(this->net_changes); + this->net_changes_lock->destroy(this->net_changes_lock); + + enumerator = this->addrs->create_enumerator(this->addrs); + while (enumerator->enumerate(enumerator, NULL, (void**)&addr)) + { + free(addr); + } + enumerator->destroy(enumerator); + this->addrs->destroy(this->addrs); + this->ifaces->destroy_function(this->ifaces, (void*)iface_entry_destroy); + this->tuns->destroy(this->tuns); + this->lock->destroy(this->lock); + this->mutex->destroy(this->mutex); + this->condvar->destroy(this->condvar); + this->roam_lock->destroy(this->roam_lock); + free(this->reply); + free(this); +} + +/* + * Described in header. + */ +kernel_pfroute_net_t *kernel_pfroute_net_create() +{ + private_kernel_pfroute_net_t *this; + + INIT(this, + .public = { + .interface = { + .get_features = _get_features, + .get_interface = _get_interface_name, + .create_address_enumerator = _create_address_enumerator, + .get_source_addr = _get_source_addr, + .get_nexthop = _get_nexthop, + .add_ip = _add_ip, + .del_ip = _del_ip, + .add_route = _add_route, + .del_route = _del_route, + .destroy = _destroy, + }, + }, + .pid = getpid(), + .ifaces = linked_list_create(), + .addrs = hashtable_create( + (hashtable_hash_t)addr_map_entry_hash, + (hashtable_equals_t)addr_map_entry_equals, 16), + .routes = hashtable_create((hashtable_hash_t)route_entry_hash, + (hashtable_equals_t)route_entry_equals, 16), + .net_changes = hashtable_create( + (hashtable_hash_t)net_change_hash, + (hashtable_equals_t)net_change_equals, 16), + .tuns = linked_list_create(), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + .condvar = condvar_create(CONDVAR_TYPE_DEFAULT), + .routes_lock = mutex_create(MUTEX_TYPE_DEFAULT), + .net_changes_lock = mutex_create(MUTEX_TYPE_DEFAULT), + .roam_lock = spinlock_create(), + .vip_wait = lib->settings->get_int(lib->settings, + "%s.plugins.kernel-pfroute.vip_wait", 1000, lib->ns), + .install_virtual_ip = lib->settings->get_bool(lib->settings, + "%s.install_virtual_ip", TRUE, lib->ns), + ); + timerclear(&this->last_route_reinstall); + timerclear(&this->next_roam); + + /* create a PF_ROUTE socket to communicate with the kernel */ + this->socket = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC); + if (this->socket == -1) + { + DBG1(DBG_KNL, "unable to create PF_ROUTE socket"); + destroy(this); + return NULL; + } + + if (streq(lib->ns, "starter")) + { + /* starter has no threads, so we do not register for kernel events */ + if (shutdown(this->socket, SHUT_RD) != 0) + { + DBG1(DBG_KNL, "closing read end of PF_ROUTE socket failed: %s", + strerror(errno)); + } + } + else + { + lib->watcher->add(lib->watcher, this->socket, WATCHER_READ, + (watcher_cb_t)receive_events, this); + } + if (init_address_list(this) != SUCCESS) + { + DBG1(DBG_KNL, "unable to get interface list"); + destroy(this); + return NULL; + } + + return &this->public; +} diff --git a/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.h b/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.h new file mode 100644 index 000000000..10c3c9eb7 --- /dev/null +++ b/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2009 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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_pfroute_net_i kernel_pfroute_net + * @{ @ingroup kernel_pfroute + */ + +#ifndef KERNEL_PFROUTE_NET_H_ +#define KERNEL_PFROUTE_NET_H_ + +#include <kernel/kernel_net.h> + +typedef struct kernel_pfroute_net_t kernel_pfroute_net_t; + +/** + * Implementation of the kernel net interface using PF_ROUTE. + */ +struct kernel_pfroute_net_t { + + /** + * Implements kernel_net_t interface + */ + kernel_net_t interface; +}; + +/** + * Create a PF_ROUTE kernel net interface instance. + * + * @return kernel_pfroute_net_t instance + */ +kernel_pfroute_net_t *kernel_pfroute_net_create(); + +#endif /** KERNEL_PFROUTE_NET_H_ @}*/ diff --git a/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_plugin.c b/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_plugin.c new file mode 100644 index 000000000..acd834ba3 --- /dev/null +++ b/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_plugin.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2009 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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_pfroute_plugin.h" + +#include "kernel_pfroute_net.h" + +typedef struct private_kernel_pfroute_plugin_t private_kernel_pfroute_plugin_t; + +/** + * private data of kernel PF_ROUTE plugin + */ +struct private_kernel_pfroute_plugin_t { + /** + * implements plugin interface + */ + kernel_pfroute_plugin_t public; +}; + +METHOD(plugin_t, get_name, char*, + private_kernel_pfroute_plugin_t *this) +{ + return "kernel-pfroute"; +} + +METHOD(plugin_t, get_features, int, + private_kernel_pfroute_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_CALLBACK(kernel_net_register, kernel_pfroute_net_create), + PLUGIN_PROVIDE(CUSTOM, "kernel-net"), + }; + *features = f; + return countof(f); +} + +METHOD(plugin_t, destroy, void, + private_kernel_pfroute_plugin_t *this) +{ + free(this); +} + +/* + * see header file + */ +plugin_t *kernel_pfroute_plugin_create() +{ + private_kernel_pfroute_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_pfroute/kernel_pfroute_plugin.h b/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_plugin.h new file mode 100644 index 000000000..50642a572 --- /dev/null +++ b/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_plugin.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2009 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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_pfroute kernel_pfroute + * @ingroup cplugins + * + * @defgroup kernel_pfroute_plugin kernel_pfroute_plugin + * @{ @ingroup kernel_pfroute + */ + +#ifndef KERNEL_PFROUTE_PLUGIN_H_ +#define KERNEL_PFROUTE_PLUGIN_H_ + +#include <plugins/plugin.h> + +typedef struct kernel_pfroute_plugin_t kernel_pfroute_plugin_t; + +/** + * PF_ROUTE kernel interface plugin + */ +struct kernel_pfroute_plugin_t { + + /** + * implements plugin interface + */ + plugin_t plugin; +}; + +#endif /** KERNEL_PFROUTE_PLUGIN_H_ @}*/ diff --git a/src/libcharon/plugins/kernel_wfp/Makefile.am b/src/libcharon/plugins/kernel_wfp/Makefile.am index 85e5089a3..737a79b6c 100644 --- a/src/libcharon/plugins/kernel_wfp/Makefile.am +++ b/src/libcharon/plugins/kernel_wfp/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/kernel_wfp/Makefile.in b/src/libcharon/plugins/kernel_wfp/Makefile.in index efb214b88..cfe643f26 100644 --- a/src/libcharon/plugins/kernel_wfp/Makefile.in +++ b/src/libcharon/plugins/kernel_wfp/Makefile.in @@ -424,6 +424,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -439,7 +441,6 @@ 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 = \ diff --git a/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c b/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c index 95f79f168..e1c429885 100644 --- a/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c +++ b/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c @@ -20,7 +20,6 @@ #include "kernel_wfp_ipsec.h" #include <daemon.h> -#include <hydra.h> #include <threading/mutex.h> #include <collections/array.h> #include <collections/hashtable.h> @@ -1396,10 +1395,9 @@ static bool uninstall_route(private_kernel_wfp_ipsec_t *this, { if (--route->refs == 0) { - if (hydra->kernel_interface->get_interface(hydra->kernel_interface, - src, &name)) + if (charon->kernel->get_interface(charon->kernel, src, &name)) { - res = hydra->kernel_interface->del_route(hydra->kernel_interface, + res = charon->kernel->del_route(charon->kernel, dst->get_address(dst), mask, gtw, src, name) == SUCCESS; free(name); } @@ -1442,10 +1440,9 @@ static bool install_route(private_kernel_wfp_ipsec_t *this, } else { - if (hydra->kernel_interface->get_interface(hydra->kernel_interface, - src, &name)) + if (charon->kernel->get_interface(charon->kernel, src, &name)) { - if (hydra->kernel_interface->add_route(hydra->kernel_interface, + if (charon->kernel->add_route(charon->kernel, dst->get_address(dst), mask, gtw, src, name) == SUCCESS) { INIT(route, @@ -1486,14 +1483,13 @@ static bool manage_route(private_kernel_wfp_ipsec_t *this, { return FALSE; } - if (hydra->kernel_interface->get_address_by_ts(hydra->kernel_interface, - src_ts, &src, NULL) != SUCCESS) + if (charon->kernel->get_address_by_ts(charon->kernel, src_ts, &src, + NULL) != SUCCESS) { dst->destroy(dst); return FALSE; } - gtw = hydra->kernel_interface->get_nexthop(hydra->kernel_interface, - remote, -1, local); + gtw = charon->kernel->get_nexthop(charon->kernel, remote, -1, local); if (add) { done = install_route(this, dst, mask, src, gtw); @@ -1650,8 +1646,7 @@ static void acquire(private_kernel_wfp_ipsec_t *this, UINT64 filter_id, { src = src ? src->clone(src) : NULL; dst = dst ? dst->clone(dst) : NULL; - hydra->kernel_interface->acquire(hydra->kernel_interface, reqid, - src, dst); + charon->kernel->acquire(charon->kernel, reqid, src, dst); } } @@ -2069,8 +2064,8 @@ static job_requeue_t expire_job(expire_data_t *data) if (entry) { - hydra->kernel_interface->expire(hydra->kernel_interface, protocol, - data->spi, data->dst, data->hard); + charon->kernel->expire(charon->kernel, protocol, data->spi, data->dst, + data->hard); } return JOB_REQUEUE_NONE; diff --git a/src/libcharon/plugins/led/Makefile.am b/src/libcharon/plugins/led/Makefile.am index 18d6af399..9868f9efa 100644 --- a/src/libcharon/plugins/led/Makefile.am +++ b/src/libcharon/plugins/led/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/led/Makefile.in b/src/libcharon/plugins/led/Makefile.in index 7942868f6..63bbf1975 100644 --- a/src/libcharon/plugins/led/Makefile.in +++ b/src/libcharon/plugins/led/Makefile.in @@ -413,6 +413,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -428,7 +430,6 @@ 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 = \ diff --git a/src/libcharon/plugins/load_tester/Makefile.am b/src/libcharon/plugins/load_tester/Makefile.am index 31e1b5c6f..af3adb257 100644 --- a/src/libcharon/plugins/load_tester/Makefile.am +++ b/src/libcharon/plugins/load_tester/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -DIPSEC_PIDDIR=\"${piddir}\" diff --git a/src/libcharon/plugins/load_tester/Makefile.in b/src/libcharon/plugins/load_tester/Makefile.in index 52dbec53f..14fcd6f4c 100644 --- a/src/libcharon/plugins/load_tester/Makefile.in +++ b/src/libcharon/plugins/load_tester/Makefile.in @@ -426,6 +426,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -441,7 +443,6 @@ 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 \ -DIPSEC_PIDDIR=\"${piddir}\" diff --git a/src/libcharon/plugins/load_tester/load_tester_config.c b/src/libcharon/plugins/load_tester/load_tester_config.c index 8a500635c..8f6abde0c 100644 --- a/src/libcharon/plugins/load_tester/load_tester_config.c +++ b/src/libcharon/plugins/load_tester/load_tester_config.c @@ -18,7 +18,6 @@ #include <netdb.h> #include <daemon.h> -#include <hydra.h> #include <attributes/mem_pool.h> #include <collections/hashtable.h> #include <threading/mutex.h> @@ -656,8 +655,8 @@ static host_t *allocate_addr(private_load_tester_config_t *this, uint num) id->destroy(id); return NULL; } - if (hydra->kernel_interface->add_ip(hydra->kernel_interface, - found, this->prefix, iface) != SUCCESS) + if (charon->kernel->add_ip(charon->kernel, found, this->prefix, + iface) != SUCCESS) { DBG1(DBG_CFG, "installing load-tester IP %H on %s failed", found, iface); found->destroy(found); @@ -852,8 +851,8 @@ METHOD(load_tester_config_t, delete_ip, void, { if (pool->release_address(pool, entry->host, entry->id)) { - hydra->kernel_interface->del_ip(hydra->kernel_interface, - entry->host, this->prefix, FALSE); + charon->kernel->del_ip(charon->kernel, entry->host, + this->prefix, FALSE); break; } } @@ -882,8 +881,8 @@ static void cleanup_leases(private_load_tester_config_t *this) { if (online) { - hydra->kernel_interface->del_ip(hydra->kernel_interface, - addr, this->prefix, FALSE); + charon->kernel->del_ip(charon->kernel, addr, this->prefix, + FALSE); entry = this->leases->remove(this->leases, addr); if (entry) { diff --git a/src/libcharon/plugins/load_tester/load_tester_plugin.c b/src/libcharon/plugins/load_tester/load_tester_plugin.c index c7380b974..6cf3a909c 100644 --- a/src/libcharon/plugins/load_tester/load_tester_plugin.c +++ b/src/libcharon/plugins/load_tester/load_tester_plugin.c @@ -23,7 +23,6 @@ #include <unistd.h> -#include <hydra.h> #include <daemon.h> #include <processing/jobs/callback_job.h> #include <threading/condvar.h> @@ -240,16 +239,24 @@ METHOD(plugin_t, get_features, int, PLUGIN_SDEPEND(PRIVKEY, KEY_RSA), PLUGIN_SDEPEND(CERT_DECODE, CERT_ANY), PLUGIN_SDEPEND(CERT_DECODE, CERT_X509), + PLUGIN_CALLBACK(kernel_ipsec_register, load_tester_ipsec_create), + PLUGIN_PROVIDE(CUSTOM, "kernel-ipsec"), }; + int count = countof(f); + *features = f; - return countof(f); + + if (!lib->settings->get_bool(lib->settings, + "%s.plugins.load-tester.fake_kernel", FALSE, lib->ns)) + { + count -= 2; + } + return count; } METHOD(plugin_t, destroy, void, private_load_tester_plugin_t *this) { - hydra->kernel_interface->remove_ipsec_interface(hydra->kernel_interface, - (kernel_ipsec_constructor_t)load_tester_ipsec_create); this->mutex->destroy(this->mutex); this->condvar->destroy(this->condvar); free(this); @@ -289,12 +296,5 @@ plugin_t *load_tester_plugin_create() .mutex = mutex_create(MUTEX_TYPE_DEFAULT), .condvar = condvar_create(CONDVAR_TYPE_DEFAULT), ); - - if (lib->settings->get_bool(lib->settings, - "%s.plugins.load-tester.fake_kernel", FALSE, lib->ns)) - { - hydra->kernel_interface->add_ipsec_interface(hydra->kernel_interface, - (kernel_ipsec_constructor_t)load_tester_ipsec_create); - } return &this->public.plugin; } diff --git a/src/libcharon/plugins/lookip/Makefile.am b/src/libcharon/plugins/lookip/Makefile.am index 223654ea9..623275b21 100644 --- a/src/libcharon/plugins/lookip/Makefile.am +++ b/src/libcharon/plugins/lookip/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -DIPSEC_PIDDIR=\"${piddir}\" diff --git a/src/libcharon/plugins/lookip/Makefile.in b/src/libcharon/plugins/lookip/Makefile.in index 264c58ff5..9b56d94fe 100644 --- a/src/libcharon/plugins/lookip/Makefile.in +++ b/src/libcharon/plugins/lookip/Makefile.in @@ -422,6 +422,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -437,7 +439,6 @@ 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 \ -DIPSEC_PIDDIR=\"${piddir}\" diff --git a/src/libcharon/plugins/maemo/Makefile.am b/src/libcharon/plugins/maemo/Makefile.am index fe5c963fd..02c283f5b 100644 --- a/src/libcharon/plugins/maemo/Makefile.am +++ b/src/libcharon/plugins/maemo/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/maemo/Makefile.in b/src/libcharon/plugins/maemo/Makefile.in index 76c9012b2..5cc654967 100644 --- a/src/libcharon/plugins/maemo/Makefile.in +++ b/src/libcharon/plugins/maemo/Makefile.in @@ -418,6 +418,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -433,7 +435,6 @@ 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 = \ diff --git a/src/libcharon/plugins/medcli/Makefile.am b/src/libcharon/plugins/medcli/Makefile.am index cfa825980..0408c8963 100644 --- a/src/libcharon/plugins/medcli/Makefile.am +++ b/src/libcharon/plugins/medcli/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/medcli/Makefile.in b/src/libcharon/plugins/medcli/Makefile.in index 35740c369..32c428487 100644 --- a/src/libcharon/plugins/medcli/Makefile.in +++ b/src/libcharon/plugins/medcli/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 = \ diff --git a/src/libcharon/plugins/medsrv/Makefile.am b/src/libcharon/plugins/medsrv/Makefile.am index f21220260..1d1cb4465 100644 --- a/src/libcharon/plugins/medsrv/Makefile.am +++ b/src/libcharon/plugins/medsrv/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/medsrv/Makefile.in b/src/libcharon/plugins/medsrv/Makefile.in index 8fe160ef3..de0217a80 100644 --- a/src/libcharon/plugins/medsrv/Makefile.in +++ b/src/libcharon/plugins/medsrv/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 = \ diff --git a/src/libcharon/plugins/osx_attr/Makefile.am b/src/libcharon/plugins/osx_attr/Makefile.am index aa1d46290..908aa8806 100644 --- a/src/libcharon/plugins/osx_attr/Makefile.am +++ b/src/libcharon/plugins/osx_attr/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/osx_attr/Makefile.in b/src/libcharon/plugins/osx_attr/Makefile.in index 9a5e438e1..6a1a81f08 100644 --- a/src/libcharon/plugins/osx_attr/Makefile.in +++ b/src/libcharon/plugins/osx_attr/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 = \ diff --git a/src/libcharon/plugins/p_cscf/Makefile.am b/src/libcharon/plugins/p_cscf/Makefile.am new file mode 100644 index 000000000..1e00a56a8 --- /dev/null +++ b/src/libcharon/plugins/p_cscf/Makefile.am @@ -0,0 +1,19 @@ +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-p-cscf.la +else +plugin_LTLIBRARIES = libstrongswan-p-cscf.la +endif + +libstrongswan_p_cscf_la_SOURCES = \ + p_cscf_plugin.c p_cscf_plugin.h \ + p_cscf_handler.c p_cscf_handler.h + +libstrongswan_p_cscf_la_LDFLAGS = -module -avoid-version diff --git a/src/libcharon/plugins/p_cscf/Makefile.in b/src/libcharon/plugins/p_cscf/Makefile.in new file mode 100644 index 000000000..7f78db85a --- /dev/null +++ b/src/libcharon/plugins/p_cscf/Makefile.in @@ -0,0 +1,782 @@ +# 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@ +subdir = src/libcharon/plugins/p_cscf +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_p_cscf_la_LIBADD = +am_libstrongswan_p_cscf_la_OBJECTS = p_cscf_plugin.lo \ + p_cscf_handler.lo +libstrongswan_p_cscf_la_OBJECTS = \ + $(am_libstrongswan_p_cscf_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_p_cscf_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(libstrongswan_p_cscf_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@MONOLITHIC_FALSE@am_libstrongswan_p_cscf_la_rpath = -rpath \ +@MONOLITHIC_FALSE@ $(plugindir) +@MONOLITHIC_TRUE@am_libstrongswan_p_cscf_la_rpath = +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_p_cscf_la_SOURCES) +DIST_SOURCES = $(libstrongswan_p_cscf_la_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@ +EASY_INSTALL = @EASY_INSTALL@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GEM = @GEM@ +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@ +PYTHONEGGINSTALLDIR = @PYTHONEGGINSTALLDIR@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +PY_TEST = @PY_TEST@ +RANLIB = @RANLIB@ +RTLIB = @RTLIB@ +RUBY = @RUBY@ +RUBYGEMDIR = @RUBYGEMDIR@ +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@ +json_CFLAGS = @json_CFLAGS@ +json_LIBS = @json_LIBS@ +libdir = @libdir@ +libexecdir = @libexecdir@ +libiptc_CFLAGS = @libiptc_CFLAGS@ +libiptc_LIBS = @libiptc_LIBS@ +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@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ +systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ +systemd_daemon_LIBS = @systemd_daemon_LIBS@ +systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ +systemd_journal_LIBS = @systemd_journal_LIBS@ +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-p-cscf.la +@MONOLITHIC_FALSE@plugin_LTLIBRARIES = libstrongswan-p-cscf.la +libstrongswan_p_cscf_la_SOURCES = \ + p_cscf_plugin.c p_cscf_plugin.h \ + p_cscf_handler.c p_cscf_handler.h + +libstrongswan_p_cscf_la_LDFLAGS = -module -avoid-version +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/p_cscf/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/libcharon/plugins/p_cscf/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-p-cscf.la: $(libstrongswan_p_cscf_la_OBJECTS) $(libstrongswan_p_cscf_la_DEPENDENCIES) $(EXTRA_libstrongswan_p_cscf_la_DEPENDENCIES) + $(AM_V_CCLD)$(libstrongswan_p_cscf_la_LINK) $(am_libstrongswan_p_cscf_la_rpath) $(libstrongswan_p_cscf_la_OBJECTS) $(libstrongswan_p_cscf_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p_cscf_handler.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p_cscf_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) +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-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-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/p_cscf/p_cscf_handler.c b/src/libcharon/plugins/p_cscf/p_cscf_handler.c new file mode 100644 index 000000000..76633845e --- /dev/null +++ b/src/libcharon/plugins/p_cscf/p_cscf_handler.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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 "p_cscf_handler.h" + +#include <networking/host.h> +#include <utils/debug.h> + +typedef struct private_p_cscf_handler_t private_p_cscf_handler_t; + +/** + * Private data + */ +struct private_p_cscf_handler_t { + + /** + * Public interface + */ + p_cscf_handler_t public; +}; + +METHOD(attribute_handler_t, handle, bool, + private_p_cscf_handler_t *this, ike_sa_t *ike_sa, + configuration_attribute_type_t type, chunk_t data) +{ + host_t *server; + int family = AF_INET6; + + switch (type) + { + case P_CSCF_IP4_ADDRESS: + family = AF_INET; + /* fall-through */ + case P_CSCF_IP6_ADDRESS: + server = host_create_from_chunk(family, data, 0); + if (!server) + { + DBG1(DBG_CFG, "received invalid P-CSCF server IP"); + return FALSE; + } + DBG1(DBG_CFG, "received P-CSCF server IP %H", server); + server->destroy(server); + return TRUE; + default: + return FALSE; + } +} + +METHOD(attribute_handler_t, release, void, + private_p_cscf_handler_t *this, ike_sa_t *ike_sa, + configuration_attribute_type_t type, chunk_t data) +{ + switch (type) + { + case P_CSCF_IP4_ADDRESS: + case P_CSCF_IP6_ADDRESS: + /* nothing to do as we only log the server IPs */ + break; + default: + break; + } +} + +/** + * Data for attribute enumerator + */ +typedef struct { + enumerator_t public; + bool request_ipv4; + bool request_ipv6; +} attr_enumerator_t; + +METHOD(enumerator_t, enumerate_attrs, bool, + attr_enumerator_t *this, configuration_attribute_type_t *type, + chunk_t *data) +{ + if (this->request_ipv4) + { + *type = P_CSCF_IP4_ADDRESS; + *data = chunk_empty; + this->request_ipv4 = FALSE; + return TRUE; + } + if (this->request_ipv6) + { + *type = P_CSCF_IP6_ADDRESS; + *data = chunk_empty; + this->request_ipv6 = FALSE; + return TRUE; + } + return FALSE; +} + +/** + * Check if the given host has a matching address family + */ +static bool is_family(host_t *host, int *family) +{ + return host->get_family(host) == *family; +} + +/** + * Check if a list has a host of a given family + */ +static bool has_host_family(linked_list_t *list, int family) +{ + return list->find_first(list, (void*)is_family, NULL, &family) == SUCCESS; +} + +METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t *, + private_p_cscf_handler_t *this, ike_sa_t *ike_sa, + linked_list_t *vips) +{ + attr_enumerator_t *enumerator; + + if (ike_sa->get_version(ike_sa) == IKEV1) + { + return enumerator_create_empty(); + } + + INIT(enumerator, + .public = { + .enumerate = (void*)_enumerate_attrs, + .destroy = (void*)free, + }, + ); + if (lib->settings->get_bool(lib->settings, "%s.plugins.p-cscf.enable.%s", + FALSE, lib->ns, ike_sa->get_name(ike_sa))) + { + enumerator->request_ipv4 = has_host_family(vips, AF_INET); + enumerator->request_ipv6 = has_host_family(vips, AF_INET6); + } + return &enumerator->public; +} + +METHOD(p_cscf_handler_t, destroy, void, + private_p_cscf_handler_t *this) +{ + free(this); +} + +/** + * See header + */ +p_cscf_handler_t *p_cscf_handler_create() +{ + private_p_cscf_handler_t *this; + + INIT(this, + .public = { + .handler = { + .handle = _handle, + .release = _release, + .create_attribute_enumerator = _create_attribute_enumerator, + }, + .destroy = _destroy, + }, + ); + + return &this->public; +} diff --git a/src/libcharon/plugins/p_cscf/p_cscf_handler.h b/src/libcharon/plugins/p_cscf/p_cscf_handler.h new file mode 100644 index 000000000..ad4f1acce --- /dev/null +++ b/src/libcharon/plugins/p_cscf/p_cscf_handler.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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 p_cscf_handler p_cscf_handler + * @{ @ingroup p_cscf + */ + +#ifndef P_CSCF_HANDLER_H_ +#define P_CSCF_HANDLER_H_ + +#include <attributes/attribute_handler.h> + +typedef struct p_cscf_handler_t p_cscf_handler_t; + +/** + * Attribute handler for P-CSCF server addresses. + */ +struct p_cscf_handler_t { + + /** + * Implements attribute_handler_t. + */ + attribute_handler_t handler; + + /** + * Destroy a p_cscf_handler_t. + */ + void (*destroy)(p_cscf_handler_t *this); +}; + +/** + * Create an p_cscf_handler_t instance. + */ +p_cscf_handler_t *p_cscf_handler_create(); + +#endif /** P_CSCF_HANDLER_H_ @}*/ diff --git a/src/libcharon/plugins/p_cscf/p_cscf_plugin.c b/src/libcharon/plugins/p_cscf/p_cscf_plugin.c new file mode 100644 index 000000000..8e2bc727e --- /dev/null +++ b/src/libcharon/plugins/p_cscf/p_cscf_plugin.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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 "p_cscf_plugin.h" +#include "p_cscf_handler.h" + +#include <daemon.h> + +typedef struct private_p_cscf_plugin_t private_p_cscf_plugin_t; + +/** + * Private data + */ +struct private_p_cscf_plugin_t { + + /** + * Public interface + */ + p_cscf_plugin_t public; + + /** + * P-CSCF server address attribute handler + */ + p_cscf_handler_t *handler; +}; + +METHOD(plugin_t, get_name, char*, + private_p_cscf_plugin_t *this) +{ + return "p-cscf"; +} + +/** + * Register handler + */ +static bool plugin_cb(private_p_cscf_plugin_t *this, + plugin_feature_t *feature, bool reg, void *cb_data) +{ + if (reg) + { + charon->attributes->add_handler(charon->attributes, + &this->handler->handler); + } + else + { + charon->attributes->remove_handler(charon->attributes, + &this->handler->handler); + } + return TRUE; +} + +METHOD(plugin_t, get_features, int, + private_p_cscf_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_CALLBACK((plugin_feature_callback_t)plugin_cb, NULL), + PLUGIN_PROVIDE(CUSTOM, "p-cscf"), + }; + *features = f; + return countof(f); +} + +METHOD(plugin_t, destroy, void, + private_p_cscf_plugin_t *this) +{ + this->handler->destroy(this->handler); + free(this); +} + +/** + * See header + */ +plugin_t *p_cscf_plugin_create() +{ + private_p_cscf_plugin_t *this; + + INIT(this, + .public = { + .plugin = { + .get_name = _get_name, + .get_features = _get_features, + .destroy = _destroy, + }, + }, + .handler = p_cscf_handler_create(), + ); + + return &this->public.plugin; +} diff --git a/src/libcharon/plugins/p_cscf/p_cscf_plugin.h b/src/libcharon/plugins/p_cscf/p_cscf_plugin.h new file mode 100644 index 000000000..51b17674d --- /dev/null +++ b/src/libcharon/plugins/p_cscf/p_cscf_plugin.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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 p_cscf p_cscf + * @ingroup cplugins + * + * @defgroup p_cscf_plugin p_cscf_plugin + * @{ @ingroup p_cscf + */ + +#ifndef P_CSCF_PLUGIN_H_ +#define P_CSCF_PLUGIN_H_ + +#include <plugins/plugin.h> + +typedef struct p_cscf_plugin_t p_cscf_plugin_t; + +/** + * Plugin that requests P-CSCF server addresses from an ePDG as specified + * in RFC 7651. + */ +struct p_cscf_plugin_t { + + /** + * Implements plugin interface. + */ + plugin_t plugin; +}; + +#endif /** P_CSCF_PLUGIN_H_ @}*/ diff --git a/src/libcharon/plugins/radattr/Makefile.am b/src/libcharon/plugins/radattr/Makefile.am index 15d5a0a1f..74d9351f2 100644 --- a/src/libcharon/plugins/radattr/Makefile.am +++ b/src/libcharon/plugins/radattr/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -I$(top_srcdir)/src/libradius diff --git a/src/libcharon/plugins/radattr/Makefile.in b/src/libcharon/plugins/radattr/Makefile.in index baff3fc76..3f39ba237 100644 --- a/src/libcharon/plugins/radattr/Makefile.in +++ b/src/libcharon/plugins/radattr/Makefile.in @@ -417,6 +417,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -432,7 +434,6 @@ 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 \ -I$(top_srcdir)/src/libradius diff --git a/src/libcharon/plugins/resolve/Makefile.am b/src/libcharon/plugins/resolve/Makefile.am index 9cfc370c0..d3d4e73cf 100644 --- a/src/libcharon/plugins/resolve/Makefile.am +++ b/src/libcharon/plugins/resolve/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -DRESOLV_CONF=\"${resolv_conf}\" diff --git a/src/libcharon/plugins/resolve/Makefile.in b/src/libcharon/plugins/resolve/Makefile.in index 91479bf52..70d97cc32 100644 --- a/src/libcharon/plugins/resolve/Makefile.in +++ b/src/libcharon/plugins/resolve/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 \ -DRESOLV_CONF=\"${resolv_conf}\" diff --git a/src/libcharon/plugins/resolve/resolve_handler.c b/src/libcharon/plugins/resolve/resolve_handler.c index 74c3960ff..ec3decc4d 100644 --- a/src/libcharon/plugins/resolve/resolve_handler.c +++ b/src/libcharon/plugins/resolve/resolve_handler.c @@ -20,7 +20,6 @@ #include <sys/stat.h> #include <unistd.h> -#include <hydra.h> #include <utils/debug.h> #include <threading/mutex.h> diff --git a/src/libcharon/plugins/smp/Makefile.am b/src/libcharon/plugins/smp/Makefile.am index 3aa533e56..252db32a6 100644 --- a/src/libcharon/plugins/smp/Makefile.am +++ b/src/libcharon/plugins/smp/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -DIPSEC_PIDDIR=\"${piddir}\" diff --git a/src/libcharon/plugins/smp/Makefile.in b/src/libcharon/plugins/smp/Makefile.in index 572e7fc2f..221cda71a 100644 --- a/src/libcharon/plugins/smp/Makefile.in +++ b/src/libcharon/plugins/smp/Makefile.in @@ -414,6 +414,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -429,7 +431,6 @@ 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 \ -DIPSEC_PIDDIR=\"${piddir}\" diff --git a/src/libcharon/plugins/smp/smp.c b/src/libcharon/plugins/smp/smp.c index 2aa061fd2..56b19c792 100644 --- a/src/libcharon/plugins/smp/smp.c +++ b/src/libcharon/plugins/smp/smp.c @@ -229,8 +229,8 @@ static void request_query_ikesa(xmlTextReaderPtr reader, xmlTextWriterPtr writer local = ike_sa->get_my_host(ike_sa); xmlTextWriterStartElement(writer, "local"); xmlTextWriterWriteFormatElement(writer, "spi", "%.16llx", - id->is_initiator(id) ? id->get_initiator_spi(id) - : id->get_responder_spi(id)); + be64toh(id->is_initiator(id) ? id->get_initiator_spi(id) + : id->get_responder_spi(id))); write_id(writer, "identification", ike_sa->get_my_id(ike_sa)); write_address(writer, "address", local); xmlTextWriterWriteFormatElement(writer, "port", "%d", @@ -246,8 +246,8 @@ static void request_query_ikesa(xmlTextReaderPtr reader, xmlTextWriterPtr writer remote = ike_sa->get_other_host(ike_sa); xmlTextWriterStartElement(writer, "remote"); xmlTextWriterWriteFormatElement(writer, "spi", "%.16llx", - id->is_initiator(id) ? id->get_responder_spi(id) - : id->get_initiator_spi(id)); + be64toh(id->is_initiator(id) ? id->get_responder_spi(id) + : id->get_initiator_spi(id))); write_id(writer, "identification", ike_sa->get_other_id(ike_sa)); write_address(writer, "address", remote); xmlTextWriterWriteFormatElement(writer, "port", "%d", diff --git a/src/libcharon/plugins/socket_default/Makefile.am b/src/libcharon/plugins/socket_default/Makefile.am index e524ffd18..7231703b3 100644 --- a/src/libcharon/plugins/socket_default/Makefile.am +++ b/src/libcharon/plugins/socket_default/Makefile.am @@ -1,7 +1,6 @@ AM_CPPFLAGS = \ -I${linux_headers} \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/socket_default/Makefile.in b/src/libcharon/plugins/socket_default/Makefile.in index 25b40995b..3dcfaf4a6 100644 --- a/src/libcharon/plugins/socket_default/Makefile.in +++ b/src/libcharon/plugins/socket_default/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -432,7 +434,6 @@ xml_LIBS = @xml_LIBS@ AM_CPPFLAGS = \ -I${linux_headers} \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/socket_default/socket_default_socket.c b/src/libcharon/plugins/socket_default/socket_default_socket.c index 13bf3e775..6e432d9cf 100644 --- a/src/libcharon/plugins/socket_default/socket_default_socket.c +++ b/src/libcharon/plugins/socket_default/socket_default_socket.c @@ -41,7 +41,6 @@ #include <netinet/udp.h> #include <net/if.h> -#include <hydra.h> #include <daemon.h> #include <threading/thread.h> @@ -720,16 +719,15 @@ static int open_socket(private_socket_default_socket_t *this, } #endif - if (!hydra->kernel_interface->bypass_socket(hydra->kernel_interface, - skt, family)) + if (!charon->kernel->bypass_socket(charon->kernel, skt, family)) { DBG1(DBG_NET, "installing IKE bypass policy failed"); } /* enable UDP decapsulation for NAT-T sockets */ if (port == &this->natt && - !hydra->kernel_interface->enable_udp_decap(hydra->kernel_interface, - skt, family, this->natt)) + !charon->kernel->enable_udp_decap(charon->kernel, skt, family, + this->natt)) { DBG1(DBG_NET, "enabling UDP decapsulation for %s on port %d failed", family == AF_INET ? "IPv4" : "IPv6", this->natt); diff --git a/src/libcharon/plugins/socket_dynamic/Makefile.am b/src/libcharon/plugins/socket_dynamic/Makefile.am index a1e21b98b..087ebb728 100644 --- a/src/libcharon/plugins/socket_dynamic/Makefile.am +++ b/src/libcharon/plugins/socket_dynamic/Makefile.am @@ -1,7 +1,6 @@ AM_CPPFLAGS = \ -I${linux_headers} \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/socket_dynamic/Makefile.in b/src/libcharon/plugins/socket_dynamic/Makefile.in index 5c010a59a..88bc22f5e 100644 --- a/src/libcharon/plugins/socket_dynamic/Makefile.in +++ b/src/libcharon/plugins/socket_dynamic/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -432,7 +434,6 @@ xml_LIBS = @xml_LIBS@ AM_CPPFLAGS = \ -I${linux_headers} \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c b/src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c index a032134c3..b89cae47b 100644 --- a/src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c +++ b/src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c @@ -36,7 +36,6 @@ #include <netinet/udp.h> #include <net/if.h> -#include <hydra.h> #include <daemon.h> #include <threading/thread.h> #include <threading/rwlock.h> @@ -438,15 +437,13 @@ static int open_socket(private_socket_dynamic_socket_t *this, return 0; } - if (!hydra->kernel_interface->bypass_socket(hydra->kernel_interface, - fd, family)) + if (!charon->kernel->bypass_socket(charon->kernel, fd, family)) { DBG1(DBG_NET, "installing IKE bypass policy failed"); } /* enable UDP decapsulation on each socket */ - if (!hydra->kernel_interface->enable_udp_decap(hydra->kernel_interface, - fd, family, *port)) + if (!charon->kernel->enable_udp_decap(charon->kernel, fd, family, *port)) { DBG1(DBG_NET, "enabling UDP decapsulation for %s on port %d failed", family == AF_INET ? "IPv4" : "IPv6", *port); diff --git a/src/libcharon/plugins/socket_win/Makefile.am b/src/libcharon/plugins/socket_win/Makefile.am index f01178fcc..293d9bc9f 100644 --- a/src/libcharon/plugins/socket_win/Makefile.am +++ b/src/libcharon/plugins/socket_win/Makefile.am @@ -1,7 +1,6 @@ AM_CPPFLAGS = \ -I${linux_headers} \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/socket_win/Makefile.in b/src/libcharon/plugins/socket_win/Makefile.in index 0c3bf31b9..683011062 100644 --- a/src/libcharon/plugins/socket_win/Makefile.in +++ b/src/libcharon/plugins/socket_win/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -432,7 +434,6 @@ xml_LIBS = @xml_LIBS@ AM_CPPFLAGS = \ -I${linux_headers} \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/socket_win/socket_win_socket.c b/src/libcharon/plugins/socket_win/socket_win_socket.c index fbfbedae1..94af08e80 100644 --- a/src/libcharon/plugins/socket_win/socket_win_socket.c +++ b/src/libcharon/plugins/socket_win/socket_win_socket.c @@ -19,7 +19,6 @@ #include "socket_win_socket.h" #include <library.h> -#include <hydra.h> #include <threading/thread.h> #include <daemon.h> @@ -397,13 +396,11 @@ static SOCKET open_socket(private_socket_win_socket_t *this, int i) closesocket(s); return INVALID_SOCKET; } - if (!hydra->kernel_interface->bypass_socket(hydra->kernel_interface, - s, AF_INET)) + if (!charon->kernel->bypass_socket(charon->kernel, s, AF_INET)) { DBG1(DBG_NET, "installing IPv4 IKE bypass policy failed"); } - if (!hydra->kernel_interface->bypass_socket(hydra->kernel_interface, - s, AF_INET6)) + if (!charon->kernel->bypass_socket(charon->kernel, s, AF_INET6)) { DBG1(DBG_NET, "installing IPv6 IKE bypass policy failed"); } diff --git a/src/libcharon/plugins/sql/Makefile.am b/src/libcharon/plugins/sql/Makefile.am index c947db892..44a3d5f4a 100644 --- a/src/libcharon/plugins/sql/Makefile.am +++ b/src/libcharon/plugins/sql/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/sql/Makefile.in b/src/libcharon/plugins/sql/Makefile.in index f74257af2..b09379b02 100644 --- a/src/libcharon/plugins/sql/Makefile.in +++ b/src/libcharon/plugins/sql/Makefile.in @@ -414,6 +414,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -429,7 +431,6 @@ 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 = \ diff --git a/src/libcharon/plugins/stroke/Makefile.am b/src/libcharon/plugins/stroke/Makefile.am index b90688791..26edc3dcd 100644 --- a/src/libcharon/plugins/stroke/Makefile.am +++ b/src/libcharon/plugins/stroke/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -I$(top_srcdir)/src/stroke \ -DIPSEC_CONFDIR=\"${sysconfdir}\" \ diff --git a/src/libcharon/plugins/stroke/Makefile.in b/src/libcharon/plugins/stroke/Makefile.in index a316f5c25..2b22b333a 100644 --- a/src/libcharon/plugins/stroke/Makefile.in +++ b/src/libcharon/plugins/stroke/Makefile.in @@ -418,6 +418,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -433,7 +435,6 @@ 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 \ -I$(top_srcdir)/src/stroke \ -DIPSEC_CONFDIR=\"${sysconfdir}\" \ diff --git a/src/libcharon/plugins/stroke/stroke_config.c b/src/libcharon/plugins/stroke/stroke_config.c index 68cf83089..d0eb2aac3 100644 --- a/src/libcharon/plugins/stroke/stroke_config.c +++ b/src/libcharon/plugins/stroke/stroke_config.c @@ -16,7 +16,6 @@ #include "stroke_config.h" -#include <hydra.h> #include <daemon.h> #include <threading/mutex.h> #include <utils/lexparser.h> @@ -201,8 +200,7 @@ static bool is_local(char *address, bool any_allowed) host = host_create_from_dns(token, 0, 0); if (host) { - if (hydra->kernel_interface->get_interface( - hydra->kernel_interface, host, NULL)) + if (charon->kernel->get_interface(charon->kernel, host, NULL)) { found = TRUE; } @@ -313,117 +311,6 @@ static void build_crl_policy(auth_cfg_t *cfg, bool local, int policy) } /** - * Parse public key / signature strength constraints - */ -static void parse_pubkey_constraints(char *auth, auth_cfg_t *cfg) -{ - enumerator_t *enumerator; - bool rsa = FALSE, ecdsa = FALSE, bliss = FALSE, - rsa_len = FALSE, ecdsa_len = FALSE, bliss_strength = FALSE; - int strength; - char *token; - - enumerator = enumerator_create_token(auth, "-", ""); - while (enumerator->enumerate(enumerator, &token)) - { - bool found = FALSE; - int i; - struct { - char *name; - signature_scheme_t scheme; - key_type_t key; - } schemes[] = { - { "md5", SIGN_RSA_EMSA_PKCS1_MD5, KEY_RSA, }, - { "sha1", SIGN_RSA_EMSA_PKCS1_SHA1, KEY_RSA, }, - { "sha224", SIGN_RSA_EMSA_PKCS1_SHA224, KEY_RSA, }, - { "sha256", SIGN_RSA_EMSA_PKCS1_SHA256, KEY_RSA, }, - { "sha384", SIGN_RSA_EMSA_PKCS1_SHA384, KEY_RSA, }, - { "sha512", SIGN_RSA_EMSA_PKCS1_SHA512, KEY_RSA, }, - { "sha1", SIGN_ECDSA_WITH_SHA1_DER, KEY_ECDSA, }, - { "sha256", SIGN_ECDSA_WITH_SHA256_DER, KEY_ECDSA, }, - { "sha384", SIGN_ECDSA_WITH_SHA384_DER, KEY_ECDSA, }, - { "sha512", SIGN_ECDSA_WITH_SHA512_DER, KEY_ECDSA, }, - { "sha256", SIGN_ECDSA_256, KEY_ECDSA, }, - { "sha384", SIGN_ECDSA_384, KEY_ECDSA, }, - { "sha512", SIGN_ECDSA_521, KEY_ECDSA, }, - { "sha256", SIGN_BLISS_WITH_SHA2_256, KEY_BLISS, }, - { "sha384", SIGN_BLISS_WITH_SHA2_384, KEY_BLISS, }, - { "sha512", SIGN_BLISS_WITH_SHA2_512, KEY_BLISS, }, - }; - - if (rsa_len || ecdsa_len || bliss_strength) - { /* expecting a key strength token */ - strength = atoi(token); - if (strength) - { - if (rsa_len) - { - cfg->add(cfg, AUTH_RULE_RSA_STRENGTH, (uintptr_t)strength); - } - else if (ecdsa_len) - { - cfg->add(cfg, AUTH_RULE_ECDSA_STRENGTH, (uintptr_t)strength); - } - else if (bliss_strength) - { - cfg->add(cfg, AUTH_RULE_BLISS_STRENGTH, (uintptr_t)strength); - } - } - rsa_len = ecdsa_len = bliss_strength = FALSE; - if (strength) - { - continue; - } - } - if (streq(token, "rsa")) - { - rsa = rsa_len = TRUE; - continue; - } - if (streq(token, "ecdsa")) - { - ecdsa = ecdsa_len = TRUE; - continue; - } - if (streq(token, "bliss")) - { - bliss = bliss_strength = TRUE; - continue; - } - if (streq(token, "pubkey")) - { - continue; - } - - for (i = 0; i < countof(schemes); i++) - { - if (streq(schemes[i].name, token)) - { - /* for each matching string, allow the scheme, if: - * - it is an RSA scheme, and we enforced RSA - * - it is an ECDSA scheme, and we enforced ECDSA - * - it is not a key type specific scheme - */ - if ((rsa && schemes[i].key == KEY_RSA) || - (ecdsa && schemes[i].key == KEY_ECDSA) || - (bliss && schemes[i].key == KEY_BLISS) || - (!rsa && !ecdsa && !bliss)) - { - cfg->add(cfg, AUTH_RULE_SIGNATURE_SCHEME, - (uintptr_t)schemes[i].scheme); - } - found = TRUE; - } - } - if (!found) - { - DBG1(DBG_CFG, "ignoring invalid auth token: '%s'", token); - } - } - enumerator->destroy(enumerator); -} - -/** * build authentication config */ static auth_cfg_t *build_auth_cfg(private_stroke_config_t *this, @@ -619,15 +506,15 @@ static auth_cfg_t *build_auth_cfg(private_stroke_config_t *this, } /* authentication metod (class, actually) */ - if (strpfx(auth, "pubkey") || + if (strpfx(auth, "ike:") || + strpfx(auth, "pubkey") || strpfx(auth, "rsa") || strpfx(auth, "ecdsa") || strpfx(auth, "bliss")) { cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY); build_crl_policy(cfg, local, msg->add_conn.crl_policy); - - parse_pubkey_constraints(auth, cfg); + cfg->add_pubkey_constraints(cfg, auth, TRUE); } else if (streq(auth, "psk") || streq(auth, "secret")) { @@ -660,7 +547,7 @@ static auth_cfg_t *build_auth_cfg(private_stroke_config_t *this, if (pos) { *pos = 0; - parse_pubkey_constraints(pos + 1, cfg); + cfg->add_pubkey_constraints(cfg, pos + 1, FALSE); } type = eap_vendor_type_from_string(auth); if (type) diff --git a/src/libcharon/plugins/stroke/stroke_control.c b/src/libcharon/plugins/stroke/stroke_control.c index 5a1a5074d..36da5ff21 100644 --- a/src/libcharon/plugins/stroke/stroke_control.c +++ b/src/libcharon/plugins/stroke/stroke_control.c @@ -16,7 +16,6 @@ #include "stroke_control.h" -#include <hydra.h> #include <daemon.h> #include <processing/jobs/delete_ike_sa_job.h> diff --git a/src/libcharon/plugins/stroke/stroke_list.c b/src/libcharon/plugins/stroke/stroke_list.c index c0192b5c0..0371c7032 100644 --- a/src/libcharon/plugins/stroke/stroke_list.c +++ b/src/libcharon/plugins/stroke/stroke_list.c @@ -2,6 +2,9 @@ * Copyright (C) 2008 Martin Willi * Hochschule fuer Technik Rapperswil * + * Copyright (C) 2015 Andreas Steffen + * HSR Hochschule fuer Technik Rapperswil + * * 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 @@ -23,22 +26,12 @@ #include <malloc.h> #endif /* HAVE_MALLINFO */ -#include <hydra.h> #include <daemon.h> #include <collections/linked_list.h> #include <plugins/plugin.h> #include <credentials/certificates/x509.h> -#include <credentials/certificates/ac.h> -#include <credentials/certificates/crl.h> -#include <credentials/certificates/pgp_certificate.h> +#include <credentials/certificates/certificate_printer.h> #include <config/peer_cfg.h> -#include <asn1/asn1.h> -#include <asn1/oid.h> - -/* warning intervals for list functions */ -#define CERT_WARNING_INTERVAL 30 /* days */ -#define CRL_WARNING_INTERVAL 7 /* days */ -#define AC_WARNING_INTERVAL 1 /* day */ typedef struct private_stroke_list_t private_stroke_list_t; @@ -69,6 +62,11 @@ struct private_stroke_list_t { }; /** + * Static certificate printer object + */ +static certificate_printer_t *cert_printer = NULL; + +/** * Log tasks of a specific queue to out */ static void log_task_q(FILE *out, ike_sa_t *ike_sa, task_queue_t q, char *name) @@ -139,8 +137,10 @@ static void log_ike_sa(FILE *out, ike_sa_t *ike_sa, bool all) fprintf(out, "%12s[%d]: %N SPIs: %.16"PRIx64"_i%s %.16"PRIx64"_r%s", ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa), ike_version_names, ike_sa->get_version(ike_sa), - id->get_initiator_spi(id), id->is_initiator(id) ? "*" : "", - id->get_responder_spi(id), id->is_initiator(id) ? "" : "*"); + be64toh(id->get_initiator_spi(id)), + id->is_initiator(id) ? "*" : "", + be64toh(id->get_responder_spi(id)), + id->is_initiator(id) ? "" : "*"); if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED) @@ -244,40 +244,36 @@ static void log_child_sa(FILE *out, child_sa_t *child_sa, bool all) proposal = child_sa->get_proposal(child_sa); if (proposal) { - u_int16_t encr_alg = ENCR_UNDEFINED, int_alg = AUTH_UNDEFINED; - u_int16_t encr_size = 0, int_size = 0; - u_int16_t esn = NO_EXT_SEQ_NUMBERS; + u_int16_t alg, ks; bool first = TRUE; - proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, - &encr_alg, &encr_size); - proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, - &int_alg, &int_size); - proposal->get_algorithm(proposal, EXTENDED_SEQUENCE_NUMBERS, - &esn, NULL); - - if (encr_alg != ENCR_UNDEFINED) + if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, + &alg, &ks) && alg != ENCR_UNDEFINED) { - fprintf(out, "%N", encryption_algorithm_names, encr_alg); + fprintf(out, "%N", encryption_algorithm_names, alg); first = FALSE; - if (encr_size) + if (ks) { - fprintf(out, "_%u", encr_size); + fprintf(out, "_%u", ks); } } - if (int_alg != AUTH_UNDEFINED) + if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, + &alg, &ks) && alg != AUTH_UNDEFINED) { - if (!first) - { - fprintf(out, "/"); - } - fprintf(out, "%N", integrity_algorithm_names, int_alg); - if (int_size) + fprintf(out, "%s%N", first ? "" : "/", + integrity_algorithm_names, alg); + if (ks) { - fprintf(out, "_%u", int_size); + fprintf(out, "_%u", ks); } } - if (esn == EXT_SEQ_NUMBERS) + if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, + &alg, NULL)) + { + fprintf(out, "/%N", diffie_hellman_group_names, alg); + } + if (proposal->get_algorithm(proposal, EXTENDED_SEQUENCE_NUMBERS, + &alg, NULL) && alg == EXT_SEQ_NUMBERS) { fprintf(out, "/ESN"); } @@ -538,8 +534,8 @@ METHOD(stroke_list_t, status, void, } enumerator->destroy(enumerator); - enumerator = hydra->kernel_interface->create_address_enumerator( - hydra->kernel_interface, ADDR_TYPE_REGULAR); + enumerator = charon->kernel->create_address_enumerator(charon->kernel, + ADDR_TYPE_REGULAR); fprintf(out, "Listening IP addresses:\n"); while (enumerator->enumerate(enumerator, (void**)&host)) { @@ -738,14 +734,20 @@ static linked_list_t* create_unique_cert_list(certificate_type_t type) } /** - * Print a single public key. + * Is there a matching private key? */ -static void list_public_key(public_key_t *public, FILE *out) +static bool has_privkey(certificate_t *cert) { + public_key_t *public; private_key_t *private = NULL; chunk_t keyid; identification_t *id; + public = cert->get_public_key(cert); + if (!public) + { + return FALSE; + } if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &keyid)) { id = identification_create_from_encoding(ID_KEY_ID, keyid); @@ -753,521 +755,56 @@ static void list_public_key(public_key_t *public, FILE *out) public->get_type(public), id, NULL); id->destroy(id); } - - fprintf(out, " pubkey: %N %d bits%s\n", - key_type_names, public->get_type(public), - public->get_keysize(public), - private ? ", has private key" : ""); - if (public->get_fingerprint(public, KEYID_PUBKEY_INFO_SHA1, &keyid)) - { - fprintf(out, " keyid: %#B\n", &keyid); - } - if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &keyid)) - { - fprintf(out, " subjkey: %#B\n", &keyid); - } + public->destroy(public); DESTROY_IF(private); -} - -/** - * list all raw public keys - */ -static void stroke_list_pubkeys(linked_list_t *list, bool utc, FILE *out) -{ - bool first = TRUE; - time_t now = time(NULL), notBefore, notAfter; - enumerator_t *enumerator; - certificate_t *cert; - - enumerator = list->create_enumerator(list); - while (enumerator->enumerate(enumerator, (void**)&cert)) - { - identification_t *subject = cert->get_subject(cert); - public_key_t *public = cert->get_public_key(cert); - - if (public) - { - if (first) - { - fprintf(out, "\n"); - fprintf(out, "List of Raw Public Keys:\n"); - first = FALSE; - } - fprintf(out, "\n"); - - /* list subject if available */ - if (subject->get_type(subject) != ID_KEY_ID) - { - fprintf(out, " subject: %#Y\n", subject); - } - - /* list validity if available*/ - cert->get_validity(cert, &now, ¬Before, ¬After); - if (notBefore != UNDEFINED_TIME && notAfter != UNDEFINED_TIME) - { - fprintf(out, " validity: not before %T, ", ¬Before, utc); - if (now < notBefore) - { - fprintf(out, "not valid yet (valid in %V)\n", &now, ¬Before); - } - else - { - fprintf(out, "ok\n"); - } - fprintf(out, " not after %T, ", ¬After, utc); - if (now > notAfter) - { - fprintf(out, "expired (%V ago)\n", &now, ¬After); - } - else - { - fprintf(out, "ok"); - if (now > notAfter - CERT_WARNING_INTERVAL * 60 * 60 * 24) - { - fprintf(out, " (expires in %V)", &now, ¬After); - } - fprintf(out, " \n"); - } - } - - list_public_key(public, out); - public->destroy(public); - } - } - enumerator->destroy(enumerator); -} - -/** - * list OpenPGP certificates - */ -static void stroke_list_pgp(linked_list_t *list,bool utc, FILE *out) -{ - bool first = TRUE; - time_t now = time(NULL); - enumerator_t *enumerator = list->create_enumerator(list); - certificate_t *cert; - - while (enumerator->enumerate(enumerator, (void**)&cert)) - { - time_t created, until; - public_key_t *public; - pgp_certificate_t *pgp_cert = (pgp_certificate_t*)cert; - chunk_t fingerprint = pgp_cert->get_fingerprint(pgp_cert); - - if (first) - { - fprintf(out, "\n"); - fprintf(out, "List of PGP End Entity Certificates:\n"); - first = FALSE; - } - fprintf(out, "\n"); - fprintf(out, " userid: '%Y'\n", cert->get_subject(cert)); - - fprintf(out, " digest: %#B\n", &fingerprint); - - /* list validity */ - cert->get_validity(cert, &now, &created, &until); - fprintf(out, " created: %T\n", &created, utc); - fprintf(out, " until: %T%s\n", &until, utc, - (until == TIME_32_BIT_SIGNED_MAX) ? " (expires never)":""); - - public = cert->get_public_key(cert); - if (public) - { - list_public_key(public, out); - public->destroy(public); - } - } - enumerator->destroy(enumerator); + return (private != NULL); } /** * list all X.509 certificates matching the flags */ -static void stroke_list_certs(linked_list_t *list, char *label, - x509_flag_t flags, bool utc, FILE *out) +static void stroke_list_x509_certs(linked_list_t *list, x509_flag_t flag) { - bool first = TRUE; - time_t now = time(NULL); enumerator_t *enumerator; certificate_t *cert; - x509_flag_t flag_mask; - - /* mask all auxiliary flags */ - flag_mask = ~(X509_SERVER_AUTH | X509_CLIENT_AUTH | X509_IKE_INTERMEDIATE | - X509_SELF_SIGNED | X509_IP_ADDR_BLOCKS); enumerator = list->create_enumerator(list); while (enumerator->enumerate(enumerator, (void**)&cert)) { x509_t *x509 = (x509_t*)cert; - x509_flag_t x509_flags = x509->get_flags(x509) & flag_mask; + x509_flag_t flags = x509->get_flags(x509) & X509_ANY; /* list only if flag is set or flag == 0 */ - if ((x509_flags & flags) || (x509_flags == flags)) + if ((flags & flag) || flags == flag) { - enumerator_t *enumerator; - identification_t *altName; - bool first_altName = TRUE; - u_int pathlen; - chunk_t serial, authkey; - time_t notBefore, notAfter; - public_key_t *public; - - if (first) - { - fprintf(out, "\n"); - fprintf(out, "List of %s:\n", label); - first = FALSE; - } - fprintf(out, "\n"); - - /* list subjectAltNames */ - enumerator = x509->create_subjectAltName_enumerator(x509); - while (enumerator->enumerate(enumerator, (void**)&altName)) - { - if (first_altName) - { - fprintf(out, " altNames: "); - first_altName = FALSE; - } - else - { - fprintf(out, ", "); - } - fprintf(out, "%Y", altName); - } - if (!first_altName) - { - fprintf(out, "\n"); - } - enumerator->destroy(enumerator); - - fprintf(out, " subject: \"%Y\"\n", cert->get_subject(cert)); - fprintf(out, " issuer: \"%Y\"\n", cert->get_issuer(cert)); - serial = chunk_skip_zero(x509->get_serial(x509)); - fprintf(out, " serial: %#B\n", &serial); - - /* list validity */ - cert->get_validity(cert, &now, ¬Before, ¬After); - fprintf(out, " validity: not before %T, ", ¬Before, utc); - if (now < notBefore) - { - fprintf(out, "not valid yet (valid in %V)\n", &now, ¬Before); - } - else - { - fprintf(out, "ok\n"); - } - fprintf(out, " not after %T, ", ¬After, utc); - if (now > notAfter) - { - fprintf(out, "expired (%V ago)\n", &now, ¬After); - } - else - { - fprintf(out, "ok"); - if (now > notAfter - CERT_WARNING_INTERVAL * 60 * 60 * 24) - { - fprintf(out, " (expires in %V)", &now, ¬After); - } - fprintf(out, " \n"); - } - - public = cert->get_public_key(cert); - if (public) - { - list_public_key(public, out); - public->destroy(public); - } - - /* list optional authorityKeyIdentifier */ - authkey = x509->get_authKeyIdentifier(x509); - if (authkey.ptr) - { - fprintf(out, " authkey: %#B\n", &authkey); - } - - /* list optional pathLenConstraint */ - pathlen = x509->get_constraint(x509, X509_PATH_LEN); - if (pathlen != X509_NO_CONSTRAINT) - { - fprintf(out, " pathlen: %u\n", pathlen); - } - - /* list optional ipAddrBlocks */ - if (x509->get_flags(x509) & X509_IP_ADDR_BLOCKS) - { - traffic_selector_t *ipAddrBlock; - bool first_ipAddrBlock = TRUE; - - fprintf(out, " addresses: "); - enumerator = x509->create_ipAddrBlock_enumerator(x509); - while (enumerator->enumerate(enumerator, &ipAddrBlock)) - { - if (first_ipAddrBlock) - { - first_ipAddrBlock = FALSE; - } - else - { - fprintf(out, ", "); - } - fprintf(out, "%R", ipAddrBlock); - } - enumerator->destroy(enumerator); - fprintf(out, "\n"); - } + cert_printer->print_caption(cert_printer, CERT_X509, flag); + cert_printer->print(cert_printer, cert, has_privkey(cert)); } } enumerator->destroy(enumerator); } /** - * list all X.509 attribute certificates + * list all other certificates types */ -static void stroke_list_acerts(linked_list_t *list, bool utc, FILE *out) +static void stroke_list_other_certs(certificate_type_t type) { - bool first = TRUE; - time_t notBefore, notAfter, now = time(NULL); enumerator_t *enumerator; certificate_t *cert; + linked_list_t *list; + + list = create_unique_cert_list(type); enumerator = list->create_enumerator(list); while (enumerator->enumerate(enumerator, &cert)) { - ac_t *ac = (ac_t*)cert; - ac_group_type_t type; - identification_t *id; - enumerator_t *groups; - chunk_t chunk; - bool firstgroup = TRUE; - - if (first) - { - fprintf(out, "\n"); - fprintf(out, "List of X.509 Attribute Certificates:\n"); - first = FALSE; - } - fprintf(out, "\n"); - - id = cert->get_subject(cert); - if (id) - { - fprintf(out, " holder: \"%Y\"\n", id); - } - id = ac->get_holderIssuer(ac); - if (id) - { - fprintf(out, " hissuer: \"%Y\"\n", id); - } - chunk = chunk_skip_zero(ac->get_holderSerial(ac)); - if (chunk.ptr) - { - fprintf(out, " hserial: %#B\n", &chunk); - } - groups = ac->create_group_enumerator(ac); - while (groups->enumerate(groups, &type, &chunk)) - { - int oid; - char *str; - - if (firstgroup) - { - fprintf(out, " groups: "); - firstgroup = FALSE; - } - else - { - fprintf(out, " "); - } - switch (type) - { - case AC_GROUP_TYPE_STRING: - fprintf(out, "%.*s", (int)chunk.len, chunk.ptr); - break; - case AC_GROUP_TYPE_OID: - oid = asn1_known_oid(chunk); - if (oid == OID_UNKNOWN) - { - str = asn1_oid_to_string(chunk); - if (str) - { - fprintf(out, "%s", str); - free(str); - } - else - { - fprintf(out, "OID:%#B", &chunk); - } - } - else - { - fprintf(out, "%s", oid_names[oid].name); - } - break; - case AC_GROUP_TYPE_OCTETS: - fprintf(out, "%#B", &chunk); - break; - } - fprintf(out, "\n"); - } - groups->destroy(groups); - fprintf(out, " issuer: \"%Y\"\n", cert->get_issuer(cert)); - chunk = chunk_skip_zero(ac->get_serial(ac)); - fprintf(out, " serial: %#B\n", &chunk); - - /* list validity */ - cert->get_validity(cert, &now, ¬Before, ¬After); - fprintf(out, " validity: not before %T, ", ¬Before, utc); - if (now < notBefore) - { - fprintf(out, "not valid yet (valid in %V)\n", &now, ¬Before); - } - else - { - fprintf(out, "ok\n"); - } - fprintf(out, " not after %T, ", ¬After, utc); - if (now > notAfter) - { - fprintf(out, "expired (%V ago)\n", &now, ¬After); - } - else - { - fprintf(out, "ok"); - if (now > notAfter - AC_WARNING_INTERVAL * 60 * 60 * 24) - { - fprintf(out, " (expires in %V)", &now, ¬After); - } - fprintf(out, " \n"); - } - - /* list optional authorityKeyIdentifier */ - chunk = ac->get_authKeyIdentifier(ac); - if (chunk.ptr) - { - fprintf(out, " authkey: %#B\n", &chunk); - } + cert_printer->print_caption(cert_printer, cert->get_type(cert), X509_NONE); + cert_printer->print(cert_printer, cert, has_privkey(cert)); } enumerator->destroy(enumerator); -} - -/** - * list all X.509 CRLs - */ -static void stroke_list_crls(linked_list_t *list, bool utc, FILE *out) -{ - bool first = TRUE; - time_t thisUpdate, nextUpdate, now = time(NULL); - enumerator_t *enumerator = list->create_enumerator(list); - certificate_t *cert; - while (enumerator->enumerate(enumerator, (void**)&cert)) - { - crl_t *crl = (crl_t*)cert; - chunk_t chunk; - - if (first) - { - fprintf(out, "\n"); - fprintf(out, "List of X.509 CRLs:\n"); - first = FALSE; - } - fprintf(out, "\n"); - - fprintf(out, " issuer: \"%Y\"\n", cert->get_issuer(cert)); - - /* list optional crlNumber */ - chunk = chunk_skip_zero(crl->get_serial(crl)); - if (chunk.ptr) - { - fprintf(out, " serial: %#B\n", &chunk); - } - if (crl->is_delta_crl(crl, &chunk)) - { - chunk = chunk_skip_zero(chunk); - fprintf(out, " delta for: %#B\n", &chunk); - } - - /* count the number of revoked certificates */ - { - int count = 0; - enumerator_t *enumerator = crl->create_enumerator(crl); - - while (enumerator->enumerate(enumerator, NULL, NULL, NULL)) - { - count++; - } - fprintf(out, " revoked: %d certificate%s\n", count, - (count == 1)? "" : "s"); - enumerator->destroy(enumerator); - } - - /* list validity */ - cert->get_validity(cert, &now, &thisUpdate, &nextUpdate); - fprintf(out, " updates: this %T\n", &thisUpdate, utc); - fprintf(out, " next %T, ", &nextUpdate, utc); - if (now > nextUpdate) - { - fprintf(out, "expired (%V ago)\n", &now, &nextUpdate); - } - else - { - fprintf(out, "ok"); - if (now > nextUpdate - CRL_WARNING_INTERVAL * 60 * 60 * 24) - { - fprintf(out, " (expires in %V)", &now, &nextUpdate); - } - fprintf(out, " \n"); - } - - /* list optional authorityKeyIdentifier */ - chunk = crl->get_authKeyIdentifier(crl); - if (chunk.ptr) - { - fprintf(out, " authkey: %#B\n", &chunk); - } - } - enumerator->destroy(enumerator); -} - -/** - * list all OCSP responses - */ -static void stroke_list_ocsp(linked_list_t* list, bool utc, FILE *out) -{ - bool first = TRUE, ok; - enumerator_t *enumerator = list->create_enumerator(list); - certificate_t *cert; - time_t produced, usable, now = time(NULL); - - while (enumerator->enumerate(enumerator, (void**)&cert)) - { - if (first) - { - fprintf(out, "\n"); - fprintf(out, "List of OCSP responses:\n"); - fprintf(out, "\n"); - first = FALSE; - } - fprintf(out, " signer: \"%Y\"\n", cert->get_issuer(cert)); - - /* check validity */ - ok = cert->get_validity(cert, &now, &produced, &usable); - fprintf(out, " validity: produced at %T\n", &produced, utc); - fprintf(out, " usable till %T, ", &usable, utc); - if (ok) - { - fprintf(out, "ok\n"); - } - else - { - fprintf(out, "expired (%V ago)\n", &now, &usable); - } - } - enumerator->destroy(enumerator); + list->destroy_offset(list, offsetof(certificate_t, destroy)); } /** @@ -1439,19 +976,15 @@ METHOD(stroke_list_t, list, void, { linked_list_t *cert_list = NULL; + cert_printer = certificate_printer_create(out, TRUE, msg->list.utc); + if (msg->list.flags & LIST_PUBKEYS) { - linked_list_t *pubkey_list = create_unique_cert_list(CERT_TRUSTED_PUBKEY); - - stroke_list_pubkeys(pubkey_list, msg->list.utc, out); - pubkey_list->destroy_offset(pubkey_list, offsetof(certificate_t, destroy)); + stroke_list_other_certs(CERT_TRUSTED_PUBKEY); } if (msg->list.flags & LIST_CERTS) { - linked_list_t *pgp_list = create_unique_cert_list(CERT_GPG); - - stroke_list_pgp(pgp_list, msg->list.utc, out); - pgp_list->destroy_offset(pgp_list, offsetof(certificate_t, destroy)); + stroke_list_other_certs(CERT_GPG); } if (msg->list.flags & (LIST_CERTS | LIST_CACERTS | LIST_OCSPCERTS | LIST_AACERTS)) { @@ -1459,47 +992,33 @@ METHOD(stroke_list_t, list, void, } if (msg->list.flags & LIST_CERTS) { - stroke_list_certs(cert_list, "X.509 End Entity Certificates", - X509_NONE, msg->list.utc, out); + stroke_list_x509_certs(cert_list, X509_NONE); } if (msg->list.flags & LIST_CACERTS) { - stroke_list_certs(cert_list, "X.509 CA Certificates", - X509_CA, msg->list.utc, out); + stroke_list_x509_certs(cert_list, X509_CA); } if (msg->list.flags & LIST_OCSPCERTS) { - stroke_list_certs(cert_list, "X.509 OCSP Signer Certificates", - X509_OCSP_SIGNER, msg->list.utc, out); + stroke_list_x509_certs(cert_list, X509_OCSP_SIGNER); } if (msg->list.flags & LIST_AACERTS) { - stroke_list_certs(cert_list, "X.509 AA Certificates", - X509_AA, msg->list.utc, out); + stroke_list_x509_certs(cert_list, X509_AA); } DESTROY_OFFSET_IF(cert_list, offsetof(certificate_t, destroy)); if (msg->list.flags & LIST_ACERTS) { - linked_list_t *ac_list = create_unique_cert_list(CERT_X509_AC); - - stroke_list_acerts(ac_list, msg->list.utc, out); - ac_list->destroy_offset(ac_list, offsetof(certificate_t, destroy)); + stroke_list_other_certs(CERT_X509_AC); } if (msg->list.flags & LIST_CRLS) { - linked_list_t *crl_list = create_unique_cert_list(CERT_X509_CRL); - - stroke_list_crls(crl_list, msg->list.utc, out); - crl_list->destroy_offset(crl_list, offsetof(certificate_t, destroy)); + stroke_list_other_certs(CERT_X509_CRL); } if (msg->list.flags & LIST_OCSP) { - linked_list_t *ocsp_list = create_unique_cert_list(CERT_X509_OCSP_RESPONSE); - - stroke_list_ocsp(ocsp_list, msg->list.utc, out); - - ocsp_list->destroy_offset(ocsp_list, offsetof(certificate_t, destroy)); + stroke_list_other_certs(CERT_X509_OCSP_RESPONSE); } if (msg->list.flags & LIST_ALGS) { @@ -1509,6 +1028,8 @@ METHOD(stroke_list_t, list, void, { list_plugins(out); } + cert_printer->destroy(cert_printer); + cert_printer = NULL; } /** diff --git a/src/libcharon/plugins/stroke/stroke_socket.c b/src/libcharon/plugins/stroke/stroke_socket.c index 29563e32f..ee32dbca2 100644 --- a/src/libcharon/plugins/stroke/stroke_socket.c +++ b/src/libcharon/plugins/stroke/stroke_socket.c @@ -590,17 +590,10 @@ static void stroke_loglevel(private_stroke_socket_t *this, fprintf(out, "command not allowed!\n"); return; } - if (strcaseeq(msg->loglevel.type, "any")) + if (!enum_from_name(debug_names, msg->loglevel.type, &group)) { - group = DBG_ANY; - } - else - { - if (!enum_from_name(debug_names, msg->loglevel.type, &group)) - { - fprintf(out, "unknown type '%s'!\n", msg->loglevel.type); - return; - } + fprintf(out, "unknown type '%s'!\n", msg->loglevel.type); + return; } charon->set_level(charon, group, msg->loglevel.level); } diff --git a/src/libcharon/plugins/systime_fix/Makefile.am b/src/libcharon/plugins/systime_fix/Makefile.am index 40a346440..95a33230f 100644 --- a/src/libcharon/plugins/systime_fix/Makefile.am +++ b/src/libcharon/plugins/systime_fix/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon if MONOLITHIC diff --git a/src/libcharon/plugins/systime_fix/Makefile.in b/src/libcharon/plugins/systime_fix/Makefile.in index be148b6c3..0daff4434 100644 --- a/src/libcharon/plugins/systime_fix/Makefile.in +++ b/src/libcharon/plugins/systime_fix/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 @MONOLITHIC_TRUE@noinst_LTLIBRARIES = libstrongswan-systime-fix.la diff --git a/src/libcharon/plugins/tnc_ifmap/Makefile.am b/src/libcharon/plugins/tnc_ifmap/Makefile.am index 90fbf4651..dab98129d 100644 --- a/src/libcharon/plugins/tnc_ifmap/Makefile.am +++ b/src/libcharon/plugins/tnc_ifmap/Makefile.am @@ -1,7 +1,6 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ -I$(top_srcdir)/src/libtls \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/tnc_ifmap/Makefile.in b/src/libcharon/plugins/tnc_ifmap/Makefile.in index 17cc341c5..f124a1b38 100644 --- a/src/libcharon/plugins/tnc_ifmap/Makefile.in +++ b/src/libcharon/plugins/tnc_ifmap/Makefile.in @@ -419,6 +419,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -435,7 +437,6 @@ xml_LIBS = @xml_LIBS@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ -I$(top_srcdir)/src/libtls \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_listener.c b/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_listener.c index d2ba2e345..2bad4fab0 100644 --- a/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_listener.c +++ b/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_listener.c @@ -18,7 +18,6 @@ #include "tnc_ifmap_renew_session_job.h" #include <daemon.h> -#include <hydra.h> #include <utils/debug.h> #define IFMAP_RENEW_SESSION_INTERVAL 150 @@ -51,8 +50,8 @@ static bool publish_device_ip_addresses(private_tnc_ifmap_listener_t *this) host_t *host; bool success = TRUE; - enumerator = hydra->kernel_interface->create_address_enumerator( - hydra->kernel_interface, ADDR_TYPE_REGULAR); + enumerator = charon->kernel->create_address_enumerator(charon->kernel, + ADDR_TYPE_REGULAR); while (enumerator->enumerate(enumerator, &host)) { if (!this->ifmap->publish_device_ip(this->ifmap, host)) diff --git a/src/libcharon/plugins/tnc_pdp/Makefile.am b/src/libcharon/plugins/tnc_pdp/Makefile.am index 3478c5b30..fcda7d76f 100644 --- a/src/libcharon/plugins/tnc_pdp/Makefile.am +++ b/src/libcharon/plugins/tnc_pdp/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -I$(top_srcdir)/src/libradius \ -I$(top_srcdir)/src/libtncif \ diff --git a/src/libcharon/plugins/tnc_pdp/Makefile.in b/src/libcharon/plugins/tnc_pdp/Makefile.in index ef05275b7..bfd8cf820 100644 --- a/src/libcharon/plugins/tnc_pdp/Makefile.in +++ b/src/libcharon/plugins/tnc_pdp/Makefile.in @@ -420,6 +420,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -435,7 +437,6 @@ 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 \ -I$(top_srcdir)/src/libradius \ -I$(top_srcdir)/src/libtncif \ diff --git a/src/libcharon/plugins/uci/Makefile.am b/src/libcharon/plugins/uci/Makefile.am index 134ced0e3..296c8db04 100644 --- a/src/libcharon/plugins/uci/Makefile.am +++ b/src/libcharon/plugins/uci/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/uci/Makefile.in b/src/libcharon/plugins/uci/Makefile.in index 2c031383a..a1c64ca1b 100644 --- a/src/libcharon/plugins/uci/Makefile.in +++ b/src/libcharon/plugins/uci/Makefile.in @@ -414,6 +414,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -429,7 +431,6 @@ 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 = \ diff --git a/src/libcharon/plugins/unity/Makefile.am b/src/libcharon/plugins/unity/Makefile.am index 38923e068..1244cb317 100644 --- a/src/libcharon/plugins/unity/Makefile.am +++ b/src/libcharon/plugins/unity/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/unity/Makefile.in b/src/libcharon/plugins/unity/Makefile.in index 4f0a7e736..00bb1498c 100644 --- a/src/libcharon/plugins/unity/Makefile.in +++ b/src/libcharon/plugins/unity/Makefile.in @@ -415,6 +415,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -430,7 +432,6 @@ 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 = \ diff --git a/src/libcharon/plugins/updown/Makefile.am b/src/libcharon/plugins/updown/Makefile.am index f03f4744c..f8738adee 100644 --- a/src/libcharon/plugins/updown/Makefile.am +++ b/src/libcharon/plugins/updown/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/updown/Makefile.in b/src/libcharon/plugins/updown/Makefile.in index 619d17a0e..863e14430 100644 --- a/src/libcharon/plugins/updown/Makefile.in +++ b/src/libcharon/plugins/updown/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 = \ diff --git a/src/libcharon/plugins/updown/updown_listener.c b/src/libcharon/plugins/updown/updown_listener.c index 96282bee0..e51caab10 100644 --- a/src/libcharon/plugins/updown/updown_listener.c +++ b/src/libcharon/plugins/updown/updown_listener.c @@ -1,7 +1,8 @@ /* * Copyright (C) 2013 Tobias Brunner * Copyright (C) 2008 Martin Willi - * Hochschule fuer Technik Rapperswil + * Copyright (C) 2016 Andreas Steffen + * HSR Hochschule fuer Technik Rapperswil * * 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 @@ -21,7 +22,6 @@ #include "updown_listener.h" #include <utils/process.h> -#include <hydra.h> #include <daemon.h> #include <config/child_cfg.h> @@ -205,25 +205,47 @@ static void push_vip_env(private_updown_listener_t *this, ike_sa_t *ike_sa, enumerator->destroy(enumerator); } +#define PORT_BUF_LEN 12 + /** * Determine proper values for port env variable */ -static u_int16_t get_port(traffic_selector_t *me, - traffic_selector_t *other, bool local) +static char* get_port(traffic_selector_t *me, traffic_selector_t *other, + char *port_buf, bool local) { + uint16_t port, to, from; + switch (max(me->get_protocol(me), other->get_protocol(other))) { case IPPROTO_ICMP: case IPPROTO_ICMPV6: { - u_int16_t port = me->get_from_port(me); - - port = max(port, other->get_from_port(other)); - return local ? traffic_selector_icmp_type(port) - : traffic_selector_icmp_code(port); + port = max(me->get_from_port(me), other->get_from_port(other)); + snprintf(port_buf, PORT_BUF_LEN, "%u", + local ? traffic_selector_icmp_type(port) + : traffic_selector_icmp_code(port)); + return port_buf; } } - return local ? me->get_from_port(me) : other->get_from_port(other); + if (local) + { + from = me->get_from_port(me); + to = me->get_to_port(me); + } + else + { + from = other->get_from_port(other); + to = other->get_to_port(other); + } + if (from == to || (from == 0 && to == 65535)) + { + snprintf(port_buf, PORT_BUF_LEN, "%u", from); + } + else + { + snprintf(port_buf, PORT_BUF_LEN, "%u:%u", from, to); + } + return port_buf; } /** @@ -241,6 +263,7 @@ static void invoke_once(private_updown_listener_t *this, ike_sa_t *ike_sa, int out; FILE *shell; process_t *process; + char port_buf[PORT_BUF_LEN]; char *envp[128] = {}; me = ike_sa->get_my_host(ike_sa); @@ -265,8 +288,7 @@ static void invoke_once(private_updown_listener_t *this, ike_sa_t *ike_sa, config->get_name(config)); if (up) { - if (hydra->kernel_interface->get_interface(hydra->kernel_interface, - me, &iface)) + if (charon->kernel->get_interface(charon->kernel, me, &iface)) { cache_iface(this, child_sa->get_reqid(child_sa), iface); } @@ -289,25 +311,29 @@ static void invoke_once(private_updown_listener_t *this, ike_sa_t *ike_sa, ike_sa->get_unique_id(ike_sa)); push_env(envp, countof(envp), "PLUTO_ME=%H", me); push_env(envp, countof(envp), "PLUTO_MY_ID=%Y", ike_sa->get_my_id(ike_sa)); - if (my_ts->to_subnet(my_ts, &host, &mask)) + if (!my_ts->to_subnet(my_ts, &host, &mask)) { - push_env(envp, countof(envp), "PLUTO_MY_CLIENT=%+H/%u", host, mask); - host->destroy(host); + DBG1(DBG_CHD, "updown approximates local TS %R " + "by next larger subnet", my_ts); } - push_env(envp, countof(envp), "PLUTO_MY_PORT=%u", - get_port(my_ts, other_ts, TRUE)); + push_env(envp, countof(envp), "PLUTO_MY_CLIENT=%+H/%u", host, mask); + host->destroy(host); + push_env(envp, countof(envp), "PLUTO_MY_PORT=%s", + get_port(my_ts, other_ts, port_buf, TRUE)); push_env(envp, countof(envp), "PLUTO_MY_PROTOCOL=%u", my_ts->get_protocol(my_ts)); push_env(envp, countof(envp), "PLUTO_PEER=%H", other); push_env(envp, countof(envp), "PLUTO_PEER_ID=%Y", ike_sa->get_other_id(ike_sa)); - if (other_ts->to_subnet(other_ts, &host, &mask)) + if (!other_ts->to_subnet(other_ts, &host, &mask)) { - push_env(envp, countof(envp), "PLUTO_PEER_CLIENT=%+H/%u", host, mask); - host->destroy(host); + DBG1(DBG_CHD, "updown approximates remote TS %R " + "by next larger subnet", other_ts); } - push_env(envp, countof(envp), "PLUTO_PEER_PORT=%u", - get_port(my_ts, other_ts, FALSE)); + push_env(envp, countof(envp), "PLUTO_PEER_CLIENT=%+H/%u", host, mask); + host->destroy(host); + push_env(envp, countof(envp), "PLUTO_PEER_PORT=%s", + get_port(my_ts, other_ts, port_buf, FALSE)); push_env(envp, countof(envp), "PLUTO_PEER_PROTOCOL=%u", other_ts->get_protocol(other_ts)); if (ike_sa->has_condition(ike_sa, COND_EAP_AUTHENTICATED) || diff --git a/src/libcharon/plugins/vici/Makefile.am b/src/libcharon/plugins/vici/Makefile.am index c99d23e4e..ca9b49906 100644 --- a/src/libcharon/plugins/vici/Makefile.am +++ b/src/libcharon/plugins/vici/Makefile.am @@ -1,6 +1,6 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ + -I$(top_srcdir)/src/libstrongswan/plugins/pubkey \ -I$(top_srcdir)/src/libcharon \ -DIPSEC_PIDDIR=\"${piddir}\" @@ -18,6 +18,7 @@ libstrongswan_vici_la_SOURCES = \ vici_message.h vici_message.c \ vici_builder.h vici_builder.c \ vici_dispatcher.h vici_dispatcher.c \ + vici_cert_info.h vici_cert_info.c \ vici_query.h vici_query.c \ vici_control.h vici_control.c \ vici_config.h vici_config.c \ @@ -38,6 +39,7 @@ ipseclib_LTLIBRARIES = libvici.la libvici_la_SOURCES = \ vici_message.c vici_message.h \ vici_builder.c vici_builder.h \ + vici_cert_info.h vici_cert_info.c \ libvici.c libvici.h libvici_la_LIBADD = $(top_builddir)/src/libstrongswan/libstrongswan.la @@ -79,3 +81,7 @@ endif if USE_PYTHON_EGGS SUBDIRS += python endif + +if USE_PERL_CPAN +SUBDIRS += perl +endif diff --git a/src/libcharon/plugins/vici/Makefile.in b/src/libcharon/plugins/vici/Makefile.in index 1a7870ae9..86ed00792 100644 --- a/src/libcharon/plugins/vici/Makefile.in +++ b/src/libcharon/plugins/vici/Makefile.in @@ -82,6 +82,7 @@ TESTS = vici_tests$(EXEEXT) check_PROGRAMS = $(am__EXEEXT_1) @USE_RUBY_GEMS_TRUE@am__append_1 = ruby @USE_PYTHON_EGGS_TRUE@am__append_2 = python +@USE_PERL_CPAN_TRUE@am__append_3 = perl subdir = src/libcharon/plugins/vici DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/depcomp @@ -134,9 +135,10 @@ LTLIBRARIES = $(ipseclib_LTLIBRARIES) $(noinst_LTLIBRARIES) \ $(plugin_LTLIBRARIES) libstrongswan_vici_la_LIBADD = am_libstrongswan_vici_la_OBJECTS = vici_socket.lo vici_message.lo \ - vici_builder.lo vici_dispatcher.lo vici_query.lo \ - vici_control.lo vici_config.lo vici_cred.lo vici_attribute.lo \ - vici_authority.lo vici_logger.lo vici_plugin.lo + vici_builder.lo vici_dispatcher.lo vici_cert_info.lo \ + vici_query.lo vici_control.lo vici_config.lo vici_cred.lo \ + vici_attribute.lo vici_authority.lo vici_logger.lo \ + vici_plugin.lo libstrongswan_vici_la_OBJECTS = $(am_libstrongswan_vici_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -150,7 +152,8 @@ libstrongswan_vici_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ @MONOLITHIC_TRUE@am_libstrongswan_vici_la_rpath = libvici_la_DEPENDENCIES = \ $(top_builddir)/src/libstrongswan/libstrongswan.la -am_libvici_la_OBJECTS = vici_message.lo vici_builder.lo libvici.lo +am_libvici_la_OBJECTS = vici_message.lo vici_builder.lo \ + vici_cert_info.lo libvici.lo libvici_la_OBJECTS = $(am_libvici_la_OBJECTS) am__EXEEXT_1 = vici_tests$(EXEEXT) am__dirstamp = $(am__leading_dot)dirstamp @@ -270,7 +273,7 @@ am__tty_colors = { \ std='[m'; \ fi; \ } -DIST_SUBDIRS = ruby python +DIST_SUBDIRS = ruby python perl DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ @@ -509,6 +512,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -524,7 +529,7 @@ xml_CFLAGS = @xml_CFLAGS@ xml_LIBS = @xml_LIBS@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ + -I$(top_srcdir)/src/libstrongswan/plugins/pubkey \ -I$(top_srcdir)/src/libcharon \ -DIPSEC_PIDDIR=\"${piddir}\" @@ -538,6 +543,7 @@ libstrongswan_vici_la_SOURCES = \ vici_message.h vici_message.c \ vici_builder.h vici_builder.c \ vici_dispatcher.h vici_dispatcher.c \ + vici_cert_info.h vici_cert_info.c \ vici_query.h vici_query.c \ vici_control.h vici_control.c \ vici_config.h vici_config.c \ @@ -553,6 +559,7 @@ ipseclib_LTLIBRARIES = libvici.la libvici_la_SOURCES = \ vici_message.c vici_message.h \ vici_builder.c vici_builder.h \ + vici_cert_info.h vici_cert_info.c \ libvici.c libvici.h libvici_la_LIBADD = $(top_builddir)/src/libstrongswan/libstrongswan.la @@ -578,7 +585,7 @@ vici_tests_LDADD = \ $(top_builddir)/src/libstrongswan/libstrongswan.la \ $(top_builddir)/src/libstrongswan/tests/libtest.la -SUBDIRS = $(am__append_1) $(am__append_2) +SUBDIRS = $(am__append_1) $(am__append_2) $(am__append_3) all: all-recursive .SUFFIXES: @@ -739,6 +746,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_attribute.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_authority.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_builder.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_cert_info.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_config.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_control.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_cred.Plo@am__quote@ diff --git a/src/libcharon/plugins/vici/README.md b/src/libcharon/plugins/vici/README.md index b9531d8a5..52929bd74 100644 --- a/src/libcharon/plugins/vici/README.md +++ b/src/libcharon/plugins/vici/README.md @@ -258,7 +258,8 @@ Initiates an SA while streaming _control-log_ events. { child = <CHILD_SA configuration name to initiate> - timeout = <timeout in seconds before returning> + ike = <optional IKE_SA configuraiton name to find child under> + timeout = <timeout in ms before returning> init-limits = <whether limits may prevent initiating the CHILD_SA> loglevel = <loglevel to issue "control-log" events for> } => { @@ -266,6 +267,9 @@ Initiates an SA while streaming _control-log_ events. errmsg = <error string on failure or timeout> } +The default timeout of 0 waits indefinitely for a result, and a timeout value +of -1 returns a result immediately. + ### terminate() ### Terminates an SA while streaming _control-log_ events. @@ -275,19 +279,40 @@ Terminates an SA while streaming _control-log_ events. ike = <terminate an IKE_SA by configuration name> child_id = <terminate a CHILD_SA by its reqid> ike_id = <terminate an IKE_SA by its unique id> - timeout = <timeout in seconds before returning> + timeout = <timeout in ms before returning> loglevel = <loglevel to issue "control-log" events for> } => { success = <yes or no> errmsg = <error string on failure or timeout> } +The default timeout of 0 waits indefinitely for a result, and a timeout value +of -1 returns a result immediately. + +### redirect() ### + +Redirect a client-initiated IKE_SA to another gateway. Only for IKEv2 and if +supported by the peer. + + { + ike = <redirect an IKE_SA by configuration name> + ike-id = <redirect an IKE_SA by its unique id> + peer-ip = <redirect an IKE_SA with matching peer IP, may also be a + subnet in CIDR notation or an IP range> + peer-id = <redirect an IKE_SA with matching peer identity, may contain + wildcards> + } => { + success = <yes or no> + errmsg = <error string on failure> + } + ### install() ### Install a trap, drop or bypass policy defined by a CHILD_SA config. { child = <CHILD_SA configuration name to install> + ike = <optional IKE_SA configuraiton name to find child under> } => { success = <yes or no> errmsg = <error string on failure> @@ -361,7 +386,9 @@ call includes all certificates known by the daemon, not only those loaded over vici. { - type = <certificate type to filter for, or ANY> + type = <certificate type to filter for, X509|X509_AC|X509_CRL| + OCSP_RESPONSE|PUBKEY or ANY> + flag = <X.509 certificate flag to filter for, NONE|CA|AA|OCSP or ANY> subject = <set to list only certificates having subject> } => { # completes after streaming list-cert events @@ -419,7 +446,8 @@ Unload a previously loaded connection definition by name. Load a certificate into the daemon. { - type = <certificate type, X509|X509CA|X509AA|X509CRL|X509AC> + type = <certificate type, X509|X509_AC|X509_CRL> + flag = <X.509 certificate flag, NONE|CA|AA|OCSP> data = <PEM or DER encoded certificate data> } => { success = <yes or no> @@ -544,6 +572,16 @@ List the currently loaded pools. } } +### get-algorithms() ### + +List currently loaded algorithms and their implementation. + + {} => { + <algorithm type> = { + <algorithm> = <plugin providing the implementation> + } + } + ## Server-issued events ## Based on the packet layer, the vici plugin raises event messages using named @@ -588,8 +626,10 @@ command. version = <IKE version, 1 or 2> state = <IKE_SA state name> local-host = <local IKE endpoint address> + local-port = <local IKE endpoint port> local-id = <local IKE identity> remote-host = <remote IKE endpoint address> + remote-port = <remote IKE endpoint port> remote-id = <remote IKE identity> remote-xauth-id = <remote XAuth identity, if XAuth-authenticated> remote-eap-id = <remote EAP identity, if EAP-authenticated> @@ -735,9 +775,13 @@ The _list-cert_ event is issued to stream loaded certificates during an active _list-certs_ command. { - type = <certificate type> + type = <certificate type, X509|X509_AC|X509_CRL|OCSP_RESPONSE|PUBKEY> + flag = <X.509 certificate flag, NONE|CA|AA|OCSP> has_privkey = <set if a private key for the certificate is available> data = <ASN1 encoded certificate data> + subject = <subject string if defined and certificate type is PUBKEY> + not-before = <time string if defined and certificate type is PUBKEY> + not-after = <time string if defined and certificate type is PUBKEY> } ### list-authority ### @@ -763,7 +807,7 @@ information during an active_list-authorities_ command. The _ike-updown_ event is issued when an IKE_SA is established or terminated. { - up = <yes or no> + up = <set if up event> <IKE_SA config name> = { <same data as in the list-sas event, but without child-sas section> } @@ -789,7 +833,7 @@ The _ike-rekey_ event is issued when an IKE_SA is rekeyed. The _child-updown_ event is issued when a CHILD_SA is established or terminated. { - up = <yes or no> + up = <set if up event> <IKE_SA config name> = { <same data as in the list-sas event, but with only the affected CHILD_SA in the child-sas section> @@ -1068,3 +1112,43 @@ dictionaries. Objects returned by the library use OrderedDicts. For more details about the Python egg refer to the comments in the Python source code. + +# Vici::Session Perl CPAN module # + +The _Vici::Session Perl CPAN module_ is a pure Perl implementation of the VICI +protocol to implement client applications. It is provided in the _perl_ +subdirectory, and gets built and installed if strongSwan has been + _./configure_'d with_--enable-vici_ and _--enable-perl-cpan_. + +The _Vici::Session_ module provides a _new()_ constructor for a high level +interface, the underlying _Vici::Packet_ and _Vici::Transport_ classes are +usually not required to build Perl applications using VICI. The _Vici::Session_ +class provides methods for the supported VICI commands. The auxiliare + _Vici::Message_ class is used to encode configuration parameters sent to +the daemon and decode data returned by the daemon. + +## Connecting to the daemon ## + + use IO::Socket::UNIX; + use Vici::Session; + use Vici::Message; + + my $socket = IO::Socket::UNIX->new( + Type => SOCK_STREAM, + Peer => '/var/run/charon.vici', + ) or die "Vici socket: $!"; + + my $session = Vici::Session->new($socket); + +## A simple client request ## + +An example to print the daemon version information is as simple as: + + my $version = $session->version()->hash(); + + foreach my $key ('daemon', 'version', 'sysname', 'release', 'machine' ) { + print $version->{$key}, " "; + } + +The _Vici::Session_ methods are explained in the perl/Vici-Session/README.pod +document. diff --git a/src/libcharon/plugins/vici/perl/Makefile.am b/src/libcharon/plugins/vici/perl/Makefile.am new file mode 100644 index 000000000..9bc6262ac --- /dev/null +++ b/src/libcharon/plugins/vici/perl/Makefile.am @@ -0,0 +1,27 @@ +EXTRA_DIST = \ + Vici-Session/Changes \ + Vici-Session/Makefile.PL \ + Vici-Session/MANIFEST \ + Vici-Session/README.pod \ + Vici-Session/t/Vici-Session.t \ + Vici-Session/lib/Vici/Message.pm \ + Vici-Session/lib/Vici/Packet.pm \ + Vici-Session/lib/Vici/Session.pm \ + Vici-Session/lib/Vici/Transport.pm + +all-local: Vici-Session/pm_to_blib + +Vici-Session/Makefile: $(srcdir)/Vici-Session/Makefile.PL + (cd $(srcdir)/Vici-Session; $(PERL) Makefile.PL) + +Vici-Session/pm_to_blib: $(EXTRA_DIST) $(srcdir)/Vici-Session/Makefile + (cd $(srcdir)/Vici-Session; make) + +clean-local: + (cd $(srcdir)/Vici-Session; [ ! -f Makefile ] || make clean) + +if PERL_CPAN_INSTALL +install-exec-local: Vici-Session/pm_to_blib + (cd $(srcdir)/Vici-Session; make install) +endif + diff --git a/src/libcharon/plugins/vici/perl/Makefile.in b/src/libcharon/plugins/vici/perl/Makefile.in new file mode 100644 index 000000000..550d3e980 --- /dev/null +++ b/src/libcharon/plugins/vici/perl/Makefile.in @@ -0,0 +1,567 @@ +# 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@ +subdir = src/libcharon/plugins/vici/perl +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am +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_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 = +SOURCES = +DIST_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) +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@ +EASY_INSTALL = @EASY_INSTALL@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GEM = @GEM@ +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@ +PYTHONEGGINSTALLDIR = @PYTHONEGGINSTALLDIR@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +PY_TEST = @PY_TEST@ +RANLIB = @RANLIB@ +RTLIB = @RTLIB@ +RUBY = @RUBY@ +RUBYGEMDIR = @RUBYGEMDIR@ +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@ +json_CFLAGS = @json_CFLAGS@ +json_LIBS = @json_LIBS@ +libdir = @libdir@ +libexecdir = @libexecdir@ +libiptc_CFLAGS = @libiptc_CFLAGS@ +libiptc_LIBS = @libiptc_LIBS@ +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@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ +systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ +systemd_daemon_LIBS = @systemd_daemon_LIBS@ +systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ +systemd_journal_LIBS = @systemd_journal_LIBS@ +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@ +EXTRA_DIST = \ + Vici-Session/Changes \ + Vici-Session/Makefile.PL \ + Vici-Session/MANIFEST \ + Vici-Session/README.pod \ + Vici-Session/t/Vici-Session.t \ + Vici-Session/lib/Vici/Message.pm \ + Vici-Session/lib/Vici/Packet.pm \ + Vici-Session/lib/Vici/Session.pm \ + Vici-Session/lib/Vici/Transport.pm + +all: all-am + +.SUFFIXES: +$(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/vici/perl/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/libcharon/plugins/vici/perl/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): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +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 all-local +installdirs: +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." +@PERL_CPAN_INSTALL_FALSE@install-exec-local: +clean: clean-am + +clean-am: clean-generic clean-libtool clean-local mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-exec-local + +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 -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am all-local check check-am clean clean-generic \ + clean-libtool clean-local cscopelist-am ctags-am distclean \ + distclean-generic distclean-libtool 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-exec-local install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags-am uninstall uninstall-am + + +all-local: Vici-Session/pm_to_blib + +Vici-Session/Makefile: $(srcdir)/Vici-Session/Makefile.PL + (cd $(srcdir)/Vici-Session; $(PERL) Makefile.PL) + +Vici-Session/pm_to_blib: $(EXTRA_DIST) $(srcdir)/Vici-Session/Makefile + (cd $(srcdir)/Vici-Session; make) + +clean-local: + (cd $(srcdir)/Vici-Session; [ ! -f Makefile ] || make clean) + +@PERL_CPAN_INSTALL_TRUE@install-exec-local: Vici-Session/pm_to_blib +@PERL_CPAN_INSTALL_TRUE@ (cd $(srcdir)/Vici-Session; make install) + +# 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/vici/perl/Vici-Session/Changes b/src/libcharon/plugins/vici/perl/Vici-Session/Changes new file mode 100644 index 000000000..0c30328fd --- /dev/null +++ b/src/libcharon/plugins/vici/perl/Vici-Session/Changes @@ -0,0 +1,6 @@ +Revision history for Perl extension Vici::Session. + +0.9 Tue Nov 17 11:45:21 2015 + - original version; created by h2xs 1.23 with options + -X -n Vici::Session + diff --git a/src/libcharon/plugins/vici/perl/Vici-Session/MANIFEST b/src/libcharon/plugins/vici/perl/Vici-Session/MANIFEST new file mode 100644 index 000000000..c19032a08 --- /dev/null +++ b/src/libcharon/plugins/vici/perl/Vici-Session/MANIFEST @@ -0,0 +1,9 @@ +Changes +Makefile.PL +MANIFEST +README.pod +t/Vici-Session.t +lib/Vici/Session.pm +lib/Vici/Message.pm +lib/Vici/Packet.pm +lib/Vici/Transport.pm diff --git a/src/libcharon/plugins/vici/perl/Vici-Session/Makefile.PL b/src/libcharon/plugins/vici/perl/Vici-Session/Makefile.PL new file mode 100644 index 000000000..65f494557 --- /dev/null +++ b/src/libcharon/plugins/vici/perl/Vici-Session/Makefile.PL @@ -0,0 +1,11 @@ +use ExtUtils::MakeMaker; +# See lib/ExtUtils/MakeMaker.pm for details of how to influence +# the contents of the Makefile that is written. +WriteMakefile( + NAME => 'Vici::Session', + VERSION_FROM => 'lib/Vici/Session.pm', # finds $VERSION + PREREQ_PM => {}, # e.g., Module::Name => 1.1 + ($] >= 5.005 ? ## Add these new keywords supported since 5.005 + (ABSTRACT_FROM => 'lib/Vici/Session.pm', # retrieve abstract from module + AUTHOR => 'Andreas Steffen <andreas.steffen@>strongswan.org') : ()), +); diff --git a/src/libcharon/plugins/vici/perl/Vici-Session/README.pod b/src/libcharon/plugins/vici/perl/Vici-Session/README.pod new file mode 100644 index 000000000..de374aa11 --- /dev/null +++ b/src/libcharon/plugins/vici/perl/Vici-Session/README.pod @@ -0,0 +1,649 @@ + +=head1 NAME + +Vici::Session - Perl binding for the strongSwan VICI configuration interface + +=head1 DESCRIPTION + +The Vici::Session module allows a Perl script to communicate with the open +source strongSwan IPsec daemon (https://www.strongswan.org) via the documented +Versatile IKE Configuration Interface (VICI). VICI allows the configuration, +management and monitoring of multiple IPsec connections. + +=head1 INSTALLATION + +To install this module type the following: + + perl Makefile.PL + make + make install + +=head1 DEPENDENCIES + +This module requires the standard networking module: + + IO::Socket::UNIX + +=head1 METHODS + +The following examples show the use of the Vici::Session interface in a +a "net-net" connection between the VPN gateways "moon" and "sun". + +=cut + +use strict; +use warnings; +use IO::Socket::UNIX; +use Vici::Message; +use Vici::Session; + +my $moon_key = "-----BEGIN RSA PRIVATE KEY-----\n" . + "MIIEowIBAAKCAQEApHwF+sUXQdH+WwYzdPMzpjuwhGGvHgsmBah1IQsPsddL9gZy" . + "gerzpTM1vvQ4kbRuvE3SZWLf9uKEbiQV9IABr87L9JAva56EHIAiUMuG8WizVbIK" . + "IhQlZc8S2mIwAW0Jc6EmnoJv9j6F/tVD9+6xvMJbwHLi0h7BUO9tBVLPy72YeGNB" . + "Y6Cob4CrOuFOJyACezJ7i9vZ+XzOfnXpu7qL0DgYP/n2maPEJGEivTFunkJD/mJ8" . + "DecyLTQcchsCj2118BMuf2qjVn4UWPCBBuhyYK5wsATB1ANeAtlFfgH+wsuHjZwt" . + "TJru05lGHBZ3F2hZ9PO68hVHbIZZj6SB8X47nwIDAQABAoIBAAQDXqX6rxGVDQ6t" . + "fQ3qbSUuKaVhOMOT5A6ZSJpQycY+CYVsLNkMoXszX6lUDhlH/Letcme03OAKMM77" . + "JGn9wYzHj+RcrDuE95Y2bh/oh1dWhaGeoW6pbSwpvD0FzkQKpANlOCr/5bltVxmb" . + "nHftI/sGBvUQGIal53ORE+jgV1+SK6I0oAIWiCpU2oZpYMAtp7WxOngsAJaGtk//" . + "m2ckH+T8uVHwe9gJ9HZnEk+Io6BXScMNNrsbd2J+pQ75wQXfzHEzHAj+ElhWzhtc" . + "5XefqHw/DfpPDX/lby3VoSoagqzsVuUx7LylgzIDxTsb9HQVOLjDzOQ+vn22Xj7g" . + "UCEjwLkCgYEA2EZguuzJdxRIWBSnIyzpCzfqm0EgybpeLuJVfzWla0yKWI6AeLhW" . + "cr+7o9UE8nCQHVffIrgjWksjc/S5FhzC9TYSHpPa8TPgebTQK4VxnP9Qkh/XRpJj" . + "CqgJ8k2MYleHYxa+AKQv/25yNhLdowkNR0iU1kbiaYRJMP0WigAmdAUCgYEAwrJe" . + "Y3LAawOkalJFMFTtLXsqZE91TFwMt9TQnzysGH3Q6+9N+qypS5KCes650+qgrwBV" . + "RmRNc1ixylToP3B0BKY5OD/BwMx1L/zSO3x7I4ZDasCu33y2ukGLcVSxrxTPTGdd" . + "8fhEiVO1CDXcM08/kSeQa049J8ziY3M+4NDchlMCgYEAw2VCO1923Tjb64gtQOBw" . + "ZAxOz5nVz6urL9yYted33is2yq9kbqzMnbuQAYKRh6Ae9APRuwJ2HjvIehjdp5aw" . + "pO4HDM00f7sI0ayEbu2PKfKZjotp6X6UMKqE4f8iGC9QSDvhyZ6NJs9YLHZ6+7NP" . + "5dkzbyx3njFAFxxxYpikJSkCgYByShB8YlUvvKCcRRUWbRQZWa6l2brqizJwCz43" . + "636+lcS5au2klAyBL0zm2Elfa+DNOe3U93Y7mrorIrJ+4v1H6We3bD3JdnvoIooq" . + "n0UNsngKx3cf++6r4WQAsA3pz9ZsbFVKgEmDL58aZbuQZxnSlJ4DT5c4sN3IMVOc" . + "1x5MvwKBgHudAaLvioIopBpYzOsK2OtEn6NQ7SwH0BLEUulHysaHqan5oExmM1bm" . + "YeivMDc9hj0YLXA47ryQHTx4vB5Nv3TI/LoUG6VrCvZvocQOXe/n7TguwAjJj7ef" . + "E55Gy8lXDRENyJMP1vif3N2iH8eQ1ASf8k/+gnBNkjSlYSSQUDfV\n" . + "-----END RSA PRIVATE KEY-----\n"; + +my $moon_cert = "-----BEGIN CERTIFICATE-----\n" . + "MIIEIjCCAwqgAwIBAgIBKzANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJDSDEZ" . + "MBcGA1UEChMQTGludXggc3Ryb25nU3dhbjEbMBkGA1UEAxMSc3Ryb25nU3dhbiBS" . + "b290IENBMB4XDTE0MDgyNzE0NDQ1NloXDTE5MDgyNjE0NDQ1NlowRjELMAkGA1UE" . + "BhMCQ0gxGTAXBgNVBAoTEExpbnV4IHN0cm9uZ1N3YW4xHDAaBgNVBAMTE21vb24u" . + "c3Ryb25nc3dhbi5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCk" . + "fAX6xRdB0f5bBjN08zOmO7CEYa8eCyYFqHUhCw+x10v2BnKB6vOlMzW+9DiRtG68" . + "TdJlYt/24oRuJBX0gAGvzsv0kC9rnoQcgCJQy4bxaLNVsgoiFCVlzxLaYjABbQlz" . + "oSaegm/2PoX+1UP37rG8wlvAcuLSHsFQ720FUs/LvZh4Y0FjoKhvgKs64U4nIAJ7" . + "MnuL29n5fM5+dem7uovQOBg/+faZo8QkYSK9MW6eQkP+YnwN5zItNBxyGwKPbXXw" . + "Ey5/aqNWfhRY8IEG6HJgrnCwBMHUA14C2UV+Af7Cy4eNnC1Mmu7TmUYcFncXaFn0" . + "87ryFUdshlmPpIHxfjufAgMBAAGjggEaMIIBFjAJBgNVHRMEAjAAMAsGA1UdDwQE" . + "AwIDqDAdBgNVHQ4EFgQU2CY9Iex8275aOQxbcMsDgCHerhMwbQYDVR0jBGYwZIAU" . + "XafdcAZRMn7ntm2zteXgYOouTe+hSaRHMEUxCzAJBgNVBAYTAkNIMRkwFwYDVQQK" . + "ExBMaW51eCBzdHJvbmdTd2FuMRswGQYDVQQDExJzdHJvbmdTd2FuIFJvb3QgQ0GC" . + "AQAwHgYDVR0RBBcwFYITbW9vbi5zdHJvbmdzd2FuLm9yZzATBgNVHSUEDDAKBggr" . + "BgEFBQcDATA5BgNVHR8EMjAwMC6gLKAqhihodHRwOi8vY3JsLnN0cm9uZ3N3YW4u" . + "b3JnL3N0cm9uZ3N3YW4uY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQCpnj6Nc+PuPLPi" . + "4E3g5hyJkr5VZy7SSglcs1uyVP2mfwj6JR9SLd5+JOsL1aCTm0y9qLcqdbHBxG8i" . + "LNLtwVKU3s1hV4EIO3saHe4XUEjxN9bDtLWEoeq5ipmYX8RJ/fXKR8/8vurBARP2" . + "xu1+wqwEhymp4jBmF0LVovT1+o+GhH66zIJnx3zR9BtfMkaeL6804hrx2ygeopeo" . + "buGvMDQ8HcnMB9OU7Y8fK0oY1kULl6hf36K5ApPA6766sRRKRvBSKlmViKSQTq5a" . + "4c8gCWAZbtdT+N/fa8hKDlZt5q10EgjTqDfGTj50xKvAneq7XdfKmYYGnIWoNLY9" . + "ga8NOzX8\n" . + "-----END CERTIFICATE-----\n"; + +my $ca_cert = "-----BEGIN CERTIFICATE-----\n" . + "MIIDuDCCAqCgAwIBAgIBADANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJDSDEZ" . + "MBcGA1UEChMQTGludXggc3Ryb25nU3dhbjEbMBkGA1UEAxMSc3Ryb25nU3dhbiBS" . + "b290IENBMB4XDTA0MDkxMDEwMDExOFoXDTE5MDkwNzEwMDExOFowRTELMAkGA1UE" . + "BhMCQ0gxGTAXBgNVBAoTEExpbnV4IHN0cm9uZ1N3YW4xGzAZBgNVBAMTEnN0cm9u" . + "Z1N3YW4gUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL/y" . + "X2LqPVZuWLPIeknK86xhz6ljd3NNhC2z+P1uoCP3sBMuZiZQEjFzhnKcbXxCeo2f" . + "FnvhOOjrrisSuVkzuu82oxXD3fIkzuS7m9V4E10EZzgmKWIf+WuNRfbgAuUINmLc" . + "4YGAXBQLPyzpP4Ou48hhz/YQo58Bics6PHy5v34qCVROIXDvqhj91P8g+pS+F21/" . + "7P+CH2jRcVIEHZtG8M/PweTPQ95dPzpYd2Ov6SZ/U7EWmbMmT8VcUYn1aChxFmy5" . + "gweVBWlkH6MP+1DeE0/tL5c87xo5KCeGK8Tdqpe7sBRC4pPEEHDQciTUvkeuJ1Pr" . + "K+1LwdqRxo7HgMRiDw8CAwEAAaOBsjCBrzASBgNVHRMBAf8ECDAGAQH/AgEBMAsG" . + "A1UdDwQEAwIBBjAdBgNVHQ4EFgQUXafdcAZRMn7ntm2zteXgYOouTe8wbQYDVR0j" . + "BGYwZIAUXafdcAZRMn7ntm2zteXgYOouTe+hSaRHMEUxCzAJBgNVBAYTAkNIMRkw" . + "FwYDVQQKExBMaW51eCBzdHJvbmdTd2FuMRswGQYDVQQDExJzdHJvbmdTd2FuIFJv" . + "b3QgQ0GCAQAwDQYJKoZIhvcNAQELBQADggEBACOSmqEBtBLR9aV3UyCI8gmzR5in" . + "Lte9aUXXS+qis6F2h2Stf4sN+Nl6Gj7REC6SpfEH4wWdwiUL5J0CJhyoOjQuDl3n" . + "1Dw3dE4/zqMZdyDKEYTU75TmvusNJBdGsLkrf7EATAjoi/nrTOYPPhSUZvPp/D+Y" . + "vORJ9Ej51GXlK1nwEB5iA8+tDYniNQn6BD1MEgIejzK+fbiy7braZB1kqhoEr2Si" . + "7luBSnU912sw494E88a2EWbmMvg2TVHPNzCpVkpNk7kifCiwmw9VldkqYy9y/lCa" . + "Epyp7lTfKw7cbD04Vk8QJW782L6Csuxkl346b17wmOqn8AZips3tFsuAY3w=\n" . + "-----END CERTIFICATE-----\n" ; + +=pod + +The VICI interface requires a UNIX socket in order to communicate with the +strongSwan charon daemon: + + use IO::Socket::UNIX; + + my $socket = IO::Socket::UNIX->new( + Type => SOCK_STREAM, + Peer => '/var/run/charon.vici', + ) or die "Vici socket: $!"; + +=cut + +my $socket = IO::Socket::UNIX->new( + Type => SOCK_STREAM, + Peer => '/var/run/charon.vici', +) or die "Vici socket: $!"; + +=over + +=item new() + +creates a new Vici::Session object. + + use Vici::Session; + use Vici::Message; + + my $session = Vici::Session->new($socket); + +=cut + +my $session = Vici::Session->new($socket); + +=item version() + +returns daemon and system specific version information. + + my $version = $session->version(); + +=cut + +print "----- version -----\n"; +my $version = $session->version(); +print $version->raw(), "\n"; + +=item load_cert() + +loads a certificate into the daemon. + + my %vars = ( type => 'X509', flag => 'CA', data => $ca_cert ); + my ($res, $errmsg) = $session->load_cert(Vici::Message->new(\%vars)); + +=cut + +print "----- load-cert -----\n"; +my %vars = ( type => 'X509', flag => 'CA', data => $ca_cert ); +my ($res, $errmsg) = $session->load_cert(Vici::Message->new(\%vars)); +print $res ? "ok\n" : "failed: $errmsg\n"; + +=item load_key() + +loads a private key into the daemon. + + my %vars = ( type => 'RSA', data => $moon_key ); + my ($res, $errmsg) = $session->load_key(Vici::Message->new(\%vars)); + +=cut + +print "----- load-key -----\n"; +%vars = ( type => 'RSA', data => $moon_key ); +($res, $errmsg) = $session->load_key(Vici::Message->new(\%vars)); +print $res ? "ok\n" : "failed: $errmsg\n"; + +=item load_shared() + +loads a shared IKE PSK, EAP or XAuth secret into the daemon. + + my @owners = ( 'carol' ); + my %vars = ( type => 'EAP', data => 'Ar3etTnp', owners => \@owners ); + my ($res, $errmsg) = $session->load_shared(Vici::Message->new(\%vars)); + +=cut + +print "----- load-shared -----\n"; +my @owners = ( 'carol' ); +%vars = ( type => 'EAP', data => 'Ar3etTnp', owners => \@owners ); +($res, $errmsg) = $session->load_shared(Vici::Message->new(\%vars)); +print $res ? "ok\n" : "failed: $errmsg\n"; + +=item load_authority() + +loads a single certification authority definition into the daemon. An existing +authority with the same name gets replaced. + + my @crl_uris = ( 'http://crl.strongswan.org/strongswan.crl' ); + my @ocsp_uris = ( 'http://ocsp.strongswan.org:8880' ); + + my %auth = ( + cacert => $ca_cert, + crl_uris => \@crl_uris, + ocsp_uris => \@ocsp_uris + ); + + my %vars = ( strongswan => \%auth ); + my ($res, $errmsg) = $session->load_authority(Vici::Message->new(\%vars)); + +=cut + +print "----- load-authority -----\n"; +my @crl_uris = ( 'http://crl.strongswan.org/strongswan.crl' ); +my @ocsp_uris = ( 'http://ocsp.strongswan.org:8880' ); +my %auth = ( + cacert => $ca_cert, + crl_uris => \@crl_uris, + ocsp_uris => \@ocsp_uris +); +%vars = ( strongswan => \%auth ); +($res, $errmsg) = $session->load_authority(Vici::Message->new(\%vars)); +print $res ? "ok\n" : "failed: $errmsg\n"; + +=item load_conn() + +loads a single connection definition into the daemon. An existing connection +with the same name gets updated or replaced. + + my @l_ts = ( '10.1.0.0/16' ); + my @r_ts = ( '10.2.0.0/16' ); + my @esp = ( 'aes128gcm128-modp3072' ); + + my %child = ( + local_ts => \@l_ts, + remote_ts => \@r_ts, + esp_proposals => \@esp, + ); + my %children = ( 'net-net' => \%child ); + + my @l_addrs = ( '192.168.0.1' ); + my @r_addrs = ( '192.168.0.2' ); + my @l_certs = ( $moon_cert ); + my %l = ( auth => 'pubkey', id => 'moon.strongswan.org', + certs => \@l_certs ); + my %r = ( auth => 'pubkey', id => 'sun.strongswan.org'); + my @ike = ( 'aes128-sha256-modp3072' ); + + my %gw = ( + version => 2, + mobike => 'no', + proposals => \@ike, + local_addrs => \@l_addrs, + remote_addrs => \@r_addrs, + local => \%l, + remote => \%r, + children => \%children, + ); + + my %vars = ( 'gw-gw' => \%gw); + my ($res, $errmsg) = $session->load_conn(Vici::Message->new(\%vars)); + +=cut + +print "----- load-conn -----\n"; +my @l_ts = ( '10.1.0.0/16' ); +my @r_ts = ( '10.2.0.0/16' ); +my @esp = ( 'aes128gcm128-modp3072' ); +my %child = ( + local_ts => \@l_ts, + remote_ts => \@r_ts, + esp_proposals => \@esp, +); +my %children = ( 'net-net' => \%child ); +my @l_addrs = ( '192.168.0.1' ); +my @r_addrs = ( '192.168.0.2' ); +my @l_certs = ( $moon_cert ); +my %l = ( auth => 'pubkey', id => 'moon.strongswan.org', certs => \@l_certs ); +my %r = ( auth => 'pubkey', id => 'sun.strongswan.org'); +my @ike = ( 'aes128-sha256-modp3072' ); +my %gw = ( + version => 2, + mobike => 'no', + proposals => \@ike, + local_addrs => \@l_addrs, + remote_addrs => \@r_addrs, + local => \%l, + remote => \%r, + children => \%children, +); +%vars = ( 'gw-gw' => \%gw); +($res, $errmsg) = $session->load_conn(Vici::Message->new(\%vars)); +print $res ? "ok\n" : "failed: $errmsg\n"; + +=item get_algorithms() + +lists all currently loaded algorithms and their implementation. + + my $algs = $session->get_algorithms(); + +=cut + +print "----- get-algorithms -----\n"; +my $algs = $session->get_algorithms(); +print $algs->raw(), "\n"; + +=item get_conns() + +returns a list of connection names loaded exclusively over VICI, not including +connections found in other backends. + + my $conns = $session->get_conns(); + +=cut + +print "----- get-conns -----\n"; +my $conns = $session->get_conns(); +print $conns->raw(), "\n"; + +=item list_conns() + +lists currently loaded connections by streaming list-conn events. This +call includes all connections known by the daemon, not only those loaded +over VICI. + + my $conns = $session->list_conns(); + + foreach my $conn (@$conns) + { + print $conn->raw(), "\n"; + } + +=cut + +print "----- list-conns -----\n"; +$conns = $session->list_conns(); +foreach my $conn (@$conns) +{ + print $conn->raw(), "\n"; +} + +=item initiate() + +initiates a CHILD_SA. + + my %vars = ( child => 'net-net' ); + my($res, $errmsg) = $session->initiate(Vici::Message->new(\%vars)); + +=cut + +print "----- initiate -----\n"; +%vars = ( child => 'net-net' ); +($res, $errmsg) = $session->initiate(Vici::Message->new(\%vars)); +print $res ? "ok\n" : "failed: $errmsg\n"; + +=item list_sas() + +lists currently active IKE_SAs and associated CHILD_SAs by streaming list-sa +events. + + my $sas = $session->list_sas(); + + foreach my $sa (@$sas) + { + print $sa->raw(), "\n"; + } + +=cut + +print "----- list-sas -----\n"; +my $sas = $session->list_sas(); +foreach my $sa (@$sas) +{ + print $sa->raw(), "\n"; +} + +=item get_authorities() + +returns a list of currently loaded certification authority names. + + my $auths = $session->get_authorities(); + +=cut + +print "----- get-authorities -----\n"; +my $auths = $session->get_authorities(); +print $auths->raw(), "\n"; + +=item list-authorities() + +lists currently loaded certification authority information by streaming +list-authority events. + + my $auths = $session->list_authorities(); + + foreach my $auth (@$auths) + { + print $auth->raw(), "\n"; + } + +=cut + +print "----- list-authorities -----\n"; +$auths = $session->list_authorities(); +foreach my $auth (@$auths) +{ + print $auth->raw(), "\n"; +} + +=item list_certs() + +lists currently loaded certificates by streaming list-cert events. This +call includes all certificates known by the daemon, not only those loaded +over VICI. + + my %vars = ( subject => 'C=CH, O=Linux strongSwan, CN=moon.strongswan.org' ); + my $certs = $session->list_certs(Vici::Message->new(\%vars)); + +=cut + +print "----- list-certs -----\n"; +%vars = ( subject => 'C=CH, O=Linux strongSwan, CN=moon.strongswan.org' ); +my $certs = $session->list_certs(Vici::Message->new(\%vars)); +foreach my $cert (@$certs) +{ + my $hash = $cert->hash(); + print $hash->{'type'}, ": ", length($hash->{'data'}), ' bytes', + $hash->{'has_privkey'} ? ', has private key' : '', "\n"; +} + +=item stats() + +returns IKE daemon statistics and load information. + + my $stats = $session->stats(); + +=cut + +print "----- stats -----\n"; +my $stats = $session->stats(); +print $stats->raw(), "\n"; + +=item terminate() + +terminates an IKE_SA or CHILD_SA. + + my %vars = ( ike => 'gw-gw' ); + my ($res, $errmsg) = $session->terminate(Vici::Message->new(\%vars)); + +=cut + +print "----- terminate -----\n"; +%vars = ( ike => 'gw-gw' ); +($res, $errmsg) = $session->terminate(Vici::Message->new(\%vars)); +print $res ? "ok\n" : "failed: $errmsg\n"; + +=item install() + +installs a trap, drop or bypass policy defined by a CHILD_SA config. + + my %vars = ( child => 'net-net' ); + my ($res, $errmsg) = $session->install(Vici::Message->new(\%vars)); + +=cut + +print "----- install -----\n"; +%vars = ( child => 'net-net' ); +($res, $errmsg) = $session->install(Vici::Message->new(\%vars)); +print $res ? "ok\n" : "failed: $errmsg\n"; + +=item list_policies() + +lists currently installed trap, drop and bypass policies by streaming +list-policy events. + + my %vars = ( trap => 'yes' ); + my $pols = $session->list_policies(Vici::Message->new(\%vars)); + + foreach my $pol (@$pols) + { + print $pol->raw(), "\n"; + } + +=cut + +print "----- list-policies -----\n"; +%vars = ( trap => 'yes' ); +my $pols = $session->list_policies(Vici::Message->new(\%vars)); +foreach my $pol (@$pols) +{ + print $pol->raw(), "\n"; +} + +=item uninstall() + +uninstalls a trap, drop or bypass policy defined by a CHILD_SA config. + + my %vars = ( child => 'net-net' ); + my ($res, $errmsg) = $session->uninstall(Vici::Message->new(\%vars)); + +=cut + +print "----- uninstall -----\n"; +%vars = ( child => 'net-net' ); +($res, $errmsg) = $session->uninstall(Vici::Message->new(\%vars)); +print $res ? "ok\n" : "failed: $errmsg\n"; + +=item reload_settings() + +reloads strongswan.conf settings and all plugins supporting configuration +reload. + + my ($res, $errmsg) = $session->reload_settings(); + print $res ? "ok\n" : "failed: $errmsg\n"; + +=cut + +print "----- reload-settings -----\n"; +($res, $errmsg) = $session->reload_settings(); +print $res ? "ok\n" : "failed: $errmsg\n"; + +=item unload_conn() + +unloads a previously loaded connection definition by name. + + my %vars = ( name => 'gw-gw' ); + my ($res, $errmsg) = $session->unload_conn(Vici::Message->new(\%vars)); + +=cut + +print "----- unload-conn -----\n"; +%vars = ( name => 'gw-gw' ); +($res, $errmsg) = $session->unload_conn(Vici::Message->new(\%vars)); +print $res ? "ok\n" : "failed: $errmsg\n"; + +=item unload_authority() + +unloads a previously loaded certification authority definition by name. + + my %vars = ( name => 'strongswan' ); + my ($res, $errmsg) = $session->unload_authority(Vici::Message->new(\%vars)); + +=cut + +print "----- unload-authority -----\n"; +%vars = ( name => 'strongswan' ); +($res, $errmsg) = $session->unload_authority(Vici::Message->new(\%vars)); +print $res ? "ok\n" : "failed: $errmsg\n"; + +=item clear_creds() + +clears all loaded certificate, private key and shared key credentials. This +affects only credentials loaded over vici, but additionally flushes the +credential cache. + + my ($res, $errmsg) = $session->clear_creds(); + +=cut + +print "----- clear-creds -----\n"; +($res, $errmsg) = $session->clear_creds(); +print $res ? "ok\n" : "failed: $errmsg\n"; + +=item load_pool() + +loads an in-memory virtual IP and configuration attribute pool. Existing +pools with the same name get updated, if possible. + + my %pool = ( addrs => '10.3.0.0/23' ); + my %vars = ( my_pool => \%pool ); + my ($res, $errmsg) = $session->load_pool(Vici::Message->new(\%vars)); + +=cut + +print "----- load-pool -----\n"; +my %pool = ( addrs => '10.3.0.0/23' ); +%vars = ( my_pool => \%pool ); +($res, $errmsg) = $session->load_pool(Vici::Message->new(\%vars)); +print $res ? "ok\n" : "failed: $errmsg\n"; + +=item get_pools() + +lists the currently loaded pools. + + my $pools = $session->get_pools(); + +=cut + +print "----- get-pools -----\n"; +my $pools = $session->get_pools(); +print $pools->raw(), "\n"; + +=item unload_pool() + +unloads a previously loaded virtual IP and configuration attribute pool. +Unloading fails for pools with leases currently online. + + my %vars = ( name => 'my_pool' ); + my ($res, $errmsg) = $session->unload_pool(Vici::Message->new(\%vars)); + +=cut + +print "----- unload-pool -----\n"; +%vars = ( name => 'my_pool' ); +($res, $errmsg) = $session->unload_pool(Vici::Message->new(\%vars)); +print $res ? "ok\n" : "failed: $errmsg\n"; + +=back + +=cut + +# close vici socket +close($socket); + +=head1 COPYRIGHT AND LICENCE + +Copyright (c) 2015 Andreas Steffen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/src/libcharon/plugins/vici/perl/Vici-Session/lib/Vici/Message.pm b/src/libcharon/plugins/vici/perl/Vici-Session/lib/Vici/Message.pm new file mode 100644 index 000000000..b0a942c04 --- /dev/null +++ b/src/libcharon/plugins/vici/perl/Vici-Session/lib/Vici/Message.pm @@ -0,0 +1,256 @@ +package Vici::Message; + +our $VERSION = '0.9'; + +use strict; +use Vici::Transport; + +use constant { + SECTION_START => 1, # Begin a new section having a name + SECTION_END => 2, # End a previously started section + KEY_VALUE => 3, # Define a value for a named key in the section + LIST_START => 4, # Begin a named list for list items + LIST_ITEM => 5, # Define an unnamed item value in the current list + LIST_END => 6, # End a previously started list +}; + +sub new { + my $class = shift; + my $hash = shift; + my $self = { + Hash => $hash + }; + bless($self, $class); + return $self; +} + +sub from_data { + my $class = shift; + my $data = shift; + my %hash = (); + + parse($data, \%hash); + + my $self = { + Hash => \%hash + }; + bless($self, $class); + return $self; +} + +sub hash { + my $self = shift; + return $self->{Hash}; +} + +sub encode { + my $self = shift; + return encode_hash($self->{'Hash'}); +} + +sub raw { + my $self = shift; + return '{' . raw_hash($self->{'Hash'}) . '}'; +} + +sub result { + my $self = shift; + my $result = $self->{'Hash'}; + return ($result->{'success'} eq 'yes', $result->{'errmsg'}); +} + +# private functions + +sub parse { + my $data = shift; + my $hash = shift; + + while (length($data) > 0) + { + (my $type, $data) = unpack('Ca*', $data); + + if ($type == SECTION_END) + { + return $data; + } + + (my $key, $data) = unpack('C/a*a*', $data); + + if ( $type == KEY_VALUE ) + { + (my $value, $data) = unpack('n/a*a*', $data); + $hash->{$key} = $value; + } + elsif ( $type == SECTION_START ) + { + my %section = (); + $data = parse($data, \%section); + $hash->{$key} = \%section; + } + elsif ( $type == LIST_START ) + { + my @list = (); + my $more = 1; + + while (length($data) > 0 and $more) + { + (my $type, $data) = unpack('Ca*', $data); + if ( $type == LIST_ITEM ) + { + (my $value, $data) = unpack('n/a*a*', $data); + push(@list, $value); + } + elsif ( $type == LIST_END ) + { + $more = 0; + $hash->{$key} = \@list; + } + else + { + die "message parsing error: ", $type, "\n" + } + } + } + else + { + die "message parsing error: ", $type, "\n" + } + } + return $data; +} + + +sub encode_hash { + my $hash = shift; + my $enc = ''; + + while ( (my $key, my $value) = each %$hash ) + { + if ( ref($value) eq 'HASH' ) + { + $enc .= pack('CC/a*', SECTION_START, $key); + $enc .= encode_hash($value); + $enc .= pack('C', SECTION_END); + } + elsif ( ref($value) eq 'ARRAY' ) + { + $enc .= pack('CC/a*', LIST_START, $key); + + foreach my $item (@$value) + { + $enc .= pack('Cn/a*', LIST_ITEM, $item); + } + $enc .= pack('C', LIST_END); + } + else + { + $enc .= pack('CC/a*n/a*', KEY_VALUE, $key, $value); + } + } + return $enc; +} + +sub raw_hash { + my $hash = shift; + my $raw = ''; + my $first = 1; + + while ( (my $key, my $value) = each %$hash ) + { + if ($first) + { + $first = 0; + } + else + { + $raw .= ' '; + } + $raw .= $key; + + if ( ref($value) eq 'HASH' ) + { + $raw .= '{' . raw_hash($value) . '}'; + } + elsif ( ref($value) eq 'ARRAY' ) + { + my $first_item = 1; + $raw .= '['; + + foreach my $item (@$value) + { + if ($first_item) + { + $first_item = 0; + } + else + { + $raw .= ' '; + } + $raw .= $item; + } + $raw .= ']'; + } + else + { + $raw .= '=' . $value; + } + } + return $raw; +} + +1; +__END__ +=head1 NAME + +Vici::Message - Perl extension for building and parsing strongSwan VICI messages + +=head1 SYNOPSIS + + use Vici::Message; + +=head1 DESCRIPTION + +The Vici::Message module is needed by the Vici::Session module to build and +parse messages used in the communication with the open source strongSwan IPsec +daemon (https://www.strongswan.com) via the documented Versatile IKE +Configuration Interface (VICI). VICI allows the configuration, management and +monitoring of multiple IPsec connections. + +=head2 EXPORT + +None by default. + +=head1 SEE ALSO + +strongSwan Wiki: https://wiki.strongswan.org/projects/strongswan/wiki/Vici + +strongSwan Mailing list: users@lists.strongswan.org + +=head1 AUTHOR + +Andreas Steffen, E<lt>andreas.steffen@strongswan.orgE<gt> + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2015 by Andreas Steffen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +=cut + diff --git a/src/libcharon/plugins/vici/perl/Vici-Session/lib/Vici/Packet.pm b/src/libcharon/plugins/vici/perl/Vici-Session/lib/Vici/Packet.pm new file mode 100644 index 000000000..9e2b77fa5 --- /dev/null +++ b/src/libcharon/plugins/vici/perl/Vici-Session/lib/Vici/Packet.pm @@ -0,0 +1,191 @@ +package Vici::Packet; + +our $VERSION = '0.9'; + +use strict; +use Vici::Message; +use Vici::Transport; + +use constant { + CMD_REQUEST => 0, # Named request message + CMD_RESPONSE => 1, # Unnamed response message for a request + CMD_UNKNOWN => 2, # Unnamed response if requested command is unknown + EVENT_REGISTER => 3, # Named event registration request + EVENT_UNREGISTER => 4, # Named event de-registration request + EVENT_CONFIRM => 5, # Unnamed confirmation for event (de-)registration + EVENT_UNKNOWN => 6, # Unnamed response if event (de-)registration failed + EVENT => 7, # Named event message +}; + +sub new { + my $class = shift; + my $socket = shift; + my $self = { + Transport => Vici::Transport->new($socket), + }; + bless($self, $class); + return $self; +} + +sub request { + my ($self, $command, $vars) = @_; + my $out = defined $vars ? $vars->encode() : ''; + my $request = pack('CC/a*a*', CMD_REQUEST, $command, $out); + $self->{'Transport'}->send($request); + + my $response = $self->{'Transport'}->receive(); + my ($type, $data) = unpack('Ca*', $response); + + if ( $type == CMD_RESPONSE ) + { + return Vici::Message->from_data($data); + } + elsif ( $type == CMD_UNKNOWN ) + { + die "unknown command '", $command, "'\n" + } + else + { + die "invalid response type\n" + } +} + +sub register { + my ($self, $event) = @_; + my $request = pack('CC/a*a*', EVENT_REGISTER, $event); + $self->{'Transport'}->send($request); + + my $response = $self->{'Transport'}->receive(); + my ($type, $data) = unpack('Ca*', $response); + + if ( $type == EVENT_CONFIRM ) + { + return + } + elsif ( $type == EVENT_UNKNOWN ) + { + die "unknown event '", $event, "'\n" + } + else + { + die "invalid response type\n" + } +} + +sub unregister { + my ($self, $event) = @_; + my $request = pack('CC/a*a*', EVENT_UNREGISTER, $event); + $self->{'Transport'}->send($request); + + my $response = $self->{'Transport'}->receive(); + my ($type, $data) = unpack('Ca*', $response); + + if ( $type == EVENT_CONFIRM ) + { + return + } + elsif ( $type == EVENT_UNKNOWN ) + { + die "unknown event '", $event, "'\n" + } + else + { + die "invalid response type\n" + } +} + +sub streamed_request { + my ($self, $command, $event, $vars) = @_; + my $out = defined $vars ? $vars->encode() : ''; + + $self->register($event); + + my $request = pack('CC/a*a*', CMD_REQUEST, $command, $out); + $self->{'Transport'}->send($request); + my $more = 1; + my @list = (); + + while ($more) + { + my $response = $self->{'Transport'}->receive(); + my ($type, $data) = unpack('Ca*', $response); + + if ( $type == EVENT ) + { + (my $event_name, $data) = unpack('C/a*a*', $data); + + if ($event_name eq $event) + { + my $msg = Vici::Message->from_data($data); + push(@list, $msg); + } + } + elsif ( $type == CMD_RESPONSE ) + { + $self->unregister($event); + $more = 0; + } + else + { + $self->unregister($event); + die "invalid response type\n"; + } + } + return \@list; +} + +1; +__END__ +=head1 NAME + +Vici::Packet - Perl extension for sending and receiving strongSwan VICI packets + +=head1 SYNOPSIS + + use Vici::Packet; + +=head1 DESCRIPTION + +The Vici::Packet module is needed by the Vici::Session module to send and +receive packets used in the communication with the open source strongSwan IPsec +daemon (https://www.strongswan.com) via the documented Versatile IKE +Configuration Interface (VICI). VICI allows the configuration, management and +monitoring of multiple IPsec connections. + +=head2 EXPORT + +None by default. + +=head1 SEE ALSO + +strongSwan Wiki: https://wiki.strongswan.org/projects/strongswan/wiki/Vici + +strongSwan Mailing list: users@lists.strongswan.org + +=head1 AUTHOR + +Andreas Steffen, E<lt>andreas.steffen@strongswan.orgE<gt> + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2015 by Andreas Steffen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +=cut diff --git a/src/libcharon/plugins/vici/perl/Vici-Session/lib/Vici/Session.pm b/src/libcharon/plugins/vici/perl/Vici-Session/lib/Vici/Session.pm new file mode 100644 index 000000000..78197136a --- /dev/null +++ b/src/libcharon/plugins/vici/perl/Vici-Session/lib/Vici/Session.pm @@ -0,0 +1,204 @@ +package Vici::Session; + +our $VERSION = '0.9'; + +use strict; +use Vici::Packet; +use Vici::Message; + +sub new { + my $class = shift; + my $socket = shift; + my $self = { + Packet => Vici::Packet->new($socket), + }; + bless($self, $class); + return $self; +} + +sub version { + return request('version', @_); +} + +sub stats { + return request('stats', @_); +} + +sub reload_settings { + return request_res('reload-settings', @_); +} + +sub initiate { + return request_vars_res('initiate', @_); +} + +sub terminate { + return request_vars_res('terminate', @_); +} + +sub redirect { + return request_vars_res('redirect', @_); +} + +sub install { + return request_vars_res('install', @_); +} + +sub uninstall { + return request_vars_res('uninstall', @_); +} + +sub list_sas { + return request_list('list-sas', 'list-sa', @_); +} + +sub list_policies { + return request_list('list-policies', 'list-policy', @_); +} + +sub list_conns { + return request_list('list-conns', 'list-conn', @_); +} + +sub get_conns { + return request('get-conns', @_); +} + +sub list_certs { + return request_list('list-certs', 'list-cert', @_); +} + +sub list_authorities { + return request_list('list-authorities', 'list-authority', @_); +} + +sub get_authorities { + return request('get-authorities', @_); +} + +sub load_conn { + return request_vars_res('load-conn', @_); +} + +sub unload_conn { + return request_vars_res('unload-conn', @_); +} + +sub load_cert { + return request_vars_res('load-cert', @_); +} + +sub load_key { + return request_vars_res('load-key', @_); +} + +sub load_shared { + return request_vars_res('load-shared', @_); +} + +sub clear_creds { + return request_res('clear-creds', @_); +} + +sub load_authority { + return request_vars_res('load-authority', @_); +} + +sub unload_authority { + return request_vars_res('unload-authority', @_); +} + +sub load_pool { + return request_vars_res('load-pool', @_); +} + +sub unload_pool { + return request_vars_res('unload-pool', @_); +} + +sub get_pools { + return request('get-pools', @_); +} + +sub get_algorithms { + return request('get-algorithms', @_); +} + +# Private functions + +sub request { + my ($command, $self) = @_; + return $self->{'Packet'}->request($command); +} + +sub request_res { + my ($command, $self) = @_; + my $msg = $self->{'Packet'}->request($command); + return $msg->result(); +} + +sub request_vars_res { + my ($command, $self, $vars) = @_; + my $msg = $self->{'Packet'}->request($command, $vars); + return $msg->result(); +} + +sub request_list { + my ($command, $event, $self, $vars) = @_; + return $self->{'Packet'}->streamed_request($command, $event, $vars); +} + +1; +__END__ +=head1 NAME + +Vici::Session - Perl binding for the strongSwan VICI configuration interface + +=head1 SYNOPSIS + + use Vici::Session; + +=head1 DESCRIPTION + +The Vici::Session module allows a Perl script to communicate with the open +source strongSwan IPsec daemon (https://www.strongswan.com) via the documented +Versatile IKE Configuration Interface (VICI). VICI allows the configuration, +management and monitoring of multiple IPsec connections. + +=head2 EXPORT + +None by default. + +=head1 SEE ALSO + +strongSwan Wiki: https://wiki.strongswan.org/projects/strongswan/wiki/Vici + +strongSwan Mailing list: users@lists.strongswan.org + +=head1 AUTHOR + +Andreas Steffen, E<lt>andreas.steffen@strongswan.orgE<gt> + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2015 by Andreas Steffen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +=cut diff --git a/src/libcharon/plugins/vici/perl/Vici-Session/lib/Vici/Transport.pm b/src/libcharon/plugins/vici/perl/Vici-Session/lib/Vici/Transport.pm new file mode 100644 index 000000000..6524bf76d --- /dev/null +++ b/src/libcharon/plugins/vici/perl/Vici-Session/lib/Vici/Transport.pm @@ -0,0 +1,88 @@ +package Vici::Transport; + +our $VERSION = '0.9'; + +use strict; + +sub new { + my $class = shift; + my $self = { + Socket => shift, + }; + bless($self, $class); + return $self; +} + +sub send { + my ($self, $data) = @_; + my $packet = pack('N/a*', $data); + $self->{'Socket'}->send($packet); +} + +sub receive { + my $self = shift; + my $packet_header; + my $data; + + $self->{'Socket'}->recv($packet_header, 4); + my $packet_len = unpack('N', $packet_header); + $self->{'Socket'}->recv($data, $packet_len); + return $data; +} + +1; +__END__ +=head1 NAME + +Vici::Transport - Perl extension for communicating via a strongSwan VICI socket + +=head1 SYNOPSIS + + use Vici::Transport; + +=head1 DESCRIPTION + +The Vici::Transport module is needed by the Vici::Packet module to send +and receive packets over the UNIX socket used in the communication with the +open source strongSwan IPsec daemon (https://www.strongswan.com) via the +documented Versatile IKE Configuration Interface (VICI). VICI allows the +onfiguration, management and monitoring of multiple IPsec connections. + +=head2 EXPORT + +None by default. + +=head1 SEE ALSO + +strongSwan Wiki: https://wiki.strongswan.org/projects/strongswan/wiki/Vici + +strongSwan Mailing list: users@lists.strongswan.org + +=head1 AUTHOR + +Andreas Steffen, E<lt>andreas.steffen@strongswan.orgE<gt> + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2015 by Andreas Steffen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +=cut + diff --git a/src/libcharon/plugins/vici/perl/Vici-Session/t/Vici-Session.t b/src/libcharon/plugins/vici/perl/Vici-Session/t/Vici-Session.t new file mode 100644 index 000000000..4c321f3e1 --- /dev/null +++ b/src/libcharon/plugins/vici/perl/Vici-Session/t/Vici-Session.t @@ -0,0 +1,18 @@ +# Before 'make install' is performed this script should be runnable with +# 'make test'. After 'make install' it should work as 'perl Vici-Session.t' + +######################### + +# change 'tests => 1' to 'tests => last_test_to_print'; + +use strict; +use warnings; + +use Test::More tests => 1; +BEGIN { use_ok('Vici::Session') }; + +######################### + +# Insert your test code below, the Test::More module is use()ed here so read +# its man page ( perldoc Test::More ) for help writing this test script. + diff --git a/src/libcharon/plugins/vici/python/Makefile.in b/src/libcharon/plugins/vici/python/Makefile.in index eb4bab6ca..894a7e275 100644 --- a/src/libcharon/plugins/vici/python/Makefile.in +++ b/src/libcharon/plugins/vici/python/Makefile.in @@ -351,6 +351,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ diff --git a/src/libcharon/plugins/vici/python/vici/session.py b/src/libcharon/plugins/vici/python/vici/session.py index 283e3d13d..66de8590a 100644 --- a/src/libcharon/plugins/vici/python/vici/session.py +++ b/src/libcharon/plugins/vici/python/vici/session.py @@ -53,6 +53,14 @@ class Session(object): """ return self.handler.streamed_request("terminate", "control-log", sa) + def redirect(self, sa): + """Redirect an IKE_SA. + + :param sa: the SA to redirect + :type sa: dict + """ + self.handler.request("redirect", sa) + def install(self, policy): """Install a trap, drop or bypass policy defined by a CHILD_SA config. diff --git a/src/libcharon/plugins/vici/ruby/Makefile.in b/src/libcharon/plugins/vici/ruby/Makefile.in index bf81e5395..b87d83de4 100644 --- a/src/libcharon/plugins/vici/ruby/Makefile.in +++ b/src/libcharon/plugins/vici/ruby/Makefile.in @@ -329,6 +329,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ diff --git a/src/libcharon/plugins/vici/ruby/lib/vici.rb b/src/libcharon/plugins/vici/ruby/lib/vici.rb index f8169add0..018f50766 100644 --- a/src/libcharon/plugins/vici/ruby/lib/vici.rb +++ b/src/libcharon/plugins/vici/ruby/lib/vici.rb @@ -505,6 +505,12 @@ module Vici end ## + # Redirect an IKE_SA. + def redirect(options) + check_success(@transp.request("redirect", Message.new(options))) + end + + ## # Install a shunt/route policy. def install(policy) check_success(@transp.request("install", Message.new(policy))) diff --git a/src/libcharon/plugins/vici/vici_cert_info.c b/src/libcharon/plugins/vici/vici_cert_info.c new file mode 100644 index 000000000..2f278de5e --- /dev/null +++ b/src/libcharon/plugins/vici/vici_cert_info.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015 Andreas Steffen + * HSR Hochschule fuer Technik Rapperswil + * + * 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 "vici_cert_info.h" + +/** + * Legacy vici certificate types and directories created by swanctl + */ +typedef struct { + + /** Certificate type string used in legacy vici messages */ + char *type_str; + /** Base certificate type */ + certificate_type_t type; + /** X.509 flag */ + x509_flag_t flag; +} cert_type_t; + +static cert_type_t cert_types[] = { + { "x509", CERT_X509, X509_NONE }, + { "x509ca", CERT_X509, X509_CA }, + { "x509ocsp", CERT_X509, X509_OCSP_SIGNER }, + { "x509aa", CERT_X509, X509_AA }, + { "x509ac", CERT_X509_AC, X509_NONE }, + { "x509crl", CERT_X509_CRL, X509_NONE }, + { "pubkey", CERT_TRUSTED_PUBKEY, X509_NONE }, +}; + +bool vici_cert_info_from_str(char *type_str, certificate_type_t *type, + x509_flag_t *flag) +{ + int i; + + for (i = 0; i < countof(cert_types); i++) + { + if (strcaseeq(type_str, cert_types[i].type_str)) + { + *type = cert_types[i].type; + *flag = cert_types[i].flag; + return TRUE; + } + } + return FALSE; +} + diff --git a/src/libcharon/plugins/vici/vici_cert_info.h b/src/libcharon/plugins/vici/vici_cert_info.h new file mode 100644 index 000000000..e2a8c4d9f --- /dev/null +++ b/src/libcharon/plugins/vici/vici_cert_info.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2015 Andreas Steffen + * HSR Hochschule fuer Technik Rapperswil + * + * 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 vici_cert_info vici_cert_info + * @{ @ingroup vici + */ + +#ifndef VICI_CERT_INFO_H_ +#define VICI_CERT_INFO_H_ + +typedef struct vici_cert_info_t vici_cert_info_t; + +#include <credentials/certificates/certificate.h> +#include <credentials/certificates/x509.h> + +bool vici_cert_info_from_str(char *type_str, certificate_type_t *type, + x509_flag_t *flag); + +#endif /** VICI_CERT_INFO_H_ @}*/ diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index ea6d2958a..6ebbedc47 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -2,7 +2,8 @@ * Copyright (C) 2014 Martin Willi * Copyright (C) 2014 revosec AG * - * Copyright (C) 2015 Andreas Steffen + * Copyright (C) 2015-2016 Tobias Brunner + * Copyright (C) 2015-2016 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -45,9 +46,12 @@ #include <daemon.h> #include <threading/rwlock.h> +#include <threading/rwlock_condvar.h> #include <collections/array.h> #include <collections/linked_list.h> +#include <pubkey_cert.h> + #include <stdio.h> /** @@ -98,6 +102,21 @@ struct private_vici_config_t { rwlock_t *lock; /** + * Condvar used to snyc running actions + */ + rwlock_condvar_t *condvar; + + /** + * True while we run or undo a start action + */ + bool handling_actions; + + /** + * Credential backend managed by VICI used for our certificates + */ + vici_cred_t *cred; + + /** * Auxiliary certification authority information */ vici_authority_t *authority; @@ -218,6 +237,24 @@ typedef struct { } request_data_t; /** + * Auth config data + */ +typedef struct { + request_data_t *request; + auth_cfg_t *cfg; + u_int32_t round; +} auth_data_t; + +/** + * Clean up auth config data + */ +static void free_auth_data(auth_data_t *data) +{ + DESTROY_IF(data->cfg); + free(data); +} + +/** * Data associated to a peer config */ typedef struct { @@ -311,7 +348,7 @@ static void log_auth(auth_cfg_t *auth) static void log_peer_data(peer_data_t *data) { enumerator_t *enumerator; - auth_cfg_t *auth; + auth_data_t *auth; host_t *host; DBG2(DBG_CFG, " version = %u", data->version); @@ -350,7 +387,7 @@ static void log_peer_data(peer_data_t *data) while (enumerator->enumerate(enumerator, &auth)) { DBG2(DBG_CFG, " local:"); - log_auth(auth); + log_auth(auth->cfg); } enumerator->destroy(enumerator); @@ -358,7 +395,7 @@ static void log_peer_data(peer_data_t *data) while (enumerator->enumerate(enumerator, &auth)) { DBG2(DBG_CFG, " remote:"); - log_auth(auth); + log_auth(auth->cfg); } enumerator->destroy(enumerator); } @@ -368,10 +405,8 @@ static void log_peer_data(peer_data_t *data) */ static void free_peer_data(peer_data_t *data) { - data->local->destroy_offset(data->local, - offsetof(auth_cfg_t, destroy)); - data->remote->destroy_offset(data->remote, - offsetof(auth_cfg_t, destroy)); + data->local->destroy_function(data->local, (void*)free_auth_data); + data->remote->destroy_function(data->remote, (void*)free_auth_data); data->children->destroy_offset(data->children, offsetof(child_cfg_t, destroy)); data->proposals->destroy_offset(data->proposals, @@ -461,14 +496,6 @@ static void free_child_data(child_data_t *data) } /** - * Auth config data - */ -typedef struct { - request_data_t *request; - auth_cfg_t *cfg; -} auth_data_t; - -/** * Common proposal parsing */ static bool parse_proposal(linked_list_t *list, protocol_id_t proto, chunk_t v) @@ -537,7 +564,7 @@ CALLBACK(parse_ts, bool, linked_list_t *out, chunk_t v) { char buf[128], *protoport, *sep, *port = "", *end; - traffic_selector_t *ts; + traffic_selector_t *ts = NULL; struct protoent *protoent; struct servent *svc; long int p; @@ -630,6 +657,22 @@ CALLBACK(parse_ts, bool, { ts = traffic_selector_create_dynamic(proto, from, to); } + else if (strchr(buf, '-')) + { + host_t *lower, *upper; + ts_type_t type; + + if (host_create_from_range(buf, &lower, &upper)) + { + type = (lower->get_family(lower) == AF_INET) ? + TS_IPV4_ADDR_RANGE : TS_IPV6_ADDR_RANGE; + ts = traffic_selector_create_from_bytes(proto, type, + lower->get_address(lower), from, + upper->get_address(upper), to); + lower->destroy(lower); + upper->destroy(upper); + } + } else { ts = traffic_selector_create_from_cidr(buf, proto, from, to); @@ -948,9 +991,14 @@ CALLBACK(parse_auth, bool, { return FALSE; } - if (strcaseeq(buf, "pubkey")) + if (strpfx(buf, "ike:") || + strpfx(buf, "pubkey") || + strpfx(buf, "rsa") || + strpfx(buf, "ecdsa") || + strpfx(buf, "bliss")) { cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY); + cfg->add_pubkey_constraints(cfg, buf, TRUE); return TRUE; } if (strcaseeq(buf, "psk")) @@ -970,8 +1018,16 @@ CALLBACK(parse_auth, bool, } if (strcasepfx(buf, "eap")) { + char *pos; + cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP); + pos = strchr(buf, ':'); + if (pos) + { + *pos = 0; + cfg->add_pubkey_constraints(cfg, pos + 1, FALSE); + } type = eap_vendor_type_from_string(buf); if (type) { @@ -1053,6 +1109,7 @@ CALLBACK(parse_group, bool, static bool parse_cert(auth_data_t *auth, auth_rule_t rule, chunk_t v) { vici_authority_t *authority; + vici_cred_t *cred; certificate_t *cert; cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, @@ -1064,6 +1121,8 @@ static bool parse_cert(auth_data_t *auth, auth_rule_t rule, chunk_t v) authority = auth->request->this->authority; authority->check_for_hash_and_url(authority, cert); } + cred = auth->request->this->cred; + cert = cred->add_cert(cred, cert); auth->cfg->add(auth->cfg, rule, cert); return TRUE; } @@ -1089,6 +1148,27 @@ CALLBACK(parse_cacerts, bool, } /** + * Parse raw public keys + */ +CALLBACK(parse_pubkeys, bool, + auth_data_t *auth, chunk_t v) +{ + vici_cred_t *cred; + certificate_t *cert; + + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_TRUSTED_PUBKEY, + BUILD_BLOB_PEM, v, BUILD_END); + if (cert) + { + cred = auth->request->this->cred; + cert = cred->add_cert(cred, cert); + auth->cfg->add(auth->cfg, AUTH_RULE_SUBJECT_CERT, cert); + return TRUE; + } + return FALSE; +} + +/** * Parse revocation status */ CALLBACK(parse_revocation, bool, @@ -1283,6 +1363,7 @@ CALLBACK(auth_li, bool, { "groups", parse_group, auth->cfg }, { "certs", parse_certs, auth }, { "cacerts", parse_cacerts, auth }, + { "pubkeys", parse_pubkeys, auth }, }; return parse_rules(rules, countof(rules), name, value, @@ -1299,6 +1380,7 @@ CALLBACK(auth_kv, bool, { "eap_id", parse_eap_id, auth->cfg }, { "xauth_id", parse_xauth_id, auth->cfg }, { "revocation", parse_revocation, auth->cfg }, + { "round", parse_uint32, &auth->round }, }; return parse_rules(rules, countof(rules), name, value, @@ -1502,40 +1584,62 @@ CALLBACK(peer_sn, bool, if (strcasepfx(name, "local") || strcasepfx(name, "remote")) { - auth_data_t auth = { + enumerator_t *enumerator; + linked_list_t *auths; + auth_data_t *auth, *current; + auth_rule_t rule; + certificate_t *cert; + pubkey_cert_t *pubkey_cert; + identification_t *id; + bool default_id = FALSE; + + INIT(auth, .request = peer->request, .cfg = auth_cfg_create(), - }; + ); - if (!message->parse(message, ctx, NULL, auth_kv, auth_li, &auth)) + if (!message->parse(message, ctx, NULL, auth_kv, auth_li, auth)) { - auth.cfg->destroy(auth.cfg); + free_auth_data(auth); return FALSE; } + id = auth->cfg->get(auth->cfg, AUTH_RULE_IDENTITY); - if (!auth.cfg->get(auth.cfg, AUTH_RULE_IDENTITY)) + enumerator = auth->cfg->create_enumerator(auth->cfg); + while (enumerator->enumerate(enumerator, &rule, &cert)) { - identification_t *id; - certificate_t *cert; - - cert = auth.cfg->get(auth.cfg, AUTH_RULE_SUBJECT_CERT); - if (cert) + if (rule == AUTH_RULE_SUBJECT_CERT && !default_id) { - id = cert->get_subject(cert); - DBG1(DBG_CFG, " id not specified, defaulting to cert id '%Y'", - id); - auth.cfg->add(auth.cfg, AUTH_RULE_IDENTITY, id->clone(id)); + if (id == NULL) + { + id = cert->get_subject(cert); + DBG1(DBG_CFG, " id not specified, defaulting to" + " cert subject '%Y'", id); + auth->cfg->add(auth->cfg, AUTH_RULE_IDENTITY, id->clone(id)); + default_id = TRUE; + } + else if (cert->get_type(cert) == CERT_TRUSTED_PUBKEY && + id->get_type != ID_ANY) + { + /* set the subject of all raw public keys to the id */ + pubkey_cert = (pubkey_cert_t*)cert; + pubkey_cert->set_subject(pubkey_cert, id); + } } } + enumerator->destroy(enumerator); - if (strcasepfx(name, "local")) + auths = strcasepfx(name, "local") ? peer->local : peer->remote; + enumerator = auths->create_enumerator(auths); + while (enumerator->enumerate(enumerator, ¤t)) { - peer->local->insert_last(peer->local, auth.cfg); - } - else - { - peer->remote->insert_last(peer->remote, auth.cfg); + if (auth->round < current->round) + { + break; + } } + auths->insert_before(auths, enumerator, auth); + enumerator->destroy(enumerator); return TRUE; } peer->request->reply = create_reply("invalid section: %s", name); @@ -1578,7 +1682,7 @@ static u_int32_t find_reqid(child_cfg_t *cfg) } /** - * Perform start actions associated to a child config + * Perform start actions associated with a child config */ static void run_start_action(private_vici_config_t *this, peer_cfg_t *peer_cfg, child_cfg_t *child_cfg) @@ -1611,19 +1715,20 @@ static void run_start_action(private_vici_config_t *this, peer_cfg_t *peer_cfg, } /** - * Undo start actions associated to a child config + * Undo start actions associated with a child config */ -static void clear_start_action(private_vici_config_t *this, +static void clear_start_action(private_vici_config_t *this, char *peer_name, child_cfg_t *child_cfg) { enumerator_t *enumerator, *children; child_sa_t *child_sa; ike_sa_t *ike_sa; - u_int32_t id = 0, *del; - array_t *ids = NULL; + u_int32_t id = 0, others; + array_t *ids = NULL, *ikeids = NULL; char *name; name = child_cfg->get_name(child_cfg); + switch (child_cfg->get_start_action(child_cfg)) { case ACTION_RESTART: @@ -1631,29 +1736,72 @@ static void clear_start_action(private_vici_config_t *this, charon->controller, TRUE); while (enumerator->enumerate(enumerator, &ike_sa)) { + if (!streq(ike_sa->get_name(ike_sa), peer_name)) + { + continue; + } + others = id = 0; children = ike_sa->create_child_sa_enumerator(ike_sa); while (children->enumerate(children, &child_sa)) { - if (streq(name, child_sa->get_name(child_sa))) + if (child_sa->get_state(child_sa) != CHILD_DELETING) { - id = child_sa->get_unique_id(child_sa); - array_insert_create(&ids, ARRAY_TAIL, &id); + if (streq(name, child_sa->get_name(child_sa))) + { + id = child_sa->get_unique_id(child_sa); + } + else + { + others++; + } } } children->destroy(children); + + if (id && !others) + { + /* found matching children only, delete full IKE_SA */ + id = ike_sa->get_unique_id(ike_sa); + array_insert_create_value(&ikeids, sizeof(id), + ARRAY_TAIL, &id); + } + else + { + children = ike_sa->create_child_sa_enumerator(ike_sa); + while (children->enumerate(children, &child_sa)) + { + if (streq(name, child_sa->get_name(child_sa))) + { + id = child_sa->get_unique_id(child_sa); + array_insert_create_value(&ids, sizeof(id), + ARRAY_TAIL, &id); + } + } + children->destroy(children); + } } enumerator->destroy(enumerator); if (array_count(ids)) { - while (array_remove(ids, ARRAY_HEAD, &del)) + while (array_remove(ids, ARRAY_HEAD, &id)) { - DBG1(DBG_CFG, "closing '%s' #%u", name, *del); + DBG1(DBG_CFG, "closing '%s' #%u", name, id); charon->controller->terminate_child(charon->controller, - *del, NULL, NULL, 0); + id, NULL, NULL, 0); } array_destroy(ids); } + if (array_count(ikeids)) + { + while (array_remove(ikeids, ARRAY_HEAD, &id)) + { + DBG1(DBG_CFG, "closing IKE_SA #%u", id); + charon->controller->terminate_ike(charon->controller, + id, NULL, NULL, 0); + } + array_destroy(ikeids); + } break; case ACTION_ROUTE: DBG1(DBG_CFG, "uninstalling '%s'", name); @@ -1687,36 +1835,56 @@ static void clear_start_action(private_vici_config_t *this, } /** - * Run start actions associated to all child configs of a peer config + * Run or undo a start actions associated with a child config */ -static void run_start_actions(private_vici_config_t *this, peer_cfg_t *peer_cfg) +static void handle_start_action(private_vici_config_t *this, + peer_cfg_t *peer_cfg, child_cfg_t *child_cfg, + bool undo) { - enumerator_t *enumerator; - child_cfg_t *child_cfg; + this->handling_actions = TRUE; + this->lock->unlock(this->lock); - enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg); - while (enumerator->enumerate(enumerator, &child_cfg)) + if (undo) + { + clear_start_action(this, peer_cfg->get_name(peer_cfg), child_cfg); + } + else { run_start_action(this, peer_cfg, child_cfg); } - enumerator->destroy(enumerator); + + this->lock->write_lock(this->lock); + this->handling_actions = FALSE; } /** - * Undo start actions associated to all child configs of a peer config + * Run or undo start actions associated with all child configs of a peer config */ -static void clear_start_actions(private_vici_config_t *this, - peer_cfg_t *peer_cfg) +static void handle_start_actions(private_vici_config_t *this, + peer_cfg_t *peer_cfg, bool undo) { enumerator_t *enumerator; child_cfg_t *child_cfg; + this->handling_actions = TRUE; + this->lock->unlock(this->lock); + enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg); while (enumerator->enumerate(enumerator, &child_cfg)) { - clear_start_action(this, child_cfg); + if (undo) + { + clear_start_action(this, peer_cfg->get_name(peer_cfg), child_cfg); + } + else + { + run_start_action(this, peer_cfg, child_cfg); + } } enumerator->destroy(enumerator); + + this->lock->write_lock(this->lock); + this->handling_actions = FALSE; } /** @@ -1727,22 +1895,12 @@ static void replace_children(private_vici_config_t *this, { enumerator_t *enumerator; child_cfg_t *child; + bool added; - enumerator = to->create_child_cfg_enumerator(to); - while (enumerator->enumerate(enumerator, &child)) + enumerator = to->replace_child_cfgs(to, from); + while (enumerator->enumerate(enumerator, &child, &added)) { - to->remove_child_cfg(to, enumerator); - clear_start_action(this, child); - child->destroy(child); - } - enumerator->destroy(enumerator); - - enumerator = from->create_child_cfg_enumerator(from); - while (enumerator->enumerate(enumerator, &child)) - { - from->remove_child_cfg(from, enumerator); - to->add_child_cfg(to, child); - run_start_action(this, to, child); + handle_start_action(this, to, child, !added); } enumerator->destroy(enumerator); } @@ -1758,6 +1916,10 @@ static void merge_config(private_vici_config_t *this, peer_cfg_t *peer_cfg) bool merged = FALSE; this->lock->write_lock(this->lock); + while (this->handling_actions) + { + this->condvar->wait(this->condvar, this->lock); + } enumerator = this->conns->create_enumerator(this->conns); while (enumerator->enumerate(enumerator, ¤t)) @@ -1778,10 +1940,10 @@ static void merge_config(private_vici_config_t *this, peer_cfg_t *peer_cfg) DBG1(DBG_CFG, "replaced vici connection: %s", peer_cfg->get_name(peer_cfg)); this->conns->remove_at(this->conns, enumerator); - clear_start_actions(this, current); - current->destroy(current); this->conns->insert_last(this->conns, peer_cfg); - run_start_actions(this, peer_cfg); + handle_start_actions(this, current, TRUE); + handle_start_actions(this, peer_cfg, FALSE); + current->destroy(current); } merged = TRUE; break; @@ -1793,9 +1955,9 @@ static void merge_config(private_vici_config_t *this, peer_cfg_t *peer_cfg) { DBG1(DBG_CFG, "added vici connection: %s", peer_cfg->get_name(peer_cfg)); this->conns->insert_last(this->conns, peer_cfg); - run_start_actions(this, peer_cfg); + handle_start_actions(this, peer_cfg, FALSE); } - + this->condvar->signal(this->condvar); this->lock->unlock(this->lock); } @@ -1828,7 +1990,7 @@ CALLBACK(config_sn, bool, peer_cfg_t *peer_cfg; ike_cfg_t *ike_cfg; child_cfg_t *child_cfg; - auth_cfg_t *auth_cfg; + auth_data_t *auth; proposal_t *proposal; host_t *host; char *str; @@ -1843,14 +2005,17 @@ CALLBACK(config_sn, bool, if (peer.local->get_count(peer.local) == 0) { - free_peer_data(&peer); - peer.request->reply = create_reply("missing local auth config"); - return FALSE; + INIT(auth, + .cfg = auth_cfg_create(), + ); + peer.local->insert_last(peer.local, auth); } if (peer.remote->get_count(peer.remote) == 0) { - auth_cfg = auth_cfg_create(); - peer.remote->insert_last(peer.remote, auth_cfg); + INIT(auth, + .cfg = auth_cfg_create(), + ); + peer.remote->insert_last(peer.remote, auth); } if (peer.proposals->get_count(peer.proposals) == 0) { @@ -1926,14 +2091,18 @@ CALLBACK(config_sn, bool, FALSE, NULL, NULL); while (peer.local->remove_first(peer.local, - (void**)&auth_cfg) == SUCCESS) + (void**)&auth) == SUCCESS) { - peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, TRUE); + peer_cfg->add_auth_cfg(peer_cfg, auth->cfg, TRUE); + auth->cfg = NULL; + free_auth_data(auth); } while (peer.remote->remove_first(peer.remote, - (void**)&auth_cfg) == SUCCESS) + (void**)&auth) == SUCCESS) { - peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, FALSE); + peer_cfg->add_auth_cfg(peer_cfg, auth->cfg, FALSE); + auth->cfg = NULL; + free_auth_data(auth); } while (peer.children->remove_first(peer.children, (void**)&child_cfg) == SUCCESS) @@ -1999,18 +2168,24 @@ CALLBACK(unload_conn, vici_message_t*, } this->lock->write_lock(this->lock); + while (this->handling_actions) + { + this->condvar->wait(this->condvar, this->lock); + } enumerator = this->conns->create_enumerator(this->conns); while (enumerator->enumerate(enumerator, &cfg)) { if (streq(cfg->get_name(cfg), conn_name)) { this->conns->remove_at(this->conns, enumerator); + handle_start_actions(this, cfg, TRUE); cfg->destroy(cfg); found = TRUE; break; } } enumerator->destroy(enumerator); + this->condvar->signal(this->condvar); this->lock->unlock(this->lock); if (!found) @@ -2066,6 +2241,7 @@ METHOD(vici_config_t, destroy, void, { manage_commands(this, FALSE); this->conns->destroy_offset(this->conns, offsetof(peer_cfg_t, destroy)); + this->condvar->destroy(this->condvar); this->lock->destroy(this->lock); free(this); } @@ -2074,7 +2250,8 @@ METHOD(vici_config_t, destroy, void, * See header */ vici_config_t *vici_config_create(vici_dispatcher_t *dispatcher, - vici_authority_t *authority) + vici_authority_t *authority, + vici_cred_t *cred) { private_vici_config_t *this; @@ -2090,7 +2267,9 @@ vici_config_t *vici_config_create(vici_dispatcher_t *dispatcher, .dispatcher = dispatcher, .conns = linked_list_create(), .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + .condvar = rwlock_condvar_create(), .authority = authority, + .cred = cred, ); manage_commands(this, TRUE); diff --git a/src/libcharon/plugins/vici/vici_config.h b/src/libcharon/plugins/vici/vici_config.h index c3245bf5c..0c237e7de 100644 --- a/src/libcharon/plugins/vici/vici_config.h +++ b/src/libcharon/plugins/vici/vici_config.h @@ -26,6 +26,7 @@ #include "vici_dispatcher.h" #include "vici_authority.h" +#include "vici_cred.h" #include <config/backend.h> @@ -51,9 +52,11 @@ struct vici_config_t { * * @param dispatcher dispatcher to receive requests from * @param authority Auxiliary certification authority information + * @param cred in-memory credential backend managed by VICI * @return config backend */ vici_config_t *vici_config_create(vici_dispatcher_t *dispatcher, - vici_authority_t *authority); + vici_authority_t *authority, + vici_cred_t *cred); #endif /** VICI_CONFIG_H_ @}*/ diff --git a/src/libcharon/plugins/vici/vici_control.c b/src/libcharon/plugins/vici/vici_control.c index 752007c24..c526d2fda 100644 --- a/src/libcharon/plugins/vici/vici_control.c +++ b/src/libcharon/plugins/vici/vici_control.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2015 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * * Copyright (C) 2014 Martin Willi * Copyright (C) 2014 revosec AG * @@ -20,6 +23,7 @@ #include <daemon.h> #include <collections/array.h> +#include <processing/jobs/redirect_job.h> typedef struct private_vici_control_t private_vici_control_t; @@ -134,7 +138,7 @@ static child_cfg_t* get_child_from_peer(peer_cfg_t *peer_cfg, char *name) /** * Find a peer/child config from a child config name */ -static child_cfg_t* find_child_cfg(char *name, peer_cfg_t **out) +static child_cfg_t* find_child_cfg(char *name, char *pname, peer_cfg_t **out) { enumerator_t *enumerator; peer_cfg_t *peer_cfg; @@ -144,6 +148,10 @@ static child_cfg_t* find_child_cfg(char *name, peer_cfg_t **out) charon->backends, NULL, NULL, NULL, NULL, IKE_ANY); while (enumerator->enumerate(enumerator, &peer_cfg)) { + if (pname && !streq(pname, peer_cfg->get_name(peer_cfg))) + { + continue; + } child_cfg = get_child_from_peer(peer_cfg, name); if (child_cfg) { @@ -161,15 +169,17 @@ CALLBACK(initiate, vici_message_t*, { child_cfg_t *child_cfg = NULL; peer_cfg_t *peer_cfg; - char *child; - u_int timeout; + char *child, *ike; + int timeout; bool limits; + controller_cb_t log_cb = NULL; log_info_t log = { .dispatcher = this->dispatcher, .id = id, }; child = request->get_str(request, NULL, "child"); + ike = request->get_str(request, NULL, "ike"); timeout = request->get_int(request, 0, "timeout"); limits = request->get_bool(request, FALSE, "init-limits"); log.level = request->get_int(request, 1, "loglevel"); @@ -178,16 +188,20 @@ CALLBACK(initiate, vici_message_t*, { return send_reply(this, "missing configuration name"); } + if (timeout >= 0) + { + log_cb = (controller_cb_t)log_vici; + } DBG1(DBG_CFG, "vici initiate '%s'", child); - child_cfg = find_child_cfg(child, &peer_cfg); + child_cfg = find_child_cfg(child, ike, &peer_cfg); if (!child_cfg) { return send_reply(this, "CHILD_SA config '%s' not found", child); } switch (charon->controller->initiate(charon->controller, peer_cfg, - child_cfg, (controller_cb_t)log_vici, &log, timeout, limits)) + child_cfg, log_cb, &log, timeout, limits)) { case SUCCESS: return send_reply(this, NULL); @@ -208,11 +222,13 @@ CALLBACK(terminate, vici_message_t*, { enumerator_t *enumerator, *isas, *csas; char *child, *ike, *errmsg = NULL; - u_int timeout, child_id, ike_id, current, *del, done = 0; + u_int child_id, ike_id, current, *del, done = 0; + int timeout; ike_sa_t *ike_sa; child_sa_t *child_sa; array_t *ids; vici_builder_t *builder; + controller_cb_t log_cb = NULL; log_info_t log = { .dispatcher = this->dispatcher, .id = id, @@ -247,6 +263,11 @@ CALLBACK(terminate, vici_message_t*, DBG1(DBG_CFG, "vici terminate CHILD_SA '%s'", child); } + if (timeout >= 0) + { + log_cb = (controller_cb_t)log_vici; + } + ids = array_create(sizeof(u_int), 0); isas = charon->controller->create_ike_sa_enumerator(charon->controller, TRUE); @@ -296,7 +317,7 @@ CALLBACK(terminate, vici_message_t*, if (child || child_id) { if (charon->controller->terminate_child(charon->controller, *del, - (controller_cb_t)log_vici, &log, timeout) == SUCCESS) + log_cb, &log, timeout) == SUCCESS) { done++; } @@ -304,7 +325,7 @@ CALLBACK(terminate, vici_message_t*, else { if (charon->controller->terminate_ike(charon->controller, *del, - (controller_cb_t)log_vici, &log, timeout) == SUCCESS) + log_cb, &log, timeout) == SUCCESS) { done++; } @@ -340,6 +361,150 @@ CALLBACK(terminate, vici_message_t*, } /** + * Parse a peer-ip specified, which can be a subnet in CIDR notation, a range + * or a single IP address. + */ +static traffic_selector_t *parse_peer_ip(char *ip) +{ + traffic_selector_t *ts; + host_t *from, *to; + ts_type_t type; + + if (host_create_from_range(ip, &from, &to)) + { + if (to->get_family(to) == AF_INET) + { + type = TS_IPV4_ADDR_RANGE; + } + else + { + type = TS_IPV6_ADDR_RANGE; + } + ts = traffic_selector_create_from_bytes(0, type, + from->get_address(from), 0, + to->get_address(to), 0xFFFF); + from->destroy(from); + to->destroy(to); + return ts; + } + return traffic_selector_create_from_cidr(ip, 0, 0, 0xFFFF); +} + +CALLBACK(redirect, vici_message_t*, + private_vici_control_t *this, char *name, u_int id, vici_message_t *request) +{ + enumerator_t *sas; + char *ike, *peer_ip, *peer_id, *gw, *errmsg = NULL; + u_int ike_id, current, found = 0; + identification_t *gateway, *identity = NULL, *other_id; + traffic_selector_t *ts = NULL; + ike_sa_t *ike_sa; + vici_builder_t *builder; + + ike = request->get_str(request, NULL, "ike"); + ike_id = request->get_int(request, 0, "ike-id"); + peer_ip = request->get_str(request, NULL, "peer-ip"); + peer_id = request->get_str(request, NULL, "peer-id"); + gw = request->get_str(request, NULL, "gateway"); + + if (!gw || !(gateway = identification_create_from_string(gw))) + { + return send_reply(this, "missing target gateway"); + } + switch (gateway->get_type(gateway)) + { + case ID_IPV4_ADDR: + case ID_IPV6_ADDR: + case ID_FQDN: + break; + default: + return send_reply(this, "unsupported gateway identity"); + } + if (peer_ip) + { + ts = parse_peer_ip(peer_ip); + if (!ts) + { + return send_reply(this, "invalid peer IP selector"); + } + DBG1(DBG_CFG, "vici redirect IKE_SAs with src %R to %Y", ts, + gateway); + } + if (peer_id) + { + identity = identification_create_from_string(peer_id); + if (!identity) + { + DESTROY_IF(ts); + return send_reply(this, "invalid peer identity selector"); + } + DBG1(DBG_CFG, "vici redirect IKE_SAs with ID '%Y' to %Y", identity, + gateway); + } + if (ike_id) + { + DBG1(DBG_CFG, "vici redirect IKE_SA #%d to '%Y'", ike_id, gateway); + } + if (ike) + { + DBG1(DBG_CFG, "vici redirect IKE_SA '%s' to '%Y'", ike, gateway); + } + if (!peer_ip && !peer_id && !ike && !ike_id) + { + return send_reply(this, "missing redirect selector"); + } + + sas = charon->controller->create_ike_sa_enumerator(charon->controller, TRUE); + while (sas->enumerate(sas, &ike_sa)) + { + if (ike_sa->get_version(ike_sa) != IKEV2) + { + continue; + } + current = ike_sa->get_unique_id(ike_sa); + if (ike_id && ike_id != current) + { + continue; + } + if (ike && !streq(ike, ike_sa->get_name(ike_sa))) + { + continue; + } + if (ts && !ts->includes(ts, ike_sa->get_other_host(ike_sa))) + { + continue; + } + if (identity) + { + other_id = ike_sa->get_other_eap_id(ike_sa); + if (!other_id->matches(other_id, identity)) + { + continue; + } + } + lib->processor->queue_job(lib->processor, + (job_t*)redirect_job_create(ike_sa->get_id(ike_sa), gateway)); + found++; + } + sas->destroy(sas); + + builder = vici_builder_create(); + if (!found) + { + errmsg = "no matching SAs to redirect found"; + } + builder->add_kv(builder, "success", errmsg ? "no" : "yes"); + if (errmsg) + { + builder->add_kv(builder, "errmsg", "%s", errmsg); + } + gateway->destroy(gateway); + DESTROY_IF(identity); + DESTROY_IF(ts); + return builder->finalize(builder); +} + +/** * Find reqid of an existing CHILD_SA */ static u_int32_t find_reqid(child_cfg_t *cfg) @@ -379,10 +544,11 @@ CALLBACK(install, vici_message_t*, { child_cfg_t *child_cfg = NULL; peer_cfg_t *peer_cfg; - char *child; + char *child, *ike; bool ok; child = request->get_str(request, NULL, "child"); + ike = request->get_str(request, NULL, "ike"); if (!child) { return send_reply(this, "missing configuration name"); @@ -390,7 +556,7 @@ CALLBACK(install, vici_message_t*, DBG1(DBG_CFG, "vici install '%s'", child); - child_cfg = find_child_cfg(child, &peer_cfg); + child_cfg = find_child_cfg(child, ike, &peer_cfg); if (!child_cfg) { return send_reply(this, "configuration name not found"); @@ -480,6 +646,7 @@ static void manage_commands(private_vici_control_t *this, bool reg) { manage_command(this, "initiate", initiate, reg); manage_command(this, "terminate", terminate, reg); + manage_command(this, "redirect", redirect, reg); manage_command(this, "install", install, reg); manage_command(this, "uninstall", uninstall, reg); manage_command(this, "reload-settings", reload_settings, reg); diff --git a/src/libcharon/plugins/vici/vici_cred.c b/src/libcharon/plugins/vici/vici_cred.c index 6631184b5..3411b7d6c 100644 --- a/src/libcharon/plugins/vici/vici_cred.c +++ b/src/libcharon/plugins/vici/vici_cred.c @@ -2,6 +2,9 @@ * Copyright (C) 2014 Martin Willi * Copyright (C) 2014 revosec AG * + * Copyright (C) 2015 Andreas Steffen + * HSR Hochschule fuer Technik Rapperswil + * * 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 @@ -15,6 +18,7 @@ #include "vici_cred.h" #include "vici_builder.h" +#include "vici_cert_info.h" #include <credentials/sets/mem_cred.h> #include <credentials/certificates/ac.h> @@ -66,9 +70,9 @@ static vici_message_t* create_reply(char *fmt, ...) CALLBACK(load_cert, vici_message_t*, private_vici_cred_t *this, char *name, u_int id, vici_message_t *message) { - certificate_type_t type; - x509_flag_t required_flags = 0, additional_flags = 0; certificate_t *cert; + certificate_type_t type; + x509_flag_t ext_flag, flag = X509_NONE; x509_t *x509; chunk_t data; bool trusted = TRUE; @@ -79,60 +83,55 @@ CALLBACK(load_cert, vici_message_t*, { return create_reply("certificate type missing"); } - if (strcaseeq(str, "x509")) - { - type = CERT_X509; - } - else if (strcaseeq(str, "x509ca")) - { - type = CERT_X509; - required_flags = X509_CA; - } - else if (strcaseeq(str, "x509aa")) - { - type = CERT_X509; - additional_flags = X509_AA; - } - else if (strcaseeq(str, "x509crl")) + if (enum_from_name(certificate_type_names, str, &type)) { - type = CERT_X509_CRL; - } - else if (strcaseeq(str, "x509ac")) - { - type = CERT_X509_AC; - trusted = FALSE; + if (type == CERT_X509) + { + str = message->get_str(message, "NONE", "flag"); + if (!enum_from_name(x509_flag_names, str, &flag)) + { + return create_reply("invalid certificate flag '%s'", str); + } + } } - else + else if (!vici_cert_info_from_str(str, &type, &flag)) { - return create_reply("invalid certificate type: %s", str); + return create_reply("invalid certificate type '%s'", str); } + data = message->get_value(message, chunk_empty, "data"); if (!data.len) { return create_reply("certificate data missing"); } + + /* do not set CA flag externally */ + ext_flag = (flag & X509_CA) ? X509_NONE : flag; + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, type, BUILD_BLOB_PEM, data, - BUILD_X509_FLAG, additional_flags, + BUILD_X509_FLAG, ext_flag, BUILD_END); if (!cert) { return create_reply("parsing %N certificate failed", certificate_type_names, type); } - if (cert->get_type(cert) == CERT_X509) + DBG1(DBG_CFG, "loaded certificate '%Y'", cert->get_subject(cert)); + + /* check if CA certificate has CA basic constraint set */ + if (flag & X509_CA) { + char err_msg[] = "ca certificate lacks CA basic constraint, rejected"; x509 = (x509_t*)cert; - if ((required_flags & x509->get_flags(x509)) != required_flags) + if (!(x509->get_flags(x509) & X509_CA)) { cert->destroy(cert); - return create_reply("certificate misses required flag, rejected"); + DBG1(DBG_CFG, " %s", err_msg); + return create_reply(err_msg); } } - - DBG1(DBG_CFG, "loaded certificate '%Y'", cert->get_subject(cert)); - if (type == CERT_X509_CRL) { this->creds->add_crl(this->creds, (crl_t*)cert); @@ -169,6 +168,10 @@ CALLBACK(load_key, vici_message_t*, { type = KEY_ECDSA; } + else if (strcaseeq(str, "bliss")) + { + type = KEY_BLISS; + } else { return create_reply("invalid key type: %s", str); @@ -305,7 +308,7 @@ static void manage_commands(private_vici_cred_t *this, bool reg) METHOD(vici_cred_t, add_cert, certificate_t*, private_vici_cred_t *this, certificate_t *cert) { - return this->creds->get_cert_ref(this->creds, cert); + return this->creds->add_cert_ref(this->creds, TRUE, cert); } METHOD(vici_cred_t, destroy, void, diff --git a/src/libcharon/plugins/vici/vici_plugin.c b/src/libcharon/plugins/vici/vici_plugin.c index 53ed8cdfb..ed7c743c7 100644 --- a/src/libcharon/plugins/vici/vici_plugin.c +++ b/src/libcharon/plugins/vici/vici_plugin.c @@ -131,7 +131,8 @@ static bool register_vici(private_vici_plugin_t *this, this->authority = vici_authority_create(this->dispatcher, this->cred); lib->credmgr->add_set(lib->credmgr, &this->authority->set); - this->config = vici_config_create(this->dispatcher, this->authority); + this->config = vici_config_create(this->dispatcher, this->authority, + this->cred); this->attrs = vici_attribute_create(this->dispatcher); this->logger = vici_logger_create(this->dispatcher); diff --git a/src/libcharon/plugins/vici/vici_query.c b/src/libcharon/plugins/vici/vici_query.c index 9a3d832da..284c23ee0 100644 --- a/src/libcharon/plugins/vici/vici_query.c +++ b/src/libcharon/plugins/vici/vici_query.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2015 Tobias Brunner, Andreas Steffen + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2014 Martin Willi * Copyright (C) 2014 revosec AG * @@ -37,6 +40,7 @@ #include "vici_query.h" #include "vici_builder.h" +#include "vici_cert_info.h" #include <inttypes.h> #include <time.h> @@ -48,6 +52,9 @@ #endif #include <daemon.h> +#include <asn1/asn1.h> +#include <credentials/certificates/certificate.h> +#include <credentials/certificates/x509.h> typedef struct private_vici_query_t private_vici_query_t; @@ -120,7 +127,7 @@ static void list_child(private_vici_query_t *this, vici_builder_t *b, } } if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, - &alg, &ks) && alg != ENCR_UNDEFINED) + &alg, &ks) && alg != AUTH_UNDEFINED) { b->add_kv(b, "integ-alg", "%N", integrity_algorithm_names, alg); if (ks) @@ -128,11 +135,6 @@ static void list_child(private_vici_query_t *this, vici_builder_t *b, b->add_kv(b, "integ-keysize", "%u", ks); } } - if (proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, - &alg, NULL)) - { - b->add_kv(b, "prf-alg", "%N", pseudo_random_function_names, alg); - } if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, &alg, NULL)) { @@ -271,15 +273,20 @@ static void list_ike(private_vici_query_t *this, vici_builder_t *b, identification_t *eap; proposal_t *proposal; u_int16_t alg, ks; + host_t *host; b->add_kv(b, "uniqueid", "%u", ike_sa->get_unique_id(ike_sa)); b->add_kv(b, "version", "%u", ike_sa->get_version(ike_sa)); b->add_kv(b, "state", "%N", ike_sa_state_names, ike_sa->get_state(ike_sa)); - b->add_kv(b, "local-host", "%H", ike_sa->get_my_host(ike_sa)); + host = ike_sa->get_my_host(ike_sa); + b->add_kv(b, "local-host", "%H", host); + b->add_kv(b, "local-port", "%d", host->get_port(host)); b->add_kv(b, "local-id", "%Y", ike_sa->get_my_id(ike_sa)); - b->add_kv(b, "remote-host", "%H", ike_sa->get_other_host(ike_sa)); + host = ike_sa->get_other_host(ike_sa); + b->add_kv(b, "remote-host", "%H", host); + b->add_kv(b, "remote-port", "%d", host->get_port(host)); b->add_kv(b, "remote-id", "%Y", ike_sa->get_other_id(ike_sa)); eap = ike_sa->get_other_eap_id(ike_sa); @@ -301,8 +308,10 @@ static void list_ike(private_vici_query_t *this, vici_builder_t *b, { b->add_kv(b, "initiator", "yes"); } - b->add_kv(b, "initiator-spi", "%.16"PRIx64, id->get_initiator_spi(id)); - b->add_kv(b, "responder-spi", "%.16"PRIx64, id->get_responder_spi(id)); + b->add_kv(b, "initiator-spi", "%.16"PRIx64, + be64toh(id->get_initiator_spi(id))); + b->add_kv(b, "responder-spi", "%.16"PRIx64, + be64toh(id->get_responder_spi(id))); add_condition(b, ike_sa, "nat-local", COND_NAT_HERE); add_condition(b, ike_sa, "nat-remote", COND_NAT_THERE); @@ -772,7 +781,7 @@ CALLBACK(list_conns, vici_message_t*, /** * Do we have a private key for given certificate */ -static bool has_privkey(private_vici_query_t *this, certificate_t *cert) +static bool has_privkey(certificate_t *cert) { private_key_t *private; public_key_t *public; @@ -800,81 +809,332 @@ static bool has_privkey(private_vici_query_t *this, certificate_t *cert) return found; } -CALLBACK(list_certs, vici_message_t*, - private_vici_query_t *this, char *name, u_int id, vici_message_t *request) +/** + * Store cert filter data + */ +typedef struct { + certificate_type_t type; + x509_flag_t flag; + identification_t *subject; +} cert_filter_t; + +/** + * Enumerate all X.509 certificates with a given flag + */ +static void enum_x509(private_vici_query_t *this, u_int id, + linked_list_t *certs, cert_filter_t *filter, + x509_flag_t flag) { - enumerator_t *enumerator, *added; - linked_list_t *list; - certificate_t *cert, *current; - chunk_t encoding; - identification_t *subject = NULL; - int type; + enumerator_t *enumerator; + certificate_t *cert; vici_builder_t *b; - bool found; - char *str; + chunk_t encoding; + x509_t *x509; - str = request->get_str(request, "ANY", "type"); - if (!enum_from_name(certificate_type_names, str, &type)) + if (filter->type != CERT_ANY && filter->flag != X509_ANY && + filter->flag != flag) { - b = vici_builder_create(); - return b->finalize(b); - } - str = request->get_str(request, NULL, "subject"); - if (str) - { - subject = identification_create_from_string(str); + return; } - list = linked_list_create(); - enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr, - type, KEY_ANY, subject, FALSE); + enumerator = certs->create_enumerator(certs); while (enumerator->enumerate(enumerator, &cert)) { - found = FALSE; - added = list->create_enumerator(list); - while (added->enumerate(added, ¤t)) + x509 = (x509_t*)cert; + if ((x509->get_flags(x509) & X509_ANY) != flag) { - if (current->equals(current, cert)) + continue; + } + + if (cert->get_encoding(cert, CERT_ASN1_DER, &encoding)) + { + b = vici_builder_create(); + b->add_kv(b, "type", "%N", certificate_type_names, CERT_X509); + b->add_kv(b, "flag", "%N", x509_flag_names, flag); + if (has_privkey(cert)) { - found = TRUE; - break; + b->add_kv(b, "has_privkey", "yes"); } + b->add(b, VICI_KEY_VALUE, "data", encoding); + free(encoding.ptr); + + this->dispatcher->raise_event(this->dispatcher, "list-cert", id, + b->finalize(b)); } - added->destroy(added); + } + enumerator->destroy(enumerator); +} + +/** + * Enumerate all non-X.509 certificate types + */ +static void enum_others(private_vici_query_t *this, u_int id, + linked_list_t *certs, certificate_type_t type) +{ + enumerator_t *enumerator; + certificate_t *cert; + vici_builder_t *b; + chunk_t encoding, t_ch; + cred_encoding_type_t encoding_type; + identification_t *subject; + time_t not_before, not_after; + + encoding_type = (type == CERT_TRUSTED_PUBKEY) ? PUBKEY_SPKI_ASN1_DER : + CERT_ASN1_DER; - if (!found && cert->get_encoding(cert, CERT_ASN1_DER, &encoding)) + enumerator = certs->create_enumerator(certs); + while (enumerator->enumerate(enumerator, &cert)) + { + if (cert->get_encoding(cert, encoding_type, &encoding)) { b = vici_builder_create(); - b->add_kv(b, "type", "%N", - certificate_type_names, cert->get_type(cert)); - if (has_privkey(this, cert)) + b->add_kv(b, "type", "%N", certificate_type_names, type); + if (has_privkey(cert)) { b->add_kv(b, "has_privkey", "yes"); } b->add(b, VICI_KEY_VALUE, "data", encoding); free(encoding.ptr); + if (type == CERT_TRUSTED_PUBKEY) + { + subject = cert->get_subject(cert); + if (subject->get_type(subject) != ID_KEY_ID) + { + b->add_kv(b, "subject", "%Y", cert->get_subject(cert)); + } + cert->get_validity(cert, NULL, ¬_before, ¬_after); + if (not_before != UNDEFINED_TIME) + { + t_ch = asn1_from_time(¬_before, ASN1_GENERALIZEDTIME); + b->add(b, VICI_KEY_VALUE, "not-before", chunk_skip(t_ch, 2)); + chunk_free(&t_ch); + } + if (not_after != UNDEFINED_TIME) + { + t_ch = asn1_from_time(¬_after, ASN1_GENERALIZEDTIME); + b->add(b, VICI_KEY_VALUE, "not-after", chunk_skip(t_ch, 2)); + chunk_free(&t_ch); + } + } this->dispatcher->raise_event(this->dispatcher, "list-cert", id, b->finalize(b)); - list->insert_last(list, cert->get_ref(cert)); } } enumerator->destroy(enumerator); +} - list->destroy_offset(list, offsetof(certificate_t, destroy)); - DESTROY_IF(subject); +/** + * Enumerate all certificates of a given type + */ +static void enum_certs(private_vici_query_t *this, u_int id, + cert_filter_t *filter, certificate_type_t type) +{ + enumerator_t *e1, *e2; + certificate_t *cert, *current; + linked_list_t *certs; + bool found; + if (filter->type != CERT_ANY && filter->type != type) + { + return; + } + certs = linked_list_create(); + + e1 = lib->credmgr->create_cert_enumerator(lib->credmgr, type, KEY_ANY, + filter->subject, FALSE); + while (e1->enumerate(e1, &cert)) + { + found = FALSE; + + e2 = certs->create_enumerator(certs); + while (e2->enumerate(e2, ¤t)) + { + if (current->equals(current, cert)) + { + found = TRUE; + break; + } + } + e2->destroy(e2); + + if (!found) + { + certs->insert_last(certs, cert->get_ref(cert)); + } + } + e1->destroy(e1); + + if (type == CERT_X509) + { + enum_x509(this, id, certs, filter, X509_NONE); + enum_x509(this, id, certs, filter, X509_CA); + enum_x509(this, id, certs, filter, X509_AA); + enum_x509(this, id, certs, filter, X509_OCSP_SIGNER); + } + else + { + enum_others(this, id, certs, type); + } + certs->destroy_offset(certs, offsetof(certificate_t, destroy)); +} + +CALLBACK(list_certs, vici_message_t*, + private_vici_query_t *this, char *name, u_int id, vici_message_t *request) +{ + cert_filter_t filter = { + .type = CERT_ANY, + .flag = X509_ANY, + .subject = NULL + }; + vici_builder_t *b; + char *str; + + str = request->get_str(request, "ANY", "type"); + if (enum_from_name(certificate_type_names, str, &filter.type)) + { + if (filter.type == CERT_X509) + { + str = request->get_str(request, "ANY", "flag"); + if (!enum_from_name(x509_flag_names, str, &filter.flag)) + { + DBG1(DBG_CFG, "invalid certificate flag '%s'", str); + goto finalize; + } + } + } + else if (!vici_cert_info_from_str(str, &filter.type, &filter.flag)) + { + DBG1(DBG_CFG, "invalid certificate type '%s'", str); + goto finalize; + } + + str = request->get_str(request, NULL, "subject"); + if (str) + { + filter.subject = identification_create_from_string(str); + } + + enum_certs(this, id, &filter, CERT_TRUSTED_PUBKEY); + enum_certs(this, id, &filter, CERT_X509); + enum_certs(this, id, &filter, CERT_X509_AC); + enum_certs(this, id, &filter, CERT_X509_CRL); + enum_certs(this, id, &filter, CERT_X509_OCSP_RESPONSE); + DESTROY_IF(filter.subject); + +finalize: b = vici_builder_create(); return b->finalize(b); } -CALLBACK(version, vici_message_t*, +/** + * Add a key/value pair of ALG => plugin + */ +static void add_algorithm(vici_builder_t *b, enum_name_t *alg_names, + int alg_type, const char *plugin_name) +{ + char alg_name[BUF_LEN]; + + sprintf(alg_name, "%N", alg_names, alg_type); + b->add_kv(b, alg_name, (char*)plugin_name); +} + +CALLBACK(get_algorithms, vici_message_t*, private_vici_query_t *this, char *name, u_int id, vici_message_t *request) { vici_builder_t *b; + enumerator_t *enumerator; + encryption_algorithm_t encryption; + integrity_algorithm_t integrity; + hash_algorithm_t hash; + pseudo_random_function_t prf; + diffie_hellman_group_t group; + rng_quality_t quality; + const char *plugin_name; b = vici_builder_create(); + b->begin_section(b, "encryption"); + enumerator = lib->crypto->create_crypter_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &encryption, &plugin_name)) + { + add_algorithm(b, encryption_algorithm_names, encryption, plugin_name); + } + enumerator->destroy(enumerator); + b->end_section(b); + + b->begin_section(b, "integrity"); + enumerator = lib->crypto->create_signer_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &integrity, &plugin_name)) + { + add_algorithm(b, integrity_algorithm_names, integrity, plugin_name); + } + enumerator->destroy(enumerator); + b->end_section(b); + + b->begin_section(b, "aead"); + enumerator = lib->crypto->create_aead_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &encryption, &plugin_name)) + { + add_algorithm(b, encryption_algorithm_names, encryption, plugin_name); + } + enumerator->destroy(enumerator); + b->end_section(b); + + b->begin_section(b, "hasher"); + enumerator = lib->crypto->create_hasher_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &hash, &plugin_name)) + { + add_algorithm(b, hash_algorithm_names, hash, plugin_name); + } + enumerator->destroy(enumerator); + b->end_section(b); + + b->begin_section(b, "prf"); + enumerator = lib->crypto->create_prf_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &prf, &plugin_name)) + { + add_algorithm(b, pseudo_random_function_names, prf, plugin_name); + } + enumerator->destroy(enumerator); + b->end_section(b); + + b->begin_section(b, "dh"); + enumerator = lib->crypto->create_dh_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &group, &plugin_name)) + { + add_algorithm(b, diffie_hellman_group_names, group, plugin_name); + } + enumerator->destroy(enumerator); + b->end_section(b); + + b->begin_section(b, "rng"); + enumerator = lib->crypto->create_rng_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &quality, &plugin_name)) + { + add_algorithm(b, rng_quality_names, quality, plugin_name); + } + enumerator->destroy(enumerator); + b->end_section(b); + + b->begin_section(b, "nonce-gen"); + enumerator = lib->crypto->create_nonce_gen_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &plugin_name)) + { + b->add_kv(b, "NONCE_GEN", (char*)plugin_name); + } + enumerator->destroy(enumerator); + b->end_section(b); + + return b->finalize(b); +} + +CALLBACK(version, vici_message_t*, + private_vici_query_t *this, char *name, u_int id, vici_message_t *request) +{ + vici_builder_t *b; + + b = vici_builder_create(); b->add_kv(b, "daemon", "%s", lib->ns); b->add_kv(b, "version", "%s", VERSION); @@ -915,18 +1175,6 @@ CALLBACK(version, vici_message_t*, return b->finalize(b); } -/** - * Callback function for memusage summary - */ -CALLBACK(sum_usage, void, - vici_builder_t *b, int count, size_t bytes, int whitelisted) -{ - b->begin_section(b, "mem"); - b->add_kv(b, "total", "%zu", bytes); - b->add_kv(b, "allocs", "%d", count); - b->end_section(b); -} - CALLBACK(stats, vici_message_t*, private_vici_query_t *this, char *name, u_int id, vici_message_t *request) { @@ -988,12 +1236,7 @@ CALLBACK(stats, vici_message_t*, enumerator->destroy(enumerator); b->end_list(b); - if (lib->leak_detective) - { - lib->leak_detective->usage(lib->leak_detective, NULL, sum_usage, b); - } #ifdef WIN32 - else { DWORD lasterr = ERROR_INVALID_HANDLE; HANDLE heaps[32]; @@ -1085,6 +1328,7 @@ static void manage_commands(private_vici_query_t *this, bool reg) manage_command(this, "list-policies", list_policies, reg); manage_command(this, "list-conns", list_conns, reg); manage_command(this, "list-certs", list_certs, reg); + manage_command(this, "get-algorithms", get_algorithms, reg); manage_command(this, "version", version, reg); manage_command(this, "stats", stats, reg); } diff --git a/src/libcharon/plugins/vici/vici_tests.c b/src/libcharon/plugins/vici/vici_tests.c index 434aa5e18..d1f8097bf 100644 --- a/src/libcharon/plugins/vici/vici_tests.c +++ b/src/libcharon/plugins/vici/vici_tests.c @@ -16,7 +16,6 @@ #include <test_runner.h> #include <daemon.h> -#include <hydra.h> /* declare test suite constructors */ #define TEST_SUITE(x) test_suite_t* x(); diff --git a/src/libcharon/plugins/whitelist/Makefile.am b/src/libcharon/plugins/whitelist/Makefile.am index 1fd01c888..7f6bfff14 100644 --- a/src/libcharon/plugins/whitelist/Makefile.am +++ b/src/libcharon/plugins/whitelist/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -DIPSEC_PIDDIR=\"${piddir}\" diff --git a/src/libcharon/plugins/whitelist/Makefile.in b/src/libcharon/plugins/whitelist/Makefile.in index e400d9f35..549ef6bce 100644 --- a/src/libcharon/plugins/whitelist/Makefile.in +++ b/src/libcharon/plugins/whitelist/Makefile.in @@ -423,6 +423,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -438,7 +440,6 @@ 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 \ -DIPSEC_PIDDIR=\"${piddir}\" diff --git a/src/libcharon/plugins/xauth_eap/Makefile.am b/src/libcharon/plugins/xauth_eap/Makefile.am index ea75c1581..5c7228e85 100644 --- a/src/libcharon/plugins/xauth_eap/Makefile.am +++ b/src/libcharon/plugins/xauth_eap/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/xauth_eap/Makefile.in b/src/libcharon/plugins/xauth_eap/Makefile.in index a9684455d..6992df820 100644 --- a/src/libcharon/plugins/xauth_eap/Makefile.in +++ b/src/libcharon/plugins/xauth_eap/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 = \ diff --git a/src/libcharon/plugins/xauth_generic/Makefile.am b/src/libcharon/plugins/xauth_generic/Makefile.am index 1ecd9fd14..282bfc4fe 100644 --- a/src/libcharon/plugins/xauth_generic/Makefile.am +++ b/src/libcharon/plugins/xauth_generic/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/xauth_generic/Makefile.in b/src/libcharon/plugins/xauth_generic/Makefile.in index 5170c924f..057a734a3 100644 --- a/src/libcharon/plugins/xauth_generic/Makefile.in +++ b/src/libcharon/plugins/xauth_generic/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 = \ diff --git a/src/libcharon/plugins/xauth_noauth/Makefile.am b/src/libcharon/plugins/xauth_noauth/Makefile.am index 3902471fe..bb41f2169 100644 --- a/src/libcharon/plugins/xauth_noauth/Makefile.am +++ b/src/libcharon/plugins/xauth_noauth/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/xauth_noauth/Makefile.in b/src/libcharon/plugins/xauth_noauth/Makefile.in index 087f5b350..6b0104e30 100644 --- a/src/libcharon/plugins/xauth_noauth/Makefile.in +++ b/src/libcharon/plugins/xauth_noauth/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 = \ diff --git a/src/libcharon/plugins/xauth_pam/Makefile.am b/src/libcharon/plugins/xauth_pam/Makefile.am index abf83ca75..cee8bf811 100644 --- a/src/libcharon/plugins/xauth_pam/Makefile.am +++ b/src/libcharon/plugins/xauth_pam/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon AM_CFLAGS = \ diff --git a/src/libcharon/plugins/xauth_pam/Makefile.in b/src/libcharon/plugins/xauth_pam/Makefile.in index 29441bcb5..ae6a4d070 100644 --- a/src/libcharon/plugins/xauth_pam/Makefile.in +++ b/src/libcharon/plugins/xauth_pam/Makefile.in @@ -416,6 +416,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -431,7 +433,6 @@ 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 = \ diff --git a/src/libcharon/processing/jobs/adopt_children_job.c b/src/libcharon/processing/jobs/adopt_children_job.c index b4f135a57..c39689012 100644 --- a/src/libcharon/processing/jobs/adopt_children_job.c +++ b/src/libcharon/processing/jobs/adopt_children_job.c @@ -19,7 +19,6 @@ #include "adopt_children_job.h" #include <daemon.h> -#include <hydra.h> #include <collections/array.h> #include <processing/jobs/delete_ike_sa_job.h> diff --git a/src/libcharon/processing/jobs/redirect_job.c b/src/libcharon/processing/jobs/redirect_job.c new file mode 100644 index 000000000..e1af662c9 --- /dev/null +++ b/src/libcharon/processing/jobs/redirect_job.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2015 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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 <daemon.h> + +#include "redirect_job.h" + +typedef struct private_redirect_job_t private_redirect_job_t; + +/** + * Private data + */ +struct private_redirect_job_t { + + /** + * Public interface + */ + redirect_job_t public; + + /** + * ID of the IKE_SA to redirect + */ + ike_sa_id_t *ike_sa_id; + + /** + * Target gateway identity + */ + identification_t *gateway; +}; + + +METHOD(job_t, destroy, void, + private_redirect_job_t *this) +{ + this->ike_sa_id->destroy(this->ike_sa_id); + this->gateway->destroy(this->gateway); + free(this); +} + +METHOD(job_t, execute, job_requeue_t, + private_redirect_job_t *this) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, + this->ike_sa_id); + if (ike_sa) + { + if (ike_sa->get_state(ike_sa) == IKE_PASSIVE) + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + return JOB_REQUEUE_NONE; + } + if (ike_sa->redirect(ike_sa, this->gateway) == DESTROY_ME) + { + charon->ike_sa_manager->checkin_and_destroy( + charon->ike_sa_manager, ike_sa); + } + else + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + } + } + return JOB_REQUEUE_NONE; +} + +METHOD(job_t, get_priority, job_priority_t, + private_redirect_job_t *this) +{ + return JOB_PRIO_MEDIUM; +} + +/* + * Described in header + */ +redirect_job_t *redirect_job_create(ike_sa_id_t *ike_sa_id, + identification_t *gateway) +{ + private_redirect_job_t *this; + + INIT(this, + .public = { + .job_interface = { + .execute = _execute, + .get_priority = _get_priority, + .destroy = _destroy, + }, + }, + .ike_sa_id = ike_sa_id->clone(ike_sa_id), + .gateway = gateway->clone(gateway), + ); + + return &(this->public); +} diff --git a/src/libcharon/processing/jobs/redirect_job.h b/src/libcharon/processing/jobs/redirect_job.h new file mode 100644 index 000000000..fe4b34ee9 --- /dev/null +++ b/src/libcharon/processing/jobs/redirect_job.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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 redirect_job redirect_job + * @{ @ingroup cjobs + */ + +#ifndef REDIRECT_JOB_H_ +#define REDIRECT_JOB_H_ + +typedef struct redirect_job_t redirect_job_t; + +#include <library.h> +#include <sa/ike_sa_id.h> +#include <processing/jobs/job.h> + +/** + * Job used to redirect an IKE_SA. + */ +struct redirect_job_t { + + /** + * The job_t interface. + */ + job_t job_interface; +}; + +/** + * Creates a job to redirect an IKE_SA. + * + * @param ike_sa_id id of the IKE_SA to redirect (cloned) + * @param gateway gateway identity (IP or FQDN) of target (cloned) + * @return created redirect_job_t object + */ +redirect_job_t *redirect_job_create(ike_sa_id_t *ike_sa_id, + identification_t *gateway); + +#endif /** REDIRECT_JOB_H_ @}*/ diff --git a/src/libcharon/processing/jobs/send_keepalive_job.c b/src/libcharon/processing/jobs/send_keepalive_job.c index 3e3477679..e06eae3d3 100644 --- a/src/libcharon/processing/jobs/send_keepalive_job.c +++ b/src/libcharon/processing/jobs/send_keepalive_job.c @@ -54,7 +54,7 @@ METHOD(job_t, execute, job_requeue_t, this->ike_sa_id); if (ike_sa) { - ike_sa->send_keepalive(ike_sa); + ike_sa->send_keepalive(ike_sa, TRUE); charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); } return JOB_REQUEUE_NONE; diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index b0f163c83..56b7cb5a4 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -23,7 +23,6 @@ #include <string.h> #include <time.h> -#include <hydra.h> #include <daemon.h> #include <collections/array.h> @@ -469,10 +468,10 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound) { if (this->my_spi) { - status = hydra->kernel_interface->query_sa(hydra->kernel_interface, - this->other_addr, this->my_addr, this->my_spi, - proto_ike2ip(this->protocol), this->mark_in, - &bytes, &packets, &time); + status = charon->kernel->query_sa(charon->kernel, this->other_addr, + this->my_addr, this->my_spi, + proto_ike2ip(this->protocol), this->mark_in, + &bytes, &packets, &time); if (status == SUCCESS) { if (bytes > this->my_usebytes) @@ -493,10 +492,10 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound) { if (this->other_spi) { - status = hydra->kernel_interface->query_sa(hydra->kernel_interface, - this->my_addr, this->other_addr, this->other_spi, - proto_ike2ip(this->protocol), this->mark_out, - &bytes, &packets, &time); + status = charon->kernel->query_sa(charon->kernel, this->my_addr, + this->other_addr, this->other_spi, + proto_ike2ip(this->protocol), this->mark_out, + &bytes, &packets, &time); if (status == SUCCESS) { if (bytes > this->other_usebytes) @@ -532,15 +531,15 @@ static bool update_usetime(private_child_sa_t *this, bool inbound) if (inbound) { - if (hydra->kernel_interface->query_policy(hydra->kernel_interface, - other_ts, my_ts, POLICY_IN, this->mark_in, &in) == SUCCESS) + if (charon->kernel->query_policy(charon->kernel, other_ts, + my_ts, POLICY_IN, this->mark_in, &in) == SUCCESS) { last_use = max(last_use, in); } if (this->mode != MODE_TRANSPORT) { - if (hydra->kernel_interface->query_policy(hydra->kernel_interface, - other_ts, my_ts, POLICY_FWD, this->mark_in, &fwd) == SUCCESS) + if (charon->kernel->query_policy(charon->kernel, other_ts, + my_ts, POLICY_FWD, this->mark_in, &fwd) == SUCCESS) { last_use = max(last_use, fwd); } @@ -548,8 +547,8 @@ static bool update_usetime(private_child_sa_t *this, bool inbound) } else { - if (hydra->kernel_interface->query_policy(hydra->kernel_interface, - my_ts, other_ts, POLICY_OUT, this->mark_out, &out) == SUCCESS) + if (charon->kernel->query_policy(charon->kernel, my_ts, + other_ts, POLICY_OUT, this->mark_out, &out) == SUCCESS) { last_use = max(last_use, out); } @@ -629,10 +628,8 @@ METHOD(child_sa_t, get_installtime, time_t, METHOD(child_sa_t, alloc_spi, u_int32_t, private_child_sa_t *this, protocol_id_t protocol) { - if (hydra->kernel_interface->get_spi(hydra->kernel_interface, - this->other_addr, this->my_addr, - proto_ike2ip(protocol), - &this->my_spi) == SUCCESS) + if (charon->kernel->get_spi(charon->kernel, this->other_addr, this->my_addr, + proto_ike2ip(protocol), &this->my_spi) == SUCCESS) { /* if we allocate a SPI, but then are unable to establish the SA, we * need to know the protocol family to delete the partial SA */ @@ -645,9 +642,8 @@ METHOD(child_sa_t, alloc_spi, u_int32_t, METHOD(child_sa_t, alloc_cpi, u_int16_t, private_child_sa_t *this) { - if (hydra->kernel_interface->get_cpi(hydra->kernel_interface, - this->other_addr, this->my_addr, - &this->my_cpi) == SUCCESS) + if (charon->kernel->get_cpi(charon->kernel, this->other_addr, this->my_addr, + &this->my_cpi) == SUCCESS) { return this->my_cpi; } @@ -711,9 +707,8 @@ METHOD(child_sa_t, install, status_t, if (!this->reqid_allocated && !this->static_reqid) { - status = hydra->kernel_interface->alloc_reqid(hydra->kernel_interface, - my_ts, other_ts, this->mark_in, this->mark_out, - &this->reqid); + status = charon->kernel->alloc_reqid(charon->kernel, my_ts, other_ts, + this->mark_in, this->mark_out, &this->reqid); if (status != SUCCESS) { return status; @@ -757,7 +752,7 @@ METHOD(child_sa_t, install, status_t, dst_ts = other_ts; } - status = hydra->kernel_interface->add_sa(hydra->kernel_interface, + status = charon->kernel->add_sa(charon->kernel, src, dst, spi, proto_ike2ip(this->protocol), this->reqid, inbound ? this->mark_in : this->mark_out, tfc, lifetime, enc_alg, encr, int_alg, integ, this->mode, @@ -776,7 +771,7 @@ static bool require_policy_update() { kernel_feature_t f; - f = hydra->kernel_interface->get_features(hydra->kernel_interface); + f = charon->kernel->get_features(charon->kernel); return !(f & KERNEL_NO_POLICY_UPDATES); } @@ -833,18 +828,18 @@ static status_t install_policies_internal(private_child_sa_t *this, ipsec_sa_cfg_t *other_sa, policy_type_t type, policy_priority_t priority) { status_t status = SUCCESS; - status |= hydra->kernel_interface->add_policy(hydra->kernel_interface, + status |= charon->kernel->add_policy(charon->kernel, my_addr, other_addr, my_ts, other_ts, POLICY_OUT, type, other_sa, this->mark_out, priority); - status |= hydra->kernel_interface->add_policy(hydra->kernel_interface, + status |= charon->kernel->add_policy(charon->kernel, other_addr, my_addr, other_ts, my_ts, POLICY_IN, type, my_sa, this->mark_in, priority); if (this->mode != MODE_TRANSPORT) { - status |= hydra->kernel_interface->add_policy(hydra->kernel_interface, + status |= charon->kernel->add_policy(charon->kernel, other_addr, my_addr, other_ts, my_ts, POLICY_FWD, type, my_sa, this->mark_in, priority); @@ -861,15 +856,15 @@ static void del_policies_internal(private_child_sa_t *this, ipsec_sa_cfg_t *other_sa, policy_type_t type, policy_priority_t priority) { - hydra->kernel_interface->del_policy(hydra->kernel_interface, + charon->kernel->del_policy(charon->kernel, my_addr, other_addr, my_ts, other_ts, POLICY_OUT, type, other_sa, this->mark_out, priority); - hydra->kernel_interface->del_policy(hydra->kernel_interface, + charon->kernel->del_policy(charon->kernel, other_addr, my_addr, other_ts, my_ts, POLICY_IN, type, my_sa, this->mark_in, priority); if (this->mode != MODE_TRANSPORT) { - hydra->kernel_interface->del_policy(hydra->kernel_interface, + charon->kernel->del_policy(charon->kernel, other_addr, my_addr, other_ts, my_ts, POLICY_FWD, type, my_sa, this->mark_in, priority); } @@ -886,8 +881,8 @@ METHOD(child_sa_t, add_policies, status_t, if (!this->reqid_allocated && !this->static_reqid) { /* trap policy, get or confirm reqid */ - status = hydra->kernel_interface->alloc_reqid( - hydra->kernel_interface, my_ts_list, other_ts_list, + status = charon->kernel->alloc_reqid( + charon->kernel, my_ts_list, other_ts_list, this->mark_in, this->mark_out, &this->reqid); if (status != SUCCESS) { @@ -967,11 +962,10 @@ static void reinstall_vip(host_t *vip, host_t *me) { char *iface; - if (hydra->kernel_interface->get_interface(hydra->kernel_interface, - me, &iface)) + if (charon->kernel->get_interface(charon->kernel, me, &iface)) { - hydra->kernel_interface->del_ip(hydra->kernel_interface, vip, -1, TRUE); - hydra->kernel_interface->add_ip(hydra->kernel_interface, vip, -1, iface); + charon->kernel->del_ip(charon->kernel, vip, -1, TRUE); + charon->kernel->add_ip(charon->kernel, vip, -1, iface); free(iface); } } @@ -1000,7 +994,7 @@ METHOD(child_sa_t, update, status_t, /* update our (initiator) SA */ if (this->my_spi) { - if (hydra->kernel_interface->update_sa(hydra->kernel_interface, + if (charon->kernel->update_sa(charon->kernel, this->my_spi, proto_ike2ip(this->protocol), this->ipcomp != IPCOMP_NONE ? this->my_cpi : 0, this->other_addr, this->my_addr, other, me, @@ -1014,7 +1008,7 @@ METHOD(child_sa_t, update, status_t, /* update his (responder) SA */ if (this->other_spi) { - if (hydra->kernel_interface->update_sa(hydra->kernel_interface, + if (charon->kernel->update_sa(charon->kernel, this->other_spi, proto_ike2ip(this->protocol), this->ipcomp != IPCOMP_NONE ? this->other_cpi : 0, this->my_addr, this->other_addr, me, other, @@ -1143,14 +1137,14 @@ METHOD(child_sa_t, destroy, void, /* delete SAs in the kernel, if they are set up */ if (this->my_spi) { - hydra->kernel_interface->del_sa(hydra->kernel_interface, + charon->kernel->del_sa(charon->kernel, this->other_addr, this->my_addr, this->my_spi, proto_ike2ip(this->protocol), this->my_cpi, this->mark_in); } if (this->other_spi) { - hydra->kernel_interface->del_sa(hydra->kernel_interface, + charon->kernel->del_sa(charon->kernel, this->my_addr, this->other_addr, this->other_spi, proto_ike2ip(this->protocol), this->other_cpi, this->mark_out); @@ -1158,7 +1152,7 @@ METHOD(child_sa_t, destroy, void, if (this->reqid_allocated) { - if (hydra->kernel_interface->release_reqid(hydra->kernel_interface, + if (charon->kernel->release_reqid(charon->kernel, this->reqid, this->mark_in, this->mark_out) != SUCCESS) { DBG1(DBG_CHD, "releasing reqid %u failed", this->reqid); diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index dcf9d5f2c..bcbff3211 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -46,7 +46,6 @@ #include "ike_sa.h" #include <library.h> -#include <hydra.h> #include <daemon.h> #include <collections/array.h> #include <utils/lexparser.h> @@ -57,6 +56,9 @@ #include <processing/jobs/rekey_ike_sa_job.h> #include <processing/jobs/retry_initiate_job.h> #include <sa/ikev2/tasks/ike_auth_lifetime.h> +#include <sa/ikev2/tasks/ike_reauth_complete.h> +#include <sa/ikev2/tasks/ike_redirect.h> +#include <credentials/sets/auth_cfg_wrapper.h> #ifdef ME #include <sa/ikev2/tasks/ike_me.h> @@ -239,6 +241,11 @@ struct private_ike_sa_t { u_int32_t keepalive_interval; /** + * The schedueld keep alive job, if any + */ + send_keepalive_job_t *keepalive_job; + + /** * interval for retries during initiation (e.g. if DNS resolution failed), * 0 to disable (default) */ @@ -278,6 +285,21 @@ struct private_ike_sa_t { * Maximum length of a single fragment, 0 for address-specific defaults */ size_t fragment_size; + + /** + * Whether to follow IKEv2 redirects + */ + bool follow_redirects; + + /** + * Original gateway address from which we got redirected + */ + host_t *redirected_from; + + /** + * Timestamps of redirect attempts to handle loops + */ + array_t *redirected_at; }; /** @@ -382,6 +404,12 @@ METHOD(ike_sa_t, set_other_host, void, this->other_host = other; } +METHOD(ike_sa_t, get_redirected_from, host_t*, + private_ike_sa_t *this) +{ + return this->redirected_from; +} + METHOD(ike_sa_t, get_peer_cfg, peer_cfg_t*, private_ike_sa_t *this) { @@ -455,6 +483,113 @@ static void flush_auth_cfgs(private_ike_sa_t *this) } } +METHOD(ike_sa_t, verify_peer_certificate, bool, + private_ike_sa_t *this) +{ + enumerator_t *e1, *e2, *certs; + auth_cfg_t *cfg, *cfg_done; + certificate_t *peer, *cert; + public_key_t *key; + auth_cfg_t *auth; + auth_cfg_wrapper_t *wrapper; + time_t not_before, not_after; + bool valid = TRUE, found; + + if (this->state != IKE_ESTABLISHED) + { + DBG1(DBG_IKE, "unable to verify peer certificate in state %N", + ike_sa_state_names, this->state); + return FALSE; + } + + if (!this->flush_auth_cfg && + lib->settings->get_bool(lib->settings, + "%s.flush_auth_cfg", FALSE, lib->ns)) + { /* we can do this check only once if auth configs are flushed */ + DBG1(DBG_IKE, "unable to verify peer certificate as authentication " + "information has been flushed"); + return FALSE; + } + this->public.set_condition(&this->public, COND_ONLINE_VALIDATION_SUSPENDED, + FALSE); + + e1 = this->peer_cfg->create_auth_cfg_enumerator(this->peer_cfg, FALSE); + e2 = array_create_enumerator(this->other_auths); + while (e1->enumerate(e1, &cfg)) + { + if (!e2->enumerate(e2, &cfg_done)) + { /* this should not happen as the authentication should never have + * succeeded */ + valid = FALSE; + break; + } + if ((uintptr_t)cfg_done->get(cfg_done, + AUTH_RULE_AUTH_CLASS) != AUTH_CLASS_PUBKEY) + { + continue; + } + peer = cfg_done->get(cfg_done, AUTH_RULE_SUBJECT_CERT); + if (!peer) + { + DBG1(DBG_IKE, "no subject certificate found, skipping certificate " + "verification"); + continue; + } + if (!peer->get_validity(peer, NULL, ¬_before, ¬_after)) + { + DBG1(DBG_IKE, "peer certificate invalid (valid from %T to %T)", + ¬_before, FALSE, ¬_after, FALSE); + valid = FALSE; + break; + } + key = peer->get_public_key(peer); + if (!key) + { + DBG1(DBG_IKE, "unable to retrieve public key, skipping certificate " + "verification"); + continue; + } + DBG1(DBG_IKE, "verifying peer certificate"); + /* serve received certificates */ + wrapper = auth_cfg_wrapper_create(cfg_done); + lib->credmgr->add_local_set(lib->credmgr, &wrapper->set, FALSE); + certs = lib->credmgr->create_trusted_enumerator(lib->credmgr, + key->get_type(key), peer->get_subject(peer), TRUE); + key->destroy(key); + + found = FALSE; + while (certs->enumerate(certs, &cert, &auth)) + { + if (peer->equals(peer, cert)) + { + cfg_done->add(cfg_done, AUTH_RULE_CERT_VALIDATION_SUSPENDED, + FALSE); + cfg_done->merge(cfg_done, auth, FALSE); + valid = cfg_done->complies(cfg_done, cfg, TRUE); + found = TRUE; + break; + } + } + certs->destroy(certs); + lib->credmgr->remove_local_set(lib->credmgr, &wrapper->set); + wrapper->destroy(wrapper); + if (!found || !valid) + { + valid = FALSE; + break; + } + } + e1->destroy(e1); + e2->destroy(e2); + + if (this->flush_auth_cfg) + { + this->flush_auth_cfg = FALSE; + flush_auth_cfgs(this); + } + return valid; +} + METHOD(ike_sa_t, get_proposal, proposal_t*, private_ike_sa_t *this) { @@ -482,14 +617,20 @@ METHOD(ike_sa_t, set_message_id, void, } METHOD(ike_sa_t, send_keepalive, void, - private_ike_sa_t *this) + private_ike_sa_t *this, bool scheduled) { - send_keepalive_job_t *job; time_t last_out, now, diff; - if (!(this->conditions & COND_NAT_HERE) || this->keepalive_interval == 0 || - this->state == IKE_PASSIVE) - { /* disable keep alives if we are not NATed anymore, or we are passive */ + if (scheduled) + { + this->keepalive_job = NULL; + } + if (!this->keepalive_interval || this->state == IKE_PASSIVE) + { /* keepalives disabled either by configuration or for passive IKE_SAs */ + return; + } + if (!(this->conditions & COND_NAT_HERE) || (this->conditions & COND_STALE)) + { /* disable keepalives if we are not NATed anymore, or the SA is stale */ return; } @@ -514,9 +655,12 @@ METHOD(ike_sa_t, send_keepalive, void, charon->sender->send_no_marker(charon->sender, packet); diff = 0; } - job = send_keepalive_job_create(this->ike_sa_id); - lib->scheduler->schedule_job(lib->scheduler, (job_t*)job, - this->keepalive_interval - diff); + if (!this->keepalive_job) + { + this->keepalive_job = send_keepalive_job_create(this->ike_sa_id); + lib->scheduler->schedule_job(lib->scheduler, (job_t*)this->keepalive_job, + this->keepalive_interval - diff); + } } METHOD(ike_sa_t, get_ike_cfg, ike_cfg_t*, @@ -563,7 +707,7 @@ METHOD(ike_sa_t, set_condition, void, case COND_NAT_HERE: DBG1(DBG_IKE, "local host is behind NAT, sending keep alives"); this->conditions |= COND_NAT_ANY; - send_keepalive(this); + send_keepalive(this, FALSE); break; case COND_NAT_THERE: DBG1(DBG_IKE, "remote host is behind NAT"); @@ -590,6 +734,9 @@ METHOD(ike_sa_t, set_condition, void, has_condition(this, COND_NAT_THERE) || has_condition(this, COND_NAT_FAKE)); break; + case COND_STALE: + send_keepalive(this, FALSE); + break; default: break; } @@ -727,6 +874,8 @@ METHOD(ike_sa_t, set_state, void, { keepalives = TRUE; } + DESTROY_IF(this->redirected_from); + this->redirected_from = NULL; } break; } @@ -749,7 +898,7 @@ METHOD(ike_sa_t, set_state, void, } if (keepalives) { - send_keepalive(this); + send_keepalive(this, FALSE); } } @@ -786,12 +935,12 @@ METHOD(ike_sa_t, add_virtual_ip, void, { char *iface; - if (hydra->kernel_interface->get_interface(hydra->kernel_interface, - this->my_host, &iface)) + if (charon->kernel->get_interface(charon->kernel, this->my_host, + &iface)) { DBG1(DBG_IKE, "installing new virtual IP %H", ip); - if (hydra->kernel_interface->add_ip(hydra->kernel_interface, - ip, -1, iface) == SUCCESS) + if (charon->kernel->add_ip(charon->kernel, ip, -1, + iface) == SUCCESS) { array_insert_create(&this->my_vips, ARRAY_TAIL, ip->clone(ip)); } @@ -828,8 +977,7 @@ METHOD(ike_sa_t, clear_virtual_ips, void, { if (local) { - hydra->kernel_interface->del_ip(hydra->kernel_interface, - vip, -1, TRUE); + charon->kernel->del_ip(charon->kernel, vip, -1, TRUE); } vip->destroy(vip); } @@ -1265,8 +1413,8 @@ static void resolve_hosts(private_ike_sa_t *this) !this->other_host->is_anyaddr(this->other_host)) { host->destroy(host); - host = hydra->kernel_interface->get_source_addr( - hydra->kernel_interface, this->other_host, NULL); + host = charon->kernel->get_source_addr(charon->kernel, + this->other_host, NULL); if (host) { host->set_port(host, this->ike_cfg->get_my_port(this->ike_cfg)); @@ -1401,9 +1549,14 @@ METHOD(ike_sa_t, process_message, status_t, status = this->task_manager->process_message(this->task_manager, message); if (this->flush_auth_cfg && this->state == IKE_ESTABLISHED) { - /* authentication completed */ - this->flush_auth_cfg = FALSE; - flush_auth_cfgs(this); + /* authentication completed but if the online validation is suspended we + * need the auth cfgs until we did the delayed verification, we flush + * them afterwards */ + if (!has_condition(this, COND_ONLINE_VALIDATION_SUSPENDED)) + { + this->flush_auth_cfg = FALSE; + flush_auth_cfgs(this); + } } return status; } @@ -1735,6 +1888,86 @@ static bool is_child_queued(private_ike_sa_t *this, task_queue_t queue) return found; } +/** + * Reestablish CHILD_SAs and migrate queued tasks. + * + * If force is true all SAs are restarted, otherwise their close/dpd_action + * is followed. + */ +static status_t reestablish_children(private_ike_sa_t *this, ike_sa_t *new, + bool force) +{ + enumerator_t *enumerator; + child_sa_t *child_sa; + child_cfg_t *child_cfg; + action_t action; + status_t status = FAILED; + + /* handle existing CHILD_SAs */ + enumerator = create_child_sa_enumerator(this); + while (enumerator->enumerate(enumerator, (void**)&child_sa)) + { + if (force) + { + switch (child_sa->get_state(child_sa)) + { + case CHILD_ROUTED: + { /* move routed child directly */ + remove_child_sa(this, enumerator); + new->add_child_sa(new, child_sa); + action = ACTION_NONE; + break; + } + default: + { /* initiate/queue all other CHILD_SAs */ + action = ACTION_RESTART; + break; + } + } + } + else + { /* only restart CHILD_SAs that are configured accordingly */ + if (this->state == IKE_DELETING) + { + action = child_sa->get_close_action(child_sa); + } + else + { + action = child_sa->get_dpd_action(child_sa); + } + } + switch (action) + { + case ACTION_RESTART: + child_cfg = child_sa->get_config(child_sa); + DBG1(DBG_IKE, "restarting CHILD_SA %s", + child_cfg->get_name(child_cfg)); + child_cfg->get_ref(child_cfg); + status = new->initiate(new, child_cfg, + child_sa->get_reqid(child_sa), NULL, NULL); + break; + default: + continue; + } + if (status == DESTROY_ME) + { + break; + } + } + enumerator->destroy(enumerator); + /* adopt any active or queued CHILD-creating tasks */ + if (status != DESTROY_ME) + { + task_manager_t *other_tasks = ((private_ike_sa_t*)new)->task_manager; + other_tasks->adopt_child_tasks(other_tasks, this->task_manager); + if (new->get_state(new) == IKE_CREATED) + { + status = new->initiate(new, NULL, 0, NULL, NULL); + } + } + return status; +} + METHOD(ike_sa_t, reestablish, status_t, private_ike_sa_t *this) { @@ -1743,7 +1976,6 @@ METHOD(ike_sa_t, reestablish, status_t, action_t action; enumerator_t *enumerator; child_sa_t *child_sa; - child_cfg_t *child_cfg; bool restart = FALSE; status_t status = FAILED; @@ -1836,8 +2068,11 @@ METHOD(ike_sa_t, reestablish, status_t, host = this->my_host; new->set_my_host(new, host->clone(host)); charon->bus->ike_reestablish_pre(charon->bus, &this->public, new); - /* resolve hosts but use the old addresses above as fallback */ - resolve_hosts((private_ike_sa_t*)new); + if (!has_condition(this, COND_REAUTHENTICATING)) + { /* reauthenticate to the same addresses, but resolve hosts if + * reestablishing (old addresses serve as fallback) */ + resolve_hosts((private_ike_sa_t*)new); + } /* if we already have a virtual IP, we reuse it */ enumerator = array_create_enumerator(this->my_vips); while (enumerator->enumerate(enumerator, &host)) @@ -1854,68 +2089,8 @@ METHOD(ike_sa_t, reestablish, status_t, else #endif /* ME */ { - /* handle existing CHILD_SAs */ - enumerator = create_child_sa_enumerator(this); - while (enumerator->enumerate(enumerator, (void**)&child_sa)) - { - if (has_condition(this, COND_REAUTHENTICATING)) - { - switch (child_sa->get_state(child_sa)) - { - case CHILD_ROUTED: - { /* move routed child directly */ - remove_child_sa(this, enumerator); - new->add_child_sa(new, child_sa); - action = ACTION_NONE; - break; - } - default: - { /* initiate/queue all other CHILD_SAs */ - action = ACTION_RESTART; - break; - } - } - } - else - { /* only restart CHILD_SAs that are configured accordingly */ - if (this->state == IKE_DELETING) - { - action = child_sa->get_close_action(child_sa); - } - else - { - action = child_sa->get_dpd_action(child_sa); - } - } - switch (action) - { - case ACTION_RESTART: - child_cfg = child_sa->get_config(child_sa); - DBG1(DBG_IKE, "restarting CHILD_SA %s", - child_cfg->get_name(child_cfg)); - child_cfg->get_ref(child_cfg); - status = new->initiate(new, child_cfg, - child_sa->get_reqid(child_sa), NULL, NULL); - break; - default: - continue; - } - if (status == DESTROY_ME) - { - break; - } - } - enumerator->destroy(enumerator); - /* adopt any active or queued CHILD-creating tasks */ - if (status != DESTROY_ME) - { - task_manager_t *other_tasks = ((private_ike_sa_t*)new)->task_manager; - other_tasks->adopt_child_tasks(other_tasks, this->task_manager); - if (new->get_state(new) == IKE_CREATED) - { - status = new->initiate(new, NULL, 0, NULL, NULL); - } - } + status = reestablish_children(this, new, + has_condition(this, COND_REAUTHENTICATING)); } if (status == DESTROY_ME) @@ -1936,6 +2111,195 @@ METHOD(ike_sa_t, reestablish, status_t, return status; } +/** + * Resolve the given gateway ID + */ +static host_t *resolve_gateway_id(identification_t *gateway) +{ + char gw[BUF_LEN]; + host_t *addr; + + snprintf(gw, sizeof(gw), "%Y", gateway); + gw[sizeof(gw)-1] = '\0'; + addr = host_create_from_dns(gw, AF_UNSPEC, IKEV2_UDP_PORT); + if (!addr) + { + DBG1(DBG_IKE, "unable to resolve gateway ID '%Y', redirect failed", + gateway); + } + return addr; +} + +/** + * Redirect the current SA to the given target host + */ +static bool redirect_established(private_ike_sa_t *this, identification_t *to) +{ + private_ike_sa_t *new_priv; + ike_sa_t *new; + host_t *other; + time_t redirect; + + new = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, + this->version, TRUE); + if (!new) + { + return FALSE; + } + new_priv = (private_ike_sa_t*)new; + new->set_peer_cfg(new, this->peer_cfg); + new_priv->redirected_from = this->other_host->clone(this->other_host); + charon->bus->ike_reestablish_pre(charon->bus, &this->public, new); + other = resolve_gateway_id(to); + if (other) + { + set_my_host(new_priv, this->my_host->clone(this->my_host)); + /* this allows us to force the remote address while we still properly + * resolve the local address */ + new_priv->remote_host = other; + resolve_hosts(new_priv); + new_priv->redirected_at = array_create(sizeof(time_t), MAX_REDIRECTS); + while (array_remove(this->redirected_at, ARRAY_HEAD, &redirect)) + { + array_insert(new_priv->redirected_at, ARRAY_TAIL, &redirect); + } + if (reestablish_children(this, new, TRUE) != DESTROY_ME) + { +#ifdef USE_IKEV2 + new->queue_task(new, (task_t*)ike_reauth_complete_create(new, + this->ike_sa_id)); +#endif + charon->bus->ike_reestablish_post(charon->bus, &this->public, new, + TRUE); + charon->ike_sa_manager->checkin(charon->ike_sa_manager, new); + charon->bus->set_sa(charon->bus, &this->public); + return TRUE; + } + } + charon->bus->ike_reestablish_post(charon->bus, &this->public, new, + FALSE); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, new); + charon->bus->set_sa(charon->bus, &this->public); + return FALSE; +} + +/** + * Redirect the current connecting SA to the given target host + */ +static bool redirect_connecting(private_ike_sa_t *this, identification_t *to) +{ + host_t *other; + + other = resolve_gateway_id(to); + if (!other) + { + return FALSE; + } + reset(this); + DESTROY_IF(this->redirected_from); + this->redirected_from = this->other_host->clone(this->other_host); + DESTROY_IF(this->remote_host); + /* this allows us to force the remote address while we still properly + * resolve the local address */ + this->remote_host = other; + resolve_hosts(this); + return TRUE; +} + +/** + * Check if the current redirect exceeds the limits for redirects + */ +static bool redirect_count_exceeded(private_ike_sa_t *this) +{ + time_t now, redirect; + + now = time_monotonic(NULL); + /* remove entries outside the defined period */ + while (array_get(this->redirected_at, ARRAY_HEAD, &redirect) && + now - redirect >= REDIRECT_LOOP_DETECT_PERIOD) + { + array_remove(this->redirected_at, ARRAY_HEAD, NULL); + } + if (array_count(this->redirected_at) < MAX_REDIRECTS) + { + if (!this->redirected_at) + { + this->redirected_at = array_create(sizeof(time_t), MAX_REDIRECTS); + } + array_insert(this->redirected_at, ARRAY_TAIL, &now); + return FALSE; + } + return TRUE; +} + +METHOD(ike_sa_t, handle_redirect, bool, + private_ike_sa_t *this, identification_t *gateway) +{ + DBG1(DBG_IKE, "redirected to %Y", gateway); + if (!this->follow_redirects) + { + DBG1(DBG_IKE, "server sent REDIRECT even though we disabled it"); + return FALSE; + } + if (redirect_count_exceeded(this)) + { + DBG1(DBG_IKE, "only %d redirects are allowed within %d seconds", + MAX_REDIRECTS, REDIRECT_LOOP_DETECT_PERIOD); + return FALSE; + } + + switch (this->state) + { + case IKE_CONNECTING: + return redirect_connecting(this, gateway); + case IKE_ESTABLISHED: + return redirect_established(this, gateway); + default: + DBG1(DBG_IKE, "unable to handle redirect for IKE_SA in state %N", + ike_sa_state_names, this->state); + return FALSE; + } +} + +METHOD(ike_sa_t, redirect, status_t, + private_ike_sa_t *this, identification_t *gateway) +{ + switch (this->state) + { + case IKE_CONNECTING: + case IKE_ESTABLISHED: + case IKE_REKEYING: + if (has_condition(this, COND_REDIRECTED)) + { /* IKE_SA already got redirected */ + return SUCCESS; + } + if (has_condition(this, COND_ORIGINAL_INITIATOR)) + { + DBG1(DBG_IKE, "unable to redirect IKE_SA as initiator"); + return FAILED; + } + if (this->version == IKEV1) + { + DBG1(DBG_IKE, "unable to redirect IKEv1 SA"); + return FAILED; + } + if (!supports_extension(this, EXT_IKE_REDIRECTION)) + { + DBG1(DBG_IKE, "client does not support IKE redirection"); + return FAILED; + } +#ifdef USE_IKEV2 + this->task_manager->queue_task(this->task_manager, + (task_t*)ike_redirect_create(&this->public, gateway)); +#endif + return this->task_manager->initiate(this->task_manager); + default: + DBG1(DBG_IKE, "unable to redirect IKE_SA in state %N", + ike_sa_state_names, this->state); + return INVALID_STATE; + } +} + METHOD(ike_sa_t, retransmit, status_t, private_ike_sa_t *this, u_int32_t message_id) { @@ -2067,8 +2431,8 @@ static bool is_current_path_valid(private_ike_sa_t *this) { bool valid = FALSE; host_t *src; - src = hydra->kernel_interface->get_source_addr(hydra->kernel_interface, - this->other_host, this->my_host); + src = charon->kernel->get_source_addr(charon->kernel, this->other_host, + this->my_host); if (src) { if (src->ip_equals(src, this->my_host)) @@ -2112,8 +2476,7 @@ static bool is_any_path_valid(private_ike_sa_t *this) continue; } DBG1(DBG_IKE, "looking for a route to %H ...", addr); - src = hydra->kernel_interface->get_source_addr( - hydra->kernel_interface, addr, NULL); + src = charon->kernel->get_source_addr(charon->kernel, addr, NULL); if (src) { break; @@ -2323,7 +2686,7 @@ METHOD(ike_sa_t, inherit_post, void, this->conditions = other->conditions; if (this->conditions & COND_NAT_HERE) { - send_keepalive(this); + send_keepalive(this, FALSE); } #ifdef ME @@ -2401,7 +2764,7 @@ METHOD(ike_sa_t, destroy, void, } while (array_remove(this->my_vips, ARRAY_TAIL, &vip)) { - hydra->kernel_interface->del_ip(hydra->kernel_interface, vip, -1, TRUE); + charon->kernel->del_ip(charon->kernel, vip, -1, TRUE); vip->destroy(vip); } if (array_count(this->other_vips)) @@ -2450,6 +2813,8 @@ METHOD(ike_sa_t, destroy, void, DESTROY_IF(this->other_id); DESTROY_IF(this->local_host); DESTROY_IF(this->remote_host); + DESTROY_IF(this->redirected_from); + array_destroy(this->redirected_at); DESTROY_IF(this->ike_cfg); DESTROY_IF(this->peer_cfg); @@ -2498,6 +2863,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, .set_peer_cfg = _set_peer_cfg, .get_auth_cfg = _get_auth_cfg, .create_auth_cfg_enumerator = _create_auth_cfg_enumerator, + .verify_peer_certificate = _verify_peer_certificate, .add_auth_cfg = _add_auth_cfg, .get_proposal = _get_proposal, .set_proposal = _set_proposal, @@ -2529,6 +2895,9 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, .destroy = _destroy, .send_dpd = _send_dpd, .send_keepalive = _send_keepalive, + .redirect = _redirect, + .handle_redirect = _handle_redirect, + .get_redirected_from = _get_redirected_from, .get_keymat = _get_keymat, .add_child_sa = _add_child_sa, .get_child_sa = _get_child_sa, @@ -2594,6 +2963,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, "%s.flush_auth_cfg", FALSE, lib->ns), .fragment_size = lib->settings->get_int(lib->settings, "%s.fragment_size", 0, lib->ns), + .follow_redirects = lib->settings->get_bool(lib->settings, + "%s.follow_redirects", TRUE, lib->ns), ); if (version == IKEV2) diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index 9dbc805c9..836360e3c 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2014 Tobias Brunner + * Copyright (C) 2006-2015 Tobias Brunner * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -66,6 +66,16 @@ typedef struct ike_sa_t ike_sa_t; #define RETRY_JITTER 20 /** + * Number of redirects allowed within REDIRECT_LOOP_DETECT_PERIOD. + */ +#define MAX_REDIRECTS 5 + +/** + * Time period in seconds in which at most MAX_REDIRECTS are allowed. + */ +#define REDIRECT_LOOP_DETECT_PERIOD 300 + +/** * Extensions (or optional features) the peer supports */ enum ike_extension_t { @@ -136,6 +146,11 @@ enum ike_extension_t { * Signature Authentication, RFC 7427 */ EXT_SIGNATURE_AUTH = (1<<12), + + /** + * IKEv2 Redirect Mechanism, RFC 5685 + */ + EXT_IKE_REDIRECTION = (1<<13), }; /** @@ -197,6 +212,16 @@ enum ike_condition_t { * This IKE_SA is currently being reauthenticated */ COND_REAUTHENTICATING = (1<<10), + + /** + * This IKE_SA has been redirected + */ + COND_REDIRECTED = (1<<11), + + /** + * Online certificate revocation checking is suspended for this IKE_SA + */ + COND_ONLINE_VALIDATION_SUSPENDED = (1<<12), }; /** @@ -502,6 +527,14 @@ struct ike_sa_t { enumerator_t* (*create_auth_cfg_enumerator)(ike_sa_t *this, bool local); /** + * Verify the trustchains (validity, revocation) in completed public key + * auth rounds. + * + * @return TRUE if certificates were valid, FALSE otherwise + */ + bool (*verify_peer_certificate)(ike_sa_t *this); + + /** * Get the selected proposal of this IKE_SA. * * @return selected proposal @@ -837,8 +870,36 @@ struct ike_sa_t { * * To refresh NAT tables in a NAT router between the peers, periodic empty * UDP packets are sent if no other traffic was sent. + * + * @param scheduled if this is a scheduled keepalive + */ + void (*send_keepalive) (ike_sa_t *this, bool scheduled); + + /** + * Redirect an active IKE_SA. + * + * @param gateway gateway ID (IP or FQDN) of the target + * @return state, including DESTROY_ME, if this IKE_SA MUST be + * destroyed + */ + status_t (*redirect)(ike_sa_t *this, identification_t *gateway); + + /** + * Handle a redirect request. + * + * The behavior is different depending on the state of the IKE_SA. + * + * @param gateway gateway ID (IP or FQDN) of the target + * @return FALSE if redirect not possible, TRUE otherwise + */ + bool (*handle_redirect)(ike_sa_t *this, identification_t *gateway); + + /** + * Get the address of the gateway that redirected us. + * + * @return original gateway address */ - void (*send_keepalive) (ike_sa_t *this); + host_t *(*get_redirected_from)(ike_sa_t *this); /** * Get the keying material of this IKE_SA. diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c index 4625df5b8..307ea3b4a 100644 --- a/src/libcharon/sa/ike_sa_manager.c +++ b/src/libcharon/sa/ike_sa_manager.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2005-2011 Martin Willi * Copyright (C) 2011 revosec AG - * Copyright (C) 2008-2015 Tobias Brunner + * Copyright (C) 2008-2016 Tobias Brunner * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil * @@ -17,12 +17,14 @@ */ #include <string.h> +#include <inttypes.h> #include "ike_sa_manager.h" #include <daemon.h> #include <sa/ike_sa_id.h> #include <bus/bus.h> +#include <threading/thread.h> #include <threading/condvar.h> #include <threading/mutex.h> #include <threading/rwlock.h> @@ -57,9 +59,9 @@ struct entry_t { condvar_t *condvar; /** - * Is this ike_sa currently checked out? + * Thread by which this IKE_SA is currently checked out, if any */ - bool checked_out; + thread_t *checked_out; /** * Does this SA drives out new threads? @@ -1142,13 +1144,16 @@ METHOD(ike_sa_manager_t, checkout, ike_sa_t*, entry_t *entry; u_int segment; - DBG2(DBG_MGR, "checkout IKE_SA"); + DBG2(DBG_MGR, "checkout %N SA with SPIs %.16"PRIx64"_i %.16"PRIx64"_r", + ike_version_names, ike_sa_id->get_ike_version(ike_sa_id), + be64toh(ike_sa_id->get_initiator_spi(ike_sa_id)), + be64toh(ike_sa_id->get_responder_spi(ike_sa_id))); if (get_entry_by_id(this, ike_sa_id, &entry, &segment) == SUCCESS) { if (wait_for_entry(this, entry, segment)) { - entry->checked_out = TRUE; + entry->checked_out = thread_current(); ike_sa = entry->ike_sa; DBG2(DBG_MGR, "IKE_SA %s[%u] successfully checked out", ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa)); @@ -1156,6 +1161,11 @@ METHOD(ike_sa_manager_t, checkout, ike_sa_t*, unlock_single_segment(this, segment); } charon->bus->set_sa(charon->bus, ike_sa); + + if (!ike_sa) + { + DBG2(DBG_MGR, "IKE_SA checkout not successful"); + } return ike_sa; } @@ -1228,7 +1238,10 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*, id = id->clone(id); id->switch_initiator(id); - DBG2(DBG_MGR, "checkout IKE_SA by message"); + DBG2(DBG_MGR, "checkout %N SA by message with SPIs %.16"PRIx64"_i " + "%.16"PRIx64"_r", ike_version_names, id->get_ike_version(id), + be64toh(id->get_initiator_spi(id)), + be64toh(id->get_responder_spi(id))); if (id->get_responder_spi(id) == 0 && message->get_message_id(message) == 0) @@ -1269,7 +1282,7 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*, DBG1(DBG_MGR, "ignoring message, failed to hash message"); DESTROY_IF(hasher); id->destroy(id); - return NULL; + goto out; } hasher->destroy(hasher); @@ -1288,20 +1301,17 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*, entry = entry_create(); entry->ike_sa = ike_sa; entry->ike_sa_id = id; + entry->processing = get_message_id_or_hash(message); + entry->init_hash = hash; segment = put_entry(this, entry); - entry->checked_out = TRUE; + entry->checked_out = thread_current(); unlock_single_segment(this, segment); - entry->processing = get_message_id_or_hash(message); - entry->init_hash = hash; - DBG2(DBG_MGR, "created IKE_SA %s[%u]", ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa)); - - charon->bus->set_sa(charon->bus, ike_sa); - return ike_sa; + goto out; } else { @@ -1317,14 +1327,14 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*, remove_init_hash(this, hash); chunk_free(&hash); id->destroy(id); - return NULL; + goto out; } case FAILED: { /* we failed to allocate an SPI */ chunk_free(&hash); id->destroy(id); DBG1(DBG_MGR, "ignoring message, failed to allocate SPI"); - return NULL; + goto out; } case ALREADY_DONE: default: @@ -1348,7 +1358,7 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*, ike_sa_id_t *ike_id; ike_id = entry->ike_sa->get_id(entry->ike_sa); - entry->checked_out = TRUE; + entry->checked_out = thread_current(); if (message->get_first_payload_type(message) != PLV1_FRAGMENT && message->get_first_payload_type(message) != PLV2_FRAGMENT) { /* TODO-FRAG: this fails if there are unencrypted payloads */ @@ -1369,7 +1379,13 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*, charon->bus->alert(charon->bus, ALERT_INVALID_IKE_SPI, message); } id->destroy(id); + +out: charon->bus->set_sa(charon->bus, ike_sa); + if (!ike_sa) + { + DBG2(DBG_MGR, "IKE_SA checkout not successful"); + } return ike_sa; } @@ -1385,11 +1401,11 @@ METHOD(ike_sa_manager_t, checkout_by_config, ike_sa_t*, DBG2(DBG_MGR, "checkout IKE_SA by config"); - if (!this->reuse_ikesa) - { /* IKE_SA reuse disable by config */ + if (!this->reuse_ikesa && peer_cfg->get_ike_version(peer_cfg) != IKEV1) + { /* IKE_SA reuse disabled by config (not possible for IKEv1) */ ike_sa = checkout_new(this, peer_cfg->get_ike_version(peer_cfg), TRUE); charon->bus->set_sa(charon->bus, ike_sa); - return ike_sa; + goto out; } enumerator = create_table_enumerator(this); @@ -1411,7 +1427,7 @@ METHOD(ike_sa_manager_t, checkout_by_config, ike_sa_t*, current_ike = current_peer->get_ike_cfg(current_peer); if (current_ike->equals(current_ike, peer_cfg->get_ike_cfg(peer_cfg))) { - entry->checked_out = TRUE; + entry->checked_out = thread_current(); ike_sa = entry->ike_sa; DBG2(DBG_MGR, "found existing IKE_SA %u with a '%s' config", ike_sa->get_unique_id(ike_sa), @@ -1429,6 +1445,12 @@ METHOD(ike_sa_manager_t, checkout_by_config, ike_sa_t*, ike_sa = checkout_new(this, peer_cfg->get_ike_version(peer_cfg), TRUE); } charon->bus->set_sa(charon->bus, ike_sa); + +out: + if (!ike_sa) + { + DBG2(DBG_MGR, "IKE_SA checkout not successful"); + } return ike_sa; } @@ -1440,7 +1462,7 @@ METHOD(ike_sa_manager_t, checkout_by_id, ike_sa_t*, ike_sa_t *ike_sa = NULL; u_int segment; - DBG2(DBG_MGR, "checkout IKE_SA by ID %u", id); + DBG2(DBG_MGR, "checkout IKE_SA by unique ID %u", id); enumerator = create_table_enumerator(this); while (enumerator->enumerate(enumerator, &entry, &segment)) @@ -1450,7 +1472,7 @@ METHOD(ike_sa_manager_t, checkout_by_id, ike_sa_t*, if (entry->ike_sa->get_unique_id(entry->ike_sa) == id) { ike_sa = entry->ike_sa; - entry->checked_out = TRUE; + entry->checked_out = thread_current(); break; } /* other threads might be waiting for this entry */ @@ -1464,6 +1486,10 @@ METHOD(ike_sa_manager_t, checkout_by_id, ike_sa_t*, DBG2(DBG_MGR, "IKE_SA %s[%u] successfully checked out", ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa)); } + else + { + DBG2(DBG_MGR, "IKE_SA checkout not successful"); + } charon->bus->set_sa(charon->bus, ike_sa); return ike_sa; } @@ -1477,6 +1503,8 @@ METHOD(ike_sa_manager_t, checkout_by_name, ike_sa_t*, child_sa_t *child_sa; u_int segment; + DBG2(DBG_MGR, "checkout IKE_SA by%s name '%s'", child ? " child" : "", name); + enumerator = create_table_enumerator(this); while (enumerator->enumerate(enumerator, &entry, &segment)) { @@ -1506,7 +1534,7 @@ METHOD(ike_sa_manager_t, checkout_by_name, ike_sa_t*, /* got one, return */ if (ike_sa) { - entry->checked_out = TRUE; + entry->checked_out = thread_current(); DBG2(DBG_MGR, "IKE_SA %s[%u] successfully checked out", ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa)); break; @@ -1518,6 +1546,11 @@ METHOD(ike_sa_manager_t, checkout_by_name, ike_sa_t*, enumerator->destroy(enumerator); charon->bus->set_sa(charon->bus, ike_sa); + + if (!ike_sa) + { + DBG2(DBG_MGR, "IKE_SA checkout not successful"); + } return ike_sa; } @@ -1598,7 +1631,7 @@ METHOD(ike_sa_manager_t, checkin, void, /* ike_sa_id must be updated */ entry->ike_sa_id->replace_values(entry->ike_sa_id, ike_sa->get_id(ike_sa)); /* signal waiting threads */ - entry->checked_out = FALSE; + entry->checked_out = NULL; entry->processing = -1; /* check if this SA is half-open */ if (entry->half_open && ike_sa->get_state(ike_sa) != IKE_CONNECTING) @@ -1623,7 +1656,6 @@ METHOD(ike_sa_manager_t, checkin, void, entry->other = other->clone(other); put_half_open(this, entry); } - DBG2(DBG_MGR, "check-in of IKE_SA successful."); entry->condvar->signal(entry->condvar); } else @@ -1639,6 +1671,7 @@ METHOD(ike_sa_manager_t, checkin, void, } segment = put_entry(this, entry); } + DBG2(DBG_MGR, "checkin of IKE_SA successful"); /* apply identities for duplicate test */ if ((ike_sa->get_state(ike_sa) == IKE_ESTABLISHED || @@ -1657,7 +1690,7 @@ METHOD(ike_sa_manager_t, checkin, void, * thread can acquire it. Since it is not yet in the list of * connected peers that will not cause a deadlock as no other * caller of check_unqiueness() will try to check out this SA */ - entry->checked_out = TRUE; + entry->checked_out = thread_current(); unlock_single_segment(this, segment); this->public.check_uniqueness(&this->public, ike_sa, TRUE); @@ -1668,7 +1701,7 @@ METHOD(ike_sa_manager_t, checkin, void, * thread is waiting, but it should still exist, so there is no * need for a lookup via get_entry_by... */ lock_single_segment(this, segment); - entry->checked_out = FALSE; + entry->checked_out = NULL; /* We already signaled waiting threads above, we have to do that * again after checking the SA out and back in again. */ entry->condvar->signal(entry->condvar); @@ -1711,8 +1744,8 @@ METHOD(ike_sa_manager_t, checkin_and_destroy, void, if (entry->driveout_waiting_threads && entry->driveout_new_threads) { /* it looks like flush() has been called and the SA is being deleted * anyway, just check it in */ - DBG2(DBG_MGR, "ignored check-in and destroy of IKE_SA during shutdown"); - entry->checked_out = FALSE; + DBG2(DBG_MGR, "ignored checkin and destroy of IKE_SA during shutdown"); + entry->checked_out = NULL; entry->condvar->broadcast(entry->condvar); unlock_single_segment(this, segment); return; @@ -1748,11 +1781,11 @@ METHOD(ike_sa_manager_t, checkin_and_destroy, void, entry_destroy(entry); - DBG2(DBG_MGR, "check-in and destroy of IKE_SA successful"); + DBG2(DBG_MGR, "checkin and destroy of IKE_SA successful"); } else { - DBG1(DBG_MGR, "tried to check-in and delete nonexisting IKE_SA"); + DBG1(DBG_MGR, "tried to checkin and delete nonexisting IKE_SA"); ike_sa->destroy(ike_sa); } charon->bus->set_sa(charon->bus, NULL); diff --git a/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c b/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c index 52228ef2e..eee7dd10b 100644 --- a/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c +++ b/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c @@ -173,13 +173,13 @@ METHOD(authenticator_t, process, status_t, sig = sig_payload->get_hash(sig_payload); auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE); enumerator = lib->credmgr->create_public_enumerator(lib->credmgr, this->type, - id, auth); + id, auth, TRUE); while (enumerator->enumerate(enumerator, &public, ¤t_auth)) { if (public->verify(public, scheme, hash, sig)) { DBG1(DBG_IKE, "authentication of '%Y' with %N successful", - id, key_type_names, this->type); + id, signature_scheme_names, scheme); status = SUCCESS; auth->merge(auth, current_auth, FALSE); auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY); diff --git a/src/libcharon/sa/ikev1/phase1.c b/src/libcharon/sa/ikev1/phase1.c index b7047e8fc..c968b2a9c 100644 --- a/src/libcharon/sa/ikev1/phase1.c +++ b/src/libcharon/sa/ikev1/phase1.c @@ -404,7 +404,7 @@ static auth_method_t get_pubkey_method(private_phase1_t *this, auth_cfg_t *auth) id = (identification_t*)auth->get(auth, AUTH_RULE_IDENTITY); if (id) { - private = lib->credmgr->get_private(lib->credmgr, KEY_ANY, id, auth); + private = lib->credmgr->get_private(lib->credmgr, KEY_ANY, id, NULL); if (private) { switch (private->get_type(private)) diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_natd.c b/src/libcharon/sa/ikev1/tasks/isakmp_natd.c index b8af6f67b..cb1a31371 100644 --- a/src/libcharon/sa/ikev1/tasks/isakmp_natd.c +++ b/src/libcharon/sa/ikev1/tasks/isakmp_natd.c @@ -41,7 +41,6 @@ #include <string.h> -#include <hydra.h> #include <daemon.h> #include <sa/ikev1/keymat_v1.h> #include <config/peer_cfg.h> @@ -104,7 +103,7 @@ static bool force_encap(ike_cfg_t *ike_cfg) { if (!ike_cfg->force_encap(ike_cfg)) { - return hydra->kernel_interface->get_features(hydra->kernel_interface) & + return charon->kernel->get_features(charon->kernel) & KERNEL_REQUIRE_UDP_ENCAPSULATION; } return TRUE; diff --git a/src/libcharon/sa/ikev1/tasks/mode_config.c b/src/libcharon/sa/ikev1/tasks/mode_config.c index a03477e18..b9f924009 100644 --- a/src/libcharon/sa/ikev1/tasks/mode_config.c +++ b/src/libcharon/sa/ikev1/tasks/mode_config.c @@ -76,35 +76,20 @@ typedef struct { */ static configuration_attribute_t *build_vip(host_t *vip) { - configuration_attribute_type_t type; - chunk_t chunk, prefix; + configuration_attribute_type_t type = INTERNAL_IP4_ADDRESS; + chunk_t chunk; - if (vip->get_family(vip) == AF_INET) + if (vip->get_family(vip) == AF_INET6) { - type = INTERNAL_IP4_ADDRESS; - if (vip->is_anyaddr(vip)) - { - chunk = chunk_empty; - } - else - { - chunk = vip->get_address(vip); - } + type = INTERNAL_IP6_ADDRESS; + } + if (vip->is_anyaddr(vip)) + { + chunk = chunk_empty; } else { - type = INTERNAL_IP6_ADDRESS; - if (vip->is_anyaddr(vip)) - { - chunk = chunk_empty; - } - else - { - prefix = chunk_alloca(1); - *prefix.ptr = 64; - chunk = vip->get_address(vip); - chunk = chunk_cata("cc", chunk, prefix); - } + chunk = vip->get_address(vip); } return configuration_attribute_create_chunk(PLV1_CONFIGURATION_ATTRIBUTE, type, chunk); @@ -165,8 +150,8 @@ static void process_attribute(private_mode_config_t *this, } else { - /* skip prefix byte in IPv6 payload*/ - if (family == AF_INET6) + /* skip prefix byte in IPv6 payload sent by older releases */ + if (family == AF_INET6 && addr.len == 17) { addr.len--; } diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c index e7d26443b..b4fe04663 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.c +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c @@ -171,6 +171,11 @@ struct private_quick_mode_t { u_int32_t rekey; /** + * Delete old child after successful rekey + */ + bool delete; + + /** * Negotiated mode, tunnel or transport */ ipsec_mode_t mode; @@ -406,8 +411,17 @@ static bool install(private_quick_mode_t *this) if (old) { charon->bus->child_rekey(charon->bus, old, this->child_sa); - /* rekeyed CHILD_SAs stay installed until they expire */ + /* rekeyed CHILD_SAs stay installed until they expire or are deleted + * by the other peer */ old->set_state(old, CHILD_REKEYED); + /* as initiator we delete the CHILD_SA if configured to do so */ + if (this->initiator && this->delete) + { + this->ike_sa->queue_task(this->ike_sa, + (task_t*)quick_delete_create(this->ike_sa, + this->proposal->get_protocol(this->proposal), + this->rekey, TRUE, FALSE)); + } } else { @@ -1450,6 +1464,8 @@ quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config, .tsi = tsi ? tsi->clone(tsi) : NULL, .tsr = tsr ? tsr->clone(tsr) : NULL, .proto = PROTO_ESP, + .delete = lib->settings->get_bool(lib->settings, + "%s.delete_rekeyed", FALSE, lib->ns), ); if (config) diff --git a/src/libcharon/sa/ikev1/tasks/xauth.c b/src/libcharon/sa/ikev1/tasks/xauth.c index c0c91574c..ecdfc780d 100644 --- a/src/libcharon/sa/ikev1/tasks/xauth.c +++ b/src/libcharon/sa/ikev1/tasks/xauth.c @@ -16,7 +16,6 @@ #include "xauth.h" #include <daemon.h> -#include <hydra.h> #include <encoding/payloads/cp_payload.h> #include <processing/jobs/adopt_children_job.h> #include <sa/ikev1/tasks/mode_config.h> diff --git a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c index 2284a484d..04ccd4f4f 100644 --- a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c +++ b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c @@ -55,11 +55,6 @@ struct private_pubkey_authenticator_t { * Reserved bytes of ID payload */ char reserved[3]; - - /** - * Whether to store signature schemes on remote auth configs. - */ - bool store_signature_scheme; }; /** @@ -130,7 +125,7 @@ static array_t *select_signature_schemes(keymat_v2_t *keymat, enumerator = auth->create_enumerator(auth); while (enumerator->enumerate(enumerator, &rule, &config)) { - if (rule != AUTH_RULE_SIGNATURE_SCHEME) + if (rule != AUTH_RULE_IKE_SIGNATURE_SCHEME) { continue; } @@ -369,6 +364,8 @@ METHOD(authenticator_t, process, status_t, signature_scheme_t scheme; status_t status = NOT_FOUND; keymat_v2_t *keymat; + const char *reason = "unsupported"; + bool online; auth_payload = (auth_payload_t*)message->get_payload(message, PLV2_AUTH); if (!auth_payload) @@ -397,8 +394,11 @@ METHOD(authenticator_t, process, status_t, { break; } + reason = "payload invalid"; /* fall-through */ default: + DBG1(DBG_IKE, "%N authentication %s", auth_method_names, + auth_method, reason); return INVALID_ARG; } id = this->ike_sa->get_other_id(this->ike_sa); @@ -409,8 +409,10 @@ METHOD(authenticator_t, process, status_t, return FAILED; } auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE); + online = !this->ike_sa->has_condition(this->ike_sa, + COND_ONLINE_VALIDATION_SUSPENDED); enumerator = lib->credmgr->create_public_enumerator(lib->credmgr, - key_type, id, auth); + key_type, id, auth, online); while (enumerator->enumerate(enumerator, &public, ¤t_auth)) { if (public->verify(public, scheme, octets, auth_data)) @@ -421,9 +423,10 @@ METHOD(authenticator_t, process, status_t, status = SUCCESS; auth->merge(auth, current_auth, FALSE); auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY); - if (this->store_signature_scheme) + auth->add(auth, AUTH_RULE_IKE_SIGNATURE_SCHEME, (uintptr_t)scheme); + if (!online) { - auth->add(auth, AUTH_RULE_SIGNATURE_SCHEME, (uintptr_t)scheme); + auth->add(auth, AUTH_RULE_CERT_VALIDATION_SUSPENDED, TRUE); } break; } @@ -497,8 +500,6 @@ pubkey_authenticator_t *pubkey_authenticator_create_verifier(ike_sa_t *ike_sa, .ike_sa = ike_sa, .ike_sa_init = received_init, .nonce = sent_nonce, - .store_signature_scheme = lib->settings->get_bool(lib->settings, - "%s.signature_authentication_constraints", TRUE, lib->ns), ); memcpy(this->reserved, reserved, sizeof(this->reserved)); diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index 4676867df..c2f972ab1 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2014 Tobias Brunner + * Copyright (C) 2007-2015 Tobias Brunner * Copyright (C) 2007-2010 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -30,10 +30,12 @@ #include <sa/ikev2/tasks/ike_rekey.h> #include <sa/ikev2/tasks/ike_reauth.h> #include <sa/ikev2/tasks/ike_reauth_complete.h> +#include <sa/ikev2/tasks/ike_redirect.h> #include <sa/ikev2/tasks/ike_delete.h> #include <sa/ikev2/tasks/ike_config.h> #include <sa/ikev2/tasks/ike_dpd.h> #include <sa/ikev2/tasks/ike_vendor.h> +#include <sa/ikev2/tasks/ike_verify_peer_cert.h> #include <sa/ikev2/tasks/child_create.h> #include <sa/ikev2/tasks/child_rekey.h> #include <sa/ikev2/tasks/child_delete.h> @@ -474,6 +476,11 @@ METHOD(task_manager_t, initiate, status_t, exchange = INFORMATIONAL; break; } + if (activate_task(this, TASK_IKE_REDIRECT)) + { + exchange = INFORMATIONAL; + break; + } if (activate_task(this, TASK_CHILD_DELETE)) { exchange = INFORMATIONAL; @@ -521,6 +528,11 @@ METHOD(task_manager_t, initiate, status_t, exchange = INFORMATIONAL; break; } + if (activate_task(this, TASK_IKE_VERIFY_PEER_CERT)) + { + exchange = INFORMATIONAL; + break; + } case IKE_REKEYING: if (activate_task(this, TASK_IKE_DELETE)) { @@ -618,7 +630,7 @@ METHOD(task_manager_t, initiate, status_t, if (this->initiating.type == EXCHANGE_TYPE_UNDEFINED) { message->destroy(message); - return SUCCESS; + return initiate(this); } if (!generate_message(this, message, &this->initiating.packets)) @@ -656,6 +668,32 @@ static status_t process_response(private_task_manager_t *this, return DESTROY_ME; } + enumerator = array_create_enumerator(this->active_tasks); + while (enumerator->enumerate(enumerator, &task)) + { + if (!task->pre_process) + { + continue; + } + switch (task->pre_process(task, message)) + { + case SUCCESS: + break; + case FAILED: + default: + /* just ignore the message */ + DBG1(DBG_IKE, "ignore invalid %N response", + exchange_type_names, message->get_exchange_type(message)); + enumerator->destroy(enumerator); + return SUCCESS; + case DESTROY_ME: + /* critical failure, destroy IKE_SA */ + enumerator->destroy(enumerator); + return DESTROY_ME; + } + } + enumerator->destroy(enumerator); + /* catch if we get resetted while processing */ this->reset = FALSE; enumerator = array_create_enumerator(this->active_tasks); @@ -992,6 +1030,11 @@ static status_t process_request(private_task_manager_t *this, * invokes all the required hooks. */ task = (task_t*)ike_delete_create( this->ike_sa, FALSE); + break; + case REDIRECT: + task = (task_t*)ike_redirect_create( + this->ike_sa, NULL); + break; default: break; } @@ -1041,6 +1084,44 @@ static status_t process_request(private_task_manager_t *this, } } + enumerator = array_create_enumerator(this->passive_tasks); + while (enumerator->enumerate(enumerator, &task)) + { + if (!task->pre_process) + { + continue; + } + switch (task->pre_process(task, message)) + { + case SUCCESS: + break; + case FAILED: + default: + /* just ignore the message */ + DBG1(DBG_IKE, "ignore invalid %N request", + exchange_type_names, message->get_exchange_type(message)); + enumerator->destroy(enumerator); + switch (message->get_exchange_type(message)) + { + case IKE_SA_INIT: + /* no point in keeping the SA when it was created with + * an invalid IKE_SA_INIT message */ + return DESTROY_ME; + default: + /* remove tasks we queued for this request */ + flush_queue(this, TASK_QUEUE_PASSIVE); + /* fall-through */ + case IKE_AUTH: + return NEED_MORE; + } + case DESTROY_ME: + /* critical failure, destroy IKE_SA */ + enumerator->destroy(enumerator); + return DESTROY_ME; + } + } + enumerator->destroy(enumerator); + /* let the tasks process the message */ enumerator = array_create_enumerator(this->passive_tasks); while (enumerator->enumerate(enumerator, (void*)&task)) @@ -1331,12 +1412,17 @@ METHOD(task_manager_t, process_message, status_t, { /* ignore messages altered to EXCHANGE_TYPE_UNDEFINED */ return SUCCESS; } - if (process_request(this, msg) != SUCCESS) + switch (process_request(this, msg)) { - flush(this); - return DESTROY_ME; + case SUCCESS: + this->responding.mid++; + break; + case NEED_MORE: + break; + default: + flush(this); + return DESTROY_ME; } - this->responding.mid++; } else if ((mid == this->responding.mid - 1) && array_count(this->responding.packets)) @@ -1570,8 +1656,12 @@ static void trigger_mbb_reauth(private_task_manager_t *this) } enumerator->destroy(enumerator); + /* suspend online revocation checking until the SA is established */ + new->set_condition(new, COND_ONLINE_VALIDATION_SUSPENDED, TRUE); + if (new->initiate(new, NULL, 0, NULL, NULL) != DESTROY_ME) { + new->queue_task(new, (task_t*)ike_verify_peer_cert_create(new)); new->queue_task(new, (task_t*)ike_reauth_complete_create(new, this->ike_sa->get_id(this->ike_sa))); charon->ike_sa_manager->checkin(charon->ike_sa_manager, new); diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index 97f73d851..3d4ded944 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -18,7 +18,6 @@ #include "child_create.h" #include <daemon.h> -#include <hydra.h> #include <sa/ikev2/keymat_v2.h> #include <crypto/diffie_hellman.h> #include <credentials/certificates/x509.h> @@ -786,7 +785,7 @@ static bool build_payloads(private_child_create_t *this, message_t *message) break; } - features = hydra->kernel_interface->get_features(hydra->kernel_interface); + features = charon->kernel->get_features(charon->kernel); if (!(features & KERNEL_ESP_V3_TFC)) { message->add_notify(message, FALSE, ESP_TFC_PADDING_NOT_SUPPORTED, @@ -1221,6 +1220,10 @@ METHOD(task_t, build_r, status_t, { /* wait until all authentication round completed */ return NEED_MORE; } + if (this->ike_sa->has_condition(this->ike_sa, COND_REDIRECTED)) + { /* no CHILD_SA is created for redirected SAs */ + return SUCCESS; + } ike_auth = TRUE; default: break; diff --git a/src/libcharon/sa/ikev2/tasks/child_rekey.c b/src/libcharon/sa/ikev2/tasks/child_rekey.c index c7a8a1342..6f0c2b2c7 100644 --- a/src/libcharon/sa/ikev2/tasks/child_rekey.c +++ b/src/libcharon/sa/ikev2/tasks/child_rekey.c @@ -279,11 +279,15 @@ static child_sa_t *handle_collision(private_child_rekey_t *this) /* don't touch child other created, it has already been deleted */ if (!this->other_child_destroyed) { - /* disable close action for the redundand child */ + /* disable close action and updown event for redundant child */ child_sa = other->child_create->get_child(other->child_create); if (child_sa) { child_sa->set_close_action(child_sa, ACTION_NONE); + if (child_sa->get_state(child_sa) != CHILD_REKEYING) + { + child_sa->set_state(child_sa, CHILD_REKEYING); + } } } } @@ -372,6 +376,11 @@ METHOD(task_t, process_i, status_t, { return SUCCESS; } + /* disable updown event for redundant CHILD_SA */ + if (to_delete->get_state(to_delete) != CHILD_REKEYING) + { + to_delete->set_state(to_delete, CHILD_REKEYING); + } spi = to_delete->get_spi(to_delete, TRUE); protocol = to_delete->get_protocol(to_delete); diff --git a/src/libcharon/sa/ikev2/tasks/ike_auth.c b/src/libcharon/sa/ikev2/tasks/ike_auth.c index 2554496c1..79a436fbf 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_auth.c +++ b/src/libcharon/sa/ikev2/tasks/ike_auth.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012-2015 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -25,6 +25,7 @@ #include <encoding/payloads/eap_payload.h> #include <encoding/payloads/nonce_payload.h> #include <sa/ikev2/authenticators/eap_authenticator.h> +#include <processing/jobs/delete_ike_sa_job.h> typedef struct private_ike_auth_t private_ike_auth_t; @@ -117,6 +118,11 @@ struct private_ike_auth_t { * Is EAP acceptable, did we strictly authenticate peer? */ bool eap_acceptable; + + /** + * Gateway ID if redirected + */ + identification_t *redirect_to; }; /** @@ -685,6 +691,7 @@ METHOD(task_t, process_r, status_t, METHOD(task_t, build_r, status_t, private_ike_auth_t *this, message_t *message) { + identification_t *gateway; auth_cfg_t *cfg; if (message->get_exchange_type(message) == IKE_SA_INIT) @@ -817,34 +824,56 @@ METHOD(task_t, build_r, status_t, { this->do_another_auth = FALSE; } - if (!this->do_another_auth && !this->expect_another_auth) + if (this->do_another_auth || this->expect_another_auth) { - if (charon->ike_sa_manager->check_uniqueness(charon->ike_sa_manager, - this->ike_sa, FALSE)) - { - DBG1(DBG_IKE, "cancelling IKE_SA setup due to uniqueness policy"); - charon->bus->alert(charon->bus, ALERT_UNIQUE_KEEP); - message->add_notify(message, TRUE, AUTHENTICATION_FAILED, - chunk_empty); - return FAILED; - } - if (!charon->bus->authorize(charon->bus, TRUE)) - { - DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, cancelling"); - goto peer_auth_failed; - } - DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]", - this->ike_sa->get_name(this->ike_sa), - this->ike_sa->get_unique_id(this->ike_sa), - this->ike_sa->get_my_host(this->ike_sa), - this->ike_sa->get_my_id(this->ike_sa), - this->ike_sa->get_other_host(this->ike_sa), - this->ike_sa->get_other_id(this->ike_sa)); - this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); - charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE); - return SUCCESS; + return NEED_MORE; } - return NEED_MORE; + + if (charon->ike_sa_manager->check_uniqueness(charon->ike_sa_manager, + this->ike_sa, FALSE)) + { + DBG1(DBG_IKE, "cancelling IKE_SA setup due to uniqueness policy"); + charon->bus->alert(charon->bus, ALERT_UNIQUE_KEEP); + message->add_notify(message, TRUE, AUTHENTICATION_FAILED, + chunk_empty); + return FAILED; + } + if (!charon->bus->authorize(charon->bus, TRUE)) + { + DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, cancelling"); + goto peer_auth_failed; + } + if (this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_REDIRECTION) && + charon->redirect->redirect_on_auth(charon->redirect, this->ike_sa, + &gateway)) + { + delete_ike_sa_job_t *job; + chunk_t data; + + DBG1(DBG_IKE, "redirecting peer to %Y", gateway); + data = redirect_data_create(gateway, chunk_empty); + message->add_notify(message, FALSE, REDIRECT, data); + gateway->destroy(gateway); + chunk_free(&data); + /* we use this condition to prevent the CHILD_SA from getting created */ + this->ike_sa->set_condition(this->ike_sa, COND_REDIRECTED, TRUE); + /* if the peer does not delete the SA we do so after a while */ + job = delete_ike_sa_job_create(this->ike_sa->get_id(this->ike_sa), TRUE); + lib->scheduler->schedule_job(lib->scheduler, (job_t*)job, + lib->settings->get_int(lib->settings, + "%s.half_open_timeout", HALF_OPEN_IKE_SA_TIMEOUT, + lib->ns)); + } + DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]", + this->ike_sa->get_name(this->ike_sa), + this->ike_sa->get_unique_id(this->ike_sa), + this->ike_sa->get_my_host(this->ike_sa), + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa)); + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); + charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE); + return SUCCESS; peer_auth_failed: message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty); @@ -964,6 +993,15 @@ METHOD(task_t, process_i, status_t, case ME_ENDPOINT: /* handled in ike_me task */ break; + case REDIRECT: + DESTROY_IF(this->redirect_to); + this->redirect_to = redirect_data_parse( + notify->get_notification_data(notify), NULL); + if (!this->redirect_to) + { + DBG1(DBG_IKE, "received invalid REDIRECT notify"); + } + break; default: { if (type <= 16383) @@ -1094,30 +1132,35 @@ METHOD(task_t, process_i, status_t, { this->expect_another_auth = FALSE; } - if (!this->expect_another_auth && !this->do_another_auth && !this->my_auth) + if (this->expect_another_auth || this->do_another_auth || this->my_auth) { - if (!update_cfg_candidates(this, TRUE)) - { - goto peer_auth_failed; - } - if (!charon->bus->authorize(charon->bus, TRUE)) - { - DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, " - "cancelling"); - goto peer_auth_failed; - } - DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]", - this->ike_sa->get_name(this->ike_sa), - this->ike_sa->get_unique_id(this->ike_sa), - this->ike_sa->get_my_host(this->ike_sa), - this->ike_sa->get_my_id(this->ike_sa), - this->ike_sa->get_other_host(this->ike_sa), - this->ike_sa->get_other_id(this->ike_sa)); - this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); - charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE); - return SUCCESS; + return NEED_MORE; } - return NEED_MORE; + if (!update_cfg_candidates(this, TRUE)) + { + goto peer_auth_failed; + } + if (!charon->bus->authorize(charon->bus, TRUE)) + { + DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, " + "cancelling"); + goto peer_auth_failed; + } + DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]", + this->ike_sa->get_name(this->ike_sa), + this->ike_sa->get_unique_id(this->ike_sa), + this->ike_sa->get_my_host(this->ike_sa), + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa)); + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); + charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE); + + if (this->redirect_to) + { + this->ike_sa->handle_redirect(this->ike_sa, this->redirect_to); + } + return SUCCESS; peer_auth_failed: charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); @@ -1141,6 +1184,7 @@ METHOD(task_t, migrate, void, DESTROY_IF(this->peer_cfg); DESTROY_IF(this->my_auth); DESTROY_IF(this->other_auth); + DESTROY_IF(this->redirect_to); this->candidates->destroy_offset(this->candidates, offsetof(peer_cfg_t, destroy)); this->my_packet = NULL; @@ -1149,6 +1193,7 @@ METHOD(task_t, migrate, void, this->peer_cfg = NULL; this->my_auth = NULL; this->other_auth = NULL; + this->redirect_to = NULL; this->do_another_auth = TRUE; this->expect_another_auth = TRUE; this->authentication_failed = FALSE; @@ -1165,6 +1210,7 @@ METHOD(task_t, destroy, void, DESTROY_IF(this->my_auth); DESTROY_IF(this->other_auth); DESTROY_IF(this->peer_cfg); + DESTROY_IF(this->redirect_to); this->candidates->destroy_offset(this->candidates, offsetof(peer_cfg_t, destroy)); free(this); } diff --git a/src/libcharon/sa/ikev2/tasks/ike_config.c b/src/libcharon/sa/ikev2/tasks/ike_config.c index 646f20c61..6c42b81a6 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_config.c +++ b/src/libcharon/sa/ikev2/tasks/ike_config.c @@ -333,6 +333,11 @@ METHOD(task_t, build_r, status_t, linked_list_t *vips, *pools; host_t *requested; + if (this->ike_sa->has_condition(this->ike_sa, COND_REDIRECTED)) + { /* don't assign attributes for redirected SAs */ + return SUCCESS; + } + id = this->ike_sa->get_other_eap_id(this->ike_sa); config = this->ike_sa->get_peer_cfg(this->ike_sa); vips = linked_list_create(); diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c index 1ff643d62..78579be95 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@ -118,6 +118,11 @@ struct private_ike_init_t { * Whether to use Signature Authentication as per RFC 7427 */ bool signature_authentication; + + /** + * Whether to follow IKEv2 redirects as per RFC 5685 + */ + bool follow_redirects; }; /** @@ -166,7 +171,7 @@ static void send_supported_hash_algorithms(private_ike_init_t *this, enumerator = auth->create_enumerator(auth); while (enumerator->enumerate(enumerator, &rule, &config)) { - if (rule == AUTH_RULE_SIGNATURE_SCHEME) + if (rule == AUTH_RULE_IKE_SIGNATURE_SCHEME) { hash = hasher_from_signature_scheme(config); if (hasher_algorithm_for_ikev2(hash)) @@ -324,6 +329,29 @@ static bool build_payloads(private_ike_init_t *this, message_t *message) send_supported_hash_algorithms(this, message); } } + /* notify other peer if we support redirection */ + if (!this->old_sa && this->initiator && this->follow_redirects) + { + identification_t *gateway; + host_t *from; + chunk_t data; + + from = this->ike_sa->get_redirected_from(this->ike_sa); + if (from) + { + gateway = identification_create_from_sockaddr( + from->get_sockaddr(from)); + data = redirect_data_create(gateway, chunk_empty); + message->add_notify(message, FALSE, REDIRECTED_FROM, data); + chunk_free(&data); + gateway->destroy(gateway); + } + else + { + message->add_notify(message, FALSE, REDIRECT_SUPPORTED, + chunk_empty); + } + } return TRUE; } @@ -391,6 +419,30 @@ static void process_payloads(private_ike_init_t *this, message_t *message) handle_supported_hash_algorithms(this, notify); } break; + case REDIRECTED_FROM: + { + identification_t *gateway; + chunk_t data; + + data = notify->get_notification_data(notify); + gateway = redirect_data_parse(data, NULL); + if (!gateway) + { + DBG1(DBG_IKE, "received invalid REDIRECTED_FROM " + "notify, ignored"); + break; + } + DBG1(DBG_IKE, "client got redirected from %Y", gateway); + gateway->destroy(gateway); + /* fall-through */ + } + case REDIRECT_SUPPORTED: + if (!this->old_sa) + { + this->ike_sa->enable_extension(this->ike_sa, + EXT_IKE_REDIRECTION); + } + break; default: /* other notifies are handled elsewhere */ break; @@ -550,6 +602,8 @@ static bool derive_keys(private_ike_init_t *this, METHOD(task_t, build_r, status_t, private_ike_init_t *this, message_t *message) { + identification_t *gateway; + /* check if we have everything we need */ if (this->proposal == NULL || this->other_nonce.len == 0 || this->my_nonce.len == 0) @@ -560,6 +614,22 @@ METHOD(task_t, build_r, status_t, } this->ike_sa->set_proposal(this->ike_sa, this->proposal); + /* check if we'd have to redirect the client */ + if (!this->old_sa && + this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_REDIRECTION) && + charon->redirect->redirect_on_init(charon->redirect, this->ike_sa, + &gateway)) + { + chunk_t data; + + DBG1(DBG_IKE, "redirecting peer to %Y", gateway); + data = redirect_data_create(gateway, this->other_nonce); + message->add_notify(message, TRUE, REDIRECT, data); + gateway->destroy(gateway); + chunk_free(&data); + return FAILED; + } + if (this->dh == NULL || !this->proposal->has_dh_group(this->proposal, this->dh_group)) { @@ -623,6 +693,54 @@ static void raise_alerts(private_ike_init_t *this, notify_type_t type) } } +METHOD(task_t, pre_process_i, status_t, + private_ike_init_t *this, message_t *message) +{ + enumerator_t *enumerator; + payload_t *payload; + + /* check for erroneous notifies */ + enumerator = message->create_payload_enumerator(message); + while (enumerator->enumerate(enumerator, &payload)) + { + if (payload->get_type(payload) == PLV2_NOTIFY) + { + notify_payload_t *notify = (notify_payload_t*)payload; + notify_type_t type = notify->get_notify_type(notify); + + switch (type) + { + case REDIRECT: + { + identification_t *gateway; + chunk_t data, nonce = chunk_empty; + status_t status = SUCCESS; + + if (this->old_sa) + { + break; + } + data = notify->get_notification_data(notify); + gateway = redirect_data_parse(data, &nonce); + if (!gateway || !chunk_equals(nonce, this->my_nonce)) + { + DBG1(DBG_IKE, "received invalid REDIRECT notify"); + status = FAILED; + } + DESTROY_IF(gateway); + chunk_free(&nonce); + enumerator->destroy(enumerator); + return status; + } + default: + break; + } + } + } + enumerator->destroy(enumerator); + return SUCCESS; +} + METHOD(task_t, process_i, status_t, private_ike_init_t *this, message_t *message) { @@ -678,6 +796,29 @@ METHOD(task_t, process_i, status_t, this->retry++; return NEED_MORE; } + case REDIRECT: + { + identification_t *gateway; + chunk_t data, nonce = chunk_empty; + status_t status = FAILED; + + if (this->old_sa) + { + DBG1(DBG_IKE, "received REDIRECT notify during rekeying" + ", ignored"); + break; + } + data = notify->get_notification_data(notify); + gateway = redirect_data_parse(data, &nonce); + if (this->ike_sa->handle_redirect(this->ike_sa, gateway)) + { + status = NEED_MORE; + } + DESTROY_IF(gateway); + chunk_free(&nonce); + enumerator->destroy(enumerator); + return status; + } default: { if (type <= 16383) @@ -802,6 +943,8 @@ ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa) .old_sa = old_sa, .signature_authentication = lib->settings->get_bool(lib->settings, "%s.signature_authentication", TRUE, lib->ns), + .follow_redirects = lib->settings->get_bool(lib->settings, + "%s.follow_redirects", TRUE, lib->ns), ); this->nonceg = this->keymat->keymat.create_nonce_gen(&this->keymat->keymat); @@ -809,6 +952,7 @@ ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa) { this->public.task.build = _build_i; this->public.task.process = _process_i; + this->public.task.pre_process = _pre_process_i; } else { diff --git a/src/libcharon/sa/ikev2/tasks/ike_me.c b/src/libcharon/sa/ikev2/tasks/ike_me.c index a7e7505a1..10d412ffd 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_me.c +++ b/src/libcharon/sa/ikev2/tasks/ike_me.c @@ -17,7 +17,6 @@ #include <string.h> -#include <hydra.h> #include <daemon.h> #include <config/peer_cfg.h> #include <encoding/payloads/id_payload.h> @@ -135,8 +134,8 @@ static void gather_and_add_endpoints(private_ike_me_t *this, message_t *message) host = this->ike_sa->get_my_host(this->ike_sa); port = host->get_port(host); - enumerator = hydra->kernel_interface->create_address_enumerator( - hydra->kernel_interface, ADDR_TYPE_REGULAR); + enumerator = charon->kernel->create_address_enumerator(charon->kernel, + ADDR_TYPE_REGULAR); while (enumerator->enumerate(enumerator, (void**)&addr)) { host = addr->clone(addr); diff --git a/src/libcharon/sa/ikev2/tasks/ike_mobike.c b/src/libcharon/sa/ikev2/tasks/ike_mobike.c index cbdc5e797..3f7bb175f 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_mobike.c +++ b/src/libcharon/sa/ikev2/tasks/ike_mobike.c @@ -18,7 +18,6 @@ #include <string.h> -#include <hydra.h> #include <daemon.h> #include <sa/ikev2/tasks/ike_natd.h> #include <encoding/payloads/notify_payload.h> @@ -196,8 +195,8 @@ static void build_address_list(private_ike_mobike_t *this, message_t *message) int added = 0; me = this->ike_sa->get_my_host(this->ike_sa); - enumerator = hydra->kernel_interface->create_address_enumerator( - hydra->kernel_interface, ADDR_TYPE_REGULAR); + enumerator = charon->kernel->create_address_enumerator(charon->kernel, + ADDR_TYPE_REGULAR); while (enumerator->enumerate(enumerator, (void**)&host)) { if (me->ip_equals(me, host)) @@ -333,8 +332,7 @@ METHOD(ike_mobike_t, transmit, bool, if (!this->check) { - me = hydra->kernel_interface->get_source_addr(hydra->kernel_interface, - other_old, me_old); + me = charon->kernel->get_source_addr(charon->kernel, other_old, me_old); if (me) { if (me->ip_equals(me, me_old)) @@ -372,8 +370,7 @@ METHOD(ike_mobike_t, transmit, bool, { continue; } - me = hydra->kernel_interface->get_source_addr( - hydra->kernel_interface, other, NULL); + me = charon->kernel->get_source_addr(charon->kernel, other, NULL); if (me) { /* reuse port for an active address, 4500 otherwise */ @@ -407,7 +404,7 @@ METHOD(task_t, build_i, status_t, /* we check if the existing address is still valid */ old = message->get_source(message); - new = hydra->kernel_interface->get_source_addr(hydra->kernel_interface, + new = charon->kernel->get_source_addr(charon->kernel, message->get_destination(message), old); if (new) { diff --git a/src/libcharon/sa/ikev2/tasks/ike_natd.c b/src/libcharon/sa/ikev2/tasks/ike_natd.c index dd34c1234..4bf5264dd 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_natd.c +++ b/src/libcharon/sa/ikev2/tasks/ike_natd.c @@ -18,7 +18,6 @@ #include <string.h> -#include <hydra.h> #include <daemon.h> #include <config/peer_cfg.h> #include <crypto/hashers/hasher.h> @@ -86,7 +85,7 @@ static bool force_encap(ike_cfg_t *ike_cfg) { if (!ike_cfg->force_encap(ike_cfg)) { - return hydra->kernel_interface->get_features(hydra->kernel_interface) & + return charon->kernel->get_features(charon->kernel) & KERNEL_REQUIRE_UDP_ENCAPSULATION; } return TRUE; @@ -327,7 +326,7 @@ METHOD(task_t, build_i, status_t, } else { - host = hydra->kernel_interface->get_source_addr(hydra->kernel_interface, + host = charon->kernel->get_source_addr(charon->kernel, this->ike_sa->get_other_host(this->ike_sa), NULL); if (host) { /* 2. */ @@ -341,8 +340,8 @@ METHOD(task_t, build_i, status_t, } else { /* 3. */ - enumerator = hydra->kernel_interface->create_address_enumerator( - hydra->kernel_interface, ADDR_TYPE_REGULAR); + enumerator = charon->kernel->create_address_enumerator( + charon->kernel, ADDR_TYPE_REGULAR); while (enumerator->enumerate(enumerator, (void**)&host)) { /* apply port 500 to host, but work on a copy */ diff --git a/src/libcharon/sa/ikev2/tasks/ike_redirect.c b/src/libcharon/sa/ikev2/tasks/ike_redirect.c new file mode 100644 index 000000000..f82c80f71 --- /dev/null +++ b/src/libcharon/sa/ikev2/tasks/ike_redirect.c @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2015 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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 "ike_redirect.h" + +#include <daemon.h> +#include <processing/jobs/delete_ike_sa_job.h> + +typedef struct private_ike_redirect_t private_ike_redirect_t; + +/** + * Private members + */ +struct private_ike_redirect_t { + + /** + * Public interface + */ + ike_redirect_t public; + + /** + * Assigned IKE_SA + */ + ike_sa_t *ike_sa; + + /** + * Gateway ID to redirect to + */ + identification_t *gateway; +}; + +METHOD(task_t, build_i, status_t, + private_ike_redirect_t *this, message_t *message) +{ + chunk_t data; + + DBG1(DBG_IKE, "redirecting peer to %Y", this->gateway); + data = redirect_data_create(this->gateway, chunk_empty); + message->add_notify(message, FALSE, REDIRECT, data); + chunk_free(&data); + this->ike_sa->set_condition(this->ike_sa, COND_REDIRECTED, TRUE); + return NEED_MORE; +} + +METHOD(task_t, process_r, status_t, + private_ike_redirect_t *this, message_t *message) +{ + notify_payload_t *notify; + identification_t *to; + + notify = message->get_notify(message, REDIRECT); + if (!notify) + { + return SUCCESS; + } + + to = redirect_data_parse(notify->get_notification_data(notify), NULL); + if (!to) + { + DBG1(DBG_IKE, "received invalid REDIRECT notify"); + } + else + { + this->ike_sa->handle_redirect(this->ike_sa, to); + to->destroy(to); + } + return SUCCESS; +} + +METHOD(task_t, build_r, status_t, + private_ike_redirect_t *this, message_t *message) +{ + /* not called because SUCCESS is returned above */ + return SUCCESS; +} + +METHOD(task_t, process_i, status_t, + private_ike_redirect_t *this, message_t *message) +{ + delete_ike_sa_job_t *job; + + /* if the peer does not delete the SA we do so after a while */ + job = delete_ike_sa_job_create(this->ike_sa->get_id(this->ike_sa), TRUE); + lib->scheduler->schedule_job(lib->scheduler, (job_t*)job, + lib->settings->get_int(lib->settings, + "%s.half_open_timeout", HALF_OPEN_IKE_SA_TIMEOUT, + lib->ns)); + return SUCCESS; +} + +METHOD(task_t, get_type, task_type_t, + private_ike_redirect_t *this) +{ + return TASK_IKE_REDIRECT; +} + +METHOD(task_t, migrate, void, + private_ike_redirect_t *this, ike_sa_t *ike_sa) +{ + this->ike_sa = ike_sa; +} + +METHOD(task_t, destroy, void, + private_ike_redirect_t *this) +{ + DESTROY_IF(this->gateway); + free(this); +} + +/* + * Described in header. + */ +ike_redirect_t *ike_redirect_create(ike_sa_t *ike_sa, identification_t *to) +{ + private_ike_redirect_t *this; + + INIT(this, + .public = { + .task = { + .get_type = _get_type, + .build = _build_r, + .process = _process_r, + .migrate = _migrate, + .destroy = _destroy, + }, + }, + .ike_sa = ike_sa, + ); + + if (to) + { + this->gateway = to->clone(to); + this->public.task.build = _build_i; + this->public.task.process = _process_i; + } + + return &this->public; +} diff --git a/src/libcharon/sa/ikev2/tasks/ike_redirect.h b/src/libcharon/sa/ikev2/tasks/ike_redirect.h new file mode 100644 index 000000000..afa00ce5d --- /dev/null +++ b/src/libcharon/sa/ikev2/tasks/ike_redirect.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2015 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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 ike_redirect ike_redirect + * @{ @ingroup tasks_v2 + */ + +#ifndef IKE_REDIRECT_H_ +#define IKE_REDIRECT_H_ + +typedef struct ike_redirect_t ike_redirect_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/task.h> + +/** + * Task that handles redirection requests for established SAs. + */ +struct ike_redirect_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * Create a new ike_redirect_t task. + * + * As initiator (i.e. original responder) pass the ID of the target gateway, + * as responder (i.e. original initiator) this argument is NULL. + * + * @param ike_sa IKE_SA this task works for + * @param to gateway ID (gets cloned), or NULL as responder + * @return task instance + */ +ike_redirect_t *ike_redirect_create(ike_sa_t *ike_sa, + identification_t *to); + +#endif /** IKE_REDIRECT_H_ @}*/ diff --git a/src/libcharon/sa/ikev2/tasks/ike_vendor.c b/src/libcharon/sa/ikev2/tasks/ike_vendor.c index cb3c270dc..e85b276e8 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_vendor.c +++ b/src/libcharon/sa/ikev2/tasks/ike_vendor.c @@ -13,6 +13,29 @@ * for more details. */ +/* + * Copyright (C) 2016 secunet Security Networks AG + * Copyright (C) 2016 Thomas Egerer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + #include "ike_vendor.h" #include <daemon.h> @@ -49,6 +72,8 @@ typedef struct { char *desc; /* extension flag negotiated with vendor ID, if any */ ike_extension_t extension; + /* Value from strongswan.conf, whether to send vendor ID */ + char *setting; /* length of vendor ID string, 0 for NULL terminated */ int len; /* vendor ID string */ @@ -68,23 +93,23 @@ static chunk_t get_vid_data(vid_data_t *data) */ static vid_data_t vids[] = { /* strongSwan MD5("strongSwan") */ - { "strongSwan", EXT_STRONGSWAN, 16, + { "strongSwan", EXT_STRONGSWAN, "send_vendor_id", 16, "\x88\x2f\xe5\x6d\x6f\xd2\x0d\xbc\x22\x51\x61\x3b\x2e\xbe\x5b\xeb"}, - { "Cisco Delete Reason", 0, 0, + { "Cisco Delete Reason", 0, NULL, 0, "CISCO-DELETE-REASON" }, - { "Cisco Copyright (c) 2009", 0, 0, + { "Cisco Copyright (c) 2009", 0, NULL, 0, "CISCO(COPYRIGHT)&Copyright (c) 2009 Cisco Systems, Inc." }, - { "FRAGMENTATION", 0, 16, + { "FRAGMENTATION", 0, NULL, 16, "\x40\x48\xb7\xd5\x6e\xbc\xe8\x85\x25\xe7\xde\x7f\x00\xd6\xc2\xd3"}, - { "MS NT5 ISAKMPOAKLEY v7", 0, 20, + { "MS NT5 ISAKMPOAKLEY v7", 0, NULL, 20, "\x1e\x2b\x51\x69\x05\x99\x1c\x7d\x7c\x96\xfc\xbf\xb5\x87\xe4\x61\x00\x00\x00\x07"}, - { "MS NT5 ISAKMPOAKLEY v8", 0, 20, + { "MS NT5 ISAKMPOAKLEY v8", 0, NULL, 20, "\x1e\x2b\x51\x69\x05\x99\x1c\x7d\x7c\x96\xfc\xbf\xb5\x87\xe4\x61\x00\x00\x00\x08"}, - { "MS NT5 ISAKMPOAKLEY v9", 0, 20, + { "MS NT5 ISAKMPOAKLEY v9", 0, NULL, 20, "\x1e\x2b\x51\x69\x05\x99\x1c\x7d\x7c\x96\xfc\xbf\xb5\x87\xe4\x61\x00\x00\x00\x09"}, - { "MS-Negotiation Discovery Capable", 0, 16, + { "MS-Negotiation Discovery Capable", 0, NULL, 16, "\xfb\x1d\xe3\xcd\xf3\x41\xb7\xea\x16\xb7\xe5\xbe\x08\x55\xf1\x20"}, - { "Vid-Initial-Contact", 0, 16, + { "Vid-Initial-Contact", 0, NULL, 16, "\x26\x24\x4d\x38\xed\xdb\x61\xb3\x17\x2a\x36\xe3\xd0\xcf\xb8\x19"}, }; @@ -92,14 +117,19 @@ METHOD(task_t, build, status_t, private_ike_vendor_t *this, message_t *message) { vendor_id_payload_t *vid; - bool strongswan; + bool send_vid; int i; - strongswan = lib->settings->get_bool(lib->settings, - "%s.send_vendor_id", FALSE, lib->ns); for (i = 0; i < countof(vids); i++) { - if (vids[i].extension == EXT_STRONGSWAN && strongswan) + send_vid = FALSE; + + if (vids[i].setting) + { + send_vid = lib->settings->get_bool(lib->settings, "%s.%s", send_vid, + lib->ns, vids[i].setting); + } + if (send_vid) { DBG2(DBG_IKE, "sending %s vendor ID", vids[i].desc); vid = vendor_id_payload_create_data(PLV2_VENDOR_ID, diff --git a/src/libcharon/sa/ikev2/tasks/ike_verify_peer_cert.c b/src/libcharon/sa/ikev2/tasks/ike_verify_peer_cert.c new file mode 100644 index 000000000..069d51d00 --- /dev/null +++ b/src/libcharon/sa/ikev2/tasks/ike_verify_peer_cert.c @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2015 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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 "ike_verify_peer_cert.h" + +#include <daemon.h> +#include <sa/ikev2/tasks/ike_delete.h> + +typedef struct private_ike_verify_peer_cert_t private_ike_verify_peer_cert_t; + +/** + * Private members + */ +struct private_ike_verify_peer_cert_t { + + /** + * Public methods and task_t interface. + */ + ike_verify_peer_cert_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Child ike_delete task, if necessary + */ + ike_delete_t *ike_delete; +}; + +METHOD(task_t, build_i, status_t, + private_ike_verify_peer_cert_t *this, message_t *message) +{ + if (!this->ike_sa->verify_peer_certificate(this->ike_sa)) + { + DBG1(DBG_IKE, "peer certificate verification failed, deleting SA"); + this->ike_delete = ike_delete_create(this->ike_sa, TRUE); + return this->ike_delete->task.build(&this->ike_delete->task, message); + } + DBG1(DBG_IKE, "peer certificate successfully verified"); + message->set_exchange_type(message, EXCHANGE_TYPE_UNDEFINED); + return SUCCESS; +} + +METHOD(task_t, process_i, status_t, + private_ike_verify_peer_cert_t *this, message_t *message) +{ + if (this->ike_delete) + { + this->ike_delete->task.process(&this->ike_delete->task, message); + /* try to reestablish the IKE_SA and all children */ + this->ike_sa->reestablish(this->ike_sa); + } + return DESTROY_ME; +} + +METHOD(task_t, get_type, task_type_t, + private_ike_verify_peer_cert_t *this) +{ + return TASK_IKE_VERIFY_PEER_CERT; +} + +METHOD(task_t, migrate, void, + private_ike_verify_peer_cert_t *this, ike_sa_t *ike_sa) +{ + if (this->ike_delete) + { + this->ike_delete->task.migrate(&this->ike_delete->task, ike_sa); + } + this->ike_sa = ike_sa; +} + +METHOD(task_t, destroy, void, + private_ike_verify_peer_cert_t *this) +{ + if (this->ike_delete) + { + this->ike_delete->task.destroy(&this->ike_delete->task); + } + free(this); +} + +/* + * Described in header. + */ +ike_verify_peer_cert_t *ike_verify_peer_cert_create(ike_sa_t *ike_sa) +{ + private_ike_verify_peer_cert_t *this; + + INIT(this, + .public = { + .task = { + .get_type = _get_type, + .migrate = _migrate, + .build = _build_i, + .process = _process_i, + .destroy = _destroy, + }, + }, + .ike_sa = ike_sa, + ); + + return &this->public; +} diff --git a/src/libcharon/sa/ikev2/tasks/ike_verify_peer_cert.h b/src/libcharon/sa/ikev2/tasks/ike_verify_peer_cert.h new file mode 100644 index 000000000..3d9aae0b3 --- /dev/null +++ b/src/libcharon/sa/ikev2/tasks/ike_verify_peer_cert.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2015 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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 ike_verify_peer_cert ike_verify_peer_cert + * @{ @ingroup tasks_v2 + */ + +#ifndef IKE_VERIFY_PEER_CERT_H_ +#define IKE_VERIFY_PEER_CERT_H_ + +typedef struct ike_verify_peer_cert_t ike_verify_peer_cert_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/task.h> + +/** + * Task of type ike_verify_peer_cert, verifies a peer's certificate. + * + * This task (re-)verifies the peer's certificate explicitly including online + * OCSP and CRL checks. + */ +struct ike_verify_peer_cert_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * Create a new ike_verify_peer_cert task. + * + * This task is initiator only. + * + * @param ike_sa IKE_SA this task works for + * @return ike_verify_peer_cert task to handle by the task_manager + */ +ike_verify_peer_cert_t *ike_verify_peer_cert_create(ike_sa_t *ike_sa); + +#endif /** IKE_VERIFY_PEER_CERT_H_ @}*/ diff --git a/src/libcharon/sa/redirect_manager.c b/src/libcharon/sa/redirect_manager.c new file mode 100644 index 000000000..ff92ac29f --- /dev/null +++ b/src/libcharon/sa/redirect_manager.c @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2015 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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 "redirect_manager.h" + +#include <collections/linked_list.h> +#include <threading/rwlock.h> +#include <bio/bio_reader.h> +#include <bio/bio_writer.h> + +typedef struct private_redirect_manager_t private_redirect_manager_t; + +/** + * Private data + */ +struct private_redirect_manager_t { + + /** + * Public interface + */ + redirect_manager_t public; + + /** + * Registered providers + */ + linked_list_t *providers; + + /** + * Lock to access list of providers + */ + rwlock_t *lock; +}; + + +/** + * Gateway identify types + * + * The encoding is the same as that for corresponding ID payloads. + */ +typedef enum { + /** IPv4 address of the VPN gateway */ + GATEWAY_ID_TYPE_IPV4 = 1, + /** IPv6 address of the VPN gateway */ + GATEWAY_ID_TYPE_IPV6 = 2, + /** FQDN of the VPN gateway */ + GATEWAY_ID_TYPE_FQDN = 3, +} gateway_id_type_t; + +/** + * Mapping of gateway identity types to identity types + */ +static id_type_t gateway_to_id_type(gateway_id_type_t type) +{ + switch (type) + { + case GATEWAY_ID_TYPE_IPV4: + return ID_IPV4_ADDR; + case GATEWAY_ID_TYPE_IPV6: + return ID_IPV6_ADDR; + case GATEWAY_ID_TYPE_FQDN: + return ID_FQDN; + default: + return 0; + } +} + +/** + * Mapping of identity types to gateway identity types + */ +static gateway_id_type_t id_type_to_gateway(id_type_t type) +{ + switch (type) + { + case ID_IPV4_ADDR: + return GATEWAY_ID_TYPE_IPV4; + case ID_IPV6_ADDR: + return GATEWAY_ID_TYPE_IPV6; + case ID_FQDN: + return GATEWAY_ID_TYPE_FQDN; + default: + return 0; + } +} + +METHOD(redirect_manager_t, add_provider, void, + private_redirect_manager_t *this, redirect_provider_t *provider) +{ + this->lock->write_lock(this->lock); + this->providers->insert_last(this->providers, provider); + this->lock->unlock(this->lock); +} + +METHOD(redirect_manager_t, remove_provider, void, + private_redirect_manager_t *this, redirect_provider_t *provider) +{ + this->lock->write_lock(this->lock); + this->providers->remove(this->providers, provider, NULL); + this->lock->unlock(this->lock); +} + +/** + * Determine whether a client should be redirected using the callback with the + * given offset into the redirect_provider_t interface. + */ +static bool should_redirect(private_redirect_manager_t *this, ike_sa_t *ike_sa, + identification_t **gateway, size_t offset) +{ + enumerator_t *enumerator; + void *provider; + bool redirect = FALSE; + + this->lock->read_lock(this->lock); + enumerator = this->providers->create_enumerator(this->providers); + while (enumerator->enumerate(enumerator, &provider)) + { + bool (**method)(void*,ike_sa_t*,identification_t**) = provider + offset; + if (*method && (*method)(provider, ike_sa, gateway)) + { + if (*gateway && id_type_to_gateway((*gateway)->get_type(*gateway))) + { + redirect = TRUE; + break; + } + else + { + DBG1(DBG_CFG, "redirect provider returned invalid gateway ID"); + DESTROY_IF(*gateway); + } + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + return redirect; +} + +METHOD(redirect_manager_t, redirect_on_init, bool, + private_redirect_manager_t *this, ike_sa_t *ike_sa, + identification_t **gateway) +{ + return should_redirect(this, ike_sa, gateway, + offsetof(redirect_provider_t, redirect_on_init)); +} + +METHOD(redirect_manager_t, redirect_on_auth, bool, + private_redirect_manager_t *this, ike_sa_t *ike_sa, + identification_t **gateway) +{ + return should_redirect(this, ike_sa, gateway, + offsetof(redirect_provider_t, redirect_on_auth)); +} + +METHOD(redirect_manager_t, destroy, void, + private_redirect_manager_t *this) +{ + this->providers->destroy(this->providers); + this->lock->destroy(this->lock); + free(this); +} + +/* + * Described in header + */ +redirect_manager_t *redirect_manager_create() +{ + private_redirect_manager_t *this; + + INIT(this, + .public = { + .add_provider = _add_provider, + .remove_provider = _remove_provider, + .redirect_on_init = _redirect_on_init, + .redirect_on_auth = _redirect_on_auth, + .destroy = _destroy, + }, + .providers = linked_list_create(), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + ); + + return &this->public; +} + +/* + * Encoding of a REDIRECT or REDIRECTED_FROM notify + * + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Next Payload |C| RESERVED | Payload Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |Protocol ID(=0)| SPI Size (=0) | Notify Message Type | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | GW Ident Type | GW Ident Len | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ~ + ~ New Responder GW Identity ~ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + ~ Nonce Data ~ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/* + * Described in header + */ +chunk_t redirect_data_create(identification_t *gw, chunk_t nonce) +{ + gateway_id_type_t type; + bio_writer_t *writer; + chunk_t data; + + type = id_type_to_gateway(gw->get_type(gw)); + if (!type) + { + return chunk_empty; + } + + writer = bio_writer_create(0); + writer->write_uint8(writer, type); + writer->write_data8(writer, gw->get_encoding(gw)); + if (nonce.ptr) + { + writer->write_data(writer, nonce); + } + + data = writer->extract_buf(writer); + writer->destroy(writer); + return data; +} + +/* + * Described in header + */ +identification_t *redirect_data_parse(chunk_t data, chunk_t *nonce) +{ + bio_reader_t *reader; + id_type_t id_type; + chunk_t gateway; + u_int8_t type; + + reader = bio_reader_create(data); + if (!reader->read_uint8(reader, &type) || + !reader->read_data8(reader, &gateway)) + { + DBG1(DBG_ENC, "invalid REDIRECT notify data"); + reader->destroy(reader); + return NULL; + } + id_type = gateway_to_id_type(type); + if (!id_type) + { + DBG1(DBG_ENC, "invalid gateway ID type (%d) in REDIRECT notify", type); + reader->destroy(reader); + return NULL; + } + if (nonce) + { + *nonce = chunk_clone(reader->peek(reader)); + } + reader->destroy(reader); + return identification_create_from_encoding(id_type, gateway); +} diff --git a/src/libcharon/sa/redirect_manager.h b/src/libcharon/sa/redirect_manager.h new file mode 100644 index 000000000..e8753265c --- /dev/null +++ b/src/libcharon/sa/redirect_manager.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2015 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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 redirect_manager redirect_manager + * @{ @ingroup sa + */ + +#ifndef REDIRECT_MANAGER_H_ +#define REDIRECT_MANAGER_H_ + +typedef struct redirect_manager_t redirect_manager_t; + +#include <sa/redirect_provider.h> + +/** + * Manages redirect providers. + */ +struct redirect_manager_t { + + /** + * Add a redirect provider. + * + * All registered providers are queried until one of them decides to + * redirect a client. + * + * A provider may be called concurrently for different IKE_SAs. + * + * @param provider provider to register + */ + void (*add_provider)(redirect_manager_t *this, + redirect_provider_t *provider); + + /** + * Remove a redirect provider. + * + * @param provider provider to unregister + */ + void (*remove_provider)(redirect_manager_t *this, + redirect_provider_t *provider); + + /** + * Determine whether a client should be redirected upon receipt of the + * IKE_SA_INIT message. + * + * @param ike_sa IKE_SA for which this is called + * @param gateway[out] new IKE gateway (IP or FQDN) + * @return TRUE if client should be redirected, FALSE otherwise + */ + bool (*redirect_on_init)(redirect_manager_t *this, ike_sa_t *ike_sa, + identification_t **gateway); + + /** + * Determine whether a client should be redirected after the IKE_AUTH has + * been handled. Should be called after the client is authenticated and + * when the server authenticates itself. + * + * @param ike_sa IKE_SA for which this is called + * @param gateway[out] new IKE gateway (IP or FQDN) + * @return TRUE if client should be redirected, FALSE otherwise + */ + bool (*redirect_on_auth)(redirect_manager_t *this, ike_sa_t *ike_sa, + identification_t **gateway); + + /** + * Destroy this instance. + */ + void (*destroy)(redirect_manager_t *this); +}; + +/** + * Create a redirect manager instance. + * + * @return manager instance + */ +redirect_manager_t *redirect_manager_create(); + +/** + * Create notification data of a REDIRECT or REDIRECT_FROM payload using the + * given gateway identity and optional nonce (only used during IKE_SA_INIT). + * + * @param gw gateway identity (IP or FQDN), gets cloned + * @param nonce nonce value, or chunk_empty, gets cloned + * @return notify data, chunk_empty if ID type is not supported + */ +chunk_t redirect_data_create(identification_t *gw, chunk_t nonce); + +/** + * Parse notification data of a REDIRECT or REDIRECTED_FROM notify payload. + * + * @param data notification data to parse + * @param[out] nonce nonce data (allocated), if any was provided + * @return gateway identity, NULL if data is invalid + */ +identification_t *redirect_data_parse(chunk_t data, chunk_t *nonce); + +#endif /** REDIRECT_MANAGER_H_ @}*/ diff --git a/src/libcharon/sa/redirect_provider.h b/src/libcharon/sa/redirect_provider.h new file mode 100644 index 000000000..ef2288ffc --- /dev/null +++ b/src/libcharon/sa/redirect_provider.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2015 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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 redirect_provider redirect_provider + * @{ @ingroup sa + */ + +#ifndef REDIRECT_PROVIDER_H_ +#define REDIRECT_PROVIDER_H_ + +typedef struct redirect_provider_t redirect_provider_t; + +#include <library.h> +#include <sa/ike_sa.h> + +/** + * Interface that allows implementations to decide whether a client is + * redirected during IKE_SA_INIT or IKE_AUTH using RFC 5685. + */ +struct redirect_provider_t { + + /** + * Decide whether a client is redirect directly upon receipt of the + * IKE_SA_INIT message. + * + * @param ike_sa IKE_SA for which this is called + * @param gateway[out] new IKE gateway (IP or FQDN) + * @return TRUE if client should be redirected, FALSE otherwise + */ + bool (*redirect_on_init)(redirect_provider_t *this, ike_sa_t *ike_sa, + identification_t **gateway); + + /** + * Decide whether a client is redirect after the IKE_AUTH has been + * handled. This is called after the client is authenticated and when the + * server authenticates itself. + * + * @param ike_sa IKE_SA for which this is called + * @param gateway[out] new IKE gateway (IP or FQDN) + * @return TRUE if client should be redirected, FALSE otherwise + */ + bool (*redirect_on_auth)(redirect_provider_t *this, ike_sa_t *ike_sa, + identification_t **gateway); +}; + +#endif /** REDIRECT_PROVIDER_H_ @}*/ diff --git a/src/libcharon/sa/shunt_manager.c b/src/libcharon/sa/shunt_manager.c index 5231994c8..0e9cf6e1f 100644 --- a/src/libcharon/sa/shunt_manager.c +++ b/src/libcharon/sa/shunt_manager.c @@ -16,7 +16,6 @@ #include "shunt_manager.h" -#include <hydra.h> #include <daemon.h> #include <threading/rwlock.h> #include <threading/rwlock_condvar.h> @@ -111,22 +110,22 @@ static bool install_shunt_policy(child_cfg_t *child) continue; } /* install out policy */ - status |= hydra->kernel_interface->add_policy( - hydra->kernel_interface, host_any, host_any, + status |= charon->kernel->add_policy(charon->kernel, + host_any, host_any, my_ts, other_ts, POLICY_OUT, policy_type, &sa, child->get_mark(child, FALSE), policy_prio); /* install in policy */ - status |= hydra->kernel_interface->add_policy( - hydra->kernel_interface, host_any, host_any, + status |= charon->kernel->add_policy(charon->kernel, + host_any, host_any, other_ts, my_ts, POLICY_IN, policy_type, &sa, child->get_mark(child, TRUE), policy_prio); /* install forward policy */ - status |= hydra->kernel_interface->add_policy( - hydra->kernel_interface, host_any, host_any, + status |= charon->kernel->add_policy(charon->kernel, + host_any, host_any, other_ts, my_ts, POLICY_FWD, policy_type, &sa, child->get_mark(child, TRUE), policy_prio); @@ -248,22 +247,22 @@ static void uninstall_shunt_policy(child_cfg_t *child) continue; } /* uninstall out policy */ - status |= hydra->kernel_interface->del_policy( - hydra->kernel_interface, host_any, host_any, + status |= charon->kernel->del_policy(charon->kernel, + host_any, host_any, my_ts, other_ts, POLICY_OUT, policy_type, &sa, child->get_mark(child, FALSE), policy_prio); /* uninstall in policy */ - status |= hydra->kernel_interface->del_policy( - hydra->kernel_interface, host_any, host_any, + status |= charon->kernel->del_policy(charon->kernel, + host_any, host_any, other_ts, my_ts, POLICY_IN, policy_type, &sa, child->get_mark(child, TRUE), policy_prio); /* uninstall forward policy */ - status |= hydra->kernel_interface->del_policy( - hydra->kernel_interface, host_any, host_any, + status |= charon->kernel->del_policy(charon->kernel, + host_any, host_any, other_ts, my_ts, POLICY_FWD, policy_type, &sa, child->get_mark(child, TRUE), policy_prio); diff --git a/src/libcharon/sa/task.c b/src/libcharon/sa/task.c index b35b58185..405eda66b 100644 --- a/src/libcharon/sa/task.c +++ b/src/libcharon/sa/task.c @@ -28,6 +28,8 @@ ENUM(task_type_names, TASK_IKE_INIT, TASK_ISAKMP_CERT_POST, "IKE_REKEY", "IKE_REAUTH", "IKE_REAUTH_COMPLETE", + "IKE_REDIRECT", + "IKE_VERIFY_PEER_CERT", "IKE_DELETE", "IKE_DPD", "IKE_VENDOR", diff --git a/src/libcharon/sa/task.h b/src/libcharon/sa/task.h index 7bd3da1fe..31d70fb3b 100644 --- a/src/libcharon/sa/task.h +++ b/src/libcharon/sa/task.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Tobias Brunner + * Copyright (C) 2007-2015 Tobias Brunner * Copyright (C) 2006 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -57,6 +57,10 @@ enum task_type_t { TASK_IKE_REAUTH, /** completion task for make-before-break IKE_SA re-authentication */ TASK_IKE_REAUTH_COMPLETE, + /** redirect an active IKE_SA */ + TASK_IKE_REDIRECT, + /** verify a peer's certificate */ + TASK_IKE_VERIFY_PEER_CERT, /** delete an IKE_SA */ TASK_IKE_DELETE, /** liveness check */ @@ -154,6 +158,18 @@ struct task_t { status_t (*process) (task_t *this, message_t *message); /** + * Verify a message before processing it (optional to implement by tasks). + * + * @param message message to verify + * @return + * - FAILED if verification is not successful, the + * message will be silently discarded + * - DESTROY_ME if IKE_SA has to be destroyed + * - SUCCESS if verification is successful + */ + status_t (*pre_process) (task_t *this, message_t *message); + + /** * Get the type of the task implementation. */ task_type_t (*get_type) (task_t *this); diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c index 90ad7e40e..85e220775 100644 --- a/src/libcharon/sa/trap_manager.c +++ b/src/libcharon/sa/trap_manager.c @@ -16,7 +16,6 @@ #include "trap_manager.h" -#include <hydra.h> #include <daemon.h> #include <threading/mutex.h> #include <threading/rwlock.h> @@ -195,8 +194,7 @@ METHOD(trap_manager_t, install, u_int32_t, if (!me || me->is_anyaddr(me)) { DESTROY_IF(me); - me = hydra->kernel_interface->get_source_addr( - hydra->kernel_interface, other, NULL); + me = charon->kernel->get_source_addr(charon->kernel, other, NULL); if (!me) { DBG1(DBG_CFG, "installing trap failed, local address unknown"); diff --git a/src/libcharon/tests/Makefile.am b/src/libcharon/tests/Makefile.am index 5fd8ca26d..0589269aa 100644 --- a/src/libcharon/tests/Makefile.am +++ b/src/libcharon/tests/Makefile.am @@ -10,7 +10,6 @@ libcharon_tests_SOURCES = \ libcharon_tests_CFLAGS = \ -I$(top_srcdir)/src/libcharon \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libstrongswan \ -I$(top_srcdir)/src/libstrongswan/tests \ -DPLUGINDIR=\""$(abs_top_builddir)/src/libstrongswan/plugins\"" \ @@ -20,6 +19,5 @@ libcharon_tests_CFLAGS = \ libcharon_tests_LDFLAGS = @COVERAGE_LDFLAGS@ libcharon_tests_LDADD = \ $(top_builddir)/src/libcharon/libcharon.la \ - $(top_builddir)/src/libhydra/libhydra.la \ $(top_builddir)/src/libstrongswan/libstrongswan.la \ $(top_builddir)/src/libstrongswan/tests/libtest.la diff --git a/src/libcharon/tests/Makefile.in b/src/libcharon/tests/Makefile.in index 910aad928..87dea161a 100644 --- a/src/libcharon/tests/Makefile.in +++ b/src/libcharon/tests/Makefile.in @@ -109,7 +109,6 @@ am_libcharon_tests_OBJECTS = \ libcharon_tests_OBJECTS = $(am_libcharon_tests_OBJECTS) libcharon_tests_DEPENDENCIES = \ $(top_builddir)/src/libcharon/libcharon.la \ - $(top_builddir)/src/libhydra/libhydra.la \ $(top_builddir)/src/libstrongswan/libstrongswan.la \ $(top_builddir)/src/libstrongswan/tests/libtest.la AM_V_lt = $(am__v_lt_@AM_V@) @@ -415,6 +414,8 @@ strongswan_conf = @strongswan_conf@ strongswan_options = @strongswan_options@ swanctldir = @swanctldir@ sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ systemd_daemon_CFLAGS = @systemd_daemon_CFLAGS@ systemd_daemon_LIBS = @systemd_daemon_LIBS@ systemd_journal_CFLAGS = @systemd_journal_CFLAGS@ @@ -436,7 +437,6 @@ libcharon_tests_SOURCES = \ libcharon_tests_CFLAGS = \ -I$(top_srcdir)/src/libcharon \ - -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libstrongswan \ -I$(top_srcdir)/src/libstrongswan/tests \ -DPLUGINDIR=\""$(abs_top_builddir)/src/libstrongswan/plugins\"" \ @@ -446,7 +446,6 @@ libcharon_tests_CFLAGS = \ libcharon_tests_LDFLAGS = @COVERAGE_LDFLAGS@ libcharon_tests_LDADD = \ $(top_builddir)/src/libcharon/libcharon.la \ - $(top_builddir)/src/libhydra/libhydra.la \ $(top_builddir)/src/libstrongswan/libstrongswan.la \ $(top_builddir)/src/libstrongswan/tests/libtest.la diff --git a/src/libcharon/tests/libcharon_tests.c b/src/libcharon/tests/libcharon_tests.c index ec96de711..4692c3094 100644 --- a/src/libcharon/tests/libcharon_tests.c +++ b/src/libcharon/tests/libcharon_tests.c @@ -14,7 +14,6 @@ */ #include <test_runner.h> -#include <hydra.h> #include <daemon.h> /* declare test suite constructors */ @@ -39,7 +38,6 @@ static bool test_runner_init(bool init) { char *plugins, *plugindir; - libhydra_init(); libcharon_init(); plugins = getenv("TESTS_PLUGINS") ?: @@ -59,7 +57,6 @@ static bool test_runner_init(bool init) lib->processor->cancel(lib->processor); lib->plugins->unload(lib->plugins); libcharon_deinit(); - libhydra_deinit(); } return TRUE; } |