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 General Public License as
6 *	published by the Free Software Foundation version 2 of the License.
7 *
8 * Copyright (c) 2008-2010 Thomas Graf <tgraf@suug.ch>
9 */
10
11/**
12 * @ingroup tc
13 * @defgroup pktloc Packet Location Aliasing
14 * Packet Location Aliasing
15 *
16 * The packet location aliasing interface eases the use of offset definitions
17 * inside packets by allowing them to be referenced by name. Known positions
18 * of protocol fields are stored in a configuration file and associated with
19 * a name for later reference. The configuration file is distributed with the
20 * library and provides a well defined set of definitions for most common
21 * protocol fields.
22 *
23 * @subsection pktloc_examples Examples
24 * @par Example 1.1 Looking up a packet location
25 * @code
26 * struct rtnl_pktloc *loc;
27 *
28 * rtnl_pktloc_lookup("ip.src", &loc);
29 * @endcode
30 * @{
31 */
32
33#include <netlink-local.h>
34#include <netlink-tc.h>
35#include <netlink/netlink.h>
36#include <netlink/utils.h>
37#include <netlink/route/pktloc.h>
38
39#include "pktloc_syntax.h"
40#include "pktloc_grammar.h"
41
42/** @cond */
43#define PKTLOC_NAME_HT_SIZ 256
44
45static struct nl_list_head pktloc_name_ht[PKTLOC_NAME_HT_SIZ];
46
47/* djb2 */
48unsigned int pktloc_hash(const char *str)
49{
50	unsigned long hash = 5381;
51	int c;
52
53	while ((c = *str++))
54		hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
55
56	return hash % PKTLOC_NAME_HT_SIZ;
57}
58
59
60void rtnl_pktloc_add(struct rtnl_pktloc *loc)
61{
62	nl_list_add_tail(&loc->list, &pktloc_name_ht[pktloc_hash(loc->name)]);
63}
64
65extern int pktloc_parse(void *scanner);
66
67/** @endcond */
68
69static void rtnl_pktloc_free(struct rtnl_pktloc *loc)
70{
71	if (!loc)
72		return;
73
74	free(loc->name);
75	free(loc);
76}
77
78static int read_pktlocs(void)
79{
80	YY_BUFFER_STATE buf;
81	yyscan_t scanner = NULL;
82	static time_t last_read;
83	struct stat st = {0};
84	char *path;
85	int i, err;
86	FILE *fd;
87
88	asprintf(&path, "%s/pktloc", SYSCONFDIR);
89
90	/* if stat fails, just try to read the file */
91	if (stat(path, &st) == 0) {
92		/* Don't re-read file if file is unchanged */
93		if (last_read == st.st_mtime)
94			return 0;
95	}
96
97	if (!(fd = fopen(path, "r")))
98		return -NLE_PKTLOC_FILE;
99
100	for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++) {
101		struct rtnl_pktloc *loc, *n;
102
103		nl_list_for_each_entry_safe(loc, n, &pktloc_name_ht[i], list)
104			rtnl_pktloc_free(loc);
105
106		nl_init_list_head(&pktloc_name_ht[i]);
107	}
108
109	if ((err = pktloc_lex_init(&scanner)) < 0)
110		return -NLE_FAILURE;
111
112	buf = pktloc__create_buffer(fd, YY_BUF_SIZE, scanner);
113	pktloc__switch_to_buffer(buf, scanner);
114
115	if ((err = pktloc_parse(scanner)) < 0)
116		return -NLE_FAILURE;
117
118	if (scanner)
119		pktloc_lex_destroy(scanner);
120
121	free(path);
122	last_read = st.st_mtime;
123
124	return 0;
125}
126
127/**
128 * Lookup packet location alias
129 * @arg name		Name of packet location.
130 *
131 * Tries to find a matching packet location alias for the supplied
132 * packet location name.
133 *
134 * The file containing the packet location definitions is automatically
135 * re-read if its modification time has changed since the last call.
136 *
137 * @return 0 on success or a negative error code.
138 * @retval NLE_PKTLOC_FILE Unable to open packet location file.
139 * @retval NLE_OBJ_NOTFOUND No matching packet location alias found.
140 */
141int rtnl_pktloc_lookup(const char *name, struct rtnl_pktloc **result)
142{
143	struct rtnl_pktloc *loc;
144	int hash, err;
145
146	if ((err = read_pktlocs()) < 0)
147		return err;
148
149	hash = pktloc_hash(name);
150	nl_list_for_each_entry(loc, &pktloc_name_ht[hash], list) {
151		if (!strcasecmp(loc->name, name)) {
152			*result = loc;
153			return 0;
154		}
155	}
156
157	return -NLE_OBJ_NOTFOUND;
158}
159
160static int __init pktloc_init(void)
161{
162	int i;
163
164	for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++)
165		nl_init_list_head(&pktloc_name_ht[i]);
166
167	return 0;
168}
169