diff options
Diffstat (limited to 'src/charon/threads/kernel_interface.c')
-rw-r--r-- | src/charon/threads/kernel_interface.c | 248 |
1 files changed, 177 insertions, 71 deletions
diff --git a/src/charon/threads/kernel_interface.c b/src/charon/threads/kernel_interface.c index 1da82c049..b7460edb9 100644 --- a/src/charon/threads/kernel_interface.c +++ b/src/charon/threads/kernel_interface.c @@ -308,30 +308,24 @@ static status_t send_message(private_kernel_interface_t *this, addr.nl_pid = 0; addr.nl_groups = 0; - /* - // set timeout to 10 secs - struct timespec tm; - tm.tv_sec = 10; - */ - 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); + DBG1(DBG_KNL, "error sending to netlink socket: %m"); return FAILED; } pthread_mutex_lock(&(this->rep_mutex)); + DBG3(DBG_KNL, "waiting for netlink message with seq: %d", + request->nlmsg_seq); + while (TRUE) { iterator_t *iterator; @@ -358,8 +352,6 @@ 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)); } @@ -368,6 +360,8 @@ static status_t send_message(private_kernel_interface_t *this, return SUCCESS; } +static int supersocket; + /** * Reads from a netlink socket and returns the message in a buffer. */ @@ -379,13 +373,14 @@ static void netlink_package_receiver(int socket, unsigned char *response, int re 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) { if (errno == EINTR) { /* interrupted, try again */ + DBG1(DBG_IKE, "wtf1"); continue; } charon->kill(charon, "receiving from netlink socket failed\n"); @@ -498,9 +493,9 @@ static void receive_xfrm_messages(private_kernel_interface_t *this) * 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) + else if (hdr->nlmsg_type == NLMSG_ERROR || + hdr->nlmsg_type == XFRM_MSG_NEWSA || + hdr->nlmsg_type == XFRM_MSG_NEWPOLICY) { add_to_package_list(this, response); } @@ -518,16 +513,19 @@ static void receive_rt_messages(private_kernel_interface_t *this) { while(TRUE) { - unsigned char response[BUFFER_SIZE]; + unsigned char response[BUFFER_SIZE*3]; struct nlmsghdr *hdr; - netlink_package_receiver(this->rt_socket,response,BUFFER_SIZE); + supersocket = this->rt_socket; + netlink_package_receiver(this->rt_socket,response, sizeof(response)); 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) + hdr->nlmsg_type == RTM_NEWROUTE || + hdr->nlmsg_type == RTM_NEWLINK || + hdr->nlmsg_type == RTM_NEWADDR) { add_to_package_list(this, response); } @@ -1338,7 +1336,7 @@ static status_t add_policy(private_kernel_interface_t *this, 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->if_index = get_iface(this, dst); 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; @@ -1610,65 +1608,173 @@ static status_t manage_ipaddr(private_kernel_interface_t *this, int nlmsg_type, return send_rtrequest(this, hdr); } -static int get_iface(private_kernel_interface_t *this, host_t* ip) +/** + * send a netlink message and wait for a reply + */ +static status_t netlink_send(int socket, struct nlmsghdr *in, + struct nlmsghdr **out, size_t *out_len) { - 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); + int len, addr_len; + struct sockaddr_nl addr; + chunk_t result = chunk_empty, tmp; + struct nlmsghdr *msg, peek; - memset(&request, 0, sizeof(request)); + static int seq = 200; + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - 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; + pthread_mutex_lock(&mutex); + + in->nlmsg_seq = ++seq; + in->nlmsg_pid = getpid(); + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_pid = 0; + addr.nl_groups = 0; - if (add_rtattr(hdr, sizeof(request), RTA_DST, - chunk.ptr, chunk.len) != SUCCESS) + while (TRUE) { - return 0; + len = sendto(socket, in, in->nlmsg_len, 0, + (struct sockaddr*)&addr, sizeof(addr)); + + if (len != in->nlmsg_len) + { + if (errno == EINTR) + { + /* interrupted, try again */ + continue; + } + pthread_mutex_unlock(&mutex); + DBG1(DBG_KNL, "error sending to netlink socket: %m"); + return FAILED; + } + break; } - - if(send_message(this, hdr, &hdr, this->rt_socket) != SUCCESS) - { - return 0; + + for(;;) + { + tmp = chunk_alloca(2048); + msg = (struct nlmsghdr*)tmp.ptr; + + len = recvfrom(socket, tmp.ptr, tmp.len, 0, + (struct sockaddr*)&addr, &addr_len); + if (len < 0) + { + if (errno == EINTR) + { + /* interrupted, try again */ + continue; + } + DBG1(DBG_IKE, "error reading from netlink socket: %m"); + pthread_mutex_unlock(&mutex); + return FAILED; + } + if (!NLMSG_OK(msg, len)) + { + DBG1(DBG_IKE, "received corrupted netlink message"); + pthread_mutex_unlock(&mutex); + return FAILED; + } + if (msg->nlmsg_seq != seq) + { + DBG1(DBG_IKE, "received invalid netlink sequence number"); + if (msg->nlmsg_seq < seq) + { + continue; + } + pthread_mutex_unlock(&mutex); + return FAILED; + } + + tmp.len = len; + result = chunk_cata("cc", result, tmp); + + /* NLM_F_MULTI flag does not seem to be set correctly, we use sequence + * numbers to detect multi header messages */ + len = recvfrom(socket, &peek, sizeof(peek), MSG_PEEK | MSG_DONTWAIT, + (struct sockaddr*)&addr, &addr_len); + + if (len == sizeof(peek) && peek.nlmsg_seq == seq) + { + /* seems to be multipart */ + continue; + } + break; } - rta = (struct rtattr*)(NLMSG_DATA(hdr) + NLMSG_LENGTH(sizeof(struct rtmsg))); + + *out_len = result.len; + *out = (struct nlmsghdr*)clalloc(result.ptr, result.len); + + pthread_mutex_unlock(&mutex); + + return SUCCESS; +} + +static int get_iface(private_kernel_interface_t *this, host_t* ip) +{ + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *hdr, *tofree; + struct rtgenmsg *msg; + int ifindex = 0; + size_t len; + chunk_t target, current; + + memset(&request, 0, sizeof(request)); - DBG1(DBG_KNL, "listing attributes:"); - while(RTA_OK(rta, hdr->nlmsg_len)) + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); + hdr->nlmsg_type = RTM_GETADDR; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH | NLM_F_ROOT; + + msg = (struct rtgenmsg*)NLMSG_DATA(hdr); + msg->rtgen_family = AF_UNSPEC; + + target = ip->get_address(ip); + + if (netlink_send(this->rt_socket, hdr, &hdr, &len) == SUCCESS) { - DBG1(DBG_KNL, " found rtattr: %d, data %b", rta->rta_type, - RTA_DATA(rta), rta->rta_len - 4); - if(rta->rta_type == RTA_OIF) + tofree = hdr; + while (NLMSG_OK(hdr, len)) { - ifindex = *((int*)RTA_DATA(rta)); + switch (hdr->nlmsg_type) + { + case RTM_NEWADDR: + { + struct ifaddrmsg* msg = (struct ifaddrmsg*)(NLMSG_DATA(hdr)); + struct rtattr *rta = IFA_RTA(msg); + size_t rtasize = IFA_PAYLOAD (hdr); + + while(RTA_OK(rta, rtasize)) + { + if (rta->rta_type == IFA_ADDRESS) + { + current.len = rta->rta_len - 4; + current.ptr = RTA_DATA(rta); + if (chunk_equals(current, target)) + { + ifindex = msg->ifa_index; + break; + } + } + rta = RTA_NEXT(rta, rtasize); + } + hdr = NLMSG_NEXT(hdr, len); + continue; + } + default: + hdr = NLMSG_NEXT(hdr, len); + continue; + case NLMSG_DONE: + break; + } break; } - rta = RTA_NEXT(rta, hdr->nlmsg_len); + free(tofree); } - free(hdr); - if (ifindex == 0) + else { - DBG1(DBG_KNL, "address %H not reachable, unable to get interface", ip); + DBG1(DBG_IKE, "unable to get interface address for %H", ip); } return ifindex; } @@ -1765,7 +1871,7 @@ static void vip_refcount_destroy(vip_refcount_t *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) + host_t *virtual_ip, host_t *iface_ip) { int targetif; vip_refcount_t *listed; @@ -1773,7 +1879,7 @@ static status_t add_ip(private_kernel_interface_t *this, DBG2(DBG_KNL, "adding ip addr: %H", virtual_ip); - targetif = get_iface(this, dst_ip); + targetif = get_iface(this, iface_ip); if (targetif == 0) { return FAILED; @@ -1811,7 +1917,7 @@ static status_t add_ip(private_kernel_interface_t *this, * 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) + host_t *virtual_ip, host_t *iface_ip) { int targetif; vip_refcount_t *listed; @@ -1819,7 +1925,7 @@ static status_t del_ip(private_kernel_interface_t *this, DBG2(DBG_KNL, "deleting ip addr: %H", virtual_ip); - targetif = get_iface(this, dst_ip); + targetif = get_iface(this, iface_ip); if (targetif == 0) { return FAILED; @@ -1909,7 +2015,7 @@ kernel_interface_t *kernel_interface_create() /* bind the xfrm socket and reqister for ACQUIRE & EXPIRE */ addr_xfrm.nl_family = AF_NETLINK; - addr_xfrm.nl_pid = getpid(); + addr_xfrm.nl_pid = 0; addr_xfrm.nl_groups = XFRMGRP_ACQUIRE | XFRMGRP_EXPIRE; if (bind(this->xfrm_socket, (struct sockaddr*)&addr_xfrm, sizeof(addr_xfrm))) { @@ -1934,7 +2040,7 @@ kernel_interface_t *kernel_interface_create() /* bind the socket_rt */ addr_rt.nl_family = AF_NETLINK; - addr_rt.nl_pid = getpid(); + addr_rt.nl_pid = 0; addr_rt.nl_groups = 0; if (bind(this->rt_socket, (struct sockaddr*)&addr_rt, sizeof(addr_rt))) { @@ -1942,7 +2048,7 @@ kernel_interface_t *kernel_interface_create() goto kill_rt; } - if (pthread_create(&this->rt_thread, NULL, + if (pthread_create(&this->rt_thread, NULL, (void*(*)(void*))receive_rt_messages, this)) { DBG1(DBG_KNL, "Unable to create rt netlink thread"); |