/* * (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 struct mnl_socket { int fd; struct sockaddr_nl addr; }; /** * mnl_socket_get_fd - obtain file descriptor from netlink socket * @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 * @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 * @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 * @nl: netlink socket obtained via mnl_socket_open() * @groups: the group of message you're interested in * @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 * @nl: netlink socket obtained via mnl_socket_open() * @buf: buffer containing the netlink message to be sent * @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 * @nl: netlink socket obtained via mnl_socket_open() * @buf: buffer that you want to use to store the netlink message * @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 * @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 * @nl: netlink socket obtained via mnl_socket_open() * @type: type of Netlink socket options * @buf: the buffer that contains the data about this option * @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 * @nl: netlink socket obtained via mnl_socket_open() * @type: type of Netlink socket options * @buf: pointer to the buffer to store the value of this option * @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); }