1 /*
2 * Copyright (c) 2017 JingPiao Chen <chenjingpiao@gmail.com>
3 * Copyright (c) 2017-2022 The strace developers.
4 * All rights reserved.
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 #include "tests.h"
10 #include <stdio.h>
11 #include <stdint.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <sys/socket.h>
15 #include "test_netlink.h"
16 #include <linux/dcbnl.h>
17 #include <linux/fib_rules.h>
18 #include <linux/if_addr.h>
19 #include <linux/if_addrlabel.h>
20 #include <linux/if_arp.h>
21 #include <linux/if_bridge.h>
22 #include <linux/if_link.h>
23 #include <linux/ip.h>
24 #include <linux/neighbour.h>
25 #include <linux/netconf.h>
26 #include <linux/rtnetlink.h>
27 #include <linux/nexthop.h>
28
29 #define TEST_NL_ROUTE_(fd_, nlh0_, type_, type_str_, obj_, print_family_, ...) \
30 do { \
31 /* family and string */ \
32 TEST_NETLINK_((fd_), (nlh0_), \
33 (type_), (type_str_), \
34 NLM_F_REQUEST, "NLM_F_REQUEST", \
35 sizeof(obj_) - 1, \
36 &(obj_), sizeof(obj_) - 1, \
37 (print_family_); \
38 printf(", ...}")); \
39 \
40 /* sizeof(obj_) */ \
41 TEST_NETLINK_((fd_), (nlh0_), \
42 (type_), (type_str_), \
43 NLM_F_REQUEST, "NLM_F_REQUEST", \
44 sizeof(obj_), &(obj_), sizeof(obj_), \
45 (print_family_); \
46 __VA_ARGS__); \
47 \
48 /* short read of sizeof(obj_) */ \
49 TEST_NETLINK_((fd_), (nlh0_), \
50 (type_), (type_str_), \
51 NLM_F_REQUEST, "NLM_F_REQUEST", \
52 sizeof(obj_), &(obj_), sizeof(obj_) - 1, \
53 (print_family_); \
54 printf(", %p}", \
55 NLMSG_DATA(TEST_NETLINK_nlh) + 1)); \
56 } while (0)
57
58 #define TEST_NL_ROUTE(fd_, nlh0_, type_, obj_, print_family_, ...) \
59 TEST_NL_ROUTE_((fd_), (nlh0_), (type_), #type_, (obj_), \
60 (print_family_), __VA_ARGS__) \
61 /* End of TEST_NL_ROUTE definition */
62
63 static void
64 test_nlmsg_type(const int fd)
65 {
66 long rc;
67 struct nlmsghdr nlh = {
68 .nlmsg_len = sizeof(nlh),
69 .nlmsg_type = RTM_GETLINK,
70 .nlmsg_flags = NLM_F_REQUEST,
71 };
72
73 rc = sendto(fd, &nlh, sizeof(nlh), MSG_DONTWAIT, NULL, 0);
74 printf("sendto(%d, {nlmsg_len=%u, nlmsg_type=RTM_GETLINK"
75 ", nlmsg_flags=NLM_F_REQUEST, nlmsg_seq=0, nlmsg_pid=0}"
76 ", %u, MSG_DONTWAIT, NULL, 0) = %s\n",
77 fd, nlh.nlmsg_len, (unsigned) sizeof(nlh), sprintrc(rc));
78 }
79
80 static void
81 test_nlmsg_flags(const int fd)
82 {
83 long rc;
84 struct nlmsghdr nlh = {
85 .nlmsg_len = sizeof(nlh),
86 };
87
88 nlh.nlmsg_type = RTM_GETLINK;
89 nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
90 rc = sendto(fd, &nlh, sizeof(nlh), MSG_DONTWAIT, NULL, 0);
91 printf("sendto(%d, {nlmsg_len=%u, nlmsg_type=RTM_GETLINK"
92 ", nlmsg_flags=NLM_F_REQUEST|NLM_F_DUMP, nlmsg_seq=0"
93 ", nlmsg_pid=0}, %u, MSG_DONTWAIT, NULL, 0) = %s\n",
94 fd, nlh.nlmsg_len, (unsigned) sizeof(nlh), sprintrc(rc));
95
96 nlh.nlmsg_type = RTM_DELACTION;
97 nlh.nlmsg_flags = NLM_F_ROOT;
98 rc = sendto(fd, &nlh, sizeof(nlh), MSG_DONTWAIT, NULL, 0);
99 printf("sendto(%d, {nlmsg_len=%u, nlmsg_type=RTM_DELACTION"
100 ", nlmsg_flags=NLM_F_ROOT, nlmsg_seq=0, nlmsg_pid=0}"
101 ", %u, MSG_DONTWAIT, NULL, 0) = %s\n",
102 fd, nlh.nlmsg_len, (unsigned) sizeof(nlh), sprintrc(rc));
103
104 nlh.nlmsg_type = RTM_NEWLINK;
105 nlh.nlmsg_flags = NLM_F_ECHO | NLM_F_REPLACE;
106 rc = sendto(fd, &nlh, sizeof(nlh), MSG_DONTWAIT, NULL, 0);
107 printf("sendto(%d, {nlmsg_len=%u, nlmsg_type=RTM_NEWLINK"
108 ", nlmsg_flags=NLM_F_ECHO|NLM_F_REPLACE, nlmsg_seq=0"
109 ", nlmsg_pid=0}, %u, MSG_DONTWAIT, NULL, 0) = %s\n",
110 fd, nlh.nlmsg_len, (unsigned) sizeof(nlh), sprintrc(rc));
111
112 nlh.nlmsg_type = RTM_DELLINK;
113 nlh.nlmsg_flags = NLM_F_NONREC | NLM_F_BULK;
114 rc = sendto(fd, &nlh, sizeof(nlh), MSG_DONTWAIT, NULL, 0);
115 printf("sendto(%d, {nlmsg_len=%u, nlmsg_type=RTM_DELLINK"
116 ", nlmsg_flags=NLM_F_NONREC|NLM_F_BULK, nlmsg_seq=0"
117 ", nlmsg_pid=0}, %u, MSG_DONTWAIT, NULL, 0) = %s\n",
118 fd, nlh.nlmsg_len, (unsigned) sizeof(nlh), sprintrc(rc));
119 }
120
121 static void
122 test_nlmsg_done(const int fd)
123 {
124 const int num = 0xabcdefad;
125 void *const nlh0 = midtail_alloc(NLMSG_HDRLEN, sizeof(num));
126
127 TEST_NETLINK(fd, nlh0, NLMSG_DONE, NLM_F_REQUEST,
128 sizeof(num), &num, sizeof(num),
129 printf("%d", num));
130 }
131
132 static void
133 test_rtnl_unspec(const int fd)
134 {
135 uint8_t family = 0;
136 char buf[sizeof(family) + 4];
137 void *const nlh0 = midtail_alloc(NLMSG_HDRLEN, sizeof(buf));
138
139 /* unspecified family only */
140 TEST_NETLINK_(fd, nlh0,
141 0xffff, "0xffff /* RTM_??? */",
142 NLM_F_REQUEST, "NLM_F_REQUEST",
143 sizeof(family), &family, sizeof(family),
144 printf("{family=AF_UNSPEC}"));
145
146 /* unknown family only */
147 family = 0xff;
148 TEST_NETLINK_(fd, nlh0,
149 0xffff, "0xffff /* RTM_??? */",
150 NLM_F_REQUEST, "NLM_F_REQUEST",
151 sizeof(family), &family, sizeof(family),
152 printf("{family=0xff /* AF_??? */}"));
153
154 /* short read of family */
155 TEST_NETLINK_(fd, nlh0,
156 0xffff, "0xffff /* RTM_??? */",
157 NLM_F_REQUEST, "NLM_F_REQUEST",
158 sizeof(family), &family, sizeof(family) - 1,
159 printf("%p", NLMSG_DATA(TEST_NETLINK_nlh)));
160
161 /* unspecified family and string */
162 family = 0;
163 memcpy(buf, &family, sizeof(family));
164 memcpy(buf + sizeof(family), "1234", 4);
165 TEST_NETLINK_(fd, nlh0,
166 0xffff, "0xffff /* RTM_??? */",
167 NLM_F_REQUEST, "NLM_F_REQUEST",
168 sizeof(buf), buf, sizeof(buf),
169 printf("{family=AF_UNSPEC, data=\"\\x31\\x32\\x33\\x34\"}"));
170
171 /* unknown family and string */
172 family = 0xfd;
173 memcpy(buf, &family, sizeof(family));
174 TEST_NETLINK_(fd, nlh0,
175 0xffff, "0xffff /* RTM_??? */",
176 NLM_F_REQUEST, "NLM_F_REQUEST",
177 sizeof(buf), buf, sizeof(buf),
178 printf("{family=%#x /* AF_??? */"
179 ", data=\"\\x31\\x32\\x33\\x34\"}", family));
180 }
181
182 static void
183 test_rtnl_unsupported_msg(const int fd, uint16_t msg, const char *str)
184 {
185 char buf[64];
186 char name[sizeof("0xffff /* RTM_??? */")];
187 void *const nlh0 = midtail_alloc(NLMSG_HDRLEN, sizeof(buf));
188
189 fill_memory(buf, sizeof(buf));
190 buf[0] = AF_INET;
191
192 if (!str)
193 snprintf(name, sizeof(name), "%#hx /* RTM_??? */", msg);
194
195 TEST_NETLINK_(fd, nlh0, msg, str ?: name,
196 NLM_F_REQUEST, "NLM_F_REQUEST",
197 1, buf, 1,
198 printf("{family=AF_INET}"));
199
200 TEST_NETLINK_(fd, nlh0, msg, str ?: name,
201 NLM_F_REQUEST, "NLM_F_REQUEST",
202 sizeof(buf), buf, sizeof(buf),
203 printf("{family=AF_INET, data=");
204 print_quoted_hex(buf + 1, DEFAULT_STRLEN);
205 printf("...}"));
206 }
207
208 static void
209 test_rtnl_unknown_msg(const int fd, uint16_t msg)
210 {
211 test_rtnl_unsupported_msg(fd, msg, NULL);
212 }
213
214 static void
215 test_rtnl_link(const int fd)
216 {
217 static const struct strval32 types[] = {
218 { ARG_STR(RTM_NEWLINK) },
219 { ARG_STR(RTM_DELLINK) },
220 { ARG_STR(RTM_GETLINK) },
221 { ARG_STR(RTM_SETLINK) },
222 };
223 const struct ifinfomsg ifinfo = {
224 .ifi_family = AF_UNIX,
225 .ifi_type = ARPHRD_LOOPBACK,
226 .ifi_index = ifindex_lo(),
227 .ifi_flags = IFF_UP,
228 .ifi_change = 0xfabcdeba
229 };
230 void *const nlh0 = midtail_alloc(NLMSG_HDRLEN, sizeof(ifinfo));
231
232 for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
233 TEST_NL_ROUTE_(fd, nlh0, types[i].val, types[i].str, ifinfo,
234 printf("{ifi_family=AF_UNIX"),
235 printf(", ifi_type=ARPHRD_LOOPBACK"
236 ", ifi_index=" IFINDEX_LO_STR
237 ", ifi_flags=IFF_UP");
238 printf(", ");
239 PRINT_FIELD_X(ifinfo, ifi_change);
240 printf("}"));
241 }
242 }
243
244 static void
245 test_rtnl_addr(const int fd)
246 {
247 static const struct strval32 types[] = {
248 { ARG_STR(RTM_NEWADDR) },
249 { ARG_STR(RTM_DELADDR) },
250 { ARG_STR(RTM_GETADDR) },
251 { ARG_STR(RTM_GETMULTICAST) },
252 { ARG_STR(RTM_GETANYCAST) },
253 };
254 const struct ifaddrmsg msg = {
255 .ifa_family = AF_UNIX,
256 .ifa_prefixlen = 0xde,
257 .ifa_flags = IFA_F_SECONDARY,
258 .ifa_scope = RT_SCOPE_UNIVERSE,
259 .ifa_index = ifindex_lo()
260 };
261 void *const nlh0 = midtail_alloc(NLMSG_HDRLEN, sizeof(msg));
262
263 for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
264 TEST_NL_ROUTE_(fd, nlh0, types[i].val, types[i].str, msg,
265 printf("{ifa_family=AF_UNIX"),
266 printf(", ");
267 PRINT_FIELD_U(msg, ifa_prefixlen);
268 printf(", ifa_flags=IFA_F_SECONDARY"
269 ", ifa_scope=RT_SCOPE_UNIVERSE"
270 ", ifa_index=" IFINDEX_LO_STR);
271 printf("}"));
272 }
273
274 test_rtnl_unknown_msg(fd, RTM_NEWADDR + 3);
275 test_rtnl_unknown_msg(fd, RTM_GETMULTICAST - 2);
276 test_rtnl_unknown_msg(fd, RTM_GETMULTICAST - 1);
277 test_rtnl_unknown_msg(fd, RTM_GETMULTICAST + 1);
278 test_rtnl_unknown_msg(fd, RTM_GETANYCAST - 2);
279 test_rtnl_unknown_msg(fd, RTM_GETANYCAST - 1);
280 test_rtnl_unknown_msg(fd, RTM_GETANYCAST + 1);
281 }
282
283 static void
284 test_rtnl_route(const int fd)
285 {
286 static const struct strval32 types[] = {
287 { ARG_STR(RTM_NEWROUTE) },
288 { ARG_STR(RTM_DELROUTE) },
289 { ARG_STR(RTM_GETROUTE) },
290 };
291 static const struct rtmsg msg = {
292 .rtm_family = AF_UNIX,
293 .rtm_dst_len = 0xaf,
294 .rtm_src_len = 0xda,
295 .rtm_tos = IPTOS_LOWDELAY,
296 .rtm_table = RT_TABLE_DEFAULT,
297 .rtm_protocol = RTPROT_KERNEL,
298 .rtm_scope = RT_SCOPE_UNIVERSE,
299 .rtm_type = RTN_LOCAL,
300 .rtm_flags = RTM_F_NOTIFY
301 };
302 void *const nlh0 = midtail_alloc(NLMSG_HDRLEN, sizeof(msg));
303
304 for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
305 TEST_NL_ROUTE_(fd, nlh0, types[i].val, types[i].str, msg,
306 printf("{rtm_family=AF_UNIX"),
307 printf(", ");
308 PRINT_FIELD_U(msg, rtm_dst_len);
309 printf(", ");
310 PRINT_FIELD_U(msg, rtm_src_len);
311 printf(", rtm_tos=IPTOS_LOWDELAY"
312 ", rtm_table=RT_TABLE_DEFAULT"
313 ", rtm_protocol=RTPROT_KERNEL"
314 ", rtm_scope=RT_SCOPE_UNIVERSE"
315 ", rtm_type=RTN_LOCAL"
316 ", rtm_flags=RTM_F_NOTIFY}"));
317 }
318
319 test_rtnl_unknown_msg(fd, RTM_NEWROUTE + 3);
320 }
321
322 static void
323 test_rtnl_rule(const int fd)
324 {
325 static const struct strval32 types[] = {
326 { ARG_STR(RTM_NEWRULE) },
327 { ARG_STR(RTM_DELRULE) },
328 { ARG_STR(RTM_GETRULE) },
329 };
330 struct rtmsg msg = {
331 .rtm_family = AF_UNIX,
332 .rtm_dst_len = 0xaf,
333 .rtm_src_len = 0xda,
334 .rtm_tos = IPTOS_LOWDELAY,
335 .rtm_table = RT_TABLE_UNSPEC,
336 .rtm_type = FR_ACT_TO_TBL,
337 .rtm_flags = FIB_RULE_INVERT
338 };
339 void *const nlh0 = midtail_alloc(NLMSG_HDRLEN, sizeof(msg));
340
341 for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
342 TEST_NL_ROUTE_(fd, nlh0, types[i].val, types[i].str, msg,
343 printf("{family=AF_UNIX"),
344 printf(", dst_len=%u, src_len=%u"
345 ", tos=IPTOS_LOWDELAY"
346 ", table=RT_TABLE_UNSPEC"
347 ", action=FR_ACT_TO_TBL"
348 ", flags=FIB_RULE_INVERT}",
349 msg.rtm_dst_len,
350 msg.rtm_src_len));
351 }
352
353 test_rtnl_unknown_msg(fd, RTM_NEWRULE + 3);
354 }
355
356 static void
357 test_rtnl_neigh(const int fd)
358 {
359 static const struct strval32 types[] = {
360 { ARG_STR(RTM_NEWNEIGH) },
361 { ARG_STR(RTM_DELNEIGH) },
362 { ARG_STR(RTM_GETNEIGH) },
363 };
364 const struct ndmsg msg = {
365 .ndm_family = AF_UNIX,
366 .ndm_ifindex = ifindex_lo(),
367 .ndm_state = NUD_PERMANENT,
368 .ndm_flags = NTF_PROXY,
369 .ndm_type = RTN_UNSPEC
370 };
371 void *const nlh0 = midtail_alloc(NLMSG_HDRLEN, sizeof(msg));
372
373 for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
374 TEST_NL_ROUTE_(fd, nlh0, types[i].val, types[i].str, msg,
375 printf("{ndm_family=AF_UNIX"),
376 printf(", ndm_ifindex=" IFINDEX_LO_STR
377 ", ndm_state=NUD_PERMANENT"
378 ", ndm_flags=NTF_PROXY"
379 ", ndm_type=RTN_UNSPEC}"));
380 }
381
382 test_rtnl_unknown_msg(fd, RTM_NEWNEIGH + 3);
383 }
384
385 static void
386 test_rtnl_neightbl(const int fd)
387 {
388 static const struct strval32 types[] = {
389 { ARG_STR(RTM_NEWNEIGHTBL) },
390 { ARG_STR(RTM_GETNEIGHTBL) },
391 { ARG_STR(RTM_SETNEIGHTBL) },
392 };
393 static const struct ndtmsg msg = {
394 .ndtm_family = AF_NETLINK
395 };
396 void *const nlh0 = midtail_alloc(NLMSG_HDRLEN, sizeof(msg));
397
398 for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
399 TEST_NETLINK_(fd, nlh0, types[i].val, types[i].str,
400 NLM_F_REQUEST, "NLM_F_REQUEST",
401 sizeof(msg), &msg, sizeof(msg),
402 printf("{ndtm_family=AF_NETLINK}"));
403 }
404
405 test_rtnl_unknown_msg(fd, RTM_NEWNEIGHTBL + 1);
406 }
407
408 static void
409 test_rtnl_tc(const int fd)
410 {
411 static const struct strval32 types[] = {
412 { ARG_STR(RTM_NEWQDISC) },
413 { ARG_STR(RTM_DELQDISC) },
414 { ARG_STR(RTM_GETQDISC) },
415 { ARG_STR(RTM_NEWTCLASS) },
416 { ARG_STR(RTM_DELTCLASS) },
417 { ARG_STR(RTM_GETTCLASS) },
418 { ARG_STR(RTM_NEWTFILTER) },
419 { ARG_STR(RTM_DELTFILTER) },
420 { ARG_STR(RTM_GETTFILTER) },
421 { ARG_STR(RTM_NEWCHAIN) },
422 { ARG_STR(RTM_DELCHAIN) },
423 { ARG_STR(RTM_GETCHAIN) },
424 };
425 const struct tcmsg msg = {
426 .tcm_family = AF_UNIX,
427 .tcm_ifindex = ifindex_lo(),
428 .tcm_handle = 0xfadcdafb,
429 .tcm_parent = 0xafbcadab,
430 .tcm_info = 0xbcaedafa
431 };
432 void *const nlh0 = midtail_alloc(NLMSG_HDRLEN, sizeof(msg));
433
434 for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
435 TEST_NL_ROUTE_(fd, nlh0, types[i].val, types[i].str, msg,
436 printf("{tcm_family=AF_UNIX"),
437 printf(", tcm_ifindex=" IFINDEX_LO_STR);
438 printf(", ");
439 PRINT_FIELD_U(msg, tcm_handle);
440 printf(", ");
441 PRINT_FIELD_U(msg, tcm_parent);
442 printf(", ");
443 PRINT_FIELD_U(msg, tcm_info);
444 printf("}"));
445 }
446
447 test_rtnl_unknown_msg(fd, RTM_NEWQDISC + 3);
448 test_rtnl_unknown_msg(fd, RTM_NEWTCLASS + 3);
449 test_rtnl_unknown_msg(fd, RTM_NEWTFILTER + 3);
450 test_rtnl_unknown_msg(fd, RTM_NEWCHAIN + 3);
451 }
452
453 static void
454 test_rtnl_tca(const int fd)
455 {
456 static const struct strval32 types[] = {
457 { ARG_STR(RTM_NEWACTION) },
458 { ARG_STR(RTM_DELACTION) },
459 { ARG_STR(RTM_GETACTION) },
460 };
461 struct tcamsg msg = {
462 .tca_family = AF_INET
463 };
464 void *const nlh0 = midtail_alloc(NLMSG_HDRLEN, sizeof(msg));
465
466 for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
467 TEST_NETLINK_(fd, nlh0, types[i].val, types[i].str,
468 NLM_F_REQUEST, "NLM_F_REQUEST",
469 sizeof(msg), &msg, sizeof(msg),
470 printf("{tca_family=AF_INET}"));
471 }
472
473 test_rtnl_unknown_msg(fd, RTM_NEWACTION + 3);
474 }
475
476 static void
477 test_rtnl_addrlabel(const int fd)
478 {
479 static const struct strval32 types[] = {
480 { ARG_STR(RTM_NEWADDRLABEL) },
481 { ARG_STR(RTM_DELADDRLABEL) },
482 { ARG_STR(RTM_GETADDRLABEL) },
483 };
484 const struct ifaddrlblmsg msg = {
485 .ifal_family = AF_UNIX,
486 .ifal_prefixlen = 0xaf,
487 .ifal_flags = 0xbd,
488 .ifal_index = ifindex_lo(),
489 .ifal_seq = 0xfadcdafb
490 };
491 void *const nlh0 = midtail_alloc(NLMSG_HDRLEN, sizeof(msg));
492
493 for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
494 TEST_NL_ROUTE_(fd, nlh0, types[i].val, types[i].str, msg,
495 printf("{ifal_family=AF_UNIX"),
496 printf(", ");
497 PRINT_FIELD_U(msg, ifal_prefixlen);
498 printf(", ");
499 PRINT_FIELD_U(msg, ifal_flags);
500 printf(", ifal_index=" IFINDEX_LO_STR);
501 printf(", ");
502 PRINT_FIELD_U(msg, ifal_seq);
503 printf("}"));
504 }
505
506 test_rtnl_unknown_msg(fd, RTM_NEWADDRLABEL + 3);
507 }
508
509 static void
510 test_rtnl_dcb(const int fd)
511 {
512 static const struct strval32 types[] = {
513 { ARG_STR(RTM_GETDCB) },
514 { ARG_STR(RTM_SETDCB) },
515 };
516 static const struct dcbmsg msg = {
517 .dcb_family = AF_UNIX,
518 .cmd = DCB_CMD_UNDEFINED
519 };
520 void *const nlh0 = midtail_alloc(NLMSG_HDRLEN, sizeof(msg));
521
522 for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
523 TEST_NL_ROUTE_(fd, nlh0, types[i].val, types[i].str, msg,
524 printf("{dcb_family=AF_UNIX"),
525 printf(", cmd=DCB_CMD_UNDEFINED}"));
526 }
527
528 test_rtnl_unknown_msg(fd, RTM_GETDCB - 2);
529 test_rtnl_unknown_msg(fd, RTM_GETDCB - 1);
530 }
531
532 static void
533 test_rtnl_netconf(const int fd)
534 {
535 static const struct strval32 types[] = {
536 { ARG_STR(RTM_NEWNETCONF) },
537 { ARG_STR(RTM_DELNETCONF) },
538 { ARG_STR(RTM_GETNETCONF) },
539 };
540 static const struct netconfmsg msg = {
541 .ncm_family = AF_INET
542 };
543 void *const nlh0 = midtail_alloc(NLMSG_HDRLEN, sizeof(msg));
544
545 for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
546 TEST_NETLINK_(fd, nlh0, types[i].val, types[i].str,
547 NLM_F_REQUEST, "NLM_F_REQUEST",
548 sizeof(msg), &msg, sizeof(msg),
549 printf("{ncm_family=AF_INET}"));
550 }
551
552 test_rtnl_unknown_msg(fd, RTM_NEWNETCONF + 3);
553 }
554
555 static void
556 test_rtnl_mdb(const int fd)
557 {
558 static const struct strval32 types[] = {
559 { ARG_STR(RTM_NEWMDB) },
560 { ARG_STR(RTM_DELMDB) },
561 { ARG_STR(RTM_GETMDB) },
562 };
563 const struct br_port_msg msg = {
564 .family = AF_UNIX,
565 .ifindex = ifindex_lo()
566 };
567 void *const nlh0 = midtail_alloc(NLMSG_HDRLEN, sizeof(msg));
568
569 for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
570 TEST_NL_ROUTE_(fd, nlh0, types[i].val, types[i].str, msg,
571 printf("{family=AF_UNIX"),
572 printf(", ifindex=" IFINDEX_LO_STR "}"));
573 }
574
575 test_rtnl_unknown_msg(fd, RTM_NEWMDB + 3);
576 }
577
578 static void
579 test_rtnl_rtgen(const int fd)
580 {
581 static const struct strval32 types[] = {
582 { ARG_STR(RTM_NEWNSID) },
583 { ARG_STR(RTM_DELNSID) },
584 { ARG_STR(RTM_GETNSID) },
585 { ARG_STR(RTM_NEWCACHEREPORT) },
586 };
587 static const struct rtgenmsg msg = {
588 .rtgen_family = AF_UNIX
589 };
590 void *const nlh0 = midtail_alloc(NLMSG_HDRLEN, sizeof(msg));
591
592 for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
593 TEST_NETLINK_(fd, nlh0, types[i].val, types[i].str,
594 NLM_F_REQUEST, "NLM_F_REQUEST",
595 sizeof(msg), &msg, sizeof(msg),
596 printf("{rtgen_family=AF_UNIX}"));
597 }
598
599 test_rtnl_unknown_msg(fd, RTM_NEWNSID + 3);
600 test_rtnl_unknown_msg(fd, RTM_NEWCACHEREPORT + 1);
601 test_rtnl_unknown_msg(fd, RTM_NEWCACHEREPORT + 2);
602 test_rtnl_unknown_msg(fd, RTM_NEWCACHEREPORT + 3);
603 }
604
605 static void
606 test_rtnl_nexthop(const int fd)
607 {
608 static const struct strval32 types[] = {
609 { ARG_STR(RTM_NEWNEXTHOP) },
610 { ARG_STR(RTM_DELNEXTHOP) },
611 { ARG_STR(RTM_GETNEXTHOP) },
612 };
613 static const struct {
614 struct nhmsg msg;
615 const char *af_str;
616 const char *rest_str;
617 } msgs[] = {
618 { { .nh_family = AF_UNIX, .nh_scope = RT_SCOPE_UNIVERSE,
619 .nh_protocol = RTPROT_KERNEL, .nh_flags = RTNH_F_DEAD, },
620 "{nh_family=AF_UNIX", ", nh_scope=RT_SCOPE_UNIVERSE"
621 ", nh_protocol=RTPROT_KERNEL, nh_flags=RTNH_F_DEAD}" },
622 { { .nh_family = 45, .nh_scope = 200,
623 .nh_protocol = 5, .resvd=1, .nh_flags = 0x80, },
624 "{nh_family=AF_MCTP", ", nh_scope=RT_SCOPE_SITE"
625 ", nh_protocol=0x5 /* RTPROT_??? */, resvd=0x1"
626 ", nh_flags=0x80 /* RTNH_F_??? */}" },
627 { { .nh_family = 46, .nh_scope = 201,
628 .nh_protocol = 99, .resvd=0xff, .nh_flags = 0xdeadbeef, },
629 "{nh_family=0x2e /* AF_??? */", ", nh_scope=0xc9"
630 ", nh_protocol=RTPROT_OPENR, resvd=0xff, nh_flags=RTNH_F_DEAD"
631 "|RTNH_F_PERVASIVE|RTNH_F_ONLINK|RTNH_F_OFFLOAD"
632 "|RTNH_F_UNRESOLVED|RTNH_F_TRAP|0xdeadbe80}" },
633 };
634 void *const nlh0 = midtail_alloc(NLMSG_HDRLEN, sizeof(msgs[0].msg));
635
636 for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
637 for (size_t j = 0; j < ARRAY_SIZE(msgs); j++) {
638 TEST_NL_ROUTE_(fd, nlh0, types[i].val, types[i].str,
639 msgs[j].msg,
640 printf("%s", msgs[j].af_str),
641 printf("%s", msgs[j].rest_str));
642 }
643 }
644
645 test_rtnl_unknown_msg(fd, RTM_NEWNEXTHOP + 3);
646 }
647
648 static void
649 test_rtnl_ifstats(const int fd)
650 {
651 static const struct strval32 types[] = {
652 { ARG_STR(RTM_NEWSTATS) },
653 { ARG_STR(RTM_GETSTATS) },
654 };
655 const struct {
656 struct if_stats_msg msg;
657 const char *af_str;
658 const char *rest_str;
659 } msgs[] = {
660 { { .family = AF_UNIX, .pad1 = 0, .pad2 = 0,
661 .ifindex = ifindex_lo(), .filter_mask = 0, },
662 "{family=AF_UNIX", ", ifindex=" IFINDEX_LO_STR
663 ", filter_mask=0}" },
664 { { .family = 45, .pad1 = 0, .pad2 = 0xdead,
665 .ifindex = 0xdeadbeef, .filter_mask = 1, },
666 "{family=AF_MCTP", ", pad2=0xdead, ifindex=3735928559"
667 ", filter_mask=1<<IFLA_STATS_UNSPEC}" },
668 { { .family = 46, .pad1 = 0xca, .pad2 = 0,
669 .ifindex = ifindex_lo(), .filter_mask = 0xff, },
670 "{family=0x2e /* AF_??? */", ", pad1=0xca"
671 ", ifindex=" IFINDEX_LO_STR
672 ", filter_mask=1<<IFLA_STATS_UNSPEC|1<<IFLA_STATS_LINK_64"
673 "|1<<IFLA_STATS_LINK_XSTATS|1<<IFLA_STATS_LINK_XSTATS_SLAVE"
674 "|1<<IFLA_STATS_LINK_OFFLOAD_XSTATS|1<<IFLA_STATS_AF_SPEC"
675 "|0xc0}" },
676 { { .family = 255, .pad1 = 0xde, .pad2 = 0xbeef,
677 .ifindex = ifindex_lo(), .filter_mask = 0xdec0dec0, },
678 "{family=0xff /* AF_??? */", ", pad1=0xde"
679 ", pad2=0xbeef, ifindex=" IFINDEX_LO_STR
680 ", filter_mask=0xdec0dec0 /* 1<<IFLA_STATS_??? */}" },
681 };
682 void *const nlh0 = midtail_alloc(NLMSG_HDRLEN, sizeof(msgs[0].msg));
683
684 for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
685 for (size_t j = 0; j < ARRAY_SIZE(msgs); j++) {
686 TEST_NL_ROUTE_(fd, nlh0, types[i].val, types[i].str,
687 msgs[j].msg,
688 printf("%s", msgs[j].af_str),
689 printf("%s", msgs[j].rest_str));
690 }
691 }
692
693 test_rtnl_unknown_msg(fd, RTM_NEWSTATS + 1);
694 test_rtnl_unknown_msg(fd, RTM_NEWSTATS + 3);
695 }
696
697 int main(void)
698 {
699 skip_if_unavailable("/proc/self/fd/");
700
701 int fd = create_nl_socket(NETLINK_ROUTE);
702
703 test_nlmsg_type(fd);
704 test_nlmsg_flags(fd);
705 test_nlmsg_done(fd);
706 test_rtnl_unspec(fd);
707
708 test_rtnl_link(fd); /* 16 */
709 test_rtnl_addr(fd); /* 20, 56, 60 */
710 test_rtnl_route(fd); /* 24 */
711 test_rtnl_neigh(fd); /* 28 */
712 test_rtnl_rule(fd); /* 32 */
713 test_rtnl_tc(fd); /* 36, 40, 44, 100 */
714 test_rtnl_tca(fd); /* 48 */
715
716 /* prefix */ /* 52 */
717 test_rtnl_unsupported_msg(fd, ARG_STR(RTM_NEWPREFIX));
718 test_rtnl_unknown_msg(fd, RTM_NEWPREFIX + 1);
719 test_rtnl_unknown_msg(fd, RTM_NEWPREFIX + 2);
720 test_rtnl_unknown_msg(fd, RTM_NEWPREFIX + 3);
721
722 test_rtnl_neightbl(fd); /* 64 */
723
724 /* nduserport */ /* 68 */
725 test_rtnl_unsupported_msg(fd, ARG_STR(RTM_NEWNDUSEROPT));
726 test_rtnl_unknown_msg(fd, RTM_NEWNDUSEROPT + 1);
727 test_rtnl_unknown_msg(fd, RTM_NEWNDUSEROPT + 2);
728 test_rtnl_unknown_msg(fd, RTM_NEWNDUSEROPT + 3);
729
730 test_rtnl_addrlabel(fd); /* 72 */
731 test_rtnl_dcb(fd); /* 76 */
732 test_rtnl_netconf(fd); /* 80 */
733 test_rtnl_mdb(fd); /* 84 */
734 test_rtnl_rtgen(fd); /* 88, 96 */
735 test_rtnl_ifstats(fd); /* 92 */
736 test_rtnl_nexthop(fd); /* 104 */
737
738 /* linkprop */ /* 108 */
739 test_rtnl_unsupported_msg(fd, ARG_STR(RTM_NEWLINKPROP));
740 test_rtnl_unsupported_msg(fd, ARG_STR(RTM_DELLINKPROP));
741 test_rtnl_unsupported_msg(fd, ARG_STR(RTM_GETLINKPROP));
742 test_rtnl_unknown_msg(fd, RTM_NEWLINKPROP + 3);
743
744 /* vlan */ /* 112 */
745 test_rtnl_unsupported_msg(fd, ARG_STR(RTM_NEWVLAN));
746 test_rtnl_unsupported_msg(fd, ARG_STR(RTM_DELVLAN));
747 test_rtnl_unsupported_msg(fd, ARG_STR(RTM_GETVLAN));
748 test_rtnl_unknown_msg(fd, RTM_NEWVLAN + 3);
749
750 /* nexthopbucket */ /* 116 */
751 test_rtnl_unsupported_msg(fd, ARG_STR(RTM_NEWNEXTHOPBUCKET));
752 test_rtnl_unsupported_msg(fd, ARG_STR(RTM_DELNEXTHOPBUCKET));
753 test_rtnl_unsupported_msg(fd, ARG_STR(RTM_GETNEXTHOPBUCKET));
754 test_rtnl_unknown_msg(fd, RTM_NEWNEXTHOPBUCKET + 3);
755
756 for (uint16_t i = 120; i < 124; i++)
757 test_rtnl_unknown_msg(fd, i);
758
759 printf("+++ exited with 0 +++\n");
760
761 return 0;
762 }