1e1e18cc1SPouria Mousavizadeh Tehrani /*
2e1e18cc1SPouria Mousavizadeh Tehrani * Copyright (c) 2026 Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
3e1e18cc1SPouria Mousavizadeh Tehrani *
4e1e18cc1SPouria Mousavizadeh Tehrani * SPDX-License-Identifier: BSD-2-Clause
5e1e18cc1SPouria Mousavizadeh Tehrani */
6e1e18cc1SPouria Mousavizadeh Tehrani
7e1e18cc1SPouria Mousavizadeh Tehrani #include <sys/param.h>
8e1e18cc1SPouria Mousavizadeh Tehrani #include <sys/module.h>
9e1e18cc1SPouria Mousavizadeh Tehrani #include <sys/types.h>
10e1e18cc1SPouria Mousavizadeh Tehrani #include <sys/socket.h>
11e1e18cc1SPouria Mousavizadeh Tehrani #include <netinet/in.h>
12e1e18cc1SPouria Mousavizadeh Tehrani #include <arpa/inet.h>
13e1e18cc1SPouria Mousavizadeh Tehrani #include <net/if_gre.h>
14e1e18cc1SPouria Mousavizadeh Tehrani
15e1e18cc1SPouria Mousavizadeh Tehrani #include <netlink/netlink.h>
16e1e18cc1SPouria Mousavizadeh Tehrani #include <netlink/netlink_route.h>
17e1e18cc1SPouria Mousavizadeh Tehrani #include "netlink/netlink_snl.h"
18e1e18cc1SPouria Mousavizadeh Tehrani #include <netlink/netlink_snl_route.h>
19e1e18cc1SPouria Mousavizadeh Tehrani #include <netlink/netlink_snl_route_compat.h>
20e1e18cc1SPouria Mousavizadeh Tehrani #include <netlink/netlink_snl_route_parsers.h>
21e1e18cc1SPouria Mousavizadeh Tehrani
22e1e18cc1SPouria Mousavizadeh Tehrani #include <atf-c.h>
23e1e18cc1SPouria Mousavizadeh Tehrani
24e1e18cc1SPouria Mousavizadeh Tehrani struct nl_parsed_gre {
25e1e18cc1SPouria Mousavizadeh Tehrani struct sockaddr *ifla_local;
26e1e18cc1SPouria Mousavizadeh Tehrani struct sockaddr *ifla_remote;
27e1e18cc1SPouria Mousavizadeh Tehrani uint32_t ifla_flags;
28e1e18cc1SPouria Mousavizadeh Tehrani uint32_t ifla_okey;
29e1e18cc1SPouria Mousavizadeh Tehrani uint32_t ifla_encap_type;
30e1e18cc1SPouria Mousavizadeh Tehrani uint16_t ifla_encap_sport;
31e1e18cc1SPouria Mousavizadeh Tehrani };
32e1e18cc1SPouria Mousavizadeh Tehrani
33e1e18cc1SPouria Mousavizadeh Tehrani struct nla_gre_info {
34e1e18cc1SPouria Mousavizadeh Tehrani const char *kind;
35e1e18cc1SPouria Mousavizadeh Tehrani struct nl_parsed_gre data;
36e1e18cc1SPouria Mousavizadeh Tehrani };
37e1e18cc1SPouria Mousavizadeh Tehrani
38e1e18cc1SPouria Mousavizadeh Tehrani struct nla_gre_link {
39e1e18cc1SPouria Mousavizadeh Tehrani uint32_t ifi_index;
40e1e18cc1SPouria Mousavizadeh Tehrani struct nla_gre_info linkinfo;
41e1e18cc1SPouria Mousavizadeh Tehrani };
42e1e18cc1SPouria Mousavizadeh Tehrani
43e1e18cc1SPouria Mousavizadeh Tehrani #define _OUT(_field) offsetof(struct nl_parsed_gre, _field)
44e1e18cc1SPouria Mousavizadeh Tehrani static const struct snl_attr_parser nla_p_gre[] = {
45e1e18cc1SPouria Mousavizadeh Tehrani { .type = IFLA_GRE_LOCAL, .off = _OUT(ifla_local), .cb = snl_attr_get_ip },
46e1e18cc1SPouria Mousavizadeh Tehrani { .type = IFLA_GRE_REMOTE, .off = _OUT(ifla_remote), .cb = snl_attr_get_ip },
47e1e18cc1SPouria Mousavizadeh Tehrani { .type = IFLA_GRE_FLAGS, .off = _OUT(ifla_flags), .cb = snl_attr_get_uint32 },
48e1e18cc1SPouria Mousavizadeh Tehrani { .type = IFLA_GRE_OKEY, .off = _OUT(ifla_okey), .cb = snl_attr_get_uint32 },
49e1e18cc1SPouria Mousavizadeh Tehrani { .type = IFLA_GRE_ENCAP_TYPE, .off = _OUT(ifla_encap_type), .cb = snl_attr_get_uint32 },
50e1e18cc1SPouria Mousavizadeh Tehrani { .type = IFLA_GRE_ENCAP_SPORT, .off = _OUT(ifla_encap_sport), .cb = snl_attr_get_uint16 },
51e1e18cc1SPouria Mousavizadeh Tehrani };
52e1e18cc1SPouria Mousavizadeh Tehrani #undef _OUT
53e1e18cc1SPouria Mousavizadeh Tehrani SNL_DECLARE_ATTR_PARSER(gre_linkinfo_data_parser, nla_p_gre);
54e1e18cc1SPouria Mousavizadeh Tehrani
55e1e18cc1SPouria Mousavizadeh Tehrani #define _OUT(_field) offsetof(struct nla_gre_info, _field)
56e1e18cc1SPouria Mousavizadeh Tehrani static const struct snl_attr_parser ap_gre_linkinfo[] = {
57e1e18cc1SPouria Mousavizadeh Tehrani { .type = IFLA_INFO_KIND, .off = _OUT(kind), .cb = snl_attr_get_string },
58e1e18cc1SPouria Mousavizadeh Tehrani { .type = IFLA_INFO_DATA, .off = _OUT(data),
59e1e18cc1SPouria Mousavizadeh Tehrani .arg = &gre_linkinfo_data_parser, .cb = snl_attr_get_nested },
60e1e18cc1SPouria Mousavizadeh Tehrani };
61e1e18cc1SPouria Mousavizadeh Tehrani #undef _OUT
62e1e18cc1SPouria Mousavizadeh Tehrani SNL_DECLARE_ATTR_PARSER(gre_linkinfo_parser, ap_gre_linkinfo);
63e1e18cc1SPouria Mousavizadeh Tehrani
64e1e18cc1SPouria Mousavizadeh Tehrani #define _IN(_field) offsetof(struct ifinfomsg, _field)
65e1e18cc1SPouria Mousavizadeh Tehrani #define _OUT(_field) offsetof(struct nla_gre_link, _field)
66e1e18cc1SPouria Mousavizadeh Tehrani static const struct snl_attr_parser ap_gre_link[] = {
67e1e18cc1SPouria Mousavizadeh Tehrani { .type = IFLA_LINKINFO, .off = _OUT(linkinfo),
68e1e18cc1SPouria Mousavizadeh Tehrani .arg = &gre_linkinfo_parser, .cb = snl_attr_get_nested },
69e1e18cc1SPouria Mousavizadeh Tehrani };
70e1e18cc1SPouria Mousavizadeh Tehrani
71e1e18cc1SPouria Mousavizadeh Tehrani static const struct snl_field_parser fp_gre_link[] = {
72e1e18cc1SPouria Mousavizadeh Tehrani { .off_in = _IN(ifi_index), .off_out = _OUT(ifi_index), .cb = snl_field_get_uint32 },
73e1e18cc1SPouria Mousavizadeh Tehrani };
74e1e18cc1SPouria Mousavizadeh Tehrani #undef _IN
75e1e18cc1SPouria Mousavizadeh Tehrani #undef _OUT
76e1e18cc1SPouria Mousavizadeh Tehrani SNL_DECLARE_PARSER(gre_parser, struct ifinfomsg, fp_gre_link, ap_gre_link);
77e1e18cc1SPouria Mousavizadeh Tehrani
78e1e18cc1SPouria Mousavizadeh Tehrani ATF_TC(test_rtnl_gre);
ATF_TC_HEAD(test_rtnl_gre,tc)79e1e18cc1SPouria Mousavizadeh Tehrani ATF_TC_HEAD(test_rtnl_gre, tc)
80e1e18cc1SPouria Mousavizadeh Tehrani {
81e1e18cc1SPouria Mousavizadeh Tehrani atf_tc_set_md_var(tc, "descr", "test gre interface using netlink");
82e1e18cc1SPouria Mousavizadeh Tehrani atf_tc_set_md_var(tc, "require.user", "root");
831635ba90SPouria Mousavizadeh Tehrani atf_tc_set_md_var(tc, "require.kmods", "netlink if_gre");
84e1e18cc1SPouria Mousavizadeh Tehrani }
85e1e18cc1SPouria Mousavizadeh Tehrani
ATF_TC_BODY(test_rtnl_gre,tc)86e1e18cc1SPouria Mousavizadeh Tehrani ATF_TC_BODY(test_rtnl_gre, tc)
87e1e18cc1SPouria Mousavizadeh Tehrani {
88e1e18cc1SPouria Mousavizadeh Tehrani struct snl_state ss;
89e1e18cc1SPouria Mousavizadeh Tehrani struct snl_writer nw;
90e1e18cc1SPouria Mousavizadeh Tehrani struct nlmsghdr *hdr, *rx_hdr;
91*49fa0079SPouria Mousavizadeh Tehrani struct in_addr src, dst;
92e1e18cc1SPouria Mousavizadeh Tehrani struct nla_gre_link lattrs = {};
93e1e18cc1SPouria Mousavizadeh Tehrani struct nl_parsed_gre attrs = {};
94e1e18cc1SPouria Mousavizadeh Tehrani struct snl_errmsg_data e = {};
95e1e18cc1SPouria Mousavizadeh Tehrani struct ifinfomsg *ifmsg;
96e1e18cc1SPouria Mousavizadeh Tehrani int off, off2;
97e1e18cc1SPouria Mousavizadeh Tehrani
98e1e18cc1SPouria Mousavizadeh Tehrani ATF_REQUIRE_MSG(snl_init(&ss, NETLINK_ROUTE), "snl_init() failed");
99e1e18cc1SPouria Mousavizadeh Tehrani
100e1e18cc1SPouria Mousavizadeh Tehrani /* Create gre interface */
101e1e18cc1SPouria Mousavizadeh Tehrani snl_init_writer(&ss, &nw);
102e1e18cc1SPouria Mousavizadeh Tehrani ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_NEWLINK)) != NULL);
103e1e18cc1SPouria Mousavizadeh Tehrani hdr->nlmsg_flags |= (NLM_F_CREATE | NLM_F_EXCL | NLM_F_REQUEST | NLM_F_ACK);
104e1e18cc1SPouria Mousavizadeh Tehrani snl_reserve_msg_object(&nw, struct ifinfomsg);
105e1e18cc1SPouria Mousavizadeh Tehrani
106e1e18cc1SPouria Mousavizadeh Tehrani /* Create parameters */
107e1e18cc1SPouria Mousavizadeh Tehrani snl_add_msg_attr_string(&nw, IFLA_IFNAME, "gre10");
108e1e18cc1SPouria Mousavizadeh Tehrani off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
109e1e18cc1SPouria Mousavizadeh Tehrani snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "gre");
110e1e18cc1SPouria Mousavizadeh Tehrani off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
111e1e18cc1SPouria Mousavizadeh Tehrani
112*49fa0079SPouria Mousavizadeh Tehrani inet_pton(AF_INET, "127.0.0.1", &src);
113*49fa0079SPouria Mousavizadeh Tehrani inet_pton(AF_INET, "127.0.0.2", &dst);
114*49fa0079SPouria Mousavizadeh Tehrani snl_add_msg_attr_ip4(&nw, IFLA_GRE_LOCAL, &src);
115*49fa0079SPouria Mousavizadeh Tehrani snl_add_msg_attr_ip4(&nw, IFLA_GRE_REMOTE, &dst);
116e1e18cc1SPouria Mousavizadeh Tehrani snl_add_msg_attr_u32(&nw, IFLA_GRE_FLAGS, (GRE_ENABLE_SEQ | GRE_ENABLE_CSUM));
117e1e18cc1SPouria Mousavizadeh Tehrani snl_add_msg_attr_u32(&nw, IFLA_GRE_OKEY, 123456);
118e1e18cc1SPouria Mousavizadeh Tehrani snl_add_msg_attr_u32(&nw, IFLA_GRE_ENCAP_TYPE, IFLA_TUNNEL_GRE_UDP);
119e1e18cc1SPouria Mousavizadeh Tehrani snl_add_msg_attr_u16(&nw, IFLA_GRE_ENCAP_SPORT, 50000);
120e1e18cc1SPouria Mousavizadeh Tehrani
121e1e18cc1SPouria Mousavizadeh Tehrani snl_end_attr_nested(&nw, off2);
122e1e18cc1SPouria Mousavizadeh Tehrani snl_end_attr_nested(&nw, off);
123e1e18cc1SPouria Mousavizadeh Tehrani
124e1e18cc1SPouria Mousavizadeh Tehrani ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);
125e1e18cc1SPouria Mousavizadeh Tehrani ATF_REQUIRE(snl_send_message(&ss, hdr));
126e1e18cc1SPouria Mousavizadeh Tehrani ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);
127e1e18cc1SPouria Mousavizadeh Tehrani ATF_REQUIRE(snl_parse_errmsg(&ss, rx_hdr, &e));
128e1e18cc1SPouria Mousavizadeh Tehrani ATF_REQUIRE_INTEQ(e.error, 0);
129e1e18cc1SPouria Mousavizadeh Tehrani
130e1e18cc1SPouria Mousavizadeh Tehrani /* Dump gre interface */
131e1e18cc1SPouria Mousavizadeh Tehrani snl_init_writer(&ss, &nw);
132e1e18cc1SPouria Mousavizadeh Tehrani ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_GETLINK)) != NULL);
133e1e18cc1SPouria Mousavizadeh Tehrani hdr->nlmsg_flags |= NLM_F_DUMP;
134e1e18cc1SPouria Mousavizadeh Tehrani snl_reserve_msg_object(&nw, struct ifinfomsg);
135e1e18cc1SPouria Mousavizadeh Tehrani snl_add_msg_attr_string(&nw, IFLA_IFNAME, "gre10");
136e1e18cc1SPouria Mousavizadeh Tehrani off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
137e1e18cc1SPouria Mousavizadeh Tehrani snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "gre");
138e1e18cc1SPouria Mousavizadeh Tehrani snl_end_attr_nested(&nw, off);
139e1e18cc1SPouria Mousavizadeh Tehrani
140e1e18cc1SPouria Mousavizadeh Tehrani ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);
141e1e18cc1SPouria Mousavizadeh Tehrani ATF_REQUIRE(snl_send_message(&ss, hdr));
142e1e18cc1SPouria Mousavizadeh Tehrani
143e1e18cc1SPouria Mousavizadeh Tehrani /* Check parameters */
144e1e18cc1SPouria Mousavizadeh Tehrani ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);
145e1e18cc1SPouria Mousavizadeh Tehrani ATF_CHECK(snl_parse_nlmsg(&ss, rx_hdr, &gre_parser, &lattrs));
146e1e18cc1SPouria Mousavizadeh Tehrani attrs = lattrs.linkinfo.data;
147e1e18cc1SPouria Mousavizadeh Tehrani ATF_CHECK_STREQ(lattrs.linkinfo.kind, "gre");
148e1e18cc1SPouria Mousavizadeh Tehrani ATF_CHECK_INTEQ(attrs.ifla_flags, (GRE_ENABLE_SEQ | GRE_ENABLE_CSUM | GRE_UDPENCAP));
149e1e18cc1SPouria Mousavizadeh Tehrani ATF_CHECK_INTEQ(attrs.ifla_okey, 123456);
150e1e18cc1SPouria Mousavizadeh Tehrani ATF_CHECK_INTEQ(attrs.ifla_encap_type, IFLA_TUNNEL_GRE_UDP);
151e1e18cc1SPouria Mousavizadeh Tehrani ATF_CHECK_INTEQ(attrs.ifla_encap_sport, 50000);
152e1e18cc1SPouria Mousavizadeh Tehrani
153e1e18cc1SPouria Mousavizadeh Tehrani /* Delete gre interface */
154e1e18cc1SPouria Mousavizadeh Tehrani snl_init_writer(&ss, &nw);
155e1e18cc1SPouria Mousavizadeh Tehrani ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_DELLINK)) != NULL);
156e1e18cc1SPouria Mousavizadeh Tehrani hdr->nlmsg_flags |= (NLM_F_ACK | NLM_F_REQUEST);
157e1e18cc1SPouria Mousavizadeh Tehrani ATF_REQUIRE((ifmsg = snl_reserve_msg_object(&nw, struct ifinfomsg)) != NULL);
158e1e18cc1SPouria Mousavizadeh Tehrani ifmsg->ifi_index = lattrs.ifi_index;
159e1e18cc1SPouria Mousavizadeh Tehrani ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);
160e1e18cc1SPouria Mousavizadeh Tehrani ATF_REQUIRE(snl_send_message(&ss, hdr));
161e1e18cc1SPouria Mousavizadeh Tehrani ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);
162e1e18cc1SPouria Mousavizadeh Tehrani ATF_REQUIRE(snl_parse_errmsg(&ss, rx_hdr, &e));
163e1e18cc1SPouria Mousavizadeh Tehrani ATF_REQUIRE_INTEQ(e.error, 0);
164e1e18cc1SPouria Mousavizadeh Tehrani }
165e1e18cc1SPouria Mousavizadeh Tehrani
ATF_TP_ADD_TCS(tp)166e1e18cc1SPouria Mousavizadeh Tehrani ATF_TP_ADD_TCS(tp)
167e1e18cc1SPouria Mousavizadeh Tehrani {
168e1e18cc1SPouria Mousavizadeh Tehrani ATF_TP_ADD_TC(tp, test_rtnl_gre);
169e1e18cc1SPouria Mousavizadeh Tehrani
170e1e18cc1SPouria Mousavizadeh Tehrani return (atf_no_error());
171e1e18cc1SPouria Mousavizadeh Tehrani }
172e1e18cc1SPouria Mousavizadeh Tehrani
173