Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tests/sys/netlink/test_rtnl_route.c
282976 views
1
/*
2
* Copyright (c) 2026 Pouria Mousavizadeh Tehrani <[email protected]>
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 *
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
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);
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
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);
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
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);
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
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);
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
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
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
378