From 61a6d3e8499367eefff43a2116bd5c00ac5473de Mon Sep 17 00:00:00 2001
From: Masahide NAKAMURA <nakam@linux-ipv6.org>
Date: Sat, 24 Feb 2007 01:46:32 +0900
Subject: [LIBNETLINK]: Sync with iproute2-ss061214.

Sync with the latest libnetlink.
Use {NLMSG,RTA}_SPACE instead of _LENGTH to alloc buffer to fix issue
about out of buffer space which is found with the libnetlink.

All buffer size related libnetlink is reset as default.
You can tune it at libnetlink.c and xfrm.c.
---
 include/libnetlink.h    |   42 +++++-
 libnetlink/libnetlink.c |  341 +++++++++++++++++++++++++++++++++++++++-------
 src/ha.c                |    2 +-
 src/movement.c          |    2 +-
 src/rtnl.c              |  105 +--------------
 src/rtnl.h              |   30 +----
 src/xfrm.c              |   31 ++---
 7 files changed, 352 insertions(+), 201 deletions(-)

diff --git a/include/libnetlink.h b/include/libnetlink.h
index a6960f8..d74c1ad 100644
--- a/include/libnetlink.h
+++ b/include/libnetlink.h
@@ -17,40 +17,72 @@ struct rtnl_handle
 	__u32			dump;
 };
 
+extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions);
+extern int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, int protocol);
 extern void rtnl_close(struct rtnl_handle *rth);
 extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type);
 extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len);
-extern int rtnl_dump_filter(struct rtnl_handle *rth,
-			    int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
+
+typedef int (*rtnl_filter_t)(const struct sockaddr_nl *,
+			     struct nlmsghdr *n, void *);
+extern int rtnl_dump_filter(struct rtnl_handle *rth, rtnl_filter_t filter,
 			    void *arg1,
-			    int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+			    rtnl_filter_t junk,
 			    void *arg2);
 extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
 		     unsigned groups, struct nlmsghdr *answer,
-		     int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+		     rtnl_filter_t junk,
 		     void *jarg);
 
 extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data);
 extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen);
+extern int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len);
 extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data);
-extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen);
+extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, const void *data, int alen);
 
 extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+extern int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+
+#define parse_rtattr_nested(tb, max, rta) \
+	(parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta)))
+
+extern int rtnl_listen(struct rtnl_handle *, rtnl_filter_t handler,
+		       void *jarg);
+
+#define NLMSG_TAIL(nmsg) \
+	((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
 
 #ifndef IFA_RTA
 #define IFA_RTA(r) \
 	((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
 #endif
+#ifndef IFA_PAYLOAD
+#define IFA_PAYLOAD(n)	NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg))
+#endif
 
 #ifndef IFLA_RTA
 #define IFLA_RTA(r) \
 	((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
 #endif
+#ifndef IFLA_PAYLOAD
+#define IFLA_PAYLOAD(n)	NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg))
+#endif
 
 #ifndef NDA_RTA
 #define NDA_RTA(r) \
 	((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
 #endif
+#ifndef NDA_PAYLOAD
+#define NDA_PAYLOAD(n)	NLMSG_PAYLOAD(n,sizeof(struct ndmsg))
+#endif
+
+#ifndef NDTA_RTA
+#define NDTA_RTA(r) \
+	((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndtmsg))))
+#endif
+#ifndef NDTA_PAYLOAD
+#define NDTA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndtmsg))
+#endif
 
 #endif /* __LIBNETLINK_H__ */
 
diff --git a/libnetlink/libnetlink.c b/libnetlink/libnetlink.c
index c8aaeaf..e4f010e 100644
--- a/libnetlink/libnetlink.c
+++ b/libnetlink/libnetlink.c
@@ -25,9 +25,83 @@
 
 #include "libnetlink.h"
 
+#define SO_SNDBUF_SIZE 32768
+#define SO_RCVBUF_SIZE 32768
+#define NL_DUMP_SIZE 16384
+#define NL_TALK_SIZE 16384
+#define NL_LISTEN_SIZE 8192
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define NLDBG(format, arg...) fprintf(stderr, format, ##arg)
+#define NLDBG_SYS(message) perror(message)
+#else
+#define NLDBG(format, arg...)
+#define NLDBG_SYS(message)
+#endif
+
 void rtnl_close(struct rtnl_handle *rth)
 {
-	close(rth->fd);
+	if (rth->fd >= 0) {
+		close(rth->fd);
+		rth->fd = -1;
+	}
+}
+
+int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions,
+		      int protocol)
+{
+	socklen_t addr_len;
+	int sndbuf = SO_SNDBUF_SIZE;
+	int rcvbuf = SO_RCVBUF_SIZE;
+
+	memset(rth, 0, sizeof(rth));
+
+	rth->fd = socket(AF_NETLINK, SOCK_RAW, protocol);
+	if (rth->fd < 0) {
+		NLDBG_SYS("Cannot open netlink socket");
+		return -1;
+	}
+
+	if (setsockopt(rth->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) {
+		NLDBG_SYS("SO_SNDBUF");
+		return -1;
+	}
+
+	if (setsockopt(rth->fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf)) < 0) {
+		NLDBG_SYS("SO_RCVBUF");
+		return -1;
+	}
+
+	memset(&rth->local, 0, sizeof(rth->local));
+	rth->local.nl_family = AF_NETLINK;
+	rth->local.nl_groups = subscriptions;
+
+	if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
+		NLDBG_SYS("Cannot bind netlink socket");
+		return -1;
+	}
+	addr_len = sizeof(rth->local);
+	if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
+		NLDBG_SYS("Cannot getsockname");
+		return -1;
+	}
+	if (addr_len != sizeof(rth->local)) {
+		NLDBG("Wrong address length %d\n", addr_len);
+		return -1;
+	}
+	if (rth->local.nl_family != AF_NETLINK) {
+		NLDBG("Wrong address family %d\n", rth->local.nl_family);
+		return -1;
+	}
+	rth->seq = time(NULL);
+	return 0;
+}
+
+int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
+{
+	return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);
 }
 
 int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
@@ -41,6 +115,7 @@ int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
 	memset(&nladdr, 0, sizeof(nladdr));
 	nladdr.nl_family = AF_NETLINK;
 
+	memset(&req, 0, sizeof(req));
 	req.nlh.nlmsg_len = sizeof(req);
 	req.nlh.nlmsg_type = type;
 	req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
@@ -48,45 +123,70 @@ int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
 	req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
 	req.g.rtgen_family = family;
 
-	return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
+	return sendto(rth->fd, (void*)&req, sizeof(req), 0,
+		      (struct sockaddr*)&nladdr, sizeof(nladdr));
+}
+
+int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
+{
+	struct nlmsghdr nlh;
+	struct sockaddr_nl nladdr;
+	struct iovec iov[2] = {
+		{ .iov_base = &nlh, .iov_len = sizeof(nlh) },
+		{ .iov_base = req, .iov_len = len }
+	};
+	struct msghdr msg = {
+		.msg_name = &nladdr,
+		.msg_namelen = 	sizeof(nladdr),
+		.msg_iov = iov,
+		.msg_iovlen = 2,
+	};
+
+	memset(&nladdr, 0, sizeof(nladdr));
+	nladdr.nl_family = AF_NETLINK;
+
+	nlh.nlmsg_len = NLMSG_LENGTH(len);
+	nlh.nlmsg_type = type;
+	nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+	nlh.nlmsg_pid = 0;
+	nlh.nlmsg_seq = rth->dump = ++rth->seq;
+
+	return sendmsg(rth->fd, &msg, 0);
 }
 
 int rtnl_dump_filter(struct rtnl_handle *rth,
-		     int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
+		     rtnl_filter_t filter,
 		     void *arg1,
-		     int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+		     rtnl_filter_t junk,
 		     void *arg2)
 {
-	char	buf[8192];
 	struct sockaddr_nl nladdr;
 	struct iovec iov;
+	struct msghdr msg = {
+		.msg_name = &nladdr,
+		.msg_namelen = sizeof(nladdr),
+		.msg_iov = &iov,
+		.msg_iovlen = 1,
+	};
+	char buf[NL_DUMP_SIZE];
 
 	iov.iov_base = buf;
-	iov.iov_len = sizeof(buf);
-
 	while (1) {
 		int status;
 		struct nlmsghdr *h;
 
-		struct msghdr msg;
-
-		msg.msg_name = (void*)&nladdr;
-		msg.msg_namelen = sizeof(nladdr);
-		msg.msg_iov = &iov;
-		msg.msg_iovlen = 1;
-		msg.msg_control = NULL;
-		msg.msg_controllen = 0;
-		msg.msg_flags = 0;
-
+		iov.iov_len = sizeof(buf);
 		status = recvmsg(rth->fd, &msg, 0);
 
 		if (status < 0) {
+			if (errno == EINTR)
+				continue;
+			NLDBG_SYS("OVERRUN");
 			continue;
 		}
+
 		if (status == 0) {
-			return -1;
-		}
-		if (msg.msg_namelen != sizeof(nladdr)) {
+			NLDBG("EOF on netlink\n");
 			return -1;
 		}
 
@@ -110,8 +210,10 @@ int rtnl_dump_filter(struct rtnl_handle *rth,
 			if (h->nlmsg_type == NLMSG_ERROR) {
 				struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
 				if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+					NLDBG("ERROR truncated\n");
 				} else {
-					return err->error;
+					errno = -err->error;
+					NLDBG_SYS("RTNETLINK answers");
 				}
 				return -1;
 			}
@@ -123,37 +225,36 @@ skip_it:
 			h = NLMSG_NEXT(h, status);
 		}
 		if (msg.msg_flags & MSG_TRUNC) {
+			NLDBG("Message truncated\n");
 			continue;
 		}
 		if (status) {
-			return -1;
+			NLDBG("!!!Remnant of size %d\n", status);
+			return -2;
 		}
 	}
 }
 
 int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
 	      unsigned groups, struct nlmsghdr *answer,
-	      int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+	      rtnl_filter_t junk,
 	      void *jarg)
 {
 	int status;
 	unsigned seq;
 	struct nlmsghdr *h;
 	struct sockaddr_nl nladdr;
-	struct iovec iov;
-	char   buf[8192];
-	struct msghdr msg;
-
-	iov.iov_base = (void*)n;
-	iov.iov_len = n->nlmsg_len;
-
-	msg.msg_name = (void*)&nladdr;
-	msg.msg_namelen = sizeof(nladdr);
-	msg.msg_iov = &iov;
-	msg.msg_iovlen = 1;
-	msg.msg_control = NULL;
-	msg.msg_controllen = 0;
-	msg.msg_flags = 0;
+	struct iovec iov = {
+		.iov_base = (void*) n,
+		.iov_len = n->nlmsg_len
+	};
+	struct msghdr msg = {
+		.msg_name = &nladdr,
+		.msg_namelen = sizeof(nladdr),
+		.msg_iov = &iov,
+		.msg_iovlen = 1,
+	};
+	char   buf[NL_TALK_SIZE];
 
 	memset(&nladdr, 0, sizeof(nladdr));
 	nladdr.nl_family = AF_NETLINK;
@@ -161,15 +262,19 @@ int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
 	nladdr.nl_groups = groups;
 
 	n->nlmsg_seq = seq = ++rtnl->seq;
+
 	if (answer == NULL)
 		n->nlmsg_flags |= NLM_F_ACK;
 
 	status = sendmsg(rtnl->fd, &msg, 0);
 
 	if (status < 0) {
+		NLDBG_SYS("Cannot talk to rtnetlink");
 		return -1;
 	}
 
+	memset(buf,0,sizeof(buf));
+
 	iov.iov_base = buf;
 
 	while (1) {
@@ -177,13 +282,18 @@ int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
 		status = recvmsg(rtnl->fd, &msg, 0);
 
 		if (status < 0) {
+			if (errno == EINTR)
+				continue;
+			NLDBG_SYS("OVERRUN");
 			continue;
 		}
 		if (status == 0) {
+			NLDBG("EOF on netlink\n");
 			return -1;
 		}
 		if (msg.msg_namelen != sizeof(nladdr)) {
-			return -1;
+			NLDBG("sender address length == %d\n", msg.msg_namelen);
+			return -2;
 		}
 		for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
 			int err;
@@ -191,7 +301,12 @@ int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
 			int l = len - sizeof(*h);
 
 			if (l<0 || len>status) {
-				return -1;
+				if (msg.msg_flags & MSG_TRUNC) {
+					NLDBG("Truncated message\n");
+					return -1;
+				}
+				NLDBG("!!!malformed message: len=%d\n", len);
+				return -2;
 			}
 
 			if (nladdr.nl_pid != peer ||
@@ -202,35 +317,117 @@ int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
 					if (err < 0)
 						return err;
 				}
+				/* Don't forget to skip that message. */
+				status -= NLMSG_ALIGN(len);
+				h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
 				continue;
 			}
 
 			if (h->nlmsg_type == NLMSG_ERROR) {
 				struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
 				if (l < sizeof(struct nlmsgerr)) {
+					NLDBG("ERROR truncated\n");
 				} else {
-					if (!err->error) {
+					errno = -err->error;
+					if (errno == 0) {
 						if (answer)
 							memcpy(answer, h, h->nlmsg_len);
 						return 0;
 					}
+					NLDBG_SYS("RTNETLINK answers");
 				}
-				return err->error;
+				return -1;
 			}
 			if (answer) {
 				memcpy(answer, h, h->nlmsg_len);
 				return 0;
 			}
 
+			NLDBG("Unexpected reply!!!\n");
+
 			status -= NLMSG_ALIGN(len);
 			h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
 		}
 		if (msg.msg_flags & MSG_TRUNC) {
+			NLDBG("Message truncated\n");
 			continue;
 		}
 		if (status) {
+			NLDBG("!!!Remnant of size %d\n", status);
+			return -2;
+		}
+	}
+}
+
+int rtnl_listen(struct rtnl_handle *rtnl,
+		rtnl_filter_t handler,
+		void *jarg)
+{
+	int status;
+	struct nlmsghdr *h;
+	struct sockaddr_nl nladdr;
+	struct iovec iov;
+	struct msghdr msg = {
+		.msg_name = &nladdr,
+		.msg_namelen = sizeof(nladdr),
+		.msg_iov = &iov,
+		.msg_iovlen = 1,
+	};
+	char   buf[NL_LISTEN_SIZE];
+
+	memset(&nladdr, 0, sizeof(nladdr));
+	nladdr.nl_family = AF_NETLINK;
+	nladdr.nl_pid = 0;
+	nladdr.nl_groups = 0;
+
+	iov.iov_base = buf;
+	while (1) {
+		iov.iov_len = sizeof(buf);
+		status = recvmsg(rtnl->fd, &msg, 0);
+
+		if (status < 0) {
+			if (errno == EINTR)
+				continue;
+			NLDBG_SYS("OVERRUN");
+			continue;
+		}
+		if (status == 0) {
+			NLDBG("EOF on netlink\n");
 			return -1;
 		}
+		if (msg.msg_namelen != sizeof(nladdr)) {
+			NLDBG("Sender address length == %d\n", msg.msg_namelen);
+			return -2;
+		}
+		for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
+			int err;
+			int len = h->nlmsg_len;
+			int l = len - sizeof(*h);
+
+			if (l<0 || len>status) {
+				if (msg.msg_flags & MSG_TRUNC) {
+					NLDBG("Truncated message\n");
+					return -1;
+				}
+				NLDBG("!!!malformed message: len=%d\n", len);
+				return -2;
+			}
+
+			err = handler(&nladdr, h, jarg);
+			if (err < 0)
+				return err;
+
+			status -= NLMSG_ALIGN(len);
+			h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+		}
+		if (msg.msg_flags & MSG_TRUNC) {
+			NLDBG("Message truncated\n");
+			continue;
+		}
+		if (status) {
+			NLDBG("!!!Remnant of size %d\n", status);
+			return -2;
+		}
 	}
 }
 
@@ -238,9 +435,11 @@ int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
 {
 	int len = RTA_LENGTH(4);
 	struct rtattr *rta;
-	if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
+	if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) {
+		NLDBG("addattr32: Error! max allowed bound %d exceeded\n",maxlen);
 		return -1;
-	rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
+	}
+	rta = NLMSG_TAIL(n);
 	rta->rta_type = type;
 	rta->rta_len = len;
 	memcpy(RTA_DATA(rta), &data, 4);
@@ -248,18 +447,34 @@ int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
 	return 0;
 }
 
-int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen)
+int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data,
+	      int alen)
 {
 	int len = RTA_LENGTH(alen);
 	struct rtattr *rta;
 
-	if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
+	if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) {
+		NLDBG("addattr_l ERROR: message exceeded bound of %d\n",maxlen);
 		return -1;
-	rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
+	}
+	rta = NLMSG_TAIL(n);
 	rta->rta_type = type;
 	rta->rta_len = len;
 	memcpy(RTA_DATA(rta), data, alen);
-	n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+	n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
+	return 0;
+}
+
+int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len)
+{
+	if (NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len) > maxlen) {
+		NLDBG("addraw_l ERROR: message exceeded bound of %d\n",maxlen);
+		return -1;
+	}
+
+	memcpy(NLMSG_TAIL(n), data, len);
+	memset((void *) NLMSG_TAIL(n) + len, 0, NLMSG_ALIGN(len) - len);
+	n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len);
 	return 0;
 }
 
@@ -268,8 +483,10 @@ int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data)
 	int len = RTA_LENGTH(4);
 	struct rtattr *subrta;
 
-	if (RTA_ALIGN(rta->rta_len) + len > maxlen)
+	if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
+		NLDBG("rta_addattr32: Error! max allowed bound %d exceeded\n",maxlen);
 		return -1;
+	}
 	subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
 	subrta->rta_type = type;
 	subrta->rta_len = len;
@@ -278,28 +495,48 @@ int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data)
 	return 0;
 }
 
-int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen)
+int rta_addattr_l(struct rtattr *rta, int maxlen, int type,
+		  const void *data, int alen)
 {
 	struct rtattr *subrta;
 	int len = RTA_LENGTH(alen);
 
-	if (RTA_ALIGN(rta->rta_len) + len > maxlen)
+	if (RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len) > maxlen) {
+		NLDBG("rta_addattr_l: Error! max allowed bound %d exceeded\n",maxlen);
 		return -1;
+	}
 	subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
 	subrta->rta_type = type;
 	subrta->rta_len = len;
 	memcpy(RTA_DATA(subrta), data, alen);
-	rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+	rta->rta_len = NLMSG_ALIGN(rta->rta_len) + RTA_ALIGN(len);
 	return 0;
 }
 
-
 int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
 {
+	memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
 	while (RTA_OK(rta, len)) {
 		if (rta->rta_type <= max)
 			tb[rta->rta_type] = rta;
 		rta = RTA_NEXT(rta,len);
 	}
+	if (len)
+		NLDBG("!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
 	return 0;
 }
+
+int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+	int i = 0;
+
+	memset(tb, 0, sizeof(struct rtattr *) * max);
+	while (RTA_OK(rta, len)) {
+		if (rta->rta_type <= max && i < max)
+			tb[i++] = rta;
+		rta = RTA_NEXT(rta,len);
+	}
+	if (len)
+		NLDBG("!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
+	return i;
+}
diff --git a/src/ha.c b/src/ha.c
index 97fc48c..d666222 100644
--- a/src/ha.c
+++ b/src/ha.c
@@ -198,7 +198,7 @@ struct ha_interface *ha_get_if_by_anycast(const struct in6_addr *anycast,
 	return NULL;
 }
 
-static int ha_if_addr_setup(struct sockaddr_nl *who,
+static int ha_if_addr_setup(const struct sockaddr_nl *who,
 			    struct nlmsghdr *n,
 			    void *arg)
 {
diff --git a/src/movement.c b/src/movement.c
index 3b62e3b..f80200c 100644
--- a/src/movement.c
+++ b/src/movement.c
@@ -1640,7 +1640,7 @@ struct rtnl_handle md_rth;
 static void *md_nl_listen(void *arg)
 {
 	pthread_dbg("thread started");
-	rtnl_ext_listen(&md_rth, process_nlmsg, NULL);
+	rtnl_listen(&md_rth, process_nlmsg, NULL);
 	pthread_exit(NULL);
 }
 
diff --git a/src/rtnl.c b/src/rtnl.c
index 916c3bd..98cbb65 100644
--- a/src/rtnl.c
+++ b/src/rtnl.c
@@ -50,108 +50,11 @@
 #define RTDBG(...) 
 #endif /* RTDBG */
 
-int rtnl_ext_open(struct rtnl_handle *rth, int proto, unsigned subscriptions)
-{
-	socklen_t addr_len;
-
-	memset(rth, 0, sizeof(rth));
-
-	rth->fd = socket(AF_NETLINK, SOCK_RAW, proto);
-	if (rth->fd < 0) {
-		syslog(LOG_ERR,
-		       "Unable to open netlink socket! "
-		       "Do you have root permissions?");
-		return -1;
-	}
-
-	memset(&rth->local, 0, sizeof(rth->local));
-	rth->local.nl_family = AF_NETLINK;
-	rth->local.nl_groups = subscriptions;
-
-	if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
-		return -1;
-	}
-	addr_len = sizeof(rth->local);
-	if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
-		return -1;
-	}
-	if (addr_len != sizeof(rth->local)) {
-		return -1;
-	}
-	if (rth->local.nl_family != AF_NETLINK) {
-		return -1;
-	}
-	rth->seq = time(NULL);
-	return 0;
-}
-
-int rtnl_ext_listen(struct rtnl_handle *rtnl, 
-		    int (*handler)(struct sockaddr_nl *,
-				   struct nlmsghdr *n,
-				   void *),
-		    void *jarg)
-{
-	int status;
-	struct nlmsghdr *h;
-	struct sockaddr_nl nladdr;
-	struct iovec iov;
-	char buf[1024];
-	struct msghdr msg;
-
-	msg.msg_name = (void*)&nladdr;
-	msg.msg_namelen = sizeof(nladdr);
-	msg.msg_iov = &iov;
-	msg.msg_iovlen = 1;
-	msg.msg_control = NULL;
-	msg.msg_controllen = 0;
-	msg.msg_flags = 0;
-
-	memset(&nladdr, 0, sizeof(nladdr));
-	nladdr.nl_family = AF_NETLINK;
-	nladdr.nl_pid = 0;
-	nladdr.nl_groups = 0;
-
-
-	iov.iov_base = buf;
-
-	while (1) {
-		iov.iov_len = sizeof(buf);
-		status = recvmsg(rtnl->fd, &msg, 0);
-
-		if (status < 0) {
-			if (errno == EBADF)
-				return -1;
-			continue;
-		}
-		if (status == 0)
-			return 0;
-
-		if (msg.msg_namelen != sizeof(nladdr))
-			continue;
-
-		for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
-			int err;
-			int len = h->nlmsg_len;
-			int l = len - sizeof(*h);
-
-			if (l < 0 || len > status)
-				break;
-
-			err = handler(&nladdr, h, jarg);
-			if (err < 0)
-				break;
-
-			status -= NLMSG_ALIGN(len);
-			h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
-		}
-	}
-}
-
 int rtnl_do(int proto, struct nlmsghdr *sn, struct nlmsghdr *rn)
 {
 	struct rtnl_handle rth;
 	int err;
-	if (rtnl_ext_open(&rth, proto, 0) < 0) {
+	if (rtnl_open_byproto(&rth, 0, proto) < 0) {
 		dbg("huh?\n");
 		return -1;
 	}
@@ -475,13 +378,11 @@ int rule_del(const char *iface, uint8_t table,
 			src, src_plen, dst, dst_plen);
 }
 
-int rtnl_iterate(int proto, int type,
-	int (*func)(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg),
-	void *extarg)
+int rtnl_iterate(int proto, int type, rtnl_filter_t func, void *extarg)
 {
 	struct rtnl_handle rth;
 
-	if (rtnl_ext_open(&rth, proto, 0) < 0)
+	if (rtnl_open_byproto(&rth, 0, proto) < 0)
 		return -1;
 
 	if (rtnl_wilddump_request(&rth, AF_INET6, type) < 0) {
diff --git a/src/rtnl.h b/src/rtnl.h
index 496f201..d6c3f93 100644
--- a/src/rtnl.h
+++ b/src/rtnl.h
@@ -21,26 +21,16 @@
 #define IP6_RULE_PRIO_MIP6_BLOCK     1003
 #define IP6_RULE_PRIO_MIP6_FWD       1004
 
-extern int rtnl_ext_open(struct rtnl_handle *rth,
-			 int proto,
-			 unsigned subscriptions);
-
-extern int rtnl_ext_listen(struct rtnl_handle *, 
-			   int (*handler)(struct sockaddr_nl *,
-					  struct nlmsghdr *n,
-					  void *),
-			   void *jarg);
-
 static inline int rtnl_route_open(struct rtnl_handle *rth, 
 				  unsigned subscriptions)
 {
-	return rtnl_ext_open(rth, NETLINK_ROUTE, subscriptions);
+	return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);
 }
 
 static inline int rtnl_xfrm_open(struct rtnl_handle *rth,
 				 unsigned subscriptions)
 {
-	return rtnl_ext_open(rth, NETLINK_XFRM, subscriptions);
+	return rtnl_open_byproto(rth, subscriptions, NETLINK_XFRM);
 }
 
 int rtnl_do(int proto, struct nlmsghdr *sn, struct nlmsghdr *rn);
@@ -91,9 +81,7 @@ int rule_del(const char *iface, uint8_t table,
 	     const struct in6_addr *src, int src_plen,
 	     const struct in6_addr *dst, int dst_plen);
 
-int rtnl_iterate(int proto, int type,
-	int (*func)(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg),
-	void *extarg);
+int rtnl_iterate(int proto, int type, rtnl_filter_t func, void *extarg);
 
 /**
  * routes_iterate - apply something to all routes
@@ -104,9 +92,7 @@ int rtnl_iterate(int proto, int type,
  * @func to all of them.  Returns zero on success, negative otherwise.
  **/
 
-static inline int routes_iterate(
-	int (*func)(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg),
-	void *extarg)
+static inline int routes_iterate(rtnl_filter_t func, void *extarg)
 {
 	return rtnl_iterate(NETLINK_ROUTE, RTM_GETROUTE, func, extarg);
 }
@@ -119,9 +105,7 @@ static inline int routes_iterate(
  * Retrieves all addresses assigned to the node and applies function
  * @func to all of them.  Returns zero on success, negative otherwise.
  **/
-static inline int addrs_iterate(
-	int (*func)(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg),
-	void *extarg)
+static inline int addrs_iterate(rtnl_filter_t func, void *extarg)
 {
 	return rtnl_iterate(NETLINK_ROUTE, RTM_GETADDR, func, extarg);
 }
@@ -134,9 +118,7 @@ static inline int addrs_iterate(
  * Retrieves all IPv6 capable interfaces to the node and applies function
  * @func to all of them.  Returns zero on success, negative otherwise.
  **/
-static inline int inet6_ifaces_iterate(
-	int (*func)(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg),
-	void *extarg)
+static inline int inet6_ifaces_iterate(rtnl_filter_t func, void *extarg)
 {
 	return rtnl_iterate(NETLINK_ROUTE, RTM_GETLINK, func, extarg);
 }
diff --git a/src/xfrm.c b/src/xfrm.c
index 4309090..943bebe 100644
--- a/src/xfrm.c
+++ b/src/xfrm.c
@@ -267,9 +267,9 @@ long xfrm_last_used(const struct in6_addr *daddr,
 		    const struct in6_addr *saddr, int proto,
 		    const struct timespec *now)
 {
-	uint8_t sbuf[NLMSG_LENGTH(sizeof(struct xfrm_usersa_id)) +
-		     RTA_LENGTH(sizeof(xfrm_address_t))];
-	uint8_t rbuf[384];
+	uint8_t sbuf[NLMSG_SPACE(sizeof(struct xfrm_usersa_id)) +
+		     RTA_SPACE(sizeof(xfrm_address_t))];
+	uint8_t rbuf[1024];
 	struct nlmsghdr *sn, *rn;
 	struct xfrm_usersa_id *sa_id;
 	struct xfrm_usersa_info *sa;
@@ -346,10 +346,10 @@ static int xfrm_policy_add(uint8_t type, const struct xfrm_selector *sel,
 			   int update, int dir, int action, int priority,
 			   struct xfrm_user_tmpl *tmpls, int num_tmpl)
 {
-	uint8_t buf[NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_info))
-		    + RTA_LENGTH(sizeof(struct xfrm_userpolicy_type))
-		    + RTA_LENGTH(sizeof(struct xfrm_user_tmpl) 
-				 * MIPV6_MAX_TMPLS)];
+	uint8_t buf[NLMSG_SPACE(sizeof(struct xfrm_userpolicy_info))
+		    + RTA_SPACE(sizeof(struct xfrm_userpolicy_type))
+		    + RTA_SPACE(sizeof(struct xfrm_user_tmpl)
+				* MIPV6_MAX_TMPLS)];
 	struct nlmsghdr *n;
 	struct xfrm_userpolicy_info *pol;
 	struct xfrm_userpolicy_type ptype;
@@ -406,9 +406,8 @@ static int xfrm_mip_policy_add(const struct xfrm_selector *sel,
 
 static int xfrm_policy_del(uint8_t type, const struct xfrm_selector *sel, int dir)
 {
-	uint8_t buf[NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_id))
-		    + RTA_LENGTH(sizeof(struct xfrm_userpolicy_type))
-		];
+	uint8_t buf[NLMSG_SPACE(sizeof(struct xfrm_userpolicy_id))
+		    + RTA_SPACE(sizeof(struct xfrm_userpolicy_type))];
 	struct nlmsghdr *n;
 	struct xfrm_userpolicy_id *pol_id;
 	struct xfrm_userpolicy_type ptype;
@@ -447,8 +446,8 @@ static int xfrm_state_add(const struct xfrm_selector *sel,
 			  int proto, const struct in6_addr *coa,
 			  int update, uint8_t flags)
 {
-	uint8_t buf[NLMSG_LENGTH(sizeof(struct xfrm_usersa_info)) 
-		    + RTA_LENGTH(sizeof(struct in6_addr))];
+	uint8_t buf[NLMSG_SPACE(sizeof(struct xfrm_usersa_info))
+		    + RTA_SPACE(sizeof(struct in6_addr))];
 	struct nlmsghdr *n;
 	struct xfrm_usersa_info *sa;
 	int err;
@@ -484,8 +483,8 @@ static int xfrm_state_add(const struct xfrm_selector *sel,
 
 static int xfrm_state_del(int proto, const struct xfrm_selector *sel)
 {
-	uint8_t buf[NLMSG_LENGTH(sizeof(struct xfrm_usersa_id)) +
-		    RTA_LENGTH(sizeof(xfrm_address_t))];
+	uint8_t buf[NLMSG_SPACE(sizeof(struct xfrm_usersa_id)) +
+		    RTA_SPACE(sizeof(xfrm_address_t))];
 	struct nlmsghdr *n;
 	struct xfrm_usersa_id *sa_id;
 	int err;
@@ -1808,7 +1807,7 @@ static int parse_report(struct nlmsghdr *msg)
 	return 0;
 }
 
-static int xfrm_rcv(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+static int xfrm_rcv(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 {
 	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
 	switch (n->nlmsg_type) {
@@ -1830,7 +1829,7 @@ struct rtnl_handle xfrm_rth;
 static void *xfrm_listen(void *dummy)
 {
 	pthread_dbg("thread started");
-	rtnl_ext_listen(&xfrm_rth, xfrm_rcv, NULL);
+	rtnl_listen(&xfrm_rth, xfrm_rcv, NULL);
 	pthread_exit(NULL);
 }
 
-- 
1.5.0.3

