libnl
3.2.3
|
00001 /* 00002 * lib/route/cls/ematch/meta.c Metadata Match 00003 * 00004 * This library is free software; you can redistribute it and/or 00005 * modify it under the terms of the GNU Lesser General Public 00006 * License as published by the Free Software Foundation version 2.1 00007 * of the License. 00008 * 00009 * Copyright (c) 2010 Thomas Graf <tgraf@suug.ch> 00010 */ 00011 00012 /** 00013 * @ingroup ematch 00014 * @defgroup em_meta Metadata Match 00015 * 00016 * @{ 00017 */ 00018 00019 #include <netlink-local.h> 00020 #include <netlink-tc.h> 00021 #include <netlink/netlink.h> 00022 #include <netlink/route/cls/ematch.h> 00023 #include <netlink/route/cls/ematch/meta.h> 00024 00025 struct rtnl_meta_value 00026 { 00027 uint8_t mv_type; 00028 uint8_t mv_shift; 00029 uint16_t mv_id; 00030 size_t mv_len; 00031 }; 00032 00033 struct meta_data 00034 { 00035 struct rtnl_meta_value * left; 00036 struct rtnl_meta_value * right; 00037 uint8_t opnd; 00038 }; 00039 00040 static struct rtnl_meta_value *meta_alloc(uint8_t type, uint16_t id, 00041 uint8_t shift, void *data, 00042 size_t len) 00043 { 00044 struct rtnl_meta_value *value; 00045 00046 if (!(value = calloc(1, sizeof(*value) + len))) 00047 return NULL; 00048 00049 value->mv_type = type; 00050 value->mv_id = id; 00051 value->mv_shift = shift; 00052 value->mv_len = len; 00053 00054 memcpy(value + 1, data, len); 00055 00056 return value; 00057 } 00058 00059 struct rtnl_meta_value *rtnl_meta_value_alloc_int(uint64_t value) 00060 { 00061 return meta_alloc(TCF_META_TYPE_INT, TCF_META_ID_VALUE, 0, &value, 8); 00062 } 00063 00064 struct rtnl_meta_value *rtnl_meta_value_alloc_var(void *data, size_t len) 00065 { 00066 return meta_alloc(TCF_META_TYPE_VAR, TCF_META_ID_VALUE, 0, data, len); 00067 } 00068 00069 struct rtnl_meta_value *rtnl_meta_value_alloc_id(uint8_t type, uint16_t id, 00070 uint8_t shift, uint64_t mask) 00071 { 00072 size_t masklen = 0; 00073 00074 if (id > TCF_META_ID_MAX) 00075 return NULL; 00076 00077 if (mask) { 00078 if (type == TCF_META_TYPE_VAR) 00079 return NULL; 00080 00081 masklen = 8; 00082 } 00083 00084 return meta_alloc(type, id, shift, &mask, masklen); 00085 } 00086 00087 void rtnl_meta_value_put(struct rtnl_meta_value *mv) 00088 { 00089 free(mv); 00090 } 00091 00092 void rtnl_ematch_meta_set_lvalue(struct rtnl_ematch *e, struct rtnl_meta_value *v) 00093 { 00094 struct meta_data *m = rtnl_ematch_data(e); 00095 m->left = v; 00096 } 00097 00098 void rtnl_ematch_meta_set_rvalue(struct rtnl_ematch *e, struct rtnl_meta_value *v) 00099 { 00100 struct meta_data *m = rtnl_ematch_data(e); 00101 m->right = v; 00102 } 00103 00104 void rtnl_ematch_meta_set_operand(struct rtnl_ematch *e, uint8_t opnd) 00105 { 00106 struct meta_data *m = rtnl_ematch_data(e); 00107 m->opnd = opnd; 00108 } 00109 00110 static struct nla_policy meta_policy[TCA_EM_META_MAX+1] = { 00111 [TCA_EM_META_HDR] = { .minlen = sizeof(struct tcf_meta_hdr) }, 00112 [TCA_EM_META_LVALUE] = { .minlen = 1, }, 00113 [TCA_EM_META_RVALUE] = { .minlen = 1, }, 00114 }; 00115 00116 static int meta_parse(struct rtnl_ematch *e, void *data, size_t len) 00117 { 00118 struct meta_data *m = rtnl_ematch_data(e); 00119 struct nlattr *tb[TCA_EM_META_MAX+1]; 00120 struct rtnl_meta_value *v; 00121 struct tcf_meta_hdr *hdr; 00122 void *vdata = NULL; 00123 size_t vlen = 0; 00124 int err; 00125 00126 if ((err = nla_parse(tb, TCA_EM_META_MAX, data, len, meta_policy)) < 0) 00127 return err; 00128 00129 if (!tb[TCA_EM_META_HDR]) 00130 return -NLE_MISSING_ATTR; 00131 00132 hdr = nla_data(tb[TCA_EM_META_HDR]); 00133 00134 if (tb[TCA_EM_META_LVALUE]) { 00135 vdata = nla_data(tb[TCA_EM_META_LVALUE]); 00136 vlen = nla_len(tb[TCA_EM_META_LVALUE]); 00137 } 00138 00139 v = meta_alloc(TCF_META_TYPE(hdr->left.kind), 00140 TCF_META_ID(hdr->left.kind), 00141 hdr->left.shift, vdata, vlen); 00142 if (!v) 00143 return -NLE_NOMEM; 00144 00145 m->left = v; 00146 00147 vlen = 0; 00148 if (tb[TCA_EM_META_RVALUE]) { 00149 vdata = nla_data(tb[TCA_EM_META_RVALUE]); 00150 vlen = nla_len(tb[TCA_EM_META_RVALUE]); 00151 } 00152 00153 v = meta_alloc(TCF_META_TYPE(hdr->right.kind), 00154 TCF_META_ID(hdr->right.kind), 00155 hdr->right.shift, vdata, vlen); 00156 if (!v) { 00157 rtnl_meta_value_put(m->left); 00158 return -NLE_NOMEM; 00159 } 00160 00161 m->right = v; 00162 m->opnd = hdr->left.op; 00163 00164 return 0; 00165 } 00166 00167 static const struct trans_tbl meta_int[] = { 00168 __ADD(TCF_META_ID_RANDOM, random) 00169 __ADD(TCF_META_ID_LOADAVG_0, loadavg_0) 00170 __ADD(TCF_META_ID_LOADAVG_1, loadavg_1) 00171 __ADD(TCF_META_ID_LOADAVG_2, loadavg_2) 00172 __ADD(TCF_META_ID_DEV, dev) 00173 __ADD(TCF_META_ID_PRIORITY, prio) 00174 __ADD(TCF_META_ID_PROTOCOL, proto) 00175 __ADD(TCF_META_ID_PKTTYPE, pkttype) 00176 __ADD(TCF_META_ID_PKTLEN, pktlen) 00177 __ADD(TCF_META_ID_DATALEN, datalen) 00178 __ADD(TCF_META_ID_MACLEN, maclen) 00179 __ADD(TCF_META_ID_NFMARK, mark) 00180 __ADD(TCF_META_ID_TCINDEX, tcindex) 00181 __ADD(TCF_META_ID_RTCLASSID, rtclassid) 00182 __ADD(TCF_META_ID_RTIIF, rtiif) 00183 __ADD(TCF_META_ID_SK_FAMILY, sk_family) 00184 __ADD(TCF_META_ID_SK_STATE, sk_state) 00185 __ADD(TCF_META_ID_SK_REUSE, sk_reuse) 00186 __ADD(TCF_META_ID_SK_REFCNT, sk_refcnt) 00187 __ADD(TCF_META_ID_SK_RCVBUF, sk_rcvbuf) 00188 __ADD(TCF_META_ID_SK_SNDBUF, sk_sndbuf) 00189 __ADD(TCF_META_ID_SK_SHUTDOWN, sk_sutdown) 00190 __ADD(TCF_META_ID_SK_PROTO, sk_proto) 00191 __ADD(TCF_META_ID_SK_TYPE, sk_type) 00192 __ADD(TCF_META_ID_SK_RMEM_ALLOC, sk_rmem_alloc) 00193 __ADD(TCF_META_ID_SK_WMEM_ALLOC, sk_wmem_alloc) 00194 __ADD(TCF_META_ID_SK_WMEM_QUEUED, sk_wmem_queued) 00195 __ADD(TCF_META_ID_SK_RCV_QLEN, sk_rcv_qlen) 00196 __ADD(TCF_META_ID_SK_SND_QLEN, sk_snd_qlen) 00197 __ADD(TCF_META_ID_SK_ERR_QLEN, sk_err_qlen) 00198 __ADD(TCF_META_ID_SK_FORWARD_ALLOCS, sk_forward_allocs) 00199 __ADD(TCF_META_ID_SK_ALLOCS, sk_allocs) 00200 __ADD(TCF_META_ID_SK_ROUTE_CAPS, sk_route_caps) 00201 __ADD(TCF_META_ID_SK_HASH, sk_hash) 00202 __ADD(TCF_META_ID_SK_LINGERTIME, sk_lingertime) 00203 __ADD(TCF_META_ID_SK_ACK_BACKLOG, sk_ack_backlog) 00204 __ADD(TCF_META_ID_SK_MAX_ACK_BACKLOG, sk_max_ack_backlog) 00205 __ADD(TCF_META_ID_SK_PRIO, sk_prio) 00206 __ADD(TCF_META_ID_SK_RCVLOWAT, sk_rcvlowat) 00207 __ADD(TCF_META_ID_SK_RCVTIMEO, sk_rcvtimeo) 00208 __ADD(TCF_META_ID_SK_SNDTIMEO, sk_sndtimeo) 00209 __ADD(TCF_META_ID_SK_SENDMSG_OFF, sk_sendmsg_off) 00210 __ADD(TCF_META_ID_SK_WRITE_PENDING, sk_write_pending) 00211 __ADD(TCF_META_ID_VLAN_TAG, vlan) 00212 __ADD(TCF_META_ID_RXHASH, rxhash) 00213 }; 00214 00215 static char *int_id2str(int id, char *buf, size_t size) 00216 { 00217 return __type2str(id, buf, size, meta_int, ARRAY_SIZE(meta_int)); 00218 } 00219 00220 static const struct trans_tbl meta_var[] = { 00221 __ADD(TCF_META_ID_DEV,devname) 00222 __ADD(TCF_META_ID_SK_BOUND_IF,sk_bound_if) 00223 }; 00224 00225 static char *var_id2str(int id, char *buf, size_t size) 00226 { 00227 return __type2str(id, buf, size, meta_var, ARRAY_SIZE(meta_var)); 00228 } 00229 00230 static void dump_value(struct rtnl_meta_value *v, struct nl_dump_params *p) 00231 { 00232 char buf[32]; 00233 00234 switch (v->mv_type) { 00235 case TCF_META_TYPE_INT: 00236 if (v->mv_id == TCF_META_ID_VALUE) { 00237 nl_dump(p, "%u", 00238 *(uint32_t *) (v + 1)); 00239 } else { 00240 nl_dump(p, "%s", 00241 int_id2str(v->mv_id, buf, sizeof(buf))); 00242 00243 if (v->mv_shift) 00244 nl_dump(p, " >> %u", v->mv_shift); 00245 00246 if (v->mv_len == 4) 00247 nl_dump(p, " & %#x", *(uint32_t *) (v + 1)); 00248 else if (v->mv_len == 8) 00249 nl_dump(p, " & %#x", *(uint64_t *) (v + 1)); 00250 } 00251 break; 00252 00253 case TCF_META_TYPE_VAR: 00254 if (v->mv_id == TCF_META_ID_VALUE) { 00255 nl_dump(p, "%s", (char *) (v + 1)); 00256 } else { 00257 nl_dump(p, "%s", 00258 var_id2str(v->mv_id, buf, sizeof(buf))); 00259 00260 if (v->mv_shift) 00261 nl_dump(p, " >> %u", v->mv_shift); 00262 } 00263 break; 00264 } 00265 } 00266 00267 static void meta_dump(struct rtnl_ematch *e, struct nl_dump_params *p) 00268 { 00269 struct meta_data *m = rtnl_ematch_data(e); 00270 char buf[32]; 00271 00272 nl_dump(p, "meta("); 00273 dump_value(m->left, p); 00274 00275 nl_dump(p, " %s ", rtnl_ematch_opnd2txt(m->opnd, buf, sizeof(buf))); 00276 00277 dump_value(m->right, p); 00278 nl_dump(p, ")"); 00279 } 00280 00281 static int meta_fill(struct rtnl_ematch *e, struct nl_msg *msg) 00282 { 00283 struct meta_data *m = rtnl_ematch_data(e); 00284 struct tcf_meta_hdr hdr; 00285 00286 if (!(m->left && m->right)) 00287 return -NLE_MISSING_ATTR; 00288 00289 memset(&hdr, 0, sizeof(hdr)); 00290 hdr.left.kind = (m->left->mv_type << 12) & TCF_META_TYPE_MASK; 00291 hdr.left.kind |= m->left->mv_id & TCF_META_ID_MASK; 00292 hdr.left.shift = m->left->mv_shift; 00293 hdr.left.op = m->opnd; 00294 hdr.right.kind = (m->right->mv_type << 12) & TCF_META_TYPE_MASK; 00295 hdr.right.kind |= m->right->mv_id & TCF_META_ID_MASK; 00296 00297 NLA_PUT(msg, TCA_EM_META_HDR, sizeof(hdr), &hdr); 00298 00299 if (m->left->mv_len) 00300 NLA_PUT(msg, TCA_EM_META_LVALUE, m->left->mv_len, (m->left + 1)); 00301 00302 if (m->right->mv_len) 00303 NLA_PUT(msg, TCA_EM_META_RVALUE, m->right->mv_len, (m->right + 1)); 00304 00305 return 0; 00306 00307 nla_put_failure: 00308 return -NLE_NOMEM; 00309 } 00310 00311 static void meta_free(struct rtnl_ematch *e) 00312 { 00313 struct meta_data *m = rtnl_ematch_data(e); 00314 free(m->left); 00315 free(m->right); 00316 } 00317 00318 static struct rtnl_ematch_ops meta_ops = { 00319 .eo_kind = TCF_EM_META, 00320 .eo_name = "meta", 00321 .eo_minlen = sizeof(struct tcf_meta_hdr), 00322 .eo_datalen = sizeof(struct meta_data), 00323 .eo_parse = meta_parse, 00324 .eo_dump = meta_dump, 00325 .eo_fill = meta_fill, 00326 .eo_free = meta_free, 00327 }; 00328 00329 static void __init meta_init(void) 00330 { 00331 rtnl_ematch_register(&meta_ops); 00332 } 00333 00334 /** @} */