Home | History | Annotate | Download | only in ematch
      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 (at) 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 
     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 
     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 
     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 
     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 
     87 void rtnl_meta_value_put(struct rtnl_meta_value *mv)
     88 {
     89 	free(mv);
     90 }
     91 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    329 static void __init meta_init(void)
    330 {
    331 	rtnl_ematch_register(&meta_ops);
    332 }
    333 
    334 /** @} */
    335