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
46static struct nl_list_head pktloc_name_ht[PKTLOC_NAME_HT_SIZ];
47
48/* djb2 */
49static 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
60static 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
77extern int pktloc_parse(void *scanner);
78
79static void rtnl_pktloc_free(struct rtnl_pktloc *loc)
80{
81	if (!loc)
82		return;
83
84	free(loc->name);
85	free(loc);
86}
87
88static 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
140errout_scanner:
141	pktloc_lex_destroy(scanner);
142errout_close:
143	fclose(fd);
144errout:
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 */
171int 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 */
184struct 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 */
201void 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 */
217int 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
236void 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
249static 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