diff options
author | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2008-07-10 12:47:56 +0000 |
---|---|---|
committer | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2008-07-10 12:47:56 +0000 |
commit | eb841c5ef668a48782ef1154fda65cb6048f5885 (patch) | |
tree | 00dd0cb4313bf2291d94ed511fe51f0b4bc7ea7a /src/charon/kernel | |
parent | 738206039047924ae7e4762a53d121be1ca43000 (diff) | |
download | vyos-strongswan-eb841c5ef668a48782ef1154fda65cb6048f5885.tar.gz vyos-strongswan-eb841c5ef668a48782ef1154fda65cb6048f5885.zip |
- Updated to new upstream.
Diffstat (limited to 'src/charon/kernel')
-rw-r--r-- | src/charon/kernel/kernel_interface.c | 509 | ||||
-rw-r--r-- | src/charon/kernel/kernel_interface.h | 144 |
2 files changed, 471 insertions, 182 deletions
diff --git a/src/charon/kernel/kernel_interface.c b/src/charon/kernel/kernel_interface.c index b7f6a1def..f5cb40977 100644 --- a/src/charon/kernel/kernel_interface.c +++ b/src/charon/kernel/kernel_interface.c @@ -1,13 +1,6 @@ -/** - * @file kernel_interface.c - * - * @brief Implementation of kernel_interface_t. - * - */ - /* + * Copyright (C) 2006-2008 Tobias Brunner * Copyright (C) 2005-2007 Martin Willi - * Copyright (C) 2006-2007 Tobias Brunner * Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter @@ -25,6 +18,8 @@ * 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. + * + * $Id: kernel_interface.c 4104 2008-06-24 15:35:09Z tobias $ */ #include <sys/types.h> @@ -116,26 +111,32 @@ struct kernel_algorithm_t { /** * Algorithms for encryption */ -kernel_algorithm_t encryption_algs[] = { -/* {ENCR_DES_IV64, "***", 0}, */ - {ENCR_DES, "des", 64}, - {ENCR_3DES, "des3_ede", 192}, -/* {ENCR_RC5, "***", 0}, */ -/* {ENCR_IDEA, "***", 0}, */ - {ENCR_CAST, "cast128", 0}, - {ENCR_BLOWFISH, "blowfish", 0}, -/* {ENCR_3IDEA, "***", 0}, */ -/* {ENCR_DES_IV32, "***", 0}, */ - {ENCR_NULL, "cipher_null", 0}, - {ENCR_AES_CBC, "aes", 0}, -/* {ENCR_AES_CTR, "***", 0}, */ +static kernel_algorithm_t encryption_algs[] = { +/* {ENCR_DES_IV64, "***", 0}, */ + {ENCR_DES, "des", 64}, + {ENCR_3DES, "des3_ede", 192}, +/* {ENCR_RC5, "***", 0}, */ +/* {ENCR_IDEA, "***", 0}, */ + {ENCR_CAST, "cast128", 0}, + {ENCR_BLOWFISH, "blowfish", 0}, +/* {ENCR_3IDEA, "***", 0}, */ +/* {ENCR_DES_IV32, "***", 0}, */ + {ENCR_NULL, "cipher_null", 0}, + {ENCR_AES_CBC, "aes", 0}, +/* {ENCR_AES_CTR, "***", 0}, */ + {ENCR_AES_CCM_ICV8, "rfc4309(ccm(aes))", 64}, /* key_size = ICV size */ + {ENCR_AES_CCM_ICV12, "rfc4309(ccm(aes))", 96}, /* key_size = ICV size */ + {ENCR_AES_CCM_ICV16, "rfc4309(ccm(aes))", 128}, /* key_size = ICV size */ + {ENCR_AES_GCM_ICV8, "rfc4106(gcm(aes))", 64}, /* key_size = ICV size */ + {ENCR_AES_GCM_ICV12, "rfc4106(gcm(aes))", 96}, /* key_size = ICV size */ + {ENCR_AES_GCM_ICV16, "rfc4106(gcm(aes))", 128}, /* key_size = ICV size */ {END_OF_LIST, NULL, 0}, }; /** * Algorithms for integrity protection */ -kernel_algorithm_t integrity_algs[] = { +static kernel_algorithm_t integrity_algs[] = { {AUTH_HMAC_MD5_96, "md5", 128}, {AUTH_HMAC_SHA1_96, "sha1", 160}, {AUTH_HMAC_SHA2_256_128, "sha256", 256}, @@ -148,22 +149,29 @@ kernel_algorithm_t integrity_algs[] = { }; /** + * Algorithms for IPComp + */ +static kernel_algorithm_t compression_algs[] = { +/* {IPCOMP_OUI, "***", 0}, */ + {IPCOMP_DEFLATE, "deflate", 0}, + {IPCOMP_LZS, "lzs", 0}, + {IPCOMP_LZJH, "lzjh", 0}, + {END_OF_LIST, NULL, 0}, +}; + +/** * Look up a kernel algorithm name and its key size */ -char* lookup_algorithm(kernel_algorithm_t *kernel_algo, - algorithm_t *ikev2_algo, u_int *key_size) +static char* lookup_algorithm(kernel_algorithm_t *kernel_algo, + u_int16_t ikev2_algo, u_int16_t *key_size) { while (kernel_algo->ikev2_id != END_OF_LIST) { - if (ikev2_algo->algorithm == kernel_algo->ikev2_id) + if (ikev2_algo == kernel_algo->ikev2_id) { /* match, evaluate key length */ - if (ikev2_algo->key_size) - { /* variable length */ - *key_size = ikev2_algo->key_size; - } - else - { /* fixed length */ + if (key_size && *key_size == 0) + { /* update key size if not set */ *key_size = kernel_algo->key_size; } return kernel_algo->name; @@ -363,6 +371,21 @@ struct private_kernel_interface_t { * time of the last roam_job */ struct timeval last_roam; + + /** + * whether to install routes along policies + */ + bool install_routes; + + /** + * routing table to install routes + */ + int routing_table; + + /** + * priority of used routing table + */ + int routing_table_prio; }; /** @@ -527,11 +550,20 @@ static void process_expire(private_kernel_interface_t *this, struct nlmsghdr *hd struct xfrm_user_expire *expire; expire = (struct xfrm_user_expire*)NLMSG_DATA(hdr); - protocol = expire->state.id.proto == KERNEL_ESP ? PROTO_ESP : PROTO_AH; + protocol = expire->state.id.proto; + protocol = (protocol == KERNEL_ESP) ? PROTO_ESP : (protocol == KERNEL_AH) ? PROTO_AH : protocol; spi = expire->state.id.spi; reqid = expire->state.reqid; DBG2(DBG_KNL, "received a XFRM_MSG_EXPIRE"); + + if (protocol != PROTO_ESP && protocol != PROTO_AH) + { + DBG2(DBG_KNL, "ignoring XFRM_MSG_EXPIRE for SA 0x%x (reqid %d) which is " + "not a CHILD_SA", ntohl(spi), reqid); + return; + } + DBG1(DBG_KNL, "creating %s job for %N CHILD_SA 0x%x (reqid %d)", expire->hard ? "delete" : "rekey", protocol_id_names, protocol, ntohl(spi), reqid); @@ -1425,11 +1457,10 @@ static status_t manage_srcroute(private_kernel_interface_t *this, int nlmsg_type struct rtmsg *msg; chunk_t chunk; -#if IPSEC_ROUTING_TABLE == 0 /* 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 (route->prefixlen == 0) + if (this->routing_table == 0 && route->prefixlen == 0) { route_entry_t half; status_t status; @@ -1446,7 +1477,6 @@ static status_t manage_srcroute(private_kernel_interface_t *this, int nlmsg_type status = manage_srcroute(this, nlmsg_type, flags, &half); return status; } -#endif memset(&request, 0, sizeof(request)); @@ -1458,7 +1488,7 @@ static status_t manage_srcroute(private_kernel_interface_t *this, int nlmsg_type msg = (struct rtmsg*)NLMSG_DATA(hdr); msg->rtm_family = route->src_ip->get_family(route->src_ip); msg->rtm_dst_len = route->prefixlen; - msg->rtm_table = IPSEC_ROUTING_TABLE; + msg->rtm_table = this->routing_table; msg->rtm_protocol = RTPROT_STATIC; msg->rtm_type = RTN_UNICAST; msg->rtm_scope = RT_SCOPE_UNIVERSE; @@ -1620,7 +1650,7 @@ static host_t *get_route(private_kernel_interface_t *this, host_t *dest, * - is the default route or * - its destination net contains our destination */ - if (msg->rtm_table != IPSEC_ROUTING_TABLE + if ((this->routing_table == 0 ||msg->rtm_table != this->routing_table) && msg->rtm_dst_len > best && (msg->rtm_dst_len == 0 || /* default route */ (rta_dst.ptr && addr_in_subnet(chunk, rta_dst, msg->rtm_dst_len)))) @@ -1639,6 +1669,13 @@ static host_t *get_route(private_kernel_interface_t *this, host_t *dest, { DESTROY_IF(src); src = host_create_from_chunk(msg->rtm_family, rta_src, 0); + if (get_vip_refcount(this, src)) + { /* skip source address if it is installed by us */ + DESTROY_IF(src); + src = NULL; + current = NLMSG_NEXT(current, len); + continue; + } } else { @@ -1823,12 +1860,11 @@ static status_t del_ip(private_kernel_interface_t *this, host_t *virtual_ip) } /** - * Implementation of kernel_interface_t.get_spi. + * Get an SPI for a specific protocol from the kernel. */ -static status_t get_spi(private_kernel_interface_t *this, - host_t *src, host_t *dst, - protocol_id_t protocol, u_int32_t reqid, - u_int32_t *spi) +static status_t get_spi_internal(private_kernel_interface_t *this, + host_t *src, host_t *dst, u_int8_t proto, u_int32_t min, u_int32_t max, + u_int32_t reqid, u_int32_t *spi) { unsigned char request[BUFFER_SIZE]; struct nlmsghdr *hdr, *out; @@ -1838,8 +1874,6 @@ static status_t get_spi(private_kernel_interface_t *this, memset(&request, 0, sizeof(request)); - DBG2(DBG_KNL, "getting SPI for reqid %d", reqid); - hdr = (struct nlmsghdr*)request; hdr->nlmsg_flags = NLM_F_REQUEST; hdr->nlmsg_type = XFRM_MSG_ALLOCSPI; @@ -1848,12 +1882,12 @@ static status_t get_spi(private_kernel_interface_t *this, userspi = (struct xfrm_userspi_info*)NLMSG_DATA(hdr); host2xfrm(src, &userspi->info.saddr); host2xfrm(dst, &userspi->info.id.daddr); - userspi->info.id.proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; + userspi->info.id.proto = proto; userspi->info.mode = TRUE; /* tunnel mode */ userspi->info.reqid = reqid; userspi->info.family = src->get_family(src); - userspi->min = 0xc0000000; - userspi->max = 0xcFFFFFFF; + userspi->min = min; + userspi->max = max; if (netlink_send(this, this->socket_xfrm, hdr, &out, &len) == SUCCESS) { @@ -1889,13 +1923,57 @@ static status_t get_spi(private_kernel_interface_t *this, if (received_spi == 0) { + return FAILED; + } + + *spi = received_spi; + return SUCCESS; +} + +/** + * Implementation of kernel_interface_t.get_spi. + */ +static status_t get_spi(private_kernel_interface_t *this, + host_t *src, host_t *dst, + protocol_id_t protocol, u_int32_t reqid, + u_int32_t *spi) +{ + DBG2(DBG_KNL, "getting SPI for reqid %d", reqid); + + if (get_spi_internal(this, src, dst, + (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH, + 0xc0000000, 0xcFFFFFFF, reqid, spi) != SUCCESS) + { DBG1(DBG_KNL, "unable to get SPI for reqid %d", reqid); return FAILED; } - DBG2(DBG_KNL, "got SPI 0x%x for reqid %d", received_spi, reqid); + DBG2(DBG_KNL, "got SPI 0x%x for reqid %d", *spi, reqid); + + return SUCCESS; +} + +/** + * Implementation of kernel_interface_t.get_cpi. + */ +static status_t get_cpi(private_kernel_interface_t *this, + host_t *src, host_t *dst, + u_int32_t reqid, u_int16_t *cpi) +{ + u_int32_t received_spi = 0; + DBG2(DBG_KNL, "getting CPI for reqid %d", reqid); + + if (get_spi_internal(this, src, dst, + IPPROTO_COMP, 0x100, 0xEFFF, reqid, &received_spi) != SUCCESS) + { + DBG1(DBG_KNL, "unable to get CPI for reqid %d", reqid); + return FAILED; + } + + *cpi = htons((u_int16_t)ntohl(received_spi)); + + DBG2(DBG_KNL, "got CPI 0x%x for reqid %d", *cpi, reqid); - *spi = received_spi; return SUCCESS; } @@ -1906,19 +1984,21 @@ static status_t add_sa(private_kernel_interface_t *this, host_t *src, host_t *dst, u_int32_t spi, protocol_id_t protocol, u_int32_t reqid, u_int64_t expire_soft, u_int64_t expire_hard, - algorithm_t *enc_alg, algorithm_t *int_alg, - prf_plus_t *prf_plus, mode_t mode, bool encap, + u_int16_t enc_alg, u_int16_t enc_size, + u_int16_t int_alg, u_int16_t int_size, + prf_plus_t *prf_plus, mode_t mode, + u_int16_t ipcomp, bool encap, bool replace) { unsigned char request[BUFFER_SIZE]; char *alg_name; - u_int key_size; + u_int16_t add_keymat = 32; /* additional 4 octets KEYMAT required for AES-GCM as of RFC4106 8.1. */ struct nlmsghdr *hdr; struct xfrm_usersa_info *sa; memset(&request, 0, sizeof(request)); - DBG2(DBG_KNL, "adding SAD entry with SPI 0x%x", spi); + DBG2(DBG_KNL, "adding SAD entry with SPI 0x%x and reqid %d", spi, reqid); hdr = (struct nlmsghdr*)request; hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; @@ -1929,10 +2009,10 @@ static status_t add_sa(private_kernel_interface_t *this, host2xfrm(src, &sa->saddr); host2xfrm(dst, &sa->id.daddr); sa->id.spi = spi; - sa->id.proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; + sa->id.proto = (protocol == PROTO_ESP) ? KERNEL_ESP : (protocol == PROTO_AH) ? KERNEL_AH : protocol; sa->family = src->get_family(src); sa->mode = mode; - sa->replay_window = 32; + sa->replay_window = (protocol == IPPROTO_COMP) ? 0 : 32; sa->reqid = reqid; /* we currently do not expire SAs by volume/packet count */ sa->lft.soft_byte_limit = XFRM_INF; @@ -1947,20 +2027,96 @@ static status_t add_sa(private_kernel_interface_t *this, struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_usersa_info); - if (enc_alg->algorithm != ENCR_UNDEFINED) + switch (enc_alg) + { + case ENCR_UNDEFINED: + /* no encryption */ + break; + case ENCR_AES_CCM_ICV8: + case ENCR_AES_CCM_ICV12: + case ENCR_AES_CCM_ICV16: + /* AES-CCM needs only 3 additional octets KEYMAT as of RFC 4309 7.1. */ + add_keymat = 24; + /* fall-through */ + case ENCR_AES_GCM_ICV8: + case ENCR_AES_GCM_ICV12: + case ENCR_AES_GCM_ICV16: + { + u_int16_t icv_size = 0; + rthdr->rta_type = XFRMA_ALG_AEAD; + alg_name = lookup_algorithm(encryption_algs, enc_alg, &icv_size); + if (alg_name == NULL) + { + 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_size); + + /* additional KEYMAT required */ + enc_size += add_keymat; + + rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo_aead) + enc_size / 8); + hdr->nlmsg_len += rthdr->rta_len; + if (hdr->nlmsg_len > sizeof(request)) + { + return FAILED; + } + + struct xfrm_algo_aead* algo = (struct xfrm_algo_aead*)RTA_DATA(rthdr); + algo->alg_key_len = enc_size; + algo->alg_icv_len = icv_size; + strcpy(algo->alg_name, alg_name); + prf_plus->get_bytes(prf_plus, enc_size / 8, algo->alg_key); + + rthdr = XFRM_RTA_NEXT(rthdr); + break; + } + default: + { + rthdr->rta_type = XFRMA_ALG_CRYPT; + alg_name = lookup_algorithm(encryption_algs, enc_alg, &enc_size); + if (alg_name == NULL) + { + 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_size); + + rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo) + enc_size / 8); + hdr->nlmsg_len += rthdr->rta_len; + if (hdr->nlmsg_len > sizeof(request)) + { + return FAILED; + } + + struct xfrm_algo* algo = (struct xfrm_algo*)RTA_DATA(rthdr); + algo->alg_key_len = enc_size; + strcpy(algo->alg_name, alg_name); + prf_plus->get_bytes(prf_plus, enc_size / 8, algo->alg_key); + + rthdr = XFRM_RTA_NEXT(rthdr); + break; + } + } + + if (int_alg != AUTH_UNDEFINED) { - rthdr->rta_type = XFRMA_ALG_CRYPT; - alg_name = lookup_algorithm(encryption_algs, enc_alg, &key_size); + rthdr->rta_type = XFRMA_ALG_AUTH; + alg_name = lookup_algorithm(integrity_algs, int_alg, &int_size); if (alg_name == NULL) { - DBG1(DBG_KNL, "algorithm %N not supported by kernel!", - encryption_algorithm_names, enc_alg->algorithm); + DBG1(DBG_KNL, "algorithm %N not supported by kernel!", + integrity_algorithm_names, int_alg); return FAILED; } - DBG2(DBG_KNL, " using encryption algorithm %N with key size %d", - encryption_algorithm_names, enc_alg->algorithm, key_size); + DBG2(DBG_KNL, " using integrity algorithm %N with key size %d", + integrity_algorithm_names, int_alg, int_size); - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo) + key_size); + rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo) + int_size / 8); hdr->nlmsg_len += rthdr->rta_len; if (hdr->nlmsg_len > sizeof(request)) { @@ -1968,27 +2124,27 @@ static status_t add_sa(private_kernel_interface_t *this, } struct xfrm_algo* algo = (struct xfrm_algo*)RTA_DATA(rthdr); - algo->alg_key_len = key_size; + algo->alg_key_len = int_size; strcpy(algo->alg_name, alg_name); - prf_plus->get_bytes(prf_plus, key_size / 8, algo->alg_key); + prf_plus->get_bytes(prf_plus, int_size / 8, algo->alg_key); rthdr = XFRM_RTA_NEXT(rthdr); } - if (int_alg->algorithm != AUTH_UNDEFINED) + if (ipcomp != IPCOMP_NONE) { - rthdr->rta_type = XFRMA_ALG_AUTH; - alg_name = lookup_algorithm(integrity_algs, int_alg, &key_size); + rthdr->rta_type = XFRMA_ALG_COMP; + alg_name = lookup_algorithm(compression_algs, ipcomp, NULL); if (alg_name == NULL) { DBG1(DBG_KNL, "algorithm %N not supported by kernel!", - integrity_algorithm_names, int_alg->algorithm); + ipcomp_transform_names, ipcomp); return FAILED; } - DBG2(DBG_KNL, " using integrity algorithm %N with key size %d", - integrity_algorithm_names, int_alg->algorithm, key_size); + DBG2(DBG_KNL, " using compression algorithm %N", + ipcomp_transform_names, ipcomp); - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo) + key_size); + rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo)); hdr->nlmsg_len += rthdr->rta_len; if (hdr->nlmsg_len > sizeof(request)) { @@ -1996,15 +2152,12 @@ static status_t add_sa(private_kernel_interface_t *this, } struct xfrm_algo* algo = (struct xfrm_algo*)RTA_DATA(rthdr); - algo->alg_key_len = key_size; + algo->alg_key_len = 0; strcpy(algo->alg_name, alg_name); - prf_plus->get_bytes(prf_plus, key_size / 8, algo->alg_key); rthdr = XFRM_RTA_NEXT(rthdr); } - /* TODO: add IPComp here */ - if (encap) { rthdr->rta_type = XFRMA_ENCAP; @@ -2042,6 +2195,91 @@ static status_t add_sa(private_kernel_interface_t *this, } /** + * Get the replay state (i.e. sequence numbers) of an SA. + */ +static status_t get_replay_state(private_kernel_interface_t *this, + u_int32_t spi, protocol_id_t protocol, host_t *dst, + struct xfrm_replay_state *replay) +{ + unsigned char request[BUFFER_SIZE]; + 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 0x%x", spi); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST; + hdr->nlmsg_type = XFRM_MSG_GETAE; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_aevent_id)); + + aevent_id = (struct xfrm_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 == PROTO_ESP) ? KERNEL_ESP : (protocol == PROTO_AH) ? KERNEL_AH : protocol; + aevent_id->sa_id.family = dst->get_family(dst); + + if (netlink_send(this, 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 == NULL) + { + DBG1(DBG_KNL, "unable to query replay state from SAD entry with SPI 0x%x", spi); + free(out); + return FAILED; + } + + 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_REPLAY_VAL) + { + memcpy(replay, RTA_DATA(rta), rta->rta_len); + free(out); + return SUCCESS; + } + rta = RTA_NEXT(rta, rtasize); + } + + DBG1(DBG_KNL, "unable to query replay state from SAD entry with SPI 0x%x", spi); + free(out); + return FAILED; +} + +/** * Implementation of kernel_interface_t.update_sa. */ static status_t update_sa(private_kernel_interface_t *this, @@ -2057,6 +2295,8 @@ static status_t update_sa(private_kernel_interface_t *this, struct rtattr *rta; size_t rtasize; struct xfrm_encap_tmpl* tmpl = NULL; + bool got_replay_state; + struct xfrm_replay_state replay; memset(&request, 0, sizeof(request)); @@ -2071,7 +2311,7 @@ static status_t update_sa(private_kernel_interface_t *this, sa_id = (struct xfrm_usersa_id*)NLMSG_DATA(hdr); host2xfrm(dst, &sa_id->daddr); sa_id->spi = spi; - sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; + sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : (protocol == PROTO_AH) ? KERNEL_AH : protocol; sa_id->family = dst->get_family(dst); if (netlink_send(this, this->socket_xfrm, hdr, &out, &len) == SUCCESS) @@ -2102,14 +2342,25 @@ static status_t update_sa(private_kernel_interface_t *this, break; } } - if (out_sa == NULL || - this->public.del_sa(&this->public, dst, spi, protocol) != SUCCESS) + if (out_sa == NULL) { DBG1(DBG_KNL, "unable to update SAD entry with SPI 0x%x", spi); free(out); return FAILED; } + /* try to get the replay state */ + got_replay_state = (get_replay_state( + this, spi, protocol, dst, &replay) == SUCCESS); + + /* delete the old SA */ + if (this->public.del_sa(&this->public, dst, spi, protocol) != SUCCESS) + { + DBG1(DBG_KNL, "unable to delete old SAD entry with SPI 0x%x", spi); + free(out); + return FAILED; + } + DBG2(DBG_KNL, "updating SAD entry with SPI 0x%x from %#H..%#H to %#H..%#H", spi, src, dst, new_src, new_dst); @@ -2146,22 +2397,46 @@ static status_t update_sa(private_kernel_interface_t *this, tmpl->encap_dport = ntohs(new_dst->get_port(new_dst)); } memcpy(pos, rta, rta->rta_len); - pos += rta->rta_len; - hdr->nlmsg_len += rta->rta_len; + pos += RTA_ALIGN(rta->rta_len); + hdr->nlmsg_len += RTA_ALIGN(rta->rta_len); } rta = RTA_NEXT(rta, rtasize); } + + rta = (struct rtattr*)pos; if (tmpl == NULL && encap) { /* add tmpl if we are enabling it */ - rta = (struct rtattr*)pos; rta->rta_type = XFRMA_ENCAP; rta->rta_len = RTA_LENGTH(sizeof(struct xfrm_encap_tmpl)); + hdr->nlmsg_len += rta->rta_len; + if (hdr->nlmsg_len > sizeof(request)) + { + return 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 (got_replay_state) + { /* copy the replay data if available */ + rta->rta_type = XFRMA_REPLAY_VAL; + rta->rta_len = RTA_LENGTH(sizeof(struct xfrm_replay_state)); + + hdr->nlmsg_len += rta->rta_len; + if (hdr->nlmsg_len > sizeof(request)) + { + return FAILED; + } + memcpy(RTA_DATA(rta), &replay, sizeof(replay)); + + rta = XFRM_RTA_NEXT(rta); } if (netlink_send_ack(this, this->socket_xfrm, hdr) != SUCCESS) @@ -2199,7 +2474,7 @@ static status_t query_sa(private_kernel_interface_t *this, host_t *dst, sa_id = (struct xfrm_usersa_id*)NLMSG_DATA(hdr); host2xfrm(dst, &sa_id->daddr); sa_id->spi = spi; - sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; + sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : (protocol == PROTO_AH) ? KERNEL_AH : protocol; sa_id->family = dst->get_family(dst); if (netlink_send(this, this->socket_xfrm, hdr, &out, &len) == SUCCESS) @@ -2265,7 +2540,7 @@ static status_t del_sa(private_kernel_interface_t *this, host_t *dst, sa_id = (struct xfrm_usersa_id*)NLMSG_DATA(hdr); host2xfrm(dst, &sa_id->daddr); sa_id->spi = spi; - sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; + sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : (protocol == PROTO_AH) ? KERNEL_AH : protocol; sa_id->family = dst->get_family(dst); if (netlink_send_ack(this, this->socket_xfrm, hdr) != SUCCESS) @@ -2285,7 +2560,8 @@ static status_t add_policy(private_kernel_interface_t *this, traffic_selector_t *src_ts, traffic_selector_t *dst_ts, policy_dir_t direction, protocol_id_t protocol, - u_int32_t reqid, bool high_prio, mode_t mode) + u_int32_t reqid, bool high_prio, mode_t mode, + u_int16_t ipcomp) { iterator_t *iterator; policy_entry_t *current, *policy; @@ -2310,7 +2586,7 @@ static status_t add_policy(private_kernel_interface_t *this, { /* use existing policy */ current->refcount++; - DBG2(DBG_KNL, "policy %R===%R already exists, increasing ", + DBG2(DBG_KNL, "policy %R===%R already exists, increasing " "refcount", src_ts, dst_ts); free(policy); policy = current; @@ -2357,10 +2633,8 @@ static status_t add_policy(private_kernel_interface_t *this, struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_userpolicy_info); rthdr->rta_type = XFRMA_TMPL; - - rthdr->rta_len = sizeof(struct xfrm_user_tmpl); - rthdr->rta_len = RTA_LENGTH(rthdr->rta_len); - + rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_user_tmpl)); + hdr->nlmsg_len += rthdr->rta_len; if (hdr->nlmsg_len > sizeof(request)) { @@ -2368,6 +2642,30 @@ static status_t add_policy(private_kernel_interface_t *this, } struct xfrm_user_tmpl *tmpl = (struct xfrm_user_tmpl*)RTA_DATA(rthdr); + + if (ipcomp != IPCOMP_NONE) + { + tmpl->reqid = reqid; + tmpl->id.proto = IPPROTO_COMP; + tmpl->aalgos = tmpl->ealgos = tmpl->calgos = ~0; + tmpl->mode = mode; + tmpl->optional = direction != POLICY_OUT; + tmpl->family = src->get_family(src); + + host2xfrm(src, &tmpl->saddr); + host2xfrm(dst, &tmpl->id.daddr); + + /* add an additional xfrm_user_tmpl */ + rthdr->rta_len += RTA_LENGTH(sizeof(struct xfrm_user_tmpl)); + hdr->nlmsg_len += RTA_LENGTH(sizeof(struct xfrm_user_tmpl)); + if (hdr->nlmsg_len > sizeof(request)) + { + return FAILED; + } + + tmpl++; + } + tmpl->reqid = reqid; tmpl->id.proto = (protocol == PROTO_AH) ? KERNEL_AH : KERNEL_ESP; tmpl->aalgos = tmpl->ealgos = tmpl->calgos = ~0; @@ -2388,9 +2686,11 @@ static status_t add_policy(private_kernel_interface_t *this, * - this is a forward policy (to just get one for each child) * - we are in tunnel mode * - we are not using IPv6 (does not work correctly yet!) + * - routing is not disabled via strongswan.conf */ if (policy->route == NULL && direction == POLICY_FWD && - mode != MODE_TRANSPORT && src->get_family(src) != AF_INET6) + mode != MODE_TRANSPORT && src->get_family(src) != AF_INET6 && + this->install_routes) { policy->route = malloc_thing(route_entry_t); if (get_address_by_ts(this, dst_ts, &policy->route->src_ip) == SUCCESS) @@ -2575,7 +2875,11 @@ static status_t del_policy(private_kernel_interface_t *this, */ static void destroy(private_kernel_interface_t *this) { - manage_rule(this, RTM_DELRULE, IPSEC_ROUTING_TABLE, IPSEC_ROUTING_TABLE_PRIO); + if (this->routing_table) + { + manage_rule(this, RTM_DELRULE, this->routing_table, + this->routing_table_prio); + } this->job->cancel(this->job); close(this->socket_xfrm_events); @@ -2597,11 +2901,12 @@ kernel_interface_t *kernel_interface_create() /* public functions */ this->public.get_spi = (status_t(*)(kernel_interface_t*,host_t*,host_t*,protocol_id_t,u_int32_t,u_int32_t*))get_spi; - this->public.add_sa = (status_t(*)(kernel_interface_t *,host_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t,u_int64_t,u_int64_t,algorithm_t*,algorithm_t*,prf_plus_t*,mode_t,bool,bool))add_sa; + this->public.get_cpi = (status_t(*)(kernel_interface_t*,host_t*,host_t*,u_int32_t,u_int16_t*))get_cpi; + this->public.add_sa = (status_t(*)(kernel_interface_t *,host_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t,u_int64_t,u_int64_t,u_int16_t,u_int16_t,u_int16_t,u_int16_t,prf_plus_t*,mode_t,u_int16_t,bool,bool))add_sa; this->public.update_sa = (status_t(*)(kernel_interface_t*,u_int32_t,protocol_id_t,host_t*,host_t*,host_t*,host_t*,bool))update_sa; this->public.query_sa = (status_t(*)(kernel_interface_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t*))query_sa; this->public.del_sa = (status_t(*)(kernel_interface_t*,host_t*,u_int32_t,protocol_id_t))del_sa; - this->public.add_policy = (status_t(*)(kernel_interface_t*,host_t*,host_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,protocol_id_t,u_int32_t,bool,mode_t))add_policy; + this->public.add_policy = (status_t(*)(kernel_interface_t*,host_t*,host_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,protocol_id_t,u_int32_t,bool,mode_t,u_int16_t))add_policy; this->public.query_policy = (status_t(*)(kernel_interface_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,u_int32_t*))query_policy; this->public.del_policy = (status_t(*)(kernel_interface_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t))del_policy; this->public.get_interface = (char*(*)(kernel_interface_t*,host_t*))get_interface_name; @@ -2620,7 +2925,12 @@ kernel_interface_t *kernel_interface_create() pthread_mutex_init(&this->nl_mutex, NULL); pthread_cond_init(&this->cond, NULL); timerclear(&this->last_roam); - + this->install_routes = lib->settings->get_bool(lib->settings, + "charon.install_routes", TRUE); + this->routing_table = lib->settings->get_int(lib->settings, + "charon.routing_table", IPSEC_ROUTING_TABLE); + this->routing_table_prio = lib->settings->get_int(lib->settings, + "charon.routing_table_prio", IPSEC_ROUTING_TABLE_PRIO); memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; @@ -2682,10 +2992,13 @@ kernel_interface_t *kernel_interface_create() charon->kill(charon, "unable to get interface list"); } - if (manage_rule(this, RTM_NEWRULE, IPSEC_ROUTING_TABLE, - IPSEC_ROUTING_TABLE_PRIO) != SUCCESS) + if (this->routing_table) { - DBG1(DBG_KNL, "unable to create routing table rule"); + if (manage_rule(this, RTM_NEWRULE, this->routing_table, + this->routing_table_prio) != SUCCESS) + { + DBG1(DBG_KNL, "unable to create routing table rule"); + } } return &this->public; diff --git a/src/charon/kernel/kernel_interface.h b/src/charon/kernel/kernel_interface.h index 256c20797..49928c74b 100644 --- a/src/charon/kernel/kernel_interface.h +++ b/src/charon/kernel/kernel_interface.h @@ -1,12 +1,6 @@ -/** - * @file kernel_interface.h - * - * @brief Interface of kernel_interface_t. - * - */ - /* - * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2006-2008 Tobias Brunner + * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -20,6 +14,13 @@ * 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. + * + * $Id: kernel_interface.h 3920 2008-05-08 16:19:11Z tobias $ + */ + +/** + * @defgroup kernel_interface kernel_interface + * @{ @ingroup kernel */ #ifndef KERNEL_INTERFACE_H_ @@ -37,8 +38,6 @@ typedef struct kernel_interface_t kernel_interface_t; * Direction of a policy. These are equal to those * defined in xfrm.h, but we want to stay implementation * neutral here. - * - * @ingroup kernel */ enum policy_dir_t { /** Policy for inbound traffic */ @@ -50,7 +49,7 @@ enum policy_dir_t { }; /** - * @brief Interface to the kernel. + * Interface to the kernel. * * The kernel interface handles the communication with the kernel * for SA and policy management. It allows setup of these, and provides @@ -59,36 +58,40 @@ enum policy_dir_t { * 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. - * - * @b Constructors: - * - kernel_interface_create() - * - * @ingroup kernel */ struct kernel_interface_t { /** - * @brief Get a SPI from the kernel. + * Get a SPI from the kernel. * * @warning get_spi() implicitely creates an SA with * the allocated SPI, therefore the replace flag * in add_sa() must be set when installing this SA. * - * @param this calling object * @param src source address of SA * @param dst destination address of SA * @param protocol protocol for SA (ESP/AH) * @param reqid unique ID for this SA - * @param[out] spi allocated spi - * @return - * - SUCCESS - * - FAILED if kernel comm failed + * @param spi allocated spi + * @return SUCCESS if operation completed */ status_t (*get_spi)(kernel_interface_t *this, host_t *src, host_t *dst, protocol_id_t protocol, u_int32_t reqid, u_int32_t *spi); /** - * @brief Add an SA to the SAD. + * Get a Compression Parameter Index (CPI) from the kernel. + * + * @param src source address of SA + * @param dst destination address of SA + * @param reqid unique ID for the corresponding 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_int32_t reqid, u_int16_t *cpi); + + /** + * Add an SA to the SAD. * * add_sa() may update an already allocated * SPI (via get_spi). In this case, the replace @@ -98,7 +101,6 @@ struct kernel_interface_t { * gets the keys itself from the PRF, as we don't know * his algorithms and key sizes. * - * @param this calling object * @param src source address for this SA * @param dst destination address for this SA * @param spi SPI allocated by us or remote peer @@ -107,32 +109,34 @@ struct kernel_interface_t { * @param expire_soft lifetime in seconds before rekeying * @param expire_hard lieftime in seconds before delete * @param enc_alg Algorithm to use for encryption (ESP only) + * @param enc_size key length of encryption algorithm, if dynamic * @param int_alg Algorithm to use for integrity protection + * @param int_size key length of integrity algorithm, if dynamic * @param prf_plus PRF to derive keys from * @param mode mode of the SA (tunnel, transport) + * @param ipcomp IPComp transform to use * @param encap enable UDP encapsulation for NAT traversal * @param replace Should an already installed SA be updated? - * @return - * - SUCCESS - * - FAILED if kernel comm failed + * @return SUCCESS if operation completed */ status_t (*add_sa) (kernel_interface_t *this, host_t *src, host_t *dst, u_int32_t spi, protocol_id_t protocol, u_int32_t reqid, u_int64_t expire_soft, u_int64_t expire_hard, - algorithm_t *enc_alg, algorithm_t *int_alg, - prf_plus_t *prf_plus, mode_t mode, bool encap, + u_int16_t enc_alg, u_int16_t enc_size, + u_int16_t int_alg, u_int16_t int_size, + prf_plus_t *prf_plus, mode_t mode, + u_int16_t ipcomp, bool encap, bool update); /** - * @brief Update the hosts on an installed SA. + * 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 this calling object * @param spi SPI of the SA * @param protocol protocol for this SA (ESP/AH) * @param src current source address @@ -140,9 +144,7 @@ struct kernel_interface_t { * @param new_src new source address * @param new_dst new destination address * @param encap use UDP encapsulation - * @return - * - SUCCESS - * - FAILED if kernel comm failed + * @return SUCCESS if operation completed */ status_t (*update_sa)(kernel_interface_t *this, u_int32_t spi, protocol_id_t protocol, @@ -150,44 +152,37 @@ struct kernel_interface_t { host_t *new_src, host_t *new_dst, bool encap); /** - * @brief Query the use time of an SA. + * Query the use time of an SA. * * The use time of an SA is not the time of the last usage, but * the time of the first usage of the SA. * - * @param this calling object * @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[out] use_time the time of this SA's last use - * @return - * - SUCCESS - * - FAILED if kernel comm failed + * @param use_time pointer receives the time of this SA's last use + * @return SUCCESS if operation completed */ status_t (*query_sa) (kernel_interface_t *this, host_t *dst, u_int32_t spi, protocol_id_t protocol, u_int32_t *use_time); /** - * @brief Delete a previusly installed SA from the SAD. + * Delete a previusly installed SA from the SAD. * - * @param this calling object * @param dst destination address for this SA * @param spi SPI allocated by us or remote peer * @param protocol protocol for this SA (ESP/AH) - * @return - * - SUCCESS - * - FAILED if kernel comm failed + * @return SUCCESS if operation completed */ status_t (*del_sa) (kernel_interface_t *this, host_t *dst, u_int32_t spi, protocol_id_t protocol); /** - * @brief Add a policy to the SPD. + * Add a policy to the SPD. * * A policy is always associated to an SA. Traffic which matches a * policy is handled by the SA with the same reqid. * - * @param this calling object * @param src source address of SA * @param dst dest address of SA * @param src_ts traffic selector to match traffic source @@ -197,31 +192,28 @@ struct kernel_interface_t { * @param reqid uniqe ID of an SA to use to enforce policy * @param high_prio if TRUE, uses a higher priority than any with FALSE * @param mode mode of SA (tunnel, transport) - * @return - * - SUCCESS - * - FAILED if kernel comm failed + * @param ipcomp the IPComp transform used + * @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, protocol_id_t protocol, - u_int32_t reqid, bool high_prio, mode_t mode); + u_int32_t reqid, bool high_prio, mode_t mode, + u_int16_t ipcomp); /** - * @brief Query the use time of a policy. + * 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 this calling object * @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, POLICY_OUT, POLICY_FWD * @param[out] use_time the time of this SA's last use - * @return - * - SUCCESS - * - FAILED if kernel comm failed + * @return SUCCESS if operation completed */ status_t (*query_policy) (kernel_interface_t *this, traffic_selector_t *src_ts, @@ -229,20 +221,17 @@ struct kernel_interface_t { policy_dir_t direction, u_int32_t *use_time); /** - * @brief Remove a policy from the SPD. + * Remove a policy from the SPD. * * The kernel interface implements reference counting for policies. * If the same policy is installed multiple times (in the case of rekeying), * the reference counter is increased. del_policy() decreases the ref counter * and removes the policy only when no more references are available. * - * @param this calling object * @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, POLICY_OUT, POLICY_FWD - * @return - * - SUCCESS - * - FAILED if kernel comm failed + * @return SUCCESS if operation completed */ status_t (*del_policy) (kernel_interface_t *this, traffic_selector_t *src_ts, @@ -250,82 +239,69 @@ struct kernel_interface_t { policy_dir_t direction); /** - * @brief Get our outgoing source address for a destination. + * 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. * - * @param this calling object * @param dest target destination address * @return outgoing source address, NULL if unreachable */ host_t* (*get_source_addr)(kernel_interface_t *this, host_t *dest); /** - * @brief Get the interface name of a local address. + * Get the interface name of a local address. * - * @param this calling object * @param host address to get interface name from * @return allocated interface name, or NULL if not found */ char* (*get_interface) (kernel_interface_t *this, host_t *host); /** - * @brief Creates an iterator over all local addresses. + * Creates an iterator over all local addresses. * * This function blocks an internal cached address list until the * iterator gets destroyed. * These hosts are read-only, do not modify or free. * - * @param this calling object * @return iterator over host_t's */ iterator_t *(*create_address_iterator) (kernel_interface_t *this); /** - * @brief Add a virtual IP to an interface. + * 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(). * The virtual IP is attached to the interface where the iface_ip is found. * - * @param this calling object * @param virtual_ip virtual ip address to assign * @param iface_ip IP of an interface to attach virtual IP - * @return - * - SUCCESS - * - FAILED if kernel comm failed + * @return SUCCESS if operation completed */ status_t (*add_ip) (kernel_interface_t *this, host_t *virtual_ip, host_t *iface_ip); /** - * @brief Remove a virtual IP from an interface. + * Remove a virtual IP from an interface. * * The kernel interface uses refcounting, see add_ip(). * - * @param this calling object * @param virtual_ip virtual ip address to assign - * @return - * - SUCCESS - * - FAILED if kernel comm failed + * @return SUCCESS if operation completed */ status_t (*del_ip) (kernel_interface_t *this, host_t *virtual_ip); /** - * @brief Destroys a kernel_interface object. - * - * @param kernel_interface_t calling object + * Destroys a kernel_interface object. */ void (*destroy) (kernel_interface_t *kernel_interface); }; /** - * @brief Creates an object of type kernel_interface_t. - * - * @ingroup kernel + * Creates an object of type kernel_interface_t. */ kernel_interface_t *kernel_interface_create(void); -#endif /*KERNEL_INTERFACE_H_*/ +#endif /*KERNEL_INTERFACE_H_ @} */ |