Make sure that the netlink socket will never block after program
initialization. Fetching if/address/index/mac mappings is done only once at program init, so it is done synchronously as an exception to this rule. Rewrite the netlink handling. Now uses NIH code that should be safe, small, and correct. No external deps FTW.
This commit is contained in:
parent
2c2cc1cf22
commit
01c4731403
@ -7,8 +7,6 @@ set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -s -std=gnu99 -pedantic -Wall -lcap -DHAVE
|
|||||||
|
|
||||||
include_directories("${PROJECT_SOURCE_DIR}/ncmlib")
|
include_directories("${PROJECT_SOURCE_DIR}/ncmlib")
|
||||||
add_subdirectory(ncmlib)
|
add_subdirectory(ncmlib)
|
||||||
include_directories("${PROJECT_SOURCE_DIR}/libmnl")
|
|
||||||
add_subdirectory(libmnl)
|
|
||||||
|
|
||||||
add_subdirectory(ifchd)
|
add_subdirectory(ifchd)
|
||||||
add_subdirectory(ndhc)
|
add_subdirectory(ndhc)
|
||||||
|
@ -5,4 +5,4 @@ cmake_minimum_required (VERSION 2.6)
|
|||||||
file(GLOB NDHC_SRCS "*.c")
|
file(GLOB NDHC_SRCS "*.c")
|
||||||
|
|
||||||
add_executable(ndhc ${NDHC_SRCS})
|
add_executable(ndhc ${NDHC_SRCS})
|
||||||
target_link_libraries(ndhc ncmlib libmnl)
|
target_link_libraries(ndhc ncmlib)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* ndhc.c - DHCP client
|
/* ndhc.c - DHCP client
|
||||||
* Time-stamp: <2011-07-03 05:43:58 njk>
|
* Time-stamp: <2011-07-03 17:19:25 njk>
|
||||||
*
|
*
|
||||||
* (c) 2004-2011 Nicholas J. Kain <njkain at gmail dot com>
|
* (c) 2004-2011 Nicholas J. Kain <njkain at gmail dot com>
|
||||||
*
|
*
|
||||||
@ -47,6 +47,7 @@
|
|||||||
#include "sys.h"
|
#include "sys.h"
|
||||||
#include "ifchange.h"
|
#include "ifchange.h"
|
||||||
#include "arp.h"
|
#include "arp.h"
|
||||||
|
#include "nl.h"
|
||||||
#include "netlink.h"
|
#include "netlink.h"
|
||||||
#include "leasefile.h"
|
#include "leasefile.h"
|
||||||
|
|
||||||
@ -308,7 +309,7 @@ int main(int argc, char **argv)
|
|||||||
write_pid(pidfile);
|
write_pid(pidfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nl_open(&cs) < 0) {
|
if ((cs.nlFd = nl_open(NETLINK_ROUTE, RTMGRP_LINK, &nlportid)) < 0) {
|
||||||
log_line("FATAL - failed to open netlink socket");
|
log_line("FATAL - failed to open netlink socket");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
113
ndhc/netlink.c
113
ndhc/netlink.c
@ -21,8 +21,6 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <asm/types.h>
|
#include <asm/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <linux/netlink.h>
|
|
||||||
#include <linux/rtnetlink.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -32,37 +30,16 @@
|
|||||||
#include <sys/select.h>
|
#include <sys/select.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <libmnl/libmnl.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include "netlink.h"
|
#include "netlink.h"
|
||||||
#include "ifchange.h"
|
#include "ifchange.h"
|
||||||
#include "arp.h"
|
#include "arp.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "nl.h"
|
||||||
|
|
||||||
static struct mnl_socket *mls;
|
static char nlbuf[8192];
|
||||||
|
int nlportid;
|
||||||
static void nl_close(struct client_state_t *cs)
|
|
||||||
{
|
|
||||||
mnl_socket_close(mls);
|
|
||||||
cs->nlFd = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nl_open(struct client_state_t *cs)
|
|
||||||
{
|
|
||||||
assert(cs->nlFd == -1);
|
|
||||||
if ((mls = mnl_socket_open(NETLINK_ROUTE)) == (struct mnl_socket *)-1)
|
|
||||||
return -1;
|
|
||||||
cs->nlFd = mnl_socket_get_fd(mls);
|
|
||||||
if (fcntl(cs->nlFd, F_SETFD, FD_CLOEXEC))
|
|
||||||
goto err_close;
|
|
||||||
if (mnl_socket_bind(mls, RTMGRP_LINK, 0))
|
|
||||||
goto err_close;
|
|
||||||
return 0;
|
|
||||||
err_close:
|
|
||||||
nl_close(cs);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void restart_if(struct client_state_t *cs)
|
static void restart_if(struct client_state_t *cs)
|
||||||
{
|
{
|
||||||
@ -84,50 +61,34 @@ static void sleep_if(struct client_state_t *cs)
|
|||||||
cs->timeout = -1;
|
cs->timeout = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nl_process_msgs_attr(const struct nlattr *attr, void *data)
|
|
||||||
{
|
|
||||||
const struct nlattr **tb = data;
|
|
||||||
int type = mnl_attr_get_type(attr);
|
|
||||||
|
|
||||||
// skip unsupported attribute in user-space
|
static int nlrtattr_assign(struct nlattr *attr, int type, void *data)
|
||||||
if (mnl_attr_type_valid(attr, IFLA_MAX) < 0)
|
{
|
||||||
return MNL_CB_OK;
|
struct nlattr **tb = data;
|
||||||
switch (type) {
|
if (type >= IFLA_MAX)
|
||||||
case IFLA_IFNAME:
|
return 0;
|
||||||
if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
|
|
||||||
log_warning("nl: IFLA_IFNAME failed validation.");
|
|
||||||
return MNL_CB_ERROR;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IFLA_ADDRESS:
|
|
||||||
if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
|
|
||||||
log_warning("nl: IFLA_ADDRESS failed validation.");
|
|
||||||
return MNL_CB_ERROR;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
tb[type] = attr;
|
tb[type] = attr;
|
||||||
return MNL_CB_OK;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void get_if_index_and_mac(const struct nlmsghdr *nlh,
|
static void get_if_index_and_mac(const struct nlmsghdr *nlh,
|
||||||
struct ifinfomsg *ifm,
|
struct ifinfomsg *ifm)
|
||||||
const struct nlattr **tb)
|
|
||||||
{
|
{
|
||||||
mnl_attr_parse(nlh, sizeof *ifm, nl_process_msgs_attr, tb);
|
struct nlattr *tb[IFLA_MAX] = {0};
|
||||||
|
nl_attr_parse(nlh, sizeof *ifm, nlrtattr_assign, tb);
|
||||||
if (!tb[IFLA_IFNAME])
|
if (!tb[IFLA_IFNAME])
|
||||||
return;
|
return;
|
||||||
if (!strcmp(client_config.interface, mnl_attr_get_str(tb[IFLA_IFNAME]))) {
|
if (!strcmp(client_config.interface, nlattr_get_data(tb[IFLA_IFNAME]))) {
|
||||||
client_config.ifindex = ifm->ifi_index;
|
client_config.ifindex = ifm->ifi_index;
|
||||||
if (!tb[IFLA_ADDRESS])
|
if (!tb[IFLA_ADDRESS])
|
||||||
suicide("FATAL: adapter %s lacks a hardware address");
|
suicide("FATAL: adapter %s lacks a hardware address");
|
||||||
int maclen = mnl_attr_get_len(tb[IFLA_ADDRESS]) - 4;
|
int maclen = nlattr_get_len(tb[IFLA_ADDRESS]) - 4;
|
||||||
if (maclen != 6)
|
if (maclen != 6)
|
||||||
suicide("FATAL: adapter hardware address length should be 6, but is %u",
|
suicide("FATAL: adapter hardware address length should be 6, but is %u",
|
||||||
maclen);
|
maclen);
|
||||||
|
|
||||||
const unsigned char *mac =
|
const unsigned char *mac =
|
||||||
(unsigned char *)mnl_attr_get_str(tb[IFLA_ADDRESS]);
|
(unsigned char *)nlattr_get_data(tb[IFLA_ADDRESS]);
|
||||||
log_line("%s hardware address %x:%x:%x:%x:%x:%x",
|
log_line("%s hardware address %x:%x:%x:%x:%x:%x",
|
||||||
client_config.interface,
|
client_config.interface,
|
||||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||||
@ -137,14 +98,13 @@ static void get_if_index_and_mac(const struct nlmsghdr *nlh,
|
|||||||
|
|
||||||
static int nl_process_msgs(const struct nlmsghdr *nlh, void *data)
|
static int nl_process_msgs(const struct nlmsghdr *nlh, void *data)
|
||||||
{
|
{
|
||||||
struct nlattr *tb[IFLA_MAX+1] = {0};
|
struct ifinfomsg *ifm = nlmsg_get_data(nlh);
|
||||||
struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh);
|
|
||||||
struct client_state_t *cs = data;
|
struct client_state_t *cs = data;
|
||||||
|
|
||||||
switch(nlh->nlmsg_type) {
|
switch(nlh->nlmsg_type) {
|
||||||
case RTM_NEWLINK:
|
case RTM_NEWLINK:
|
||||||
if (!client_config.ifindex)
|
if (!client_config.ifindex)
|
||||||
get_if_index_and_mac(nlh, ifm, (const struct nlattr **)tb);
|
get_if_index_and_mac(nlh, ifm);
|
||||||
if (ifm->ifi_index != client_config.ifindex)
|
if (ifm->ifi_index != client_config.ifindex)
|
||||||
break;
|
break;
|
||||||
// IFF_UP corresponds to ifconfig down or ifconfig up.
|
// IFF_UP corresponds to ifconfig down or ifconfig up.
|
||||||
@ -194,41 +154,50 @@ static int nl_process_msgs(const struct nlmsghdr *nlh, void *data)
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return MNL_CB_OK;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_nl_message(struct client_state_t *cs)
|
void handle_nl_message(struct client_state_t *cs)
|
||||||
{
|
{
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
ssize_t ret;
|
||||||
int ret;
|
|
||||||
assert(cs->nlFd != -1);
|
assert(cs->nlFd != -1);
|
||||||
do {
|
do {
|
||||||
ret = mnl_socket_recvfrom(mls, buf, sizeof buf);
|
ret = nl_recv_buf(cs->nlFd, nlbuf, sizeof nlbuf);
|
||||||
ret = mnl_cb_run(buf, ret, 0, 0, nl_process_msgs, cs);
|
if (ret == -1)
|
||||||
|
break;
|
||||||
|
if (nl_foreach_nlmsg(nlbuf, ret, nlportid, nl_process_msgs, cs) == -1)
|
||||||
|
break;
|
||||||
} while (ret > 0);
|
} while (ret > 0);
|
||||||
if (ret == -1)
|
|
||||||
log_line("nl callback function returned error: %s", strerror(errno));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int nl_getifdata(const char *ifname, struct client_state_t *cs)
|
int nl_getifdata(const char *ifname, struct client_state_t *cs)
|
||||||
{
|
{
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
char buf[8192];
|
||||||
struct nlmsghdr *nlh;
|
struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
|
||||||
struct ifinfomsg *ifinfo;
|
struct ifinfomsg *ifinfo;
|
||||||
unsigned int seq;
|
size_t msgsize = NLMSG_HDRLEN + NLMSG_ALIGN(sizeof *ifinfo);
|
||||||
|
|
||||||
nlh = mnl_nlmsg_put_header(buf);
|
memset(buf, 0, msgsize);
|
||||||
|
nlh->nlmsg_len = msgsize;
|
||||||
nlh->nlmsg_type = RTM_GETLINK;
|
nlh->nlmsg_type = RTM_GETLINK;
|
||||||
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
|
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
|
||||||
nlh->nlmsg_seq = seq = time(NULL);
|
nlh->nlmsg_seq = time(NULL);
|
||||||
|
ifinfo = (struct ifinfomsg *)((char *)buf + NLMSG_HDRLEN);
|
||||||
ifinfo = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ifinfomsg));
|
|
||||||
ifinfo->ifi_family = AF_UNSPEC;
|
ifinfo->ifi_family = AF_UNSPEC;
|
||||||
|
|
||||||
if (mnl_socket_sendto(mls, nlh, nlh->nlmsg_len) < 0)
|
struct sockaddr_nl addr = {
|
||||||
|
.nl_family = AF_NETLINK,
|
||||||
|
};
|
||||||
|
if (sendto(cs->nlFd, buf, nlh->nlmsg_len, 0, (struct sockaddr *)&addr,
|
||||||
|
sizeof addr) == -1)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
// This is rather ugly, but hey!
|
||||||
|
if (fcntl(cs->nlFd, F_SETFL, fcntl(cs->nlFd, F_GETFL) & ~O_NONBLOCK) == -1)
|
||||||
|
suicide("nl_getifdata: failed to remove O_NONBLOCK");
|
||||||
handle_nl_message(cs);
|
handle_nl_message(cs);
|
||||||
|
if (fcntl(cs->nlFd, F_SETFL, fcntl(cs->nlFd, F_GETFL) | O_NONBLOCK) == -1)
|
||||||
|
suicide("nl_getifdata: failed to restore O_NONBLOCK");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#ifndef NK_NETLINK_H_
|
#ifndef NK_NETLINK_H_
|
||||||
#define NK_NETLINK_H_
|
#define NK_NETLINK_H_
|
||||||
|
|
||||||
|
#include <linux/rtnetlink.h>
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@ -30,7 +31,8 @@ enum {
|
|||||||
IFS_REMOVED
|
IFS_REMOVED
|
||||||
};
|
};
|
||||||
|
|
||||||
int nl_open(struct client_state_t *cs);
|
extern int nlportid;
|
||||||
|
|
||||||
void handle_nl_message(struct client_state_t *cs);
|
void handle_nl_message(struct client_state_t *cs);
|
||||||
int nl_getifdata(const char *ifname, struct client_state_t *cs);
|
int nl_getifdata(const char *ifname, struct client_state_t *cs);
|
||||||
|
|
||||||
|
137
ndhc/nl.c
Normal file
137
ndhc/nl.c
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "log.h"
|
||||||
|
#include "nl.h"
|
||||||
|
|
||||||
|
void nl_attr_parse(const struct nlmsghdr *nlh, size_t offset,
|
||||||
|
nl_attr_parse_fn workfn, void *data)
|
||||||
|
{
|
||||||
|
struct nlattr *attr;
|
||||||
|
for (attr = (struct nlattr *)
|
||||||
|
((char *)nlh + NLMSG_HDRLEN + NLMSG_ALIGN(offset));
|
||||||
|
nl_attr_ok(attr, (char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len) -
|
||||||
|
(char *)attr);
|
||||||
|
attr = (struct nlattr *)((char *)attr + NLMSG_ALIGN(attr->nla_len)))
|
||||||
|
{
|
||||||
|
int type = attr->nla_type & NLA_TYPE_MASK;
|
||||||
|
if (workfn(attr, type, data) < 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t nl_recv_buf(int fd, char *buf, size_t blen)
|
||||||
|
{
|
||||||
|
struct sockaddr_nl addr;
|
||||||
|
struct iovec iov = {
|
||||||
|
.iov_base = buf,
|
||||||
|
.iov_len = blen,
|
||||||
|
};
|
||||||
|
struct msghdr msg = {
|
||||||
|
.msg_name = &addr,
|
||||||
|
.msg_namelen = sizeof addr,
|
||||||
|
.msg_iov = &iov,
|
||||||
|
.msg_iovlen = 1,
|
||||||
|
};
|
||||||
|
size_t ret = recvmsg(fd, &msg, 0);
|
||||||
|
if (ret == -1) {
|
||||||
|
if (errno != EAGAIN && errno != EWOULDBLOCK)
|
||||||
|
log_error("nl_fill_buf: recvmsg failed: %s", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (msg.msg_flags & MSG_TRUNC) {
|
||||||
|
log_error("nl_fill_buf: buffer not long enough for message");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (msg.msg_namelen != sizeof addr) {
|
||||||
|
log_error("nl_fill_buf: response was not of the same address family");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nl_foreach_nlmsg(char *buf, size_t blen, int portid,
|
||||||
|
nlmsg_foreach_fn pfn, void *fnarg)
|
||||||
|
{
|
||||||
|
const struct nlmsghdr *nlh = (const struct nlmsghdr *)buf;
|
||||||
|
|
||||||
|
assert(pfn);
|
||||||
|
while (NLMSG_OK(nlh, blen)) {
|
||||||
|
// PortID should be zero for messages from the kernel.
|
||||||
|
if (nlh->nlmsg_pid && nlh->nlmsg_pid != portid)
|
||||||
|
continue;
|
||||||
|
// XXX don't bother with sequence # tracking (0 = kernel, ours = ??)
|
||||||
|
|
||||||
|
if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {
|
||||||
|
pfn(nlh, fnarg);
|
||||||
|
} else {
|
||||||
|
switch (nlh->nlmsg_type) {
|
||||||
|
case NLMSG_ERROR:
|
||||||
|
log_line("nl: received a NLMSG_ERROR: %s",
|
||||||
|
strerror(nlmsg_get_error(nlh)));
|
||||||
|
return -1;
|
||||||
|
case NLMSG_DONE:
|
||||||
|
return 0;
|
||||||
|
case NLMSG_OVERRUN:
|
||||||
|
log_line("nl: received a NLMSG_OVERRUN");
|
||||||
|
case NLMSG_NOOP:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nlh = NLMSG_NEXT(nlh, blen);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nl_open(int nltype, int nlgroup, int *nlportid)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
fd = socket(AF_NETLINK, SOCK_RAW, nltype);
|
||||||
|
if (fd == -1) {
|
||||||
|
log_error("nl_open: socket failed: %s", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK) == -1) {
|
||||||
|
log_error("nl_open: set non-blocking failed: %s", strerror(errno));
|
||||||
|
goto err_close;
|
||||||
|
}
|
||||||
|
if (fcntl(fd, F_SETFD, FD_CLOEXEC)) {
|
||||||
|
log_error("nl_open: set close-on-exec failed: %s", strerror(errno));
|
||||||
|
goto err_close;
|
||||||
|
}
|
||||||
|
socklen_t al;
|
||||||
|
struct sockaddr_nl nlsock = {
|
||||||
|
.nl_family = AF_NETLINK,
|
||||||
|
.nl_groups = nlgroup,
|
||||||
|
};
|
||||||
|
if (bind(fd, (struct sockaddr *)&nlsock, sizeof nlsock) == -1) {
|
||||||
|
log_error("nl_open: bind to group failed: %s", strerror(errno));
|
||||||
|
goto err_close;
|
||||||
|
}
|
||||||
|
al = sizeof nlsock;
|
||||||
|
if (getsockname(fd, (struct sockaddr *)&nlsock, &al) == -1) {
|
||||||
|
log_error("nl_open: getsockname failed: %s", strerror(errno));
|
||||||
|
goto err_close;
|
||||||
|
}
|
||||||
|
if (al != sizeof nlsock) {
|
||||||
|
log_error("nl_open: bound socket doesn't have right family size");
|
||||||
|
goto err_close;
|
||||||
|
}
|
||||||
|
if (nlsock.nl_family != AF_NETLINK) {
|
||||||
|
log_error("nl_open: bound socket isn't AF_NETLINK");
|
||||||
|
goto err_close;
|
||||||
|
}
|
||||||
|
if (nlportid)
|
||||||
|
*nlportid = nlsock.nl_pid;
|
||||||
|
return fd;
|
||||||
|
err_close:
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
55
ndhc/nl.h
Normal file
55
ndhc/nl.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#ifndef NK_NL_H_
|
||||||
|
#define NK_NL_H_
|
||||||
|
|
||||||
|
// Limited netlink code. The horrors...
|
||||||
|
|
||||||
|
#include <linux/netlink.h>
|
||||||
|
//#include <linux/rtnetlink.h>
|
||||||
|
|
||||||
|
static inline int nl_attr_ok(const struct nlattr *attr, size_t len)
|
||||||
|
{
|
||||||
|
if (len < sizeof *attr)
|
||||||
|
return 0;
|
||||||
|
if (attr->nla_len < sizeof *attr)
|
||||||
|
return 0;
|
||||||
|
if (attr->nla_len > len)
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t nlattr_get_len(const struct nlattr *attr)
|
||||||
|
{
|
||||||
|
return attr->nla_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *nlattr_get_data(const struct nlattr *attr)
|
||||||
|
{
|
||||||
|
return (char *)attr + NLA_HDRLEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *nlmsg_get_data(const struct nlmsghdr *nlh)
|
||||||
|
{
|
||||||
|
return (char *)nlh + NLMSG_HDRLEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int nlmsg_get_error(const struct nlmsghdr *nlh)
|
||||||
|
{
|
||||||
|
const struct nlmsgerr *err = nlmsg_get_data(nlh);
|
||||||
|
if (nlh->nlmsg_len < sizeof(struct nlmsgerr) + NLMSG_HDRLEN)
|
||||||
|
return EBADMSG;
|
||||||
|
return err->error & 0x7fffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef int (*nl_attr_parse_fn)(struct nlattr *attr, int type, void *data);
|
||||||
|
void nl_attr_parse(const struct nlmsghdr *nlh, size_t offset,
|
||||||
|
nl_attr_parse_fn workfn, void *data);
|
||||||
|
|
||||||
|
ssize_t nl_recv_buf(int fd, char *buf, size_t blen);
|
||||||
|
|
||||||
|
typedef int (*nlmsg_foreach_fn)(const struct nlmsghdr *, void *);
|
||||||
|
int nl_foreach_nlmsg(char *buf, size_t blen, int portid,
|
||||||
|
nlmsg_foreach_fn pfn, void *fnarg);
|
||||||
|
|
||||||
|
int nl_open(int nltype, int nlgroup, int *nlportid);
|
||||||
|
|
||||||
|
#endif /* NK_NL_H_ */
|
Loading…
Reference in New Issue
Block a user