/* SPDX-License-Identifier: LGPL-2.1-only */ /* * lib/genl/genl.c Generic Netlink * * This library 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 version 2.1 * of the License. * * Copyright (c) 2003-2012 Thomas Graf */ /** * @defgroup genl Generic Netlink Library (libnl-genl) * * @{ */ #include #include #include #include /** * @name Generic Netlink Socket * @{ */ /** * Connect a Generic Netlink socket * @arg sk Unconnected Netlink socket * * This function expects a struct nl_socket object previously allocated via * nl_socket_alloc(). It calls nl_connect() to create the local socket file * descriptor and binds the socket to the \c NETLINK_GENERIC Netlink protocol. * * Using this function is equivalent to: * @code * nl_connect(sk, NETLINK_GENERIC); * @endcode * * @see nl_connect() * * @return 0 on success or a negative error code. */ int genl_connect(struct nl_sock *sk) { return nl_connect(sk, NETLINK_GENERIC); } /** @} */ /** * @name Sending Data * @{ */ /** * Send a Generic Netlink message consisting only of a header * @arg sk Generic Netlink socket * @arg family Numeric family identifier * @arg cmd Numeric command identifier * @arg version Interface version * @arg flags Additional Netlink message flags (optional) * * This function is a shortcut for sending a Generic Netlink message without * any message payload. The message will only consist of the Netlink and * Generic Netlink headers. The header is constructed based on the specified * parameters and passed on to nl_send_simple() to send it on the specified * socket. * * @par Example: * @code * #include * #include * * err = genl_send_simple(sk, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, CTRL_VERSION, * NLM_F_DUMP); * @endcode * * @see nl_send_simple() * * @return 0 on success or a negative error code. Due to a bug, this function * returns the number of bytes sent. Treat any non-negative number as success. */ int genl_send_simple(struct nl_sock *sk, int family, int cmd, int version, int flags) { struct genlmsghdr hdr = { .cmd = cmd, .version = version, }; return nl_send_simple(sk, family, flags, &hdr, sizeof(hdr)); } /** @} */ /** * @name Message Parsing * @{ */ /** * Validate Generic Netlink message headers * @arg nlh Pointer to Netlink message header * @arg hdrlen Length of user header * * Verifies the integrity of the Netlink and Generic Netlink headers by * enforcing the following requirements: * - Valid Netlink message header (nlmsg_valid_hdr()) * - Presence of a complete Generic Netlink header * - At least \c hdrlen bytes of payload included after the generic * netlink header. * * @return A positive integer (true) if the headers are valid or * 0 (false) if not. */ int genlmsg_valid_hdr(struct nlmsghdr *nlh, int hdrlen) { struct genlmsghdr *ghdr; if (!nlmsg_valid_hdr(nlh, GENL_HDRLEN)) return 0; ghdr = nlmsg_data(nlh); if (genlmsg_len(ghdr) < NLMSG_ALIGN(hdrlen)) return 0; return 1; } /** * Validate Generic Netlink message including attributes * @arg nlh Pointer to Netlink message header * @arg hdrlen Length of user header * @arg maxtype Maximum attribtue id expected * @arg policy Attribute validation policy * * Verifies the validity of the Netlink and Generic Netlink headers using * genlmsg_valid_hdr() and calls nla_validate() on the message payload to * verify the integrity of eventual attributes. * * @note You may call genlmsg_parse() directly to perform validation and * parsing in a single step. * * @see genlmsg_valid_hdr() * @see nla_validate() * @see genlmsg_parse() * * @return 0 on success or a negative error code. */ int genlmsg_validate(struct nlmsghdr *nlh, int hdrlen, int maxtype, const struct nla_policy *policy) { struct genlmsghdr *ghdr; if (!genlmsg_valid_hdr(nlh, hdrlen)) return -NLE_MSG_TOOSHORT; ghdr = nlmsg_data(nlh); return nla_validate(genlmsg_attrdata(ghdr, hdrlen), genlmsg_attrlen(ghdr, hdrlen), maxtype, policy); } /** * Parse Generic Netlink message including attributes * @arg nlh Pointer to Netlink message header * @arg hdrlen Length of user header * @arg tb Array to store parsed attributes * @arg maxtype Maximum attribute id expected * @arg policy Attribute validation policy * * Verifies the validity of the Netlink and Generic Netlink headers using * genlmsg_valid_hdr() and calls nla_parse() on the message payload to * parse eventual attributes. * * @par Example: * @code * struct nlattr *attrs[MY_TYPE_MAX+1]; * * if ((err = genlmsg_parse(nlmsg_hdr(msg), sizeof(struct my_hdr), attrs, * MY_TYPE_MAX, attr_policy)) < 0) * // ERROR * @endcode * * @see genlmsg_valid_hdr() * @see genlmsg_validate() * @see nla_parse() * * @return 0 on success or a negative error code. */ int genlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], int maxtype, const struct nla_policy *policy) { struct genlmsghdr *ghdr; if (!genlmsg_valid_hdr(nlh, hdrlen)) return -NLE_MSG_TOOSHORT; ghdr = nlmsg_data(nlh); return nla_parse(tb, maxtype, genlmsg_attrdata(ghdr, hdrlen), genlmsg_attrlen(ghdr, hdrlen), policy); } /** * Return pointer to Generic Netlink header * @arg nlh Netlink message header * * @return Pointer to Generic Netlink message header */ struct genlmsghdr *genlmsg_hdr(struct nlmsghdr *nlh) { return nlmsg_data(nlh); } /** * Return length of message payload including user header * @arg gnlh Generic Netlink message header * * @see genlmsg_data() * * @return Length of user payload including an eventual user header in * number of bytes. */ int genlmsg_len(const struct genlmsghdr *gnlh) { const struct nlmsghdr *nlh; nlh = (const struct nlmsghdr *)((const unsigned char *) gnlh - NLMSG_HDRLEN); return (nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN); } /** * Return pointer to user header * @arg gnlh Generic Netlink message header * * Calculates the pointer to the user header based on the pointer to * the Generic Netlink message header. * * @return Pointer to the user header */ void *genlmsg_user_hdr(const struct genlmsghdr *gnlh) { return genlmsg_data(gnlh); } /** * Return pointer to user data * @arg gnlh Generic netlink message header * @arg hdrlen Length of user header * * Calculates the pointer to the user data based on the pointer to * the Generic Netlink message header. * * @see genlmsg_user_datalen() * * @return Pointer to the user data */ void *genlmsg_user_data(const struct genlmsghdr *gnlh, const int hdrlen) { return (char *) genlmsg_user_hdr(gnlh) + NLMSG_ALIGN(hdrlen); } /** * Return length of user data * @arg gnlh Generic Netlink message header * @arg hdrlen Length of user header * * @see genlmsg_user_data() * * @return Length of user data in bytes */ int genlmsg_user_datalen(const struct genlmsghdr *gnlh, const int hdrlen) { return genlmsg_len(gnlh) - NLMSG_ALIGN(hdrlen); } /** * Return pointer to message attributes * @arg gnlh Generic Netlink message header * @arg hdrlen Length of user header * * @see genlmsg_attrlen() * * @return Pointer to the start of the message's attributes section. */ struct nlattr *genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen) { return genlmsg_user_data(gnlh, hdrlen); } /** * Return length of message attributes * @arg gnlh Generic Netlink message header * @arg hdrlen Length of user header * * @see genlmsg_attrdata() * * @return Length of the message section containing attributes in number * of bytes. */ int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen) { return genlmsg_len(gnlh) - NLMSG_ALIGN(hdrlen); } /** @} */ /** * @name Message Construction * @{ */ /** * Add Generic Netlink headers to Netlink message * @arg msg Netlink message object * @arg port Netlink port or NL_AUTO_PORT * @arg seq Sequence number of message or NL_AUTO_SEQ * @arg family Numeric family identifier * @arg hdrlen Length of user header * @arg flags Additional Netlink message flags (optional) * @arg cmd Numeric command identifier * @arg version Interface version * * Calls nlmsg_put() on the specified message object to reserve space for * the Netlink header, the Generic Netlink header, and a user header of * specified length. Fills out the header fields with the specified * parameters. * * @par Example: * @code * struct nl_msg *msg; * struct my_hdr *user_hdr; * * if (!(msg = nlmsg_alloc())) * // ERROR * * user_hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, family_id, * sizeof(struct my_hdr), 0, MY_CMD_FOO, 0); * if (!user_hdr) * // ERROR * @endcode * * @see nlmsg_put() * * Returns Pointer to user header or NULL if an error occurred. */ void *genlmsg_put(struct nl_msg *msg, uint32_t port, uint32_t seq, int family, int hdrlen, int flags, uint8_t cmd, uint8_t version) { struct nlmsghdr *nlh; struct genlmsghdr hdr = { .cmd = cmd, .version = version, }; nlh = nlmsg_put(msg, port, seq, family, GENL_HDRLEN + hdrlen, flags); if (nlh == NULL) return NULL; memcpy(nlmsg_data(nlh), &hdr, sizeof(hdr)); NL_DBG(2, "msg %p: Added generic netlink header cmd=%d version=%d\n", msg, cmd, version); return (char *) nlmsg_data(nlh) + GENL_HDRLEN; } /** @} */ /** * @name Deprecated * @{ */ /** * Return pointer to message payload * @arg gnlh Generic Netlink message header * * @deprecated This function has been deprecated due to inability to specify * the length of the user header. Use genlmsg_user_hdr() * respectively genlmsg_user_data(). * * @return Pointer to payload section */ void *genlmsg_data(const struct genlmsghdr *gnlh) { return ((unsigned char *) gnlh + GENL_HDRLEN); } /** @} */ /** @} */