implemented get_iface() using RTM_GETADDR

added support for multi-header netlink messages
really ugly now, need a lot of refactoring
This commit is contained in:
Martin Willi
2007-03-02 17:08:38 +00:00
parent ebc59e3bb8
commit 1d51abb71b
2 changed files with 181 additions and 75 deletions
+3 -3
View File
@@ -1620,11 +1620,11 @@ static void set_virtual_ip(private_ike_sa_t *this, bool local, host_t *ip)
DBG1(DBG_IKE, "removing old virtual IP %H", this->my_virtual_ip);
charon->kernel_interface->del_ip(charon->kernel_interface,
this->my_virtual_ip,
this->other_host);
this->my_host);
this->my_virtual_ip->destroy(this->my_virtual_ip);
}
if (charon->kernel_interface->add_ip(charon->kernel_interface, ip,
this->other_host) == SUCCESS)
this->my_host) == SUCCESS)
{
this->my_virtual_ip = ip->clone(ip);
}
@@ -1848,7 +1848,7 @@ static void destroy(private_ike_sa_t *this)
if (this->my_virtual_ip)
{
charon->kernel_interface->del_ip(charon->kernel_interface,
this->my_virtual_ip, this->other_host);
this->my_virtual_ip, this->my_host);
this->my_virtual_ip->destroy(this->my_virtual_ip);
}
DESTROY_IF(this->other_virtual_ip);
+178 -72
View File
@@ -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);
}
/**
* 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)
{
int len, addr_len;
struct sockaddr_nl addr;
chunk_t result = chunk_empty, tmp;
struct nlmsghdr *msg, peek;
static int seq = 200;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
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;
while (TRUE)
{
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;
}
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;
}
*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;
struct rtmsg *msg;
struct rtattr* rta;
chunk_t chunk;
struct nlmsghdr *hdr, *tofree;
struct rtgenmsg *msg;
int ifindex = 0;
DBG2(DBG_KNL, "getting interface for %H", ip);
size_t len;
chunk_t target, current;
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));
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 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)
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)
{
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)));
DBG1(DBG_KNL, "listing attributes:");
while(RTA_OK(rta, hdr->nlmsg_len))
{
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");