Logo Search packages:      
Sourcecode: libnl2 version File versions  Download package

ematch.c

/*
 * lib/route/cls/ematch.c     Extended Matches
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation version 2.1
 *    of the License.
 *
 * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
 */

/**
 * @ingroup cls
 * @defgroup ematch Extended Match
 *
 * @{
 */

#include <netlink-local.h>
#include <netlink-tc.h>
#include <netlink/netlink.h>
#include <netlink/route/classifier.h>
#include <netlink/route/classifier-modules.h>
#include <netlink/route/cls/ematch.h>

/**
 * @name Module Registration
 * @{
 */

static NL_LIST_HEAD(ematch_ops_list);

/**
 * Register ematch module
 * @arg ops       Module operations.
 *
 * @return 0 on success or a negative error code.
 */
00039 int rtnl_ematch_register(struct rtnl_ematch_ops *ops)
{
      if (rtnl_ematch_lookup_ops(ops->eo_kind))
            return -NLE_EXIST;

      nl_list_add_tail(&ops->eo_list, &ematch_ops_list);

      return 0;
}

/**
 * Unregister ematch module
 * @arg ops       Module operations.
 *
 * @return 0 on success or a negative error code.
 */
00055 int rtnl_ematch_unregister(struct rtnl_ematch_ops *ops)
{
      struct rtnl_ematch_ops *o;

      nl_list_for_each_entry(o, &ematch_ops_list, eo_list) {
            if (ops->eo_kind == o->eo_kind) {
                  nl_list_del(&o->eo_list);
                  return 0;
            }
      }

      return -NLE_OBJ_NOTFOUND;
}

/**
 * Lookup ematch module by kind
 * @arg kind            Module kind.
 *
 * @return Module operations or NULL if not found.
 */
00075 struct rtnl_ematch_ops *rtnl_ematch_lookup_ops(int kind)
{
      struct rtnl_ematch_ops *ops;

      nl_list_for_each_entry(ops, &ematch_ops_list, eo_list)
            if (ops->eo_kind == kind)
                  return ops;

      return NULL;
}

/**
 * Lookup ematch module by name
 * @arg name            Name of ematch module.
 *
 * @return Module operations or NULL if not fuond.
 */
00092 struct rtnl_ematch_ops *rtnl_ematch_lookup_ops_name(const char *name)
{
      struct rtnl_ematch_ops *ops;

      nl_list_for_each_entry(ops, &ematch_ops_list, eo_list)
            if (!strcasecmp(ops->eo_name, name))
                  return ops;

      return NULL;
}

/** @} */

/**
 * @name Match
 */

struct rtnl_ematch *rtnl_ematch_alloc(struct rtnl_ematch_ops *ops)
{
      struct rtnl_ematch *e;
      size_t len = sizeof(*e) + (ops ? ops->eo_datalen : 0);

      if (!(e = calloc(1, len)))
            return NULL;

      NL_INIT_LIST_HEAD(&e->e_list);
      NL_INIT_LIST_HEAD(&e->e_childs);

      if (ops) {
            e->e_ops = ops;
            e->e_kind = ops->eo_kind;
      }

      return e;
}

/**
 * Add ematch to the end of the parent's list of children.
 * @arg parent          Parent ematch.
 * @arg child           Ematch to be added as new child of parent.
 */
00133 void rtnl_ematch_add_child(struct rtnl_ematch *parent,
                     struct rtnl_ematch *child)
{
      nl_list_add_tail(&child->e_list, &parent->e_childs);
}

/**
 * Remove ematch from the list it is linked to.
 * @arg ematch          Ematch to be unlinked.
 */
00143 void rtnl_ematch_unlink(struct rtnl_ematch *ematch)
{
      nl_list_del(&ematch->e_list);
}

void rtnl_ematch_free(struct rtnl_ematch *ematch)
{
      if (!ematch)
            return;

      free(ematch);
}

void rtnl_ematch_set_flags(struct rtnl_ematch *ematch, uint16_t flags)
{
      ematch->e_flags |= flags;
}

void rtnl_ematch_unset_flags(struct rtnl_ematch *ematch, uint16_t flags)
{
      ematch->e_flags &= ~flags;
}

uint16_t rtnl_ematch_get_flags(struct rtnl_ematch *ematch)
{
      return ematch->e_flags;
}

void *rtnl_ematch_data(struct rtnl_ematch *ematch)
{
      return ematch->e_data;
}

/** @} */

/**
 * @name Tree
 */

struct rtnl_ematch_tree *rtnl_ematch_tree_alloc(uint16_t progid)
{
      struct rtnl_ematch_tree *tree;

      if (!(tree = calloc(1, sizeof(*tree))))
            return NULL;

      NL_INIT_LIST_HEAD(&tree->et_list);
      tree->et_progid = progid;

      return tree;
}

static void free_ematch_list(struct nl_list_head *head)
{
      struct rtnl_ematch *pos, *next;

      nl_list_for_each_entry_safe(pos, next, head, e_list) {
            if (!nl_list_empty(&pos->e_childs))
                  free_ematch_list(&pos->e_childs);
            rtnl_ematch_free(pos);
      }
}

void rtnl_ematch_tree_free(struct rtnl_ematch_tree *tree)
{
      if (!tree)
            return;

      free_ematch_list(&tree->et_list);
      free(tree);
}

void rtnl_ematch_tree_add_tail(struct rtnl_ematch_tree *tree,
                         struct rtnl_ematch *ematch)
{
      nl_list_add_tail(&ematch->e_list, &tree->et_list);
}

static inline uint32_t container_ref(struct rtnl_ematch *ematch)
{
      return *((uint32_t *) rtnl_ematch_data(ematch));
}

static int link_tree(struct rtnl_ematch *index[], int nmatches, int pos,
                 struct nl_list_head *root)
{
      struct rtnl_ematch *ematch;
      int i;

      for (i = pos; i < nmatches; i++) {
            ematch = index[i];

            nl_list_add_tail(&ematch->e_list, root);

            if (ematch->e_kind == TCF_EM_CONTAINER)
                  link_tree(index, nmatches, container_ref(ematch),
                          &ematch->e_childs);

            if (!(ematch->e_flags & TCF_EM_REL_MASK))
                  return 0;
      }

      /* Last entry in chain can't possibly have no relation */
      return -NLE_INVAL;
}

static struct nla_policy tree_policy[TCA_EMATCH_TREE_MAX+1] = {
      [TCA_EMATCH_TREE_HDR]  = { .minlen=sizeof(struct tcf_ematch_tree_hdr) },
      [TCA_EMATCH_TREE_LIST] = { .type = NLA_NESTED },
};

/**
 * Parse ematch netlink attributes
 *
 * @return 0 on success or a negative error code.
 */
00259 int rtnl_ematch_parse(struct nlattr *attr, struct rtnl_ematch_tree **result)
{
      struct nlattr *a, *tb[TCA_EMATCH_TREE_MAX+1];
      struct tcf_ematch_tree_hdr *thdr;
      struct rtnl_ematch_tree *tree;
      struct rtnl_ematch **index;
      int nmatches = 0, err, remaining;

      err = nla_parse_nested(tb, TCA_EMATCH_TREE_MAX, attr, tree_policy);
      if (err < 0)
            return err;

      if (!tb[TCA_EMATCH_TREE_HDR])
            return -NLE_MISSING_ATTR;

      thdr = nla_data(tb[TCA_EMATCH_TREE_HDR]);

      /* Ignore empty trees */
      if (thdr->nmatches == 0)
            return 0;

      if (!tb[TCA_EMATCH_TREE_LIST])
            return -NLE_MISSING_ATTR;

      if (thdr->nmatches > (nla_len(tb[TCA_EMATCH_TREE_LIST]) /
                        nla_total_size(sizeof(struct tcf_ematch_hdr))))
            return -NLE_INVAL;

      if (!(index = calloc(thdr->nmatches, sizeof(struct rtnl_ematch *))))
            return -NLE_NOMEM;

      if (!(tree = rtnl_ematch_tree_alloc(thdr->progid))) {
            err = -NLE_NOMEM;
            goto errout;
      }

      nla_for_each_nested(a, tb[TCA_EMATCH_TREE_LIST], remaining) {
            struct rtnl_ematch_ops *ops;
            struct tcf_ematch_hdr *hdr;
            struct rtnl_ematch *ematch;
            void *data;
            size_t len;

            if (nla_len(a) < sizeof(*hdr)) {
                  err = -NLE_INVAL;
                  goto errout;
            }

            if (nmatches >= thdr->nmatches) {
                  err = -NLE_RANGE;
                  goto errout;
            }

            hdr = nla_data(a);
            data = nla_data(a) + NLA_ALIGN(sizeof(*hdr));
            len = nla_len(a) - NLA_ALIGN(sizeof(*hdr));

            ops = rtnl_ematch_lookup_ops(hdr->kind);
            if (ops && ops->eo_datalen && len < ops->eo_datalen) {
                  err = -NLE_INVAL;
                  goto errout;
            }

            if (!(ematch = rtnl_ematch_alloc(ops))) {
                  err = -NLE_NOMEM;
                  goto errout;
            }

            ematch->e_id = hdr->matchid;
            ematch->e_kind = hdr->kind;
            ematch->e_flags = hdr->flags;

            if (ops && (err = ops->eo_parse(ematch, data, len)) < 0)
                  goto errout;

            if (hdr->kind == TCF_EM_CONTAINER &&
                container_ref(ematch) >= thdr->nmatches) {
                  err = -NLE_INVAL;
                  goto errout;
            }

            index[nmatches++] = ematch;
      }

      if (nmatches != thdr->nmatches) {
            err = -NLE_INVAL;
            goto errout;
      }

      err = link_tree(index, nmatches, 0, &tree->et_list);
      if (err < 0)
            goto errout;

      free(index);
      *result = tree;

      return 0;

errout:
      rtnl_ematch_tree_free(tree);
      free(index);
      return err;
}

static void dump_ematch_sequence(struct nl_list_head *head,
                         struct nl_dump_params *p)
{
      struct rtnl_ematch *match;

      nl_list_for_each_entry(match, head, e_list) {
            if (match->e_flags & TCF_EM_INVERT)
                  nl_dump(p, "NOT ");

            if (match->e_kind == TCF_EM_CONTAINER) {
                  nl_dump(p, "(");
                  dump_ematch_sequence(&match->e_childs, p);
                  nl_dump(p, ")");
            } else if (!match->e_ops) {
                  nl_dump(p, "[unknown ematch %d]", match->e_kind);
            } else {
                  nl_dump(p, "%s(", match->e_ops->eo_name);

                  if (match->e_ops->eo_dump)
                        match->e_ops->eo_dump(match, p);

                  nl_dump(p, ")");
            }

            switch (match->e_flags & TCF_EM_REL_MASK) {
            case TCF_EM_REL_AND:
                  nl_dump(p, " AND ");
                  break;
            case TCF_EM_REL_OR:
                  nl_dump(p, " OR ");
                  break;
            default:
                  /* end of first level ematch sequence */
                  return;
            }
      }
}

void rtnl_ematch_tree_dump(struct rtnl_ematch_tree *tree,
                     struct nl_dump_params *p)
{
      dump_ematch_sequence(&tree->et_list, p);
      nl_dump(p, "\n");
}

/** @} */

/** @} */

Generated by  Doxygen 1.6.0   Back to index