diff options
author | Martin Willi <martin@strongswan.org> | 2006-07-12 11:42:36 +0000 |
---|---|---|
committer | Martin Willi <martin@strongswan.org> | 2006-07-12 11:42:36 +0000 |
commit | aeeb4f4f977e151e0e57f71da7c19249e36fb734 (patch) | |
tree | 0b1516c49879514c25e111b2280c0d853e129388 /src/charon/threads/kernel_interface.c | |
parent | 269f7f448bb638fbdb5b42231565fc56d7f0819e (diff) | |
download | strongswan-aeeb4f4f977e151e0e57f71da7c19249e36fb734.tar.bz2 strongswan-aeeb4f4f977e151e0e57f71da7c19249e36fb734.tar.xz |
added policy cache to kernel interface
allows refcounting of multiple installed policies
finally brings us stable simultaneous rekeying
Diffstat (limited to 'src/charon/threads/kernel_interface.c')
-rw-r--r-- | src/charon/threads/kernel_interface.c | 785 |
1 files changed, 447 insertions, 338 deletions
diff --git a/src/charon/threads/kernel_interface.c b/src/charon/threads/kernel_interface.c index 0f4a4735a..c4966f4d3 100644 --- a/src/charon/threads/kernel_interface.c +++ b/src/charon/threads/kernel_interface.c @@ -43,20 +43,29 @@ #include <queues/jobs/delete_child_sa_job.h> #include <queues/jobs/rekey_child_sa_job.h> - +/** kernel level protocol identifiers */ #define KERNEL_ESP 50 #define KERNEL_AH 51 +/** default priority of installed policies */ #define SPD_PRIORITY 1024 #define BUFFER_SIZE 1024 -/* returns a pointer to the first rtattr following the nlmsghdr *nlh and the 'usual' netlink data x like 'struct xfrm_usersa_info' */ +/** + * returns a pointer to the first rtattr following the nlmsghdr *nlh and the + * 'usual' netlink data x like 'struct xfrm_usersa_info' + */ #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 !!! */ +/** + * 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') */ +/** + * returns the total size of attached rta data + * (after 'usual' netlink data x like 'struct xfrm_usersa_info') + */ #define XFRM_PAYLOAD(nlh, x) NLMSG_PAYLOAD(nlh, sizeof(x)) typedef struct kernel_algorithm_t kernel_algorithm_t; @@ -117,7 +126,8 @@ kernel_algorithm_t integrity_algs[] = { /** * 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) +char* lookup_algorithm(kernel_algorithm_t *kernel_algo, + algorithm_t *ikev2_algo, u_int *key_size) { while (kernel_algo->ikev2_id != END_OF_LIST) { @@ -139,11 +149,33 @@ char* lookup_algorithm(kernel_algorithm_t *kernel_algo, algorithm_t *ikev2_algo, return NULL; } + + +typedef struct kernel_policy_t kernel_policy_t; + +/** + * Installed kernel policy. + */ +struct kernel_policy_t { + + /** direction of this policy: in, out, forward */ + u_int8_t direction; + + /** reqid of the policy */ + u_int32_t reqid; + + /** parameters of installed policy */ + struct xfrm_selector sel; + + /** by how many CHILD_SA's this policy is used */ + u_int refcount; +}; + + typedef struct private_kernel_interface_t private_kernel_interface_t; - /** - * @brief Private Variables and Functions of kernel_interface class. - * +/** + * Private Variables and Functions of kernel_interface class. */ struct private_kernel_interface_t { /** @@ -152,6 +184,16 @@ struct private_kernel_interface_t { kernel_interface_t public; /** + * List of installed policies (kernel_policy_t) + */ + linked_list_t *policies; + + /** + * Mutex locks access to policies list. + */ + pthread_mutex_t pol_mutex; + + /** * Netlink communication socket. */ int socket; @@ -179,7 +221,7 @@ struct private_kernel_interface_t { /** * Mutex locks access to replies list. */ - pthread_mutex_t mutex; + pthread_mutex_t rep_mutex; /** * Condvar allows signaling of threads waiting for a reply. @@ -190,17 +232,181 @@ struct private_kernel_interface_t { * Logger for XFRM stuff */ logger_t *logger; +}; + + +/** + * Send a message down to the kernel and wait for its response + */ +static status_t send_message(private_kernel_interface_t *this, + struct nlmsghdr *request, struct nlmsghdr **response) +{ + size_t length; + struct sockaddr_nl addr; - /** - * Function for the thread, receives messages. - */ - void (*receive_messages) (private_kernel_interface_t *this); + request->nlmsg_seq = ++this->seq; + request->nlmsg_pid = 0; - /** - * Sends a netlink_message_t down to the kernel and wait for reply. - */ - status_t (*send_message) (private_kernel_interface_t *this, struct nlmsghdr *request, struct nlmsghdr **response); -}; + memset(&addr, 0, sizeof(struct sockaddr_nl)); + addr.nl_family = AF_NETLINK; + addr.nl_pid = 0; + addr.nl_groups = 0; + + length = sendto(this->socket,(void *)request, request->nlmsg_len, 0, + (struct sockaddr *)&addr, sizeof(addr)); + + if (length < 0) + { + return FAILED; + } + else if (length != request->nlmsg_len) + { + return FAILED; + } + + pthread_mutex_lock(&(this->rep_mutex)); + + while (TRUE) + { + iterator_t *iterator; + bool found = FALSE; + /* search list, break if found */ + iterator = this->responses->create_iterator(this->responses, TRUE); + while (iterator->has_next(iterator)) + { + struct nlmsghdr *listed_response; + iterator->current(iterator, (void**)&listed_response); + if (listed_response->nlmsg_seq == request->nlmsg_seq) + { + /* matches our request, this is the reply */ + *response = listed_response; + iterator->remove(iterator); + found = TRUE; + break; + } + } + iterator->destroy(iterator); + + if (found) + { + break; + } + /* TODO: we should time out, if something goes wrong!??? */ + pthread_cond_wait(&(this->condvar), &(this->rep_mutex)); + } + + pthread_mutex_unlock(&(this->rep_mutex)); + + return SUCCESS; +} + +/** + * Implementation of private_kernel_interface_t.receive_messages. + */ +static void receive_messages(private_kernel_interface_t *this) +{ + while(TRUE) + { + unsigned char response[BUFFER_SIZE]; + struct nlmsghdr *hdr, *listed_response; + while (TRUE) + { + struct sockaddr_nl addr; + socklen_t addr_length; + size_t length; + + addr_length = sizeof(addr); + + length = recvfrom(this->socket, &response, sizeof(response), 0, (struct sockaddr*)&addr, &addr_length); + if (length < 0) + { + if (errno == EINTR) + { + /* interrupted, try again */ + continue; + } + charon->kill(charon, "receiving from netlink socket failed"); + } + if (!NLMSG_OK((struct nlmsghdr *)response, length)) + { + /* bad netlink message */ + continue; + } + if (addr.nl_pid != 0) + { + /* not from kernel. not interested, try another one */ + continue; + } + /* good message, handle it */ + break; + } + + /* we handle ACQUIRE and EXPIRE messages directly + */ + hdr = (struct nlmsghdr*)response; + if (hdr->nlmsg_type == XFRM_MSG_ACQUIRE) + { + struct xfrm_user_acquire *acquire; + + acquire = (struct xfrm_user_acquire*)NLMSG_DATA(hdr); + this->logger->log(this->logger, CONTROL, + "Received a XFRM_MSG_ACQUIRE with index %d. Ignored", + acquire->policy.index); + + } + else if (hdr->nlmsg_type == XFRM_MSG_EXPIRE) + { + job_t *job; + protocol_id_t protocol; + u_int32_t spi; + struct xfrm_user_expire *expire; + + expire = (struct xfrm_user_expire*)NLMSG_DATA(hdr); + protocol = expire->state.id.proto == KERNEL_ESP ? + PROTO_ESP : PROTO_AH; + spi = expire->state.id.spi; + + this->logger->log(this->logger, CONTROL|LEVEL1, + "Received a XFRM_MSG_EXPIRE"); + this->logger->log(this->logger, CONTROL, + "creating %s job for %s CHILD_SA 0x%x", + expire->hard ? "delete" : "rekey", + mapping_find(protocol_id_m, protocol), ntohl(spi)); + if (expire->hard) + { + job = (job_t*)delete_child_sa_job_create(protocol, spi); + } + else + { + job = (job_t*)rekey_child_sa_job_create(protocol, spi); + } + charon->job_queue->add(charon->job_queue, job); + } + /* NLMSG_ERROR is sent back for acknowledge (or on error), an + * XFRM_MSG_NEWSA is returned when we alloc spis and when + * updating SAs. + * XFRM_MSG_NEWPOLICY is returned when we query a policy. + * list these responses for the sender + */ + else if (hdr->nlmsg_type == NLMSG_ERROR || + hdr->nlmsg_type == XFRM_MSG_NEWSA || + hdr->nlmsg_type == XFRM_MSG_NEWPOLICY) + { + /* add response to queue */ + listed_response = malloc(hdr->nlmsg_len); + memcpy(listed_response, &response, hdr->nlmsg_len); + + pthread_mutex_lock(&(this->rep_mutex)); + this->responses->insert_last(this->responses, (void*)listed_response); + pthread_mutex_unlock(&(this->rep_mutex)); + /* signal ALL waiting threads */ + pthread_cond_broadcast(&(this->condvar)); + } + /* we are not interested in anything other. + * anyway, move on to the next message */ + continue; + } +} /** * Implementation of kernel_interface_t.get_spi. @@ -212,18 +418,20 @@ static status_t get_spi(private_kernel_interface_t *this, { unsigned char request[BUFFER_SIZE]; struct nlmsghdr *response; - + struct nlmsghdr *hdr; + struct xfrm_userspi_info *userspi; + memset(&request, 0, sizeof(request)); status_t status = SUCCESS; this->logger->log(this->logger, CONTROL|LEVEL2, "getting spi"); - struct nlmsghdr *hdr = (struct nlmsghdr*)request; + hdr = (struct nlmsghdr*)request; hdr->nlmsg_flags = NLM_F_REQUEST; hdr->nlmsg_type = XFRM_MSG_ALLOCSPI; hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userspi_info)); - struct xfrm_userspi_info *userspi = (struct xfrm_userspi_info*)NLMSG_DATA(hdr); + userspi = (struct xfrm_userspi_info*)NLMSG_DATA(hdr); userspi->info.saddr = src->get_xfrm_addr(src); userspi->info.id.daddr = dest->get_xfrm_addr(dest); userspi->info.id.proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; @@ -233,7 +441,7 @@ static status_t get_spi(private_kernel_interface_t *this, userspi->min = 0xc0000000; userspi->max = 0xcFFFFFFF; - if (this->send_message(this, hdr, &response) != SUCCESS) + if (send_message(this, hdr, &response) != SUCCESS) { this->logger->log(this->logger, ERROR, "netlink communication failed"); return FAILED; @@ -280,18 +488,20 @@ static status_t add_sa(private_kernel_interface_t *this, struct nlmsghdr *response; char *alg_name; size_t key_size; + struct nlmsghdr *hdr; + struct xfrm_usersa_info *sa; memset(&request, 0, sizeof(request)); status_t status = SUCCESS; this->logger->log(this->logger, CONTROL|LEVEL2, "adding SA"); - struct nlmsghdr *hdr = (struct nlmsghdr*)request; + hdr = (struct nlmsghdr*)request; hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; hdr->nlmsg_type = replace ? XFRM_MSG_UPDSA : XFRM_MSG_NEWSA; hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_info)); - struct xfrm_usersa_info *sa = (struct xfrm_usersa_info*)NLMSG_DATA(hdr); + sa = (struct xfrm_usersa_info*)NLMSG_DATA(hdr); sa->saddr = me->get_xfrm_addr(me); sa->id.daddr = other->get_xfrm_addr(other); @@ -401,7 +611,7 @@ static status_t add_sa(private_kernel_interface_t *this, rthdr = XFRM_RTA_NEXT(rthdr); } - if (this->send_message(this, hdr, &response) != SUCCESS) + if (send_message(this, hdr, &response) != SUCCESS) { this->logger->log(this->logger, ERROR, "netlink communication failed"); return FAILED; @@ -422,6 +632,9 @@ static status_t add_sa(private_kernel_interface_t *this, return status; } +/** + * Implementation of kernel_interface_t.update_sa_hosts. + */ static status_t update_sa_hosts( private_kernel_interface_t *this, host_t *src, host_t *dst, @@ -431,24 +644,26 @@ static status_t update_sa_hosts( { unsigned char request[BUFFER_SIZE]; struct nlmsghdr *update, *response; + struct nlmsghdr *hdr; + struct xfrm_usersa_id *sa_id; memset(&request, 0, sizeof(request)); status_t status = SUCCESS; this->logger->log(this->logger, CONTROL|LEVEL2, "getting SA"); - struct nlmsghdr *hdr = (struct nlmsghdr*)request; + hdr = (struct nlmsghdr*)request; hdr->nlmsg_flags = NLM_F_REQUEST; hdr->nlmsg_type = XFRM_MSG_GETSA; hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_id)); - struct xfrm_usersa_id *sa_id = (struct xfrm_usersa_id*)NLMSG_DATA(hdr); + sa_id = (struct xfrm_usersa_id*)NLMSG_DATA(hdr); sa_id->daddr = dst->get_xfrm_addr(dst); sa_id->spi = spi; sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; sa_id->family = dst->get_family(dst); - if (this->send_message(this, hdr, &update) != SUCCESS) + if (send_message(this, hdr, &update) != SUCCESS) { this->logger->log(this->logger, ERROR, "netlink communication failed"); return FAILED; @@ -508,7 +723,7 @@ static status_t update_sa_hosts( } } - if (this->send_message(this, update, &response) != SUCCESS) + if (send_message(this, update, &response) != SUCCESS) { this->logger->log(this->logger, ERROR, "netlink communication failed"); free(update); @@ -535,32 +750,86 @@ static status_t update_sa_hosts( free(response); return status; } + +/** + * Implementation of kernel_interface_t.query_sa. + */ +static status_t query_sa(private_kernel_interface_t *this, host_t *dst, + u_int32_t spi, protocol_id_t protocol, time_t *use_time) +{ + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *response; + struct nlmsghdr *hdr; + struct xfrm_usersa_id *sa_id; + struct xfrm_usersa_info *sa_info; + + this->logger->log(this->logger, CONTROL|LEVEL2, "querying SA"); + memset(&request, 0, sizeof(request)); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST; + hdr->nlmsg_type = XFRM_MSG_GETSA; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_info)); + + sa_id = (struct xfrm_usersa_id*)NLMSG_DATA(hdr); + sa_id->daddr = dst->get_xfrm_addr(dst); + sa_id->spi = spi; + sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; + sa_id->family = dst->get_family(dst); + + if (send_message(this, hdr, &response) != SUCCESS) + { + this->logger->log(this->logger, ERROR, "netlink communication failed"); + return FAILED; + } + else if (response->nlmsg_type != XFRM_MSG_NEWSA) + { + this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_GETSA not acknowledged"); + free(response); + return FAILED; + } + else if (response->nlmsg_len < NLMSG_LENGTH(sizeof(struct xfrm_usersa_info))) + { + this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_GETSA got an invalid reply"); + free(response); + return FAILED; + } + + sa_info = (struct xfrm_usersa_info*)NLMSG_DATA(response); + *use_time = (time_t)sa_info->curlft.use_time; -static status_t del_sa( private_kernel_interface_t *this, - host_t *dst, - u_int32_t spi, - protocol_id_t protocol) + free(response); + return SUCCESS; +} + +/** + * Implementation of kernel_interface_t.del_sa. + */ +static status_t del_sa(private_kernel_interface_t *this, host_t *dst, + u_int32_t spi, protocol_id_t protocol) { unsigned char request[BUFFER_SIZE]; struct nlmsghdr *response; + struct nlmsghdr *hdr; + struct xfrm_usersa_id *sa_id; memset(&request, 0, sizeof(request)); status_t status = SUCCESS; this->logger->log(this->logger, CONTROL|LEVEL2, "deleting SA"); - - struct nlmsghdr *hdr = (struct nlmsghdr*)request; + + hdr = (struct nlmsghdr*)request; hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; hdr->nlmsg_type = XFRM_MSG_DELSA; hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_id)); - - struct xfrm_usersa_id *sa_id = (struct xfrm_usersa_id*)NLMSG_DATA(hdr); + + sa_id = (struct xfrm_usersa_id*)NLMSG_DATA(hdr); sa_id->daddr = dst->get_xfrm_addr(dst); sa_id->spi = spi; sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; sa_id->family = dst->get_family(dst); - if (this->send_message(this, hdr, &response) != SUCCESS) + if (send_message(this, hdr, &response) != SUCCESS) { this->logger->log(this->logger, ERROR, "netlink communication failed"); return FAILED; @@ -588,52 +857,85 @@ static status_t add_policy(private_kernel_interface_t *this, host_t *me, host_t *other, host_t *src, host_t *dst, u_int8_t src_hostbits, u_int8_t dst_hostbits, - int direction, int upper_proto, + u_int8_t direction, u_int8_t upper_proto, protocol_id_t protocol, - u_int32_t reqid) + u_int32_t reqid, bool update) { + iterator_t *iterator; + kernel_policy_t *current, *policy; + bool found = FALSE; unsigned char request[BUFFER_SIZE]; struct nlmsghdr *response; - - memset(&request, 0, sizeof(request)); + struct xfrm_userpolicy_info *policy_info; + struct nlmsghdr *hdr; status_t status = SUCCESS; - this->logger->log(this->logger, CONTROL|LEVEL2, "adding policy"); - - struct nlmsghdr *hdr = (struct nlmsghdr*)request; - hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; - hdr->nlmsg_type = XFRM_MSG_UPDPOLICY; - hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_info)); - - struct xfrm_userpolicy_info *policy = (struct xfrm_userpolicy_info*)NLMSG_DATA(hdr); - - policy->sel.sport = htons(src->get_port(src)); - policy->sel.sport_mask = (policy->sel.sport) ? ~0 : 0; + /* create a policy */ + policy = malloc_thing(kernel_policy_t); + memset(policy, 0, sizeof(kernel_policy_t)); policy->sel.saddr = src->get_xfrm_addr(src); policy->sel.prefixlen_s = src_hostbits; - - policy->sel.dport = htons(dst->get_port(dst)); - policy->sel.dport_mask = (policy->sel.dport) ? ~0 : 0; + policy->sel.sport = htons(src->get_port(src)); + policy->sel.sport_mask = (policy->sel.sport) ? ~0 : 0; policy->sel.daddr = dst->get_xfrm_addr(dst); policy->sel.prefixlen_d = dst_hostbits; - + policy->sel.dport = htons(dst->get_port(dst)); + policy->sel.dport_mask = (policy->sel.dport) ? ~0 : 0; policy->sel.proto = upper_proto; policy->sel.family = src->get_family(src); + policy->direction = direction; - policy->dir = direction; - policy->priority = SPD_PRIORITY; - policy->action = XFRM_POLICY_ALLOW; - policy->share = XFRM_SHARE_ANY; - - /* policies currently don't expire */ - policy->lft.soft_byte_limit = XFRM_INF; - policy->lft.soft_packet_limit = XFRM_INF; - policy->lft.hard_byte_limit = XFRM_INF; - policy->lft.hard_packet_limit = XFRM_INF; - policy->lft.soft_add_expires_seconds = 0; - policy->lft.hard_add_expires_seconds = 0; - policy->lft.soft_use_expires_seconds = 0; - policy->lft.hard_use_expires_seconds = 0; + /* find the policy, which matches EXACTLY */ + pthread_mutex_lock(&this->pol_mutex); + iterator = this->policies->create_iterator(this->policies, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + if (memcmp(current, policy, sizeof(struct xfrm_selector)) == 0 && + policy->direction == current->direction) + { + /* use existing policy */ + if (!update) + { + current->refcount++; + } + free(policy); + policy = current; + found = TRUE; + break; + } + } + iterator->destroy(iterator); + if (!found) + { /* apply the new one, if we have no such policy */ + this->policies->insert_last(this->policies, policy); + policy->refcount = 1; + } + + this->logger->log(this->logger, CONTROL|LEVEL2, "adding policy"); + + memset(&request, 0, sizeof(request)); + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = XFRM_MSG_UPDPOLICY; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_info)); + + policy_info = (struct xfrm_userpolicy_info*)NLMSG_DATA(hdr); + policy_info->sel = policy->sel; + policy_info->dir = direction; + policy_info->priority = SPD_PRIORITY; + policy_info->action = XFRM_POLICY_ALLOW; + policy_info->share = XFRM_SHARE_ANY; + pthread_mutex_unlock(&this->pol_mutex); + + /* policies don't expire */ + policy_info->lft.soft_byte_limit = XFRM_INF; + policy_info->lft.soft_packet_limit = XFRM_INF; + policy_info->lft.hard_byte_limit = XFRM_INF; + policy_info->lft.hard_packet_limit = XFRM_INF; + policy_info->lft.soft_add_expires_seconds = 0; + policy_info->lft.hard_add_expires_seconds = 0; + 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); rthdr->rta_type = XFRMA_TMPL; @@ -656,19 +958,21 @@ static status_t add_policy(private_kernel_interface_t *this, tmpl->saddr = me->get_xfrm_addr(me); tmpl->id.daddr = other->get_xfrm_addr(other); - if (this->send_message(this, hdr, &response) != SUCCESS) + if (send_message(this, hdr, &response) != SUCCESS) { this->logger->log(this->logger, ERROR, "netlink communication failed"); return FAILED; } else if (response->nlmsg_type != NLMSG_ERROR) { - this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_NEWPOLICY not acknowledged"); + this->logger->log(this->logger, ERROR, + "netlink request XFRM_MSG_UPDPOLICY not acknowledged"); status = FAILED; } else if (((struct nlmsgerr*)NLMSG_DATA(response))->error) { - this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_NEWPOLICY got an error: %s", + this->logger->log(this->logger, ERROR, + "netlink request XFRM_MSG_UPDPOLICY got an error: %s", strerror(-((struct nlmsgerr*)NLMSG_DATA(response))->error)); status = FAILED; } @@ -677,114 +981,84 @@ static status_t add_policy(private_kernel_interface_t *this, return status; } -static status_t query_policy(private_kernel_interface_t *this, - host_t *me, host_t *other, - host_t *src, host_t *dst, - u_int8_t src_hostbits, u_int8_t dst_hostbits, - int direction, int upper_proto, - time_t *use_time) +/** + * Implementation of kernel_interface_t.del_policy. + */ +static status_t del_policy(private_kernel_interface_t *this, + host_t *src, host_t *dst, + u_int8_t src_hostbits, u_int8_t dst_hostbits, + u_int8_t direction, u_int8_t upper_proto) { + kernel_policy_t *current, policy, *to_delete = NULL; unsigned char request[BUFFER_SIZE]; struct nlmsghdr *response; - - memset(&request, 0, sizeof(request)); + struct nlmsghdr *hdr; + struct xfrm_userpolicy_id *policy_id; + iterator_t *iterator; status_t status = SUCCESS; - this->logger->log(this->logger, CONTROL|LEVEL2, "querying policy"); - - struct nlmsghdr *hdr = (struct nlmsghdr*)request; - hdr->nlmsg_flags = NLM_F_REQUEST; - hdr->nlmsg_type = XFRM_MSG_GETPOLICY; - hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_id)); - - struct xfrm_userpolicy_id *policy_id = (struct xfrm_userpolicy_id*)NLMSG_DATA(hdr); - policy_id->sel.sport = htons(src->get_port(src)); - policy_id->sel.sport_mask = (policy_id->sel.sport) ? ~0 : 0; - policy_id->sel.saddr = src->get_xfrm_addr(src); - policy_id->sel.prefixlen_s = src_hostbits; - - policy_id->sel.dport = htons(dst->get_port(dst)); - policy_id->sel.dport_mask = (policy_id->sel.dport) ? ~0 : 0; - policy_id->sel.daddr = dst->get_xfrm_addr(dst); - policy_id->sel.prefixlen_d = dst_hostbits; - - policy_id->sel.proto = upper_proto; - policy_id->sel.family = src->get_family(src); + this->logger->log(this->logger, CONTROL|LEVEL2, "deleting policy"); - policy_id->dir = direction; - - if (this->send_message(this, hdr, &response) != SUCCESS) - { - this->logger->log(this->logger, ERROR, "netlink communication failed"); - return FAILED; - } - else if (response->nlmsg_type == NLMSG_ERROR) - { - this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_GETPOLICY got an error: %s", - strerror(-((struct nlmsgerr*)NLMSG_DATA(response))->error)); - free(response); - return FAILED; - } - else if (response->nlmsg_type != XFRM_MSG_NEWPOLICY) + /* create a policy */ + memset(&policy, 0, sizeof(kernel_policy_t)); + policy.sel.saddr = src->get_xfrm_addr(src); + policy.sel.prefixlen_s = src_hostbits; + policy.sel.sport = htons(src->get_port(src)); + policy.sel.sport_mask = (policy.sel.sport) ? ~0 : 0; + policy.sel.daddr = dst->get_xfrm_addr(dst); + policy.sel.prefixlen_d = dst_hostbits; + policy.sel.dport = htons(dst->get_port(dst)); + policy.sel.dport_mask = (policy.sel.dport) ? ~0 : 0; + policy.sel.proto = upper_proto; + policy.sel.family = src->get_family(src); + policy.direction = direction; + + /* find the policy */ + pthread_mutex_lock(&this->pol_mutex); + iterator = this->policies->create_iterator(this->policies, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) { - this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_GETPOLICY got an unknown reply"); - free(response); - return FAILED; + if (memcmp(¤t->sel, &policy.sel, sizeof(struct xfrm_selector)) == 0 && + policy.direction == current->direction) + { + to_delete = current; + if (--to_delete->refcount > 0) + { + /* is used by more SAs, keep in kernel */ + this->logger->log(this->logger, CONTROL|LEVEL2, + "is used by other SAs, not removed"); + iterator->destroy(iterator); + pthread_mutex_unlock(&this->pol_mutex); + return SUCCESS; + } + /* remove if last reference */ + iterator->remove(iterator); + break; + } } - else if (response->nlmsg_len < NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_info))) + iterator->destroy(iterator); + pthread_mutex_unlock(&this->pol_mutex); + if (!to_delete) { - this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_GETPOLICY got an invalid reply"); - free(response); - return FAILED; + this->logger->log(this->logger, CONTROL|LEVEL2, + "no such policy found"); + return NOT_FOUND; } - - struct xfrm_userpolicy_info *policy = (struct xfrm_userpolicy_info*)NLMSG_DATA(response); - - *use_time = (time_t)policy->curlft.use_time; - - free(response); - return status; -} - -/** - * Implementation of kernel_interface_t.del_policy. - */ -static status_t del_policy(private_kernel_interface_t *this, - host_t *me, host_t *other, - host_t *src, host_t *dst, - u_int8_t src_hostbits, u_int8_t dst_hostbits, - int direction, int upper_proto) -{ - unsigned char request[BUFFER_SIZE]; - struct nlmsghdr *response; memset(&request, 0, sizeof(request)); - status_t status = SUCCESS; - this->logger->log(this->logger, CONTROL|LEVEL2, "deleting policy"); - - struct nlmsghdr *hdr = (struct nlmsghdr*)request; + hdr = (struct nlmsghdr*)request; hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; hdr->nlmsg_type = XFRM_MSG_DELPOLICY; hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_id)); - struct xfrm_userpolicy_id *policy_id = (struct xfrm_userpolicy_id*)NLMSG_DATA(hdr); - policy_id->sel.sport = htons(src->get_port(src)); - policy_id->sel.sport_mask = (policy_id->sel.sport) ? ~0 : 0; - policy_id->sel.saddr = src->get_xfrm_addr(src); - policy_id->sel.prefixlen_s = src_hostbits; - - policy_id->sel.dport = htons(dst->get_port(dst)); - policy_id->sel.dport_mask = (policy_id->sel.dport) ? ~0 : 0; - policy_id->sel.daddr = dst->get_xfrm_addr(dst); - policy_id->sel.prefixlen_d = dst_hostbits; + policy_id = (struct xfrm_userpolicy_id*)NLMSG_DATA(hdr); + policy_id->sel = to_delete->sel; + policy_id->dir = direction; - policy_id->sel.proto = upper_proto; - policy_id->sel.family = src->get_family(src); + free(to_delete); - policy_id->dir = direction; - - if (this->send_message(this, hdr, &response) != SUCCESS) + if (send_message(this, hdr, &response) != SUCCESS) { this->logger->log(this->logger, ERROR, "netlink communication failed"); return FAILED; @@ -806,172 +1080,6 @@ static status_t del_policy(private_kernel_interface_t *this, } /** - * Implementation of private_kernel_interface_t.send_message. - */ -static status_t send_message(private_kernel_interface_t *this, struct nlmsghdr *request, struct nlmsghdr **response) -{ - size_t length; - struct sockaddr_nl addr; - - request->nlmsg_seq = ++this->seq; - request->nlmsg_pid = 0; - - memset(&addr, 0, sizeof(struct sockaddr_nl)); - addr.nl_family = AF_NETLINK; - addr.nl_pid = 0; - addr.nl_groups = 0; - - length = sendto(this->socket,(void *)request, request->nlmsg_len, 0, (struct sockaddr *)&addr, sizeof(addr)); - - if (length < 0) - { - return FAILED; - } - else if (length != request->nlmsg_len) - { - return FAILED; - } - - pthread_mutex_lock(&(this->mutex)); - - while (TRUE) - { - iterator_t *iterator; - bool found = FALSE; - /* search list, break if found */ - iterator = this->responses->create_iterator(this->responses, TRUE); - while (iterator->has_next(iterator)) - { - struct nlmsghdr *listed_response; - iterator->current(iterator, (void**)&listed_response); - if (listed_response->nlmsg_seq == request->nlmsg_seq) - { - /* matches our request, this is the reply */ - *response = listed_response; - iterator->remove(iterator); - found = TRUE; - break; - } - } - iterator->destroy(iterator); - - if (found) - { - break; - } - /* TODO: we should time out, if something goes wrong!??? */ - pthread_cond_wait(&(this->condvar), &(this->mutex)); - } - - pthread_mutex_unlock(&(this->mutex)); - - return SUCCESS; -} - -/** - * Implementation of private_kernel_interface_t.receive_messages. - */ -static void receive_messages(private_kernel_interface_t *this) -{ - while(TRUE) - { - unsigned char response[BUFFER_SIZE]; - struct nlmsghdr *hdr, *listed_response; - while (TRUE) - { - struct sockaddr_nl addr; - socklen_t addr_length; - size_t length; - - addr_length = sizeof(addr); - - length = recvfrom(this->socket, &response, sizeof(response), 0, (struct sockaddr*)&addr, &addr_length); - if (length < 0) - { - if (errno == EINTR) - { - /* interrupted, try again */ - continue; - } - charon->kill(charon, "receiving from netlink socket failed"); - } - if (!NLMSG_OK((struct nlmsghdr *)response, length)) - { - /* bad netlink message */ - continue; - } - if (addr.nl_pid != 0) - { - /* not from kernel. not interested, try another one */ - continue; - } - /* good message, handle it */ - break; - } - - /* we handle ACQUIRE and EXPIRE messages directly - */ - hdr = (struct nlmsghdr*)response; - if (hdr->nlmsg_type == XFRM_MSG_ACQUIRE) - { - this->logger->log(this->logger, CONTROL, - "Received a XFRM_MSG_ACQUIRE. Ignored"); - } - else if (hdr->nlmsg_type == XFRM_MSG_EXPIRE) - { - job_t *job; - protocol_id_t protocol; - u_int32_t spi; - struct xfrm_user_expire *expire; - - expire = (struct xfrm_user_expire*)NLMSG_DATA(hdr); - protocol = expire->state.id.proto == KERNEL_ESP ? - PROTO_ESP : PROTO_AH; - spi = expire->state.id.spi; - - this->logger->log(this->logger, CONTROL|LEVEL1, - "Received a XFRM_MSG_EXPIRE"); - this->logger->log(this->logger, CONTROL, - "creating %s job for %s CHILD_SA 0x%x", - expire->hard ? "delete" : "rekey", - mapping_find(protocol_id_m, protocol), ntohl(spi)); - if (expire->hard) - { - job = (job_t*)delete_child_sa_job_create(protocol, spi); - } - else - { - job = (job_t*)rekey_child_sa_job_create(protocol, spi); - } - charon->job_queue->add(charon->job_queue, job); - } - /* NLMSG_ERROR is sent back for acknowledge (or on error), an - * XFRM_MSG_NEWSA is returned when we alloc spis and when - * updating SAs. - * XFRM_MSG_NEWPOLICY is returned when we query a policy. - * list these responses for the sender - */ - else if (hdr->nlmsg_type == NLMSG_ERROR || - hdr->nlmsg_type == XFRM_MSG_NEWSA || - hdr->nlmsg_type == XFRM_MSG_NEWPOLICY) - { - /* add response to queue */ - listed_response = malloc(hdr->nlmsg_len); - memcpy(listed_response, &response, hdr->nlmsg_len); - - pthread_mutex_lock(&(this->mutex)); - this->responses->insert_last(this->responses, (void*)listed_response); - pthread_mutex_unlock(&(this->mutex)); - /* signal ALL waiting threads */ - pthread_cond_broadcast(&(this->condvar)); - } - /* we are not interested in anything other. - * anyway, move on to the next message */ - continue; - } -} - -/** * Implementation of kernel_interface_t.destroy. */ static void destroy(private_kernel_interface_t *this) @@ -980,6 +1088,7 @@ static void destroy(private_kernel_interface_t *this) pthread_join(this->thread, NULL); close(this->socket); this->responses->destroy(this->responses); + this->policies->destroy(this->policies); free(this); } @@ -995,19 +1104,19 @@ kernel_interface_t *kernel_interface_create() 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*,natt_conf_t*,bool))add_sa; this->public.update_sa_hosts = (status_t(*)(kernel_interface_t*,host_t*,host_t*,host_t*,host_t*,int,int,u_int32_t,protocol_id_t))update_sa_hosts; + this->public.query_sa = (status_t(*)(kernel_interface_t*,host_t*,u_int32_t,protocol_id_t,time_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*,host_t*,host_t*,u_int8_t,u_int8_t,int,int,protocol_id_t,u_int32_t))add_policy; - this->public.query_policy = (status_t(*)(kernel_interface_t*,host_t*,host_t*,host_t*,host_t*,u_int8_t,u_int8_t,int,int,time_t*))query_policy; - this->public.del_policy = (status_t(*)(kernel_interface_t*,host_t*,host_t*,host_t*,host_t*,u_int8_t,u_int8_t,int,int))del_policy; + this->public.add_policy = (status_t(*)(kernel_interface_t*,host_t*,host_t*,host_t*,host_t*,u_int8_t,u_int8_t,u_int8_t,u_int8_t,protocol_id_t,u_int32_t,bool))add_policy; + this->public.del_policy = (status_t(*)(kernel_interface_t*,host_t*,host_t*,u_int8_t,u_int8_t,u_int8_t,u_int8_t))del_policy; this->public.destroy = (void(*)(kernel_interface_t*)) destroy; /* private members */ - this->receive_messages = receive_messages; - this->send_message = send_message; this->pid = getpid(); this->responses = linked_list_create(); + this->policies = linked_list_create(); this->logger = logger_manager->get_logger(logger_manager, XFRM); - pthread_mutex_init(&(this->mutex),NULL); + pthread_mutex_init(&(this->rep_mutex),NULL); + pthread_mutex_init(&(this->pol_mutex),NULL); pthread_cond_init(&(this->condvar),NULL); this->seq = 0; @@ -1032,7 +1141,7 @@ kernel_interface_t *kernel_interface_create() charon->kill(charon, "Unable to bind netlink socket"); } - if (pthread_create(&(this->thread), NULL, (void*(*)(void*))this->receive_messages, this) != 0) + if (pthread_create(&this->thread, NULL, (void*(*)(void*))receive_messages, this) != 0) { this->responses->destroy(this->responses); close(this->socket); @@ -1040,5 +1149,5 @@ kernel_interface_t *kernel_interface_create() charon->kill(charon, "Unable to create netlink thread"); } - return (&this->public); + return &this->public; } |