pktloc.c

00001 /*
00002  * lib/route/pktloc.c     Packet Location Aliasing
00003  *
00004  *      This library is free software; you can redistribute it and/or
00005  *      modify it under the terms of the GNU General Public License as
00006  *      published by the Free Software Foundation version 2 of the License.
00007  *
00008  * Copyright (c) 2008-2010 Thomas Graf <tgraf@suug.ch>
00009  */
00010 
00011 /**
00012  * @ingroup tc
00013  * @defgroup pktloc Packet Location Aliasing
00014  * Packet Location Aliasing
00015  *
00016  * The packet location aliasing interface eases the use of offset definitions
00017  * inside packets by allowing them to be referenced by name. Known positions
00018  * of protocol fields are stored in a configuration file and associated with
00019  * a name for later reference. The configuration file is distributed with the
00020  * library and provides a well defined set of definitions for most common
00021  * protocol fields.
00022  *
00023  * @subsection pktloc_examples Examples
00024  * @par Example 1.1 Looking up a packet location
00025  * @code
00026  * struct rtnl_pktloc *loc;
00027  *
00028  * rtnl_pktloc_lookup("ip.src", &loc);
00029  * @endcode
00030  * @{
00031  */
00032 
00033 #include <netlink-local.h>
00034 #include <netlink-tc.h>
00035 #include <netlink/netlink.h>
00036 #include <netlink/utils.h>
00037 #include <netlink/route/pktloc.h>
00038 
00039 #include "pktloc_syntax.h"
00040 #include "pktloc_grammar.h"
00041 
00042 /** @cond SKIP */
00043 #define PKTLOC_NAME_HT_SIZ 256
00044 
00045 static struct nl_list_head pktloc_name_ht[PKTLOC_NAME_HT_SIZ];
00046 
00047 /* djb2 */
00048 unsigned int pktloc_hash(const char *str)
00049 {
00050         unsigned long hash = 5381;
00051         int c;
00052 
00053         while ((c = *str++))
00054                 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
00055 
00056         return hash % PKTLOC_NAME_HT_SIZ;
00057 }
00058 
00059 static int __pktloc_lookup(const char *name, struct rtnl_pktloc **result)
00060 {
00061         struct rtnl_pktloc *loc;
00062         int hash;
00063 
00064         hash = pktloc_hash(name);
00065         nl_list_for_each_entry(loc, &pktloc_name_ht[hash], list) {
00066                 if (!strcasecmp(loc->name, name)) {
00067                         loc->refcnt++;
00068                         *result = loc;
00069                         return 0;
00070                 }
00071         }
00072 
00073         return -NLE_OBJ_NOTFOUND;
00074 }
00075 
00076 extern int pktloc_parse(void *scanner);
00077 
00078 static void rtnl_pktloc_free(struct rtnl_pktloc *loc)
00079 {
00080         if (!loc)
00081                 return;
00082 
00083         free(loc->name);
00084         free(loc);
00085 }
00086 
00087 static int read_pktlocs(void)
00088 {
00089         YY_BUFFER_STATE buf = NULL;
00090         yyscan_t scanner = NULL;
00091         static time_t last_read;
00092         struct stat st = {0};
00093         char *path;
00094         int i, err;
00095         FILE *fd;
00096 
00097         asprintf(&path, "%s/pktloc", SYSCONFDIR);
00098 
00099         /* if stat fails, just try to read the file */
00100         if (stat(path, &st) == 0) {
00101                 /* Don't re-read file if file is unchanged */
00102                 if (last_read == st.st_mtime)
00103                         return 0;
00104         }
00105 
00106         NL_DBG(2, "Reading packet location file \"%s\"\n", path);
00107 
00108         if (!(fd = fopen(path, "r"))) {
00109                 err = -NLE_PKTLOC_FILE;
00110                 goto errout;
00111         }
00112 
00113         for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++) {
00114                 struct rtnl_pktloc *loc, *n;
00115 
00116                 nl_list_for_each_entry_safe(loc, n, &pktloc_name_ht[i], list)
00117                         rtnl_pktloc_put(loc);
00118 
00119                 nl_init_list_head(&pktloc_name_ht[i]);
00120         }
00121 
00122         if ((err = pktloc_lex_init(&scanner)) < 0) {
00123                 err = -NLE_FAILURE;
00124                 goto errout_close;
00125         }
00126 
00127         buf = pktloc__create_buffer(fd, YY_BUF_SIZE, scanner);
00128         pktloc__switch_to_buffer(buf, scanner);
00129 
00130         if ((err = pktloc_parse(scanner)) != 0) {
00131                 pktloc__delete_buffer(buf, scanner);
00132                 err = -NLE_PARSE_ERR;
00133                 goto errout_scanner;
00134         }
00135 
00136         last_read = st.st_mtime;
00137 
00138 errout_scanner:
00139         if (scanner)
00140                 pktloc_lex_destroy(scanner);
00141 errout_close:
00142         fclose(fd);
00143 errout:
00144         free(path);
00145 
00146         return 0;
00147 }
00148 
00149 /** @endcond */
00150 
00151 /**
00152  * Lookup packet location alias
00153  * @arg name            Name of packet location.
00154  *
00155  * Tries to find a matching packet location alias for the supplied
00156  * packet location name.
00157  *
00158  * The file containing the packet location definitions is automatically
00159  * re-read if its modification time has changed since the last call.
00160  *
00161  * The returned packet location has to be returned after use by calling
00162  * rtnl_pktloc_put() in order to allow freeing its memory after the last
00163  * user has abandoned it.
00164  *
00165  * @return 0 on success or a negative error code.
00166  * @retval NLE_PKTLOC_FILE Unable to open packet location file.
00167  * @retval NLE_OBJ_NOTFOUND No matching packet location alias found.
00168  */
00169 int rtnl_pktloc_lookup(const char *name, struct rtnl_pktloc **result)
00170 {
00171         int err;
00172 
00173         if ((err = read_pktlocs()) < 0)
00174                 return err;
00175         
00176         return __pktloc_lookup(name, result);
00177 }
00178 
00179 /**
00180  * Allocate packet location object
00181  */
00182 struct rtnl_pktloc *rtnl_pktloc_alloc(void)
00183 {
00184         struct rtnl_pktloc *loc;
00185 
00186         if (!(loc = calloc(1, sizeof(*loc))))
00187                 return NULL;
00188 
00189         loc->refcnt = 1;
00190         nl_init_list_head(&loc->list);
00191 
00192         return loc;
00193 }
00194 
00195 /**
00196  * Return reference of a packet location
00197  * @arg loc             packet location object.
00198  */
00199 void rtnl_pktloc_put(struct rtnl_pktloc *loc)
00200 {
00201         if (!loc)
00202                 return;
00203 
00204         loc->refcnt--;
00205         if (loc->refcnt <= 0)
00206                 rtnl_pktloc_free(loc);
00207 }
00208 
00209 /**
00210  * Add a packet location to the hash table
00211  * @arg loc             packet location object
00212  *
00213  * @return 0 on success or a negative error code.
00214  */
00215 int rtnl_pktloc_add(struct rtnl_pktloc *loc)
00216 {
00217         struct rtnl_pktloc *l;
00218 
00219         if (__pktloc_lookup(loc->name, &l) == 0) {
00220                 rtnl_pktloc_put(l);
00221                 return -NLE_EXIST;
00222         }
00223 
00224         NL_DBG(2, "New packet location entry \"%s\" align=%u layer=%u "
00225                   "offset=%u mask=%#x shift=%u refnt=%u\n",
00226                   loc->name, loc->align, loc->layer, loc->offset,
00227                   loc->mask, loc->shift, loc->refcnt);
00228 
00229         nl_list_add_tail(&loc->list, &pktloc_name_ht[pktloc_hash(loc->name)]);
00230 
00231         return 0;
00232 }
00233 
00234 void rtnl_pktloc_foreach(void (*cb)(struct rtnl_pktloc *, void *), void *arg)
00235 {
00236         struct rtnl_pktloc *loc;
00237         int i;
00238 
00239         /* ignore errors */
00240         read_pktlocs();
00241 
00242         for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++)
00243                 nl_list_for_each_entry(loc, &pktloc_name_ht[i], list)
00244                         cb(loc, arg);
00245 }
00246 
00247 static int __init pktloc_init(void)
00248 {
00249         int i;
00250 
00251         for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++)
00252                 nl_init_list_head(&pktloc_name_ht[i]);
00253         
00254         return 0;
00255 }
00256 
00257 /** @} */