Several nlmsghdr in the message netlink packet

Emmanuel Thierry emmanuel.thierry at telecom-bretagne.eu
Wed Dec 11 12:21:56 EST 2013


Hello,

I'm working on a daemon that needs to send a lot of netlink messages in the same time. For performance purposes, i thought about sending all of it in batch in the same netlink message.
I tried a proof of concept implementation with the code below, on the XFRM system. All nlmsghdr are independent, they all give a distinct order and all have distinct sequence numbers, they can issue distinct ACKs. As an example you can in the same batch add a policy and delete it afterward. So the netlink system clearly supports this.

However, i didn't see any function to ease this kind of operations in libnl3. This results in a really hacky code (see below).
Did i miss something ? Is there a plan to support such a feature ?

Thanks
Emmanuel Thierry



#include <errno.h>
#include <stdio.h>
#include <stdio.h>
#include <linux/netlink.h>
#include <linux/xfrm.h>
#include <netlink/netlink.h>
#include <netlink/errno.h>
#include <netlink/msg.h>
#include <netlink/socket.h>
#include <sys/socket.h>


struct ucred
{
	pid_t pid;
	uid_t uid;
	gid_t gid;
};

struct nl_msg
{
        int                     nm_protocol;
        int                     nm_flags;
        struct sockaddr_nl      nm_src;
        struct sockaddr_nl      nm_dst;
        struct ucred            nm_creds;
        struct nlmsghdr *       nm_nlh;
        size_t                  nm_size;
        int                     nm_refcnt;
};


int nl_sendmulti(struct nl_sock *sk, struct nl_msg *msg)
{
	int ret, i, remaining, chunkcnt = 0;
	struct iovec *iovs;
	struct nlmsghdr *hdr;

	remaining = nlmsg_get_max_size(msg);
	for (hdr = nlmsg_hdr(msg); nlmsg_ok(hdr, remaining);
	     hdr = nlmsg_next(hdr, &remaining))
		chunkcnt++;

	iovs = malloc(chunkcnt * sizeof(struct iovec));
	if (!iovs)
		return -nl_syserr2nlerr(errno);

	remaining = nlmsg_get_max_size(msg);
	hdr = nlmsg_hdr(msg);
	for (i=0; i<chunkcnt; i++) {
		iovs[i].iov_base = hdr;
		iovs[i].iov_len = hdr->nlmsg_len;
		hdr = nlmsg_next(hdr, &remaining);
	}

	ret = nl_send_iovec(sk, msg, iovs, i);
	free(iovs);

	return ret;
}

struct nlmsghdr *nlmsg_reserve_hdr(struct nl_msg *n, size_t len, int pad)
{
	int remaining, tlen, hdr_tlen = 0, diff;
	void *buf = n->nm_nlh;
	struct nlmsghdr *hdr;

	remaining = nlmsg_get_max_size(n);
	for (hdr = nlmsg_hdr(n); nlmsg_ok(hdr, remaining);
	     hdr = nlmsg_next(hdr, &remaining))
		hdr_tlen += NLMSG_ALIGN(hdr->nlmsg_len);

	len += NLMSG_HDRLEN;
	tlen = pad ? ((len + (pad - 1)) & ~(pad - 1)) : len;
	if ((tlen + hdr_tlen) > n->nm_size)
		return NULL;

	hdr = (struct nlmsghdr *) (buf + hdr_tlen);
	hdr->nlmsg_len = tlen;

	if (tlen > len)
		memset(buf + len, 0, tlen - len);

	return hdr;
}

void *nl_complete_hdr(struct nl_sock *sk, struct nlmsghdr *hdr)
{
	hdr->nlmsg_pid = nl_socket_get_local_port(sk);
	hdr->nlmsg_seq = nl_socket_use_seq(sk);
}

void _populate_newpolicy(struct xfrm_userpolicy_info *policy_info,
                         xfrm_address_t *src, xfrm_address_t *dst,
                         uint8_t dir, uint8_t action)
{
	memset(policy_info, 0, sizeof(*policy_info));
	memcpy(&policy_info->sel.daddr, dst, sizeof(*dst));
	memcpy(&policy_info->sel.saddr, src, sizeof(*src));
	policy_info->sel.family = AF_INET6;
	policy_info->sel.prefixlen_d = 128;
	policy_info->sel.prefixlen_s = 128;
	policy_info->sel.proto = 0;
	policy_info->dir = dir;
	policy_info->action = action;
	policy_info->share = XFRM_SHARE_ANY;
	policy_info->lft.soft_byte_limit = XFRM_INF;
	policy_info->lft.soft_packet_limit = XFRM_INF;
	policy_info->lft.hard_byte_limit = XFRM_INF;
	policy_info->lft.hard_packet_limit = XFRM_INF;
}

void _populate_delpolicy(struct xfrm_userpolicy_id *policy_id,
                         xfrm_address_t *src, xfrm_address_t *dst,
                         uint8_t dir)
{
	memset(policy_id, 0, sizeof(*policy_id));
	memcpy(&policy_id->sel.daddr, dst, sizeof(*dst));
	memcpy(&policy_id->sel.saddr, src, sizeof(*src));
	policy_id->sel.family = AF_INET6;
	policy_id->sel.prefixlen_d = 128;
	policy_id->sel.prefixlen_s = 128;
	policy_id->dir = dir;
}

int main(int argc, const char *argv[])
{
	struct nl_sock *sk;
	struct nl_msg *msg;
	struct nlmsghdr *hdr;
	xfrm_address_t addr1 = { .a6 = { 0x000001fd, 0x0, 0x0, 0x01000000}};
	xfrm_address_t addr2 = { .a6 = { 0x000002fd, 0x0, 0x0, 0x02000000}};
	xfrm_address_t addr3 = { .a6 = { 0x000003fd, 0x0, 0x0, 0x03000000}};
	int err = 0;
	struct xfrm_userpolicy_type ptype = { .type = XFRM_POLICY_TYPE_MAIN };

	sk = nl_socket_alloc();
	if (!sk) {
		err = -NLE_NOMEM;
		goto out_err;
	}

	err = nl_connect(sk, NETLINK_XFRM);
	if (err)
                goto out_free_sk;
	nl_socket_set_peer_port(sk, 0);
	nl_socket_disable_auto_ack(sk);

	msg = nlmsg_alloc();
	if (!msg) {
		err = -NLE_NOMEM;
		goto out_close_sk;
	}


	/* Message 1: add policy */

	if (!nlmsg_reserve(msg, sizeof(struct xfrm_userpolicy_info), NLMSG_ALIGNTO)) {
		err = -NLE_NOMEM;
		goto out_free_msg;
	}
	hdr = nlmsg_hdr(msg);
	hdr->nlmsg_type = XFRM_MSG_NEWPOLICY;
	hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
	nl_complete_hdr(sk, hdr);
	_populate_newpolicy((struct xfrm_userpolicy_info *) nlmsg_data(hdr),
	                    &addr1, &addr2, XFRM_POLICY_OUT, XFRM_POLICY_ALLOW);

	/* Message 2: add policy */

	hdr = nlmsg_reserve_hdr(msg, sizeof(struct xfrm_userpolicy_info), NLMSG_ALIGNTO);
	if (!hdr) {
		err = -NLE_NOMEM;
		goto out_free_msg;
	}
	hdr->nlmsg_type = XFRM_MSG_NEWPOLICY;
	hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
	nl_complete_hdr(sk, hdr);
	_populate_newpolicy((struct xfrm_userpolicy_info *) nlmsg_data(hdr),
	                     &addr1, &addr3, XFRM_POLICY_OUT, XFRM_POLICY_BLOCK);

	/* Message 3: del policy (delete Message 1) */

	hdr = nlmsg_reserve_hdr(msg, sizeof(struct xfrm_userpolicy_id), NLMSG_ALIGNTO);
	if (!hdr) {
		err = -NLE_NOMEM;
		goto out_free_msg;
	}
	hdr->nlmsg_type = XFRM_MSG_DELPOLICY;
	hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
	nl_complete_hdr(sk, hdr);
	_populate_delpolicy((struct xfrm_userpolicy_id *) nlmsg_data(hdr),
	                    &addr1, &addr2, XFRM_POLICY_OUT);

	/* Message 4: add policy */

	hdr = nlmsg_reserve_hdr(msg, sizeof(struct xfrm_userpolicy_info), NLMSG_ALIGNTO);
	if (!hdr) {
		err = -NLE_NOMEM;
		goto out_free_msg;
	}
	hdr->nlmsg_type = XFRM_MSG_NEWPOLICY;
	hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
	nl_complete_hdr(sk, hdr);
	_populate_newpolicy((struct xfrm_userpolicy_info *) nlmsg_data(hdr),
	                     &addr2, &addr3, XFRM_POLICY_IN, XFRM_POLICY_BLOCK);

	err = nl_sendmulti(sk, msg);
	if (err > 0)
		err = 0;

out_free_msg:
	nlmsg_free(msg);
out_close_sk:
	nl_close(sk);
out_free_sk:
	nl_socket_free(sk);
out_err:
	if (err)
		printf("Error %d: %s\n", -err, nl_geterror(-err));
	exit(-err);
}




More information about the libnl mailing list