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