(root)/
strace-6.5/
tests-mx32/
netlink_route.c
       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  }