diff options
author | Martin Willi <martin@strongswan.org> | 2007-02-28 14:04:36 +0000 |
---|---|---|
committer | Martin Willi <martin@strongswan.org> | 2007-02-28 14:04:36 +0000 |
commit | c60c7694d2d8925c5d93ff33d132f561ad89e071 (patch) | |
tree | 9c7957b0749139c5e7c9b008c927e79d69f8e500 /src/charon/threads/kernel_interface.c | |
parent | a7a5e834e318d0582b6db979b63a5739c0a8244f (diff) | |
download | strongswan-c60c7694d2d8925c5d93ff33d132f561ad89e071.tar.bz2 strongswan-c60c7694d2d8925c5d93ff33d132f561ad89e071.tar.xz |
merged tasking branch into trunk
Diffstat (limited to 'src/charon/threads/kernel_interface.c')
-rw-r--r-- | src/charon/threads/kernel_interface.c | 821 |
1 files changed, 715 insertions, 106 deletions
diff --git a/src/charon/threads/kernel_interface.c b/src/charon/threads/kernel_interface.c index 3ec2bd58e..e595108ee 100644 --- a/src/charon/threads/kernel_interface.c +++ b/src/charon/threads/kernel_interface.c @@ -6,7 +6,9 @@ */ /* - * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser + * Copyright (C) 2006-2007 Tobias Brunner + * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -36,6 +38,9 @@ #include <fcntl.h> #include <errno.h> #include <string.h> +#include <net/if.h> +#include <sys/ioctl.h> +#include <ifaddrs.h> #include "kernel_interface.h" @@ -152,7 +157,21 @@ char* lookup_algorithm(kernel_algorithm_t *kernel_algo, return NULL; } +typedef struct rt_refcount_t rt_refcount_t; +struct rt_refcount_t { + /** Index of the interface the route is bound to */ + int if_index; + + /** Source ip of the route */ + host_t *src_ip; + + /** Destination net */ + chunk_t dst_net; + + /** Destination net prefixlen */ + u_int8_t prefixlen; +}; typedef struct kernel_policy_t kernel_policy_t; @@ -170,15 +189,33 @@ struct kernel_policy_t { /** parameters of installed policy */ struct xfrm_selector sel; + /** associated route installed for this policy */ + rt_refcount_t *route; + /** by how many CHILD_SA's this policy is used */ u_int refcount; }; +typedef struct vip_refcount_t vip_refcount_t; + +/** + * Reference counter for for virtual ips. + */ +struct vip_refcount_t { + /** Index of the interface the ip is bound to */ + u_int8_t if_index; + + /** The ip address */ + host_t *ip; + + /** Number of times this ip is used */ + u_int refcount; +}; typedef struct private_kernel_interface_t private_kernel_interface_t; /** - * Private Variables and Functions of kernel_interface class. + * Private variables and functions of kernel_interface class. */ struct private_kernel_interface_t { /** @@ -197,9 +234,14 @@ struct private_kernel_interface_t { pthread_mutex_t pol_mutex; /** - * Netlink communication socket. + * Netlink communication socket for XFRM IPsec. */ - int socket; + int xfrm_socket; + + /** + * Netlink communication socket for routing & addresses. + */ + int rt_socket; /** * Process id of kernel thread @@ -217,9 +259,14 @@ struct private_kernel_interface_t { linked_list_t *responses; /** - * Thread which receives messages. + * Thread which receives xfrm messages. + */ + pthread_t xfrm_thread; + + /** + * Thread which receives rt messages. */ - pthread_t thread; + pthread_t rt_thread; /** * Mutex locks access to replies list. @@ -230,35 +277,56 @@ struct private_kernel_interface_t { * Condvar allows signaling of threads waiting for a reply. */ pthread_cond_t condvar; -}; + + /** + * List of reference counter objects for all virtual ips. + */ + linked_list_t *vips; + /** + * Mutex to lock access to vip list. + */ + pthread_mutex_t vip_mutex; +}; /** - * Send a message down to the kernel and wait for its response + * Sends a message down to the kernel and waits for its response */ static status_t send_message(private_kernel_interface_t *this, - struct nlmsghdr *request, struct nlmsghdr **response) + struct nlmsghdr *request, + struct nlmsghdr **response, + int socket) { size_t length; struct sockaddr_nl addr; request->nlmsg_seq = ++this->seq; - request->nlmsg_pid = 0; + request->nlmsg_pid = getpid(); memset(&addr, 0, sizeof(struct sockaddr_nl)); addr.nl_family = AF_NETLINK; addr.nl_pid = 0; addr.nl_groups = 0; + + /* + // set timeout to 10 secs + struct timespec tm; + tm.tv_sec = 10; + */ - length = sendto(this->socket,(void *)request, request->nlmsg_len, 0, + length = sendto(socket,(void *)request, request->nlmsg_len, 0, (struct sockaddr *)&addr, sizeof(addr)); + DBG2(DBG_IKE, "%d bytes sent to kernel", length); if (length < 0) { + DBG1(DBG_IKE,"0 byte could be sent"); return FAILED; } else if (length != request->nlmsg_len) { + DBG1(DBG_IKE,"Request length %d does not match the sent bytes %d", + request->nlmsg_len, length); return FAILED; } @@ -290,6 +358,8 @@ static status_t send_message(private_kernel_interface_t *this, break; } /* TODO: we should time out, if something goes wrong!??? */ + //if(pthread_cond_timedwait(&(this->condvar), &(this->rep_mutex), &tm) == ETIMEDOUT) + // return FAILED; pthread_cond_wait(&(this->condvar), &(this->rep_mutex)); } @@ -299,46 +369,73 @@ static status_t send_message(private_kernel_interface_t *this, } /** - * Implementation of private_kernel_interface_t.receive_messages. + * Reads from a netlink socket and returns the message in a buffer. */ -static void receive_messages(private_kernel_interface_t *this) +static void netlink_package_receiver(int socket, unsigned char *response, int response_size) { - while(TRUE) + 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(socket, response, response_size, 0, (struct sockaddr*)&addr, &addr_length); + if (length < 0) { - 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)) + if (errno == EINTR) { - /* bad netlink message */ + /* interrupted, try again */ continue; } - if (addr.nl_pid != 0) - { - /* not from kernel. not interested, try another one */ - continue; - } - /* good message, handle it */ - break; + charon->kill(charon, "receiving from netlink socket failed\n"); } + 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 */ + return; + } +} + +/** + * Takes a Netlink package from the response buffer and writes it to this->responses. + * Then it signals all waiting threads. + */ +static void add_to_package_list(private_kernel_interface_t *this, unsigned char *response) +{ + struct nlmsghdr *hdr = (struct nlmsghdr*)response; + /* add response to queue */ + struct nlmsghdr *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)); +} + +/** + * Receives packages from this->xfrm_socket and puts them to this->package_list + */ +static void receive_xfrm_messages(private_kernel_interface_t *this) +{ + while(TRUE) + { + unsigned char response[BUFFER_SIZE]; + struct nlmsghdr *hdr; + netlink_package_receiver(this->xfrm_socket, response, sizeof(response)); + /* we handle ACQUIRE and EXPIRE messages directly */ hdr = (struct nlmsghdr*)response; if (hdr->nlmsg_type == XFRM_MSG_ACQUIRE) @@ -396,28 +493,47 @@ static void receive_messages(private_kernel_interface_t *this) } 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) + /* NLMSG_ERROR is sent back for acknowledge (or on error). + * XFRM_MSG_NEWSA is returned when we alloc spis and when + * updating SAs. + * XFRM_MSG_NEWPOLICY is returned when we query a policy. + */ + 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)); + add_to_package_list(this, response); + } + /* we are not interested in anything other. + * anyway, move on to the next message + */ + continue; + } +} + +/** + * Receives packages from this->rt_socket and puts them to this->package_list + */ +static void receive_rt_messages(private_kernel_interface_t *this) +{ + while(TRUE) + { + unsigned char response[BUFFER_SIZE]; + struct nlmsghdr *hdr; + netlink_package_receiver(this->rt_socket,response,BUFFER_SIZE); + + hdr = (struct nlmsghdr*)response; + /* NLMSG_ERROR is sent back for acknowledge (or on error). + * RTM_NEWROUTE is returned when we add a route. + */ + if (hdr->nlmsg_type == NLMSG_ERROR || + hdr->nlmsg_type == RTM_NEWROUTE) + { + add_to_package_list(this, response); } /* we are not interested in anything other. - * anyway, move on to the next message */ + * anyway, move on to the next message. + */ continue; } } @@ -464,7 +580,7 @@ static status_t get_spi(private_kernel_interface_t *this, userspi->min = 0xc0000000; userspi->max = 0xcFFFFFFF; - if (send_message(this, hdr, &response) != SUCCESS) + if (send_message(this, hdr, &response, this->xfrm_socket) != SUCCESS) { DBG1(DBG_KNL, "netlink communication failed"); return FAILED; @@ -627,12 +743,12 @@ static status_t add_sa(private_kernel_interface_t *this, * -> does that mean that NAT-T encap doesn't work in transport mode? * 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. */ + * the IPsec checks it marks them "checksum ok" so OA isn't needed. */ rthdr = XFRM_RTA_NEXT(rthdr); } - if (send_message(this, hdr, &response) != SUCCESS) + if (send_message(this, hdr, &response, this->xfrm_socket) != SUCCESS) { DBG1(DBG_KNL, "netlink communication failed"); return FAILED; @@ -684,7 +800,7 @@ static status_t update_sa( sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; sa_id->family = dst->get_family(dst); - if (send_message(this, hdr, &update) != SUCCESS) + if (send_message(this, hdr, &update, this->xfrm_socket) != SUCCESS) { DBG1(DBG_KNL, "netlink communication failed"); return FAILED; @@ -744,7 +860,7 @@ static status_t update_sa( } } - if (send_message(this, update, &response) != SUCCESS) + if (send_message(this, update, &response, this->xfrm_socket) != SUCCESS) { DBG1(DBG_KNL, "netlink communication failed"); free(update); @@ -798,7 +914,7 @@ static status_t query_sa(private_kernel_interface_t *this, host_t *dst, sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; sa_id->family = dst->get_family(dst); - if (send_message(this, hdr, &response) != SUCCESS) + if (send_message(this, hdr, &response, this->xfrm_socket) != SUCCESS) { DBG1(DBG_KNL, "netlink communication failed"); return FAILED; @@ -850,7 +966,7 @@ static status_t del_sa(private_kernel_interface_t *this, host_t *dst, sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; sa_id->family = dst->get_family(dst); - if (send_message(this, hdr, &response) != SUCCESS) + if (send_message(this, hdr, &response, this->xfrm_socket) != SUCCESS) { DBG1(DBG_KNL, "netlink communication failed"); return FAILED; @@ -880,7 +996,7 @@ static void ts2subnet(traffic_selector_t* ts, /* there is no way to do this cleanly, as the address range may * be anything else but a subnet. We use from_addr as subnet * and try to calculate a usable subnet mask. - */ + */ int byte, bit; bool found = FALSE; chunk_t from, to; @@ -891,7 +1007,8 @@ static void ts2subnet(traffic_selector_t* ts, *mask = (size * 8); /* go trough all bits of the addresses, beginning in the front. - * As longer as they equal, the subnet gets larger */ + * as long as they are equal, the subnet gets larger + */ for (byte = 0; byte < size; byte++) { for (bit = 7; bit >= 0; bit--) @@ -963,6 +1080,106 @@ static struct xfrm_selector ts2selector(traffic_selector_t *src, } /** + * Tries to find an ip address of a local interface that is included in the + * supplied traffic selector. + */ +static status_t find_addr_by_ts(traffic_selector_t *ts, host_t **ip) +{ + host_t *try = NULL; + +#ifdef HAVE_GETIFADDRS + struct ifaddrs *list; + struct ifaddrs *cur; + + if (getifaddrs(&list) < 0) + { + return FAILED; + } + + for (cur = list; cur != NULL; cur = cur->ifa_next) + { + if (!(cur->ifa_flags & IFF_UP) || !cur->ifa_addr) + { + /* ignore interfaces which are down or have no address assigned */ + continue; + } + + try = host_create_from_sockaddr(cur->ifa_addr); + + if (try && ts->includes(ts, try)) + { + if (ip) + { + *ip = try; + } + freeifaddrs(list); + return SUCCESS; + } + + DESTROY_IF(try); + } + freeifaddrs(list); + return FAILED; +#else /* !HAVE_GETIFADDRS */ + + /* only IPv4 supported yet */ + if (ts->get_type != TS_IPV4_ADDR_RANGE) + { + return FAILED; + } + + int skt = socket(PF_INET, SOCK_DGRAM, 0); + struct ifconf conf; + struct ifreq reqs[16]; + + conf.ifc_len = sizeof(reqs); + conf.ifc_req = reqs; + + if (ioctl(skt, SIOCGIFCONF, &conf) == -1) + { + DBG1(DBG_NET, "checking address using ioctl() failed: %m"); + close(skt); + return FAILED; + } + close(skt); + + while (conf.ifc_len >= sizeof(struct ifreq)) + { + /* only IPv4 supported yet */ + if (conf.ifc_req->ifr_addr.sa_family != AF_INET) + { + continue; + } + + try = host_create_from_sockaddr(conf.ifc_req->ifr_addr); + + if (try && ts->includes(ts, try)) + { + if (ip) + { + *ip = try; + } + + return SUCCESS; + } + + DESTROY_IF(try); + + conf.ifc_len -= sizeof(struct ifreq); + conf.ifc_req++; + } + return FAILED; +#endif /* HAVE_GETIFADDRS */ +} + +/** + * forward declarations + */ +static status_t manage_srcroute(private_kernel_interface_t*,int,int,rt_refcount_t*); +static int get_iface(private_kernel_interface_t*,host_t*); +static void rt_refcount_destroy(rt_refcount_t*); + +/** * Implementation of kernel_interface_t.add_policy. */ static status_t add_policy(private_kernel_interface_t *this, @@ -980,7 +1197,6 @@ static status_t add_policy(private_kernel_interface_t *this, struct nlmsghdr *response; struct xfrm_userpolicy_info *policy_info; struct nlmsghdr *hdr; - status_t status = SUCCESS; /* create a policy */ policy = malloc_thing(kernel_policy_t); @@ -1075,7 +1291,7 @@ static status_t add_policy(private_kernel_interface_t *this, host2xfrm(src, &tmpl->saddr); host2xfrm(dst, &tmpl->id.daddr); - if (send_message(this, hdr, &response) != SUCCESS) + if (send_message(this, hdr, &response, this->xfrm_socket) != SUCCESS) { DBG1(DBG_KNL, "netlink communication failed"); return FAILED; @@ -1083,17 +1299,45 @@ static status_t add_policy(private_kernel_interface_t *this, else if (response->nlmsg_type != NLMSG_ERROR) { DBG1(DBG_KNL, "netlink request XFRM_MSG_UPDPOLICY not acknowledged"); - status = FAILED; + free(response); + return FAILED; } else if (((struct nlmsgerr*)NLMSG_DATA(response))->error) { DBG1(DBG_KNL, "netlink request XFRM_MSG_UPDPOLICY got an error: %s", strerror(-((struct nlmsgerr*)NLMSG_DATA(response))->error)); - status = FAILED; + free(response); + return FAILED; } + + if (direction == POLICY_FWD) + { + policy->route = malloc_thing(rt_refcount_t); + if (find_addr_by_ts(dst_ts, &policy->route->src_ip) == SUCCESS) + { + policy->route->if_index = get_iface(this, src); + policy->route->dst_net = chunk_alloc(policy->sel.family == AF_INET ? 4 : 16); + memcpy(policy->route->dst_net.ptr, &policy->sel.saddr, policy->route->dst_net.len); + policy->route->prefixlen = policy->sel.prefixlen_s; + + if (manage_srcroute(this, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, + policy->route) != SUCCESS) + { + DBG1(DBG_KNL, "error installing route"); + rt_refcount_destroy(policy->route); + policy->route = NULL; + } + } + else + { + free(policy->route); + policy->route = NULL; + } + } + free(response); - return status; + return SUCCESS; } /** @@ -1124,7 +1368,7 @@ static status_t query_policy(private_kernel_interface_t *this, policy_id->sel = ts2selector(src_ts, dst_ts); policy_id->dir = direction; - if (send_message(this, hdr, &response) != SUCCESS) + if (send_message(this, hdr, &response, this->xfrm_socket) != SUCCESS) { DBG1(DBG_KNL, "netlink communication failed"); return FAILED; @@ -1166,12 +1410,12 @@ static status_t del_policy(private_kernel_interface_t *this, policy_dir_t direction) { kernel_policy_t *current, policy, *to_delete = NULL; + rt_refcount_t *route; unsigned char request[BUFFER_SIZE]; struct nlmsghdr *response; struct nlmsghdr *hdr; struct xfrm_userpolicy_id *policy_id; iterator_t *iterator; - status_t status = SUCCESS; DBG2(DBG_KNL, "deleting policy"); @@ -1221,9 +1465,10 @@ static status_t del_policy(private_kernel_interface_t *this, policy_id->sel = to_delete->sel; policy_id->dir = direction; + route = to_delete->route; free(to_delete); - if (send_message(this, hdr, &response) != SUCCESS) + if (send_message(this, hdr, &response, this->xfrm_socket) != SUCCESS) { DBG1(DBG_KNL, "netlink communication failed"); return FAILED; @@ -1231,27 +1476,347 @@ static status_t del_policy(private_kernel_interface_t *this, else if (response->nlmsg_type != NLMSG_ERROR) { DBG1(DBG_KNL, "netlink request XFRM_MSG_DELPOLICY not acknowledged"); - status = FAILED; + free(response); + return FAILED; } else if (((struct nlmsgerr*)NLMSG_DATA(response))->error) { DBG1(DBG_KNL, "netlink request XFRM_MSG_DELPOLICY got an error: %s", strerror(-((struct nlmsgerr*)NLMSG_DATA(response))->error)); - status = FAILED; + free(response); + return FAILED; + } + + if (route) + { + if (manage_srcroute(this, RTM_DELROUTE, 0, route) != SUCCESS) + { + DBG1(DBG_KNL, "error uninstalling route"); + } + rt_refcount_destroy(route); } free(response); - return status; + return SUCCESS; +} + +/** + * Sends an RT_NETLINK request to the kernel. + */ +static status_t send_rtrequest(private_kernel_interface_t *this, struct nlmsghdr *hdr) +{ + struct nlmsghdr *response; + + if (send_message(this, hdr, &response, this->rt_socket) != SUCCESS) + { + DBG1(DBG_KNL, "netlink communication failed"); + return FAILED; + } + else if (((struct nlmsgerr*)NLMSG_DATA(response))->error) + { + DBG1(DBG_KNL, "netlink request got an error: %s (%d)", + strerror(-((struct nlmsgerr*)NLMSG_DATA(response))->error), + -((struct nlmsgerr*)NLMSG_DATA(response))->error); + free(response); + return FAILED; + } + + free(response); + return SUCCESS; +} + +/** + * Creates an rtattr and adds it to the netlink message. + */ +static status_t add_rtattr(struct nlmsghdr *hdr, int max_len, + int rta_type, void *data, int data_len) +{ + struct rtattr *rta; + + if (NLMSG_ALIGN(hdr->nlmsg_len) + RTA_ALIGN(data_len) > max_len) + { + DBG1(DBG_KNL, "netlink message exceeded bound of %d", max_len); + return FAILED; + } + + rta = (struct rtattr*)(((char*)hdr) + NLMSG_ALIGN(hdr->nlmsg_len)); + + rta->rta_type = rta_type; + rta->rta_len = RTA_LENGTH(data_len); + memcpy(RTA_DATA(rta), data, data_len); + + hdr->nlmsg_len = NLMSG_ALIGN(hdr->nlmsg_len) + rta->rta_len; + return SUCCESS; +} + +/** + * Manages the creation and deletion of ip addresses on an interface. + * By setting the appropriate nlmsg_type, the ip will be set or unset. + */ +static status_t manage_ipaddr(private_kernel_interface_t *this, int nlmsg_type, + int flags, int if_index, host_t *ip) +{ + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *hdr; + struct ifaddrmsg *msg; + chunk_t chunk; + + DBG2(DBG_KNL, "adding virtual IP %H to interface %d", ip, if_index); + + memset(&request, 0, sizeof(request)); + + chunk = ip->get_address(ip); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags; + hdr->nlmsg_type = nlmsg_type; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + + msg = (struct ifaddrmsg*)NLMSG_DATA(hdr); + msg->ifa_family = ip->get_family(ip); + msg->ifa_flags = 0; + msg->ifa_prefixlen = 8 * chunk.len; + msg->ifa_scope = RT_SCOPE_UNIVERSE; + msg->ifa_index = if_index; + + if (add_rtattr(hdr, sizeof(request), IFA_LOCAL, + chunk.ptr, chunk.len) != SUCCESS) + { + return FAILED; + } + + return send_rtrequest(this, hdr); +} + +static int get_iface(private_kernel_interface_t *this, host_t* ip) +{ + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *hdr; + struct rtmsg *msg; + struct rtattr* rta; + chunk_t chunk; + int ifindex = 0; + + DBG2(DBG_KNL, "getting interface for %H", ip); + + memset(&request, 0, sizeof(request)); + + chunk = ip->get_address(ip); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST; + hdr->nlmsg_type = RTM_GETROUTE; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + + msg = (struct rtmsg*)NLMSG_DATA(hdr); + msg->rtm_family = ip->get_family(ip); + msg->rtm_table = 0; + msg->rtm_protocol = 0; + msg->rtm_scope = 0; + msg->rtm_type = 0; + msg->rtm_src_len = 0; + msg->rtm_dst_len = 8 * chunk.len; + msg->rtm_tos = 0; + msg->rtm_flags = RT_TABLE_UNSPEC | RTPROT_UNSPEC; + + if (add_rtattr(hdr, sizeof(request), RTA_DST, + chunk.ptr, chunk.len) != SUCCESS) + { + return 0; + } + + if(send_message(this, hdr, &hdr, this->rt_socket) != SUCCESS) + { + return 0; + } + rta = (struct rtattr*)(NLMSG_DATA(hdr) + NLMSG_LENGTH(sizeof(struct rtmsg))); + + while(RTA_OK(rta, hdr->nlmsg_len)) + { + if(rta->rta_type == RTA_OIF) + { + ifindex = *((int*)RTA_DATA(rta)); + break; + } + rta = RTA_NEXT(rta, hdr->nlmsg_len); + } + free(hdr); + if (ifindex == 0) + { + DBG1(DBG_KNL, "address %H not reachable, unable to get interface", ip); + } + return ifindex; +} + +/** + * Manages source routes in the routing table. + * By setting the appropriate nlmsg_type, the route will be set or unset. + */ +static status_t manage_srcroute(private_kernel_interface_t *this, + int nlmsg_type, int flags, rt_refcount_t *route) +{ + struct nlmsghdr *hdr; + struct rtmsg *msg; + unsigned char request[BUFFER_SIZE]; + chunk_t src; + + memset(&request, 0, sizeof(request)); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags; + hdr->nlmsg_type = nlmsg_type; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + + 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 = RT_TABLE_MAIN; + msg->rtm_protocol = RTPROT_STATIC; + msg->rtm_type = RTN_UNICAST; + msg->rtm_scope = RT_SCOPE_UNIVERSE; + + src = route->src_ip->get_address(route->src_ip); + + if (add_rtattr(hdr, sizeof(request), RTA_DST, + route->dst_net.ptr, route->dst_net.len) != SUCCESS) + { + return FAILED; + } + + if (add_rtattr(hdr, sizeof(request), RTA_PREFSRC, + src.ptr, src.len) != SUCCESS) + { + return FAILED; + } + + if (add_rtattr(hdr, sizeof(request), RTA_OIF, + &route->if_index, sizeof(route->if_index)) != SUCCESS) + { + return FAILED; + } + + return send_rtrequest(this, hdr); +} + +/** + * destroy an rt_refcount object + */ +static void rt_refcount_destroy(rt_refcount_t *this) +{ + this->src_ip->destroy(this->src_ip); + chunk_free(&this->dst_net); + free(this); +} + +/** + * destroy a vip_refcount object + */ +static void vip_refcount_destroy(vip_refcount_t *this) +{ + this->ip->destroy(this->ip); + free(this); +} + +/** + * Implementation of kernel_interface_t.add_ip. + */ +static status_t add_ip(private_kernel_interface_t *this, + host_t *virtual_ip, host_t *dst_ip) +{ + int targetif; + vip_refcount_t *listed; + iterator_t *iterator; + + DBG2(DBG_KNL, "adding ip addr: %H", virtual_ip); + + targetif = get_iface(this, dst_ip); + if (targetif == 0) + { + return FAILED; + } + + /* beware of deadlocks (e.g. send/receive packets while holding the lock) */ + iterator = this->vips->create_iterator_locked(this->vips, &(this->vip_mutex)); + while (iterator->iterate(iterator, (void**)&listed)) + { + if (listed->if_index == targetif && + virtual_ip->ip_equals(virtual_ip, listed->ip)) + { + listed->refcount++; + iterator->destroy(iterator); + return SUCCESS; + } + } + iterator->destroy(iterator); + + if (manage_ipaddr(this, RTM_NEWADDR, NLM_F_CREATE | NLM_F_EXCL, + targetif, virtual_ip) == SUCCESS) + { + listed = malloc_thing(vip_refcount_t); + listed->ip = virtual_ip->clone(virtual_ip); + listed->if_index = targetif; + listed->refcount = 1; + this->vips->insert_last(this->vips, listed); + return SUCCESS; + } + + return FAILED; +} + +/** + * Implementation of kernel_interface_t.del_ip. + */ +static status_t del_ip(private_kernel_interface_t *this, + host_t *virtual_ip, host_t *dst_ip) +{ + int targetif; + vip_refcount_t *listed; + iterator_t *iterator; + + DBG2(DBG_KNL, "deleting ip addr: %H", virtual_ip); + + targetif = get_iface(this, dst_ip); + if (targetif == 0) + { + return FAILED; + } + + /* beware of deadlocks (e.g. send/receive packets while holding the lock) */ + iterator = this->vips->create_iterator_locked(this->vips, &(this->vip_mutex)); + while (iterator->iterate(iterator, (void**)&listed)) + { + if (listed->if_index == targetif && + virtual_ip->ip_equals(virtual_ip, listed->ip)) + { + listed->refcount--; + if (listed->refcount == 0) + { + iterator->remove(iterator); + vip_refcount_destroy(listed); + iterator->destroy(iterator); + return manage_ipaddr(this, RTM_DELADDR, 0, targetif, virtual_ip); + } + iterator->destroy(iterator); + return SUCCESS; + } + } + iterator->destroy(iterator); + + return FAILED; } /** * Implementation of kernel_interface_t.destroy. */ static void destroy(private_kernel_interface_t *this) -{ - pthread_cancel(this->thread); - pthread_join(this->thread, NULL); - close(this->socket); +{ + pthread_cancel(this->xfrm_thread); + pthread_join(this->xfrm_thread, NULL); + pthread_cancel(this->rt_thread); + pthread_join(this->rt_thread, NULL); + close(this->xfrm_socket); + close(this->rt_socket); + this->vips->destroy_function(this->vips, (void*)vip_refcount_destroy); this->responses->destroy(this->responses); this->policies->destroy(this->policies); free(this); @@ -1262,7 +1827,8 @@ static void destroy(private_kernel_interface_t *this) */ kernel_interface_t *kernel_interface_create() { - struct sockaddr_nl addr; + struct sockaddr_nl addr_xfrm; + struct sockaddr_nl addr_rt; private_kernel_interface_t *this = malloc_thing(private_kernel_interface_t); /* public functions */ @@ -1274,45 +1840,88 @@ kernel_interface_t *kernel_interface_create() 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,bool))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.add_ip = (status_t(*)(kernel_interface_t*,host_t*,host_t*)) add_ip; + this->public.del_ip = (status_t(*)(kernel_interface_t*,host_t*,host_t*)) del_ip; this->public.destroy = (void(*)(kernel_interface_t*)) destroy; /* private members */ this->pid = getpid(); this->responses = linked_list_create(); + this->vips = linked_list_create(); this->policies = linked_list_create(); pthread_mutex_init(&(this->rep_mutex),NULL); pthread_mutex_init(&(this->pol_mutex),NULL); + pthread_mutex_init(&(this->vip_mutex),NULL); pthread_cond_init(&(this->condvar),NULL); this->seq = 0; - /* open netlink socket */ - this->socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_XFRM); - if (this->socket <= 0) + /* open xfrm netlink socket */ + this->xfrm_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_XFRM); + if (this->xfrm_socket <= 0) { - this->responses->destroy(this->responses); - free(this); - charon->kill(charon, "Unable to create netlink socket"); + DBG1(DBG_KNL, "Unable to create xfrm netlink socket"); + goto kill; } - /* bind the socket and reqister for ACQUIRE & EXPIRE */ - addr.nl_family = AF_NETLINK; - addr.nl_pid = getpid(); - addr.nl_groups = XFRMGRP_ACQUIRE | XFRMGRP_EXPIRE; - if (bind(this->socket, (struct sockaddr*)&addr, sizeof(addr)) != 0) + /* bind the xfrm socket and reqister for ACQUIRE & EXPIRE */ + addr_xfrm.nl_family = AF_NETLINK; + addr_xfrm.nl_pid = getpid(); + addr_xfrm.nl_groups = XFRMGRP_ACQUIRE | XFRMGRP_EXPIRE; + if (bind(this->xfrm_socket, (struct sockaddr*)&addr_xfrm, sizeof(addr_xfrm))) { - this->responses->destroy(this->responses); - close(this->socket); - free(this); - charon->kill(charon, "Unable to bind netlink socket"); + DBG1(DBG_KNL, "Unable to bind xfrm netlink socket"); + goto kill_xfrm; } - if (pthread_create(&this->thread, NULL, (void*(*)(void*))receive_messages, this) != 0) + if (pthread_create(&this->xfrm_thread, NULL, + (void*(*)(void*))receive_xfrm_messages, this)) + { + DBG1(DBG_KNL, "Unable to create xfrm netlink thread"); + goto kill_xfrm; + } + + /* open rt netlink socket */ + this->rt_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (this->rt_socket <= 0) + { + DBG1(DBG_KNL, "Unable to create rt netlink socket"); + goto kill_xfrm_all; + } + + /* bind the socket_rt */ + addr_rt.nl_family = AF_NETLINK; + addr_rt.nl_pid = getpid(); + addr_rt.nl_groups = 0; + if (bind(this->rt_socket, (struct sockaddr*)&addr_rt, sizeof(addr_rt))) { - this->responses->destroy(this->responses); - close(this->socket); - free(this); - charon->kill(charon, "Unable to create netlink thread"); + DBG1(DBG_KNL, "Unable to bind rt netlink socket"); + goto kill_rt; } + if (pthread_create(&this->rt_thread, NULL, + (void*(*)(void*))receive_rt_messages, this)) + { + DBG1(DBG_KNL, "Unable to create rt netlink thread"); + goto kill_rt; + } + return &this->public; + +kill_rt: + close(this->rt_socket); +kill_xfrm_all: + pthread_cancel(this->xfrm_thread); + pthread_join(this->xfrm_thread, NULL); +kill_xfrm: + close(this->xfrm_socket); +kill: + this->responses->destroy(this->responses); + this->policies->destroy(this->policies); + this->vips->destroy(this->vips); + free(this); + charon->kill(charon, "Unable to create kernel_interface"); + return NULL; } + +/* vim: set ts=4 sw=4 noet: */ + |