/* * (C) 2008-2010 by Pablo Neira Ayuso * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include #include #include #include #include #include #include /** * \mainpage * * libmnl is a minimalistic user-space library oriented to Netlink developers. * There are a lot of common tasks in parsing, validating, constructing of * both the Netlink header and TLVs that are repetitive and easy to get wrong. * This library aims to provide simple helpers that allows you to avoid * re-inventing the wheel in common Netlink tasks. * * \verbatim "Simplify, simplify" -- Henry David Thoureau. Walden (1854) \endverbatim * * The acronim libmnl stands for LIBrary Minimalistic NetLink. * * (temporary) libmnl homepage is: * http://1984.lsi.us.es/projects/libmnl/ * * \section features Main Features * - Small: the shared library requires around 30KB in a x86-based computer. * - Simple: this library avoids complex abstractions that tend to hide Netlink * details. It avoids elaborated object-oriented infrastructure and complex * callback-based workflow. * - Easy to use: the library simplifies the work for Netlink-wise developers. * It provides functions to make socket handling, message building, * validating, parsing, and sequence tracking, easier. * - Easy to re-use: you can use this library to build your own abstraction * layer upon this library, if you want to provide another library that * hides Netlink details to your users. * - Decoupling: the interdependency of the main bricks that compose this * library is reduced, ie. the library provides many helpers but the * programmer is not forced to use them. * * \section Dependencies * You have to install the Linux kernel headers that you want to use to develop * your application. Moreover, this library requires that you have some basics * on Netlink. * * \section scm Git Tree * The current development version of libmnl can be accessed at: * http://1984.lsi.us.es/git/?p=libmnl/.git;a=summary * * \section using Using libmnl * You can access several examples files under examples/ in the libmnl source * code tree. */ struct mnl_socket { int fd; struct sockaddr_nl addr; }; /** * \defgroup socket Netlink socket helpers * @{ */ /** * mnl_socket_get_fd - obtain file descriptor from netlink socket * \param nl netlink socket obtained via mnl_socket_open() * * This function returns the file descriptor of a given netlink socket. */ int mnl_socket_get_fd(const struct mnl_socket *nl) { return nl->fd; } /** * mnl_socket_get_portid - obtain Netlink PortID from netlink socket * \param nl netlink socket obtained via mnl_socket_open() * * This function returns the Netlink PortID of a given netlink socket. * It's a common mistake to assume that this PortID equals the process ID * which is not always true. This is the case if you open more than one * socket that is binded to the same Netlink subsystem from the same process. */ unsigned int mnl_socket_get_portid(const struct mnl_socket *nl) { return nl->addr.nl_pid; } /** * mnl_socket_open - open a netlink socket * \param unit the netlink socket bus ID (see NETLINK_* constants) * * On error, it returns -1 and errno is appropriately set. Otherwise, it * returns a valid pointer to the mnl_socket structure. */ struct mnl_socket *mnl_socket_open(int bus) { struct mnl_socket *nl; nl = calloc(sizeof(struct mnl_socket), 1); if (nl == NULL) return NULL; nl->fd = socket(AF_NETLINK, SOCK_RAW, bus); if (nl->fd == -1) { free(nl); return NULL; } return nl; } /** * mnl_socket_bind - bind netlink socket * \param nl netlink socket obtained via mnl_socket_open() * \param groups the group of message you're interested in * \param pid the port ID you want to use (use zero for automatic selection) * * On error, this function returns -1 and errno is appropriately set. On * success, 0 is returned. You can use MNL_SOCKET_AUTOPID which is 0 for * automatic port ID selection. */ int mnl_socket_bind(struct mnl_socket *nl, int groups, int pid) { int ret; socklen_t addr_len; nl->addr.nl_family = AF_NETLINK; nl->addr.nl_groups = groups; nl->addr.nl_pid = pid; ret = bind(nl->fd, (struct sockaddr *) &nl->addr, sizeof (nl->addr)); if (ret < 0) return ret; addr_len = sizeof(nl->addr); ret = getsockname(nl->fd, (struct sockaddr *) &nl->addr, &addr_len); if (ret < 0) return ret; if (addr_len != sizeof(nl->addr)) { errno = EINVAL; return -1; } if (nl->addr.nl_family != AF_NETLINK) { errno = EINVAL; return -1; } return 0; } /** * mnl_socket_sendto - send a netlink message of a certain size * \param nl netlink socket obtained via mnl_socket_open() * \param buf buffer containing the netlink message to be sent * \param bufsiz number of bytes in the buffer that you want to send * * On error, it returns -1 and errno is appropriately set. Otherwise, it * returns the number of bytes sent. */ int mnl_socket_sendto(const struct mnl_socket *nl, const void *buf, size_t len) { struct sockaddr_nl snl = { .nl_family = AF_NETLINK }; return sendto(nl->fd, buf, len, 0, (struct sockaddr *) &snl, sizeof(snl)); } /** * mnl_socket_recvfrom - receive a netlink message * \param nl netlink socket obtained via mnl_socket_open() * \param buf buffer that you want to use to store the netlink message * \param bufsiz size of the buffer passed to store the netlink message * * On error, it returns -1 and errno is appropriately set. If errno is set * to ENOSPC, it means that the buffer that you have passed to store the * netlink message is small so you have received a truncated message. Make * sure your program set a buffer big enough to store the netlink message. */ int mnl_socket_recvfrom(const struct mnl_socket *nl, void *buf, size_t bufsiz) { int ret; struct sockaddr_nl addr; struct iovec iov = { .iov_base = buf, .iov_len = bufsiz, }; struct msghdr msg = { .msg_name = (void *)&addr, .msg_namelen = sizeof(struct sockaddr_nl), .msg_iov = &iov, .msg_iovlen = 1, .msg_control = NULL, .msg_controllen = 0, .msg_flags = 0, }; ret = recvmsg(nl->fd, &msg, 0); if (ret == -1) return ret; if (msg.msg_flags & MSG_TRUNC) { errno = ENOSPC; return -1; } if (msg.msg_namelen != sizeof(struct sockaddr_nl)) { errno = EINVAL; return -1; } return ret; } /** * mnl_socket_close - close a given netlink socket * \param nl netlink socket obtained via mnl_socket_open() * * On error, this function returns -1 and errno is appropriately set. * On success, it returns 0. */ int mnl_socket_close(struct mnl_socket *nl) { int ret = close(nl->fd); free(nl); nl = NULL; return ret; } /** * mnl_socket_setsockopt - set Netlink socket option * \param nl netlink socket obtained via mnl_socket_open() * \param type type of Netlink socket options * \param buf the buffer that contains the data about this option * \param len the size of the buffer passed * * This function allows you to set some Netlink socket option. As of writing * this, the existing options are: * * #define NETLINK_ADD_MEMBERSHIP 1 * #define NETLINK_DROP_MEMBERSHIP 2 * #define NETLINK_PKTINFO 3 * #define NETLINK_BROADCAST_ERROR 4 * #define NETLINK_NO_ENOBUFS 5 * * In the early days, Netlink only supported 32 groups expressed in a * 32-bits mask. However, since 2.6.14, Netlink may have up to 2^32 multicast * groups but you have to use setsockopt() with NETLINK_ADD_MEMBERSHIP to * join a given multicast group. This function internally calls setsockopt() * to join a given netlink multicast group. You can still use mnl_bind() * and the 32-bit mask to join a set of Netlink multicast groups. * * On error, this function returns -1 and errno is appropriately set. */ int mnl_socket_setsockopt(const struct mnl_socket *nl, int type, void *buf, socklen_t len) { return setsockopt(nl->fd, SOL_NETLINK, type, buf, len); } /** * mnl_socket_getsockopt - get a Netlink socket option * \param nl netlink socket obtained via mnl_socket_open() * \param type type of Netlink socket options * \param buf pointer to the buffer to store the value of this option * \param len size of the information written in the buffer * * On error, this function returns -1 and errno is appropriately set. */ int mnl_socket_getsockopt(const struct mnl_socket *nl, int type, void *buf, socklen_t *len) { return getsockopt(nl->fd, SOL_NETLINK, type, buf, len); } /** * @} */