|
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 |
}
|