You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
263 lines
5.6 KiB
263 lines
5.6 KiB
/* SPDX-License-Identifier: LGPL-2.1-only */
|
|
/*
|
|
* 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) {
|
|
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 ((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;
|
|
}
|
|
|
|
/** @} */
|