Blame tools/daemon/nl.c

Packit 6d2c1b
/*
Packit 6d2c1b
 * Copyright (c) 2001-2020 Mellanox Technologies, Ltd. All rights reserved.
Packit 6d2c1b
 *
Packit 6d2c1b
 * This software is available to you under a choice of one of two
Packit 6d2c1b
 * licenses.  You may choose to be licensed under the terms of the GNU
Packit 6d2c1b
 * General Public License (GPL) Version 2, available from the file
Packit 6d2c1b
 * COPYING in the main directory of this source tree, or the
Packit 6d2c1b
 * BSD license below:
Packit 6d2c1b
 *
Packit 6d2c1b
 *     Redistribution and use in source and binary forms, with or
Packit 6d2c1b
 *     without modification, are permitted provided that the following
Packit 6d2c1b
 *     conditions are met:
Packit 6d2c1b
 *
Packit 6d2c1b
 *      - Redistributions of source code must retain the above
Packit 6d2c1b
 *        copyright notice, this list of conditions and the following
Packit 6d2c1b
 *        disclaimer.
Packit 6d2c1b
 *
Packit 6d2c1b
 *      - Redistributions in binary form must reproduce the above
Packit 6d2c1b
 *        copyright notice, this list of conditions and the following
Packit 6d2c1b
 *        disclaimer in the documentation and/or other materials
Packit 6d2c1b
 *        provided with the distribution.
Packit 6d2c1b
 *
Packit 6d2c1b
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Packit 6d2c1b
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
Packit 6d2c1b
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
Packit 6d2c1b
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
Packit 6d2c1b
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
Packit 6d2c1b
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
Packit 6d2c1b
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
Packit 6d2c1b
 * SOFTWARE.
Packit 6d2c1b
 */
Packit 6d2c1b
Packit 6d2c1b
Packit 6d2c1b
#include <errno.h>
Packit 6d2c1b
#include <inttypes.h>
Packit 6d2c1b
#include <string.h>
Packit 6d2c1b
#include <sys/socket.h>
Packit 6d2c1b
#include <unistd.h>
Packit 6d2c1b
Packit 6d2c1b
#include "hash.h"
Packit 6d2c1b
#include "tc.h"
Packit 6d2c1b
#include "daemon.h"
Packit 6d2c1b
#include "nl.h"
Packit 6d2c1b
Packit 6d2c1b
Packit 6d2c1b
/**
Packit 6d2c1b
 * @struct nl_object
Packit 6d2c1b
 * @brief netlink container
Packit 6d2c1b
 */
Packit 6d2c1b
struct nl_object {
Packit 6d2c1b
	int fd;            /**< the netlink socket file descriptor used for communication */
Packit 6d2c1b
	int seq;           /**< sequence number of send operation */
Packit 6d2c1b
	char buf[81920];   /**< buffer for receive data */
Packit 6d2c1b
};
Packit 6d2c1b
Packit 6d2c1b
nl_t nl_create(void)
Packit 6d2c1b
{
Packit 6d2c1b
	nl_t nt = NULL;
Packit 6d2c1b
	int fd = -1;
Packit 6d2c1b
Packit 6d2c1b
	nt = (struct nl_object *)malloc(sizeof(*nt));
Packit 6d2c1b
	if (nt) {
Packit 6d2c1b
		int sndbuf_size = 32768;
Packit 6d2c1b
		int rcvbuf_size = 32768;
Packit 6d2c1b
		struct sockaddr_nl local;
Packit 6d2c1b
Packit 6d2c1b
		fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
Packit 6d2c1b
		if (fd < 0) {
Packit 6d2c1b
			log_error("Unable to create a netlink socket\n");
Packit 6d2c1b
			goto err;
Packit 6d2c1b
		}
Packit 6d2c1b
		if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, sizeof(int))) {
Packit 6d2c1b
			log_error("Unable to set SO_SNDBUF\n");
Packit 6d2c1b
			goto err;
Packit 6d2c1b
		}
Packit 6d2c1b
		if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf_size, sizeof(int))) {
Packit 6d2c1b
			log_error("Unable to set SO_RCVBUF\n");
Packit 6d2c1b
			goto err;
Packit 6d2c1b
		}
Packit 6d2c1b
		memset(&local, 0, sizeof(local));
Packit 6d2c1b
		local.nl_family = AF_NETLINK;
Packit 6d2c1b
		local.nl_groups = 0;
Packit 6d2c1b
		if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) {
Packit 6d2c1b
			log_error("Unable to bind to the netlink socket\n");
Packit 6d2c1b
			goto err;
Packit 6d2c1b
		}
Packit 6d2c1b
Packit 6d2c1b
		memset(nt, 0, sizeof(*nt));
Packit 6d2c1b
		nt->fd = fd;
Packit 6d2c1b
		nt->seq = 0;
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	return nt;
Packit 6d2c1b
err:
Packit 6d2c1b
	if (fd >= 0) {
Packit 6d2c1b
		close(fd);
Packit 6d2c1b
	}
Packit 6d2c1b
	if (nt) {
Packit 6d2c1b
		free(nt);
Packit 6d2c1b
	}
Packit 6d2c1b
	nt = NULL;
Packit 6d2c1b
Packit 6d2c1b
	return NULL;
Packit 6d2c1b
}
Packit 6d2c1b
Packit 6d2c1b
void nl_destroy(nl_t nt)
Packit 6d2c1b
{
Packit 6d2c1b
	if (nt) {
Packit 6d2c1b
		close(nt->fd);
Packit 6d2c1b
		free(nt);
Packit 6d2c1b
		nt = NULL;
Packit 6d2c1b
	}
Packit 6d2c1b
}
Packit 6d2c1b
Packit 6d2c1b
int nl_send(nl_t nt, struct nlmsghdr *nl_msg)
Packit 6d2c1b
{
Packit 6d2c1b
	struct sockaddr_nl nladdr;
Packit 6d2c1b
	struct iovec iov;
Packit 6d2c1b
	struct msghdr msg;
Packit 6d2c1b
	int ret = -1;
Packit 6d2c1b
Packit 6d2c1b
	nl_msg->nlmsg_seq = nt->seq++;
Packit 6d2c1b
Packit 6d2c1b
	memset(&nladdr, 0, sizeof(nladdr));
Packit 6d2c1b
	nladdr.nl_family = AF_NETLINK;
Packit 6d2c1b
	nladdr.nl_pid = 0;
Packit 6d2c1b
	nladdr.nl_groups = 0;
Packit 6d2c1b
Packit 6d2c1b
	iov.iov_base = nl_msg;
Packit 6d2c1b
	iov.iov_len = nl_msg->nlmsg_len;
Packit 6d2c1b
Packit 6d2c1b
	memset(&msg, 0, sizeof(msg));
Packit 6d2c1b
	msg.msg_name = &nladdr;
Packit 6d2c1b
	msg.msg_namelen = sizeof(nladdr);
Packit 6d2c1b
	msg.msg_iov = &iov;
Packit 6d2c1b
	msg.msg_iovlen = 1;
Packit 6d2c1b
Packit 6d2c1b
	log_hexdump((void *)nl_msg, nl_msg->nlmsg_len);
Packit 6d2c1b
	ret = sendmsg(nt->fd, &msg, 0);
Packit 6d2c1b
	if (ret < 0) {
Packit 6d2c1b
		log_error("Failed to send netlink message: %s (%d)\n",
Packit 6d2c1b
			strerror(errno), errno);
Packit 6d2c1b
		return ret;
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	return ret;
Packit 6d2c1b
}
Packit 6d2c1b
Packit 6d2c1b
int nl_recv(nl_t nt, int (*cb)(struct nlmsghdr *, void *arg), void *arg)
Packit 6d2c1b
{
Packit 6d2c1b
	struct sockaddr_nl nladdr;
Packit 6d2c1b
	struct iovec iov;
Packit 6d2c1b
	struct msghdr msg;
Packit 6d2c1b
	int ret = 0;
Packit 6d2c1b
	int multipart = 0;
Packit 6d2c1b
Packit 6d2c1b
	memset(&nladdr, 0, sizeof(nladdr));
Packit 6d2c1b
Packit 6d2c1b
	iov.iov_base = nt->buf;
Packit 6d2c1b
	iov.iov_len = sizeof(nt->buf);
Packit 6d2c1b
Packit 6d2c1b
	memset(&msg, 0, sizeof(msg));
Packit 6d2c1b
	msg.msg_name = &nladdr;
Packit 6d2c1b
	msg.msg_namelen = sizeof(nladdr);
Packit 6d2c1b
	msg.msg_iov = &iov;
Packit 6d2c1b
	msg.msg_iovlen = 1;
Packit 6d2c1b
Packit 6d2c1b
	do {
Packit 6d2c1b
		struct nlmsghdr *nl_msg;
Packit 6d2c1b
		int recv_bytes = 0;
Packit 6d2c1b
Packit 6d2c1b
		recv_bytes = recvmsg(nt->fd, &msg, 0);
Packit 6d2c1b
		if (recv_bytes <= 0) {
Packit 6d2c1b
			if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
Packit 6d2c1b
				continue;
Packit 6d2c1b
			}
Packit 6d2c1b
			return -1;
Packit 6d2c1b
		}
Packit 6d2c1b
Packit 6d2c1b
		for (nl_msg = (struct nlmsghdr *)nt->buf;
Packit 6d2c1b
		     NLMSG_OK(nl_msg, (unsigned int)recv_bytes);
Packit 6d2c1b
		     nl_msg = NLMSG_NEXT(nl_msg, recv_bytes)) {
Packit 6d2c1b
			if (nl_msg->nlmsg_type == NLMSG_ERROR) {
Packit 6d2c1b
				struct nlmsgerr *err_data = NLMSG_DATA(nl_msg);
Packit 6d2c1b
Packit 6d2c1b
				if (err_data->error < 0) {
Packit 6d2c1b
					errno = -err_data->error;
Packit 6d2c1b
					return -1;
Packit 6d2c1b
				}
Packit 6d2c1b
				/* Ack message. */
Packit 6d2c1b
				return 0;
Packit 6d2c1b
			}
Packit 6d2c1b
			/* Multi-part msgs and their trailing DONE message. */
Packit 6d2c1b
			if (nl_msg->nlmsg_flags & NLM_F_MULTI) {
Packit 6d2c1b
				if (nl_msg->nlmsg_type == NLMSG_DONE) {
Packit 6d2c1b
					return 0;
Packit 6d2c1b
				}
Packit 6d2c1b
				multipart = 1;
Packit 6d2c1b
			}
Packit 6d2c1b
			if (cb) {
Packit 6d2c1b
				ret = cb(nl_msg, arg);
Packit 6d2c1b
			}
Packit 6d2c1b
		}
Packit 6d2c1b
	} while (multipart || (msg.msg_flags & MSG_TRUNC));
Packit 6d2c1b
Packit 6d2c1b
	return ret;
Packit 6d2c1b
}
Packit 6d2c1b
Packit 6d2c1b
void nl_attr_add(struct nlmsghdr *nl_msg, unsigned short type,
Packit 6d2c1b
		const void *data, unsigned int data_len)
Packit 6d2c1b
{
Packit 6d2c1b
	struct rtattr *rta;
Packit 6d2c1b
Packit 6d2c1b
	if ((NLMSG_ALIGN(nl_msg->nlmsg_len) + RTA_ALIGN(RTA_LENGTH(data_len))) > NLMSG_BUF) {
Packit 6d2c1b
		log_error("Message size is: %d that exceeds limit: %d\n",
Packit 6d2c1b
				(NLMSG_ALIGN(nl_msg->nlmsg_len) + RTA_ALIGN(RTA_LENGTH(data_len))), NLMSG_BUF);
Packit 6d2c1b
		return ;
Packit 6d2c1b
	}
Packit 6d2c1b
	rta = (struct rtattr *)NLMSG_TAIL(nl_msg);
Packit 6d2c1b
	rta->rta_len = RTA_LENGTH(data_len);
Packit 6d2c1b
	rta->rta_type = type;
Packit 6d2c1b
	if (data && data_len) {
Packit 6d2c1b
		memcpy(RTA_DATA(rta), data, data_len);
Packit 6d2c1b
	}
Packit 6d2c1b
	nl_msg->nlmsg_len = NLMSG_ALIGN(nl_msg->nlmsg_len) + RTA_ALIGN(rta->rta_len);
Packit 6d2c1b
}
Packit 6d2c1b
Packit 6d2c1b
struct rtattr *nl_attr_nest_start(struct nlmsghdr *nl_msg, int type)
Packit 6d2c1b
{
Packit 6d2c1b
	struct rtattr *nest = NLMSG_TAIL(nl_msg);
Packit 6d2c1b
Packit 6d2c1b
	nl_attr_add(nl_msg, type, NULL, 0);
Packit 6d2c1b
Packit 6d2c1b
	return nest;
Packit 6d2c1b
}
Packit 6d2c1b
Packit 6d2c1b
int nl_attr_nest_end(struct nlmsghdr *nl_msg, struct rtattr *nest)
Packit 6d2c1b
{
Packit 6d2c1b
	nest->rta_len = (uintptr_t)NLMSG_TAIL(nl_msg) - (uintptr_t)nest;
Packit 6d2c1b
Packit 6d2c1b
	return nest->rta_len;
Packit 6d2c1b
}