| /* |
| * lib/msg.c Netlink Messages Interface |
| * |
| * 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-2008 Thomas Graf <tgraf@suug.ch> |
| */ |
| |
| /** |
| * @ingroup nl |
| * @defgroup msg Messages |
| * Netlink Message Construction/Parsing Interface |
| * |
| * The following information is partly extracted from RFC3549 |
| * (ftp://ftp.rfc-editor.org/in-notes/rfc3549.txt) |
| * |
| * @par Message Format |
| * Netlink messages consist of a byte stream with one or multiple |
| * Netlink headers and an associated payload. If the payload is too big |
| * to fit into a single message it, can be split over multiple Netlink |
| * messages, collectively called a multipart message. For multipart |
| * messages, the first and all following headers have the \c NLM_F_MULTI |
| * Netlink header flag set, except for the last header which has the |
| * Netlink header type \c NLMSG_DONE. |
| * |
| * @par |
| * The Netlink message header (\link nlmsghdr struct nlmsghdr\endlink) is shown below. |
| * @code |
| * 0 1 2 3 |
| * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
| * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| * | Length | |
| * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| * | Type | Flags | |
| * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| * | Sequence Number | |
| * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| * | Process ID (PID) | |
| * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| * @endcode |
| * |
| * @par |
| * The netlink message header and payload must be aligned properly: |
| * @code |
| * <------- NLMSG_ALIGN(hlen) ------> <---- NLMSG_ALIGN(len) ---> |
| * +----------------------------+- - -+- - - - - - - - - - -+- - -+ |
| * | Header | Pad | Payload | Pad | |
| * | struct nlmsghdr | | | | |
| * +----------------------------+- - -+- - - - - - - - - - -+- - -+ |
| * @endcode |
| * @par |
| * Message Format: |
| * @code |
| * <--- nlmsg_total_size(payload) ---> |
| * <-- nlmsg_msg_size(payload) -> |
| * +----------+- - -+-------------+- - -+-------- - - |
| * | nlmsghdr | Pad | Payload | Pad | nlmsghdr |
| * +----------+- - -+-------------+- - -+-------- - - |
| * nlmsg_data(nlh)---^ ^ |
| * nlmsg_next(nlh)-----------------------+ |
| * @endcode |
| * @par |
| * The payload may consist of arbitary data but may have strict |
| * alignment and formatting rules depening on the specific netlink |
| * families. |
| * @par |
| * @code |
| * <---------------------- nlmsg_len(nlh) ---------------------> |
| * <------ hdrlen ------> <- nlmsg_attrlen(nlh, hdrlen) -> |
| * +----------------------+- - -+--------------------------------+ |
| * | Family Header | Pad | Attributes | |
| * +----------------------+- - -+--------------------------------+ |
| * nlmsg_attrdata(nlh, hdrlen)---^ |
| * @endcode |
| * @par The ACK Netlink Message |
| * This message is actually used to denote both an ACK and a NACK. |
| * Typically, the direction is from FEC to CPC (in response to an ACK |
| * request message). However, the CPC should be able to send ACKs back |
| * to FEC when requested. |
| * @code |
| * 0 1 2 3 |
| * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
| * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| * | Netlink message header | |
| * | type = NLMSG_ERROR | |
| * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| * | Error code | |
| * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| * | OLD Netlink message header | |
| * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| * @endcode |
| * |
| * @par Example |
| * @code |
| * // Various methods exist to create/allocate a new netlink |
| * // message. |
| * // |
| * // nlmsg_alloc() will allocate an empty netlink message with |
| * // a maximum payload size which defaults to the page size of |
| * // the system. This default size can be modified using the |
| * // function nlmsg_set_default_size(). |
| * struct nl_msg *msg = nlmsg_alloc(); |
| * |
| * // Very often, the message type and message flags are known |
| * // at allocation time while the other fields are auto generated: |
| * struct nl_msg *msg = nlmsg_alloc_simple(MY_TYPE, MY_FLAGS); |
| * |
| * // Alternatively an existing netlink message header can be used |
| * // to inherit the header values: |
| * struct nlmsghdr hdr = { |
| * .nlmsg_type = MY_TYPE, |
| * .nlmsg_flags = MY_FLAGS, |
| * }; |
| * struct nl_msg *msg = nlmsg_inherit(&hdr); |
| * |
| * // Last but not least, netlink messages received from netlink sockets |
| * // can be converted into nl_msg objects using nlmsg_convert(). This |
| * // will create a message with a maximum payload size which equals the |
| * // length of the existing netlink message, therefore no more data can |
| * // be appened without calling nlmsg_expand() first. |
| * struct nl_msg *msg = nlmsg_convert(nlh_from_nl_sock); |
| * |
| * // Payload may be added to the message via nlmsg_append(). The fourth |
| * // parameter specifies the number of alignment bytes the data should |
| * // be padding with at the end. Common values are 0 to disable it or |
| * // NLMSG_ALIGNTO to ensure proper netlink message padding. |
| * nlmsg_append(msg, &mydata, sizeof(mydata), 0); |
| * |
| * // Sometimes it may be necessary to reserve room for data but defer |
| * // the actual copying to a later point, nlmsg_reserve() can be used |
| * // for this purpose: |
| * void *data = nlmsg_reserve(msg, sizeof(mydata), NLMSG_ALIGNTO); |
| * |
| * // Attributes may be added using the attributes interface. |
| * |
| * // After successful use of the message, the memory must be freed |
| * // using nlmsg_free() |
| * nlmsg_free(msg); |
| * @endcode |
| * |
| * @par 4) Parsing messages |
| * @code |
| * int n; |
| * unsigned char *buf; |
| * struct nlmsghdr *hdr; |
| * |
| * n = nl_recv(handle, NULL, &buf); |
| * |
| * hdr = (struct nlmsghdr *) buf; |
| * while (nlmsg_ok(hdr, n)) { |
| * // Process message here... |
| * hdr = nlmsg_next(hdr, &n); |
| * } |
| * @endcode |
| * @{ |
| */ |
| |
| #include <netlink-local.h> |
| #include <netlink/netlink.h> |
| #include <netlink/utils.h> |
| #include <netlink/cache.h> |
| #include <netlink/attr.h> |
| #include <linux/socket.h> |
| |
| static size_t default_msg_size; |
| |
| static void __init init_msg_size(void) |
| { |
| default_msg_size = getpagesize(); |
| } |
| |
| /** |
| * @name Size Calculations |
| * @{ |
| */ |
| |
| /** |
| * length of netlink message not including padding |
| * @arg payload length of message payload |
| */ |
| int nlmsg_msg_size(int payload) |
| { |
| return NLMSG_HDRLEN + payload; |
| } |
| |
| /** |
| * length of netlink message including padding |
| * @arg payload length of message payload |
| */ |
| int nlmsg_total_size(int payload) |
| { |
| return NLMSG_ALIGN(nlmsg_msg_size(payload)); |
| } |
| |
| /** |
| * length of padding at the message's tail |
| * @arg payload length of message payload |
| */ |
| int nlmsg_padlen(int payload) |
| { |
| return nlmsg_total_size(payload) - nlmsg_msg_size(payload); |
| } |
| |
| /** @} */ |
| |
| /** |
| * @name Payload Access |
| * @{ |
| */ |
| |
| /** |
| * head of message payload |
| * @arg nlh netlink messsage header |
| */ |
| void *nlmsg_data(const struct nlmsghdr *nlh) |
| { |
| return (unsigned char *) nlh + NLMSG_HDRLEN; |
| } |
| |
| void *nlmsg_tail(const struct nlmsghdr *nlh) |
| { |
| return (unsigned char *) nlh + NLMSG_ALIGN(nlh->nlmsg_len); |
| } |
| |
| /** |
| * length of message payload |
| * @arg nlh netlink message header |
| */ |
| int nlmsg_len(const struct nlmsghdr *nlh) |
| { |
| return nlh->nlmsg_len - NLMSG_HDRLEN; |
| } |
| |
| /** @} */ |
| |
| /** |
| * @name Attribute Access |
| * @{ |
| */ |
| |
| /** |
| * head of attributes data |
| * @arg nlh netlink message header |
| * @arg hdrlen length of family specific header |
| */ |
| struct nlattr *nlmsg_attrdata(const struct nlmsghdr *nlh, int hdrlen) |
| { |
| unsigned char *data = nlmsg_data(nlh); |
| return (struct nlattr *) (data + NLMSG_ALIGN(hdrlen)); |
| } |
| |
| /** |
| * length of attributes data |
| * @arg nlh netlink message header |
| * @arg hdrlen length of family specific header |
| */ |
| int nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen) |
| { |
| return nlmsg_len(nlh) - NLMSG_ALIGN(hdrlen); |
| } |
| |
| /** @} */ |
| |
| /** |
| * @name Message Parsing |
| * @{ |
| */ |
| |
| int nlmsg_valid_hdr(const struct nlmsghdr *nlh, int hdrlen) |
| { |
| if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) |
| return 0; |
| |
| return 1; |
| } |
| |
| /** |
| * check if the netlink message fits into the remaining bytes |
| * @arg nlh netlink message header |
| * @arg remaining number of bytes remaining in message stream |
| */ |
| int nlmsg_ok(const struct nlmsghdr *nlh, int remaining) |
| { |
| return (remaining >= sizeof(struct nlmsghdr) && |
| nlh->nlmsg_len >= sizeof(struct nlmsghdr) && |
| nlh->nlmsg_len <= remaining); |
| } |
| |
| /** |
| * next netlink message in message stream |
| * @arg nlh netlink message header |
| * @arg remaining number of bytes remaining in message stream |
| * |
| * @returns the next netlink message in the message stream and |
| * decrements remaining by the size of the current message. |
| */ |
| struct nlmsghdr *nlmsg_next(struct nlmsghdr *nlh, int *remaining) |
| { |
| int totlen = NLMSG_ALIGN(nlh->nlmsg_len); |
| |
| *remaining -= totlen; |
| |
| return (struct nlmsghdr *) ((unsigned char *) nlh + totlen); |
| } |
| |
| /** |
| * parse attributes of a netlink message |
| * @arg nlh netlink message header |
| * @arg hdrlen length of family specific header |
| * @arg tb destination array with maxtype+1 elements |
| * @arg maxtype maximum attribute type to be expected |
| * @arg policy validation policy |
| * |
| * See nla_parse() |
| */ |
| int nlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], |
| int maxtype, struct nla_policy *policy) |
| { |
| if (!nlmsg_valid_hdr(nlh, hdrlen)) |
| return -NLE_MSG_TOOSHORT; |
| |
| return nla_parse(tb, maxtype, nlmsg_attrdata(nlh, hdrlen), |
| nlmsg_attrlen(nlh, hdrlen), policy); |
| } |
| |
| /** |
| * nlmsg_find_attr - find a specific attribute in a netlink message |
| * @arg nlh netlink message header |
| * @arg hdrlen length of familiy specific header |
| * @arg attrtype type of attribute to look for |
| * |
| * Returns the first attribute which matches the specified type. |
| */ |
| struct nlattr *nlmsg_find_attr(struct nlmsghdr *nlh, int hdrlen, int attrtype) |
| { |
| return nla_find(nlmsg_attrdata(nlh, hdrlen), |
| nlmsg_attrlen(nlh, hdrlen), attrtype); |
| } |
| |
| /** |
| * nlmsg_validate - validate a netlink message including attributes |
| * @arg nlh netlinket message header |
| * @arg hdrlen length of familiy specific header |
| * @arg maxtype maximum attribute type to be expected |
| * @arg policy validation policy |
| */ |
| int nlmsg_validate(struct nlmsghdr *nlh, int hdrlen, int maxtype, |
| struct nla_policy *policy) |
| { |
| if (!nlmsg_valid_hdr(nlh, hdrlen)) |
| return -NLE_MSG_TOOSHORT; |
| |
| return nla_validate(nlmsg_attrdata(nlh, hdrlen), |
| nlmsg_attrlen(nlh, hdrlen), maxtype, policy); |
| } |
| |
| /** @} */ |
| |
| /** |
| * @name Message Building/Access |
| * @{ |
| */ |
| |
| static struct nl_msg *__nlmsg_alloc(size_t len) |
| { |
| struct nl_msg *nm; |
| |
| nm = calloc(1, sizeof(*nm)); |
| if (!nm) |
| goto errout; |
| |
| nm->nm_refcnt = 1; |
| |
| nm->nm_nlh = malloc(len); |
| if (!nm->nm_nlh) |
| goto errout; |
| |
| memset(nm->nm_nlh, 0, sizeof(struct nlmsghdr)); |
| |
| nm->nm_protocol = -1; |
| nm->nm_size = len; |
| nm->nm_nlh->nlmsg_len = nlmsg_total_size(0); |
| |
| NL_DBG(2, "msg %p: Allocated new message, maxlen=%zu\n", nm, len); |
| |
| return nm; |
| errout: |
| free(nm); |
| return NULL; |
| } |
| |
| /** |
| * Allocate a new netlink message with the default maximum payload size. |
| * |
| * Allocates a new netlink message without any further payload. The |
| * maximum payload size defaults to PAGESIZE or as otherwise specified |
| * with nlmsg_set_default_size(). |
| * |
| * @return Newly allocated netlink message or NULL. |
| */ |
| struct nl_msg *nlmsg_alloc(void) |
| { |
| return __nlmsg_alloc(default_msg_size); |
| } |
| |
| /** |
| * Allocate a new netlink message with maximum payload size specified. |
| */ |
| struct nl_msg *nlmsg_alloc_size(size_t max) |
| { |
| return __nlmsg_alloc(max); |
| } |
| |
| /** |
| * Allocate a new netlink message and inherit netlink message header |
| * @arg hdr Netlink message header template |
| * |
| * Allocates a new netlink message and inherits the original message |
| * header. If \a hdr is not NULL it will be used as a template for |
| * the netlink message header, otherwise the header is left blank. |
| * |
| * @return Newly allocated netlink message or NULL |
| */ |
| struct nl_msg *nlmsg_inherit(struct nlmsghdr *hdr) |
| { |
| struct nl_msg *nm; |
| |
| nm = nlmsg_alloc(); |
| if (nm && hdr) { |
| struct nlmsghdr *new = nm->nm_nlh; |
| |
| new->nlmsg_type = hdr->nlmsg_type; |
| new->nlmsg_flags = hdr->nlmsg_flags; |
| new->nlmsg_seq = hdr->nlmsg_seq; |
| new->nlmsg_pid = hdr->nlmsg_pid; |
| } |
| |
| return nm; |
| } |
| |
| /** |
| * Allocate a new netlink message |
| * @arg nlmsgtype Netlink message type |
| * @arg flags Message flags. |
| * |
| * @return Newly allocated netlink message or NULL. |
| */ |
| struct nl_msg *nlmsg_alloc_simple(int nlmsgtype, int flags) |
| { |
| struct nl_msg *msg; |
| struct nlmsghdr nlh = { |
| .nlmsg_type = nlmsgtype, |
| .nlmsg_flags = flags, |
| }; |
| |
| msg = nlmsg_inherit(&nlh); |
| if (msg) |
| NL_DBG(2, "msg %p: Allocated new simple message\n", msg); |
| |
| return msg; |
| } |
| |
| /** |
| * Set the default maximum message payload size for allocated messages |
| * @arg max Size of payload in bytes. |
| */ |
| void nlmsg_set_default_size(size_t max) |
| { |
| if (max < nlmsg_total_size(0)) |
| max = nlmsg_total_size(0); |
| |
| default_msg_size = max; |
| } |
| |
| /** |
| * Convert a netlink message received from a netlink socket to a nl_msg |
| * @arg hdr Netlink message received from netlink socket. |
| * |
| * Allocates a new netlink message and copies all of the data pointed to |
| * by \a hdr into the new message object. |
| * |
| * @return Newly allocated netlink message or NULL. |
| */ |
| struct nl_msg *nlmsg_convert(struct nlmsghdr *hdr) |
| { |
| struct nl_msg *nm; |
| |
| nm = __nlmsg_alloc(NLMSG_ALIGN(hdr->nlmsg_len)); |
| if (!nm) |
| goto errout; |
| |
| memcpy(nm->nm_nlh, hdr, hdr->nlmsg_len); |
| |
| return nm; |
| errout: |
| nlmsg_free(nm); |
| return NULL; |
| } |
| |
| /** |
| * Reserve room for additional data in a netlink message |
| * @arg n netlink message |
| * @arg len length of additional data to reserve room for |
| * @arg pad number of bytes to align data to |
| * |
| * Reserves room for additional data at the tail of the an |
| * existing netlink message. Eventual padding required will |
| * be zeroed out. |
| * |
| * @return Pointer to start of additional data tailroom or NULL. |
| */ |
| void *nlmsg_reserve(struct nl_msg *n, size_t len, int pad) |
| { |
| void *buf = n->nm_nlh; |
| size_t nlmsg_len = n->nm_nlh->nlmsg_len; |
| size_t tlen; |
| |
| tlen = pad ? ((len + (pad - 1)) & ~(pad - 1)) : len; |
| |
| if ((tlen + nlmsg_len) > n->nm_size) |
| return NULL; |
| |
| buf += nlmsg_len; |
| n->nm_nlh->nlmsg_len += tlen; |
| |
| if (tlen > len) |
| memset(buf + len, 0, tlen - len); |
| |
| NL_DBG(2, "msg %p: Reserved %zu bytes, pad=%d, nlmsg_len=%d\n", |
| n, len, pad, n->nm_nlh->nlmsg_len); |
| |
| return buf; |
| } |
| |
| /** |
| * Append data to tail of a netlink message |
| * @arg n netlink message |
| * @arg data data to add |
| * @arg len length of data |
| * @arg pad Number of bytes to align data to. |
| * |
| * Extends the netlink message as needed and appends the data of given |
| * length to the message. |
| * |
| * @return 0 on success or a negative error code |
| */ |
| int nlmsg_append(struct nl_msg *n, void *data, size_t len, int pad) |
| { |
| void *tmp; |
| |
| tmp = nlmsg_reserve(n, len, pad); |
| if (tmp == NULL) |
| return -NLE_NOMEM; |
| |
| memcpy(tmp, data, len); |
| NL_DBG(2, "msg %p: Appended %zu bytes with padding %d\n", n, len, pad); |
| |
| return 0; |
| } |
| |
| /** |
| * Expand maximum payload size of a netlink message |
| * @arg n Netlink message. |
| * @arg newlen New maximum payload size. |
| * |
| * Reallocates the payload section of a netlink message and increases |
| * the maximum payload size of the message. |
| * |
| * @note Any pointers pointing to old payload block will be stale and |
| * need to be refetched. Therfore, do not expand while constructing |
| * nested attributes or while reserved data blocks are held. |
| * |
| * @return 0 on success or a negative error code. |
| */ |
| int nlmsg_expand(struct nl_msg *n, size_t newlen) |
| { |
| void *tmp; |
| |
| if (newlen <= n->nm_size) |
| return -NLE_INVAL; |
| |
| tmp = realloc(n->nm_nlh, newlen); |
| if (tmp == NULL) |
| return -NLE_NOMEM; |
| |
| n->nm_nlh = tmp; |
| n->nm_size = newlen; |
| |
| return 0; |
| } |
| |
| /** |
| * Add a netlink message header to a netlink message |
| * @arg n netlink message |
| * @arg pid netlink process id or NL_AUTO_PID |
| * @arg seq sequence number of message or NL_AUTO_SEQ |
| * @arg type message type |
| * @arg payload length of message payload |
| * @arg flags message flags |
| * |
| * Adds or overwrites the netlink message header in an existing message |
| * object. If \a payload is greater-than zero additional room will be |
| * reserved, f.e. for family specific headers. It can be accesed via |
| * nlmsg_data(). |
| * |
| * @return A pointer to the netlink message header or NULL. |
| */ |
| struct nlmsghdr *nlmsg_put(struct nl_msg *n, uint32_t pid, uint32_t seq, |
| int type, int payload, int flags) |
| { |
| struct nlmsghdr *nlh; |
| |
| if (n->nm_nlh->nlmsg_len < NLMSG_HDRLEN) |
| BUG(); |
| |
| nlh = (struct nlmsghdr *) n->nm_nlh; |
| nlh->nlmsg_type = type; |
| nlh->nlmsg_flags = flags; |
| nlh->nlmsg_pid = pid; |
| nlh->nlmsg_seq = seq; |
| |
| NL_DBG(2, "msg %p: Added netlink header type=%d, flags=%d, pid=%d, " |
| "seq=%d\n", n, type, flags, pid, seq); |
| |
| if (payload > 0 && |
| nlmsg_reserve(n, payload, NLMSG_ALIGNTO) == NULL) |
| return NULL; |
| |
| return nlh; |
| } |
| |
| /** |
| * Return actual netlink message |
| * @arg n netlink message |
| * |
| * Returns the actual netlink message casted to the type of the netlink |
| * message header. |
| * |
| * @return A pointer to the netlink message. |
| */ |
| struct nlmsghdr *nlmsg_hdr(struct nl_msg *n) |
| { |
| return n->nm_nlh; |
| } |
| |
| /** |
| * Acquire a reference on a netlink message |
| * @arg msg message to acquire reference from |
| */ |
| void nlmsg_get(struct nl_msg *msg) |
| { |
| msg->nm_refcnt++; |
| NL_DBG(4, "New reference to message %p, total %d\n", |
| msg, msg->nm_refcnt); |
| } |
| |
| /** |
| * Release a reference from an netlink message |
| * @arg msg message to release reference from |
| * |
| * Frees memory after the last reference has been released. |
| */ |
| void nlmsg_free(struct nl_msg *msg) |
| { |
| if (!msg) |
| return; |
| |
| msg->nm_refcnt--; |
| NL_DBG(4, "Returned message reference %p, %d remaining\n", |
| msg, msg->nm_refcnt); |
| |
| if (msg->nm_refcnt < 0) |
| BUG(); |
| |
| if (msg->nm_refcnt <= 0) { |
| free(msg->nm_nlh); |
| free(msg); |
| NL_DBG(2, "msg %p: Freed\n", msg); |
| } |
| } |
| |
| /** @} */ |
| |
| /** |
| * @name Attributes |
| * @{ |
| */ |
| |
| void nlmsg_set_proto(struct nl_msg *msg, int protocol) |
| { |
| msg->nm_protocol = protocol; |
| } |
| |
| int nlmsg_get_proto(struct nl_msg *msg) |
| { |
| return msg->nm_protocol; |
| } |
| |
| size_t nlmsg_get_max_size(struct nl_msg *msg) |
| { |
| return msg->nm_size; |
| } |
| |
| void nlmsg_set_src(struct nl_msg *msg, struct sockaddr_nl *addr) |
| { |
| memcpy(&msg->nm_src, addr, sizeof(*addr)); |
| } |
| |
| struct sockaddr_nl *nlmsg_get_src(struct nl_msg *msg) |
| { |
| return &msg->nm_src; |
| } |
| |
| void nlmsg_set_dst(struct nl_msg *msg, struct sockaddr_nl *addr) |
| { |
| memcpy(&msg->nm_dst, addr, sizeof(*addr)); |
| } |
| |
| struct sockaddr_nl *nlmsg_get_dst(struct nl_msg *msg) |
| { |
| return &msg->nm_dst; |
| } |
| |
| void nlmsg_set_creds(struct nl_msg *msg, struct ucred *creds) |
| { |
| memcpy(&msg->nm_creds, creds, sizeof(*creds)); |
| msg->nm_flags |= NL_MSG_CRED_PRESENT; |
| } |
| |
| struct ucred *nlmsg_get_creds(struct nl_msg *msg) |
| { |
| if (msg->nm_flags & NL_MSG_CRED_PRESENT) |
| return &msg->nm_creds; |
| return NULL; |
| } |
| |
| /** @} */ |
| |
| /** |
| * @name Netlink Message Type Translations |
| * @{ |
| */ |
| |
| static struct trans_tbl nl_msgtypes[] = { |
| __ADD(NLMSG_NOOP,NOOP) |
| __ADD(NLMSG_ERROR,ERROR) |
| __ADD(NLMSG_DONE,DONE) |
| __ADD(NLMSG_OVERRUN,OVERRUN) |
| }; |
| |
| char *nl_nlmsgtype2str(int type, char *buf, size_t size) |
| { |
| return __type2str(type, buf, size, nl_msgtypes, |
| ARRAY_SIZE(nl_msgtypes)); |
| } |
| |
| int nl_str2nlmsgtype(const char *name) |
| { |
| return __str2type(name, nl_msgtypes, ARRAY_SIZE(nl_msgtypes)); |
| } |
| |
| /** @} */ |
| |
| /** |
| * @name Netlink Message Flags Translations |
| * @{ |
| */ |
| |
| char *nl_nlmsg_flags2str(int flags, char *buf, size_t len) |
| { |
| memset(buf, 0, len); |
| |
| #define PRINT_FLAG(f) \ |
| if (flags & NLM_F_##f) { \ |
| flags &= ~NLM_F_##f; \ |
| strncat(buf, #f, len - strlen(buf) - 1); \ |
| if (flags) \ |
| strncat(buf, ",", len - strlen(buf) - 1); \ |
| } |
| |
| PRINT_FLAG(REQUEST); |
| PRINT_FLAG(MULTI); |
| PRINT_FLAG(ACK); |
| PRINT_FLAG(ECHO); |
| PRINT_FLAG(ROOT); |
| PRINT_FLAG(MATCH); |
| PRINT_FLAG(ATOMIC); |
| PRINT_FLAG(REPLACE); |
| PRINT_FLAG(EXCL); |
| PRINT_FLAG(CREATE); |
| PRINT_FLAG(APPEND); |
| |
| if (flags) { |
| char s[32]; |
| snprintf(s, sizeof(s), "0x%x", flags); |
| strncat(buf, s, len - strlen(buf) - 1); |
| } |
| #undef PRINT_FLAG |
| |
| return buf; |
| } |
| |
| /** @} */ |
| |
| /** |
| * @name Direct Parsing |
| * @{ |
| */ |
| |
| /** @cond SKIP */ |
| struct dp_xdata { |
| void (*cb)(struct nl_object *, void *); |
| void *arg; |
| }; |
| /** @endcond */ |
| |
| static int parse_cb(struct nl_object *obj, struct nl_parser_param *p) |
| { |
| struct dp_xdata *x = p->pp_arg; |
| |
| x->cb(obj, x->arg); |
| return 0; |
| } |
| |
| int nl_msg_parse(struct nl_msg *msg, void (*cb)(struct nl_object *, void *), |
| void *arg) |
| { |
| struct nl_cache_ops *ops; |
| struct nl_parser_param p = { |
| .pp_cb = parse_cb |
| }; |
| struct dp_xdata x = { |
| .cb = cb, |
| .arg = arg, |
| }; |
| |
| ops = nl_cache_ops_associate(nlmsg_get_proto(msg), |
| nlmsg_hdr(msg)->nlmsg_type); |
| if (ops == NULL) |
| return -NLE_MSGTYPE_NOSUPPORT; |
| p.pp_arg = &x; |
| |
| return nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p); |
| } |
| |
| /** @} */ |
| |
| /** |
| * @name Dumping |
| * @{ |
| */ |
| |
| static void prefix_line(FILE *ofd, int prefix) |
| { |
| int i; |
| |
| for (i = 0; i < prefix; i++) |
| fprintf(ofd, " "); |
| } |
| |
| static inline void dump_hex(FILE *ofd, char *start, int len, int prefix) |
| { |
| int i, a, c, limit; |
| char ascii[21] = {0}; |
| |
| limit = 18 - (prefix * 2); |
| prefix_line(ofd, prefix); |
| fprintf(ofd, " "); |
| |
| for (i = 0, a = 0, c = 0; i < len; i++) { |
| int v = *(uint8_t *) (start + i); |
| |
| fprintf(ofd, "%02x ", v); |
| ascii[a++] = isprint(v) ? v : '.'; |
| |
| if (c == limit-1) { |
| fprintf(ofd, "%s\n", ascii); |
| if (i < (len - 1)) { |
| prefix_line(ofd, prefix); |
| fprintf(ofd, " "); |
| } |
| a = c = 0; |
| memset(ascii, 0, sizeof(ascii)); |
| } else |
| c++; |
| } |
| |
| if (c != 0) { |
| for (i = 0; i < (limit - c); i++) |
| fprintf(ofd, " "); |
| fprintf(ofd, "%s\n", ascii); |
| } |
| } |
| |
| static void print_hdr(FILE *ofd, struct nl_msg *msg) |
| { |
| struct nlmsghdr *nlh = nlmsg_hdr(msg); |
| struct nl_cache_ops *ops; |
| struct nl_msgtype *mt; |
| char buf[128]; |
| |
| fprintf(ofd, " .nlmsg_len = %d\n", nlh->nlmsg_len); |
| |
| ops = nl_cache_ops_associate(nlmsg_get_proto(msg), nlh->nlmsg_type); |
| if (ops) { |
| mt = nl_msgtype_lookup(ops, nlh->nlmsg_type); |
| if (!mt) |
| BUG(); |
| |
| snprintf(buf, sizeof(buf), "%s::%s", ops->co_name, mt->mt_name); |
| } else |
| nl_nlmsgtype2str(nlh->nlmsg_type, buf, sizeof(buf)); |
| |
| fprintf(ofd, " .nlmsg_type = %d <%s>\n", nlh->nlmsg_type, buf); |
| fprintf(ofd, " .nlmsg_flags = %d <%s>\n", nlh->nlmsg_flags, |
| nl_nlmsg_flags2str(nlh->nlmsg_flags, buf, sizeof(buf))); |
| fprintf(ofd, " .nlmsg_seq = %d\n", nlh->nlmsg_seq); |
| fprintf(ofd, " .nlmsg_pid = %d\n", nlh->nlmsg_pid); |
| |
| } |
| |
| static void dump_attrs(FILE *ofd, struct nlattr *attrs, int attrlen, |
| int prefix) |
| { |
| int rem; |
| struct nlattr *nla; |
| |
| nla_for_each_attr(nla, attrs, attrlen, rem) { |
| int padlen, alen = nla_len(nla); |
| |
| prefix_line(ofd, prefix); |
| fprintf(ofd, " [ATTR %02d%s] %d octets\n", nla_type(nla), |
| nla->nla_type & NLA_F_NESTED ? " NESTED" : "", |
| alen); |
| |
| if (nla->nla_type & NLA_F_NESTED) |
| dump_attrs(ofd, nla_data(nla), alen, prefix+1); |
| else |
| dump_hex(ofd, nla_data(nla), alen, prefix); |
| |
| padlen = nla_padlen(alen); |
| if (padlen > 0) { |
| prefix_line(ofd, prefix); |
| fprintf(ofd, " [PADDING] %d octets\n", |
| padlen); |
| dump_hex(ofd, nla_data(nla) + alen, |
| padlen, prefix); |
| } |
| } |
| |
| if (rem) { |
| prefix_line(ofd, prefix); |
| fprintf(ofd, " [LEFTOVER] %d octets\n", rem); |
| } |
| } |
| |
| /** |
| * Dump message in human readable format to file descriptor |
| * @arg msg Message to print |
| * @arg ofd File descriptor. |
| */ |
| void nl_msg_dump(struct nl_msg *msg, FILE *ofd) |
| { |
| struct nlmsghdr *hdr = nlmsg_hdr(msg); |
| |
| fprintf(ofd, |
| "-------------------------- BEGIN NETLINK MESSAGE " |
| "---------------------------\n"); |
| |
| fprintf(ofd, " [HEADER] %Zu octets\n", sizeof(struct nlmsghdr)); |
| print_hdr(ofd, msg); |
| |
| if (hdr->nlmsg_type == NLMSG_ERROR && |
| hdr->nlmsg_len >= nlmsg_msg_size(sizeof(struct nlmsgerr))) { |
| struct nl_msg *errmsg; |
| struct nlmsgerr *err = nlmsg_data(hdr); |
| |
| fprintf(ofd, " [ERRORMSG] %Zu octets\n", sizeof(*err)); |
| fprintf(ofd, " .error = %d \"%s\"\n", err->error, |
| strerror(-err->error)); |
| fprintf(ofd, " [ORIGINAL MESSAGE] %Zu octets\n", sizeof(*hdr)); |
| |
| errmsg = nlmsg_inherit(&err->msg); |
| print_hdr(ofd, errmsg); |
| nlmsg_free(errmsg); |
| } else if (nlmsg_len(hdr) > 0) { |
| struct nl_cache_ops *ops; |
| int payloadlen = nlmsg_len(hdr); |
| int attrlen = 0; |
| |
| ops = nl_cache_ops_associate(nlmsg_get_proto(msg), |
| hdr->nlmsg_type); |
| if (ops) { |
| attrlen = nlmsg_attrlen(hdr, ops->co_hdrsize); |
| payloadlen -= attrlen; |
| } |
| |
| fprintf(ofd, " [PAYLOAD] %d octets\n", payloadlen); |
| dump_hex(ofd, nlmsg_data(hdr), payloadlen, 0); |
| |
| if (attrlen) { |
| struct nlattr *attrs; |
| int attrlen; |
| |
| attrs = nlmsg_attrdata(hdr, ops->co_hdrsize); |
| attrlen = nlmsg_attrlen(hdr, ops->co_hdrsize); |
| dump_attrs(ofd, attrs, attrlen, 0); |
| } |
| } |
| |
| fprintf(ofd, |
| "--------------------------- END NETLINK MESSAGE " |
| "---------------------------\n"); |
| } |
| |
| /** @} */ |
| |
| /** @} */ |