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