/* Netlink helpers for zbuf * Copyright (c) 2014-2015 Timo Teräs * * This file is free software: you may copy, redistribute and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include "znl.h" #define ZNL_ALIGN(len) (((len)+3) & ~3) void *znl_push(struct zbuf *zb, size_t n) { return zbuf_pushn(zb, ZNL_ALIGN(n)); } void *znl_pull(struct zbuf *zb, size_t n) { return zbuf_pulln(zb, ZNL_ALIGN(n)); } struct nlmsghdr *znl_nlmsg_push(struct zbuf *zb, uint16_t type, uint16_t flags) { struct nlmsghdr *n; n = znl_push(zb, sizeof(*n)); if (!n) return NULL; *n = (struct nlmsghdr){ .nlmsg_type = type, .nlmsg_flags = flags, }; return n; } void znl_nlmsg_complete(struct zbuf *zb, struct nlmsghdr *n) { n->nlmsg_len = zb->tail - (uint8_t *)n; } struct nlmsghdr *znl_nlmsg_pull(struct zbuf *zb, struct zbuf *payload) { struct nlmsghdr *n; size_t plen; n = znl_pull(zb, sizeof(*n)); if (!n) return NULL; plen = n->nlmsg_len - sizeof(*n); zbuf_init(payload, znl_pull(zb, plen), plen, plen); zbuf_may_pulln(zb, ZNL_ALIGN(plen) - plen); return n; } struct rtattr *znl_rta_push(struct zbuf *zb, uint16_t type, const void *val, size_t len) { struct rtattr *rta; uint8_t *dst; rta = znl_push(zb, ZNL_ALIGN(sizeof(*rta)) + ZNL_ALIGN(len)); if (!rta) return NULL; *rta = (struct rtattr){ .rta_type = type, .rta_len = ZNL_ALIGN(sizeof(*rta)) + len, }; dst = (uint8_t *)(rta + 1); memcpy(dst, val, len); memset(dst + len, 0, ZNL_ALIGN(len) - len); return rta; } struct rtattr *znl_rta_push_u32(struct zbuf *zb, uint16_t type, uint32_t val) { return znl_rta_push(zb, type, &val, sizeof(val)); } struct rtattr *znl_rta_nested_push(struct zbuf *zb, uint16_t type) { struct rtattr *rta; rta = znl_push(zb, sizeof(*rta)); if (!rta) return NULL; *rta = (struct rtattr){ .rta_type = type, }; return rta; } void znl_rta_nested_complete(struct zbuf *zb, struct rtattr *rta) { size_t len = zb->tail - (uint8_t *)rta; size_t align = ZNL_ALIGN(len) - len; if (align) { void *dst = zbuf_pushn(zb, align); if (dst) memset(dst, 0, align); } rta->rta_len = len; } struct rtattr *znl_rta_pull(struct zbuf *zb, struct zbuf *payload) { struct rtattr *rta; size_t plen; rta = znl_pull(zb, sizeof(*rta)); if (!rta) return NULL; if (rta->rta_len > sizeof(*rta)) { plen = rta->rta_len - sizeof(*rta); zbuf_init(payload, znl_pull(zb, plen), plen, plen); } else { zbuf_init(payload, NULL, 0, 0); } return rta; } int znl_open(int protocol, int groups) { struct sockaddr_nl addr; int fd, buf = 128 * 1024; fd = socket(AF_NETLINK, SOCK_RAW, protocol); if (fd < 0) return -1; if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) < 0) goto error; if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) goto error; if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &buf, sizeof(buf)) < 0) goto error; memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; addr.nl_groups = groups; if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) goto error; return fd; error: close(fd); return -1; }