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