Branch data Line data Source code
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 : :
15 : : struct qdisc {
16 : : uint32_t handle;
17 : : uint32_t parent;
18 : : };
19 : :
20 : : struct list_args {
21 : : int nlsk_fd;
22 : : unsigned int ifindex;
23 : : void *custom_arg;
24 : : };
25 : :
26 : : struct 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 : : */
44 : : void
45 : 0 : tc_init_msg(struct tap_nlmsg *msg, unsigned int ifindex, uint16_t type, uint16_t flags)
46 : : {
47 : : struct nlmsghdr *n = &msg->nh;
48 : :
49 : 0 : n->nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
50 : 0 : n->nlmsg_type = type;
51 [ # # ]: 0 : if (flags)
52 : 0 : n->nlmsg_flags = flags;
53 : : else
54 : 0 : n->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
55 : 0 : msg->t.tcm_family = AF_UNSPEC;
56 : 0 : msg->t.tcm_ifindex = ifindex;
57 : 0 : }
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 : : */
72 : : static int
73 : 0 : qdisc_del(int nlsk_fd, unsigned int ifindex, struct qdisc *qinfo)
74 : : {
75 : : struct tap_nlmsg msg;
76 : : int fd = 0;
77 : :
78 : 0 : tc_init_msg(&msg, ifindex, RTM_DELQDISC, 0);
79 : 0 : msg.t.tcm_handle = qinfo->handle;
80 : 0 : msg.t.tcm_parent = qinfo->parent;
81 : : /* if no netlink socket is provided, create one */
82 [ # # ]: 0 : if (!nlsk_fd) {
83 : 0 : fd = tap_nl_init(0);
84 [ # # ]: 0 : if (fd < 0) {
85 : 0 : TAP_LOG(ERR,
86 : : "Could not delete QDISC: null netlink socket");
87 : 0 : return -1;
88 : : }
89 : : } else {
90 : : fd = nlsk_fd;
91 : : }
92 [ # # ]: 0 : if (tap_nl_send(fd, &msg.nh) < 0)
93 : 0 : goto error;
94 [ # # ]: 0 : if (tap_nl_recv_ack(fd) < 0)
95 : 0 : goto error;
96 [ # # ]: 0 : if (!nlsk_fd)
97 : 0 : return tap_nl_final(fd);
98 : : return 0;
99 : 0 : error:
100 [ # # ]: 0 : if (!nlsk_fd)
101 : 0 : 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 : : */
116 : : int
117 : 0 : qdisc_add_multiq(int nlsk_fd, unsigned int ifindex)
118 : : {
119 : 0 : struct tc_multiq_qopt opt = {0};
120 : : struct tap_nlmsg msg;
121 : :
122 : 0 : tc_init_msg(&msg, ifindex, RTM_NEWQDISC,
123 : : NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
124 : 0 : msg.t.tcm_handle = TC_H_MAKE(MULTIQ_MAJOR_HANDLE, 0);
125 : 0 : msg.t.tcm_parent = TC_H_ROOT;
126 : 0 : tap_nlattr_add(&msg.nh, TCA_KIND, sizeof("multiq"), "multiq");
127 : 0 : tap_nlattr_add(&msg.nh, TCA_OPTIONS, sizeof(opt), &opt);
128 [ # # ]: 0 : if (tap_nl_send(nlsk_fd, &msg.nh) < 0)
129 : : return -1;
130 [ # # ]: 0 : if (tap_nl_recv_ack(nlsk_fd) < 0)
131 : 0 : 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 : : */
146 : : int
147 : 0 : qdisc_add_ingress(int nlsk_fd, unsigned int ifindex)
148 : : {
149 : : struct tap_nlmsg msg;
150 : :
151 : 0 : tc_init_msg(&msg, ifindex, RTM_NEWQDISC,
152 : : NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
153 : 0 : msg.t.tcm_handle = TC_H_MAKE(TC_H_INGRESS, 0);
154 : 0 : msg.t.tcm_parent = TC_H_INGRESS;
155 : 0 : tap_nlattr_add(&msg.nh, TCA_KIND, sizeof("ingress"), "ingress");
156 [ # # ]: 0 : if (tap_nl_send(nlsk_fd, &msg.nh) < 0)
157 : : return -1;
158 [ # # ]: 0 : if (tap_nl_recv_ack(nlsk_fd) < 0)
159 : 0 : 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 : : */
174 : : static int
175 : 0 : qdisc_del_cb(struct nlmsghdr *nh, void *arg)
176 : : {
177 : : struct tcmsg *t = NLMSG_DATA(nh);
178 : : struct list_args *args = arg;
179 : :
180 : 0 : struct qdisc qinfo = {
181 : 0 : .handle = t->tcm_handle,
182 : 0 : .parent = t->tcm_parent,
183 : : };
184 : :
185 : : /* filter out other ifaces' qdiscs */
186 [ # # ]: 0 : 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 : 0 : 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 : : */
210 : : static int
211 : 0 : qdisc_iterate(int nlsk_fd, unsigned int ifindex,
212 : : int (*callback)(struct nlmsghdr *, void *), void *arg)
213 : : {
214 : : struct tap_nlmsg msg;
215 : 0 : struct list_args args = {
216 : : .nlsk_fd = nlsk_fd,
217 : : .ifindex = ifindex,
218 : : .custom_arg = arg,
219 : : };
220 : :
221 : 0 : tc_init_msg(&msg, ifindex, RTM_GETQDISC, NLM_F_REQUEST | NLM_F_DUMP);
222 [ # # ]: 0 : if (tap_nl_send(nlsk_fd, &msg.nh) < 0)
223 : : return -1;
224 [ # # ]: 0 : if (tap_nl_recv(nlsk_fd, callback, &args) < 0)
225 : 0 : 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 : : */
240 : : int
241 : 0 : qdisc_flush(int nlsk_fd, unsigned int ifindex)
242 : : {
243 : 0 : 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 : : */
258 : : int
259 : 0 : qdisc_create_multiq(int nlsk_fd, unsigned int ifindex)
260 : : {
261 : : int err = 0;
262 : :
263 : 0 : err = qdisc_add_multiq(nlsk_fd, ifindex);
264 [ # # # # ]: 0 : if (err < 0 && errno != -EEXIST) {
265 : 0 : TAP_LOG(ERR, "Could not add multiq qdisc (%d): %s",
266 : : errno, strerror(errno));
267 : 0 : 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 : : */
284 : : int
285 : 0 : qdisc_create_ingress(int nlsk_fd, unsigned int ifindex)
286 : : {
287 : : int err = 0;
288 : :
289 : 0 : err = qdisc_add_ingress(nlsk_fd, ifindex);
290 [ # # # # ]: 0 : if (err < 0 && errno != -EEXIST) {
291 : 0 : TAP_LOG(ERR, "Could not add ingress qdisc (%d): %s",
292 : : errno, strerror(errno));
293 : 0 : return -1;
294 : : }
295 : : return 0;
296 : : }
|