4 * Copyright 2017 6WIND S.A.
5 * Copyright 2017 Mellanox.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * * Neither the name of 6WIND S.A. nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 #include <linux/netlink.h>
40 #include <tap_tcmsgs.h>
53 struct qdisc_custom_arg {
60 * Initialize a netlink message with a TC header.
63 * The netlink message to fill.
65 * The netdevice ifindex where the rule will be applied.
67 * The type of TC message to create (RTM_NEWTFILTER, RTM_NEWQDISC, etc.).
69 * Overrides the default netlink flags for this msg with those specified.
72 tc_init_msg(struct nlmsg *msg, uint16_t ifindex, uint16_t type, uint16_t flags)
74 struct nlmsghdr *n = &msg->nh;
76 n->nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
79 n->nlmsg_flags = flags;
81 n->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
82 msg->t.tcm_family = AF_UNSPEC;
83 msg->t.tcm_ifindex = ifindex;
87 * Delete a specific QDISC identified by its iface, and it's handle and parent.
90 * The netlink socket file descriptor used for communication.
92 * The netdevice ifindex on whom the deletion will happen.
94 * Additional info to identify the QDISC (handle and parent).
97 * 0 on success, -1 otherwise.
100 qdisc_del(int nlsk_fd, uint16_t ifindex, struct qdisc *qinfo)
105 tc_init_msg(&msg, ifindex, RTM_DELQDISC, 0);
106 msg.t.tcm_handle = qinfo->handle;
107 msg.t.tcm_parent = qinfo->parent;
108 /* if no netlink socket is provided, create one */
113 "Could not delete QDISC: null netlink socket\n");
119 if (nl_send(fd, &msg.nh) < 0)
121 if (nl_recv_ack(fd) < 0)
129 * Add the multiqueue QDISC with MULTIQ_MAJOR_HANDLE handle.
132 * The netlink socket file descriptor used for communication.
134 * The netdevice ifindex where to add the multiqueue QDISC.
137 * -1 if the qdisc cannot be added, and 0 otherwise.
140 qdisc_add_multiq(int nlsk_fd, uint16_t ifindex)
142 struct tc_multiq_qopt opt;
145 tc_init_msg(&msg, ifindex, RTM_NEWQDISC,
146 NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
147 msg.t.tcm_handle = TC_H_MAKE(MULTIQ_MAJOR_HANDLE, 0);
148 msg.t.tcm_parent = TC_H_ROOT;
149 nlattr_add(&msg.nh, TCA_KIND, sizeof("multiq"), "multiq");
150 nlattr_add(&msg.nh, TCA_OPTIONS, sizeof(opt), &opt);
151 if (nl_send(nlsk_fd, &msg.nh) < 0)
153 if (nl_recv_ack(nlsk_fd) < 0)
159 * Add the ingress QDISC with default ffff: handle.
162 * The netlink socket file descriptor used for communication.
164 * The netdevice ifindex where the QDISC will be added.
167 * -1 if the qdisc cannot be added, and 0 otherwise.
170 qdisc_add_ingress(int nlsk_fd, uint16_t ifindex)
174 tc_init_msg(&msg, ifindex, RTM_NEWQDISC,
175 NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
176 msg.t.tcm_handle = TC_H_MAKE(TC_H_INGRESS, 0);
177 msg.t.tcm_parent = TC_H_INGRESS;
178 nlattr_add(&msg.nh, TCA_KIND, sizeof("ingress"), "ingress");
179 if (nl_send(nlsk_fd, &msg.nh) < 0)
181 if (nl_recv_ack(nlsk_fd) < 0)
187 * Callback function to check for QDISC existence.
188 * If the QDISC is found to exist, increment "exists" in the custom arg.
191 * The netlink message to parse, received from the kernel.
192 * @param[in, out] arg
193 * Custom arguments for the callback.
199 qdisc_exist_cb(struct nlmsghdr *nh, void *arg)
201 struct list_args *args = (struct list_args *)arg;
202 struct qdisc_custom_arg *custom = args->custom_arg;
203 struct tcmsg *t = NLMSG_DATA(nh);
205 /* filter by request iface */
206 if (args->ifindex != (unsigned int)t->tcm_ifindex)
208 if (t->tcm_handle != custom->handle || t->tcm_parent != custom->parent)
215 * Callback function to delete a QDISC.
218 * The netlink message to parse, received from the kernel.
220 * Custom arguments for the callback.
226 qdisc_del_cb(struct nlmsghdr *nh, void *arg)
228 struct tcmsg *t = NLMSG_DATA(nh);
229 struct list_args *args = arg;
231 struct qdisc qinfo = {
232 .handle = t->tcm_handle,
233 .parent = t->tcm_parent,
236 /* filter out other ifaces' qdiscs */
237 if (args->ifindex != (unsigned int)t->tcm_ifindex)
240 * Use another nlsk_fd (0) to avoid tampering with the current list
243 return qdisc_del(0, args->ifindex, &qinfo);
247 * Iterate over all QDISC, and call the callback() function for each.
250 * The netlink socket file descriptor used for communication.
252 * The netdevice ifindex where to find QDISCs.
253 * @param[in] callback
254 * The function to call for each QDISC.
255 * @param[in, out] arg
256 * The arguments to provide the callback function with.
259 * -1 if either sending the netlink message failed, or if receiving the answer
260 * failed, or finally if the callback returned a negative value for that
262 * 0 is returned otherwise.
265 qdisc_iterate(int nlsk_fd, uint16_t ifindex,
266 int (*callback)(struct nlmsghdr *, void *), void *arg)
269 struct list_args args = {
275 tc_init_msg(&msg, ifindex, RTM_GETQDISC, NLM_F_REQUEST | NLM_F_DUMP);
276 if (nl_send(nlsk_fd, &msg.nh) < 0)
278 if (nl_recv(nlsk_fd, callback, &args) < 0)
284 * Check whether a given QDISC already exists for the netdevice.
287 * The netlink socket file descriptor used for communication.
289 * The netdevice ifindex to check QDISC existence for.
290 * @param[in] callback
291 * The function to call for each QDISC.
292 * @param[in, out] arg
293 * The arguments to provide the callback function with.
296 * 1 if the qdisc exists, 0 otherwise.
299 qdisc_exists(int nlsk_fd, uint16_t ifindex, uint32_t handle, uint32_t parent)
301 struct qdisc_custom_arg arg = {
307 qdisc_iterate(nlsk_fd, ifindex, qdisc_exist_cb, &arg);
314 * Delete all QDISCs for a given netdevice.
317 * The netlink socket file descriptor used for communication.
319 * The netdevice ifindex where to find QDISCs.
322 * -1 if the lookup failed, 0 otherwise.
325 qdisc_flush(int nlsk_fd, uint16_t ifindex)
327 return qdisc_iterate(nlsk_fd, ifindex, qdisc_del_cb, NULL);
331 * Create the multiqueue QDISC, only if it does not exist already.
334 * The netlink socket file descriptor used for communication.
336 * The netdevice ifindex where to add the multiqueue QDISC.
339 * 0 if the qdisc exists or if has been successfully added.
340 * Return -1 otherwise.
343 qdisc_create_multiq(int nlsk_fd, uint16_t ifindex)
345 if (!qdisc_exists(nlsk_fd, ifindex,
346 TC_H_MAKE(MULTIQ_MAJOR_HANDLE, 0), TC_H_ROOT)) {
347 if (qdisc_add_multiq(nlsk_fd, ifindex) < 0) {
348 RTE_LOG(ERR, PMD, "Could not add multiq qdisc\n");
356 * Create the ingress QDISC, only if it does not exist already.
359 * The netlink socket file descriptor used for communication.
361 * The netdevice ifindex where to add the ingress QDISC.
364 * 0 if the qdisc exists or if has been successfully added.
365 * Return -1 otherwise.
368 qdisc_create_ingress(int nlsk_fd, uint16_t ifindex)
370 if (!qdisc_exists(nlsk_fd, ifindex,
371 TC_H_MAKE(TC_H_INGRESS, 0), TC_H_INGRESS)) {
372 if (qdisc_add_ingress(nlsk_fd, ifindex) < 0) {
373 RTE_LOG(ERR, PMD, "Could not add ingress qdisc\n");