xref: /freebsd/tests/sys/netlink/test_rtnl_route.c (revision 188631e43a1a5d2985156141c2e244a925670683)
1 /*
2  * Copyright (c) 2026 Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  */
6 
7 #include <sys/param.h>
8 #include <sys/module.h>
9 #include <sys/types.h>
10 #include <sys/socket.h>
11 #include <netinet/in.h>
12 #include <arpa/inet.h>
13 
14 #include <netlink/netlink.h>
15 #include <netlink/netlink_route.h>
16 #include "netlink/netlink_snl.h"
17 #include <netlink/netlink_snl_route.h>
18 #include <netlink/netlink_snl_route_compat.h>
19 #include <netlink/netlink_snl_route_parsers.h>
20 
21 #include <errno.h>
22 #include <unistd.h>
23 #include <time.h>
24 
25 #include <atf-c.h>
26 
27 static struct rtmsg *
prepare_rtm_by_dst(struct snl_writer * nw,char * dst)28 prepare_rtm_by_dst(struct snl_writer *nw, char *dst)
29 {
30 	struct rtmsg *rtm;
31 	struct in_addr in_dst;
32 
33 	inet_pton(AF_INET, dst, &in_dst);
34 	rtm = snl_reserve_msg_object(nw, struct rtmsg);
35 	if (rtm == NULL)
36 		return (NULL);
37 
38 	rtm->rtm_family = AF_INET;
39 	rtm->rtm_protocol = RTPROT_STATIC;
40 	rtm->rtm_type = RTN_UNICAST;
41 	rtm->rtm_dst_len = 24;
42 	rtm->rtm_flags = RTF_GATEWAY;
43 	snl_add_msg_attr_ip4(nw, RTA_DST, &in_dst);
44 
45 	return (rtm);
46 }
47 
48 static void
cleanup_route_by_dst(struct snl_state * ss,struct snl_writer * nw,char * dst)49 cleanup_route_by_dst(struct snl_state *ss, struct snl_writer *nw, char *dst)
50 {
51 	struct nlmsghdr *hdr, *rx_hdr;
52 	struct snl_errmsg_data e = {};
53 
54 	/* Delete route */
55 	snl_init_writer(ss, nw);
56 	ATF_REQUIRE((hdr = snl_create_msg_request(nw, RTM_DELROUTE)) != NULL);
57 	ATF_REQUIRE(prepare_rtm_by_dst(nw, dst) != NULL);
58 	ATF_REQUIRE((hdr = snl_finalize_msg(nw)) != NULL);
59 	ATF_REQUIRE(snl_send_message(ss, hdr));
60 	ATF_REQUIRE((rx_hdr = snl_read_reply(ss, hdr->nlmsg_seq)) != NULL);
61 }
62 
63 ATF_TC(rtnl_nhgrp);
ATF_TC_HEAD(rtnl_nhgrp,tc)64 ATF_TC_HEAD(rtnl_nhgrp, tc)
65 {
66 	atf_tc_set_md_var(tc, "descr", "test nexthop group using netlink");
67 	atf_tc_set_md_var(tc, "require.user", "root");
68 	atf_tc_set_md_var(tc, "require.kmods", "netlink");
69 }
70 
ATF_TC_BODY(rtnl_nhgrp,tc)71 ATF_TC_BODY(rtnl_nhgrp, tc)
72 {
73 	struct snl_state ss;
74 	struct snl_writer nw;
75 	struct nlmsghdr *hdr, *rx_hdr;
76 	struct in_addr gw1, gw2;
77 	struct snl_errmsg_data e = {};
78 	struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT };
79 	struct rtmsg *rtm;
80 	struct rtnexthop *rtnh;
81 	int off, off2;
82 
83 	ATF_REQUIRE_MSG(snl_init(&ss, NETLINK_ROUTE), "snl_init() failed");
84 
85 	inet_pton(AF_INET, "127.0.0.1", &gw1);
86 	inet_pton(AF_INET, "127.0.0.2", &gw2);
87 
88 	/* Create new multipath route */
89 	snl_init_writer(&ss, &nw);
90 	ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_NEWROUTE)) != NULL);
91 	hdr->nlmsg_flags |= NLM_F_CREATE;
92 	ATF_REQUIRE((rtm = prepare_rtm_by_dst(&nw, "192.0.2.0")) != NULL);
93 
94 	off = snl_add_msg_attr_nested(&nw, RTA_MULTIPATH);
95 	/* first nexthop */
96 	off2 = snl_get_msg_offset(&nw);
97 	rtnh = snl_reserve_msg_object(&nw, struct rtnexthop);
98 	rtnh->rtnh_flags = 0;
99 	rtnh->rtnh_hops = 1;
100 	rtnh->rtnh_ifindex = 0;
101 	snl_add_msg_attr_ip4(&nw, RTA_GATEWAY, &gw1);
102 	rtnh = snl_restore_msg_offset(&nw, off2, struct rtnexthop);
103 	rtnh->rtnh_len = snl_get_msg_offset(&nw) - off2;
104 
105 	/* second nexthop */
106 	off2 = snl_get_msg_offset(&nw);
107 	rtnh = snl_reserve_msg_object(&nw, struct rtnexthop);
108 	rtnh->rtnh_flags = 0;
109 	rtnh->rtnh_hops = 1;
110 	rtnh->rtnh_ifindex = 0;
111 	snl_add_msg_attr_ip4(&nw, RTA_GATEWAY, &gw2);
112 	rtnh = snl_restore_msg_offset(&nw, off2, struct rtnexthop);
113 	rtnh->rtnh_len = snl_get_msg_offset(&nw) - off2;
114 
115 	snl_end_attr_nested(&nw, off);
116 
117 	ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);
118 	ATF_REQUIRE(snl_send_message(&ss, hdr));
119 	ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);
120 	ATF_REQUIRE(snl_parse_errmsg(&ss, rx_hdr, &e));
121 	ATF_REQUIRE_INTEQ(e.error, 0);
122 
123 	/* Get route and check for its nexthop group */
124 	snl_init_writer(&ss, &nw);
125 	ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_GETROUTE)) != NULL);
126 	ATF_REQUIRE((rtm = prepare_rtm_by_dst(&nw, "192.0.2.0")) != NULL);
127 	ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);
128 	ATF_REQUIRE(snl_send_message(&ss, hdr));
129 	ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);
130 	ATF_CHECK(snl_parse_nlmsg(&ss, rx_hdr, &snl_rtm_route_parser, &r));
131 	ATF_CHECK(r.rta_knh_id != 0);
132 	ATF_CHECK_INTEQ(r.rta_multipath.num_nhops, 2);
133 
134 	cleanup_route_by_dst(&ss, &nw, "192.0.2.0");
135 }
136 
137 ATF_TC(rtnl_nhop_merge);
ATF_TC_HEAD(rtnl_nhop_merge,tc)138 ATF_TC_HEAD(rtnl_nhop_merge, tc)
139 {
140 	atf_tc_set_md_var(tc, "descr", "test merge of two independent nexthop using netlink");
141 	atf_tc_set_md_var(tc, "require.user", "root");
142 	atf_tc_set_md_var(tc, "require.kmods", "netlink");
143 }
144 
ATF_TC_BODY(rtnl_nhop_merge,tc)145 ATF_TC_BODY(rtnl_nhop_merge, tc)
146 {
147 	struct snl_state ss;
148 	struct snl_writer nw;
149 	struct nlmsghdr *hdr, *rx_hdr;
150 	struct in_addr gw1, gw2;
151 	struct snl_errmsg_data e = {};
152 	struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT };
153 	struct rtmsg *rtm;
154 	struct rtnexthop *rtnh;
155 
156 	ATF_REQUIRE_MSG(snl_init(&ss, NETLINK_ROUTE), "snl_init() failed");
157 
158 	inet_pton(AF_INET, "127.0.1.1", &gw1);
159 	inet_pton(AF_INET, "127.0.1.2", &gw2);
160 
161 	/* Create new route with single nhop */
162 	snl_init_writer(&ss, &nw);
163 	ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_NEWROUTE)) != NULL);
164 	hdr->nlmsg_flags |= NLM_F_CREATE;
165 	ATF_REQUIRE((rtm = prepare_rtm_by_dst(&nw, "198.51.100.0")) != NULL);
166 	snl_add_msg_attr_ip4(&nw, RTA_GATEWAY, &gw1);
167 	ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);
168 	ATF_REQUIRE(snl_send_message(&ss, hdr));
169 	ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);
170 	ATF_REQUIRE(snl_parse_errmsg(&ss, rx_hdr, &e));
171 	ATF_REQUIRE_INTEQ(e.error, 0);
172 
173 	/* Get route and verify it's NOT a nexthop group */
174 	snl_init_writer(&ss, &nw);
175 	ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_GETROUTE)) != NULL);
176 	ATF_REQUIRE((rtm = prepare_rtm_by_dst(&nw, "198.51.100.0")) != NULL);
177 	ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);
178 	ATF_REQUIRE(snl_send_message(&ss, hdr));
179 	ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);
180 	ATF_CHECK(snl_parse_nlmsg(&ss, rx_hdr, &snl_rtm_route_parser, &r));
181 	ATF_CHECK(r.rta_knh_id != 0);
182 	ATF_CHECK_INTEQ(r.rta_multipath.num_nhops, 0);
183 
184 	/* Append anoher nhop */
185 	snl_init_writer(&ss, &nw);
186 	ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_NEWROUTE)) != NULL);
187 	hdr->nlmsg_flags |= NLM_F_APPEND;
188 	ATF_REQUIRE((rtm = prepare_rtm_by_dst(&nw, "198.51.100.0")) != NULL);
189 	snl_add_msg_attr_ip4(&nw, RTA_GATEWAY, &gw2);
190 	ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);
191 	ATF_REQUIRE(snl_send_message(&ss, hdr));
192 	ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);
193 	ATF_REQUIRE(snl_parse_errmsg(&ss, rx_hdr, &e));
194 	ATF_REQUIRE_INTEQ(e.error, 0);
195 
196 	/* Get route and verify it became a nexthop group */
197 	snl_init_writer(&ss, &nw);
198 	ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_GETROUTE)) != NULL);
199 	ATF_REQUIRE((rtm = prepare_rtm_by_dst(&nw, "198.51.100.0")) != NULL);
200 	ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);
201 	ATF_REQUIRE(snl_send_message(&ss, hdr));
202 	ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);
203 	ATF_CHECK(snl_parse_nlmsg(&ss, rx_hdr, &snl_rtm_route_parser, &r));
204 	ATF_CHECK(r.rta_knh_id != 0);
205 	ATF_CHECK_INTEQ(r.rta_multipath.num_nhops, 2);
206 
207 	cleanup_route_by_dst(&ss, &nw, "198.51.100.0");
208 }
209 
210 ATF_TC(rtnl_nhgrp_expire);
ATF_TC_HEAD(rtnl_nhgrp_expire,tc)211 ATF_TC_HEAD(rtnl_nhgrp_expire, tc)
212 {
213 	atf_tc_set_md_var(tc, "descr", "test nhop expiration of a member inside nhgrp using netlink");
214 	atf_tc_set_md_var(tc, "require.user", "root");
215 	atf_tc_set_md_var(tc, "require.kmods", "netlink");
216 }
217 
ATF_TC_BODY(rtnl_nhgrp_expire,tc)218 ATF_TC_BODY(rtnl_nhgrp_expire, tc)
219 {
220 	struct snl_state ss;
221 	struct snl_writer nw;
222 	struct nlmsghdr *hdr, *rx_hdr;
223 	struct in_addr gw1, gw2, gw3;
224 	struct snl_errmsg_data e = {};
225 	struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT };
226 	struct rtmsg *rtm;
227 	struct rtnexthop *rtnh;
228 	struct timespec ts;
229 	int off, off2;
230 
231 	ATF_REQUIRE_MSG(snl_init(&ss, NETLINK_ROUTE), "snl_init() failed");
232 
233 	inet_pton(AF_INET, "127.0.2.1", &gw1);
234 	inet_pton(AF_INET, "127.0.2.2", &gw2);
235 	inet_pton(AF_INET, "127.0.2.3", &gw3);
236 
237 	/* create new multipath route */
238 	snl_init_writer(&ss, &nw);
239 	ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_NEWROUTE)) != NULL);
240 	hdr->nlmsg_flags |= NLM_F_CREATE;
241 	ATF_REQUIRE((rtm = prepare_rtm_by_dst(&nw, "203.0.113.0")) != NULL);
242 
243 	off = snl_add_msg_attr_nested(&nw, RTA_MULTIPATH);
244 	/* first nexthop */
245 	off2 = snl_get_msg_offset(&nw);
246 	rtnh = snl_reserve_msg_object(&nw, struct rtnexthop);
247 	rtnh->rtnh_flags = 0;
248 	rtnh->rtnh_hops = 1;
249 	rtnh->rtnh_ifindex = 0;
250 	snl_add_msg_attr_ip4(&nw, RTA_GATEWAY, &gw1);
251 	rtnh = snl_restore_msg_offset(&nw, off2, struct rtnexthop);
252 	rtnh->rtnh_len = snl_get_msg_offset(&nw) - off2;
253 
254 	/* second nexthop */
255 	off2 = snl_get_msg_offset(&nw);
256 	rtnh = snl_reserve_msg_object(&nw, struct rtnexthop);
257 	rtnh->rtnh_flags = 0;
258 	rtnh->rtnh_hops = 1;
259 	rtnh->rtnh_ifindex = 0;
260 	snl_add_msg_attr_ip4(&nw, RTA_GATEWAY, &gw2);
261 	rtnh = snl_restore_msg_offset(&nw, off2, struct rtnexthop);
262 	rtnh->rtnh_len = snl_get_msg_offset(&nw) - off2;
263 
264 	snl_end_attr_nested(&nw, off);
265 
266 	ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);
267 	ATF_REQUIRE(snl_send_message(&ss, hdr));
268 	ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);
269 	ATF_REQUIRE(snl_parse_errmsg(&ss, rx_hdr, &e));
270 	ATF_REQUIRE_INTEQ(e.error, 0);
271 
272 	/* append anoher nhop with expiration time */
273 	snl_init_writer(&ss, &nw);
274 	ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_NEWROUTE)) != NULL);
275 	hdr->nlmsg_flags |= NLM_F_APPEND;
276 	ATF_REQUIRE((rtm = prepare_rtm_by_dst(&nw, "203.0.113.0")) != NULL);
277 	snl_add_msg_attr_ip4(&nw, RTA_GATEWAY, &gw3);
278 	/* expire after 1 seconds */
279 	clock_gettime(CLOCK_REALTIME_FAST, &ts);
280 	snl_add_msg_attr_u32(&nw, RTA_EXPIRES, ts.tv_sec + 1);
281 	ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);
282 	ATF_REQUIRE(snl_send_message(&ss, hdr));
283 	ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);
284 	ATF_REQUIRE(snl_parse_errmsg(&ss, rx_hdr, &e));
285 	ATF_REQUIRE_INTEQ(e.error, 0);
286 
287 	/* get route and check for number of nhops */
288 	snl_init_writer(&ss, &nw);
289 	ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_GETROUTE)) != NULL);
290 	ATF_REQUIRE((rtm = prepare_rtm_by_dst(&nw, "203.0.113.0")) != NULL);
291 	ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);
292 	ATF_REQUIRE(snl_send_message(&ss, hdr));
293 	ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);
294 	ATF_CHECK(snl_parse_nlmsg(&ss, rx_hdr, &snl_rtm_route_parser, &r));
295 	ATF_CHECK(r.rta_knh_id != 0);
296 	ATF_CHECK_INTEQ(r.rta_multipath.num_nhops, 3);
297 
298 	/* wait for 2 seconds and try again */
299 	sleep(2);
300 
301 	/* get route and check for number of nhops */
302 	snl_init_writer(&ss, &nw);
303 	ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_GETROUTE)) != NULL);
304 	ATF_REQUIRE((rtm = prepare_rtm_by_dst(&nw, "203.0.113.0")) != NULL);
305 	ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);
306 	ATF_REQUIRE(snl_send_message(&ss, hdr));
307 	ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);
308 	ATF_CHECK(snl_parse_nlmsg(&ss, rx_hdr, &snl_rtm_route_parser, &r));
309 	ATF_CHECK_INTEQ(r.rta_multipath.num_nhops, 2);
310 
311 	cleanup_route_by_dst(&ss, &nw, "203.0.113.0");
312 }
313 
314 ATF_TC(rtnl_nhgrp_big_nhops);
ATF_TC_HEAD(rtnl_nhgrp_big_nhops,tc)315 ATF_TC_HEAD(rtnl_nhgrp_big_nhops, tc)
316 {
317 	atf_tc_set_md_var(tc, "descr", "test RTA_MULTIPATH with too many nhops using netlink");
318 	atf_tc_set_md_var(tc, "require.user", "root");
319 	atf_tc_set_md_var(tc, "require.kmods", "netlink");
320 }
321 
ATF_TC_BODY(rtnl_nhgrp_big_nhops,tc)322 ATF_TC_BODY(rtnl_nhgrp_big_nhops, tc)
323 {
324 	struct snl_state ss;
325 	struct snl_writer nw;
326 	struct nlmsghdr *hdr, *rx_hdr;
327 	struct in_addr gw;
328 	struct snl_errmsg_data e = {};
329 	struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT };
330 	struct rtmsg *rtm;
331 	struct rtnexthop *rtnh;
332 	int nhop, max_nhop, off, off2;
333 
334 	max_nhop = 25;
335 	ATF_REQUIRE_MSG(snl_init(&ss, NETLINK_ROUTE), "snl_init() failed");
336 
337 	inet_pton(AF_INET, "127.0.2.1", &gw);
338 
339 	/* create new multipath route */
340 	snl_init_writer(&ss, &nw);
341 	ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_NEWROUTE)) != NULL);
342 	hdr->nlmsg_flags |= NLM_F_CREATE;
343 	ATF_REQUIRE((rtm = prepare_rtm_by_dst(&nw, "203.0.113.0")) != NULL);
344 
345 	off = snl_add_msg_attr_nested(&nw, RTA_MULTIPATH);
346 	for (nhop = 0; nhop < max_nhop; nhop++) {
347 		off2 = snl_get_msg_offset(&nw);
348 		rtnh = snl_reserve_msg_object(&nw, struct rtnexthop);
349 		rtnh->rtnh_flags = 0;
350 		rtnh->rtnh_hops = nhop + 1;
351 		rtnh->rtnh_ifindex = 0;
352 		snl_add_msg_attr_ip4(&nw, RTA_GATEWAY, &gw);
353 		rtnh = snl_restore_msg_offset(&nw, off2, struct rtnexthop);
354 		rtnh->rtnh_len = snl_get_msg_offset(&nw) - off2;
355 	}
356 	snl_end_attr_nested(&nw, off);
357 
358 	ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);
359 	ATF_REQUIRE(snl_send_message(&ss, hdr));
360 	ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);
361 	ATF_REQUIRE(snl_parse_errmsg(&ss, rx_hdr, &e));
362 	ATF_REQUIRE_INTEQ(e.error, ENOMEM);
363 
364 	cleanup_route_by_dst(&ss, &nw, "203.0.113.0");
365 }
366 
367 
ATF_TP_ADD_TCS(tp)368 ATF_TP_ADD_TCS(tp)
369 {
370 	ATF_TP_ADD_TC(tp, rtnl_nhgrp);
371 	ATF_TP_ADD_TC(tp, rtnl_nhgrp_expire);
372 	ATF_TP_ADD_TC(tp, rtnl_nhop_merge);
373 	ATF_TP_ADD_TC(tp, rtnl_nhgrp_big_nhops);
374 
375 	return (atf_no_error());
376 }
377