1 /*
2 * Copyright (c) 2026 Ishan Agrawal
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7 #include <sys/param.h>
8 #include <sys/types.h>
9 #include <sys/socket.h>
10 #include <netinet/in.h>
11 #include <arpa/inet.h>
12 #include <netlink/netlink.h>
13 #include <netlink/netlink_generic.h>
14 #include <netlink/netlink_snl.h>
15 #include <netpfil/pf/pf_nl.h>
16
17 #include <stdio.h>
18 #include <stdbool.h>
19 #include <stddef.h>
20 #include <stdlib.h>
21 #include <string.h>
22
23 #include "sysdecode.h"
24 #include "support.h"
25
26 /*
27 * Decodes a buffer as a Netlink message stream.
28 *
29 * Returns true if the data was successfully decoded as Netlink.
30 * Returns false if the data is malformed, allowing the caller
31 * to fallback to a standard hex/string dump.
32 */
33
34 static struct name_table *family_table = NULL;
35 static size_t num_family = 0;
36
37 typedef void decode_attr_f(FILE *fp, const struct nlattr *attr,
38 const char *attr_name, const void *args);
39 struct nlattr_decoder {
40 uint16_t type; /* Attribute type */
41 const char *attr_name; /* Attribute name*/
42 decode_attr_f *cb; /* decoder function to call */
43 const void *args; /* nested decoder */
44 };
45
46 struct nlattr_decoder_set {
47 const struct nlattr_decoder *decoders; /* PFNL CMD Decoders */
48 size_t count; /*Attribute Count*/
49 };
50
51 struct pfnl_cmd_decoder {
52 int cmd_num; /* PFNL CMD */
53 const struct nlattr_decoder_set *ds; /* PFNL CMD Decoder set */
54 };
55
56 #define NL_DECLARE_ATTR_DECODER(_name, _np) \
57 static const struct nlattr_decoder_set _name = { \
58 .decoders = &((_np)[0]), \
59 .count = nitems(_np), \
60 }
61
62 static void nl_decode_attrs_raw(FILE *fp, const struct nlattr *nla_head,
63 size_t len, const struct nlattr_decoder *ps, size_t pslen);
64 static void sysdecode_netlink_pf_constructor(void) __attribute__ ((__constructor__));
65
66 static void
nlattr_decode_in6_addr(FILE * fp,const struct nlattr * attr,const char * attr_name,const void * args)67 nlattr_decode_in6_addr(FILE *fp, const struct nlattr *attr,
68 const char *attr_name, const void *args)
69 {
70 (void)args;
71
72 struct in6_addr target;
73
74 if (NLA_DATA_LEN(attr) < (int)sizeof(target))
75 return;
76
77 memcpy(&target, NLA_DATA_CONST(attr), sizeof(struct in6_addr));
78
79 fprintf(fp, "%s=", attr_name);
80
81 char buf[INET6_ADDRSTRLEN];
82
83 if (inet_ntop(AF_INET6, &target, buf, sizeof(buf)) != NULL)
84 fprintf(fp, "%s", buf);
85 }
86
87 static void
nlattr_decode_uint64(FILE * fp,const struct nlattr * attr,const char * attr_name,const void * args __unused)88 nlattr_decode_uint64(FILE *fp, const struct nlattr *attr, const char *attr_name,
89 const void *args __unused)
90 {
91 uint64_t target;
92
93 if (NLA_DATA_LEN(attr) < (int)sizeof(target))
94 return;
95
96 memcpy(&target, NLA_DATA_CONST(attr), sizeof(uint64_t));
97
98 fprintf(fp, "%s=%ju", attr_name, target);
99 }
100
101 static void
nlattr_decode_uint32(FILE * fp,const struct nlattr * attr,const char * attr_name,const void * args)102 nlattr_decode_uint32(FILE *fp, const struct nlattr *attr, const char *attr_name,
103 const void *args)
104 {
105 (void)args;
106
107 uint32_t target;
108
109 if (NLA_DATA_LEN(attr) < (int)sizeof(target))
110 return;
111
112 memcpy(&target, NLA_DATA_CONST(attr), sizeof(uint32_t));
113
114 fprintf(fp, "%s=%u", attr_name, target);
115 }
116
117 static void
nlattr_decode_uint16(FILE * fp,const struct nlattr * attr,const char * attr_name,const void * args __unused)118 nlattr_decode_uint16(FILE *fp, const struct nlattr *attr, const char *attr_name,
119 const void *args __unused)
120 {
121 uint16_t target;
122
123 if (NLA_DATA_LEN(attr) < (int)sizeof(target))
124 return;
125
126 memcpy(&target, NLA_DATA_CONST(attr), sizeof(uint16_t));
127
128 fprintf(fp, "%s=%u", attr_name, target);
129 }
130
131 static void
nlattr_decode_uint8(FILE * fp,const struct nlattr * attr,const char * attr_name,const void * args)132 nlattr_decode_uint8(FILE *fp, const struct nlattr *attr, const char *attr_name,
133 const void *args)
134 {
135 (void)args;
136
137 uint8_t target;
138
139 if (NLA_DATA_LEN(attr) < (int)sizeof(target))
140 return;
141
142 memcpy(&target, NLA_DATA_CONST(attr), sizeof(uint8_t));
143
144 fprintf(fp, "%s=%u", attr_name, target);
145 }
146
147 static void
nlattr_decode_bool(FILE * fp,const struct nlattr * attr,const char * attr_name,const void * args __unused)148 nlattr_decode_bool(FILE *fp, const struct nlattr *attr, const char *attr_name,
149 const void *args __unused)
150 {
151 bool target;
152
153 if (NLA_DATA_LEN(attr) < (int)sizeof(target))
154 return;
155
156 memcpy(&target, NLA_DATA_CONST(attr), sizeof(bool));
157
158 fprintf(fp, "%s=", attr_name);
159
160 if (target)
161 fprintf(fp, "TRUE");
162 else
163 fprintf(fp, "FALSE");
164 }
165
166 static void
nlattr_decode_string(FILE * fp,const struct nlattr * attr,const char * attr_name,const void * args)167 nlattr_decode_string(FILE *fp, const struct nlattr *attr, const char *attr_name,
168 const void *args)
169 {
170 (void)args;
171
172 if (NLA_DATA_LEN(attr) == 0)
173 return;
174
175 const char *target = (const char *)NLA_DATA_CONST(attr);
176
177 fprintf(fp, "%s=%s", attr_name, target);
178 }
179
180 static void
nlattr_decode_nested(FILE * fp,const struct nlattr * attr,const char * attr_name,const void * args)181 nlattr_decode_nested(FILE *fp, const struct nlattr *attr, const char *attr_name,
182 const void *args)
183 {
184 const struct nlattr_decoder_set *set = args;
185
186 if (set == NULL)
187 return;
188
189 fprintf(fp, "%s=", attr_name);
190
191 nl_decode_attrs_raw(fp, NLA_DATA_CONST(attr), NLA_DATA_LEN(attr),
192 set->decoders, set->count);
193 }
194
195 static const struct nlattr_decoder *
search_decoders(const struct nlattr_decoder * ps,size_t pslen,int key)196 search_decoders(const struct nlattr_decoder *ps, size_t pslen, int key)
197 {
198 size_t left_i = 0, right_i = pslen - 1;
199
200 if (pslen == 0)
201 return (NULL);
202
203 if (key < ps[0].type || key > ps[pslen - 1].type)
204 return (NULL);
205
206 while (left_i + 1 < right_i) {
207 size_t mid_i = (left_i + right_i) / 2;
208 if (key < ps[mid_i].type)
209 right_i = mid_i;
210 else if (key > ps[mid_i].type)
211 left_i = mid_i + 1;
212 else
213 return (&ps[mid_i]);
214 }
215 if (ps[left_i].type == key)
216 return (&ps[left_i]);
217 else if (ps[right_i].type == key)
218 return (&ps[right_i]);
219 return (NULL);
220 }
221
222 static const struct pfnl_cmd_decoder *
search_cmd_decoders(const struct pfnl_cmd_decoder * ps,size_t pslen,int key)223 search_cmd_decoders(const struct pfnl_cmd_decoder *ps, size_t pslen, int key)
224 {
225 size_t left_i = 0, right_i = pslen - 1;
226
227 if (pslen == 0)
228 return (NULL);
229
230 if (key < ps[0].cmd_num || key > ps[pslen - 1].cmd_num)
231 return (NULL);
232
233 while (left_i + 1 < right_i) {
234 size_t mid_i = (left_i + right_i) / 2;
235 if (key < ps[mid_i].cmd_num)
236 right_i = mid_i;
237 else if (key > ps[mid_i].cmd_num)
238 left_i = mid_i + 1;
239 else
240 return (&ps[mid_i]);
241 }
242 if (ps[left_i].cmd_num == key)
243 return (&ps[left_i]);
244 else if (ps[right_i].cmd_num == key)
245 return (&ps[right_i]);
246 return (NULL);
247 }
248
249 static void
nl_decode_attrs_raw(FILE * fp,const struct nlattr * nla_head,size_t len,const struct nlattr_decoder * ps,size_t pslen)250 nl_decode_attrs_raw(FILE *fp, const struct nlattr *nla_head, size_t len,
251 const struct nlattr_decoder *ps, size_t pslen)
252 {
253 const struct nlattr_decoder *s;
254 const struct nlattr *nla;
255 bool first = true;
256
257 fprintf(fp, "{");
258
259 NLA_FOREACH_CONST(nla, nla_head, len) {
260 s = search_decoders(ps, pslen, nla->nla_type & NLA_TYPE_MASK);
261
262 if (s != NULL && s->cb != NULL) {
263 if (!first)
264 fprintf(fp, ",");
265
266 s->cb(fp, nla, s->attr_name, s->args);
267 }
268 first = false;
269 }
270
271 fprintf(fp, "}");
272 }
273
274 static const struct nlattr_decoder nla_d_getrules[] = {
275 { .type = PF_GR_ANCHOR, .attr_name = "anchor", .cb = nlattr_decode_string },
276 { .type = PF_GR_ACTION, .attr_name = "action", .cb = nlattr_decode_uint8 },
277 { .type = PF_GR_NR, .attr_name = "nr", .cb = nlattr_decode_uint32 },
278 { .type = PF_GR_TICKET, .attr_name = "ticket", .cb = nlattr_decode_uint32 },
279 { .type = PF_GR_CLEAR, .attr_name = "clear", .cb = nlattr_decode_uint8 },
280 };
281 NL_DECLARE_ATTR_DECODER(getrules_decoder, nla_d_getrules);
282
283 static const struct nlattr_decoder nla_d_set_limit[] = {
284 { .type = PF_LI_INDEX, .attr_name = "index", .cb = nlattr_decode_uint32 },
285 { .type = PF_LI_LIMIT, .attr_name = "limit", .cb = nlattr_decode_uint32 },
286 };
287 NL_DECLARE_ATTR_DECODER(set_limit_decoder, nla_d_set_limit);
288
289 static const struct nlattr_decoder nla_d_addr_wrap[] = {
290 { .type = PF_AT_ADDR, .attr_name = "addr", .cb = nlattr_decode_in6_addr },
291 { .type = PF_AT_MASK, .attr_name = "mask", .cb = nlattr_decode_in6_addr },
292 { .type = PF_AT_IFNAME, .attr_name = "ifname", .cb = nlattr_decode_string },
293 { .type = PF_AT_TABLENAME, .attr_name = "tablename", .cb = nlattr_decode_string },
294 { .type = PF_AT_TYPE, .attr_name = "type", .cb = nlattr_decode_uint8 },
295 { .type = PF_AT_IFLAGS, .attr_name = "iflags", .cb = nlattr_decode_uint8 },
296 };
297 NL_DECLARE_ATTR_DECODER(addr_wrap_decoder, nla_d_addr_wrap);
298
299 static const struct nlattr_decoder nla_d_pool_addr[] = {
300 { .type = PF_PA_ADDR, .attr_name = "addr", .cb = nlattr_decode_nested, .args = &addr_wrap_decoder},
301 { .type = PF_PA_IFNAME, .attr_name = "ifname", .cb = nlattr_decode_string },
302 };
303 NL_DECLARE_ATTR_DECODER(pool_addr_decoder, nla_d_pool_addr);
304
305 static const struct nlattr_decoder nla_d_add_addr[] = {
306 { .type = PF_AA_ACTION, .attr_name = "action", .cb = nlattr_decode_uint32 },
307 { .type = PF_AA_TICKET, .attr_name = "ticket", .cb = nlattr_decode_uint32 },
308 { .type = PF_AA_NR, .attr_name = "nr", .cb = nlattr_decode_uint32 },
309 { .type = PF_AA_R_NUM, .attr_name = "r_num", .cb = nlattr_decode_uint32 },
310 { .type = PF_AA_R_ACTION, .attr_name = "r_action", .cb = nlattr_decode_uint8 },
311 { .type = PF_AA_R_LAST, .attr_name = "r_last", .cb = nlattr_decode_uint8 },
312 { .type = PF_AA_AF, .attr_name = "af", .cb = nlattr_decode_uint8 },
313 { .type = PF_AA_ANCHOR, .attr_name = "anchor", .cb = nlattr_decode_string },
314 { .type = PF_AA_ADDR, .attr_name = "addr", .cb = nlattr_decode_nested , .args = &pool_addr_decoder},
315 { .type = PF_AA_WHICH, .attr_name = "which", .cb = nlattr_decode_uint32 },
316 };
317 NL_DECLARE_ATTR_DECODER(addr_decoder, nla_d_add_addr);
318
319 static const struct nlattr_decoder nla_d_ruleaddr[] = {
320 { .type = PF_RAT_ADDR, .attr_name = "addr", .cb = nlattr_decode_nested, .args = &addr_wrap_decoder },
321 { .type = PF_RAT_SRC_PORT, .attr_name = "src_port", .cb = nlattr_decode_uint16 },
322 { .type = PF_RAT_DST_PORT, .attr_name = "dst_port", .cb = nlattr_decode_uint16 },
323 { .type = PF_RAT_NEG, .attr_name = "neg", .cb = nlattr_decode_uint8 },
324 { .type = PF_RAT_OP, .attr_name = "op", .cb = nlattr_decode_uint8 },
325 };
326 NL_DECLARE_ATTR_DECODER(rule_addr_decoder, nla_d_ruleaddr);
327
328 static const struct nlattr_decoder nla_d_clear_states[] = {
329 { .type = PF_CS_CMP_ID, .attr_name = "cmp_id", .cb = nlattr_decode_uint64 },
330 { .type = PF_CS_CMP_CREATORID, .attr_name = "cmp_creatorid", .cb = nlattr_decode_uint32 },
331 { .type = PF_CS_CMP_DIR, .attr_name = "cmp_dir", .cb = nlattr_decode_uint8 },
332 { .type = PF_CS_AF, .attr_name = "af", .cb = nlattr_decode_uint8 },
333 { .type = PF_CS_PROTO, .attr_name = "proto", .cb = nlattr_decode_uint8 },
334 { .type = PF_CS_SRC, .attr_name = "src", .cb = nlattr_decode_nested, .args = &rule_addr_decoder },
335 { .type = PF_CS_DST, .attr_name = "dst", .cb = nlattr_decode_nested, .args = &rule_addr_decoder },
336 { .type = PF_CS_RT_ADDR, .attr_name = "rt_addr", .cb = nlattr_decode_nested, .args = &rule_addr_decoder },
337 { .type = PF_CS_IFNAME, .attr_name = "ifname", .cb = nlattr_decode_string },
338 { .type = PF_CS_LABEL, .attr_name = "label", .cb = nlattr_decode_string },
339 { .type = PF_CS_KILL_MATCH, .attr_name = "kill_match", .cb = nlattr_decode_bool },
340 { .type = PF_CS_NAT, .attr_name = "nat", .cb = nlattr_decode_bool },
341 };
342 NL_DECLARE_ATTR_DECODER(killclear_states_decoder, nla_d_clear_states);
343
344 static const struct nlattr_decoder nla_d_ruleset[] = {
345 { .type = PF_RS_PATH, .attr_name = "path", .cb = nlattr_decode_string },
346 { .type = PF_RS_NR, .attr_name = "nr", .cb = nlattr_decode_uint32 },
347 };
348 NL_DECLARE_ATTR_DECODER(ruleset_decoder, nla_d_ruleset);
349
350 static inline void
nl_verify_decoders(const struct nlattr_decoder_set ** decoder,size_t count)351 nl_verify_decoders(const struct nlattr_decoder_set **decoder, size_t count)
352 {
353 for (size_t i = 0; i < count; i++) {
354 const struct nlattr_decoder_set *p = decoder[i];
355 for (size_t j = 1; j < p->count; j++) {
356 assert(p->decoders[j].type > p->decoders[j-1].type);
357 }
358 }
359 }
360 #define NL_VERIFY_DECODERS(_p) nl_verify_decoders((_p), nitems(_p))
361
362 static const struct nlattr_decoder_set *all_decoders[] = {
363 &getrules_decoder,
364 &set_limit_decoder,
365 &addr_wrap_decoder,
366 &pool_addr_decoder,
367 &addr_decoder,
368 &rule_addr_decoder,
369 &killclear_states_decoder,
370 &ruleset_decoder,
371 };
372
373 static const struct pfnl_cmd_decoder cmd_decoder[] = {
374 { .cmd_num = PFNL_CMD_GETRULES, .ds = &getrules_decoder },
375 { .cmd_num = PFNL_CMD_KILLSTATES, .ds = &killclear_states_decoder },
376 { .cmd_num = PFNL_CMD_GET_LIMIT, .ds = &set_limit_decoder },
377 { .cmd_num = PFNL_CMD_GET_ADDRS, .ds = &addr_decoder },
378 { .cmd_num = PFNL_CMD_GET_ADDR, .ds = &addr_decoder },
379 { .cmd_num = PFNL_CMD_GET_RULESETS, .ds = &ruleset_decoder },
380 { .cmd_num = PFNL_CMD_GET_RULESET, .ds = &ruleset_decoder },
381 };
382
383 static inline void
pfnl_verify_cmd_decoders(const struct pfnl_cmd_decoder * cmds,size_t count)384 pfnl_verify_cmd_decoders(const struct pfnl_cmd_decoder *cmds, size_t count)
385 {
386 int num = cmds[0].cmd_num;
387
388 for (size_t i = 1; i < count; i++) {
389 const struct pfnl_cmd_decoder *p = &cmds[i];
390 assert(p->cmd_num > num);
391 num = p->cmd_num;
392 }
393 }
394
395 static void
sysdecode_netlink_pf(FILE * fp,const struct genlmsghdr * genl,size_t nlm_len)396 sysdecode_netlink_pf(FILE *fp, const struct genlmsghdr *genl, size_t nlm_len)
397 {
398 uint8_t cmd = genl->cmd;
399 const char *cmd_name = sysdecode_pfnl_cmd(cmd);
400
401 if (cmd_name != NULL)
402 fprintf(fp, "cmd=%s", cmd_name);
403 else
404 fprintf(fp, "cmd=%u", cmd);
405
406 const struct nlattr *nla = (const struct nlattr *)(const void *)
407 ((const char *)genl + sizeof(struct genlmsghdr));
408
409 const struct pfnl_cmd_decoder *d;
410
411 d = search_cmd_decoders(cmd_decoder, nitems(cmd_decoder), cmd);
412 if (d != NULL) {
413 nl_decode_attrs_raw(fp, nla, nlm_len,
414 d->ds->decoders, d->ds->count);
415 }
416 }
417
418 bool
sysdecode_netlink(FILE * fp,const void * buf,size_t len,int protocol)419 sysdecode_netlink(FILE *fp, const void *buf, size_t len, int protocol)
420 {
421 const struct nlmsghdr *nl = buf;
422 size_t remaining = len;
423 bool first = true;
424
425 /* Basic sanity check: Buffer must be at least one header size. */
426 if (remaining < sizeof(struct nlmsghdr))
427 return (false);
428
429 /* * Protocol Sanity Check:
430 * The first message length must be valid (>= header) and fit
431 * inside the provided buffer snapshot.
432 */
433 if (nl->nlmsg_len < sizeof(struct nlmsghdr) || nl->nlmsg_len > remaining)
434 return (false);
435
436 if (family_table == NULL) {
437 family_table = malloc((num_family + 1) *
438 sizeof(struct name_table));
439 family_table[num_family] = (struct name_table){0, NULL};
440 }
441
442 fprintf(fp, "netlink{");
443
444 while (remaining >= sizeof(struct nlmsghdr)) {
445 if (!first)
446 fprintf(fp, ",");
447
448 /* Safety check for current message. */
449 if (nl->nlmsg_len < sizeof(struct nlmsghdr) ||
450 nl->nlmsg_len > remaining) {
451 fprintf(fp, "<truncated>");
452 break;
453 }
454
455 fprintf(fp, "flags=");
456 sysdecode_nlm_flag(fp, nl->nlmsg_flags);
457
458 fprintf(fp, ",seq=%u,pid=%u", nl->nlmsg_seq, nl->nlmsg_pid);
459
460 fprintf(fp, ",len=%u,type=", nl->nlmsg_len);
461
462 /* Decode Standard Message Types. */
463 switch (nl->nlmsg_type) {
464 case NLMSG_NOOP:
465 fprintf(fp, "NLMSG_NOOP");
466 break;
467 case NLMSG_ERROR:
468 fprintf(fp, "NLMSG_ERROR");
469 break;
470 case NLMSG_DONE:
471 fprintf(fp, "NLMSG_DONE");
472 break;
473 case NLMSG_OVERRUN:
474 fprintf(fp, "NLMSG_OVERRUN");
475 break;
476 case GENL_ID_CTRL:
477 if (protocol != NETLINK_GENERIC)
478 break;
479
480 fprintf(fp, "GENL_ID_CTRL");
481
482 const struct genlmsghdr *genl =
483 (const struct genlmsghdr *)(const void *)
484 ((const char *)nl + sizeof(struct nlmsghdr));
485
486 uint16_t family_id = 0;
487 const char *family_name = NULL;
488
489 fprintf(fp,
490 ",genl={cmd=%u,"
491 "ver=%u,reserve=%u",
492 genl->cmd,
493 genl->version, genl->reserved);
494
495 size_t cur_len = (sizeof(struct nlmsghdr)
496 + sizeof(struct genlmsghdr));
497 size_t nla_len = nl->nlmsg_len - cur_len;
498
499 const struct nlattr *nla;
500 const struct nlattr *nla_head = (const struct nlattr *)
501 (const void *)((const char *)nl + cur_len);
502
503 NLA_FOREACH_CONST(nla, nla_head, nla_len) {
504 switch (nla->nla_type) {
505 case CTRL_ATTR_FAMILY_ID:
506 memcpy(&family_id, NLA_DATA_CONST(nla),
507 sizeof(family_id));
508 fprintf(fp, ",family_id=%u", family_id);
509 break;
510 case CTRL_ATTR_FAMILY_NAME:
511 family_name =
512 ((const char *)NLA_DATA_CONST(nla));
513 fprintf(fp, ",family_name=%s",
514 family_name);
515 break;
516 default:
517 break;
518 }
519 }
520
521 if (family_name && family_id &&
522 !lookup_value(family_table, family_id)) {
523 num_family++;
524
525 family_table = realloc(family_table,
526 (num_family + 1) *
527 sizeof(struct name_table));
528 family_table[num_family - 1].val =
529 family_id;
530 family_table[num_family - 1].str =
531 strdup(family_name);
532
533 family_table[num_family] =
534 (struct name_table){0, NULL};
535 }
536 fprintf(fp, "}");
537 break;
538 default:
539 fprintf(fp, "%u", nl->nlmsg_type);
540 break;
541 }
542
543 const char *family = lookup_value(family_table, nl->nlmsg_type);
544
545 if (family != NULL && protocol == NETLINK_GENERIC) {
546 fprintf(fp, ",%s={", family);
547
548 if (strcmp(family, "pfctl") == 0) {
549 const struct genlmsghdr *genl =
550 (const struct genlmsghdr *)(const void *)
551 ((const char *)nl +
552 sizeof(struct nlmsghdr));
553 const size_t nlm_len = nl->nlmsg_len -
554 (sizeof(struct nlmsghdr) +
555 sizeof(struct genlmsghdr));
556
557 sysdecode_netlink_pf(fp, genl, nlm_len);
558 }
559
560 fprintf(fp, "}");
561 }
562
563 /* Handle Alignment (Netlink messages are 4-byte aligned). */
564 size_t aligned_len = NLMSG_ALIGN(nl->nlmsg_len);
565 if (aligned_len > remaining)
566 remaining = 0;
567 else
568 remaining -= aligned_len;
569
570 nl = (const struct nlmsghdr *)(const void *)((const char *)nl + aligned_len);
571 first = false;
572 }
573
574 fprintf(fp, "}");
575 return (true);
576 }
577
578 static void
sysdecode_netlink_pf_constructor(void)579 sysdecode_netlink_pf_constructor(void)
580 {
581 NL_VERIFY_DECODERS(all_decoders);
582 pfnl_verify_cmd_decoders(cmd_decoder, nitems(cmd_decoder));
583 }
584