1 /*
2 * Copyright (c) 2021 Eugene Syromyatnikov <evgsyr@gmail.com>
3 * All rights reserved.
4 *
5 * SPDX-License-Identifier: LGPL-2.1-or-later
6 */
7
8 #include "defs.h"
9 #include "netlink_route.h"
10 #include "nlattr.h"
11 #include "print_fields.h"
12
13 #include <linux/rtnetlink.h>
14 #include <linux/nexthop.h>
15
16 #include "xlat/rtnl_nexthop_attrs.h"
17 #include "xlat/rtnl_nexthop_grp_types.h"
18 #include "xlat/rtnl_nha_res_bucket_attrs.h"
19 #include "xlat/rtnl_nha_res_group_attrs.h"
20
21 static bool
22 print_nh_grp(struct tcb *const tcp, void *const elem_buf,
23 const size_t elem_size, void *const opaque_data)
24 {
25 struct nexthop_grp *grp = (struct nexthop_grp *) elem_buf;
26
27 tprint_struct_begin();
28 PRINT_FIELD_U(*grp, id);
29 tprint_struct_next();
30 PRINT_FIELD_U(*grp, weight);
31 if (grp->resvd1) {
32 tprint_struct_next();
33 PRINT_FIELD_X(*grp, resvd1);
34 }
35 if (grp->resvd2) {
36 tprint_struct_next();
37 PRINT_FIELD_X(*grp, resvd2);
38 }
39 tprint_struct_end();
40
41 return true;
42 }
43
44 static bool
45 decode_nha_nh_grp(struct tcb *const tcp,
46 const kernel_ulong_t addr,
47 const unsigned int len,
48 const void *const opaque_data)
49 {
50 struct nexthop_grp elem;
51 const size_t nmemb = len / sizeof(elem);
52
53 if (!nmemb)
54 return false;
55
56 print_array(tcp, addr, nmemb, &elem, sizeof(elem),
57 tfetch_mem, print_nh_grp, NULL);
58
59 return true;
60 }
61
62 static bool
63 decode_nha_nh_grp_type(struct tcb *const tcp,
64 const kernel_ulong_t addr,
65 const unsigned int len,
66 const void *const opaque_data)
67 {
68 static const struct decode_nla_xlat_opts opts = {
69 .xlat = rtnl_nexthop_grp_types,
70 .dflt = "NEXTHOP_GRP_TYPE_???",
71 .size = 2,
72 };
73
74 return decode_nla_xval(tcp, addr, len, &opts);
75 }
76
77 static bool
78 decode_nha_addr(struct tcb *const tcp,
79 const kernel_ulong_t addr,
80 const unsigned int len,
81 const void *const opaque_data)
82 {
83 const struct nhmsg *const nhmsg = opaque_data;
84
85 decode_inet_addr(tcp, addr, len, nhmsg->nh_family, NULL);
86
87 return true;
88 }
89
90 static const nla_decoder_t nha_res_group_nla_decoders[] = {
91 [NHA_RES_GROUP_PAD] = NULL,
92 [NHA_RES_GROUP_BUCKETS] = decode_nla_u16,
93 [NHA_RES_GROUP_IDLE_TIMER] = decode_nla_clock_t,
94 [NHA_RES_GROUP_UNBALANCED_TIMER] = decode_nla_clock_t,
95 [NHA_RES_GROUP_UNBALANCED_TIME] = decode_nla_clock_t,
96 };
97
98 static bool
99 decode_nha_res_group(struct tcb *const tcp,
100 const kernel_ulong_t addr,
101 const unsigned int len,
102 const void *const opaque_data)
103 {
104 decode_nlattr(tcp, addr, len, rtnl_nha_res_group_attrs,
105 "NHA_RES_GROUP_???",
106 ARRSZ_PAIR(nha_res_group_nla_decoders), opaque_data);
107
108 return true;
109 }
110
111 static const nla_decoder_t nha_res_bucket_nla_decoders[] = {
112 [NHA_RES_BUCKET_PAD] = NULL,
113 [NHA_RES_BUCKET_INDEX] = decode_nla_u16,
114 [NHA_RES_BUCKET_IDLE_TIME] = decode_nla_clock_t,
115 [NHA_RES_BUCKET_NH_ID] = decode_nla_u32,
116 };
117
118 static bool
119 decode_nha_res_bucket(struct tcb *const tcp,
120 const kernel_ulong_t addr,
121 const unsigned int len,
122 const void *const opaque_data)
123 {
124 decode_nlattr(tcp, addr, len, rtnl_nha_res_bucket_attrs,
125 "NHA_RES_BUCKET_???",
126 ARRSZ_PAIR(nha_res_bucket_nla_decoders), opaque_data);
127
128 return true;
129 }
130
131 static const nla_decoder_t nhmsg_nla_decoders[] = {
132 [NHA_UNSPEC] = NULL,
133 [NHA_ID] = decode_nla_u32,
134 [NHA_GROUP] = decode_nha_nh_grp,
135 [NHA_GROUP_TYPE] = decode_nha_nh_grp_type,
136 [NHA_BLACKHOLE] = decode_nla_u32,
137 [NHA_OIF] = decode_nla_ifindex,
138 [NHA_GATEWAY] = decode_nha_addr,
139 [NHA_ENCAP_TYPE] = decode_nla_lwt_encap_type,
140 [NHA_ENCAP] = NULL, /* unimplemented */
141 [NHA_GROUPS] = decode_nla_u32,
142 [NHA_MASTER] = decode_nla_ifindex,
143 [NHA_FDB] = decode_nla_u32,
144 [NHA_RES_GROUP] = decode_nha_res_group,
145 [NHA_RES_BUCKET] = decode_nha_res_bucket,
146 };
147
148 DECL_NETLINK_ROUTE_DECODER(decode_nhmsg)
149 {
150 struct nhmsg nhmsg = { .nh_family = family };
151 size_t offset = sizeof(nhmsg.nh_family);
152 bool decode_nla = false;
153
154 tprint_struct_begin();
155 PRINT_FIELD_XVAL(nhmsg, nh_family, addrfams, "AF_???");
156 tprint_struct_next();
157
158 if (len >= sizeof(nhmsg)) {
159 if (!umoven_or_printaddr(tcp, addr + offset,
160 sizeof(nhmsg) - offset,
161 (char *) &nhmsg + offset)) {
162 PRINT_FIELD_XVAL(nhmsg, nh_scope,
163 routing_scopes, NULL);
164 tprint_struct_next();
165 PRINT_FIELD_XVAL(nhmsg, nh_protocol,
166 routing_protocols, "RTPROT_???");
167 if (nhmsg.resvd) {
168 tprint_struct_next();
169 PRINT_FIELD_X(nhmsg, resvd);
170 }
171 tprint_struct_next();
172 PRINT_FIELD_FLAGS(nhmsg, nh_flags,
173 route_nexthop_flags, "RTNH_F_???");
174 decode_nla = true;
175 }
176 } else {
177 tprint_more_data_follows();
178 }
179 tprint_struct_end();
180
181 offset = NLMSG_ALIGN(sizeof(nhmsg));
182 if (decode_nla && len > offset) {
183 tprint_array_next();
184 decode_nlattr(tcp, addr + offset, len - offset,
185 rtnl_nexthop_attrs, "NHA_???",
186 ARRSZ_PAIR(nhmsg_nla_decoders), &nhmsg);
187 }
188 }