#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
-#include <syslog.h>
#include <fcntl.h>
#include <net/if_arp.h>
#include <sys/socket.h>
#include <errno.h>
#include <time.h>
#include <sys/uio.h>
+#include <linux/if_addrlabel.h>
+#include <linux/if_bridge.h>
#include "libnetlink.h"
}
/* dump netlink extended ack error message */
-static int nl_dump_ext_err(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
+int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
{
struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {};
const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
const struct nlmsghdr *err_nlh = NULL;
unsigned int hlen = sizeof(*err);
- const char *errmsg = NULL;
+ const char *msg = NULL;
uint32_t off = 0;
/* no TLVs, nothing to do here */
return 0;
if (tb[NLMSGERR_ATTR_MSG])
- errmsg = mnl_attr_get_str(tb[NLMSGERR_ATTR_MSG]);
+ msg = mnl_attr_get_str(tb[NLMSGERR_ATTR_MSG]);
if (tb[NLMSGERR_ATTR_OFFS]) {
off = mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]);
}
if (errfn)
- return errfn(errmsg, off, err_nlh);
+ return errfn(msg, off, err_nlh);
- if (errmsg && *errmsg != '\0') {
- fprintf(stderr, "Error: %s", errmsg);
- if (errmsg[strlen(errmsg) - 1] != '.')
+ if (msg && *msg != '\0') {
+ bool is_err = !!err->error;
+
+ fprintf(stderr, "%s: %s",
+ is_err ? "Error" : "Warning", msg);
+ if (msg[strlen(msg) - 1] != '.')
fprintf(stderr, ".");
fprintf(stderr, "\n");
- return 1;
+ return is_err ? 1 : 0;
}
return 0;
#warning "libmnl required for error support"
/* No extended error ack without libmnl */
-static int nl_dump_ext_err(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
+int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
{
return 0;
}
return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);
}
+int rtnl_addrdump_req(struct rtnl_handle *rth, int family)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct ifaddrmsg ifm;
+ } req = {
+ .nlh.nlmsg_len = sizeof(req),
+ .nlh.nlmsg_type = RTM_GETADDR,
+ .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+ .nlh.nlmsg_seq = rth->dump = ++rth->seq,
+ .ifm.ifa_family = family,
+ };
+
+ return send(rth->fd, &req, sizeof(req), 0);
+}
+
+int rtnl_addrlbldump_req(struct rtnl_handle *rth, int family)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct ifaddrlblmsg ifal;
+ } req = {
+ .nlh.nlmsg_len = sizeof(req),
+ .nlh.nlmsg_type = RTM_GETADDRLABEL,
+ .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+ .nlh.nlmsg_seq = rth->dump = ++rth->seq,
+ .ifal.ifal_family = family,
+ };
+
+ return send(rth->fd, &req, sizeof(req), 0);
+}
+
+int rtnl_routedump_req(struct rtnl_handle *rth, int family)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct rtmsg rtm;
+ } req = {
+ .nlh.nlmsg_len = sizeof(req),
+ .nlh.nlmsg_type = RTM_GETROUTE,
+ .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+ .nlh.nlmsg_seq = rth->dump = ++rth->seq,
+ .rtm.rtm_family = family,
+ };
+
+ return send(rth->fd, &req, sizeof(req), 0);
+}
+
+int rtnl_mdbdump_req(struct rtnl_handle *rth, int family)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct br_port_msg bpm;
+ } req = {
+ .nlh.nlmsg_len = sizeof(req),
+ .nlh.nlmsg_type = RTM_GETMDB,
+ .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+ .nlh.nlmsg_seq = rth->dump = ++rth->seq,
+ .bpm.family = family,
+ };
+
+ return send(rth->fd, &req, sizeof(req), 0);
+}
+
int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
{
return rtnl_wilddump_req_filter(rth, family, type, RTEXT_FILTER_VF);
return len;
}
+ /* check for any messages returned from kernel */
+ nl_dump_ext_ack(h, NULL);
+
return 0;
}
static void rtnl_talk_error(struct nlmsghdr *h, struct nlmsgerr *err,
nl_ext_ack_fn_t errfn)
{
- if (nl_dump_ext_err(h, errfn))
+ if (nl_dump_ext_ack(h, errfn))
return;
fprintf(stderr, "RTNETLINK answers: %s\n",
strerror(-err->error));
}
-static int __rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
- struct nlmsghdr **answer,
- bool show_rtnl_err, nl_ext_ack_fn_t errfn)
+
+static int __rtnl_talk_iov(struct rtnl_handle *rtnl, struct iovec *iov,
+ size_t iovlen, struct nlmsghdr **answer,
+ bool show_rtnl_err, nl_ext_ack_fn_t errfn)
{
- int status;
- unsigned int seq;
- struct nlmsghdr *h;
struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
- struct iovec iov = {
- .iov_base = n,
- .iov_len = n->nlmsg_len
- };
+ struct iovec riov;
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
- .msg_iov = &iov,
- .msg_iovlen = 1,
+ .msg_iov = iov,
+ .msg_iovlen = iovlen,
};
+ unsigned int seq = 0;
+ struct nlmsghdr *h;
+ int i, status;
char *buf;
- n->nlmsg_seq = seq = ++rtnl->seq;
-
- if (answer == NULL)
- n->nlmsg_flags |= NLM_F_ACK;
+ for (i = 0; i < iovlen; i++) {
+ h = iov[i].iov_base;
+ h->nlmsg_seq = seq = ++rtnl->seq;
+ if (answer == NULL)
+ h->nlmsg_flags |= NLM_F_ACK;
+ }
status = sendmsg(rtnl->fd, &msg, 0);
if (status < 0) {
return -1;
}
+ /* change msg to use the response iov */
+ msg.msg_iov = &riov;
+ msg.msg_iovlen = 1;
+ i = 0;
while (1) {
status = rtnl_recvmsg(rtnl->fd, &msg, &buf);
+ ++i;
if (status < 0)
return status;
if (nladdr.nl_pid != 0 ||
h->nlmsg_pid != rtnl->local.nl_pid ||
- h->nlmsg_seq != seq) {
+ h->nlmsg_seq > seq || h->nlmsg_seq < seq - iovlen) {
/* Don't forget to skip that message. */
status -= NLMSG_ALIGN(len);
h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len));
if (l < sizeof(struct nlmsgerr)) {
fprintf(stderr, "ERROR truncated\n");
- } else if (!err->error) {
- if (answer)
- *answer = (struct nlmsghdr *)buf;
- else
- free(buf);
- return 0;
+ free(buf);
+ return -1;
}
- if (rtnl->proto != NETLINK_SOCK_DIAG &&
- show_rtnl_err)
- rtnl_talk_error(h, err, errfn);
+ if (!err->error)
+ /* check messages from kernel */
+ nl_dump_ext_ack(h, errfn);
+ else {
+ errno = -err->error;
- errno = -err->error;
- free(buf);
- return -1;
+ if (rtnl->proto != NETLINK_SOCK_DIAG &&
+ show_rtnl_err)
+ rtnl_talk_error(h, err, errfn);
+ }
+
+ if (answer)
+ *answer = (struct nlmsghdr *)buf;
+ else
+ free(buf);
+
+ return err->error ? -i : 0;
}
if (answer) {
}
}
+static int __rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
+ struct nlmsghdr **answer,
+ bool show_rtnl_err, nl_ext_ack_fn_t errfn)
+{
+ struct iovec iov = {
+ .iov_base = n,
+ .iov_len = n->nlmsg_len
+ };
+
+ return __rtnl_talk_iov(rtnl, &iov, 1, answer, show_rtnl_err, errfn);
+}
+
int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
struct nlmsghdr **answer)
{
return __rtnl_talk(rtnl, n, answer, true, NULL);
}
+int rtnl_talk_iov(struct rtnl_handle *rtnl, struct iovec *iovec, size_t iovlen,
+ struct nlmsghdr **answer)
+{
+ return __rtnl_talk_iov(rtnl, iovec, iovlen, answer, true, NULL);
+}
+
int rtnl_talk_extack(struct rtnl_handle *rtnl, struct nlmsghdr *n,
struct nlmsghdr **answer,
nl_ext_ack_fn_t errfn)