/* * lib/route/pktloc.c Packet Location Aliasing * * 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-2013 Thomas Graf <tgraf@suug.ch> */ /** * @ingroup tc * @defgroup pktloc Packet Location Aliasing * Packet Location Aliasing * * The packet location aliasing interface eases the use of offset definitions * inside packets by allowing them to be referenced by name. Known positions * of protocol fields are stored in a configuration file and associated with * a name for later reference. The configuration file is distributed with the * library and provides a well defined set of definitions for most common * protocol fields. * * @section pktloc_examples Examples * @par Example 1.1 Looking up a packet location * @code * struct rtnl_pktloc *loc; * * rtnl_pktloc_lookup("ip.src", &loc); * @endcode * @{ */ #include <netlink-private/netlink.h> #include <netlink-private/tc.h> #include <netlink/netlink.h> #include <netlink/utils.h> #include <netlink/route/pktloc.h> #include "pktloc_syntax.h" #include "pktloc_grammar.h" /** @cond SKIP */ #define PKTLOC_NAME_HT_SIZ 256 static struct nl_list_head pktloc_name_ht[PKTLOC_NAME_HT_SIZ]; /* djb2 */ static unsigned int pktloc_hash(const char *str) { unsigned long hash = 5381; int c; while ((c = *str++)) hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ return hash % PKTLOC_NAME_HT_SIZ; } static int __pktloc_lookup(const char *name, struct rtnl_pktloc **result) { struct rtnl_pktloc *loc; int hash; hash = pktloc_hash(name); nl_list_for_each_entry(loc, &pktloc_name_ht[hash], list) { if (!strcasecmp(loc->name, name)) { loc->refcnt++; *result = loc; return 0; } } return -NLE_OBJ_NOTFOUND; } extern int pktloc_parse(void *scanner); static void rtnl_pktloc_free(struct rtnl_pktloc *loc) { if (!loc) return; free(loc->name); free(loc); } static int read_pktlocs(void) { YY_BUFFER_STATE buf = NULL; yyscan_t scanner = NULL; static time_t last_read; struct stat st; char *path; int i, err; FILE *fd; if (build_sysconf_path(&path, "pktloc") < 0) return -NLE_NOMEM; /* if stat fails, just try to read the file */ if (stat(path, &st) == 0) { /* Don't re-read file if file is unchanged */ if (last_read == st.st_mtime) return 0; } NL_DBG(2, "Reading packet location file \"%s\"\n", path); if (!(fd = fopen(path, "r"))) { err = -NLE_PKTLOC_FILE; goto errout; } for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++) { struct rtnl_pktloc *loc, *n; nl_list_for_each_entry_safe(loc, n, &pktloc_name_ht[i], list) rtnl_pktloc_put(loc); nl_init_list_head(&pktloc_name_ht[i]); } if ((err = pktloc_lex_init(&scanner)) < 0) { err = -NLE_FAILURE; goto errout_close; } buf = pktloc__create_buffer(fd, YY_BUF_SIZE, scanner); pktloc__switch_to_buffer(buf, scanner); if ((err = pktloc_parse(scanner)) != 0) { pktloc__delete_buffer(buf, scanner); err = -NLE_PARSE_ERR; goto errout_scanner; } last_read = st.st_mtime; errout_scanner: pktloc_lex_destroy(scanner); errout_close: fclose(fd); errout: free(path); return err; } /** @endcond */ /** * Lookup packet location alias * @arg name Name of packet location. * @arg result Result pointer * * Tries to find a matching packet location alias for the supplied * packet location name. * * The file containing the packet location definitions is automatically * re-read if its modification time has changed since the last call. * * The returned packet location has to be returned after use by calling * rtnl_pktloc_put() in order to allow freeing its memory after the last * user has abandoned it. * * @return 0 on success or a negative error code. * @retval NLE_PKTLOC_FILE Unable to open packet location file. * @retval NLE_OBJ_NOTFOUND No matching packet location alias found. */ int rtnl_pktloc_lookup(const char *name, struct rtnl_pktloc **result) { int err; if ((err = read_pktlocs()) < 0) return err; return __pktloc_lookup(name, result); } /** * Allocate packet location object */ struct rtnl_pktloc *rtnl_pktloc_alloc(void) { struct rtnl_pktloc *loc; if (!(loc = calloc(1, sizeof(*loc)))) return NULL; loc->refcnt = 1; nl_init_list_head(&loc->list); return loc; } /** * Return reference of a packet location * @arg loc packet location object. */ void rtnl_pktloc_put(struct rtnl_pktloc *loc) { if (!loc) return; loc->refcnt--; if (loc->refcnt <= 0) rtnl_pktloc_free(loc); } /** * Add a packet location to the hash table * @arg loc packet location object * * @return 0 on success or a negative error code. */ int rtnl_pktloc_add(struct rtnl_pktloc *loc) { struct rtnl_pktloc *l; if (__pktloc_lookup(loc->name, &l) == 0) { rtnl_pktloc_put(l); return -NLE_EXIST; } NL_DBG(2, "New packet location entry \"%s\" align=%u layer=%u " "offset=%u mask=%#x shift=%u refnt=%u\n", loc->name, loc->align, loc->layer, loc->offset, loc->mask, loc->shift, loc->refcnt); nl_list_add_tail(&loc->list, &pktloc_name_ht[pktloc_hash(loc->name)]); return 0; } void rtnl_pktloc_foreach(void (*cb)(struct rtnl_pktloc *, void *), void *arg) { struct rtnl_pktloc *loc; int i; /* ignore errors */ read_pktlocs(); for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++) nl_list_for_each_entry(loc, &pktloc_name_ht[i], list) cb(loc, arg); } static int __init pktloc_init(void) { int i; for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++) nl_init_list_head(&pktloc_name_ht[i]); return 0; } /** @} */