/* * (C) 2008-2012 by Pablo Neira Ayuso * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. */ #include /* for INT_MAX */ #include #include #include #include "internal.h" /** * \defgroup attr Netlink attribute helpers * * Netlink Type-Length-Value (TLV) attribute: * \verbatim |<-- 2 bytes -->|<-- 2 bytes -->|<-- variable -->| ------------------------------------------------- | length | type | value | ------------------------------------------------- |<--------- header ------------>|<-- payload --->| \endverbatim * The payload of the Netlink message contains sequences of attributes that are * expressed in TLV format. * * @{ */ /** * mnl_attr_get_type - get type of netlink attribute * \param attr pointer to netlink attribute * * This function returns the attribute type. */ EXPORT_SYMBOL(mnl_attr_get_type); uint16_t mnl_attr_get_type(const struct nlattr *attr) { return attr->nla_type & NLA_TYPE_MASK; } /** * mnl_attr_get_len - get length of netlink attribute * \param attr pointer to netlink attribute * * This function returns the attribute length that is the attribute header * plus the attribute payload. */ EXPORT_SYMBOL(mnl_attr_get_len); uint16_t mnl_attr_get_len(const struct nlattr *attr) { return attr->nla_len; } /** * mnl_attr_get_payload_len - get the attribute payload-value length * \param attr pointer to netlink attribute * * This function returns the attribute payload-value length. */ EXPORT_SYMBOL(mnl_attr_get_payload_len); uint16_t mnl_attr_get_payload_len(const struct nlattr *attr) { return attr->nla_len - MNL_ATTR_HDRLEN; } /** * mnl_attr_get_payload - get pointer to the attribute payload * \param attr pointer to netlink attribute * * This function return a pointer to the attribute payload. */ EXPORT_SYMBOL(mnl_attr_get_payload); void *mnl_attr_get_payload(const struct nlattr *attr) { return (void *)attr + MNL_ATTR_HDRLEN; } /** * mnl_attr_ok - check if there is room for an attribute in a buffer * \param attr attribute that we want to check if there is room for * \param len remaining bytes in a buffer that contains the attribute * * This function is used to check that a buffer, which is supposed to contain * an attribute, has enough room for the attribute that it stores, i.e. this * function can be used to verify that an attribute is neither malformed nor * truncated. * * This function does not set errno in case of error since it is intended * for iterations. Thus, it returns true on success and false on error. * * The len parameter may be negative in the case of malformed messages during * attribute iteration, that is why we use a signed integer. */ EXPORT_SYMBOL(mnl_attr_ok); bool mnl_attr_ok(const struct nlattr *attr, int len) { return len >= (int)sizeof(struct nlattr) && attr->nla_len >= sizeof(struct nlattr) && (int)attr->nla_len <= len; } /** * mnl_attr_next - get the next attribute in the payload of a netlink message * \param attr pointer to the current attribute * * This function returns a pointer to the next attribute after the one passed * as parameter. You have to use mnl_attr_ok() to ensure that the next * attribute is valid. */ EXPORT_SYMBOL(mnl_attr_next); struct nlattr *mnl_attr_next(const struct nlattr *attr) { return (struct nlattr *)((void *)attr + MNL_ALIGN(attr->nla_len)); } /** * mnl_attr_type_valid - check if the attribute type is valid * \param attr pointer to attribute to be checked * \param max maximum attribute type * * 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. On success, this function returns 1. * * Strict attribute checking in user-space is not a good idea since you may * run an old application with a newer kernel that supports new attributes. * This leads to backward compatibility breakages in user-space. Better check * if you support an attribute, if not, skip it. */ EXPORT_SYMBOL(mnl_attr_type_valid); int mnl_attr_type_valid(const struct nlattr *attr, uint16_t max) { if (mnl_attr_get_type(attr) > max) { errno = EOPNOTSUPP; return -1; } return 1; } static int __mnl_attr_validate(const struct nlattr *attr, enum mnl_attr_data_type type, size_t exp_len) { uint16_t attr_len = mnl_attr_get_payload_len(attr); const char *attr_data = mnl_attr_get_payload(attr); if (attr_len < exp_len) { errno = ERANGE; return -1; } 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; } static const 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), [MNL_TYPE_MSECS] = sizeof(uint64_t), }; /** * mnl_attr_validate - validate netlink attribute (simplified version) * \param attr pointer to netlink attribute that we want to validate * \param type data type (see enum mnl_attr_data_type) * * The validation is based on the data type. Specifically, it checks that * integers (u8, u16, u32 and u64) have enough room for them. This function * returns -1 in case of error, and errno is explicitly set. */ EXPORT_SYMBOL(mnl_attr_validate); int mnl_attr_validate(const struct nlattr *attr, enum mnl_attr_data_type type) { int exp_len; if (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) * \param attr pointer to netlink attribute that we want to validate * \param type attribute type (see enum mnl_attr_data_type) * \param exp_len expected attribute data size * * 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. */ EXPORT_SYMBOL(mnl_attr_validate2); int mnl_attr_validate2(const struct nlattr *attr, enum mnl_attr_data_type type, size_t exp_len) { if (type >= MNL_TYPE_MAX) { errno = EINVAL; return -1; } return __mnl_attr_validate(attr, type, exp_len); } /** * mnl_attr_parse - parse attributes * \param nlh pointer to netlink message * \param offset offset to start parsing from (if payload is after any header) * \param cb callback function that is called for each attribute * \param data pointer to data that is passed to the callback function * * This function allows to iterate over the sequence of attributes that compose * the Netlink message. You can then put the attribute in an array as it * usually happens at this stage or you can use any other data structure (such * as lists or trees). * * This function propagates the return value of the callback, which can be * MNL_CB_ERROR, MNL_CB_OK or MNL_CB_STOP. */ EXPORT_SYMBOL(mnl_attr_parse); int mnl_attr_parse(const struct nlmsghdr *nlh, unsigned int offset, mnl_attr_cb_t cb, void *data) { int ret = MNL_CB_OK; const struct nlattr *attr; mnl_attr_for_each(attr, nlh, offset) if ((ret = cb(attr, data)) <= MNL_CB_STOP) return ret; return ret; } /** * mnl_attr_parse_nested - parse attributes inside a nest * \param nested pointer to netlink attribute that contains a nest * \param cb callback function that is called for each attribute in the nest * \param data pointer to data passed to the callback function * * This function allows to iterate over the sequence of attributes that compose * the Netlink message. You can then put the attribute in an array as it * usually happens at this stage or you can use any other data structure (such * as lists or trees). * * This function propagates the return value of the callback, which can be * MNL_CB_ERROR, MNL_CB_OK or MNL_CB_STOP. */ EXPORT_SYMBOL(mnl_attr_parse_nested); int mnl_attr_parse_nested(const struct nlattr *nested, mnl_attr_cb_t cb, void *data) { int ret = MNL_CB_OK; const struct nlattr *attr; mnl_attr_for_each_nested(attr, nested) if ((ret = cb(attr, data)) <= MNL_CB_STOP) return ret; return ret; } /** * mnl_attr_parse_payload - parse attributes in payload of Netlink message * \param payload pointer to payload of the Netlink message * \param payload_len payload length that contains the attributes * \param cb callback function that is called for each attribute * \param data pointer to data that is passed to the callback function * * This function takes a pointer to the area that contains the attributes, * commonly known as the payload of the Netlink message. Thus, you have to * pass a pointer to the Netlink message payload, instead of the entire * message. * * This function allows you to iterate over the sequence of attributes that are * located at some payload offset. You can then put the attributes in one array * as usual, or you can use any other data structure (such as lists or trees). * * This function propagates the return value of the callback, which can be * MNL_CB_ERROR, MNL_CB_OK or MNL_CB_STOP. */ EXPORT_SYMBOL(mnl_attr_parse_payload); int mnl_attr_parse_payload(const void *payload, size_t payload_len, mnl_attr_cb_t cb, void *data) { int ret = MNL_CB_OK; const struct nlattr *attr; mnl_attr_for_each_payload(payload, payload_len) if ((ret = cb(attr, data)) <= MNL_CB_STOP) return ret; return ret; } /** * mnl_attr_get_u8 - returns 8-bit unsigned integer attribute payload * \param attr pointer to netlink attribute * * This function returns the 8-bit value of the attribute payload. */ EXPORT_SYMBOL(mnl_attr_get_u8); uint8_t mnl_attr_get_u8(const struct nlattr *attr) { return *((uint8_t *)mnl_attr_get_payload(attr)); } /** * mnl_attr_get_u16 - returns 16-bit unsigned integer attribute payload * \param attr pointer to netlink attribute * * This function returns the 16-bit value of the attribute payload. */ EXPORT_SYMBOL(mnl_attr_get_u16); uint16_t mnl_attr_get_u16(const struct nlattr *attr) { return *((uint16_t *)mnl_attr_get_payload(attr)); } /** * mnl_attr_get_u32 - returns 32-bit unsigned integer attribute payload * \param attr pointer to netlink attribute * * This function returns the 32-bit value of the attribute payload. */ EXPORT_SYMBOL(mnl_attr_get_u32); uint32_t mnl_attr_get_u32(const struct nlattr *attr) { return *((uint32_t *)mnl_attr_get_payload(attr)); } /** * mnl_attr_get_u64 - returns 64-bit unsigned integer attribute. * \param attr pointer to netlink attribute * * This function returns the 64-bit value of the attribute payload. This * function is align-safe, since accessing 64-bit Netlink attributes is a * common source of alignment issues. */ EXPORT_SYMBOL(mnl_attr_get_u64); uint64_t mnl_attr_get_u64(const struct nlattr *attr) { uint64_t tmp; memcpy(&tmp, mnl_attr_get_payload(attr), sizeof(tmp)); return tmp; } /** * mnl_attr_get_str - returns pointer to string attribute. * \param attr pointer to netlink attribute * * This function returns the payload of string attribute value. */ EXPORT_SYMBOL(mnl_attr_get_str); const char *mnl_attr_get_str(const struct nlattr *attr) { return mnl_attr_get_payload(attr); } /** * mnl_attr_put - add an attribute to netlink message * \param nlh pointer to the netlink message * \param type netlink attribute type that you want to add * \param len netlink attribute payload length * \param data pointer to the data that will be stored by the new attribute * * This function updates the length field of the Netlink message (nlmsg_len) * by adding the size (header + payload) of the new attribute. */ EXPORT_SYMBOL(mnl_attr_put); void mnl_attr_put(struct nlmsghdr *nlh, uint16_t type, size_t len, const void *data) { struct nlattr *attr = mnl_nlmsg_get_payload_tail(nlh); uint16_t payload_len = MNL_ALIGN(sizeof(struct nlattr)) + len; int pad; attr->nla_type = type; attr->nla_len = payload_len; memcpy(mnl_attr_get_payload(attr), data, len); pad = MNL_ALIGN(len) - len; if (pad > 0) memset(mnl_attr_get_payload(attr) + len, 0, pad); nlh->nlmsg_len += MNL_ALIGN(payload_len); } /** * mnl_attr_put_u8 - add 8-bit unsigned integer attribute to netlink message * \param nlh pointer to the netlink message * \param type netlink attribute type * \param data 8-bit unsigned integer data that is stored by the new attribute * * This function updates the length field of the Netlink message (nlmsg_len) * by adding the size (header + payload) of the new attribute. */ EXPORT_SYMBOL(mnl_attr_put_u8); void mnl_attr_put_u8(struct nlmsghdr *nlh, uint16_t type, uint8_t data) { mnl_attr_put(nlh, type, sizeof(uint8_t), &data); } /** * mnl_attr_put_u16 - add 16-bit unsigned integer attribute to netlink message * \param nlh pointer to the netlink message * \param type netlink attribute type * \param data 16-bit unsigned integer data that is stored by the new attribute * * This function updates the length field of the Netlink message (nlmsg_len) * by adding the size (header + payload) of the new attribute. */ EXPORT_SYMBOL(mnl_attr_put_u16); void mnl_attr_put_u16(struct nlmsghdr *nlh, uint16_t type, uint16_t data) { mnl_attr_put(nlh, type, sizeof(uint16_t), &data); } /** * mnl_attr_put_u32 - add 32-bit unsigned integer attribute to netlink message * \param nlh pointer to the netlink message * \param type netlink attribute type * \param data 32-bit unsigned integer data that is stored by the new attribute * * This function updates the length field of the Netlink message (nlmsg_len) * by adding the size (header + payload) of the new attribute. */ EXPORT_SYMBOL(mnl_attr_put_u32); void mnl_attr_put_u32(struct nlmsghdr *nlh, uint16_t type, uint32_t data) { mnl_attr_put(nlh, type, sizeof(uint32_t), &data); } /** * mnl_attr_put_u64 - add 64-bit unsigned integer attribute to netlink message * \param nlh pointer to the netlink message * \param type netlink attribute type * \param data 64-bit unsigned integer data that is stored by the new attribute * * This function updates the length field of the Netlink message (nlmsg_len) * by adding the size (header + payload) of the new attribute. */ EXPORT_SYMBOL(mnl_attr_put_u64); void mnl_attr_put_u64(struct nlmsghdr *nlh, uint16_t type, uint64_t data) { mnl_attr_put(nlh, type, sizeof(uint64_t), &data); } /** * mnl_attr_put_str - add string attribute to netlink message * \param nlh pointer to the netlink message * \param type netlink attribute type * \param data pointer to string data that is stored by the new attribute * * This function updates the length field of the Netlink message (nlmsg_len) * by adding the size (header + payload) of the new attribute. */ EXPORT_SYMBOL(mnl_attr_put_str); void mnl_attr_put_str(struct nlmsghdr *nlh, uint16_t type, const char *data) { mnl_attr_put(nlh, type, strlen(data), data); } /** * mnl_attr_put_strz - add string attribute to netlink message * \param nlh pointer to the netlink message * \param type netlink attribute type * \param data pointer to string data that is stored by the new attribute * * This function is similar to mnl_attr_put_str, but it includes the * NUL/zero ('\0') terminator at the end of the string. * * This function updates the length field of the Netlink message (nlmsg_len) * by adding the size (header + payload) of the new attribute. */ EXPORT_SYMBOL(mnl_attr_put_strz); void mnl_attr_put_strz(struct nlmsghdr *nlh, uint16_t type, const char *data) { mnl_attr_put(nlh, type, strlen(data)+1, data); } /** * mnl_attr_nest_start - start an attribute nest * \param nlh pointer to the netlink message * \param type netlink attribute type * * This function adds the attribute header that identifies the beginning of * an attribute nest. This function always returns a valid pointer to the * beginning of the nest. */ EXPORT_SYMBOL(mnl_attr_nest_start); struct nlattr *mnl_attr_nest_start(struct nlmsghdr *nlh, uint16_t type) { struct nlattr *start = mnl_nlmsg_get_payload_tail(nlh); /* set start->nla_len in mnl_attr_nest_end() */ start->nla_type = NLA_F_NESTED | type; nlh->nlmsg_len += MNL_ALIGN(sizeof(struct nlattr)); return start; } /** * mnl_attr_put_check - add an attribute to netlink message * \param nlh pointer to the netlink message * \param buflen size of buffer which stores the message * \param type netlink attribute type that you want to add * \param len netlink attribute payload length * \param data pointer to the data that will be stored by the new attribute * * This function first checks that the data can be added to the message * (fits into the buffer) and then updates the length field of the Netlink * message (nlmsg_len) by adding the size (header + payload) of the new * attribute. The function returns true if the attribute could be added * to the message, otherwise false is returned. */ EXPORT_SYMBOL(mnl_attr_put_check); bool mnl_attr_put_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, size_t len, const void *data) { if (nlh->nlmsg_len + MNL_ATTR_HDRLEN + MNL_ALIGN(len) > buflen) return false; mnl_attr_put(nlh, type, len, data); return true; } /** * mnl_attr_put_u8_check - add 8-bit unsigned int attribute to netlink message * \param nlh pointer to the netlink message * \param buflen size of buffer which stores the message * \param type netlink attribute type * \param data 8-bit unsigned integer data that is stored by the new attribute * * This function first checks that the data can be added to the message * (fits into the buffer) and then updates the length field of the Netlink * message (nlmsg_len) by adding the size (header + payload) of the new * attribute. The function returns true if the attribute could be added * to the message, otherwise false is returned. */ EXPORT_SYMBOL(mnl_attr_put_u8_check); bool mnl_attr_put_u8_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, uint8_t data) { return mnl_attr_put_check(nlh, buflen, type, sizeof(uint8_t), &data); } /** * mnl_attr_put_u16_check - add 16-bit unsigned int attribute to netlink message * \param nlh pointer to the netlink message * \param buflen size of buffer which stores the message * \param type netlink attribute type * \param data 16-bit unsigned integer data that is stored by the new attribute * * This function first checks that the data can be added to the message * (fits into the buffer) and then updates the length field of the Netlink * message (nlmsg_len) by adding the size (header + payload) of the new * attribute. The function returns true if the attribute could be added * to the message, otherwise false is returned. * This function updates the length field of the Netlink message (nlmsg_len) * by adding the size (header + payload) of the new attribute. */ EXPORT_SYMBOL(mnl_attr_put_u16_check); bool mnl_attr_put_u16_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, uint16_t data) { return mnl_attr_put_check(nlh, buflen, type, sizeof(uint16_t), &data); } /** * mnl_attr_put_u32_check - add 32-bit unsigned int attribute to netlink message * \param nlh pointer to the netlink message * \param buflen size of buffer which stores the message * \param type netlink attribute type * \param data 32-bit unsigned integer data that is stored by the new attribute * * This function first checks that the data can be added to the message * (fits into the buffer) and then updates the length field of the Netlink * message (nlmsg_len) by adding the size (header + payload) of the new * attribute. The function returns true if the attribute could be added * to the message, otherwise false is returned. * This function updates the length field of the Netlink message (nlmsg_len) * by adding the size (header + payload) of the new attribute. */ EXPORT_SYMBOL(mnl_attr_put_u32_check); bool mnl_attr_put_u32_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, uint32_t data) { return mnl_attr_put_check(nlh, buflen, type, sizeof(uint32_t), &data); } /** * mnl_attr_put_u64_check - add 64-bit unsigned int attribute to netlink message * \param nlh pointer to the netlink message * \param buflen size of buffer which stores the message * \param type netlink attribute type * \param data 64-bit unsigned integer data that is stored by the new attribute * * This function first checks that the data can be added to the message * (fits into the buffer) and then updates the length field of the Netlink * message (nlmsg_len) by adding the size (header + payload) of the new * attribute. The function returns true if the attribute could be added * to the message, otherwise false is returned. * This function updates the length field of the Netlink message (nlmsg_len) * by adding the size (header + payload) of the new attribute. */ EXPORT_SYMBOL(mnl_attr_put_u64_check); bool mnl_attr_put_u64_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, uint64_t data) { return mnl_attr_put_check(nlh, buflen, type, sizeof(uint64_t), &data); } /** * mnl_attr_put_str_check - add string attribute to netlink message * \param nlh pointer to the netlink message * \param buflen size of buffer which stores the message * \param type netlink attribute type * \param data pointer to string data that is stored by the new attribute * * This function first checks that the data can be added to the message * (fits into the buffer) and then updates the length field of the Netlink * message (nlmsg_len) by adding the size (header + payload) of the new * attribute. The function returns true if the attribute could be added * to the message, otherwise false is returned. * This function updates the length field of the Netlink message (nlmsg_len) * by adding the size (header + payload) of the new attribute. */ EXPORT_SYMBOL(mnl_attr_put_str_check); bool mnl_attr_put_str_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, const char *data) { return mnl_attr_put_check(nlh, buflen, type, strlen(data), data); } /** * mnl_attr_put_strz_check - add string attribute to netlink message * \param nlh pointer to the netlink message * \param buflen size of buffer which stores the message * \param type netlink attribute type * \param data pointer to string data that is stored by the new attribute * * This function is similar to mnl_attr_put_str, but it includes the * NUL/zero ('\0') terminator at the end of the string. * * This function first checks that the data can be added to the message * (fits into the buffer) and then updates the length field of the Netlink * message (nlmsg_len) by adding the size (header + payload) of the new * attribute. The function returns true if the attribute could be added * to the message, otherwise false is returned. */ EXPORT_SYMBOL(mnl_attr_put_strz_check); bool mnl_attr_put_strz_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, const char *data) { return mnl_attr_put_check(nlh, buflen, type, strlen(data)+1, data); } /** * mnl_attr_nest_start_check - start an attribute nest * \param buflen size of buffer which stores the message * \param nlh pointer to the netlink message * \param type netlink attribute type * * This function adds the attribute header that identifies the beginning of * an attribute nest. If the nested attribute cannot be added then NULL, * otherwise valid pointer to the beginning of the nest is returned. */ EXPORT_SYMBOL(mnl_attr_nest_start_check); struct nlattr *mnl_attr_nest_start_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type) { if (nlh->nlmsg_len + MNL_ATTR_HDRLEN > buflen) return NULL; return mnl_attr_nest_start(nlh, type); } /** * mnl_attr_nest_end - end an attribute nest * \param nlh pointer to the netlink message * \param start pointer to the attribute nest returned by mnl_attr_nest_start() * * This function updates the attribute header that identifies the nest. */ EXPORT_SYMBOL(mnl_attr_nest_end); void mnl_attr_nest_end(struct nlmsghdr *nlh, struct nlattr *start) { start->nla_len = mnl_nlmsg_get_payload_tail(nlh) - (void *)start; } /** * mnl_attr_nest_cancel - cancel an attribute nest * \param nlh pointer to the netlink message * \param start pointer to the attribute nest returned by mnl_attr_nest_start() * * This function updates the attribute header that identifies the nest. */ EXPORT_SYMBOL(mnl_attr_nest_cancel); void mnl_attr_nest_cancel(struct nlmsghdr *nlh, struct nlattr *start) { nlh->nlmsg_len -= mnl_nlmsg_get_payload_tail(nlh) - (void *)start; } /** * @} */