libnl 3.10.0
meta.c
1/* SPDX-License-Identifier: LGPL-2.1-only */
2/*
3 * Copyright (c) 2010-2013 Thomas Graf <tgraf@suug.ch>
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
24{
25 uint8_t mv_type;
26 uint8_t mv_shift;
27 uint16_t mv_id;
28 size_t mv_len;
29};
30
32{
33 struct rtnl_meta_value * left;
34 struct rtnl_meta_value * right;
35 uint8_t opnd;
36};
37
38static 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
58struct 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
63struct 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
68struct 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
86void rtnl_meta_value_put(struct rtnl_meta_value *mv)
87{
88 free(mv);
89}
90
91void 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
97void 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
103void 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
109static 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
115static 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
166static 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
214static 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
219static 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
224static 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
229static 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
266static 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
280static 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
306nla_put_failure:
307 return -NLE_NOMEM;
308}
309
310static 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
317static 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
328static void _nl_init meta_init(void)
329{
330 rtnl_ematch_register(&meta_ops);
331}
332
333/** @} */
int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len, const struct nla_policy *policy)
Create attribute index based on a stream of attributes.
Definition attr.c:243
void * nla_data(const struct nlattr *nla)
Return pointer to the payload section.
Definition attr.c:119
#define NLA_PUT(msg, attrtype, attrlen, data)
Add unspecific attribute to netlink message.
Definition attr.h:159
int nla_len(const struct nlattr *nla)
Return length of the payload .
Definition attr.c:130
int rtnl_ematch_register(struct rtnl_ematch_ops *ops)
Register ematch module.
Definition ematch.c:44
void nl_dump(struct nl_dump_params *params, const char *fmt,...)
Dump a formatted character string.
Definition utils.c:1015
Dumping parameters.
Definition types.h:32
Attribute validation policy.
Definition attr.h:63
uint16_t minlen
Minimal length of payload required.
Definition attr.h:68
uint16_t type
Type of attribute or NLA_UNSPEC.
Definition attr.h:65
Extended Match Operations.
Definition ematch.h:28