New upstream version 18.08
[deb_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 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
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
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
117 qdisc_add_multiq(int nlsk_fd, uint16_t ifindex)
118 {
119         struct tc_multiq_qopt opt;
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
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
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
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
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
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
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 }