diff options
-rw-r--r-- | examples/Makefile.am | 15 | ||||
-rw-r--r-- | examples/genl-family-get.c | 113 | ||||
-rw-r--r-- | examples/rtnl-link-dump.c | 32 | ||||
-rw-r--r-- | examples/rtnl-link-dump2.c | 103 | ||||
-rw-r--r-- | examples/rtnl-link-dump3.c | 101 | ||||
-rw-r--r-- | examples/rtnl-link-event.c | 32 | ||||
-rw-r--r-- | examples/rtnl-route-dump.c | 55 | ||||
-rw-r--r-- | include/libmnl/libmnl.h | 53 | ||||
-rw-r--r-- | src/attr.c | 188 |
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 @@ -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; } /** |