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