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