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