60a4493e53d4a5d00c3f152cf6a6d6ba859cb911
[dpdk.git] / drivers / net / mlx5 / mlx5_nl_flow.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2018 6WIND S.A.
3  * Copyright 2018 Mellanox Technologies, Ltd
4  */
5
6 #include <errno.h>
7 #include <libmnl/libmnl.h>
8 #include <linux/netlink.h>
9 #include <linux/pkt_sched.h>
10 #include <linux/rtnetlink.h>
11 #include <stdalign.h>
12 #include <stddef.h>
13 #include <stdint.h>
14 #include <stdlib.h>
15 #include <sys/socket.h>
16
17 #include <rte_errno.h>
18 #include <rte_flow.h>
19
20 #include "mlx5.h"
21
22 /* Normally found in linux/netlink.h. */
23 #ifndef NETLINK_CAP_ACK
24 #define NETLINK_CAP_ACK 10
25 #endif
26
27 /**
28  * Send Netlink message with acknowledgment.
29  *
30  * @param nl
31  *   Libmnl socket to use.
32  * @param nlh
33  *   Message to send. This function always raises the NLM_F_ACK flag before
34  *   sending.
35  *
36  * @return
37  *   0 on success, a negative errno value otherwise and rte_errno is set.
38  */
39 static int
40 mlx5_nl_flow_nl_ack(struct mnl_socket *nl, struct nlmsghdr *nlh)
41 {
42         alignas(struct nlmsghdr)
43         uint8_t ans[mnl_nlmsg_size(sizeof(struct nlmsgerr)) +
44                     nlh->nlmsg_len - sizeof(*nlh)];
45         uint32_t seq = random();
46         int ret;
47
48         nlh->nlmsg_flags |= NLM_F_ACK;
49         nlh->nlmsg_seq = seq;
50         ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len);
51         if (ret != -1)
52                 ret = mnl_socket_recvfrom(nl, ans, sizeof(ans));
53         if (ret != -1)
54                 ret = mnl_cb_run
55                         (ans, ret, seq, mnl_socket_get_portid(nl), NULL, NULL);
56         if (!ret)
57                 return 0;
58         rte_errno = errno;
59         return -rte_errno;
60 }
61
62 /**
63  * Initialize ingress qdisc of a given network interface.
64  *
65  * @param nl
66  *   Libmnl socket of the @p NETLINK_ROUTE kind.
67  * @param ifindex
68  *   Index of network interface to initialize.
69  * @param[out] error
70  *   Perform verbose error reporting if not NULL.
71  *
72  * @return
73  *   0 on success, a negative errno value otherwise and rte_errno is set.
74  */
75 int
76 mlx5_nl_flow_init(struct mnl_socket *nl, unsigned int ifindex,
77                   struct rte_flow_error *error)
78 {
79         struct nlmsghdr *nlh;
80         struct tcmsg *tcm;
81         alignas(struct nlmsghdr)
82         uint8_t buf[mnl_nlmsg_size(sizeof(*tcm) + 128)];
83
84         /* Destroy existing ingress qdisc and everything attached to it. */
85         nlh = mnl_nlmsg_put_header(buf);
86         nlh->nlmsg_type = RTM_DELQDISC;
87         nlh->nlmsg_flags = NLM_F_REQUEST;
88         tcm = mnl_nlmsg_put_extra_header(nlh, sizeof(*tcm));
89         tcm->tcm_family = AF_UNSPEC;
90         tcm->tcm_ifindex = ifindex;
91         tcm->tcm_handle = TC_H_MAKE(TC_H_INGRESS, 0);
92         tcm->tcm_parent = TC_H_INGRESS;
93         /* Ignore errors when qdisc is already absent. */
94         if (mlx5_nl_flow_nl_ack(nl, nlh) &&
95             rte_errno != EINVAL && rte_errno != ENOENT)
96                 return rte_flow_error_set
97                         (error, rte_errno, RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
98                          NULL, "netlink: failed to remove ingress qdisc");
99         /* Create fresh ingress qdisc. */
100         nlh = mnl_nlmsg_put_header(buf);
101         nlh->nlmsg_type = RTM_NEWQDISC;
102         nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
103         tcm = mnl_nlmsg_put_extra_header(nlh, sizeof(*tcm));
104         tcm->tcm_family = AF_UNSPEC;
105         tcm->tcm_ifindex = ifindex;
106         tcm->tcm_handle = TC_H_MAKE(TC_H_INGRESS, 0);
107         tcm->tcm_parent = TC_H_INGRESS;
108         mnl_attr_put_strz_check(nlh, sizeof(buf), TCA_KIND, "ingress");
109         if (mlx5_nl_flow_nl_ack(nl, nlh))
110                 return rte_flow_error_set
111                         (error, rte_errno, RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
112                          NULL, "netlink: failed to create ingress qdisc");
113         return 0;
114 }
115
116 /**
117  * Create and configure a libmnl socket for Netlink flow rules.
118  *
119  * @return
120  *   A valid libmnl socket object pointer on success, NULL otherwise and
121  *   rte_errno is set.
122  */
123 struct mnl_socket *
124 mlx5_nl_flow_socket_create(void)
125 {
126         struct mnl_socket *nl = mnl_socket_open(NETLINK_ROUTE);
127
128         if (nl) {
129                 mnl_socket_setsockopt(nl, NETLINK_CAP_ACK, &(int){ 1 },
130                                       sizeof(int));
131                 if (!mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID))
132                         return nl;
133         }
134         rte_errno = errno;
135         if (nl)
136                 mnl_socket_close(nl);
137         return NULL;
138 }
139
140 /**
141  * Destroy a libmnl socket.
142  */
143 void
144 mlx5_nl_flow_socket_destroy(struct mnl_socket *nl)
145 {
146         mnl_socket_close(nl);
147 }