/* * 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 General Public License as * published by the Free Software Foundation version 2 of the License. * * Copyright (c) 2008-2010 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. * * @subsection 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-local.h> #include <netlink-tc.h> #include <netlink/netlink.h> #include <netlink/utils.h> #include <netlink/route/pktloc.h> #include "pktloc_syntax.h" #include "pktloc_grammar.h" /** @cond */ #define PKTLOC_NAME_HT_SIZ 256 static struct nl_list_head pktloc_name_ht[PKTLOC_NAME_HT_SIZ]; /* djb2 */ 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; } void rtnl_pktloc_add(struct rtnl_pktloc *loc) { nl_list_add_tail(&loc->list, &pktloc_name_ht[pktloc_hash(loc->name)]); } extern int pktloc_parse(void *scanner); /** @endcond */ 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; yyscan_t scanner = NULL; static time_t last_read; struct stat st = {0}; char *path; int i, err; FILE *fd; asprintf(&path, "%s/pktloc", SYSCONFDIR); /* 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; } if (!(fd = fopen(path, "r"))) return -NLE_PKTLOC_FILE; 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_free(loc); nl_init_list_head(&pktloc_name_ht[i]); } if ((err = pktloc_lex_init(&scanner)) < 0) return -NLE_FAILURE; buf = pktloc__create_buffer(fd, YY_BUF_SIZE, scanner); pktloc__switch_to_buffer(buf, scanner); if ((err = pktloc_parse(scanner)) < 0) return -NLE_FAILURE; if (scanner) pktloc_lex_destroy(scanner); free(path); last_read = st.st_mtime; return 0; } /** * Lookup packet location alias * @arg name Name of packet location. * * 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. * * @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) { struct rtnl_pktloc *loc; int hash, err; if ((err = read_pktlocs()) < 0) return err; hash = pktloc_hash(name); nl_list_for_each_entry(loc, &pktloc_name_ht[hash], list) { if (!strcasecmp(loc->name, name)) { *result = loc; return 0; } } return -NLE_OBJ_NOTFOUND; } 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; }