1 /*
2 * Copyright (c) 2021 Eugene Syromyatnikov <evgsyr@gmail.com>
3 * All rights reserved.
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8 #include "tests.h"
9
10 #include <arpa/inet.h>
11 #include <inttypes.h>
12 #include <linux/ip.h>
13 #include <linux/rtnetlink.h>
14 #include <linux/nexthop.h>
15 #include <netinet/in.h>
16 #include <stdint.h>
17 #include <stdio.h>
18 #include <unistd.h>
19
20 #include "test_nlattr.h"
21
22 #include "xlat.h"
23 #define XLAT_MACROS_ONLY
24 # include "xlat/rtnl_nexthop_attrs.h"
25 # include "xlat/rtnl_nha_res_group_attrs.h"
26 # include "xlat/rtnl_nha_res_bucket_attrs.h"
27 #undef XLAT_MACROS_ONLY
28
29 #define DEF_NLATTR_NHMSG_FUNCS(sfx_, af_) \
30 static void \
31 init_##sfx_(struct nlmsghdr *const nlh, const unsigned int msg_len) \
32 { \
33 SET_STRUCT(struct nlmsghdr, nlh, \
34 .nlmsg_len = msg_len, \
35 .nlmsg_type = RTM_GETNEXTHOP, \
36 .nlmsg_flags = NLM_F_DUMP, \
37 ); \
38 \
39 struct nhmsg *const msg = NLMSG_DATA(nlh); \
40 SET_STRUCT(struct nhmsg, msg, \
41 .nh_family = (af_), \
42 .nh_scope = RT_SCOPE_NOWHERE, \
43 .nh_protocol = RTPROT_UNSPEC, \
44 .nh_flags = 0x22, \
45 ); \
46 } \
47 \
48 static void \
49 print_##sfx_(const unsigned int msg_len) \
50 { \
51 printf("{nlmsg_len=%u, nlmsg_type=" XLAT_FMT \
52 ", nlmsg_flags=" XLAT_FMT ", nlmsg_seq=0" \
53 ", nlmsg_pid=0}, {nh_family=" XLAT_FMT \
54 ", nh_scope=" XLAT_FMT ", nh_protocol=" XLAT_FMT \
55 ", nh_flags=" XLAT_FMT "}", \
56 msg_len, XLAT_ARGS(RTM_GETNEXTHOP), \
57 XLAT_ARGS(NLM_F_DUMP), XLAT_SEL(af_, #af_), \
58 XLAT_ARGS(RT_SCOPE_NOWHERE), \
59 XLAT_ARGS(RTPROT_UNSPEC), \
60 XLAT_ARGS(RTNH_F_PERVASIVE|RTNH_F_UNRESOLVED)); \
61 } \
62 /* End of DEF_NLATTR_NHMSG_FUNCS */
63
64 #define DEF_NLATTR_NHMSG_NESTED_FUNCS(sfx_, attr_) \
65 static void \
66 init_nhmsg_##sfx_(struct nlmsghdr *const nlh, \
67 const unsigned int msg_len) \
68 { \
69 init_nhmsg(nlh, msg_len); \
70 struct nlattr *nla = NLMSG_ATTR(nlh, sizeof(struct nhmsg)); \
71 SET_STRUCT(struct nlattr, nla, \
72 .nla_len = msg_len \
73 - NLMSG_SPACE(sizeof(struct nhmsg)), \
74 .nla_type = attr_, \
75 ); \
76 } \
77 \
78 static void \
79 print_nhmsg_##sfx_(const unsigned int msg_len) \
80 { \
81 print_nhmsg(msg_len); \
82 printf(", [{nla_len=%u, nla_type=" XLAT_FMT "}", \
83 (unsigned int) (msg_len - NLMSG_HDRLEN \
84 - NLMSG_ALIGN(sizeof(struct nhmsg))), \
85 XLAT_SEL(attr_, #attr_)); \
86 } \
87 /* End of DEF_NLATTR_NHMSG_NESTED_FUNCS */
88
89 DEF_NLATTR_NHMSG_FUNCS(nhmsg, AF_UNIX)
90 DEF_NLATTR_NHMSG_FUNCS(nhmsg_inet, AF_INET)
91 DEF_NLATTR_NHMSG_FUNCS(nhmsg_inet6, AF_INET6)
92
93 DEF_NLATTR_NHMSG_NESTED_FUNCS(res_grp, NHA_RES_GROUP)
94 DEF_NLATTR_NHMSG_NESTED_FUNCS(res_bkt, NHA_RES_BUCKET)
95
96 static void
97 print_nh_grp(const struct nexthop_grp *const elem, size_t idx)
98 {
99 switch (idx) {
100 case 0: printf("{id=3735928559, weight=0}"); break;
101 case 1: printf("{id=0, weight=218, resvd2=0xdead}"); break;
102 case 2: printf("{id=4207869677, weight=190, resvd1=0xec}"); break;
103 case 3: printf("{id=0, weight=0, resvd1=0xca, resvd2=0xbeef}"); break;
104 default: error_msg_and_fail("Unexpected grp index: %zu", idx);
105 }
106 }
107
108 int
109 main(void)
110 {
111 skip_if_unavailable("/proc/self/fd/");
112
113 const int fd = create_nl_socket(NETLINK_ROUTE);
114 const unsigned int hdrlen = sizeof(struct nhmsg);
115 char nla_type_str[256];
116 void *nlh0 = midtail_alloc(NLMSG_SPACE(hdrlen),
117 NLA_HDRLEN + 42);
118
119 static char pattern[4096];
120 fill_memory_ex(pattern, sizeof(pattern), 'a', 'z' - 'a' + 1);
121
122
123 /* Unknown attrs */
124 static const uint16_t unk_types[] = { 14, 0xffff & NLA_TYPE_MASK };
125 for (size_t i = 0; i < ARRAY_SIZE(unk_types); i++) {
126 sprintf(nla_type_str, "%#x" NRAW(" /* NHA_??? */"), unk_types[i]);
127 TEST_NLATTR_(fd, nlh0, hdrlen,
128 init_nhmsg, print_nhmsg,
129 unk_types[i], nla_type_str,
130 4, pattern, 4,
131 print_quoted_hex(pattern, 4));
132 }
133
134
135 /* unimplemented, no semantics: NHA_UNSPEC, NHA_ENCAP */
136 static const struct strval32 unimp_types[] = {
137 { ARG_XLAT_KNOWN(0, "NHA_UNSPEC") },
138 { ARG_XLAT_KNOWN(0x8, "NHA_ENCAP") } };
139 for (size_t i = 0; i < ARRAY_SIZE(unimp_types); i++) {
140 TEST_NLATTR_(fd, nlh0, hdrlen,
141 init_nhmsg, print_nhmsg,
142 unimp_types[i].val, unimp_types[i].str,
143 42, pattern, 42,
144 print_quoted_hex(pattern, 32);
145 printf("..."));
146 }
147
148
149 /* u32 attrs: NHA_ID, NHA_BLACKHOLE, NHA_GROUPS, NHA_FDB */
150 static const struct strval32 u32_attrs[] = {
151 { ARG_XLAT_KNOWN(0x1, "NHA_ID") },
152 { ARG_XLAT_KNOWN(0x4, "NHA_BLACKHOLE") },
153 { ARG_XLAT_KNOWN(0x9, "NHA_GROUPS") },
154 { ARG_XLAT_KNOWN(0xb, "NHA_FDB") },
155 };
156 for (size_t i = 0; i < ARRAY_SIZE(u32_attrs); i++) {
157 check_u32_nlattr(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg,
158 u32_attrs[i].val, u32_attrs[i].str, pattern,
159 0);
160 }
161
162
163 /* NHA_GROUP */
164 static const struct nexthop_grp grps[] = {
165 { .id = 0xdeadbeef, .weight = 0, .resvd1 = 0, .resvd2 = 0 },
166 { .id = 0, .weight = 218, .resvd1 = 0, .resvd2 = 0xdead },
167 { .id = 0xfacefeed, .weight = 190, .resvd1 = 236, .resvd2 = 0 },
168 { .id = 0, .weight = 0, .resvd1 = 202, .resvd2 = 0xbeef },
169 };
170 TEST_NLATTR_ARRAY_(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg,
171 NHA_GROUP, XLAT_KNOWN(0x2, "NHA_GROUP"),
172 pattern, grps, print_nh_grp);
173
174
175 /* NHA_GROUP_TYPE */
176 static const struct strval16 grp_types[] = {
177 { ARG_XLAT_KNOWN(0, "NEXTHOP_GRP_TYPE_MPATH") },
178 { ARG_XLAT_KNOWN(0x1, "NEXTHOP_GRP_TYPE_RES") },
179 { ARG_XLAT_UNKNOWN(0x2, "NEXTHOP_GRP_TYPE_???") },
180 { ARG_XLAT_UNKNOWN(0xbeef, "NEXTHOP_GRP_TYPE_???") },
181 };
182 for (size_t i = 0; i < ARRAY_SIZE(grp_types); i++) {
183 TEST_NLATTR_OBJECT_(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg,
184 NHA_GROUP_TYPE,
185 XLAT_KNOWN(0x3, "NHA_GROUP_TYPE"),
186 pattern, grp_types[i].val,
187 printf("%s", grp_types[i].str));
188 TEST_NLATTR_(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg,
189 NHA_GROUP_TYPE, XLAT_KNOWN(0x3, "NHA_GROUP_TYPE"),
190 sizeof(grp_types[i].val) + 4,
191 &grp_types[i].val, sizeof(grp_types[i].val),
192 printf("%s", grp_types[i].str));
193 }
194
195
196 /* ifindex: NHA_OIF, NHA_MASTER */
197 static const struct strval32 if_attrs[] = {
198 { ARG_XLAT_KNOWN(0x5, "NHA_OIF") },
199 { ARG_XLAT_KNOWN(0xa, "NHA_MASTER") },
200 };
201 const uint32_t ifindex = ifindex_lo();
202 for (size_t i = 0; i < ARRAY_SIZE(if_attrs); i++) {
203 static const uint32_t bogus = 0xdeadc0de;
204 TEST_NLATTR_OBJECT_(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg,
205 if_attrs[i].val, if_attrs[i].str,
206 pattern, bogus,
207 printf("3735929054"));
208 TEST_NLATTR_(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg,
209 if_attrs[i].val, if_attrs[i].str,
210 sizeof(bogus) + 4, &bogus, sizeof(bogus),
211 printf("3735929054"));
212
213 TEST_NLATTR_OBJECT_(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg,
214 if_attrs[i].val, if_attrs[i].str,
215 pattern, ifindex,
216 printf(XLAT_FMT_U,
217 XLAT_SEL(ifindex, IFINDEX_LO_STR)));
218 TEST_NLATTR_(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg,
219 if_attrs[i].val, if_attrs[i].str,
220 sizeof(ifindex) + 4, &ifindex, sizeof(ifindex),
221 printf(XLAT_FMT_U,
222 XLAT_SEL(ifindex, IFINDEX_LO_STR)));
223 }
224
225
226 /* NHA_GATEWAY */
227 static const struct {
228 uint8_t af;
229 uint8_t addr[16];
230 const char *str;
231 void (* init_fn)(struct nlmsghdr *, unsigned int);
232 void (* print_fn)(unsigned int);
233 uint32_t len;
234 } addrs[] = {
235 { AF_UNIX, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
236 "\"\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\"",
237 init_nhmsg, print_nhmsg, 10 },
238 { AF_INET, { 0xde, 0xca, 0xff, 0xed },
239 "inet_addr(\"222.202.255.237\")",
240 init_nhmsg_inet, print_nhmsg_inet, 4 },
241 { AF_INET6, { 0xfa, 0xce, 0xbe, 0xef, [15] = 0xda },
242 "inet_pton(AF_INET6, \"face:beef::da\")",
243 init_nhmsg_inet6, print_nhmsg_inet6, 16 },
244 };
245 static const struct strval32 addr_attrs[] = {
246 { ARG_XLAT_KNOWN(0x6, "NHA_GATEWAY") },
247 };
248 for (size_t i = 0; i < ARRAY_SIZE(addrs); i++) {
249 for (size_t j = 0; j < ARRAY_SIZE(addr_attrs); j++) {
250 TEST_NLATTR_(fd, nlh0, hdrlen,
251 addrs[i].init_fn, addrs[i].print_fn,
252 addr_attrs[j].val, addr_attrs[j].str,
253 addrs[i].len - 1, addrs[i].addr,
254 addrs[i].len - 1,
255 print_quoted_hex(addrs[i].addr,
256 addrs[i].len - 1)
257 );
258 TEST_NLATTR_(fd, nlh0, hdrlen,
259 addrs[i].init_fn, addrs[i].print_fn,
260 addr_attrs[j].val, addr_attrs[j].str,
261 addrs[i].len, addrs[i].addr, addrs[i].len,
262 #if XLAT_RAW || XLAT_VERBOSE
263 print_quoted_hex(addrs[i].addr,
264 addrs[i].len);
265 #endif
266 #if !XLAT_RAW
267 if (!(XLAT_VERBOSE
268 && addrs[i].af == AF_UNIX))
269 printf(VERB(" /* ") "%s"
270 VERB(" */"), addrs[i].str);
271 #endif
272 );
273 }
274 }
275
276
277 /* NHA_ENCAP_TYPE */
278 static const struct strval16 enc_types[] = {
279 { ARG_XLAT_KNOWN(0, "LWTUNNEL_ENCAP_NONE") },
280 { ARG_XLAT_KNOWN(0x8, "LWTUNNEL_ENCAP_RPL") },
281 { ARG_XLAT_UNKNOWN(0x9, "LWTUNNEL_ENCAP_???") },
282 { ARG_XLAT_UNKNOWN(0xbeef, "LWTUNNEL_ENCAP_???") },
283 };
284 for (size_t i = 0; i < ARRAY_SIZE(enc_types); i++) {
285 TEST_NLATTR_OBJECT_(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg,
286 NHA_ENCAP_TYPE,
287 XLAT_KNOWN(0x7, "NHA_ENCAP_TYPE"),
288 pattern, enc_types[i].val,
289 printf("%s", enc_types[i].str));
290 TEST_NLATTR_(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg,
291 NHA_ENCAP_TYPE, XLAT_KNOWN(0x7, "NHA_ENCAP_TYPE"),
292 sizeof(enc_types[i].val) + 4,
293 &enc_types[i].val, sizeof(enc_types[i].val),
294 printf("%s", enc_types[i].str));
295 }
296
297
298 /* NHA_RES_GROUP */
299 static const unsigned int res_grp_hdrlen =
300 sizeof(struct nhmsg) + sizeof(struct nlattr);
301 void *nlh1 = midtail_alloc(NLMSG_SPACE(res_grp_hdrlen),
302 NLA_HDRLEN + 16);
303
304 TEST_NLATTR_(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg,
305 NHA_RES_GROUP, XLAT_KNOWN(0xc, "NHA_RES_GROUP"),
306 3, pattern, 3,
307 print_quoted_hex(pattern, 3));
308
309 /* unknown NHA_RES_GROUP_* attr */
310 static const uint16_t unk_res_grp_types[] = {
311 5, 0xffff & NLA_TYPE_MASK,
312 };
313 for (size_t i = 0; i < ARRAY_SIZE(unk_res_grp_types); i++) {
314 sprintf(nla_type_str, "%#x" NRAW(" /* NHA_RES_GROUP_??? */"),
315 unk_res_grp_types[i]);
316 TEST_NLATTR_(fd, nlh1, res_grp_hdrlen,
317 init_nhmsg_res_grp, print_nhmsg_res_grp,
318 unk_res_grp_types[i], nla_type_str,
319 16, pattern, 16,
320 print_quoted_hex(pattern, 16);
321 printf("]"));
322 }
323
324 /* not decoded: NHA_RES_GROUP_UNSPEC/NHA_RES_GROUP_PAD */
325 TEST_NLATTR_(fd, nlh1, res_grp_hdrlen,
326 init_nhmsg_res_grp, print_nhmsg_res_grp,
327 NHA_RES_GROUP_PAD, XLAT_KNOWN(0, "NHA_RES_GROUP_PAD"),
328 8, pattern, 8,
329 print_quoted_hex(pattern, 8);
330 printf("]"));
331
332 /* u16: NHA_RES_GROUP_BUCKETS */
333 check_u16_nlattr(fd, nlh0, hdrlen,
334 init_nhmsg_res_grp, print_nhmsg_res_grp,
335 ARG_XLAT_KNOWN(0x1, "NHA_RES_GROUP_BUCKETS"),
336 pattern, 1);
337
338 /* clock_t: NHA_RES_GROUP_IDLE_TIMER, NHA_RES_GROUP_UNBALANCED_TIMER,
339 * NHA_RES_GROUP_UNBALANCED_TIME */
340 static const struct strval32 res_grp_clk_attrs[] = {
341 { ARG_XLAT_KNOWN(0x2, "NHA_RES_GROUP_IDLE_TIMER") },
342 { ARG_XLAT_KNOWN(0x3, "NHA_RES_GROUP_UNBALANCED_TIMER") },
343 { ARG_XLAT_KNOWN(0x4, "NHA_RES_GROUP_UNBALANCED_TIME") },
344 };
345 for (size_t i = 0; i < ARRAY_SIZE(res_grp_clk_attrs); i++) {
346 check_clock_t_nlattr(fd, nlh0, hdrlen,
347 init_nhmsg_res_grp, print_nhmsg_res_grp,
348 res_grp_clk_attrs[i].val,
349 res_grp_clk_attrs[i].str, 1);
350 }
351
352
353 /* NHA_RES_BUCKET */
354 static const unsigned int res_bkt_hdrlen =
355 sizeof(struct nhmsg) + sizeof(struct nlattr);
356
357 TEST_NLATTR_(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg,
358 NHA_RES_BUCKET, XLAT_KNOWN(0xd, "NHA_RES_BUCKET"),
359 3, pattern, 3,
360 print_quoted_hex(pattern, 3));
361
362 /* unknown NHA_RES_GROUP_* attr */
363 static const uint16_t unk_res_bkt_types[] = {
364 4, 0xffff & NLA_TYPE_MASK,
365 };
366 for (size_t i = 0; i < ARRAY_SIZE(unk_res_bkt_types); i++) {
367 sprintf(nla_type_str, "%#x" NRAW(" /* NHA_RES_BUCKET_??? */"),
368 unk_res_bkt_types[i]);
369 TEST_NLATTR_(fd, nlh1, res_bkt_hdrlen,
370 init_nhmsg_res_bkt, print_nhmsg_res_bkt,
371 unk_res_bkt_types[i], nla_type_str,
372 16, pattern, 16,
373 print_quoted_hex(pattern, 16);
374 printf("]"));
375 }
376
377 /* not decoded: NHA_RES_BUCKET_UNSPEC/NHA_RES_BUCKET_PAD */
378 TEST_NLATTR_(fd, nlh1, res_bkt_hdrlen,
379 init_nhmsg_res_bkt, print_nhmsg_res_bkt,
380 NHA_RES_BUCKET_PAD, XLAT_KNOWN(0, "NHA_RES_BUCKET_PAD"),
381 8, pattern, 8,
382 print_quoted_hex(pattern, 8);
383 printf("]"));
384
385 /* u16: NHA_RES_BUCKET_INDEX */
386 check_u16_nlattr(fd, nlh0, hdrlen,
387 init_nhmsg_res_bkt, print_nhmsg_res_bkt,
388 ARG_XLAT_KNOWN(0x1, "NHA_RES_BUCKET_INDEX"),
389 pattern, 1);
390
391 /* clock_t: NHA_RES_BUCKET_IDLE_TIME */
392 static const struct strval32 res_bkt_clk_attrs[] = {
393 { ARG_XLAT_KNOWN(0x2, "NHA_RES_BUCKET_IDLE_TIME") },
394 };
395 for (size_t i = 0; i < ARRAY_SIZE(res_bkt_clk_attrs); i++) {
396 check_clock_t_nlattr(fd, nlh0, hdrlen,
397 init_nhmsg_res_bkt, print_nhmsg_res_bkt,
398 res_bkt_clk_attrs[i].val,
399 res_bkt_clk_attrs[i].str, 1);
400 }
401
402 /* u32: NHA_RES_BUCKET_NH_ID */
403 check_u32_nlattr(fd, nlh0, hdrlen,
404 init_nhmsg_res_bkt, print_nhmsg_res_bkt,
405 ARG_XLAT_KNOWN(0x3, "NHA_RES_BUCKET_NH_ID"),
406 pattern, 1);
407
408
409 puts("+++ exited with 0 +++");
410 return 0;
411 }