1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (c) 2020 Petr Vorel <pvorel@suse.cz>
4f08c3bdfSopenharmony_ci */
5f08c3bdfSopenharmony_ci
6f08c3bdfSopenharmony_ci#include "config.h"
7f08c3bdfSopenharmony_ci#include "tst_test.h"
8f08c3bdfSopenharmony_ci
9f08c3bdfSopenharmony_ci#ifdef HAVE_LIBMNL
10f08c3bdfSopenharmony_ci
11f08c3bdfSopenharmony_ci#include <string.h>
12f08c3bdfSopenharmony_ci
13f08c3bdfSopenharmony_ci#include <libmnl/libmnl.h>
14f08c3bdfSopenharmony_ci#include <linux/rtnetlink.h>
15f08c3bdfSopenharmony_ci#include <net/if.h>
16f08c3bdfSopenharmony_ci#include <netdb.h>
17f08c3bdfSopenharmony_ci#include <netinet/in.h>
18f08c3bdfSopenharmony_ci
19f08c3bdfSopenharmony_ci#include "tst_net.h"
20f08c3bdfSopenharmony_ci#include "tst_safe_net.h"
21f08c3bdfSopenharmony_ci#include "tst_safe_stdio.h"
22f08c3bdfSopenharmony_ci
23f08c3bdfSopenharmony_ci#define IP_ADDR_DELIM ','
24f08c3bdfSopenharmony_ci
25f08c3bdfSopenharmony_cistatic char *c_opt, *d_opt, *g_opt, *ipv6_opt, *p_opt, *r_opt;
26f08c3bdfSopenharmony_ci
27f08c3bdfSopenharmony_cistatic int family = AF_INET;
28f08c3bdfSopenharmony_cistatic int fd, num_loops, port;
29f08c3bdfSopenharmony_ci
30f08c3bdfSopenharmony_cistatic unsigned int is_ipv6, max, prefix;
31f08c3bdfSopenharmony_ci
32f08c3bdfSopenharmony_cistatic struct mnl_socket *nl;
33f08c3bdfSopenharmony_cistatic struct addrinfo hints;
34f08c3bdfSopenharmony_ci
35f08c3bdfSopenharmony_cistruct iface {
36f08c3bdfSopenharmony_ci	unsigned int index;
37f08c3bdfSopenharmony_ci	struct iface *next;
38f08c3bdfSopenharmony_ci	char iface[IFNAMSIZ];
39f08c3bdfSopenharmony_ci};
40f08c3bdfSopenharmony_ci
41f08c3bdfSopenharmony_cistruct ip_addr {
42f08c3bdfSopenharmony_ci	struct addrinfo *ip;
43f08c3bdfSopenharmony_ci	struct ip_addr *next;
44f08c3bdfSopenharmony_ci	char ip_str[INET6_ADDRSTRLEN];
45f08c3bdfSopenharmony_ci};
46f08c3bdfSopenharmony_ci
47f08c3bdfSopenharmony_cistatic struct ip_addr *dst, *gw, *rhost;
48f08c3bdfSopenharmony_cistatic struct iface *iface;
49f08c3bdfSopenharmony_cistatic unsigned int gw_len, iface_len, rhost_len;
50f08c3bdfSopenharmony_ci
51f08c3bdfSopenharmony_civoid save_iface(void **data, const char *item)
52f08c3bdfSopenharmony_ci{
53f08c3bdfSopenharmony_ci	struct iface *n = SAFE_MALLOC(sizeof(*n));
54f08c3bdfSopenharmony_ci	struct iface **list = (struct iface**)data;
55f08c3bdfSopenharmony_ci
56f08c3bdfSopenharmony_ci	strncpy(n->iface, item, sizeof(n->iface));
57f08c3bdfSopenharmony_ci	n->iface[sizeof(n->iface)-1] = '\0';
58f08c3bdfSopenharmony_ci
59f08c3bdfSopenharmony_ci	n->index = if_nametoindex(item);
60f08c3bdfSopenharmony_ci	if (!n->index)
61f08c3bdfSopenharmony_ci		tst_brk(TBROK, "if_nametoindex failed, '%s' not found", item);
62f08c3bdfSopenharmony_ci	n->next = *list;
63f08c3bdfSopenharmony_ci	*list = n;
64f08c3bdfSopenharmony_ci}
65f08c3bdfSopenharmony_ci
66f08c3bdfSopenharmony_civoid save_ip(void **data, const char *item)
67f08c3bdfSopenharmony_ci{
68f08c3bdfSopenharmony_ci	struct ip_addr *n = SAFE_MALLOC(sizeof(*n));
69f08c3bdfSopenharmony_ci	struct ip_addr **list = (struct ip_addr**)data;
70f08c3bdfSopenharmony_ci
71f08c3bdfSopenharmony_ci	strncpy(n->ip_str, item, sizeof(n->ip_str));
72f08c3bdfSopenharmony_ci	n->ip_str[sizeof(n->ip_str)-1] = '\0';
73f08c3bdfSopenharmony_ci
74f08c3bdfSopenharmony_ci	SAFE_GETADDRINFO(item, p_opt, &hints, &n->ip);
75f08c3bdfSopenharmony_ci	n->next = *list;
76f08c3bdfSopenharmony_ci	*list = n;
77f08c3bdfSopenharmony_ci}
78f08c3bdfSopenharmony_ci
79f08c3bdfSopenharmony_ciint save_item(void **list, char *item, void (*callback)(void **, const char *))
80f08c3bdfSopenharmony_ci{
81f08c3bdfSopenharmony_ci	int len = 0;
82f08c3bdfSopenharmony_ci
83f08c3bdfSopenharmony_ci	while ((item = strtok(item, TST_TO_STR(IP_ADDR_DELIM))) != NULL) {
84f08c3bdfSopenharmony_ci		callback(list, item);
85f08c3bdfSopenharmony_ci		item = NULL;
86f08c3bdfSopenharmony_ci		len++;
87f08c3bdfSopenharmony_ci	}
88f08c3bdfSopenharmony_ci
89f08c3bdfSopenharmony_ci	return len;
90f08c3bdfSopenharmony_ci}
91f08c3bdfSopenharmony_ci
92f08c3bdfSopenharmony_cistatic void setup(void)
93f08c3bdfSopenharmony_ci{
94f08c3bdfSopenharmony_ci	prefix = 24;
95f08c3bdfSopenharmony_ci	if (ipv6_opt) {
96f08c3bdfSopenharmony_ci		family = AF_INET6;
97f08c3bdfSopenharmony_ci		is_ipv6 = 1;
98f08c3bdfSopenharmony_ci		prefix = 64;
99f08c3bdfSopenharmony_ci	}
100f08c3bdfSopenharmony_ci
101f08c3bdfSopenharmony_ci	if (!c_opt)
102f08c3bdfSopenharmony_ci		tst_brk(TBROK, "missing number of loops (-c num)");
103f08c3bdfSopenharmony_ci
104f08c3bdfSopenharmony_ci	if (!d_opt)
105f08c3bdfSopenharmony_ci		tst_brk(TBROK, "missing iface (-d iface)");
106f08c3bdfSopenharmony_ci
107f08c3bdfSopenharmony_ci	if (!p_opt)
108f08c3bdfSopenharmony_ci		tst_brk(TBROK, "missing rhost port (-p port)");
109f08c3bdfSopenharmony_ci
110f08c3bdfSopenharmony_ci	if (!r_opt)
111f08c3bdfSopenharmony_ci		tst_brk(TBROK, "missing rhost IP (-r IP)");
112f08c3bdfSopenharmony_ci
113f08c3bdfSopenharmony_ci	if (tst_parse_int(p_opt, &port, 1, 65535))
114f08c3bdfSopenharmony_ci		tst_brk(TBROK, "invalid rhost port '%s'", p_opt);
115f08c3bdfSopenharmony_ci
116f08c3bdfSopenharmony_ci	if (tst_parse_int(c_opt, &num_loops, 1, INT_MAX)) {
117f08c3bdfSopenharmony_ci		num_loops = INT_MAX;
118f08c3bdfSopenharmony_ci		tst_res(TWARN, "invalid number of loops (-c %s), using: %d",
119f08c3bdfSopenharmony_ci			c_opt, num_loops);
120f08c3bdfSopenharmony_ci	}
121f08c3bdfSopenharmony_ci
122f08c3bdfSopenharmony_ci	iface_len = save_item((void **)&iface, d_opt, save_iface);
123f08c3bdfSopenharmony_ci	rhost_len = save_item((void **)&rhost, r_opt, save_ip);
124f08c3bdfSopenharmony_ci
125f08c3bdfSopenharmony_ci	max = MAX(iface_len, rhost_len);
126f08c3bdfSopenharmony_ci	if (iface_len > 1 && rhost_len > 1 && iface_len != max)
127f08c3bdfSopenharmony_ci		tst_brk(TBROK, "-d specifies more NICs and -r more IPs, they need to have the same count");
128f08c3bdfSopenharmony_ci
129f08c3bdfSopenharmony_ci	if (g_opt) {
130f08c3bdfSopenharmony_ci		gw_len = save_item((void **)&gw, g_opt, save_ip);
131f08c3bdfSopenharmony_ci		max = MAX(gw_len, max);
132f08c3bdfSopenharmony_ci
133f08c3bdfSopenharmony_ci		if (gw_len > 1 && max > 1 && gw_len != max) {
134f08c3bdfSopenharmony_ci			if (iface_len == max)
135f08c3bdfSopenharmony_ci				tst_brk(TBROK, "-d specifies more NICs and -r more IPs, they need to have the same count");
136f08c3bdfSopenharmony_ci			else
137f08c3bdfSopenharmony_ci				tst_brk(TBROK, "-g and -r specify more IP, they need to have the same count");
138f08c3bdfSopenharmony_ci		}
139f08c3bdfSopenharmony_ci	}
140f08c3bdfSopenharmony_ci
141f08c3bdfSopenharmony_ci	struct ip_addr *p_rhost = rhost;
142f08c3bdfSopenharmony_ci
143f08c3bdfSopenharmony_ci	while (p_rhost) {
144f08c3bdfSopenharmony_ci		char dst_str[INET6_ADDRSTRLEN];
145f08c3bdfSopenharmony_ci
146f08c3bdfSopenharmony_ci		if (!strncpy(dst_str, p_rhost->ip_str, sizeof(dst_str)))
147f08c3bdfSopenharmony_ci			tst_brk(TBROK, "failed copy IP '%s'", p_rhost->ip_str);
148f08c3bdfSopenharmony_ci		dst_str[strlen(p_rhost->ip_str)-1] = '\0';
149f08c3bdfSopenharmony_ci
150f08c3bdfSopenharmony_ci		if (!strcat(dst_str, "0"))
151f08c3bdfSopenharmony_ci			tst_brk(TBROK, "strcat failed: '%s'", dst_str);
152f08c3bdfSopenharmony_ci
153f08c3bdfSopenharmony_ci		save_ip((void **)&dst, dst_str);
154f08c3bdfSopenharmony_ci		p_rhost = p_rhost->next;
155f08c3bdfSopenharmony_ci	}
156f08c3bdfSopenharmony_ci
157f08c3bdfSopenharmony_ci	fd = SAFE_SOCKET(family, SOCK_DGRAM, IPPROTO_UDP);
158f08c3bdfSopenharmony_ci
159f08c3bdfSopenharmony_ci	memset(&hints, 0, sizeof(struct addrinfo));
160f08c3bdfSopenharmony_ci	hints.ai_family = family;
161f08c3bdfSopenharmony_ci	hints.ai_socktype = SOCK_DGRAM;
162f08c3bdfSopenharmony_ci	hints.ai_flags = 0;
163f08c3bdfSopenharmony_ci	hints.ai_protocol = 0;
164f08c3bdfSopenharmony_ci	hints.ai_addr = INADDR_ANY;
165f08c3bdfSopenharmony_ci}
166f08c3bdfSopenharmony_ci
167f08c3bdfSopenharmony_cistatic void cleanup(void)
168f08c3bdfSopenharmony_ci{
169f08c3bdfSopenharmony_ci	if (fd > 0)
170f08c3bdfSopenharmony_ci		close(fd);
171f08c3bdfSopenharmony_ci
172f08c3bdfSopenharmony_ci	if (nl)
173f08c3bdfSopenharmony_ci		mnl_socket_close(nl);
174f08c3bdfSopenharmony_ci}
175f08c3bdfSopenharmony_ci
176f08c3bdfSopenharmony_cistatic void brk_on_route_error(const char *msg, int iface,
177f08c3bdfSopenharmony_ci			       struct sockaddr *dst, struct sockaddr *gw, int type)
178f08c3bdfSopenharmony_ci{
179f08c3bdfSopenharmony_ci	char dst_str[INET6_ADDRSTRLEN], gw_str[INET6_ADDRSTRLEN];
180f08c3bdfSopenharmony_ci	tst_sock_addr(dst, sizeof(dst), dst_str, sizeof(dst_str));
181f08c3bdfSopenharmony_ci	if (gw)
182f08c3bdfSopenharmony_ci		tst_sock_addr(gw, sizeof(gw), gw_str, sizeof(gw_str));
183f08c3bdfSopenharmony_ci
184f08c3bdfSopenharmony_ci	tst_res(TINFO, "type: %s, iface: %d, dst: %s, gw: %s",
185f08c3bdfSopenharmony_ci		type == RTM_NEWROUTE ? "RTM_NEWROUTE" : "RTM_DELROUTE",
186f08c3bdfSopenharmony_ci		iface, dst_str, gw ? gw_str : "null");
187f08c3bdfSopenharmony_ci	tst_brk(TBROK, "%s failed (errno=%d): %s", msg, errno, strerror(errno));
188f08c3bdfSopenharmony_ci}
189f08c3bdfSopenharmony_ci
190f08c3bdfSopenharmony_cistatic void rtnl_route(int iface, struct addrinfo *dst, struct addrinfo *gw,
191f08c3bdfSopenharmony_ci		       int type)
192f08c3bdfSopenharmony_ci{
193f08c3bdfSopenharmony_ci	struct mnl_socket *nl;
194f08c3bdfSopenharmony_ci	char buf[MNL_SOCKET_BUFFER_SIZE];
195f08c3bdfSopenharmony_ci	struct nlmsghdr *nlh;
196f08c3bdfSopenharmony_ci	struct rtmsg *rtm;
197f08c3bdfSopenharmony_ci	uint32_t seq, portid;
198f08c3bdfSopenharmony_ci	struct in6_addr dst_in6, gw_in6;
199f08c3bdfSopenharmony_ci	in_addr_t dst_ip, gw_ip;
200f08c3bdfSopenharmony_ci	int ret;
201f08c3bdfSopenharmony_ci
202f08c3bdfSopenharmony_ci	nlh = mnl_nlmsg_put_header(buf);
203f08c3bdfSopenharmony_ci	nlh->nlmsg_type	= type;
204f08c3bdfSopenharmony_ci
205f08c3bdfSopenharmony_ci	nlh->nlmsg_flags = NLM_F_ACK;
206f08c3bdfSopenharmony_ci	if (type == RTM_NEWROUTE)
207f08c3bdfSopenharmony_ci		nlh->nlmsg_flags |= NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE;
208f08c3bdfSopenharmony_ci
209f08c3bdfSopenharmony_ci	nlh->nlmsg_seq = seq = time(NULL);
210f08c3bdfSopenharmony_ci
211f08c3bdfSopenharmony_ci	rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg));
212f08c3bdfSopenharmony_ci	rtm->rtm_family = family;
213f08c3bdfSopenharmony_ci	rtm->rtm_dst_len = prefix;
214f08c3bdfSopenharmony_ci	rtm->rtm_src_len = 0;
215f08c3bdfSopenharmony_ci	rtm->rtm_tos = 0;
216f08c3bdfSopenharmony_ci	rtm->rtm_protocol = RTPROT_STATIC;
217f08c3bdfSopenharmony_ci	rtm->rtm_table = RT_TABLE_MAIN;
218f08c3bdfSopenharmony_ci	rtm->rtm_type = RTN_UNICAST;
219f08c3bdfSopenharmony_ci	rtm->rtm_scope = gw ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK;
220f08c3bdfSopenharmony_ci	rtm->rtm_flags = 0;
221f08c3bdfSopenharmony_ci
222f08c3bdfSopenharmony_ci	if (is_ipv6) {
223f08c3bdfSopenharmony_ci		dst_in6 = ((struct sockaddr_in6 *)dst->ai_addr)->sin6_addr;
224f08c3bdfSopenharmony_ci		mnl_attr_put(nlh, RTA_DST, sizeof(struct in6_addr), &dst_in6);
225f08c3bdfSopenharmony_ci	} else {
226f08c3bdfSopenharmony_ci		dst_ip = ((struct sockaddr_in *)dst->ai_addr)->sin_addr.s_addr;
227f08c3bdfSopenharmony_ci		mnl_attr_put_u32(nlh, RTA_DST, dst_ip);
228f08c3bdfSopenharmony_ci	}
229f08c3bdfSopenharmony_ci
230f08c3bdfSopenharmony_ci	mnl_attr_put_u32(nlh, RTA_OIF, iface);
231f08c3bdfSopenharmony_ci
232f08c3bdfSopenharmony_ci	if (gw) {
233f08c3bdfSopenharmony_ci		if (is_ipv6) {
234f08c3bdfSopenharmony_ci			gw_in6 = ((struct sockaddr_in6 *)gw->ai_addr)->sin6_addr;
235f08c3bdfSopenharmony_ci			mnl_attr_put(nlh, RTA_GATEWAY, sizeof(struct in6_addr), &gw_in6);
236f08c3bdfSopenharmony_ci		} else {
237f08c3bdfSopenharmony_ci			gw_ip = ((struct sockaddr_in *)gw->ai_addr)->sin_addr.s_addr;
238f08c3bdfSopenharmony_ci			mnl_attr_put_u32(nlh, RTA_GATEWAY, gw_ip);
239f08c3bdfSopenharmony_ci		}
240f08c3bdfSopenharmony_ci	}
241f08c3bdfSopenharmony_ci
242f08c3bdfSopenharmony_ci	nl = mnl_socket_open(NETLINK_ROUTE);
243f08c3bdfSopenharmony_ci	if (nl == NULL)
244f08c3bdfSopenharmony_ci		brk_on_route_error("mnl_socket_open", iface, dst->ai_addr, gw ?
245f08c3bdfSopenharmony_ci				   gw->ai_addr : NULL, type);
246f08c3bdfSopenharmony_ci
247f08c3bdfSopenharmony_ci	if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
248f08c3bdfSopenharmony_ci		brk_on_route_error("mnl_socket_bind", iface, dst->ai_addr, gw ?
249f08c3bdfSopenharmony_ci				   gw->ai_addr : NULL, type);
250f08c3bdfSopenharmony_ci
251f08c3bdfSopenharmony_ci	portid = mnl_socket_get_portid(nl);
252f08c3bdfSopenharmony_ci
253f08c3bdfSopenharmony_ci	if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
254f08c3bdfSopenharmony_ci		brk_on_route_error("mnl_socket_sendto", iface, dst->ai_addr, gw
255f08c3bdfSopenharmony_ci				   ? gw->ai_addr : NULL, type);
256f08c3bdfSopenharmony_ci
257f08c3bdfSopenharmony_ci	ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
258f08c3bdfSopenharmony_ci	if (ret < 0)
259f08c3bdfSopenharmony_ci		brk_on_route_error("mnl_socket_recvfrom", iface, dst->ai_addr,
260f08c3bdfSopenharmony_ci				   gw ? gw->ai_addr : NULL, type);
261f08c3bdfSopenharmony_ci
262f08c3bdfSopenharmony_ci	ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
263f08c3bdfSopenharmony_ci	if (ret < 0)
264f08c3bdfSopenharmony_ci		brk_on_route_error("mnl_cb_run", iface, dst->ai_addr, gw ?
265f08c3bdfSopenharmony_ci				   gw->ai_addr : NULL, type);
266f08c3bdfSopenharmony_ci
267f08c3bdfSopenharmony_ci	mnl_socket_close(nl);
268f08c3bdfSopenharmony_ci}
269f08c3bdfSopenharmony_ci
270f08c3bdfSopenharmony_cistatic void send_udp(struct addrinfo *rhost_addrinfo)
271f08c3bdfSopenharmony_ci{
272f08c3bdfSopenharmony_ci	const char *msg = "foo";
273f08c3bdfSopenharmony_ci
274f08c3bdfSopenharmony_ci	SAFE_SENDTO(1, fd, msg, sizeof(msg), MSG_CONFIRM,
275f08c3bdfSopenharmony_ci		rhost_addrinfo->ai_addr, rhost_addrinfo->ai_addrlen);
276f08c3bdfSopenharmony_ci}
277f08c3bdfSopenharmony_ci
278f08c3bdfSopenharmony_cistatic void run(void)
279f08c3bdfSopenharmony_ci{
280f08c3bdfSopenharmony_ci	int i;
281f08c3bdfSopenharmony_ci
282f08c3bdfSopenharmony_ci	tst_res(TINFO, "adding and deleting route %d times", num_loops);
283f08c3bdfSopenharmony_ci
284f08c3bdfSopenharmony_ci	struct ip_addr *p_dst = dst, *p_gw = gw, *p_rhost = rhost;
285f08c3bdfSopenharmony_ci	struct iface *p_iface = iface;
286f08c3bdfSopenharmony_ci
287f08c3bdfSopenharmony_ci	for (i = 0; i < num_loops; i++) {
288f08c3bdfSopenharmony_ci		rtnl_route(p_iface->index, p_dst->ip, gw ? p_gw->ip : NULL,
289f08c3bdfSopenharmony_ci			   RTM_NEWROUTE);
290f08c3bdfSopenharmony_ci		send_udp(p_rhost->ip);
291f08c3bdfSopenharmony_ci		rtnl_route(p_iface->index, p_dst->ip, gw ? p_gw->ip : NULL,
292f08c3bdfSopenharmony_ci			   RTM_DELROUTE);
293f08c3bdfSopenharmony_ci
294f08c3bdfSopenharmony_ci		if (gw)
295f08c3bdfSopenharmony_ci			p_gw = p_gw->next ?: gw;
296f08c3bdfSopenharmony_ci		p_dst = p_dst->next ?: dst;
297f08c3bdfSopenharmony_ci		p_iface = p_iface->next ?: iface;
298f08c3bdfSopenharmony_ci		p_rhost = p_rhost->next ?: rhost;
299f08c3bdfSopenharmony_ci	}
300f08c3bdfSopenharmony_ci
301f08c3bdfSopenharmony_ci	tst_res(TPASS, "routes created and deleted");
302f08c3bdfSopenharmony_ci}
303f08c3bdfSopenharmony_ci
304f08c3bdfSopenharmony_cistatic struct tst_test test = {
305f08c3bdfSopenharmony_ci	.test_all = run,
306f08c3bdfSopenharmony_ci	.needs_root = 1,
307f08c3bdfSopenharmony_ci	.setup = setup,
308f08c3bdfSopenharmony_ci	.cleanup = cleanup,
309f08c3bdfSopenharmony_ci	.options = (struct tst_option[]) {
310f08c3bdfSopenharmony_ci		{"6", &ipv6_opt, "Use IPv6 (default is IPv4)"},
311f08c3bdfSopenharmony_ci		{"c:", &c_opt, "Num loops (mandatory)"},
312f08c3bdfSopenharmony_ci		{"d:", &d_opt, "Interface to work on (mandatory)"},
313f08c3bdfSopenharmony_ci		{"g:", &g_opt, "Gateway IP"},
314f08c3bdfSopenharmony_ci		{"p:", &p_opt, "Rhost port (mandatory)"},
315f08c3bdfSopenharmony_ci		{"r:", &r_opt, "Rhost IP (mandatory)\n\n-g, -r IP parameter can contain more IP, separated by "
316f08c3bdfSopenharmony_ci			TST_TO_STR(IP_ADDR_DELIM)},
317f08c3bdfSopenharmony_ci		{}
318f08c3bdfSopenharmony_ci	},
319f08c3bdfSopenharmony_ci};
320f08c3bdfSopenharmony_ci#else
321f08c3bdfSopenharmony_ci	TST_TEST_TCONF("libmnl library and headers are required");
322f08c3bdfSopenharmony_ci#endif /* HAVE_LIBMNL */
323