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