summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2010-04-03 08:29:16 +0200
committerPablo Neira Ayuso <pablo@netfilter.org>2010-04-03 13:26:44 +0200
commit8ce5d4ca70884654988eb86734cb3022e0b71995 (patch)
treea42c6a5b87d5b6c2ff01446f127824e355de3ee8
parentba57ffc48d3a97421c8358947bc8cf9f2e7ff7c6 (diff)
add validation infrastructure and rework attribute parsing
This patch includes the new validation infrastructure which is decoupled from the attribute parsing. It is composed of: - mnl_attr_type_invalid: that allows to check if the attribute type is valid (ie. the type is not higher than WXYZ_MAX). - mnl_attr_validate: that allows to validate that there's enough room for the attribute data. The patch includes the rework of the attribute parsers. Now, you don't have to use an array of pointer to store the result of the parsing, you can use whatever data structure instead. The prototype as it follows: 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) There are three versions of rtnl-link-dump.c that show how attribute parsing can be done now. Probably that many examples are not good idea, I may remove some of them from the tree in the future. This patch also merges mnl_attr_parse_at_offset into mnl_attr_parse. This patch modifies MNL_ALIGN so that we can use it in static arrays (the use of mnl_align() is not allowed in compilation time to initialize an array field). I have added the mnl_attr_for_each() macro and I have changed mnl_attr_for_each_nested() to declare the length variable internally. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-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;
}
/**