summaryrefslogtreecommitdiff
path: root/lib/route/pktloc.c
blob: b677ab50874f3ebd4ea31f47eec5674ec6ac63e2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
/*
 * lib/route/pktloc.c     Packet Location Aliasing
 *
 *	This library is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU General Public License as
 *	published by the Free Software Foundation version 2 of the License.
 *
 * Copyright (c) 2008-2010 Thomas Graf <tgraf@suug.ch>
 */

/**
 * @ingroup tc
 * @defgroup pktloc Packet Location Aliasing
 * Packet Location Aliasing
 *
 * The packet location aliasing interface eases the use of offset definitions
 * inside packets by allowing them to be referenced by name. Known positions
 * of protocol fields are stored in a configuration file and associated with
 * a name for later reference. The configuration file is distributed with the
 * library and provides a well defined set of definitions for most common
 * protocol fields.
 *
 * @section pktloc_examples Examples
 * @par Example 1.1 Looking up a packet location
 * @code
 * struct rtnl_pktloc *loc;
 *
 * rtnl_pktloc_lookup("ip.src", &loc);
 * @endcode
 * @{
 */

#include <netlink-local.h>
#include <netlink-tc.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
#include <netlink/route/pktloc.h>

#include "pktloc_syntax.h"
#include "pktloc_grammar.h"

/** @cond SKIP */
#define PKTLOC_NAME_HT_SIZ 256

static struct nl_list_head pktloc_name_ht[PKTLOC_NAME_HT_SIZ];

/* djb2 */
unsigned int pktloc_hash(const char *str)
{
	unsigned long hash = 5381;
	int c;

	while ((c = *str++))
		hash = ((hash << 5) + hash) + c; /* hash * 33 + c */

	return hash % PKTLOC_NAME_HT_SIZ;
}

static int __pktloc_lookup(const char *name, struct rtnl_pktloc **result)
{
	struct rtnl_pktloc *loc;
	int hash;

	hash = pktloc_hash(name);
	nl_list_for_each_entry(loc, &pktloc_name_ht[hash], list) {
		if (!strcasecmp(loc->name, name)) {
			loc->refcnt++;
			*result = loc;
			return 0;
		}
	}

	return -NLE_OBJ_NOTFOUND;
}

extern int pktloc_parse(void *scanner);

static void rtnl_pktloc_free(struct rtnl_pktloc *loc)
{
	if (!loc)
		return;

	free(loc->name);
	free(loc);
}

static int read_pktlocs(void)
{
	YY_BUFFER_STATE buf = NULL;
	yyscan_t scanner = NULL;
	static time_t last_read;
	struct stat st = {0};
	char *path;
	int i, err;
	FILE *fd;

	if (build_sysconf_path(&path, "pktloc") < 0)
		return -NLE_NOMEM;

	/* if stat fails, just try to read the file */
	if (stat(path, &st) == 0) {
		/* Don't re-read file if file is unchanged */
		if (last_read == st.st_mtime)
			return 0;
	}

	NL_DBG(2, "Reading packet location file \"%s\"\n", path);

	if (!(fd = fopen(path, "r"))) {
		err = -NLE_PKTLOC_FILE;
		goto errout;
	}

	for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++) {
		struct rtnl_pktloc *loc, *n;

		nl_list_for_each_entry_safe(loc, n, &pktloc_name_ht[i], list)
			rtnl_pktloc_put(loc);

		nl_init_list_head(&pktloc_name_ht[i]);
	}

	if ((err = pktloc_lex_init(&scanner)) < 0) {
		err = -NLE_FAILURE;
		goto errout_close;
	}

	buf = pktloc__create_buffer(fd, YY_BUF_SIZE, scanner);
	pktloc__switch_to_buffer(buf, scanner);

	if ((err = pktloc_parse(scanner)) != 0) {
		pktloc__delete_buffer(buf, scanner);
		err = -NLE_PARSE_ERR;
		goto errout_scanner;
	}

	last_read = st.st_mtime;

errout_scanner:
	if (scanner)
		pktloc_lex_destroy(scanner);
errout_close:
	fclose(fd);
errout:
	free(path);

	return 0;
}

/** @endcond */

/**
 * Lookup packet location alias
 * @arg name		Name of packet location.
 * @arg result		Result pointer
 *
 * Tries to find a matching packet location alias for the supplied
 * packet location name.
 *
 * The file containing the packet location definitions is automatically
 * re-read if its modification time has changed since the last call.
 *
 * The returned packet location has to be returned after use by calling
 * rtnl_pktloc_put() in order to allow freeing its memory after the last
 * user has abandoned it.
 *
 * @return 0 on success or a negative error code.
 * @retval NLE_PKTLOC_FILE Unable to open packet location file.
 * @retval NLE_OBJ_NOTFOUND No matching packet location alias found.
 */
int rtnl_pktloc_lookup(const char *name, struct rtnl_pktloc **result)
{
	int err;

	if ((err = read_pktlocs()) < 0)
		return err;
	
	return __pktloc_lookup(name, result);
}

/**
 * Allocate packet location object
 */
struct rtnl_pktloc *rtnl_pktloc_alloc(void)
{
	struct rtnl_pktloc *loc;

	if (!(loc = calloc(1, sizeof(*loc))))
		return NULL;

	loc->refcnt = 1;
	nl_init_list_head(&loc->list);

	return loc;
}

/**
 * Return reference of a packet location
 * @arg loc		packet location object.
 */
void rtnl_pktloc_put(struct rtnl_pktloc *loc)
{
	if (!loc)
		return;

	loc->refcnt--;
	if (loc->refcnt <= 0)
		rtnl_pktloc_free(loc);
}

/**
 * Add a packet location to the hash table
 * @arg loc		packet location object
 *
 * @return 0 on success or a negative error code.
 */
int rtnl_pktloc_add(struct rtnl_pktloc *loc)
{
	struct rtnl_pktloc *l;

	if (__pktloc_lookup(loc->name, &l) == 0) {
		rtnl_pktloc_put(l);
		return -NLE_EXIST;
	}

	NL_DBG(2, "New packet location entry \"%s\" align=%u layer=%u "
		  "offset=%u mask=%#x shift=%u refnt=%u\n",
		  loc->name, loc->align, loc->layer, loc->offset,
		  loc->mask, loc->shift, loc->refcnt);

	nl_list_add_tail(&loc->list, &pktloc_name_ht[pktloc_hash(loc->name)]);

	return 0;
}

void rtnl_pktloc_foreach(void (*cb)(struct rtnl_pktloc *, void *), void *arg)
{
	struct rtnl_pktloc *loc;
	int i;

	/* ignore errors */
	read_pktlocs();

	for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++)
		nl_list_for_each_entry(loc, &pktloc_name_ht[i], list)
			cb(loc, arg);
}

static int __init pktloc_init(void)
{
	int i;

	for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++)
		nl_init_list_head(&pktloc_name_ht[i]);
	
	return 0;
}

/** @} */