1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3 * Copyright (c) 2008-2013 Thomas Graf <[email protected]>
4 */
5
6 /**
7 * @ingroup tc
8 * @defgroup pktloc Packet Location Aliasing
9 * Packet Location Aliasing
10 *
11 * The packet location aliasing interface eases the use of offset definitions
12 * inside packets by allowing them to be referenced by name. Known positions
13 * of protocol fields are stored in a configuration file and associated with
14 * a name for later reference. The configuration file is distributed with the
15 * library and provides a well defined set of definitions for most common
16 * protocol fields.
17 *
18 * @section pktloc_examples Examples
19 * @par Example 1.1 Looking up a packet location
20 * @code
21 * struct rtnl_pktloc *loc;
22 *
23 * rtnl_pktloc_lookup("ip.src", &loc);
24 * @endcode
25 * @{
26 */
27
28 #include "nl-default.h"
29
30 #include <sys/stat.h>
31
32 #include <netlink/netlink.h>
33 #include <netlink/utils.h>
34 #include <netlink/route/pktloc.h>
35
36 #include "nl-route.h"
37
38 #include "pktloc_syntax.h"
39 #include "pktloc_grammar.h"
40
41 /** @cond SKIP */
42 #define PKTLOC_NAME_HT_SIZ 256
43
44 static struct nl_list_head pktloc_name_ht[PKTLOC_NAME_HT_SIZ];
45
46 /* djb2 */
pktloc_hash(const char * str)47 static unsigned int pktloc_hash(const char *str)
48 {
49 unsigned long hash = 5381;
50 int c;
51
52 while ((c = *str++))
53 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
54
55 return hash % PKTLOC_NAME_HT_SIZ;
56 }
57
__pktloc_lookup(const char * name,struct rtnl_pktloc ** result)58 static int __pktloc_lookup(const char *name, struct rtnl_pktloc **result)
59 {
60 struct rtnl_pktloc *loc;
61 int hash;
62
63 hash = pktloc_hash(name);
64 nl_list_for_each_entry(loc, &pktloc_name_ht[hash], list) {
65 if (!strcasecmp(loc->name, name)) {
66 loc->refcnt++;
67 *result = loc;
68 return 0;
69 }
70 }
71
72 return -NLE_OBJ_NOTFOUND;
73 }
74
75 extern int pktloc_parse(void *scanner);
76
rtnl_pktloc_free(struct rtnl_pktloc * loc)77 static void rtnl_pktloc_free(struct rtnl_pktloc *loc)
78 {
79 if (!loc)
80 return;
81
82 free(loc->name);
83 free(loc);
84 }
85
read_pktlocs(void)86 static int read_pktlocs(void)
87 {
88 YY_BUFFER_STATE buf = NULL;
89 yyscan_t scanner = NULL;
90 static time_t last_read;
91 struct stat st;
92 char *path;
93 int i, err;
94 FILE *fd;
95
96 if (build_sysconf_path(&path, "pktloc") < 0)
97 return -NLE_NOMEM;
98
99 /* if stat fails, just try to read the file */
100 if (stat(path, &st) == 0) {
101 /* Don't re-read file if file is unchanged */
102 if (last_read == st.st_mtime) {
103 err = 0;
104 goto errout;
105 }
106 }
107
108 NL_DBG(2, "Reading packet location file \"%s\"\n", path);
109
110 if (!(fd = fopen(path, "re"))) {
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 (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 */
rtnl_pktloc_lookup(const char * name,struct rtnl_pktloc ** result)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 */
rtnl_pktloc_alloc(void)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 */
rtnl_pktloc_put(struct rtnl_pktloc * loc)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 */
rtnl_pktloc_add(struct rtnl_pktloc * loc)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
rtnl_pktloc_foreach(void (* cb)(struct rtnl_pktloc *,void *),void * arg)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
pktloc_init(void)249 static int _nl_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