Home | History | Annotate | Download | only in route
      1 /*
      2  * lib/route/pktloc.c     Packet Location Aliasing
      3  *
      4  *	This library is free software; you can redistribute it and/or
      5  *	modify it under the terms of the GNU Lesser General Public
      6  *	License as published by the Free Software Foundation version 2.1
      7  *	of the License.
      8  *
      9  * Copyright (c) 2008-2013 Thomas Graf <tgraf (at) suug.ch>
     10  */
     11 
     12 /**
     13  * @ingroup tc
     14  * @defgroup pktloc Packet Location Aliasing
     15  * Packet Location Aliasing
     16  *
     17  * The packet location aliasing interface eases the use of offset definitions
     18  * inside packets by allowing them to be referenced by name. Known positions
     19  * of protocol fields are stored in a configuration file and associated with
     20  * a name for later reference. The configuration file is distributed with the
     21  * library and provides a well defined set of definitions for most common
     22  * protocol fields.
     23  *
     24  * @section pktloc_examples Examples
     25  * @par Example 1.1 Looking up a packet location
     26  * @code
     27  * struct rtnl_pktloc *loc;
     28  *
     29  * rtnl_pktloc_lookup("ip.src", &loc);
     30  * @endcode
     31  * @{
     32  */
     33 
     34 #include <netlink-private/netlink.h>
     35 #include <netlink-private/tc.h>
     36 #include <netlink/netlink.h>
     37 #include <netlink/utils.h>
     38 #include <netlink/route/pktloc.h>
     39 
     40 #include "pktloc_syntax.h"
     41 #include "pktloc_grammar.h"
     42 
     43 /** @cond SKIP */
     44 #define PKTLOC_NAME_HT_SIZ 256
     45 
     46 static struct nl_list_head pktloc_name_ht[PKTLOC_NAME_HT_SIZ];
     47 
     48 /* djb2 */
     49 static unsigned int pktloc_hash(const char *str)
     50 {
     51 	unsigned long hash = 5381;
     52 	int c;
     53 
     54 	while ((c = *str++))
     55 		hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
     56 
     57 	return hash % PKTLOC_NAME_HT_SIZ;
     58 }
     59 
     60 static int __pktloc_lookup(const char *name, struct rtnl_pktloc **result)
     61 {
     62 	struct rtnl_pktloc *loc;
     63 	int hash;
     64 
     65 	hash = pktloc_hash(name);
     66 	nl_list_for_each_entry(loc, &pktloc_name_ht[hash], list) {
     67 		if (!strcasecmp(loc->name, name)) {
     68 			loc->refcnt++;
     69 			*result = loc;
     70 			return 0;
     71 		}
     72 	}
     73 
     74 	return -NLE_OBJ_NOTFOUND;
     75 }
     76 
     77 extern int pktloc_parse(void *scanner);
     78 
     79 static void rtnl_pktloc_free(struct rtnl_pktloc *loc)
     80 {
     81 	if (!loc)
     82 		return;
     83 
     84 	free(loc->name);
     85 	free(loc);
     86 }
     87 
     88 static int read_pktlocs(void)
     89 {
     90 	YY_BUFFER_STATE buf = NULL;
     91 	yyscan_t scanner = NULL;
     92 	static time_t last_read;
     93 	struct stat st;
     94 	char *path;
     95 	int i, err;
     96 	FILE *fd;
     97 
     98 	if (build_sysconf_path(&path, "pktloc") < 0)
     99 		return -NLE_NOMEM;
    100 
    101 	/* if stat fails, just try to read the file */
    102 	if (stat(path, &st) == 0) {
    103 		/* Don't re-read file if file is unchanged */
    104 		if (last_read == st.st_mtime)
    105 			return 0;
    106 	}
    107 
    108 	NL_DBG(2, "Reading packet location file \"%s\"\n", path);
    109 
    110 	if (!(fd = fopen(path, "r"))) {
    111 		err = -NLE_PKTLOC_FILE;
    112 		goto errout;
    113 	}
    114 
    115 	for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++) {
    116 		struct rtnl_pktloc *loc, *n;
    117 
    118 		nl_list_for_each_entry_safe(loc, n, &pktloc_name_ht[i], list)
    119 			rtnl_pktloc_put(loc);
    120 
    121 		nl_init_list_head(&pktloc_name_ht[i]);
    122 	}
    123 
    124 	if ((err = pktloc_lex_init(&scanner)) < 0) {
    125 		err = -NLE_FAILURE;
    126 		goto errout_close;
    127 	}
    128 
    129 	buf = pktloc__create_buffer(fd, YY_BUF_SIZE, scanner);
    130 	pktloc__switch_to_buffer(buf, scanner);
    131 
    132 	if ((err = pktloc_parse(scanner)) != 0) {
    133 		pktloc__delete_buffer(buf, scanner);
    134 		err = -NLE_PARSE_ERR;
    135 		goto errout_scanner;
    136 	}
    137 
    138 	last_read = st.st_mtime;
    139 
    140 errout_scanner:
    141 	pktloc_lex_destroy(scanner);
    142 errout_close:
    143 	fclose(fd);
    144 errout:
    145 	free(path);
    146 
    147 	return err;
    148 }
    149 
    150 /** @endcond */
    151 
    152 /**
    153  * Lookup packet location alias
    154  * @arg name		Name of packet location.
    155  * @arg result		Result pointer
    156  *
    157  * Tries to find a matching packet location alias for the supplied
    158  * packet location name.
    159  *
    160  * The file containing the packet location definitions is automatically
    161  * re-read if its modification time has changed since the last call.
    162  *
    163  * The returned packet location has to be returned after use by calling
    164  * rtnl_pktloc_put() in order to allow freeing its memory after the last
    165  * user has abandoned it.
    166  *
    167  * @return 0 on success or a negative error code.
    168  * @retval NLE_PKTLOC_FILE Unable to open packet location file.
    169  * @retval NLE_OBJ_NOTFOUND No matching packet location alias found.
    170  */
    171 int rtnl_pktloc_lookup(const char *name, struct rtnl_pktloc **result)
    172 {
    173 	int err;
    174 
    175 	if ((err = read_pktlocs()) < 0)
    176 		return err;
    177 
    178 	return __pktloc_lookup(name, result);
    179 }
    180 
    181 /**
    182  * Allocate packet location object
    183  */
    184 struct rtnl_pktloc *rtnl_pktloc_alloc(void)
    185 {
    186 	struct rtnl_pktloc *loc;
    187 
    188 	if (!(loc = calloc(1, sizeof(*loc))))
    189 		return NULL;
    190 
    191 	loc->refcnt = 1;
    192 	nl_init_list_head(&loc->list);
    193 
    194 	return loc;
    195 }
    196 
    197 /**
    198  * Return reference of a packet location
    199  * @arg loc		packet location object.
    200  */
    201 void rtnl_pktloc_put(struct rtnl_pktloc *loc)
    202 {
    203 	if (!loc)
    204 		return;
    205 
    206 	loc->refcnt--;
    207 	if (loc->refcnt <= 0)
    208 		rtnl_pktloc_free(loc);
    209 }
    210 
    211 /**
    212  * Add a packet location to the hash table
    213  * @arg loc		packet location object
    214  *
    215  * @return 0 on success or a negative error code.
    216  */
    217 int rtnl_pktloc_add(struct rtnl_pktloc *loc)
    218 {
    219 	struct rtnl_pktloc *l;
    220 
    221 	if (__pktloc_lookup(loc->name, &l) == 0) {
    222 		rtnl_pktloc_put(l);
    223 		return -NLE_EXIST;
    224 	}
    225 
    226 	NL_DBG(2, "New packet location entry \"%s\" align=%u layer=%u "
    227 		  "offset=%u mask=%#x shift=%u refnt=%u\n",
    228 		  loc->name, loc->align, loc->layer, loc->offset,
    229 		  loc->mask, loc->shift, loc->refcnt);
    230 
    231 	nl_list_add_tail(&loc->list, &pktloc_name_ht[pktloc_hash(loc->name)]);
    232 
    233 	return 0;
    234 }
    235 
    236 void rtnl_pktloc_foreach(void (*cb)(struct rtnl_pktloc *, void *), void *arg)
    237 {
    238 	struct rtnl_pktloc *loc;
    239 	int i;
    240 
    241 	/* ignore errors */
    242 	read_pktlocs();
    243 
    244 	for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++)
    245 		nl_list_for_each_entry(loc, &pktloc_name_ht[i], list)
    246 			cb(loc, arg);
    247 }
    248 
    249 static int __init pktloc_init(void)
    250 {
    251 	int i;
    252 
    253 	for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++)
    254 		nl_init_list_head(&pktloc_name_ht[i]);
    255 
    256 	return 0;
    257 }
    258 
    259 /** @} */
    260