1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright 2018 6WIND S.A.
3 * Copyright 2018 Mellanox Technologies, Ltd
7 #include <libmnl/libmnl.h>
8 #include <linux/netlink.h>
9 #include <linux/pkt_sched.h>
10 #include <linux/rtnetlink.h>
15 #include <sys/socket.h>
17 #include <rte_errno.h>
22 /* Normally found in linux/netlink.h. */
23 #ifndef NETLINK_CAP_ACK
24 #define NETLINK_CAP_ACK 10
28 * Send Netlink message with acknowledgment.
31 * Libmnl socket to use.
33 * Message to send. This function always raises the NLM_F_ACK flag before
37 * 0 on success, a negative errno value otherwise and rte_errno is set.
40 mlx5_nl_flow_nl_ack(struct mnl_socket *nl, struct nlmsghdr *nlh)
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();
48 nlh->nlmsg_flags |= NLM_F_ACK;
50 ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len);
52 ret = mnl_socket_recvfrom(nl, ans, sizeof(ans));
55 (ans, ret, seq, mnl_socket_get_portid(nl), NULL, NULL);
63 * Initialize ingress qdisc of a given network interface.
66 * Libmnl socket of the @p NETLINK_ROUTE kind.
68 * Index of network interface to initialize.
70 * Perform verbose error reporting if not NULL.
73 * 0 on success, a negative errno value otherwise and rte_errno is set.
76 mlx5_nl_flow_init(struct mnl_socket *nl, unsigned int ifindex,
77 struct rte_flow_error *error)
81 alignas(struct nlmsghdr)
82 uint8_t buf[mnl_nlmsg_size(sizeof(*tcm) + 128)];
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");
117 * Create and configure a libmnl socket for Netlink flow rules.
120 * A valid libmnl socket object pointer on success, NULL otherwise and
124 mlx5_nl_flow_socket_create(void)
126 struct mnl_socket *nl = mnl_socket_open(NETLINK_ROUTE);
129 mnl_socket_setsockopt(nl, NETLINK_CAP_ACK, &(int){ 1 },
131 if (!mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID))
136 mnl_socket_close(nl);
141 * Destroy a libmnl socket.
144 mlx5_nl_flow_socket_destroy(struct mnl_socket *nl)
146 mnl_socket_close(nl);