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