8772bbfa71
On Linux and most other systems, network interface index is a 32-bit integer. Indexes overflowing the 16-bit integer are frequently seen when used inside a Docker container. Fixes:7c25284e30
("net/tap: add netlink back-end for flow API") Fixes:2bc06869cd
("net/tap: add remote netdevice traffic capture") Cc: stable@dpdk.org Signed-off-by: Alex Kiselev <alex@bisonrouter.com> Acked-by: Stephen Hemminger <stephen@networkplumber.org>
297 lines
6.9 KiB
C
297 lines
6.9 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright 2017 6WIND S.A.
|
|
* Copyright 2017 Mellanox Technologies, Ltd
|
|
*/
|
|
|
|
#include <inttypes.h>
|
|
#include <linux/netlink.h>
|
|
#include <net/if.h>
|
|
#include <string.h>
|
|
|
|
#include <rte_log.h>
|
|
#include <tap_tcmsgs.h>
|
|
#include "tap_log.h"
|
|
|
|
struct qdisc {
|
|
uint32_t handle;
|
|
uint32_t parent;
|
|
};
|
|
|
|
struct list_args {
|
|
int nlsk_fd;
|
|
unsigned int ifindex;
|
|
void *custom_arg;
|
|
};
|
|
|
|
struct qdisc_custom_arg {
|
|
uint32_t handle;
|
|
uint32_t parent;
|
|
uint8_t exists;
|
|
};
|
|
|
|
/**
|
|
* Initialize a netlink message with a TC header.
|
|
*
|
|
* @param[in, out] msg
|
|
* The netlink message to fill.
|
|
* @param[in] ifindex
|
|
* The netdevice ifindex where the rule will be applied.
|
|
* @param[in] type
|
|
* The type of TC message to create (RTM_NEWTFILTER, RTM_NEWQDISC, etc.).
|
|
* @param[in] flags
|
|
* Overrides the default netlink flags for this msg with those specified.
|
|
*/
|
|
void
|
|
tc_init_msg(struct nlmsg *msg, unsigned int ifindex, uint16_t type, uint16_t flags)
|
|
{
|
|
struct nlmsghdr *n = &msg->nh;
|
|
|
|
n->nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
|
|
n->nlmsg_type = type;
|
|
if (flags)
|
|
n->nlmsg_flags = flags;
|
|
else
|
|
n->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
|
|
msg->t.tcm_family = AF_UNSPEC;
|
|
msg->t.tcm_ifindex = ifindex;
|
|
}
|
|
|
|
/**
|
|
* Delete a specific QDISC identified by its iface, and it's handle and parent.
|
|
*
|
|
* @param[in] nlsk_fd
|
|
* The netlink socket file descriptor used for communication.
|
|
* @param[in] ifindex
|
|
* The netdevice ifindex on whom the deletion will happen.
|
|
* @param[in] qinfo
|
|
* Additional info to identify the QDISC (handle and parent).
|
|
*
|
|
* @return
|
|
* 0 on success, -1 otherwise with errno set.
|
|
*/
|
|
static int
|
|
qdisc_del(int nlsk_fd, unsigned int ifindex, struct qdisc *qinfo)
|
|
{
|
|
struct nlmsg msg;
|
|
int fd = 0;
|
|
|
|
tc_init_msg(&msg, ifindex, RTM_DELQDISC, 0);
|
|
msg.t.tcm_handle = qinfo->handle;
|
|
msg.t.tcm_parent = qinfo->parent;
|
|
/* if no netlink socket is provided, create one */
|
|
if (!nlsk_fd) {
|
|
fd = tap_nl_init(0);
|
|
if (fd < 0) {
|
|
TAP_LOG(ERR,
|
|
"Could not delete QDISC: null netlink socket");
|
|
return -1;
|
|
}
|
|
} else {
|
|
fd = nlsk_fd;
|
|
}
|
|
if (tap_nl_send(fd, &msg.nh) < 0)
|
|
goto error;
|
|
if (tap_nl_recv_ack(fd) < 0)
|
|
goto error;
|
|
if (!nlsk_fd)
|
|
return tap_nl_final(fd);
|
|
return 0;
|
|
error:
|
|
if (!nlsk_fd)
|
|
tap_nl_final(fd);
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Add the multiqueue QDISC with MULTIQ_MAJOR_HANDLE handle.
|
|
*
|
|
* @param[in] nlsk_fd
|
|
* The netlink socket file descriptor used for communication.
|
|
* @param[in] ifindex
|
|
* The netdevice ifindex where to add the multiqueue QDISC.
|
|
*
|
|
* @return
|
|
* 0 on success, -1 otherwise with errno set.
|
|
*/
|
|
int
|
|
qdisc_add_multiq(int nlsk_fd, unsigned int ifindex)
|
|
{
|
|
struct tc_multiq_qopt opt = {0};
|
|
struct nlmsg msg;
|
|
|
|
tc_init_msg(&msg, ifindex, RTM_NEWQDISC,
|
|
NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
|
|
msg.t.tcm_handle = TC_H_MAKE(MULTIQ_MAJOR_HANDLE, 0);
|
|
msg.t.tcm_parent = TC_H_ROOT;
|
|
tap_nlattr_add(&msg.nh, TCA_KIND, sizeof("multiq"), "multiq");
|
|
tap_nlattr_add(&msg.nh, TCA_OPTIONS, sizeof(opt), &opt);
|
|
if (tap_nl_send(nlsk_fd, &msg.nh) < 0)
|
|
return -1;
|
|
if (tap_nl_recv_ack(nlsk_fd) < 0)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Add the ingress QDISC with default ffff: handle.
|
|
*
|
|
* @param[in] nlsk_fd
|
|
* The netlink socket file descriptor used for communication.
|
|
* @param[in] ifindex
|
|
* The netdevice ifindex where the QDISC will be added.
|
|
*
|
|
* @return
|
|
* 0 on success, -1 otherwise with errno set.
|
|
*/
|
|
int
|
|
qdisc_add_ingress(int nlsk_fd, unsigned int ifindex)
|
|
{
|
|
struct nlmsg msg;
|
|
|
|
tc_init_msg(&msg, ifindex, RTM_NEWQDISC,
|
|
NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
|
|
msg.t.tcm_handle = TC_H_MAKE(TC_H_INGRESS, 0);
|
|
msg.t.tcm_parent = TC_H_INGRESS;
|
|
tap_nlattr_add(&msg.nh, TCA_KIND, sizeof("ingress"), "ingress");
|
|
if (tap_nl_send(nlsk_fd, &msg.nh) < 0)
|
|
return -1;
|
|
if (tap_nl_recv_ack(nlsk_fd) < 0)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Callback function to delete a QDISC.
|
|
*
|
|
* @param[in] nh
|
|
* The netlink message to parse, received from the kernel.
|
|
* @param[in] arg
|
|
* Custom arguments for the callback.
|
|
*
|
|
* @return
|
|
* 0 on success, -1 otherwise with errno set.
|
|
*/
|
|
static int
|
|
qdisc_del_cb(struct nlmsghdr *nh, void *arg)
|
|
{
|
|
struct tcmsg *t = NLMSG_DATA(nh);
|
|
struct list_args *args = arg;
|
|
|
|
struct qdisc qinfo = {
|
|
.handle = t->tcm_handle,
|
|
.parent = t->tcm_parent,
|
|
};
|
|
|
|
/* filter out other ifaces' qdiscs */
|
|
if (args->ifindex != (unsigned int)t->tcm_ifindex)
|
|
return 0;
|
|
/*
|
|
* Use another nlsk_fd (0) to avoid tampering with the current list
|
|
* iteration.
|
|
*/
|
|
return qdisc_del(0, args->ifindex, &qinfo);
|
|
}
|
|
|
|
/**
|
|
* Iterate over all QDISC, and call the callback() function for each.
|
|
*
|
|
* @param[in] nlsk_fd
|
|
* The netlink socket file descriptor used for communication.
|
|
* @param[in] ifindex
|
|
* The netdevice ifindex where to find QDISCs.
|
|
* @param[in] callback
|
|
* The function to call for each QDISC.
|
|
* @param[in, out] arg
|
|
* The arguments to provide the callback function with.
|
|
*
|
|
* @return
|
|
* 0 on success, -1 otherwise with errno set.
|
|
*/
|
|
static int
|
|
qdisc_iterate(int nlsk_fd, unsigned int ifindex,
|
|
int (*callback)(struct nlmsghdr *, void *), void *arg)
|
|
{
|
|
struct nlmsg msg;
|
|
struct list_args args = {
|
|
.nlsk_fd = nlsk_fd,
|
|
.ifindex = ifindex,
|
|
.custom_arg = arg,
|
|
};
|
|
|
|
tc_init_msg(&msg, ifindex, RTM_GETQDISC, NLM_F_REQUEST | NLM_F_DUMP);
|
|
if (tap_nl_send(nlsk_fd, &msg.nh) < 0)
|
|
return -1;
|
|
if (tap_nl_recv(nlsk_fd, callback, &args) < 0)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Delete all QDISCs for a given netdevice.
|
|
*
|
|
* @param[in] nlsk_fd
|
|
* The netlink socket file descriptor used for communication.
|
|
* @param[in] ifindex
|
|
* The netdevice ifindex where to find QDISCs.
|
|
*
|
|
* @return
|
|
* 0 on success, -1 otherwise with errno set.
|
|
*/
|
|
int
|
|
qdisc_flush(int nlsk_fd, unsigned int ifindex)
|
|
{
|
|
return qdisc_iterate(nlsk_fd, ifindex, qdisc_del_cb, NULL);
|
|
}
|
|
|
|
/**
|
|
* Create the multiqueue QDISC, only if it does not exist already.
|
|
*
|
|
* @param[in] nlsk_fd
|
|
* The netlink socket file descriptor used for communication.
|
|
* @param[in] ifindex
|
|
* The netdevice ifindex where to add the multiqueue QDISC.
|
|
*
|
|
* @return
|
|
* 0 if the qdisc exists or if has been successfully added.
|
|
* Return -1 otherwise.
|
|
*/
|
|
int
|
|
qdisc_create_multiq(int nlsk_fd, unsigned int ifindex)
|
|
{
|
|
int err = 0;
|
|
|
|
err = qdisc_add_multiq(nlsk_fd, ifindex);
|
|
if (err < 0 && errno != -EEXIST) {
|
|
TAP_LOG(ERR, "Could not add multiq qdisc (%d): %s",
|
|
errno, strerror(errno));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Create the ingress QDISC, only if it does not exist already.
|
|
*
|
|
* @param[in] nlsk_fd
|
|
* The netlink socket file descriptor used for communication.
|
|
* @param[in] ifindex
|
|
* The netdevice ifindex where to add the ingress QDISC.
|
|
*
|
|
* @return
|
|
* 0 if the qdisc exists or if has been successfully added.
|
|
* Return -1 otherwise.
|
|
*/
|
|
int
|
|
qdisc_create_ingress(int nlsk_fd, unsigned int ifindex)
|
|
{
|
|
int err = 0;
|
|
|
|
err = qdisc_add_ingress(nlsk_fd, ifindex);
|
|
if (err < 0 && errno != -EEXIST) {
|
|
TAP_LOG(ERR, "Could not add ingress qdisc (%d): %s",
|
|
errno, strerror(errno));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|