• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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