diff options
Diffstat (limited to 'src/libhydra')
-rw-r--r-- | src/libhydra/attributes/mem_pool.c | 55 | ||||
-rw-r--r-- | src/libhydra/attributes/mem_pool.h | 16 | ||||
-rw-r--r-- | src/libhydra/kernel/kernel_interface.c | 18 | ||||
-rw-r--r-- | src/libhydra/kernel/kernel_interface.h | 19 | ||||
-rw-r--r-- | src/libhydra/kernel/kernel_ipsec.h | 8 | ||||
-rw-r--r-- | src/libhydra/kernel/kernel_net.h | 8 | ||||
-rw-r--r-- | src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c | 398 | ||||
-rw-r--r-- | src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c | 4 | ||||
-rw-r--r-- | src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c | 23 | ||||
-rw-r--r-- | src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.h | 25 | ||||
-rw-r--r-- | src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c | 4 |
11 files changed, 293 insertions, 285 deletions
diff --git a/src/libhydra/attributes/mem_pool.c b/src/libhydra/attributes/mem_pool.c index af53e10de..c82b1d02f 100644 --- a/src/libhydra/attributes/mem_pool.c +++ b/src/libhydra/attributes/mem_pool.c @@ -21,7 +21,7 @@ #include <collections/linked_list.h> #include <threading/mutex.h> -#define POOL_LIMIT (sizeof(uintptr_t)*8) +#define POOL_LIMIT (sizeof(u_int)*8 - 1) typedef struct private_mem_pool_t private_mem_pool_t; @@ -513,12 +513,11 @@ METHOD(mem_pool_t, destroy, void, } /** - * Described in header + * Generic constructor */ -mem_pool_t *mem_pool_create(char *name, host_t *base, int bits) +static private_mem_pool_t *create_generic(char *name) { private_mem_pool_t *this; - int addr_bits; INIT(this, .public = { @@ -538,6 +537,18 @@ mem_pool_t *mem_pool_create(char *name, host_t *base, int bits) .mutex = mutex_create(MUTEX_TYPE_DEFAULT), ); + return this; +} + +/** + * Described in header + */ +mem_pool_t *mem_pool_create(char *name, host_t *base, int bits) +{ + private_mem_pool_t *this; + int addr_bits; + + this = create_generic(name); if (base) { addr_bits = base->get_family(base) == AF_INET ? 32 : 128; @@ -550,7 +561,7 @@ mem_pool_t *mem_pool_create(char *name, host_t *base, int bits) DBG1(DBG_CFG, "virtual IP pool too large, limiting to %H/%d", base, addr_bits - bits); } - this->size = 1 << (bits); + this->size = 1 << bits; if (this->size > 2) { /* do not use first and last addresses of a block */ @@ -563,3 +574,37 @@ mem_pool_t *mem_pool_create(char *name, host_t *base, int bits) return &this->public; } +/** + * Described in header + */ +mem_pool_t *mem_pool_create_range(char *name, host_t *from, host_t *to) +{ + private_mem_pool_t *this; + chunk_t fromaddr, toaddr; + u_int32_t diff; + + fromaddr = from->get_address(from); + toaddr = to->get_address(to); + + if (from->get_family(from) != to->get_family(to) || + fromaddr.len != toaddr.len || fromaddr.len < sizeof(diff) || + memcmp(fromaddr.ptr, toaddr.ptr, toaddr.len) > 0) + { + DBG1(DBG_CFG, "invalid IP address range: %H-%H", from, to); + return NULL; + } + if (fromaddr.len > sizeof(diff) && + !chunk_equals(chunk_create(fromaddr.ptr, fromaddr.len - sizeof(diff)), + chunk_create(toaddr.ptr, toaddr.len - sizeof(diff)))) + { + DBG1(DBG_CFG, "IP address range too large: %H-%H", from, to); + return NULL; + } + this = create_generic(name); + this->base = from->clone(from); + diff = untoh32(toaddr.ptr + toaddr.len - sizeof(diff)) - + untoh32(fromaddr.ptr + fromaddr.len - sizeof(diff)); + this->size = diff + 1; + + return &this->public; +} diff --git a/src/libhydra/attributes/mem_pool.h b/src/libhydra/attributes/mem_pool.h index 692885ecd..7347bb547 100644 --- a/src/libhydra/attributes/mem_pool.h +++ b/src/libhydra/attributes/mem_pool.h @@ -89,7 +89,7 @@ struct mem_pool_t { * * @param id the id to acquire an address for * @param requested acquire this address, if possible - * @param existing TRUE to look for an existing lease, FALSE for a new one + * @param operation acquire operation to perform, see above * @return the acquired address */ host_t* (*acquire_address)(mem_pool_t *this, identification_t *id, @@ -128,9 +128,19 @@ struct mem_pool_t { * * @param name name of this pool * @param base base address of this pool, NULL to create an empty pool - * @param bits net mask + * @param bits number of non-network bits in base, as in CIDR notation + * @return memory pool instance */ mem_pool_t *mem_pool_create(char *name, host_t *base, int bits); -#endif /** MEM_POOL_H_ @} */ +/** + * Create an in-memory IP address from a range. + * + * @param name name of this pool + * @param from start of ranged pool + * @param to end of ranged pool + * @return memory pool instance, NULL if range invalid + */ +mem_pool_t *mem_pool_create_range(char *name, host_t *from, host_t *to); +#endif /** MEM_POOL_H_ @} */ diff --git a/src/libhydra/kernel/kernel_interface.c b/src/libhydra/kernel/kernel_interface.c index 8948e0561..b82fd6d3d 100644 --- a/src/libhydra/kernel/kernel_interface.c +++ b/src/libhydra/kernel/kernel_interface.c @@ -137,6 +137,22 @@ struct private_kernel_interface_t { 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 reqid, u_int32_t *spi) @@ -682,6 +698,7 @@ kernel_interface_t *kernel_interface_create() INIT(this, .public = { + .get_features = _get_features, .get_spi = _get_spi, .get_cpi = _get_cpi, .add_sa = _add_sa, @@ -757,4 +774,3 @@ kernel_interface_t *kernel_interface_create() return &this->public; } - diff --git a/src/libhydra/kernel/kernel_interface.h b/src/libhydra/kernel/kernel_interface.h index 8d8ef2e83..8d8d048d0 100644 --- a/src/libhydra/kernel/kernel_interface.h +++ b/src/libhydra/kernel/kernel_interface.h @@ -47,6 +47,7 @@ #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> @@ -56,6 +57,17 @@ typedef struct kernel_interface_t kernel_interface_t; #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), +}; + +/** * Constructor function for ipsec kernel interface */ typedef kernel_ipsec_t* (*kernel_ipsec_constructor_t)(void); @@ -74,6 +86,13 @@ typedef kernel_net_t* (*kernel_net_constructor_t)(void); 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 diff --git a/src/libhydra/kernel/kernel_ipsec.h b/src/libhydra/kernel/kernel_ipsec.h index 1da0805cb..c8afcaffd 100644 --- a/src/libhydra/kernel/kernel_ipsec.h +++ b/src/libhydra/kernel/kernel_ipsec.h @@ -30,6 +30,7 @@ typedef struct kernel_ipsec_t kernel_ipsec_t; #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. @@ -45,6 +46,13 @@ typedef struct kernel_ipsec_t kernel_ipsec_t; 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 diff --git a/src/libhydra/kernel/kernel_net.h b/src/libhydra/kernel/kernel_net.h index 6a3b2cee7..0d3417f1d 100644 --- a/src/libhydra/kernel/kernel_net.h +++ b/src/libhydra/kernel/kernel_net.h @@ -28,6 +28,7 @@ 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) @@ -56,6 +57,13 @@ enum kernel_address_type_t { 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. diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c index f9b2634a0..3e84d1699 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -95,12 +95,6 @@ #define XFRM_RTA(nlh, x) ((struct rtattr*)(NLMSG_DATA(nlh) + \ NLMSG_ALIGN(sizeof(x)))) /** - * Returns a pointer to the next rtattr following rta. - * !!! Do not use this to parse messages. Use RTA_NEXT and RTA_OK instead !!! - */ -#define XFRM_RTA_NEXT(rta) ((struct rtattr*)(((char*)(rta)) + \ - RTA_ALIGN((rta)->rta_len))) -/** * Returns the total size of attached rta data * (after 'usual' netlink data x like 'struct xfrm_usersa_info') */ @@ -176,8 +170,6 @@ ENUM(xfrm_attr_type_names, XFRMA_UNSPEC, XFRMA_REPLAY_ESN_VAL, "XFRMA_REPLAY_ESN_VAL", ); -#define END_OF_LIST -1 - /** * Algorithms for encryption */ @@ -208,7 +200,6 @@ static kernel_algorithm_t encryption_algs[] = { /* {ENCR_CAMELLIA_CCM_ICV16, "***" }, */ {ENCR_SERPENT_CBC, "serpent" }, {ENCR_TWOFISH_CBC, "twofish" }, - {END_OF_LIST, NULL } }; /** @@ -226,7 +217,6 @@ static kernel_algorithm_t integrity_algs[] = { /* {AUTH_DES_MAC, "***" }, */ /* {AUTH_KPDK_MD5, "***" }, */ {AUTH_AES_XCBC_96, "xcbc(aes)" }, - {END_OF_LIST, NULL } }; /** @@ -237,7 +227,6 @@ static kernel_algorithm_t compression_algs[] = { {IPCOMP_DEFLATE, "deflate" }, {IPCOMP_LZS, "lzs" }, {IPCOMP_LZJH, "lzjh" }, - {END_OF_LIST, NULL } }; /** @@ -246,33 +235,39 @@ static kernel_algorithm_t compression_algs[] = { static char* lookup_algorithm(transform_type_t type, int ikev2) { kernel_algorithm_t *list; - char *name = NULL; + 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; } - while (list->ikev2 != END_OF_LIST) + for (i = 0; i < count; i++) { - if (list->ikev2 == ikev2) + if (list[i].ikev2 == ikev2) { - return list->name; + return list[i].name; } - list++; } - hydra->kernel_interface->lookup_algorithm(hydra->kernel_interface, ikev2, - type, NULL, &name); - return name; + if (hydra->kernel_interface->lookup_algorithm(hydra->kernel_interface, + ikev2, type, NULL, &name)) + { + return name; + } + return NULL; } typedef struct private_kernel_netlink_ipsec_t private_kernel_netlink_ipsec_t; @@ -787,7 +782,7 @@ static traffic_selector_t* selector2ts(struct xfrm_selector *sel, bool src) if (host) { return traffic_selector_create_from_subnet(host, prefixlen, - sel->proto, port); + sel->proto, port, port ?: 65535); } return NULL; } @@ -1036,6 +1031,12 @@ static job_requeue_t receive_events(private_kernel_netlink_ipsec_t *this) return JOB_REQUEUE_DIRECT; } +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. */ @@ -1144,6 +1145,26 @@ METHOD(kernel_ipsec_t, get_cpi, status_t, 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, @@ -1216,8 +1237,6 @@ METHOD(kernel_ipsec_t, add_sa, status_t, sa->lft.soft_use_expires_seconds = 0; sa->lft.hard_use_expires_seconds = 0; - struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_usersa_info); - switch (enc_alg) { case ENCR_UNDEFINED: @@ -1250,23 +1269,17 @@ METHOD(kernel_ipsec_t, add_sa, status_t, DBG2(DBG_KNL, " using encryption algorithm %N with key size %d", encryption_algorithm_names, enc_alg, enc_key.len * 8); - rthdr->rta_type = XFRMA_ALG_AEAD; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo_aead) + - enc_key.len); - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) + algo = netlink_reserve(hdr, sizeof(request), XFRMA_ALG_AEAD, + sizeof(*algo) + enc_key.len); + if (!algo) { goto failed; } - - algo = (struct xfrm_algo_aead*)RTA_DATA(rthdr); 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); - - rthdr = XFRM_RTA_NEXT(rthdr); break; } default: @@ -1283,21 +1296,16 @@ METHOD(kernel_ipsec_t, add_sa, status_t, DBG2(DBG_KNL, " using encryption algorithm %N with key size %d", encryption_algorithm_names, enc_alg, enc_key.len * 8); - rthdr->rta_type = XFRMA_ALG_CRYPT; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo) + enc_key.len); - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) + algo = netlink_reserve(hdr, sizeof(request), XFRMA_ALG_CRYPT, + sizeof(*algo) + enc_key.len); + if (!algo) { goto failed; } - - algo = (struct xfrm_algo*)RTA_DATA(rthdr); 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); - - rthdr = XFRM_RTA_NEXT(rthdr); } } @@ -1335,17 +1343,12 @@ METHOD(kernel_ipsec_t, add_sa, status_t, /* 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. */ - rthdr->rta_type = XFRMA_ALG_AUTH_TRUNC; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo_auth) + - int_key.len); - - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) + algo = netlink_reserve(hdr, sizeof(request), XFRMA_ALG_AUTH_TRUNC, + sizeof(*algo) + int_key.len); + if (!algo) { goto failed; } - - algo = (struct xfrm_algo_auth*)RTA_DATA(rthdr); algo->alg_key_len = int_key.len * 8; algo->alg_trunc_len = trunc_len; strncpy(algo->alg_name, alg_name, sizeof(algo->alg_name)); @@ -1356,27 +1359,23 @@ METHOD(kernel_ipsec_t, add_sa, status_t, { struct xfrm_algo* algo; - rthdr->rta_type = XFRMA_ALG_AUTH; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo) + int_key.len); - - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) + algo = netlink_reserve(hdr, sizeof(request), XFRMA_ALG_AUTH, + sizeof(*algo) + int_key.len); + if (!algo) { goto failed; } - - algo = (struct xfrm_algo*)RTA_DATA(rthdr); 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); } - rthdr = XFRM_RTA_NEXT(rthdr); } if (ipcomp != IPCOMP_NONE) { - rthdr->rta_type = XFRMA_ALG_COMP; + struct xfrm_algo* algo; + alg_name = lookup_algorithm(COMPRESSION_ALGORITHM, ipcomp); if (alg_name == NULL) { @@ -1387,35 +1386,26 @@ METHOD(kernel_ipsec_t, add_sa, status_t, DBG2(DBG_KNL, " using compression algorithm %N", ipcomp_transform_names, ipcomp); - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo)); - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) + algo = netlink_reserve(hdr, sizeof(request), XFRMA_ALG_COMP, + sizeof(*algo)); + if (!algo) { goto failed; } - - struct xfrm_algo* algo = (struct xfrm_algo*)RTA_DATA(rthdr); algo->alg_key_len = 0; strncpy(algo->alg_name, alg_name, sizeof(algo->alg_name)); algo->alg_name[sizeof(algo->alg_name) - 1] = '\0'; - - rthdr = XFRM_RTA_NEXT(rthdr); } if (encap) { struct xfrm_encap_tmpl *tmpl; - rthdr->rta_type = XFRMA_ENCAP; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_encap_tmpl)); - - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) + tmpl = netlink_reserve(hdr, sizeof(request), XFRMA_ENCAP, sizeof(*tmpl)); + if (!tmpl) { goto failed; } - - tmpl = (struct xfrm_encap_tmpl*)RTA_DATA(rthdr); tmpl->encap_type = UDP_ENCAP_ESPINUDP; tmpl->encap_sport = htons(src->get_port(src)); tmpl->encap_dport = htons(dst->get_port(dst)); @@ -1430,44 +1420,24 @@ METHOD(kernel_ipsec_t, add_sa, status_t, * 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. */ - rthdr = XFRM_RTA_NEXT(rthdr); } - if (mark.value) + if (!add_mark(hdr, sizeof(request), mark)) { - struct xfrm_mark *mrk; - - rthdr->rta_type = XFRMA_MARK; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_mark)); - - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) - { - goto failed; - } - - mrk = (struct xfrm_mark*)RTA_DATA(rthdr); - mrk->v = mark.value; - mrk->m = mark.mask; - rthdr = XFRM_RTA_NEXT(rthdr); + goto failed; } if (tfc) { u_int32_t *tfcpad; - rthdr->rta_type = XFRMA_TFCPAD; - rthdr->rta_len = RTA_LENGTH(sizeof(u_int32_t)); - - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) + tfcpad = netlink_reserve(hdr, sizeof(request), XFRMA_TFCPAD, + sizeof(*tfcpad)); + if (!tfcpad) { goto failed; } - - tfcpad = (u_int32_t*)RTA_DATA(rthdr); *tfcpad = tfc; - rthdr = XFRM_RTA_NEXT(rthdr); } if (protocol != IPPROTO_COMP) @@ -1478,24 +1448,18 @@ METHOD(kernel_ipsec_t, add_sa, status_t, * XFRMA_REPLAY_ESN_VAL attribute to configure a bitmap */ struct xfrm_replay_state_esn *replay; - rthdr->rta_type = XFRMA_REPLAY_ESN_VAL; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_replay_state_esn) + - (this->replay_window + 7) / 8); - - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) + replay = netlink_reserve(hdr, sizeof(request), XFRMA_REPLAY_ESN_VAL, + sizeof(*replay) + (this->replay_window + 7) / 8); + if (!replay) { goto failed; } - - replay = (struct xfrm_replay_state_esn*)RTA_DATA(rthdr); /* bmp_len contains number uf __u32's */ replay->bmp_len = this->replay_bmp; replay->replay_window = this->replay_window; DBG2(DBG_KNL, " using replay window of %u packets", this->replay_window); - rthdr = XFRM_RTA_NEXT(rthdr); if (esn) { DBG2(DBG_KNL, " using extended sequence numbers (ESN)"); @@ -1567,22 +1531,9 @@ static void get_replay_state(private_kernel_netlink_ipsec_t *this, aevent_id->sa_id.proto = protocol; aevent_id->sa_id.family = dst->get_family(dst); - if (mark.value) + if (!add_mark(hdr, sizeof(request), mark)) { - struct xfrm_mark *mrk; - struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_aevent_id); - - rthdr->rta_type = XFRMA_MARK; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_mark)); - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) - { - return; - } - - mrk = (struct xfrm_mark*)RTA_DATA(rthdr); - mrk->v = mark.value; - mrk->m = mark.mask; + return; } if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) @@ -1668,22 +1619,9 @@ METHOD(kernel_ipsec_t, query_sa, status_t, sa_id->proto = protocol; sa_id->family = dst->get_family(dst); - if (mark.value) + if (!add_mark(hdr, sizeof(request), mark)) { - struct xfrm_mark *mrk; - struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_usersa_id); - - rthdr->rta_type = XFRMA_MARK; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_mark)); - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) - { - return FAILED; - } - - mrk = (struct xfrm_mark*)RTA_DATA(rthdr); - mrk->v = mark.value; - mrk->m = mark.mask; + return FAILED; } if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) @@ -1771,22 +1709,9 @@ METHOD(kernel_ipsec_t, del_sa, status_t, sa_id->proto = protocol; sa_id->family = dst->get_family(dst); - if (mark.value) + if (!add_mark(hdr, sizeof(request), mark)) { - struct xfrm_mark *mrk; - struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_usersa_id); - - rthdr->rta_type = XFRMA_MARK; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_mark)); - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) - { - return FAILED; - } - - mrk = (struct xfrm_mark*)RTA_DATA(rthdr); - mrk->v = mark.value; - mrk->m = mark.mask; + return FAILED; } switch (this->socket_xfrm->send_ack(this->socket_xfrm, hdr)) @@ -1818,7 +1743,6 @@ METHOD(kernel_ipsec_t, update_sa, status_t, bool old_encap, bool new_encap, mark_t mark) { netlink_buf_t request; - u_char *pos; struct nlmsghdr *hdr, *out = NULL; struct xfrm_usersa_id *sa_id; struct xfrm_usersa_info *out_sa = NULL, *sa; @@ -1853,22 +1777,9 @@ METHOD(kernel_ipsec_t, update_sa, status_t, sa_id->proto = protocol; sa_id->family = dst->get_family(dst); - if (mark.value) + if (!add_mark(hdr, sizeof(request), mark)) { - struct xfrm_mark *mrk; - struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_usersa_id); - - rthdr->rta_type = XFRMA_MARK; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_mark)); - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) - { - return FAILED; - } - - mrk = (struct xfrm_mark*)RTA_DATA(rthdr); - mrk->v = mark.value; - mrk->m = mark.mask; + return FAILED; } if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) @@ -1919,11 +1830,11 @@ METHOD(kernel_ipsec_t, update_sa, status_t, ntohl(spi), src, dst, new_src, new_dst); /* copy over the SA from out to request */ hdr = (struct nlmsghdr*)request; - memcpy(hdr, out, min(out->nlmsg_len, sizeof(request))); 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)) @@ -1937,75 +1848,60 @@ METHOD(kernel_ipsec_t, update_sa, status_t, rta = XFRM_RTA(out, struct xfrm_usersa_info); rtasize = XFRM_PAYLOAD(out, struct xfrm_usersa_info); - pos = (u_char*)XFRM_RTA(hdr, struct xfrm_usersa_info); - while(RTA_OK(rta, rtasize)) + 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 = (struct xfrm_encap_tmpl*)RTA_DATA(rta); + tmpl = RTA_DATA(rta); tmpl->encap_sport = ntohs(new_src->get_port(new_src)); tmpl->encap_dport = ntohs(new_dst->get_port(new_dst)); } - memcpy(pos, rta, rta->rta_len); - pos += RTA_ALIGN(rta->rta_len); - hdr->nlmsg_len += RTA_ALIGN(rta->rta_len); + netlink_add_attribute(hdr, rta->rta_type, + chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta)), + sizeof(request)); } rta = RTA_NEXT(rta, rtasize); } - rta = (struct rtattr*)pos; if (tmpl == NULL && new_encap) { /* add tmpl if we are enabling it */ - rta->rta_type = XFRMA_ENCAP; - rta->rta_len = RTA_LENGTH(sizeof(struct xfrm_encap_tmpl)); - - hdr->nlmsg_len += RTA_ALIGN(rta->rta_len); - if (hdr->nlmsg_len > sizeof(request)) + tmpl = netlink_reserve(hdr, sizeof(request), XFRMA_ENCAP, sizeof(*tmpl)); + if (!tmpl) { goto failed; } - - tmpl = (struct xfrm_encap_tmpl*)RTA_DATA(rta); 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)); - - rta = XFRM_RTA_NEXT(rta); } if (replay_esn) { - rta->rta_type = XFRMA_REPLAY_ESN_VAL; - rta->rta_len = RTA_LENGTH(sizeof(struct xfrm_replay_state_esn) + - this->replay_bmp); + struct xfrm_replay_state_esn *state; - hdr->nlmsg_len += RTA_ALIGN(rta->rta_len); - if (hdr->nlmsg_len > sizeof(request)) + state = netlink_reserve(hdr, sizeof(request), XFRMA_REPLAY_ESN_VAL, + sizeof(*state) + this->replay_bmp); + if (!state) { goto failed; } - memcpy(RTA_DATA(rta), replay_esn, - sizeof(struct xfrm_replay_state_esn) + this->replay_bmp); - - rta = XFRM_RTA_NEXT(rta); + memcpy(state, replay_esn, sizeof(*state) + this->replay_bmp); } else if (replay) { - rta->rta_type = XFRMA_REPLAY_VAL; - rta->rta_len = RTA_LENGTH(sizeof(struct xfrm_replay_state)); + struct xfrm_replay_state *state; - hdr->nlmsg_len += RTA_ALIGN(rta->rta_len); - if (hdr->nlmsg_len > sizeof(request)) + state = netlink_reserve(hdr, sizeof(request), XFRMA_REPLAY_VAL, + sizeof(*state)); + if (!state) { goto failed; } - memcpy(RTA_DATA(rta), replay, sizeof(struct xfrm_replay_state)); - - rta = XFRM_RTA_NEXT(rta); + memcpy(state, replay, sizeof(*state)); } else { @@ -2102,11 +1998,9 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, policy_info->lft.soft_use_expires_seconds = 0; policy_info->lft.hard_use_expires_seconds = 0; - struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_userpolicy_info); - if (mapping->type == POLICY_IPSEC) { - struct xfrm_user_tmpl *tmpl = (struct xfrm_user_tmpl*)RTA_DATA(rthdr); + struct xfrm_user_tmpl *tmpl; struct { u_int8_t proto; bool use; @@ -2116,25 +2010,29 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, { IPPROTO_AH, ipsec->cfg.ah.use }, }; ipsec_mode_t proto_mode = ipsec->cfg.mode; - - rthdr->rta_type = XFRMA_TMPL; - rthdr->rta_len = 0; /* actual length is set below */ + int count = 0; for (i = 0; i < countof(protos); i++) { - if (!protos[i].use) + if (protos[i].use) { - continue; + count++; } + } + tmpl = netlink_reserve(hdr, sizeof(request), XFRMA_TMPL, + count * sizeof(*tmpl)); + if (!tmpl) + { + this->mutex->unlock(this->mutex); + return FAILED; + } - rthdr->rta_len += RTA_LENGTH(sizeof(struct xfrm_user_tmpl)); - hdr->nlmsg_len += RTA_ALIGN(RTA_LENGTH(sizeof(struct xfrm_user_tmpl))); - if (hdr->nlmsg_len > sizeof(request)) + for (i = 0; i < countof(protos); i++) + { + if (!protos[i].use) { - this->mutex->unlock(this->mutex); - return FAILED; + continue; } - tmpl->reqid = ipsec->cfg.reqid; tmpl->id.proto = protos[i].proto; tmpl->aalgos = tmpl->ealgos = tmpl->calgos = ~0; @@ -2154,27 +2052,12 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, /* use transport mode for other SAs */ proto_mode = MODE_TRANSPORT; } - - rthdr = XFRM_RTA_NEXT(rthdr); } - if (ipsec->mark.value) + if (!add_mark(hdr, sizeof(request), ipsec->mark)) { - struct xfrm_mark *mrk; - - rthdr->rta_type = XFRMA_MARK; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_mark)); - - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) - { - this->mutex->unlock(this->mutex); - return FAILED; - } - - mrk = (struct xfrm_mark*)RTA_DATA(rthdr); - mrk->v = ipsec->mark.value; - mrk->m = ipsec->mark.mask; + this->mutex->unlock(this->mutex); + return FAILED; } this->mutex->unlock(this->mutex); @@ -2196,14 +2079,15 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, /* install a route, if: * - this is a forward policy (to just get one for each child) - * - we are in tunnel/BEET mode + * - we are in tunnel/BEET mode or install a bypass policy * - routing is not disabled via strongswan.conf */ - if (policy->direction == POLICY_FWD && - ipsec->cfg.mode != MODE_TRANSPORT && this->install_routes) + 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, @@ -2219,9 +2103,17 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, 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 (!hydra->kernel_interface->get_interface(hydra->kernel_interface, - ipsec->dst, &route->if_name)) + iface, &route->if_name)) { this->mutex->unlock(this->mutex); route_entry_destroy(route); @@ -2392,23 +2284,9 @@ METHOD(kernel_ipsec_t, query_policy, status_t, policy_id->sel = ts2selector(src_ts, dst_ts); policy_id->dir = direction; - if (mark.value) + if (!add_mark(hdr, sizeof(request), mark)) { - struct xfrm_mark *mrk; - struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_userpolicy_id); - - rthdr->rta_type = XFRMA_MARK; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_mark)); - - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) - { - return FAILED; - } - - mrk = (struct xfrm_mark*)RTA_DATA(rthdr); - mrk->v = mark.value; - mrk->m = mark.mask; + return FAILED; } if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) @@ -2564,23 +2442,9 @@ METHOD(kernel_ipsec_t, del_policy, status_t, policy_id->sel = current->sel; policy_id->dir = direction; - if (mark.value) + if (!add_mark(hdr, sizeof(request), mark)) { - struct xfrm_mark *mrk; - struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_userpolicy_id); - - rthdr->rta_type = XFRMA_MARK; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_mark)); - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) - { - this->mutex->unlock(this->mutex); - return FAILED; - } - - mrk = (struct xfrm_mark*)RTA_DATA(rthdr); - mrk->v = mark.value; - mrk->m = mark.mask; + return FAILED; } if (current->route) @@ -2734,6 +2598,7 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() INIT(this, .public = { .interface = { + .get_features = _get_features, .get_spi = _get_spi, .get_cpi = _get_cpi, .add_sa = _add_sa, @@ -2822,4 +2687,3 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() return &this->public; } - diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c index e47887859..3e0725a35 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c @@ -1757,6 +1757,10 @@ METHOD(kernel_net_t, add_ip, status_t, DBG2(DBG_KNL, "virtual IP %H installed on %s", virtual_ip, entry->iface->ifname); this->lock->unlock(this->lock); + /* 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(entry->iface->ifname)); return SUCCESS; } } diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c index 561e8529d..fd00c23af 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c @@ -292,7 +292,7 @@ void netlink_add_attribute(struct nlmsghdr *hdr, int rta_type, chunk_t data, { struct rtattr *rta; - if (NLMSG_ALIGN(hdr->nlmsg_len) + RTA_ALIGN(data.len) > buflen) + if (NLMSG_ALIGN(hdr->nlmsg_len) + RTA_LENGTH(data.len) > buflen) { DBG1(DBG_KNL, "unable to add attribute, buffer too small"); return; @@ -304,3 +304,24 @@ void netlink_add_attribute(struct nlmsghdr *hdr, int rta_type, chunk_t data, 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/libhydra/plugins/kernel_netlink/kernel_netlink_shared.h b/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.h index dfd27a21a..8be935bc3 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.h +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.h @@ -42,7 +42,8 @@ struct netlink_socket_t { * @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); + 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. @@ -67,11 +68,23 @@ netlink_socket_t *netlink_socket_create(int protocol); /** * 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 + * @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); +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/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index 71bdbbe2b..88b028447 100644 --- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -953,7 +953,8 @@ static traffic_selector_t* sadb_address2ts(struct sadb_address *address) ts = traffic_selector_create_from_subnet(host, address->sadb_address_prefixlen, address->sadb_address_proto, - host->get_port(host)); + host->get_port(host), + host->get_port(host) ?: 65535); return ts; } @@ -2654,4 +2655,3 @@ kernel_pfkey_ipsec_t *kernel_pfkey_ipsec_create() return &this->public; } - |