summaryrefslogtreecommitdiffstats
path: root/lib/route/pktloc.c
blob: f0d0155c96fcc0e8b15e02ff7f0e4b3485e6cba2 (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
/*
 * 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.
 *
 * @subsection 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 */
#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;
}


void rtnl_pktloc_add(struct rtnl_pktloc *loc)
{
	nl_list_add_tail(&loc->list, &pktloc_name_ht[pktloc_hash(loc->name)]);
}

extern int pktloc_parse(void *scanner);

/** @endcond */

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;
	yyscan_t scanner = NULL;
	static time_t last_read;
	struct stat st = {0};
	char *path;
	int i, err;
	FILE *fd;

	asprintf(&path, "%s/pktloc", SYSCONFDIR);

	/* 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;
	}

	if (!(fd = fopen(path, "r")))
		return -NLE_PKTLOC_FILE;

	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_free(loc);

		nl_init_list_head(&pktloc_name_ht[i]);
	}

	if ((err = pktloc_lex_init(&scanner)) < 0)
		return -NLE_FAILURE;

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

	if ((err = pktloc_parse(scanner)) < 0)
		return -NLE_FAILURE;

	if (scanner)
		pktloc_lex_destroy(scanner);

	free(path);
	last_read = st.st_mtime;

	return 0;
}

/**
 * Lookup packet location alias
 * @arg name		Name of packet location.
 *
 * 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.
 *
 * @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)
{
	struct rtnl_pktloc *loc;
	int hash, err;

	if ((err = read_pktlocs()) < 0)
		return err;

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

	return -NLE_OBJ_NOTFOUND;
}

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;
}