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