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