kernel-pfroute: refactor PF_ROUTE message processing, use an enumerator

This commit is contained in:
Martin Willi
2013-05-06 16:10:12 +02:00
parent 889efae4cf
commit b1c6b68e4c
@@ -40,9 +40,6 @@
/** delay before firing roam events (ms) */ /** delay before firing roam events (ms) */
#define ROAM_DELAY 100 #define ROAM_DELAY 100
/** buffer size for PF_ROUTE messages */
#define PFROUTE_BUFFER_SIZE 4096
typedef struct addr_entry_t addr_entry_t; typedef struct addr_entry_t addr_entry_t;
/** /**
@@ -290,33 +287,91 @@ static void fire_roam_event(private_kernel_pfroute_net_t *this, bool address)
} }
} }
/**
* Data for enumerator over rtmsg sockaddrs
*/
typedef struct {
/** implements enumerator */
enumerator_t public;
/** copy of attribute bitfield */
int types;
/** bytes remaining in buffer */
int remaining;
/** next sockaddr to enumerate */
struct sockaddr *addr;
} rt_enumerator_t;
METHOD(enumerator_t, rt_enumerate, bool,
rt_enumerator_t *this, int *xtype, struct sockaddr **addr)
{
int i, type;
if (this->remaining < sizeof(this->addr->sa_len) ||
this->remaining < this->addr->sa_len)
{
return FALSE;
}
for (i = 0; i < RTAX_MAX; i++)
{
type = (1 << i);
if (this->types & type)
{
this->types &= ~type;
*addr = this->addr;
*xtype = i;
this->remaining -= this->addr->sa_len;
this->addr = (void*)this->addr + this->addr->sa_len;
return TRUE;
}
}
return FALSE;
}
/**
* Create a safe enumerator over sockaddrs in ifa/ifam/rt_msg
*/
static enumerator_t *create_rtmsg_enumerator(void *hdr, size_t hdrlen)
{
struct rt_msghdr *rthdr = hdr;
rt_enumerator_t *this;
INIT(this,
.public = {
.enumerate = (void*)_rt_enumerate,
.destroy = (void*)free,
},
.types = rthdr->rtm_addrs,
.remaining = rthdr->rtm_msglen - hdrlen,
.addr = hdr + hdrlen,
);
return &this->public;
}
/** /**
* Process an RTM_*ADDR message from the kernel * Process an RTM_*ADDR message from the kernel
*/ */
static void process_addr(private_kernel_pfroute_net_t *this, static void process_addr(private_kernel_pfroute_net_t *this,
struct rt_msghdr *msg) struct ifa_msghdr *ifa)
{ {
struct ifa_msghdr *ifa = (struct ifa_msghdr*)msg; struct sockaddr *sockaddr;
sockaddr_t *sockaddr = (sockaddr_t*)(ifa + 1);
host_t *host = NULL; host_t *host = NULL;
enumerator_t *ifaces, *addrs; enumerator_t *ifaces, *addrs;
iface_entry_t *iface; iface_entry_t *iface;
addr_entry_t *addr; addr_entry_t *addr;
bool found = FALSE, changed = FALSE, roam = FALSE; bool found = FALSE, changed = FALSE, roam = FALSE;
int i; enumerator_t *enumerator;
int type;
for (i = 1; i < (1 << RTAX_MAX); i <<= 1) enumerator = create_rtmsg_enumerator(ifa, sizeof(*ifa));
while (enumerator->enumerate(enumerator, &type, &sockaddr))
{ {
if (ifa->ifam_addrs & i) if (type == RTAX_IFA)
{ {
if (RTA_IFA & i) host = host_create_from_sockaddr(sockaddr);
{ break;
host = host_create_from_sockaddr(sockaddr);
break;
}
sockaddr = (sockaddr_t*)((char*)sockaddr + sockaddr->sa_len);
} }
} }
enumerator->destroy(enumerator);
if (!host) if (!host)
{ {
@@ -391,9 +446,8 @@ static void process_addr(private_kernel_pfroute_net_t *this,
* Process an RTM_IFINFO message from the kernel * Process an RTM_IFINFO message from the kernel
*/ */
static void process_link(private_kernel_pfroute_net_t *this, static void process_link(private_kernel_pfroute_net_t *this,
struct rt_msghdr *hdr) struct if_msghdr *msg)
{ {
struct if_msghdr *msg = (struct if_msghdr*)hdr;
enumerator_t *enumerator; enumerator_t *enumerator;
iface_entry_t *iface; iface_entry_t *iface;
bool roam = FALSE; bool roam = FALSE;
@@ -440,17 +494,23 @@ static void process_route(private_kernel_pfroute_net_t *this,
} }
/** /**
* Receives events from kernel * Receives PF_ROUTE messages from kernel
*/ */
static job_requeue_t receive_events(private_kernel_pfroute_net_t *this) static job_requeue_t receive_events(private_kernel_pfroute_net_t *this)
{ {
unsigned char buf[PFROUTE_BUFFER_SIZE]; struct {
struct rt_msghdr *msg = (struct rt_msghdr*)buf; union {
int len; struct rt_msghdr rtm;
struct if_msghdr ifm;
struct ifa_msghdr ifam;
};
char buf[sizeof(struct sockaddr_storage) * RTAX_MAX];
} msg;
int len, hdrlen;
bool oldstate; bool oldstate;
oldstate = thread_cancelability(TRUE); oldstate = thread_cancelability(TRUE);
len = recvfrom(this->socket, buf, sizeof(buf), 0, NULL, 0); len = recv(this->socket, &msg, sizeof(msg), 0);
thread_cancelability(oldstate); thread_cancelability(oldstate);
if (len < 0) if (len < 0)
@@ -458,10 +518,7 @@ static job_requeue_t receive_events(private_kernel_pfroute_net_t *this)
switch (errno) switch (errno)
{ {
case EINTR: case EINTR:
/* interrupted, try again */
return JOB_REQUEUE_DIRECT;
case EAGAIN: case EAGAIN:
/* no data ready, select again */
return JOB_REQUEUE_DIRECT; return JOB_REQUEUE_DIRECT;
default: default:
DBG1(DBG_KNL, "unable to receive from PF_ROUTE event socket"); DBG1(DBG_KNL, "unable to receive from PF_ROUTE event socket");
@@ -470,30 +527,55 @@ static job_requeue_t receive_events(private_kernel_pfroute_net_t *this)
} }
} }
if (len < sizeof(*msg) || len < msg->rtm_msglen || if (len < offsetof(struct rt_msghdr, rtm_flags) || len < msg.rtm.rtm_msglen)
msg->rtm_version != RTM_VERSION)
{ {
DBG2(DBG_KNL, "received corrupted PF_ROUTE message"); DBG1(DBG_KNL, "received invalid PF_ROUTE message");
return JOB_REQUEUE_DIRECT; return JOB_REQUEUE_DIRECT;
} }
if (msg.rtm.rtm_version != RTM_VERSION)
switch (msg->rtm_type) {
DBG1(DBG_KNL, "received PF_ROUTE message with unsupported version: %d",
msg.rtm.rtm_version);
return JOB_REQUEUE_DIRECT;
}
switch (msg.rtm.rtm_type)
{ {
case RTM_NEWADDR: case RTM_NEWADDR:
case RTM_DELADDR: case RTM_DELADDR:
process_addr(this, msg); hdrlen = sizeof(msg.ifam);
break; break;
case RTM_IFINFO: case RTM_IFINFO:
/*case RTM_IFANNOUNCE <- what about this*/ hdrlen = sizeof(msg.ifm);
process_link(this, msg);
break; break;
case RTM_ADD: case RTM_ADD:
case RTM_DELETE: case RTM_DELETE:
process_route(this, msg); case RTM_GET:
hdrlen = sizeof(msg.rtm);
break;
default:
return JOB_REQUEUE_DIRECT;
}
if (msg.rtm.rtm_msglen < hdrlen)
{
DBG1(DBG_KNL, "ignoring short PF_ROUTE message");
return JOB_REQUEUE_DIRECT;
}
switch (msg.rtm.rtm_type)
{
case RTM_NEWADDR:
case RTM_DELADDR:
process_addr(this, &msg.ifam);
break;
case RTM_IFINFO:
process_link(this, &msg.ifm);
break;
case RTM_ADD:
case RTM_DELETE:
process_route(this, &msg.rtm);
break;
default: default:
break; break;
} }
return JOB_REQUEUE_DIRECT; return JOB_REQUEUE_DIRECT;
} }