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