xref: /f-stack/dpdk/drivers/net/tap/tap_tcmsgs.c (revision 1646932a)
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 	uint16_t 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
tc_init_msg(struct nlmsg * msg,uint16_t ifindex,uint16_t type,uint16_t flags)45 tc_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  */
72 static int
qdisc_del(int nlsk_fd,uint16_t ifindex,struct qdisc * qinfo)73 qdisc_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;
99 error:
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  */
116 int
qdisc_add_multiq(int nlsk_fd,uint16_t ifindex)117 qdisc_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  */
146 int
qdisc_add_ingress(int nlsk_fd,uint16_t ifindex)147 qdisc_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  */
174 static int
qdisc_del_cb(struct nlmsghdr * nh,void * arg)175 qdisc_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  */
210 static int
qdisc_iterate(int nlsk_fd,uint16_t ifindex,int (* callback)(struct nlmsghdr *,void *),void * arg)211 qdisc_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  */
240 int
qdisc_flush(int nlsk_fd,uint16_t ifindex)241 qdisc_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  */
258 int
qdisc_create_multiq(int nlsk_fd,uint16_t ifindex)259 qdisc_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  */
284 int
qdisc_create_ingress(int nlsk_fd,uint16_t ifindex)285 qdisc_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