commit 9a69641b34675de26c3989082795ab97325db55c Author: Paul Wouters Date: Mon Mar 1 14:57:31 2021 -0500 IKEv2: Fix TCP socket to have IP_XFRM_POLICY sockopt set. Without this, transport mode or host-to-host will not properly work on a number of kernels, such as RHEL8 4.18.0-291.el8.x86_64 Reported by: Sabrina Dubroca diff --git a/programs/pluto/iface_tcp.c b/programs/pluto/iface_tcp.c index 9a66343f3f..3b4f57d07d 100644 --- a/programs/pluto/iface_tcp.c +++ b/programs/pluto/iface_tcp.c @@ -52,6 +52,16 @@ #include "nat_traversal.h" /* for nat_traversal_enabled which seems like a broken idea */ #include "pluto_stats.h" +/* work around weird combo's of glibc and kernel header conflicts */ +#ifndef GLIBC_KERN_FLIP_HEADERS +# include "linux/xfrm.h" /* local (if configured) or system copy */ +# include "libreswan.h" +#else +# include "libreswan.h" +# include "linux/xfrm.h" /* local (if configured) or system copy */ +#endif + + static void accept_ike_in_tcp_cb(struct evconnlistener *evcon UNUSED, int accepted_fd, struct sockaddr *sockaddr, int sockaddr_len, @@ -383,6 +393,8 @@ static void iketcp_message_listener_cb(evutil_socket_t unused_fd UNUSED, struct logger from_logger = logger_from(&global_logger, &ifp->iketcp_remote_endpoint); struct logger *logger = &from_logger; + bool v6 = ifp->ip_dev->id_address.version == 6; + switch (ifp->iketcp_state) { case IKETCP_OPEN: @@ -443,7 +455,19 @@ static void iketcp_message_listener_cb(evutil_socket_t unused_fd UNUSED, if (impair.tcp_skip_setsockopt_espintcp) { llog(RC_LOG, logger, "IMPAIR: TCP: skipping setsockopt(ESPINTCP)"); } else { + struct xfrm_userpolicy_info policy_in = { + .action = XFRM_POLICY_ALLOW, + .sel.family = v6 ? AF_INET6 :AF_INET, + .dir = XFRM_POLICY_IN, + }; + struct xfrm_userpolicy_info policy_out = { + .action = XFRM_POLICY_ALLOW, + .sel.family = v6 ? AF_INET6 :AF_INET, + .dir = XFRM_POLICY_OUT, + }; + dbg("TCP: OPEN: socket %d enabling ESPINTCP", ifp->fd); + if (setsockopt(ifp->fd, IPPROTO_TCP, TCP_ULP, "espintcp", sizeof("espintcp"))) { int e = errno; @@ -459,6 +483,24 @@ static void iketcp_message_listener_cb(evutil_socket_t unused_fd UNUSED, free_any_iface_endpoint(&ifp); return; } + + if (setsockopt(ifp->fd, IPPROTO_IP, IP_XFRM_POLICY, &policy_in, sizeof(policy_in))) { + int e = errno; + llog(RC_LOG, logger, + "TCP: setsockopt(%d, SOL_TCP, IP_XFRM_POLICY, \"policy_in\") failed; closing socket "PRI_ERRNO, + ifp->fd, pri_errno(e)); + free_any_iface_endpoint(&ifp); + return; + } + if (setsockopt(ifp->fd, IPPROTO_IP, IP_XFRM_POLICY, &policy_out, sizeof(policy_out))) { + int e = errno; + llog(RC_LOG, logger, + "TCP: setsockopt(%d, SOL_TCP, IP_XFRM_POLICY, \"policy_out\") failed; closing socket "PRI_ERRNO, + ifp->fd, pri_errno(e)); + free_any_iface_endpoint(&ifp); + return; + } + } /* @@ -650,6 +692,17 @@ stf_status create_tcp_interface(struct state *st) if (impair.tcp_skip_setsockopt_espintcp) { log_state(RC_LOG, st, "IMPAIR: TCP: skipping setsockopt(espintcp)"); } else { + bool v6 = st->st_remote_endpoint.version == 6; + struct xfrm_userpolicy_info policy_in = { + .action = XFRM_POLICY_ALLOW, + .sel.family = v6 ? AF_INET6 :AF_INET, + .dir = XFRM_POLICY_IN, + }; + struct xfrm_userpolicy_info policy_out = { + .action = XFRM_POLICY_ALLOW, + .sel.family = v6 ? AF_INET6 :AF_INET, + .dir = XFRM_POLICY_OUT, + }; dbg("TCP: socket %d enabling \"espintcp\"", fd); if (setsockopt(fd, IPPROTO_TCP, TCP_ULP, "espintcp", sizeof("espintcp"))) { log_errno(st->st_logger, errno, @@ -657,6 +710,18 @@ stf_status create_tcp_interface(struct state *st) close(fd); return STF_FATAL; } + if (setsockopt(fd, IPPROTO_IP, IP_XFRM_POLICY, &policy_in, sizeof(policy_in))) { + log_errno(st->st_logger, errno, + "setsockopt(PPROTO_IP, IP_XFRM_POLICY(in)) failed in netlink_espintcp()"); + close(fd); + return STF_FATAL; + } + if (setsockopt(fd, IPPROTO_IP, IP_XFRM_POLICY, &policy_out, sizeof(policy_out))) { + log_errno(st->st_logger, errno, + "setsockopt(PPROTO_IP, IP_XFRM_POLICY(out)) failed in netlink_espintcp()"); + close(fd); + return STF_FATAL; + } } struct iface_endpoint *ifp = alloc_thing(struct iface_endpoint, "TCP iface initiator"); commit 7c38cd473d89b8c860ee7e3b8b31cfe012370f1d Author: Paul Wouters Date: Mon Mar 1 15:09:16 2021 -0500 documentation: small TCP doc update in ipsec.conf.in diff --git a/configs/ipsec.conf.in b/configs/ipsec.conf.in index bb2cc16e64..9fa3300176 100644 --- a/configs/ipsec.conf.in +++ b/configs/ipsec.conf.in @@ -28,9 +28,10 @@ config setup # dnssec-enable=no # # To enable IKE and IPsec over TCP for VPN server. Requires at least - # Linux 5.7 kernel. For TCP support as a VPN client, specify - # tcp-remote-port=4500 in the client conn section. + # Linux 5.7 kernel or a kernel with TCP backport (like RHEL8 4.18.0-291) # listen-tcp=yes + # To enable IKE and IPsec over TCP for VPN client, also specify + # tcp-remote-port=4500 in the client's conn section. # if it exists, include system wide crypto-policy defaults # include /etc/crypto-policies/back-ends/libreswan.config