summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/Makefile.am15
-rw-r--r--examples/genl-family-get.c113
-rw-r--r--examples/rtnl-link-dump.c32
-rw-r--r--examples/rtnl-link-dump2.c103
-rw-r--r--examples/rtnl-link-dump3.c101
-rw-r--r--examples/rtnl-link-event.c32
-rw-r--r--examples/rtnl-route-dump.c55
-rw-r--r--include/libmnl/libmnl.h53
-rw-r--r--src/attr.c188
9 files changed, 617 insertions, 75 deletions
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 92c5342..1874971 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -1,12 +1,23 @@
include $(top_srcdir)/Make_global.am
-check_PROGRAMS = rtnl-link-dump rtnl-link-event rtnl-link-set \
- rtnl-route-dump genl-family-get
+check_PROGRAMS = rtnl-link-dump rtnl-link-dump2 rtnl-link-dump3 \
+ rtnl-link-event \
+ rtnl-link-set \
+ rtnl-route-dump \
+ genl-family-get
rtnl_link_dump_SOURCES = rtnl-link-dump.c
rtnl_link_dump_LDADD = ../src/libmnl.la
rtnl_link_dump_LDFLAGS = -dynamic -ldl
+rtnl_link_dump2_SOURCES = rtnl-link-dump2.c
+rtnl_link_dump2_LDADD = ../src/libmnl.la
+rtnl_link_dump2_LDFLAGS = -dynamic -ldl
+
+rtnl_link_dump3_SOURCES = rtnl-link-dump3.c
+rtnl_link_dump3_LDADD = ../src/libmnl.la
+rtnl_link_dump3_LDFLAGS = -dynamic -ldl
+
rtnl_link_event_SOURCES = rtnl-link-event.c
rtnl_link_event_LDADD = ../src/libmnl.la
rtnl_link_event_LDFLAGS = -dynamic -ldl
diff --git a/examples/genl-family-get.c b/examples/genl-family-get.c
index 3e741cb..00f601c 100644
--- a/examples/genl-family-get.c
+++ b/examples/genl-family-get.c
@@ -5,15 +5,43 @@
#include <libmnl/libmnl.h>
#include <linux/genetlink.h>
+static int parse_mc_grps_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = (const struct nlattr **)data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_invalid(attr, CTRL_ATTR_MCAST_GRP_MAX) < 0) {
+ perror("mnl_attr_type_invalid");
+ return MNL_CB_ERROR;
+ }
+
+ switch(type) {
+ case CTRL_ATTR_MCAST_GRP_ID:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case CTRL_ATTR_MCAST_GRP_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
static void parse_genl_mc_grps(struct nlattr *nested)
{
struct nlattr *pos;
int len;
- mnl_attr_for_each_nested(pos, nested, len) {
- struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX+1];
+ mnl_attr_for_each_nested(pos, nested) {
+ struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX+1] = {};
- mnl_attr_parse_nested(pos, tb, CTRL_ATTR_MCAST_GRP_MAX);
+ mnl_attr_parse_nested(pos, parse_mc_grps_cb, tb);
if (tb[CTRL_ATTR_MCAST_GRP_ID]) {
printf("id-0x%x ",
mnl_attr_get_u32(tb[CTRL_ATTR_MCAST_GRP_ID]));
@@ -26,15 +54,41 @@ static void parse_genl_mc_grps(struct nlattr *nested)
}
}
+static int parse_family_ops_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = (const struct nlattr **)data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_invalid(attr, CTRL_ATTR_OP_MAX) < 0) {
+ perror("mnl_attr_type_invalid");
+ return MNL_CB_ERROR;
+ }
+
+ switch(type) {
+ case CTRL_ATTR_OP_ID:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case CTRL_ATTR_OP_MAX:
+ break;
+ default:
+ return MNL_CB_OK;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
static void parse_genl_family_ops(struct nlattr *nested)
{
struct nlattr *pos;
int len;
- mnl_attr_for_each_nested(pos, nested, len) {
- struct nlattr *tb[CTRL_ATTR_OP_MAX+1];
+ mnl_attr_for_each_nested(pos, nested) {
+ struct nlattr *tb[CTRL_ATTR_OP_MAX+1] = {};
- mnl_attr_parse_nested(pos, tb, CTRL_ATTR_OP_MAX);
+ mnl_attr_parse_nested(pos, parse_family_ops_cb, tb);
if (tb[CTRL_ATTR_OP_ID]) {
printf("id-0x%x ",
mnl_attr_get_u32(tb[CTRL_ATTR_OP_ID]));
@@ -46,12 +100,55 @@ static void parse_genl_family_ops(struct nlattr *nested)
}
}
+static int data_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = (const struct nlattr **)data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_invalid(attr, CTRL_ATTR_MAX) < 0) {
+ perror("mnl_attr_type_invalid");
+ return MNL_CB_ERROR;
+ }
+
+ switch(type) {
+ case CTRL_ATTR_FAMILY_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case CTRL_ATTR_FAMILY_ID:
+ if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case CTRL_ATTR_VERSION:
+ case CTRL_ATTR_HDRSIZE:
+ case CTRL_ATTR_MAXATTR:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case CTRL_ATTR_OPS:
+ case CTRL_ATTR_MCAST_GROUPS:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
static int data_cb(const struct nlmsghdr *nlh, void *data)
{
- struct nlattr *tb[CTRL_ATTR_MAX+1];
+ struct nlattr *tb[CTRL_ATTR_MAX+1] = {};
struct genlmsghdr *genl = mnl_nlmsg_get_data(nlh);
- mnl_attr_parse_at_offset(nlh, sizeof(*genl), tb, CTRL_ATTR_MAX);
+ mnl_attr_parse(nlh, sizeof(*genl), data_attr_cb, tb);
if (tb[CTRL_ATTR_FAMILY_NAME]) {
printf("name=%s\t",
mnl_attr_get_str(tb[CTRL_ATTR_FAMILY_NAME]));
diff --git a/examples/rtnl-link-dump.c b/examples/rtnl-link-dump.c
index 7cf061d..9e3f114 100644
--- a/examples/rtnl-link-dump.c
+++ b/examples/rtnl-link-dump.c
@@ -7,9 +7,37 @@
#include <linux/if_link.h>
#include <linux/rtnetlink.h>
+static int data_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = (const struct nlattr **)data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_invalid(attr, IFLA_MAX) < 0) {
+ perror("mnl_attr_type_invalid");
+ return MNL_CB_ERROR;
+ }
+
+ switch(type) {
+ case IFLA_MTU:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case IFLA_IFNAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
+ perror("mnl_attr_validate2");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
static int data_cb(const struct nlmsghdr *nlh, void *data)
{
- struct nlattr *tb[IFLA_MAX+1];
+ struct nlattr *tb[IFLA_MAX+1] = {};
struct ifinfomsg *ifm = mnl_nlmsg_get_data(nlh);
int len = mnl_nlmsg_get_len(nlh);
struct nlattr *attr;
@@ -23,7 +51,7 @@ static int data_cb(const struct nlmsghdr *nlh, void *data)
else
printf("[NOT RUNNING] ");
- mnl_attr_parse_at_offset(nlh, sizeof(*ifm), tb, IFLA_MAX);
+ mnl_attr_parse(nlh, sizeof(*ifm), data_attr_cb, tb);
if (tb[IFLA_MTU]) {
printf("mtu=%d ", mnl_attr_get_u32(tb[IFLA_MTU]));
}
diff --git a/examples/rtnl-link-dump2.c b/examples/rtnl-link-dump2.c
new file mode 100644
index 0000000..dc44c54
--- /dev/null
+++ b/examples/rtnl-link-dump2.c
@@ -0,0 +1,103 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/if.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+static int data_attr_cb(const struct nlattr *attr, void *data)
+{
+ if (mnl_attr_type_invalid(attr, IFLA_MAX) < 0) {
+ perror("mnl_attr_type_invalid");
+ return MNL_CB_ERROR;
+ }
+
+ switch(mnl_attr_get_type(attr)) {
+ case IFLA_MTU:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ printf("mtu=%d ", mnl_attr_get_u32(attr));
+ break;
+ case IFLA_IFNAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
+ perror("mnl_attr_validate2");
+ return MNL_CB_ERROR;
+ }
+ printf("name=%s ", mnl_attr_get_str(attr));
+ break;
+ }
+ return MNL_CB_OK;
+}
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct ifinfomsg *ifm = mnl_nlmsg_get_data(nlh);
+ int len = mnl_nlmsg_get_len(nlh);
+ struct nlattr *attr;
+
+ printf("index=%d type=%d flags=%d family=%d ",
+ ifm->ifi_index, ifm->ifi_type,
+ ifm->ifi_flags, ifm->ifi_family);
+
+ if (ifm->ifi_flags & IFF_RUNNING)
+ printf("[RUNNING] ");
+ else
+ printf("[NOT RUNNING] ");
+
+ mnl_attr_parse(nlh, sizeof(*ifm), data_attr_cb, NULL);
+ printf("\n");
+ return MNL_CB_OK;
+}
+
+int main()
+{
+ struct mnl_socket *nl;
+ char buf[getpagesize()];
+ struct nlmsghdr *nlh;
+ struct rtgenmsg *rt;
+ int ret;
+ unsigned int seq;
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = RTM_GETLINK;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ nlh->nlmsg_seq = seq = time(NULL);
+ rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
+ rt->rtgen_family = AF_PACKET;
+
+ nl = mnl_socket_open(NETLINK_ROUTE);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_sendto(nl, nlh, mnl_nlmsg_get_len(nlh)) < 0) {
+ perror("mnl_socket_send");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, seq, data_cb, NULL);
+ if (ret <= MNL_CB_STOP)
+ break;
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+ if (ret == -1) {
+ perror("error");
+ exit(EXIT_FAILURE);
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/examples/rtnl-link-dump3.c b/examples/rtnl-link-dump3.c
new file mode 100644
index 0000000..d5e4458
--- /dev/null
+++ b/examples/rtnl-link-dump3.c
@@ -0,0 +1,101 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/if.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct ifinfomsg *ifm = mnl_nlmsg_get_data(nlh);
+ int len = mnl_nlmsg_get_len(nlh);
+ struct nlattr *attr;
+
+ printf("index=%d type=%d flags=%d family=%d ",
+ ifm->ifi_index, ifm->ifi_type,
+ ifm->ifi_flags, ifm->ifi_family);
+
+ if (ifm->ifi_flags & IFF_RUNNING)
+ printf("[RUNNING] ");
+ else
+ printf("[NOT RUNNING] ");
+
+ mnl_attr_for_each(attr, nlh, sizeof(*ifm)) {
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_invalid(attr, IFLA_MAX) < 0) {
+ perror("mnl_attr_type_invalid");
+ return MNL_CB_ERROR;
+ }
+ switch(type) {
+ case IFLA_MTU:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ printf("mtu=%d ", mnl_attr_get_u32(attr));
+ break;
+ case IFLA_IFNAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ printf("name=%s ", mnl_attr_get_str(attr));
+ break;
+ }
+ }
+ printf("\n");
+
+ return MNL_CB_OK;
+}
+
+int main()
+{
+ struct mnl_socket *nl;
+ char buf[getpagesize()];
+ struct nlmsghdr *nlh;
+ struct rtgenmsg *rt;
+ int ret;
+ unsigned int seq;
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = RTM_GETLINK;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ nlh->nlmsg_seq = seq = time(NULL);
+ rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
+ rt->rtgen_family = AF_PACKET;
+
+ nl = mnl_socket_open(NETLINK_ROUTE);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_sendto(nl, nlh, mnl_nlmsg_get_len(nlh)) < 0) {
+ perror("mnl_socket_send");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, seq, data_cb, NULL);
+ if (ret <= MNL_CB_STOP)
+ break;
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+ if (ret == -1) {
+ perror("error");
+ exit(EXIT_FAILURE);
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/examples/rtnl-link-event.c b/examples/rtnl-link-event.c
index ed5a577..3e25b6f 100644
--- a/examples/rtnl-link-event.c
+++ b/examples/rtnl-link-event.c
@@ -7,9 +7,37 @@
#include <linux/if_link.h>
#include <linux/rtnetlink.h>
+static int data_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = (const struct nlattr **)data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_invalid(attr, IFLA_MAX) < 0) {
+ perror("mnl_attr_type_invalid");
+ return MNL_CB_ERROR;
+ }
+
+ switch(type) {
+ case IFLA_MTU:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case IFLA_IFNAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
+ perror("mnl_attr_validate2");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
static int data_cb(const struct nlmsghdr *nlh, void *data)
{
- struct nlattr *tb[IFLA_MAX+1];
+ struct nlattr *tb[IFLA_MAX+1] = {};
struct ifinfomsg *ifm = mnl_nlmsg_get_data(nlh);
int len = mnl_nlmsg_get_len(nlh);
struct nlattr *attr;
@@ -23,7 +51,7 @@ static int data_cb(const struct nlmsghdr *nlh, void *data)
else
printf("[NOT RUNNING] ");
- mnl_attr_parse_at_offset(nlh, sizeof(*ifm), tb, IFLA_MAX);
+ mnl_attr_parse(nlh, sizeof(*ifm), data_attr_cb, tb);
if (tb[IFLA_MTU]) {
printf("mtu=%d ", mnl_attr_get_u32(tb[IFLA_MTU]));
}
diff --git a/examples/rtnl-route-dump.c b/examples/rtnl-route-dump.c
index e8f3a0c..eb36bbc 100644
--- a/examples/rtnl-route-dump.c
+++ b/examples/rtnl-route-dump.c
@@ -7,6 +7,22 @@
#include <linux/if_link.h>
#include <linux/rtnetlink.h>
+static int data_attr_cb2(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = (const struct nlattr **)data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_invalid(attr, RTAX_MAX) < 0) {
+ perror("mnl_attr_type_invalid");
+ return MNL_CB_ERROR;
+ }
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ return MNL_CB_OK;
+}
+
static void attributes_show_ipv4(struct nlattr *tb[])
{
if (tb[RTA_TABLE]) {
@@ -35,7 +51,7 @@ static void attributes_show_ipv4(struct nlattr *tb[])
int i;
struct nlattr *tbx[RTAX_MAX+1] = {};
- mnl_attr_parse_nested(tb[RTA_METRICS], tbx, RTAX_MAX);
+ mnl_attr_parse_nested(tb[RTA_METRICS], data_attr_cb2, tbx);
for (i=0; i<RTAX_MAX; i++) {
if (tbx[i]) {
@@ -47,9 +63,42 @@ static void attributes_show_ipv4(struct nlattr *tb[])
printf("\n");
}
+static int data_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = (const struct nlattr **)data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_invalid(attr, RTA_MAX) < 0) {
+ perror("mnl_attr_type_invalid");
+ return MNL_CB_ERROR;
+ }
+
+ switch(type) {
+ case RTA_TABLE:
+ case RTA_DST:
+ case RTA_SRC:
+ case RTA_OIF:
+ case RTA_FLOW:
+ case RTA_PREFSRC:
+ case RTA_GATEWAY:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case RTA_METRICS:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
static int data_cb(const struct nlmsghdr *nlh, void *data)
{
- /* parse() ya está inicializando este array, qué hacer ? */
struct nlattr *tb[RTA_MAX+1] = {};
struct rtmsg *rm = mnl_nlmsg_get_data(nlh);
int len = mnl_nlmsg_get_len(nlh);
@@ -131,7 +180,7 @@ static int data_cb(const struct nlmsghdr *nlh, void *data)
*/
printf("flags=%x\n", rm->rtm_flags);
- mnl_attr_parse_at_offset(nlh, sizeof(*rm), tb, RTA_MAX);
+ mnl_attr_parse(nlh, sizeof(*rm), data_attr_cb, tb);
switch(rm->rtm_family) {
case AF_INET:
diff --git a/include/libmnl/libmnl.h b/include/libmnl/libmnl.h
index e114cef..6a2b8a6 100644
--- a/include/libmnl/libmnl.h
+++ b/include/libmnl/libmnl.h
@@ -29,8 +29,9 @@ extern int mnl_socket_getsockopt(const struct mnl_socket *nl, int type, void *bu
* generic netlink message API
*/
-#define MNL_ALIGNTO 4
-#define MNL_NLMSG_HDRLEN mnl_align(sizeof(struct nlmsghdr))
+#define MNL_ALIGNTO 4
+#define MNL_ALIGN(len) (((len)+MNL_ALIGNTO-1) & ~(MNL_ALIGNTO-1))
+#define MNL_NLMSG_HDRLEN MNL_ALIGN(sizeof(struct nlmsghdr))
extern int mnl_align(int len);
extern size_t mnl_nlmsg_size(int len);
@@ -60,7 +61,7 @@ extern void mnl_nlmsg_print(const struct nlmsghdr *nlh);
/*
* generic netlink attributes API
*/
-#define MNL_ATTR_HDRLEN mnl_align(sizeof(struct nlattr))
+#define MNL_ATTR_HDRLEN MNL_ALIGN(sizeof(struct nlattr))
/* TLV attribute getters */
extern uint16_t mnl_attr_get_type(const struct nlattr *attr);
@@ -82,17 +83,47 @@ extern void mnl_attr_put_u64(struct nlmsghdr *nlh, int type, uint64_t data);
extern void mnl_attr_put_str(struct nlmsghdr *nlh, int type, const void *data);
extern void mnl_attr_put_str_null(struct nlmsghdr *nlh, int type, const void *data);
-/* TLV attribute parsers */
-extern int mnl_attr_parse(const struct nlmsghdr *nlh, struct nlattr *tb[], int max);
-extern int mnl_attr_parse_at_offset(const struct nlmsghdr *nlh, int offset, struct nlattr *tb[], int max);
-extern int mnl_attr_parse_nested(const struct nlattr *attr, struct nlattr *tb[], int max);
+/* TLV validation */
+enum mnl_attr_data_type {
+ MNL_TYPE_UNSPEC,
+ MNL_TYPE_U8,
+ MNL_TYPE_U16,
+ MNL_TYPE_U32,
+ MNL_TYPE_U64,
+ MNL_TYPE_STRING,
+ MNL_TYPE_FLAG,
+ MNL_TYPE_MSECS,
+ MNL_TYPE_NESTED,
+ MNL_TYPE_NESTED_COMPAT,
+ MNL_TYPE_NUL_STRING,
+ MNL_TYPE_BINARY,
+ MNL_TYPE_MAX,
+};
+
+extern int mnl_attr_validate(const struct nlattr *attr, enum mnl_attr_data_type type);
+extern int mnl_attr_validate2(const struct nlattr *attr, enum mnl_attr_data_type type, int minlen);
+
+/* TLV iterators */
extern int mnl_attr_ok(const struct nlattr *attr, int len);
extern struct nlattr *mnl_attr_next(const struct nlattr *attr, int *len);
-#define mnl_attr_for_each_nested(pos, head, len) \
- for (pos = mnl_attr_get_data(head), len = mnl_attr_get_len(head); \
- mnl_attr_ok(pos, len); \
- pos = mnl_attr_next(pos, &(len)))
+#define mnl_attr_for_each(attr, nlh, offset) \
+ int __len__ = mnl_nlmsg_payload_size(nlh); \
+ for (attr = mnl_nlmsg_get_data_offset(nlh, offset); \
+ mnl_attr_ok(attr, __len__); \
+ attr = mnl_attr_next(attr, &(__len__)))
+
+#define mnl_attr_for_each_nested(attr, nest) \
+ int __len__ = mnl_attr_get_len(nest); \
+ for (pos = mnl_attr_get_data(nest); \
+ mnl_attr_ok(attr, __len__); \
+ pos = mnl_attr_next(attr, &(__len__)))
+
+/* TLV callback-based attribute parsers */
+typedef int (*mnl_attr_cb_t)(const struct nlattr *attr, void *data);
+
+extern int mnl_attr_parse(const struct nlmsghdr *nlh, int offset, mnl_attr_cb_t cb, void *data);
+extern int mnl_attr_parse_nested(const struct nlattr *attr, mnl_attr_cb_t cb, void *data);
/*
* callback API
diff --git a/src/attr.c b/src/attr.c
index 0c09aef..cc4a774 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -9,6 +9,8 @@
#include <libmnl/libmnl.h>
#include <string.h>
+#include <values.h> /* for INT_MAX */
+#include <errno.h>
/*
* Netlink Type-Length-Value (TLV) attribute:
@@ -94,80 +96,172 @@ struct nlattr *mnl_attr_next(const struct nlattr *attr, int *len)
}
/**
- * mnl_attr_parse_at_offset - returns an array of attributes from offset
- * @nlh: pointer to netlink message
- * @offset: offset to start parse from
- * @tb: array of pointers to the attribute found
- * @max: size of the attribute array
- *
- * This functions zeroes the array of pointers. Thus, you don't need to
- * initialize this array.
+ * mnl_attr_type_invalid - check if the attribute type is valid
+ * @attr: pointer to attribute to be checked
+ * @max: maximum attribute type
*
- * This function returns an array of pointers to the attributes that has been
- * found in a netlink payload. This function return 0 on sucess, and >0 to
- * indicate the number of bytes the remaining bytes.
+ * This function allows to check if the attribute type is higher than the
+ * maximum supported type. If the attribute type is invalid, this function
+ * returns -1 and errno is explicitly set.
*/
-int mnl_attr_parse_at_offset(const struct nlmsghdr *nlh, int offset,
- struct nlattr *tb[], int max)
+int mnl_attr_type_invalid(const struct nlattr *attr, int max)
{
- struct nlattr *attr = mnl_nlmsg_get_data_offset(nlh, offset);
- int len = mnl_nlmsg_get_len(nlh);
+ if (mnl_attr_get_type(attr) > max) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
- memset(tb, 0, sizeof(struct nlattr *) * (max + 1));
+static int __mnl_attr_validate(const struct nlattr *attr,
+ enum mnl_attr_data_type type, int exp_len)
+{
+ uint16_t attr_len = mnl_attr_get_payload_len(attr);
+ char *attr_data = mnl_attr_get_data(attr);
- while (mnl_attr_ok(attr, len)) {
- if (mnl_attr_get_type(attr) <= max)
- tb[mnl_attr_get_type(attr)] = attr;
- attr = mnl_attr_next(attr, &len);
+ if (attr_len < exp_len) {
+ errno = ERANGE;
+ return -1;
}
- return len;
+ switch(type) {
+ case MNL_TYPE_FLAG:
+ if (attr_len > 0) {
+ errno = ERANGE;
+ return -1;
+ }
+ break;
+ case MNL_TYPE_NUL_STRING:
+ if (attr_len == 0) {
+ errno = ERANGE;
+ return -1;
+ }
+ if (attr_data[attr_len-1] != '\0') {
+ errno = EINVAL;
+ return -1;
+ }
+ break;
+ case MNL_TYPE_STRING:
+ if (attr_len == 0) {
+ errno = ERANGE;
+ return -1;
+ }
+ break;
+ case MNL_TYPE_NESTED:
+ /* empty nested attributes are OK. */
+ if (attr_len == 0)
+ break;
+ /* if not empty, they must contain one header, eg. flag */
+ if (attr_len < MNL_ATTR_HDRLEN) {
+ errno = ERANGE;
+ return -1;
+ }
+ break;
+ default:
+ /* make gcc happy. */
+ break;
+ }
+ if (exp_len && attr_len > exp_len) {
+ errno = ERANGE;
+ return -1;
+ }
+ return 0;
}
/**
- * mnl_attr_parse - returns an array with the attributes in the netlink message
- * @nlh: pointer to netlink message header
- * @tb: array of pointers to the attribute found
- * @max: size of the attribute array
+ * mnl_attr_validate - validate netlink attribute (simplified version)
+ * @attr: pointer to netlink attribute that we want to validate
+ * @type: data type (see enum mnl_attr_data_type)
*
- * This functions zeroes the array of pointers. Thus, you don't need to
- * initialize this array.
+ * The validation is based on the data type. This functions returns -1 in
+ * case of error and errno is explicitly set.
+ */
+static size_t mnl_attr_data_type_len[MNL_TYPE_MAX] = {
+ [MNL_TYPE_U8] = sizeof(uint8_t),
+ [MNL_TYPE_U16] = sizeof(uint16_t),
+ [MNL_TYPE_U32] = sizeof(uint32_t),
+ [MNL_TYPE_U64] = sizeof(uint64_t),
+};
+
+int mnl_attr_validate(const struct nlattr *attr, enum mnl_attr_data_type type)
+{
+ int exp_len;
+
+ if (type < 0 || type >= MNL_TYPE_MAX) {
+ errno = EINVAL;
+ return -1;
+ }
+ exp_len = mnl_attr_data_type_len[type];
+ return __mnl_attr_validate(attr, type, exp_len);
+}
+
+/**
+ * mnl_attr_validate2 - validate netlink attribute (extended version)
+ * @attr: pointer to netlink attribute that we want to validate
+ * @type: attribute type (see enum mnl_attr_data_type)
+ * @exp_len: expected attribute data size
*
- * This function returns an array of pointers to the attributes that has been
- * found in a netlink payload. This function return 0 on sucess, and >0 to
- * indicate the number of bytes the remaining bytes.
+ * This function allows to perform a more accurate validation for attributes
+ * whose size is variable. If the size of the attribute is not what we expect,
+ * this functions returns -1 and errno is explicitly set.
*/
-int mnl_attr_parse(const struct nlmsghdr *nlh, struct nlattr *tb[], int max)
+int mnl_attr_validate2(const struct nlattr *attr,
+ enum mnl_attr_data_type type, int exp_len)
{
- return mnl_attr_parse_at_offset(nlh, 0, tb, max);
+ if (type < 0 || type >= MNL_TYPE_MAX) {
+ errno = EINVAL;
+ return -1;
+ }
+ return __mnl_attr_validate(attr, type, exp_len);
}
/**
- * mnl_attr_parse_nested - returns an array with the attributes from nested
- * @nested: pointer to netlink attribute that contains a nest
- * @tb: array of pointers to the attribute found
- * @max: size of the attribute array
+ * mnl_attr_parse - parse attributes
+ * @nlh: pointer to netlink message
+ * @offset: offset to start parsing from
+ * @cb: callback function
+ * @data: pointer to data passed to the callback function
*
- * This functions zeroes the array of pointers. Thus, you don't need to
- * initialize this array.
+ * This function propagates the return value of the callback that can be
+ * MNL_CB_ERROR, MNL_CB_OK or MNL_CB_STOP.
+ */
+int mnl_attr_parse(const struct nlmsghdr *nlh, int offset,
+ mnl_attr_cb_t cb, void *data)
+{
+ int ret = MNL_CB_OK;
+ struct nlattr *attr = mnl_nlmsg_get_data_offset(nlh, offset);
+ int len = mnl_nlmsg_get_len(nlh);
+
+ while (mnl_attr_ok(attr, len)) {
+ if (cb && (ret = cb(attr, data)) <= MNL_CB_STOP)
+ return ret;
+ attr = mnl_attr_next(attr, &len);
+ }
+ return ret;
+}
+
+/**
+ * mnl_attr_parse_nested - parse attributes inside a nest
+ * @nested: pointer to netlink attribute that contains a nest
+ * @offset: offset to start parsing from
+ * @cb: callback function
+ * @data: pointer to data passed to the callback function
*
- * This function returns an array of pointers to the attributes that has been
- * found in a netlink payload. This function return 0 on sucess, and >0 to
- * indicate the number of bytes the remaining bytes.
+ * This function propagates the return value of the callback that can be
+ * MNL_CB_ERROR, MNL_CB_OK or MNL_CB_STOP.
*/
int mnl_attr_parse_nested(const struct nlattr *nested,
- struct nlattr *tb[], int max)
+ mnl_attr_cb_t cb, void *data)
{
+ int ret = MNL_CB_OK;
struct nlattr *attr = mnl_attr_get_data(nested);
int len = mnl_attr_get_payload_len(nested);
- memset(tb, 0, sizeof(struct nlattr *) * (max + 1));
-
while (mnl_attr_ok(attr, len)) {
- if (mnl_attr_get_type(attr) <= max)
- tb[mnl_attr_get_type(attr)] = attr;
+ if (cb && (ret = cb(attr, data)) <= MNL_CB_STOP)
+ return ret;
attr = mnl_attr_next(attr, &len);
}
- return len;
+ return ret;
}
/**