1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3 * lib/route/pktloc.c Packet Location Aliasing
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation version 2.1
8 * of the License.
9 *
10 * Copyright (c) 2008-2013 Thomas Graf <tgraf@suug.ch>
11 */
12
13 /**
14 * @ingroup tc
15 * @defgroup pktloc Packet Location Aliasing
16 * Packet Location Aliasing
17 *
18 * The packet location aliasing interface eases the use of offset definitions
19 * inside packets by allowing them to be referenced by name. Known positions
20 * of protocol fields are stored in a configuration file and associated with
21 * a name for later reference. The configuration file is distributed with the
22 * library and provides a well defined set of definitions for most common
23 * protocol fields.
24 *
25 * @section pktloc_examples Examples
26 * @par Example 1.1 Looking up a packet location
27 * @code
28 * struct rtnl_pktloc *loc;
29 *
30 * rtnl_pktloc_lookup("ip.src", &loc);
31 * @endcode
32 * @{
33 */
34
35 #include <netlink-private/netlink.h>
36 #include <netlink-private/tc.h>
37 #include <netlink/netlink.h>
38 #include <netlink/utils.h>
39 #include <netlink/route/pktloc.h>
40
41 #include "pktloc_syntax.h"
42 #include "pktloc_grammar.h"
43
44 /** @cond SKIP */
45 #define PKTLOC_NAME_HT_SIZ 256
46
47 static struct nl_list_head pktloc_name_ht[PKTLOC_NAME_HT_SIZ];
48
49 /* djb2 */
pktloc_hash(const char * str)50 static unsigned int pktloc_hash(const char *str)
51 {
52 unsigned long hash = 5381;
53 int c;
54
55 while ((c = *str++))
56 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
57
58 return hash % PKTLOC_NAME_HT_SIZ;
59 }
60
__pktloc_lookup(const char * name,struct rtnl_pktloc ** result)61 static int __pktloc_lookup(const char *name, struct rtnl_pktloc **result)
62 {
63 struct rtnl_pktloc *loc;
64 int hash;
65
66 hash = pktloc_hash(name);
67 nl_list_for_each_entry(loc, &pktloc_name_ht[hash], list) {
68 if (!strcasecmp(loc->name, name)) {
69 loc->refcnt++;
70 *result = loc;
71 return 0;
72 }
73 }
74
75 return -NLE_OBJ_NOTFOUND;
76 }
77
78 extern int pktloc_parse(void *scanner);
79
rtnl_pktloc_free(struct rtnl_pktloc * loc)80 static void rtnl_pktloc_free(struct rtnl_pktloc *loc)
81 {
82 if (!loc)
83 return;
84
85 free(loc->name);
86 free(loc);
87 }
88
read_pktlocs(void)89 static int read_pktlocs(void)
90 {
91 YY_BUFFER_STATE buf = NULL;
92 yyscan_t scanner = NULL;
93 static time_t last_read;
94 struct stat st;
95 char *path;
96 int i, err;
97 FILE *fd;
98
99 if (build_sysconf_path(&path, "pktloc") < 0)
100 return -NLE_NOMEM;
101
102 /* if stat fails, just try to read the file */
103 if (stat(path, &st) == 0) {
104 /* Don't re-read file if file is unchanged */
105 if (last_read == st.st_mtime) {
106 err = 0;
107 goto errout;
108 }
109 }
110
111 NL_DBG(2, "Reading packet location file \"%s\"\n", path);
112
113 if (!(fd = fopen(path, "re"))) {
114 err = -NLE_PKTLOC_FILE;
115 goto errout;
116 }
117
118 for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++) {
119 struct rtnl_pktloc *loc, *n;
120
121 nl_list_for_each_entry_safe(loc, n, &pktloc_name_ht[i], list)
122 rtnl_pktloc_put(loc);
123
124 nl_init_list_head(&pktloc_name_ht[i]);
125 }
126
127 if ((err = pktloc_lex_init(&scanner)) < 0) {
128 err = -NLE_FAILURE;
129 goto errout_close;
130 }
131
132 buf = pktloc__create_buffer(fd, YY_BUF_SIZE, scanner);
133 pktloc__switch_to_buffer(buf, scanner);
134
135 if ((err = pktloc_parse(scanner)) != 0) {
136 pktloc__delete_buffer(buf, scanner);
137 err = -NLE_PARSE_ERR;
138 goto errout_scanner;
139 }
140
141 last_read = st.st_mtime;
142
143 errout_scanner:
144 pktloc_lex_destroy(scanner);
145 errout_close:
146 fclose(fd);
147 errout:
148 free(path);
149
150 return err;
151 }
152
153 /** @endcond */
154
155 /**
156 * Lookup packet location alias
157 * @arg name Name of packet location.
158 * @arg result Result pointer
159 *
160 * Tries to find a matching packet location alias for the supplied
161 * packet location name.
162 *
163 * The file containing the packet location definitions is automatically
164 * re-read if its modification time has changed since the last call.
165 *
166 * The returned packet location has to be returned after use by calling
167 * rtnl_pktloc_put() in order to allow freeing its memory after the last
168 * user has abandoned it.
169 *
170 * @return 0 on success or a negative error code.
171 * @retval NLE_PKTLOC_FILE Unable to open packet location file.
172 * @retval NLE_OBJ_NOTFOUND No matching packet location alias found.
173 */
rtnl_pktloc_lookup(const char * name,struct rtnl_pktloc ** result)174 int rtnl_pktloc_lookup(const char *name, struct rtnl_pktloc **result)
175 {
176 int err;
177
178 if ((err = read_pktlocs()) < 0)
179 return err;
180
181 return __pktloc_lookup(name, result);
182 }
183
184 /**
185 * Allocate packet location object
186 */
rtnl_pktloc_alloc(void)187 struct rtnl_pktloc *rtnl_pktloc_alloc(void)
188 {
189 struct rtnl_pktloc *loc;
190
191 if (!(loc = calloc(1, sizeof(*loc))))
192 return NULL;
193
194 loc->refcnt = 1;
195 nl_init_list_head(&loc->list);
196
197 return loc;
198 }
199
200 /**
201 * Return reference of a packet location
202 * @arg loc packet location object.
203 */
rtnl_pktloc_put(struct rtnl_pktloc * loc)204 void rtnl_pktloc_put(struct rtnl_pktloc *loc)
205 {
206 if (!loc)
207 return;
208
209 loc->refcnt--;
210 if (loc->refcnt <= 0)
211 rtnl_pktloc_free(loc);
212 }
213
214 /**
215 * Add a packet location to the hash table
216 * @arg loc packet location object
217 *
218 * @return 0 on success or a negative error code.
219 */
rtnl_pktloc_add(struct rtnl_pktloc * loc)220 int rtnl_pktloc_add(struct rtnl_pktloc *loc)
221 {
222 struct rtnl_pktloc *l;
223
224 if (__pktloc_lookup(loc->name, &l) == 0) {
225 rtnl_pktloc_put(l);
226 return -NLE_EXIST;
227 }
228
229 NL_DBG(2, "New packet location entry \"%s\" align=%u layer=%u "
230 "offset=%u mask=%#x shift=%u refnt=%u\n",
231 loc->name, loc->align, loc->layer, loc->offset,
232 loc->mask, loc->shift, loc->refcnt);
233
234 nl_list_add_tail(&loc->list, &pktloc_name_ht[pktloc_hash(loc->name)]);
235
236 return 0;
237 }
238
rtnl_pktloc_foreach(void (* cb)(struct rtnl_pktloc *,void *),void * arg)239 void rtnl_pktloc_foreach(void (*cb)(struct rtnl_pktloc *, void *), void *arg)
240 {
241 struct rtnl_pktloc *loc;
242 int i;
243
244 /* ignore errors */
245 read_pktlocs();
246
247 for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++)
248 nl_list_for_each_entry(loc, &pktloc_name_ht[i], list)
249 cb(loc, arg);
250 }
251
pktloc_init(void)252 static int __init pktloc_init(void)
253 {
254 int i;
255
256 for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++)
257 nl_init_list_head(&pktloc_name_ht[i]);
258
259 return 0;
260 }
261
262 /** @} */
263