meta.c

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 /** @} */