summaryrefslogtreecommitdiffstats
path: root/src/attr.c
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 /src/attr.c
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>
Diffstat (limited to 'src/attr.c')
-rw-r--r--src/attr.c188
1 files changed, 141 insertions, 47 deletions
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;
}
/**