1 /*
2 * Copyright (c) 2017 JingPiao Chen <chenjingpiao@gmail.com>
3 * Copyright (c) 2017-2023 The strace developers.
4 * All rights reserved.
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 #include "tests.h"
10
11 #include <stdio.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
14 #include "test_nlattr.h"
15 #include <linux/neighbour.h>
16 #include <linux/rtnetlink.h>
17
18 static const unsigned int hdrlen = sizeof(struct ndmsg);
19
20 static void
21 init_ndmsg(struct nlmsghdr *const nlh, const unsigned int msg_len)
22 {
23 SET_STRUCT(struct nlmsghdr, nlh,
24 .nlmsg_len = msg_len,
25 .nlmsg_type = RTM_GETNEIGH,
26 .nlmsg_flags = NLM_F_DUMP
27 );
28
29 struct ndmsg *const msg = NLMSG_DATA(nlh);
30 SET_STRUCT(struct ndmsg, msg,
31 .ndm_family = AF_UNIX,
32 .ndm_ifindex = ifindex_lo(),
33 .ndm_state = NUD_PERMANENT,
34 .ndm_flags = NTF_PROXY,
35 .ndm_type = RTN_UNSPEC
36 );
37 }
38
39 static void
40 print_ndmsg(const unsigned int msg_len)
41 {
42 printf("{nlmsg_len=%u, nlmsg_type=RTM_GETNEIGH, nlmsg_flags=NLM_F_DUMP"
43 ", nlmsg_seq=0, nlmsg_pid=0}, {ndm_family=AF_UNIX"
44 ", ndm_ifindex=" IFINDEX_LO_STR
45 ", ndm_state=NUD_PERMANENT"
46 ", ndm_flags=NTF_PROXY"
47 ", ndm_type=RTN_UNSPEC}",
48 msg_len);
49 }
50
51 static void
52 init_ndmsg_nfea(struct nlmsghdr *const nlh, const unsigned int msg_len)
53 {
54 init_ndmsg(nlh, msg_len);
55
56 struct nlattr *nla = NLMSG_ATTR(nlh, hdrlen);
57 SET_STRUCT(struct nlattr, nla,
58 .nla_len = msg_len - NLMSG_SPACE(hdrlen),
59 .nla_type = NDA_FDB_EXT_ATTRS,
60 );
61 }
62
63 static void
64 print_ndmsg_nfea(const unsigned int msg_len)
65 {
66 print_ndmsg(msg_len);
67 printf(", [{nla_len=%u, nla_type=NDA_FDB_EXT_ATTRS}",
68 msg_len - NLMSG_SPACE(hdrlen));
69 }
70
71 int
72 main(void)
73 {
74 skip_if_unavailable("/proc/self/fd/");
75
76 const int fd = create_nl_socket(NETLINK_ROUTE);
77 void *nlh0 = midtail_alloc(NLMSG_SPACE(hdrlen),
78 NLA_HDRLEN + sizeof(struct nda_cacheinfo));
79
80 static char pattern[4096];
81 fill_memory_ex(pattern, sizeof(pattern), 'a', 'z' - 'a' + 1);
82
83 const unsigned int nla_type = 0xffff & NLA_TYPE_MASK;
84 char nla_type_str[256];
85 sprintf(nla_type_str, "%#x /* NDA_??? */", nla_type);
86 TEST_NLATTR_(fd, nlh0, hdrlen,
87 init_ndmsg, print_ndmsg,
88 nla_type, nla_type_str,
89 4, pattern, 4,
90 print_quoted_hex(pattern, 4));
91
92 TEST_NLATTR(fd, nlh0, hdrlen,
93 init_ndmsg, print_ndmsg,
94 NDA_DST, 4, pattern, 4,
95 print_quoted_hex(pattern, 4));
96
97 static const struct nda_cacheinfo ci = {
98 .ndm_confirmed = 0xabcdedad,
99 .ndm_used = 0xbcdaedad,
100 .ndm_updated = 0xcdbadeda,
101 .ndm_refcnt = 0xdeadbeda
102 };
103
104 TEST_NLATTR_OBJECT(fd, nlh0, hdrlen,
105 init_ndmsg, print_ndmsg,
106 NDA_CACHEINFO, pattern, ci,
107 printf("{");
108 PRINT_FIELD_U(ci, ndm_confirmed);
109 printf(", ");
110 PRINT_FIELD_U(ci, ndm_used);
111 printf(", ");
112 PRINT_FIELD_U(ci, ndm_updated);
113 printf(", ");
114 PRINT_FIELD_U(ci, ndm_refcnt);
115 printf("}"));
116
117 const uint16_t port = 0xabcd;
118 TEST_NLATTR_OBJECT(fd, nlh0, hdrlen,
119 init_ndmsg, print_ndmsg,
120 NDA_PORT, pattern, port,
121 printf("htons(%u)", ntohs(port)));
122
123 static const uint8_t mac[6] = "\xf8\xc2\x49\x13\x57\xbd";
124 TEST_NLATTR(fd, nlh0, hdrlen, init_ndmsg, print_ndmsg,
125 NDA_LLADDR, sizeof(mac) - 1, mac, sizeof(mac) - 1,
126 for (unsigned int i = 0; i < sizeof(mac) - 1; ++i)
127 printf("%s%02x", i ? ":" : "", mac[i]));
128
129 TEST_NLATTR(fd, nlh0, hdrlen, init_ndmsg, print_ndmsg,
130 NDA_LLADDR, sizeof(mac), mac, sizeof(mac),
131 for (unsigned int i = 0; i < sizeof(mac); ++i)
132 printf("%s%02x", i ? ":" : "", mac[i]));
133
134 /* u32 attrs */
135 static const struct strval16 u32_attrs[] = {
136 { ENUM_KNOWN(0x4, NDA_PROBES) },
137 { ENUM_KNOWN(0x7, NDA_VNI) },
138 { ENUM_KNOWN(0xa, NDA_LINK_NETNSID) },
139 { ENUM_KNOWN(0xb, NDA_SRC_VNI) },
140 { ENUM_KNOWN(0xd, NDA_NH_ID) },
141 };
142 void *nlh_u32 = midtail_alloc(NLMSG_SPACE(hdrlen), sizeof(uint32_t));
143
144 for (size_t i = 0; i < ARRAY_SIZE(u32_attrs); i++) {
145 check_u32_nlattr(fd, nlh_u32, hdrlen, init_ndmsg, print_ndmsg,
146 u32_attrs[i].val, u32_attrs[i].str, pattern, 0);
147 }
148
149 /* NDA_FDB_EXT_ATTRS: unknown, undecoded */
150 static const struct strval16 nfea_unk_attrs[] = {
151 { ENUM_KNOWN(0, NFEA_UNSPEC) },
152 { ENUM_KNOWN(0x2, NFEA_DONT_REFRESH) },
153 { ARG_XLAT_UNKNOWN(0x3, "NFEA_???") },
154 { ARG_XLAT_UNKNOWN(0x1ace, "NFEA_???") },
155 };
156 static const uint32_t dummy = BE_LE(0xbadc0ded, 0xed0ddcba);
157
158 for (size_t i = 0; i < ARRAY_SIZE(nfea_unk_attrs); i++) {
159 TEST_NESTED_NLATTR_(fd, nlh0, hdrlen,
160 init_ndmsg_nfea, print_ndmsg_nfea,
161 nfea_unk_attrs[i].val,
162 nfea_unk_attrs[i].str,
163 sizeof(dummy), &dummy, sizeof(dummy), 1,
164 printf("\"\\xba\\xdc\\x0d\\xed\""));
165 }
166
167 /* NDA_FDB_EXT_ATTRS: NFEA_ACTIVITY_NOTIFY */
168 static const struct strval8 fan_flags[] = {
169 { ARG_STR(0) },
170 { ARG_XLAT_KNOWN(0x1, "FDB_NOTIFY_BIT") },
171 { ARG_XLAT_KNOWN(0xef, "FDB_NOTIFY_BIT"
172 "|FDB_NOTIFY_INACTIVE_BIT|0xec") },
173 { ARG_XLAT_UNKNOWN(0xfc, "FDB_NOTIFY_???") },
174 };
175
176 for (size_t i = 0; i < ARRAY_SIZE(fan_flags); i++) {
177 TEST_NESTED_NLATTR_(fd, nlh0, hdrlen,
178 init_ndmsg_nfea, print_ndmsg_nfea,
179 NFEA_ACTIVITY_NOTIFY,
180 "NFEA_ACTIVITY_NOTIFY",
181 1, &fan_flags[i].val, 1, 1,
182 printf("%s", fan_flags[i].str));
183 }
184
185 /* NDA_FLAGS_EXT */
186 static const struct strval32 ntfe_flags[] = {
187 { ARG_STR(0) },
188 { ARG_XLAT_KNOWN(0x1, "NTF_EXT_MANAGED") },
189 { ARG_XLAT_KNOWN(0x2, "NTF_EXT_LOCKED") },
190 { ARG_XLAT_KNOWN(0xdeadbeef, "NTF_EXT_MANAGED|NTF_EXT_LOCKED"
191 "|0xdeadbeec") },
192 { ARG_XLAT_UNKNOWN(0xfeedcafc, "NTF_EXT_???") },
193 };
194
195 for (size_t i = 0; i < ARRAY_SIZE(ntfe_flags); i++) {
196 TEST_NLATTR(fd, nlh0, hdrlen, init_ndmsg, print_ndmsg,
197 NDA_FLAGS_EXT, 4, &ntfe_flags[i].val, 4,
198 printf("%s", ntfe_flags[i].str));
199 }
200
201 /* NDA_NDM_STATE_MASK */
202 static const struct strval16 states_flags[] = {
203 { ARG_XLAT_KNOWN(0, "NUD_NONE") },
204 { ARG_XLAT_KNOWN(0x1, "NUD_INCOMPLETE") },
205 { ARG_XLAT_KNOWN(0xabed, "NUD_INCOMPLETE|NUD_STALE|NUD_DELAY"
206 "|NUD_FAILED|NUD_NOARP|NUD_PERMANENT"
207 "|0xab00") },
208 { ARG_XLAT_UNKNOWN(0xff00, "NUD_???") },
209 };
210
211 for (size_t i = 0; i < ARRAY_SIZE(states_flags); i++) {
212 TEST_NLATTR(fd, nlh0, hdrlen, init_ndmsg, print_ndmsg,
213 NDA_NDM_STATE_MASK, 2, &states_flags[i].val, 2,
214 printf("%s", states_flags[i].str));
215 }
216
217 /* NDA_NDM_FLAGS_MASK */
218 static const struct strval8 ndm_flags[] = {
219 { ARG_STR(0) },
220 { ARG_XLAT_KNOWN(0x1, "NTF_USE") },
221 { ARG_XLAT_KNOWN(0xbe, "NTF_SELF|NTF_MASTER|NTF_PROXY"
222 "|NTF_EXT_LEARNED|NTF_OFFLOADED"
223 "|NTF_ROUTER") },
224 };
225
226 for (size_t i = 0; i < ARRAY_SIZE(ndm_flags); i++) {
227 TEST_NLATTR(fd, nlh0, hdrlen, init_ndmsg, print_ndmsg,
228 NDA_NDM_FLAGS_MASK, 1, &ndm_flags[i].val, 1,
229 printf("%s", ndm_flags[i].str));
230 }
231
232 puts("+++ exited with 0 +++");
233 return 0;
234 }