diff --git a/src/include/linux/xfrm.h b/src/include/linux/xfrm.h index 80630dfd4..0c2191bf9 100644 --- a/src/include/linux/xfrm.h +++ b/src/include/linux/xfrm.h @@ -30,7 +30,7 @@ struct xfrm_sec_ctx { __u8 ctx_alg; __u16 ctx_len; __u32 ctx_sid; - char ctx_str[0]; + char ctx_str[]; }; /* Security Context Domains of Interpretation */ @@ -93,27 +93,27 @@ struct xfrm_replay_state_esn { __u32 oseq_hi; __u32 seq_hi; __u32 replay_window; - __u32 bmp[0]; + __u32 bmp[]; }; struct xfrm_algo { char alg_name[64]; unsigned int alg_key_len; /* in bits */ - char alg_key[0]; + char alg_key[]; }; struct xfrm_algo_auth { char alg_name[64]; unsigned int alg_key_len; /* in bits */ unsigned int alg_trunc_len; /* in bits */ - char alg_key[0]; + char alg_key[]; }; struct xfrm_algo_aead { char alg_name[64]; unsigned int alg_key_len; /* in bits */ unsigned int alg_icv_len; /* in bits */ - char alg_key[0]; + char alg_key[]; }; struct xfrm_stats { @@ -137,6 +137,11 @@ enum { XFRM_POLICY_MAX = 3 }; +enum xfrm_sa_dir { + XFRM_SA_DIR_IN = 1, + XFRM_SA_DIR_OUT = 2 +}; + enum { XFRM_SHARE_ANY, /* No limitations */ XFRM_SHARE_SESSION, /* For this session only */ @@ -212,6 +217,11 @@ enum { XFRM_MSG_MAPPING, #define XFRM_MSG_MAPPING XFRM_MSG_MAPPING + + XFRM_MSG_SETDEFAULT, +#define XFRM_MSG_SETDEFAULT XFRM_MSG_SETDEFAULT + XFRM_MSG_GETDEFAULT, +#define XFRM_MSG_GETDEFAULT XFRM_MSG_GETDEFAULT __XFRM_MSG_MAX }; #define XFRM_MSG_MAX (__XFRM_MSG_MAX - 1) @@ -288,7 +298,7 @@ enum xfrm_attr_type_t { XFRMA_ETIMER_THRESH, XFRMA_SRCADDR, /* xfrm_address_t */ XFRMA_COADDR, /* xfrm_address_t */ - XFRMA_LASTUSED, /* __u64 */ + XFRMA_LASTUSED, /* __u64 */ XFRMA_POLICY_TYPE, /* struct xfrm_userpolicy_type */ XFRMA_MIGRATE, XFRMA_ALG_AEAD, /* struct xfrm_algo_aead */ @@ -301,10 +311,12 @@ enum xfrm_attr_type_t { XFRMA_PROTO, /* __u8 */ XFRMA_ADDRESS_FILTER, /* struct xfrm_address_filter */ XFRMA_PAD, - XFRMA_OFFLOAD_DEV, /* struct xfrm_state_offload */ + XFRMA_OFFLOAD_DEV, /* struct xfrm_user_offload */ XFRMA_SET_MARK, /* __u32 */ XFRMA_SET_MARK_MASK, /* __u32 */ XFRMA_IF_ID, /* __u32 */ + XFRMA_MTIMER_THRESH, /* __u32 in seconds for input SA */ + XFRMA_SA_DIR, /* __u8 */ __XFRMA_MAX #define XFRMA_OUTPUT_MARK XFRMA_SET_MARK /* Compatibility */ @@ -384,6 +396,7 @@ struct xfrm_usersa_info { }; #define XFRM_SA_XFLAG_DONT_ENCAP_DSCP 1 +#define XFRM_SA_XFLAG_OSEQ_MAY_WRAP 2 struct xfrm_usersa_id { xfrm_address_t daddr; @@ -501,10 +514,30 @@ struct xfrm_user_offload { int ifindex; __u8 flags; }; +/* This flag was exposed without any kernel code that supports it. + * Unfortunately, strongswan has the code that sets this flag, + * which makes it impossible to reuse this bit. + * + * So leave it here to make sure that it won't be reused by mistake. + */ #define XFRM_OFFLOAD_IPV6 1 #define XFRM_OFFLOAD_INBOUND 2 +/* Two bits above are relevant for state path only, while + * offload is used for both policy and state flows. + * + * In policy offload mode, they are free and can be safely reused. + */ #define XFRM_OFFLOAD_PACKET 4 +struct xfrm_userpolicy_default { +#define XFRM_USERPOLICY_UNSPEC 0 +#define XFRM_USERPOLICY_BLOCK 1 +#define XFRM_USERPOLICY_ACCEPT 2 + __u8 in; + __u8 fwd; + __u8 out; +}; + #ifndef __KERNEL__ /* backwards compatibility for userspace */ #define XFRMGRP_ACQUIRE 1 diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c index db0b2ac37..c6e50cc05 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -139,7 +139,7 @@ struct kernel_algorithm_t { const char *name; }; -ENUM(xfrm_msg_names, XFRM_MSG_NEWSA, XFRM_MSG_MAPPING, +ENUM(xfrm_msg_names, XFRM_MSG_NEWSA, __XFRM_MSG_MAX, "XFRM_MSG_NEWSA", "XFRM_MSG_DELSA", "XFRM_MSG_GETSA", @@ -162,10 +162,13 @@ ENUM(xfrm_msg_names, XFRM_MSG_NEWSA, XFRM_MSG_MAPPING, "XFRM_MSG_GETSADINFO", "XFRM_MSG_NEWSPDINFO", "XFRM_MSG_GETSPDINFO", - "XFRM_MSG_MAPPING" + "XFRM_MSG_MAPPING", + "XFRM_MSG_SETDEFAULT", + "XFRM_MSG_GETDEFAULT", + "XFRM_MSG_MAX", ); -ENUM(xfrm_attr_type_names, XFRMA_UNSPEC, XFRMA_OFFLOAD_DEV, +ENUM(xfrm_attr_type_names, XFRMA_UNSPEC, __XFRMA_MAX, "XFRMA_UNSPEC", "XFRMA_ALG_AUTH", "XFRMA_ALG_CRYPT", @@ -195,6 +198,12 @@ ENUM(xfrm_attr_type_names, XFRMA_UNSPEC, XFRMA_OFFLOAD_DEV, "XFRMA_ADDRESS_FILTER", "XFRMA_PAD", "XFRMA_OFFLOAD_DEV", + "XFRMA_SET_MARK", + "XFRMA_SET_MARK_MASK", + "XFRMA_IF_ID", + "XFRMA_MTIMER_THRESH", + "XFRMA_SA_DIR", + "XFRMA_MAX", ); /** @@ -353,6 +362,11 @@ struct private_kernel_netlink_ipsec_t { */ bool sa_lastused; + /** + * Whether the kernel supports setting the SA direction + */ + bool sa_dir; + /** * Whether to install routes along policies */ @@ -1178,114 +1192,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 */ @@ -1375,6 +1281,137 @@ 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 (this->sa_dir && + !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 @@ -1715,11 +1752,6 @@ METHOD(kernel_ipsec_t, add_sa, status_t, sa->family = id->src->get_family(id->src); sa->mode = mode2kernel(mode); - if (!data->copy_df) - { - sa->flags |= XFRM_STATE_NOPMTUDISC; - } - if (!data->copy_ecn) { sa->flags |= XFRM_STATE_NOECN; @@ -1739,6 +1771,10 @@ METHOD(kernel_ipsec_t, add_sa, status_t, } else { + if (!data->copy_df) + { + sa->flags |= XFRM_STATE_NOPMTUDISC; + } switch (data->copy_dscp) { case DSCP_COPY_IN_ONLY: @@ -2038,13 +2074,21 @@ METHOD(kernel_ipsec_t, add_sa, status_t, } } + if (this->sa_dir && + !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, - * when using ESN the kernel rejects the attribute if it is 0 */ + /* we don't need a replay window for outbound SAs, however, older + * kernels reject the attribute if it is 0 when using ESN, while + * newer kernels reject it if > 0 if the SA's direction is set */ if (!data->inbound && data->replay_window) { - data->replay_window = data->esn ? 1 : 0; + data->replay_window = (data->esn && !this->sa_dir) ? 1 : 0; } if (data->esn || data->replay_window > 32) { @@ -4125,6 +4169,9 @@ static void check_kernel_features(private_kernel_netlink_ipsec_t *this) /* before 6.2 the kernel only provided the last used time for * specific outbound IPv6 SAs */ this->sa_lastused = a > 6 || (a == 6 && b >= 2); + /* 6.10 added support for SA direction and enforces certain + * flags e.g. 0 replay window for outbound SAs */ + this->sa_dir = a > 6 || (a == 6 && b >= 10); break; default: break;