From 661f6bd0ad9eee61968c30652b4c707c1779b3f5 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 30 Apr 2024 11:39:00 +0200 Subject: [PATCH] kernel-netlink: Add SA direction attribute --- .../kernel_netlink/kernel_netlink_ipsec.c | 244 ++++++++++-------- 1 file changed, 136 insertions(+), 108 deletions(-) diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c index 493a22910..88734b0da 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -1187,114 +1187,6 @@ METHOD(kernel_ipsec_t, get_features, kernel_feature_t, (this->sa_lastused ? KERNEL_SA_USE_TIME : 0); } -/** - * 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, uint8_t proto, uint32_t min, uint32_t max, - uint32_t *spi) -{ - netlink_buf_t request; - struct nlmsghdr *hdr, *out; - struct xfrm_userspi_info *userspi; - uint32_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: - { - netlink_log_error(hdr, "allocating SPI failed"); - 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, - uint8_t protocol, uint32_t *spi) -{ - uint32_t spi_min, spi_max; - - spi_min = lib->settings->get_int(lib->settings, "%s.spi_min", - KERNEL_SPI_MIN, lib->ns); - spi_max = lib->settings->get_int(lib->settings, "%s.spi_max", - KERNEL_SPI_MAX, lib->ns); - - if (get_spi_internal(this, src, dst, protocol, min(spi_min, spi_max), - max(spi_min, spi_max), 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, - uint16_t *cpi) -{ - uint32_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((uint16_t)ntohl(received_spi)); - - DBG2(DBG_KNL, "got CPI %.4x", ntohs(*cpi)); - return SUCCESS; -} - /** * Format the mark for debug messages */ @@ -1384,6 +1276,136 @@ static bool add_uint32(struct nlmsghdr *hdr, int buflen, return TRUE; } +/** + * Add a uint8 attribute to message + */ +static bool add_uint8(struct nlmsghdr *hdr, int buflen, + enum xfrm_attr_type_t type, uint8_t value) +{ + uint8_t *xvalue; + + xvalue = netlink_reserve(hdr, buflen, type, sizeof(*xvalue)); + if (!xvalue) + { + return FALSE; + } + *xvalue = value; + return TRUE; +} + +/** + * 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, uint8_t proto, uint32_t min, uint32_t max, + uint32_t *spi) +{ + netlink_buf_t request; + struct nlmsghdr *hdr, *out; + struct xfrm_userspi_info *userspi; + uint32_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 (!add_uint8(hdr, sizeof(request), XFRMA_SA_DIR, XFRM_SA_DIR_IN)) + { + 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: + { + struct xfrm_usersa_info* usersa = NLMSG_DATA(hdr); + received_spi = usersa->id.spi; + break; + } + case NLMSG_ERROR: + { + netlink_log_error(hdr, "allocating SPI failed"); + 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, + uint8_t protocol, uint32_t *spi) +{ + uint32_t spi_min, spi_max; + + spi_min = lib->settings->get_int(lib->settings, "%s.spi_min", + KERNEL_SPI_MIN, lib->ns); + spi_max = lib->settings->get_int(lib->settings, "%s.spi_max", + KERNEL_SPI_MAX, lib->ns); + + if (get_spi_internal(this, src, dst, protocol, min(spi_min, spi_max), + max(spi_min, spi_max), 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, + uint16_t *cpi) +{ + uint32_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((uint16_t)ntohl(received_spi)); + + DBG2(DBG_KNL, "got CPI %.4x", ntohs(*cpi)); + return SUCCESS; +} + /* ETHTOOL_GSSET_INFO is available since 2.6.34 and ETH_SS_FEATURES (enum) and * ETHTOOL_GFEATURES since 2.6.39, so check for the latter */ #ifdef ETHTOOL_GFEATURES @@ -2046,6 +2068,12 @@ METHOD(kernel_ipsec_t, add_sa, status_t, } } + if (!add_uint8(hdr, sizeof(request), XFRMA_SA_DIR, + data->inbound ? XFRM_SA_DIR_IN : XFRM_SA_DIR_OUT)) + { + goto failed; + } + if (id->proto != IPPROTO_COMP) { /* generally, we don't need a replay window for outbound SAs, however,