numam-dpdk/drivers/net/tap/tap_tcmsgs.c
Pascal Mazon 6fc6de7e0e net/tap: update netlink error code management
Some errors received from the kernel are acceptable, such as a -ENOENT
for a rule deletion (the rule was already no longer existing in the
kernel). Make sure we consider return codes properly. For that,
nl_recv() has been simplified.

qdisc_exists() function is no longer needed as we can check whether the
kernel returned -EEXIST when requiring the qdisc creation. It's simpler
and faster.

Add a few messages for clarity when a netlink error occurs.

Signed-off-by: Pascal Mazon <pascal.mazon@6wind.com>
2017-04-04 19:03:03 +02:00

324 lines
8.3 KiB
C

/*-
* BSD LICENSE
*
* Copyright 2017 6WIND S.A.
* Copyright 2017 Mellanox.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of 6WIND S.A. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <inttypes.h>
#include <linux/netlink.h>
#include <net/if.h>
#include <string.h>
#include <rte_log.h>
#include <tap_tcmsgs.h>
struct qdisc {
uint32_t handle;
uint32_t parent;
};
struct list_args {
int nlsk_fd;
uint16_t 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, uint16_t 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, uint16_t 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 = nl_init(0);
if (fd < 0) {
RTE_LOG(ERR, PMD,
"Could not delete QDISC: null netlink socket\n");
return -1;
}
} else {
fd = nlsk_fd;
}
if (nl_send(fd, &msg.nh) < 0)
goto error;
if (nl_recv_ack(fd) < 0)
goto error;
if (!nlsk_fd)
return nl_final(fd);
return 0;
error:
if (!nlsk_fd)
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, uint16_t ifindex)
{
struct tc_multiq_qopt opt;
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;
nlattr_add(&msg.nh, TCA_KIND, sizeof("multiq"), "multiq");
nlattr_add(&msg.nh, TCA_OPTIONS, sizeof(opt), &opt);
if (nl_send(nlsk_fd, &msg.nh) < 0)
return -1;
if (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, uint16_t 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;
nlattr_add(&msg.nh, TCA_KIND, sizeof("ingress"), "ingress");
if (nl_send(nlsk_fd, &msg.nh) < 0)
return -1;
if (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, uint16_t 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 (nl_send(nlsk_fd, &msg.nh) < 0)
return -1;
if (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, uint16_t 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, uint16_t ifindex)
{
int err = 0;
err = qdisc_add_multiq(nlsk_fd, ifindex);
if (err < 0 && errno != -EEXIST) {
RTE_LOG(ERR, PMD, "Could not add multiq qdisc (%d): %s\n",
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, uint16_t ifindex)
{
int err = 0;
err = qdisc_add_ingress(nlsk_fd, ifindex);
if (err < 0 && errno != -EEXIST) {
RTE_LOG(ERR, PMD, "Could not add ingress qdisc (%d): %s\n",
errno, strerror(errno));
return -1;
}
return 0;
}