xref: /aosp_15_r20/external/libnl/lib/route/cls/ematch/meta.c (revision 4dc78e53d49367fa8e61b07018507c90983a077d)
1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3  * Copyright (c) 2010-2013 Thomas Graf <[email protected]>
4  */
5 
6 /**
7  * @ingroup ematch
8  * @defgroup em_meta Metadata Match
9  *
10  * @{
11  */
12 
13 #include "nl-default.h"
14 
15 #include <linux/tc_ematch/tc_em_meta.h>
16 
17 #include <netlink/netlink.h>
18 #include <netlink/route/cls/ematch.h>
19 #include <netlink/route/cls/ematch/meta.h>
20 
21 #include "nl-priv-dynamic-core/nl-core.h"
22 
23 struct rtnl_meta_value
24 {
25 	uint8_t			mv_type;
26 	uint8_t			mv_shift;
27 	uint16_t		mv_id;
28 	size_t			mv_len;
29 };
30 
31 struct meta_data
32 {
33 	struct rtnl_meta_value *	left;
34 	struct rtnl_meta_value *	right;
35 	uint8_t				opnd;
36 };
37 
meta_alloc(uint8_t type,uint16_t id,uint8_t shift,void * data,size_t len)38 static struct rtnl_meta_value *meta_alloc(uint8_t type, uint16_t id,
39 					  uint8_t shift, void *data,
40 					  size_t len)
41 {
42 	struct rtnl_meta_value *value;
43 
44 	if (!(value = calloc(1, sizeof(*value) + len)))
45 		return NULL;
46 
47 	value->mv_type = type;
48 	value->mv_id = id;
49 	value->mv_shift = shift;
50 	value->mv_len = len;
51 
52 	if (len)
53 		memcpy(value + 1, data, len);
54 
55 	return value;
56 }
57 
rtnl_meta_value_alloc_int(uint64_t value)58 struct rtnl_meta_value *rtnl_meta_value_alloc_int(uint64_t value)
59 {
60 	return meta_alloc(TCF_META_TYPE_INT, TCF_META_ID_VALUE, 0, &value, 8);
61 }
62 
rtnl_meta_value_alloc_var(void * data,size_t len)63 struct rtnl_meta_value *rtnl_meta_value_alloc_var(void *data, size_t len)
64 {
65 	return meta_alloc(TCF_META_TYPE_VAR, TCF_META_ID_VALUE, 0, data, len);
66 }
67 
rtnl_meta_value_alloc_id(uint8_t type,uint16_t id,uint8_t shift,uint64_t mask)68 struct rtnl_meta_value *rtnl_meta_value_alloc_id(uint8_t type, uint16_t id,
69 						 uint8_t shift, uint64_t mask)
70 {
71 	size_t masklen = 0;
72 
73 	if (id > TCF_META_ID_MAX)
74 		return NULL;
75 
76 	if (mask) {
77 		if (type == TCF_META_TYPE_VAR)
78 			return NULL;
79 
80 		masklen = 8;
81 	}
82 
83 	return meta_alloc(type, id, shift, &mask, masklen);
84 }
85 
rtnl_meta_value_put(struct rtnl_meta_value * mv)86 void rtnl_meta_value_put(struct rtnl_meta_value *mv)
87 {
88 	free(mv);
89 }
90 
rtnl_ematch_meta_set_lvalue(struct rtnl_ematch * e,struct rtnl_meta_value * v)91 void rtnl_ematch_meta_set_lvalue(struct rtnl_ematch *e, struct rtnl_meta_value *v)
92 {
93 	struct meta_data *m = rtnl_ematch_data(e);
94 	m->left = v;
95 }
96 
rtnl_ematch_meta_set_rvalue(struct rtnl_ematch * e,struct rtnl_meta_value * v)97 void rtnl_ematch_meta_set_rvalue(struct rtnl_ematch *e, struct rtnl_meta_value *v)
98 {
99 	struct meta_data *m = rtnl_ematch_data(e);
100 	m->right = v;
101 }
102 
rtnl_ematch_meta_set_operand(struct rtnl_ematch * e,uint8_t opnd)103 void rtnl_ematch_meta_set_operand(struct rtnl_ematch *e, uint8_t opnd)
104 {
105 	struct meta_data *m = rtnl_ematch_data(e);
106 	m->opnd = opnd;
107 }
108 
109 static struct nla_policy meta_policy[TCA_EM_META_MAX+1] = {
110 	[TCA_EM_META_HDR]	= { .minlen = sizeof(struct tcf_meta_hdr) },
111 	[TCA_EM_META_LVALUE]	= { .minlen = 1, },
112 	[TCA_EM_META_RVALUE]	= { .minlen = 1, },
113 };
114 
meta_parse(struct rtnl_ematch * e,void * data,size_t len)115 static int meta_parse(struct rtnl_ematch *e, void *data, size_t len)
116 {
117 	struct meta_data *m = rtnl_ematch_data(e);
118 	struct nlattr *tb[TCA_EM_META_MAX+1];
119 	struct rtnl_meta_value *v;
120 	struct tcf_meta_hdr *hdr;
121 	void *vdata = NULL;
122 	size_t vlen = 0;
123 	int err;
124 
125 	if ((err = nla_parse(tb, TCA_EM_META_MAX, data, len, meta_policy)) < 0)
126 		return err;
127 
128 	if (!tb[TCA_EM_META_HDR])
129 		return -NLE_MISSING_ATTR;
130 
131 	hdr = nla_data(tb[TCA_EM_META_HDR]);
132 
133 	if (tb[TCA_EM_META_LVALUE]) {
134 		vdata = nla_data(tb[TCA_EM_META_LVALUE]);
135 		vlen = nla_len(tb[TCA_EM_META_LVALUE]);
136 	}
137 
138 	v = meta_alloc(TCF_META_TYPE(hdr->left.kind),
139 		       TCF_META_ID(hdr->left.kind),
140 		       hdr->left.shift, vdata, vlen);
141 	if (!v)
142 		return -NLE_NOMEM;
143 
144 	m->left = v;
145 
146 	vlen = 0;
147 	if (tb[TCA_EM_META_RVALUE]) {
148 		vdata = nla_data(tb[TCA_EM_META_RVALUE]);
149 		vlen = nla_len(tb[TCA_EM_META_RVALUE]);
150 	}
151 
152 	v = meta_alloc(TCF_META_TYPE(hdr->right.kind),
153 		       TCF_META_ID(hdr->right.kind),
154 		       hdr->right.shift, vdata, vlen);
155 	if (!v) {
156 		rtnl_meta_value_put(m->left);
157 		return -NLE_NOMEM;
158 	}
159 
160 	m->right = v;
161 	m->opnd = hdr->left.op;
162 
163 	return 0;
164 }
165 
166 static const struct trans_tbl meta_int[] = {
167 	__ADD(TCF_META_ID_RANDOM, random),
168 	__ADD(TCF_META_ID_LOADAVG_0, loadavg_0),
169 	__ADD(TCF_META_ID_LOADAVG_1, loadavg_1),
170 	__ADD(TCF_META_ID_LOADAVG_2, loadavg_2),
171 	__ADD(TCF_META_ID_DEV, dev),
172 	__ADD(TCF_META_ID_PRIORITY, prio),
173 	__ADD(TCF_META_ID_PROTOCOL, proto),
174 	__ADD(TCF_META_ID_PKTTYPE, pkttype),
175 	__ADD(TCF_META_ID_PKTLEN, pktlen),
176 	__ADD(TCF_META_ID_DATALEN, datalen),
177 	__ADD(TCF_META_ID_MACLEN, maclen),
178 	__ADD(TCF_META_ID_NFMARK, mark),
179 	__ADD(TCF_META_ID_TCINDEX, tcindex),
180 	__ADD(TCF_META_ID_RTCLASSID, rtclassid),
181 	__ADD(TCF_META_ID_RTIIF, rtiif),
182 	__ADD(TCF_META_ID_SK_FAMILY, sk_family),
183 	__ADD(TCF_META_ID_SK_STATE, sk_state),
184 	__ADD(TCF_META_ID_SK_REUSE, sk_reuse),
185 	__ADD(TCF_META_ID_SK_REFCNT, sk_refcnt),
186 	__ADD(TCF_META_ID_SK_RCVBUF, sk_rcvbuf),
187 	__ADD(TCF_META_ID_SK_SNDBUF, sk_sndbuf),
188 	__ADD(TCF_META_ID_SK_SHUTDOWN, sk_sutdown),
189 	__ADD(TCF_META_ID_SK_PROTO, sk_proto),
190 	__ADD(TCF_META_ID_SK_TYPE, sk_type),
191 	__ADD(TCF_META_ID_SK_RMEM_ALLOC, sk_rmem_alloc),
192 	__ADD(TCF_META_ID_SK_WMEM_ALLOC, sk_wmem_alloc),
193 	__ADD(TCF_META_ID_SK_WMEM_QUEUED, sk_wmem_queued),
194 	__ADD(TCF_META_ID_SK_RCV_QLEN, sk_rcv_qlen),
195 	__ADD(TCF_META_ID_SK_SND_QLEN, sk_snd_qlen),
196 	__ADD(TCF_META_ID_SK_ERR_QLEN, sk_err_qlen),
197 	__ADD(TCF_META_ID_SK_FORWARD_ALLOCS, sk_forward_allocs),
198 	__ADD(TCF_META_ID_SK_ALLOCS, sk_allocs),
199 	__ADD(__TCF_META_ID_SK_ROUTE_CAPS, sk_route_caps),
200 	__ADD(TCF_META_ID_SK_HASH, sk_hash),
201 	__ADD(TCF_META_ID_SK_LINGERTIME, sk_lingertime),
202 	__ADD(TCF_META_ID_SK_ACK_BACKLOG, sk_ack_backlog),
203 	__ADD(TCF_META_ID_SK_MAX_ACK_BACKLOG, sk_max_ack_backlog),
204 	__ADD(TCF_META_ID_SK_PRIO, sk_prio),
205 	__ADD(TCF_META_ID_SK_RCVLOWAT, sk_rcvlowat),
206 	__ADD(TCF_META_ID_SK_RCVTIMEO, sk_rcvtimeo),
207 	__ADD(TCF_META_ID_SK_SNDTIMEO, sk_sndtimeo),
208 	__ADD(TCF_META_ID_SK_SENDMSG_OFF, sk_sendmsg_off),
209 	__ADD(TCF_META_ID_SK_WRITE_PENDING, sk_write_pending),
210 	__ADD(TCF_META_ID_VLAN_TAG, vlan),
211 	__ADD(TCF_META_ID_RXHASH, rxhash),
212 };
213 
int_id2str(int id,char * buf,size_t size)214 static char *int_id2str(int id, char *buf, size_t size)
215 {
216 	return __type2str(id, buf, size, meta_int, ARRAY_SIZE(meta_int));
217 }
218 
219 static const struct trans_tbl meta_var[] = {
220 	__ADD(TCF_META_ID_DEV,devname),
221 	__ADD(TCF_META_ID_SK_BOUND_IF,sk_bound_if),
222 };
223 
var_id2str(int id,char * buf,size_t size)224 static char *var_id2str(int id, char *buf, size_t size)
225 {
226 	return __type2str(id, buf, size, meta_var, ARRAY_SIZE(meta_var));
227 }
228 
dump_value(struct rtnl_meta_value * v,struct nl_dump_params * p)229 static void dump_value(struct rtnl_meta_value *v, struct nl_dump_params *p)
230 {
231 	char buf[32];
232 
233 	switch (v->mv_type) {
234 		case TCF_META_TYPE_INT:
235 			if (v->mv_id == TCF_META_ID_VALUE) {
236 				nl_dump(p, "%u",
237 					*(uint32_t *) (v + 1));
238 			} else {
239 				nl_dump(p, "%s",
240 					int_id2str(v->mv_id, buf, sizeof(buf)));
241 
242 				if (v->mv_shift)
243 					nl_dump(p, " >> %u", v->mv_shift);
244 
245 				if (v->mv_len == 4)
246 					nl_dump(p, " & %#lx", (long unsigned) *(uint32_t *) (v + 1));
247 				else if (v->mv_len == 8)
248 					nl_dump(p, " & %#llx", (long long unsigned) (*(uint64_t *) (v + 1)));
249 			}
250 		break;
251 
252 		case TCF_META_TYPE_VAR:
253 			if (v->mv_id == TCF_META_ID_VALUE) {
254 				nl_dump(p, "%s", (char *) (v + 1));
255 			} else {
256 				nl_dump(p, "%s",
257 					var_id2str(v->mv_id, buf, sizeof(buf)));
258 
259 				if (v->mv_shift)
260 					nl_dump(p, " >> %u", v->mv_shift);
261 			}
262 		break;
263 	}
264 }
265 
meta_dump(struct rtnl_ematch * e,struct nl_dump_params * p)266 static void meta_dump(struct rtnl_ematch *e, struct nl_dump_params *p)
267 {
268 	struct meta_data *m = rtnl_ematch_data(e);
269 	char buf[32];
270 
271 	nl_dump(p, "meta(");
272 	dump_value(m->left, p);
273 
274 	nl_dump(p, " %s ", rtnl_ematch_opnd2txt(m->opnd, buf, sizeof(buf)));
275 
276 	dump_value(m->right, p);
277 	nl_dump(p, ")");
278 }
279 
meta_fill(struct rtnl_ematch * e,struct nl_msg * msg)280 static int meta_fill(struct rtnl_ematch *e, struct nl_msg *msg)
281 {
282 	struct meta_data *m = rtnl_ematch_data(e);
283 	struct tcf_meta_hdr hdr;
284 
285 	if (!(m->left && m->right))
286 		return -NLE_MISSING_ATTR;
287 
288 	memset(&hdr, 0, sizeof(hdr));
289 	hdr.left.kind = (m->left->mv_type << 12) & TCF_META_TYPE_MASK;
290 	hdr.left.kind |= m->left->mv_id & TCF_META_ID_MASK;
291 	hdr.left.shift = m->left->mv_shift;
292 	hdr.left.op = m->opnd;
293 	hdr.right.kind = (m->right->mv_type << 12) & TCF_META_TYPE_MASK;
294 	hdr.right.kind |= m->right->mv_id & TCF_META_ID_MASK;
295 
296 	NLA_PUT(msg, TCA_EM_META_HDR, sizeof(hdr), &hdr);
297 
298 	if (m->left->mv_len)
299 		NLA_PUT(msg, TCA_EM_META_LVALUE, m->left->mv_len, (m->left + 1));
300 
301 	if (m->right->mv_len)
302 		NLA_PUT(msg, TCA_EM_META_RVALUE, m->right->mv_len, (m->right + 1));
303 
304 	return 0;
305 
306 nla_put_failure:
307 	return -NLE_NOMEM;
308 }
309 
meta_free(struct rtnl_ematch * e)310 static void meta_free(struct rtnl_ematch *e)
311 {
312 	struct meta_data *m = rtnl_ematch_data(e);
313 	free(m->left);
314 	free(m->right);
315 }
316 
317 static struct rtnl_ematch_ops meta_ops = {
318 	.eo_kind	= TCF_EM_META,
319 	.eo_name	= "meta",
320 	.eo_minlen	= sizeof(struct tcf_meta_hdr),
321 	.eo_datalen	= sizeof(struct meta_data),
322 	.eo_parse	= meta_parse,
323 	.eo_dump	= meta_dump,
324 	.eo_fill	= meta_fill,
325 	.eo_free	= meta_free,
326 };
327 
meta_init(void)328 static void _nl_init meta_init(void)
329 {
330 	rtnl_ematch_register(&meta_ops);
331 }
332 
333 /** @} */
334