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