dpdk/drivers/net/tap/tap_tcmsgs.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: BSD-3-Clause
   2 * Copyright 2017 6WIND S.A.
   3 * Copyright 2017 Mellanox Technologies, Ltd
   4 */
   5
   6#include <inttypes.h>
   7#include <linux/netlink.h>
   8#include <net/if.h>
   9#include <string.h>
  10
  11#include <rte_log.h>
  12#include <tap_tcmsgs.h>
  13#include "tap_log.h"
  14
  15struct qdisc {
  16        uint32_t handle;
  17        uint32_t parent;
  18};
  19
  20struct list_args {
  21        int nlsk_fd;
  22        uint16_t ifindex;
  23        void *custom_arg;
  24};
  25
  26struct qdisc_custom_arg {
  27        uint32_t handle;
  28        uint32_t parent;
  29        uint8_t exists;
  30};
  31
  32/**
  33 * Initialize a netlink message with a TC header.
  34 *
  35 * @param[in, out] msg
  36 *   The netlink message to fill.
  37 * @param[in] ifindex
  38 *   The netdevice ifindex where the rule will be applied.
  39 * @param[in] type
  40 *   The type of TC message to create (RTM_NEWTFILTER, RTM_NEWQDISC, etc.).
  41 * @param[in] flags
  42 *   Overrides the default netlink flags for this msg with those specified.
  43 */
  44void
  45tc_init_msg(struct nlmsg *msg, uint16_t ifindex, uint16_t type, uint16_t flags)
  46{
  47        struct nlmsghdr *n = &msg->nh;
  48
  49        n->nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
  50        n->nlmsg_type = type;
  51        if (flags)
  52                n->nlmsg_flags = flags;
  53        else
  54                n->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
  55        msg->t.tcm_family = AF_UNSPEC;
  56        msg->t.tcm_ifindex = ifindex;
  57}
  58
  59/**
  60 * Delete a specific QDISC identified by its iface, and it's handle and parent.
  61 *
  62 * @param[in] nlsk_fd
  63 *   The netlink socket file descriptor used for communication.
  64 * @param[in] ifindex
  65 *   The netdevice ifindex on whom the deletion will happen.
  66 * @param[in] qinfo
  67 *   Additional info to identify the QDISC (handle and parent).
  68 *
  69 * @return
  70 *   0 on success, -1 otherwise with errno set.
  71 */
  72static int
  73qdisc_del(int nlsk_fd, uint16_t ifindex, struct qdisc *qinfo)
  74{
  75        struct nlmsg msg;
  76        int fd = 0;
  77
  78        tc_init_msg(&msg, ifindex, RTM_DELQDISC, 0);
  79        msg.t.tcm_handle = qinfo->handle;
  80        msg.t.tcm_parent = qinfo->parent;
  81        /* if no netlink socket is provided, create one */
  82        if (!nlsk_fd) {
  83                fd = tap_nl_init(0);
  84                if (fd < 0) {
  85                        TAP_LOG(ERR,
  86                                "Could not delete QDISC: null netlink socket");
  87                        return -1;
  88                }
  89        } else {
  90                fd = nlsk_fd;
  91        }
  92        if (tap_nl_send(fd, &msg.nh) < 0)
  93                goto error;
  94        if (tap_nl_recv_ack(fd) < 0)
  95                goto error;
  96        if (!nlsk_fd)
  97                return tap_nl_final(fd);
  98        return 0;
  99error:
 100        if (!nlsk_fd)
 101                tap_nl_final(fd);
 102        return -1;
 103}
 104
 105/**
 106 * Add the multiqueue QDISC with MULTIQ_MAJOR_HANDLE handle.
 107 *
 108 * @param[in] nlsk_fd
 109 *   The netlink socket file descriptor used for communication.
 110 * @param[in] ifindex
 111 *   The netdevice ifindex where to add the multiqueue QDISC.
 112 *
 113 * @return
 114 *   0 on success, -1 otherwise with errno set.
 115 */
 116int
 117qdisc_add_multiq(int nlsk_fd, uint16_t ifindex)
 118{
 119        struct tc_multiq_qopt opt = {0};
 120        struct nlmsg msg;
 121
 122        tc_init_msg(&msg, ifindex, RTM_NEWQDISC,
 123                    NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
 124        msg.t.tcm_handle = TC_H_MAKE(MULTIQ_MAJOR_HANDLE, 0);
 125        msg.t.tcm_parent = TC_H_ROOT;
 126        tap_nlattr_add(&msg.nh, TCA_KIND, sizeof("multiq"), "multiq");
 127        tap_nlattr_add(&msg.nh, TCA_OPTIONS, sizeof(opt), &opt);
 128        if (tap_nl_send(nlsk_fd, &msg.nh) < 0)
 129                return -1;
 130        if (tap_nl_recv_ack(nlsk_fd) < 0)
 131                return -1;
 132        return 0;
 133}
 134
 135/**
 136 * Add the ingress QDISC with default ffff: handle.
 137 *
 138 * @param[in] nlsk_fd
 139 *   The netlink socket file descriptor used for communication.
 140 * @param[in] ifindex
 141 *   The netdevice ifindex where the QDISC will be added.
 142 *
 143 * @return
 144 *   0 on success, -1 otherwise with errno set.
 145 */
 146int
 147qdisc_add_ingress(int nlsk_fd, uint16_t ifindex)
 148{
 149        struct nlmsg msg;
 150
 151        tc_init_msg(&msg, ifindex, RTM_NEWQDISC,
 152                    NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
 153        msg.t.tcm_handle = TC_H_MAKE(TC_H_INGRESS, 0);
 154        msg.t.tcm_parent = TC_H_INGRESS;
 155        tap_nlattr_add(&msg.nh, TCA_KIND, sizeof("ingress"), "ingress");
 156        if (tap_nl_send(nlsk_fd, &msg.nh) < 0)
 157                return -1;
 158        if (tap_nl_recv_ack(nlsk_fd) < 0)
 159                return -1;
 160        return 0;
 161}
 162
 163/**
 164 * Callback function to delete a QDISC.
 165 *
 166 * @param[in] nh
 167 *   The netlink message to parse, received from the kernel.
 168 * @param[in] arg
 169 *   Custom arguments for the callback.
 170 *
 171 * @return
 172 *   0 on success, -1 otherwise with errno set.
 173 */
 174static int
 175qdisc_del_cb(struct nlmsghdr *nh, void *arg)
 176{
 177        struct tcmsg *t = NLMSG_DATA(nh);
 178        struct list_args *args = arg;
 179
 180        struct qdisc qinfo = {
 181                .handle = t->tcm_handle,
 182                .parent = t->tcm_parent,
 183        };
 184
 185        /* filter out other ifaces' qdiscs */
 186        if (args->ifindex != (unsigned int)t->tcm_ifindex)
 187                return 0;
 188        /*
 189         * Use another nlsk_fd (0) to avoid tampering with the current list
 190         * iteration.
 191         */
 192        return qdisc_del(0, args->ifindex, &qinfo);
 193}
 194
 195/**
 196 * Iterate over all QDISC, and call the callback() function for each.
 197 *
 198 * @param[in] nlsk_fd
 199 *   The netlink socket file descriptor used for communication.
 200 * @param[in] ifindex
 201 *   The netdevice ifindex where to find QDISCs.
 202 * @param[in] callback
 203 *   The function to call for each QDISC.
 204 * @param[in, out] arg
 205 *   The arguments to provide the callback function with.
 206 *
 207 * @return
 208 *   0 on success, -1 otherwise with errno set.
 209 */
 210static int
 211qdisc_iterate(int nlsk_fd, uint16_t ifindex,
 212              int (*callback)(struct nlmsghdr *, void *), void *arg)
 213{
 214        struct nlmsg msg;
 215        struct list_args args = {
 216                .nlsk_fd = nlsk_fd,
 217                .ifindex = ifindex,
 218                .custom_arg = arg,
 219        };
 220
 221        tc_init_msg(&msg, ifindex, RTM_GETQDISC, NLM_F_REQUEST | NLM_F_DUMP);
 222        if (tap_nl_send(nlsk_fd, &msg.nh) < 0)
 223                return -1;
 224        if (tap_nl_recv(nlsk_fd, callback, &args) < 0)
 225                return -1;
 226        return 0;
 227}
 228
 229/**
 230 * Delete all QDISCs for a given netdevice.
 231 *
 232 * @param[in] nlsk_fd
 233 *   The netlink socket file descriptor used for communication.
 234 * @param[in] ifindex
 235 *   The netdevice ifindex where to find QDISCs.
 236 *
 237 * @return
 238 *   0 on success, -1 otherwise with errno set.
 239 */
 240int
 241qdisc_flush(int nlsk_fd, uint16_t ifindex)
 242{
 243        return qdisc_iterate(nlsk_fd, ifindex, qdisc_del_cb, NULL);
 244}
 245
 246/**
 247 * Create the multiqueue QDISC, only if it does not exist already.
 248 *
 249 * @param[in] nlsk_fd
 250 *   The netlink socket file descriptor used for communication.
 251 * @param[in] ifindex
 252 *   The netdevice ifindex where to add the multiqueue QDISC.
 253 *
 254 * @return
 255 *   0 if the qdisc exists or if has been successfully added.
 256 *   Return -1 otherwise.
 257 */
 258int
 259qdisc_create_multiq(int nlsk_fd, uint16_t ifindex)
 260{
 261        int err = 0;
 262
 263        err = qdisc_add_multiq(nlsk_fd, ifindex);
 264        if (err < 0 && errno != -EEXIST) {
 265                TAP_LOG(ERR, "Could not add multiq qdisc (%d): %s",
 266                        errno, strerror(errno));
 267                return -1;
 268        }
 269        return 0;
 270}
 271
 272/**
 273 * Create the ingress QDISC, only if it does not exist already.
 274 *
 275 * @param[in] nlsk_fd
 276 *   The netlink socket file descriptor used for communication.
 277 * @param[in] ifindex
 278 *   The netdevice ifindex where to add the ingress QDISC.
 279 *
 280 * @return
 281 *   0 if the qdisc exists or if has been successfully added.
 282 *   Return -1 otherwise.
 283 */
 284int
 285qdisc_create_ingress(int nlsk_fd, uint16_t ifindex)
 286{
 287        int err = 0;
 288
 289        err = qdisc_add_ingress(nlsk_fd, ifindex);
 290        if (err < 0 && errno != -EEXIST) {
 291                TAP_LOG(ERR, "Could not add ingress qdisc (%d): %s",
 292                        errno, strerror(errno));
 293                return -1;
 294        }
 295        return 0;
 296}
 297