• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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 */
pktloc_hash(const char * str)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 
__pktloc_lookup(const char * name,struct rtnl_pktloc ** result)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 
rtnl_pktloc_free(struct rtnl_pktloc * loc)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 
read_pktlocs(void)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  */
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 __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