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