| /* SPDX-License-Identifier: LGPL-2.1-only */ |
| /* |
| * Copyright (c) 2008-2013 Thomas Graf <[email protected]> |
| */ |
| |
| /** |
| * @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 "nl-default.h" |
| |
| #include <sys/stat.h> |
| |
| #include <netlink/netlink.h> |
| #include <netlink/utils.h> |
| #include <netlink/route/pktloc.h> |
| |
| #include "nl-route.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) { |
| err = 0; |
| goto errout; |
| } |
| } |
| |
| NL_DBG(2, "Reading packet location file \"%s\"\n", path); |
| |
| if (!(fd = fopen(path, "re"))) { |
| 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 (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 _nl_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; |
| } |
| |
| /** @} */ |