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@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 46static struct nl_list_head pktloc_name_ht[PKTLOC_NAME_HT_SIZ]; 47 48/* djb2 */ 49static 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 60static 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 77extern int pktloc_parse(void *scanner); 78 79static void rtnl_pktloc_free(struct rtnl_pktloc *loc) 80{ 81 if (!loc) 82 return; 83 84 free(loc->name); 85 free(loc); 86} 87 88static 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 140errout_scanner: 141 pktloc_lex_destroy(scanner); 142errout_close: 143 fclose(fd); 144errout: 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 */ 171int 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 */ 184struct 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 */ 201void 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 */ 217int 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 236void 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 249static 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