1/* Shared library add-on to iptables to add realm matching support. */
2#include <stdio.h>
3#include <netdb.h>
4#include <string.h>
5#include <stdlib.h>
6#include <errno.h>
7#include <ctype.h>
8#include <getopt.h>
9#if defined(__GLIBC__) && __GLIBC__ == 2
10#include <net/ethernet.h>
11#else
12#include <linux/if_ether.h>
13#endif
14#include <iptables.h>
15#include <linux/netfilter_ipv4/ipt_realm.h>
16
17/* Function which prints out usage message. */
18static void
19help(void)
20{
21	printf(
22"realm v%s options:\n"
23" --realm [!] value[/mask]\n"
24"				Match realm\n"
25"\n", IPTABLES_VERSION);
26}
27
28static struct option opts[] = {
29	{ "realm", 1, 0, '1' },
30	{0}
31};
32
33struct realmname {
34	int	id;
35	char*	name;
36	int	len;
37	struct realmname* next;
38};
39
40/* array of realms from /etc/iproute2/rt_realms */
41static struct realmname *realms = NULL;
42/* 1 if loading failed */
43static int rdberr = 0;
44
45
46void load_realms()
47{
48	const char* rfnm = "/etc/iproute2/rt_realms";
49	char buf[512];
50	FILE *fil;
51	char *cur, *nxt;
52	int id;
53	struct realmname *oldnm = NULL, *newnm = NULL;
54
55	fil = fopen(rfnm, "r");
56	if (!fil) {
57		rdberr = 1;
58		return;
59	}
60
61	while (fgets(buf, sizeof(buf), fil)) {
62		cur = buf;
63		while ((*cur == ' ') || (*cur == '\t'))
64			cur++;
65		if ((*cur == '#') || (*cur == '\n') || (*cur == 0))
66			continue;
67
68		/* iproute2 allows hex and dec format */
69		errno = 0;
70		id = strtoul(cur, &nxt, strncmp(cur, "0x", 2) ? 10 : 16);
71		if ((nxt == cur) || errno)
72			continue;
73
74		/* same boundaries as in iproute2 */
75		if (id < 0 || id > 255)
76			continue;
77		cur = nxt;
78
79		if (!isspace(*cur))
80			continue;
81		while ((*cur == ' ') || (*cur == '\t'))
82			cur++;
83		if ((*cur == '#') || (*cur == '\n') || (*cur == 0))
84			continue;
85		nxt = cur;
86		while ((*nxt != 0) && !isspace(*nxt))
87			nxt++;
88		if (nxt == cur)
89			continue;
90
91		/* found valid data */
92		newnm = (struct realmname*)malloc(sizeof(struct realmname));
93		if (newnm == NULL) {
94			perror("libipt_realm: malloc failed");
95			exit(1);
96		}
97		newnm->id = id;
98		newnm->len = nxt - cur;
99		newnm->name = (char*)malloc(newnm->len + 1);
100		if (newnm->name == NULL) {
101			perror("libipt_realm: malloc failed");
102			exit(1);
103		}
104		strncpy(newnm->name, cur, newnm->len);
105		newnm->name[newnm->len] = 0;
106		newnm->next = NULL;
107
108		if (oldnm)
109			oldnm->next = newnm;
110		else
111			realms = newnm;
112		oldnm = newnm;
113	}
114
115	fclose(fil);
116}
117
118/* get realm id for name, -1 if error/not found */
119int realm_name2id(const char* name)
120{
121	struct realmname* cur;
122
123	if ((realms == NULL) && (rdberr == 0))
124		load_realms();
125	cur = realms;
126	if (cur == NULL)
127		return -1;
128	while (cur) {
129		if (!strncmp(name, cur->name, cur->len + 1))
130			return cur->id;
131		cur = cur->next;
132	}
133	return -1;
134}
135
136/* get realm name for id, NULL if error/not found */
137const char* realm_id2name(int id)
138{
139	struct realmname* cur;
140
141	if ((realms == NULL) && (rdberr == 0))
142		load_realms();
143	cur = realms;
144	if (cur == NULL)
145		return NULL;
146	while (cur) {
147		if (id == cur->id)
148			return cur->name;
149		cur = cur->next;
150	}
151	return NULL;
152}
153
154
155/* Function which parses command options; returns true if it
156   ate an option */
157static int
158parse(int c, char **argv, int invert, unsigned int *flags,
159      const struct ipt_entry *entry,
160      unsigned int *nfcache,
161      struct ipt_entry_match **match)
162{
163	struct ipt_realm_info *realminfo = (struct ipt_realm_info *)(*match)->data;
164	int id;
165
166	switch (c) {
167		char *end;
168	case '1':
169		check_inverse(argv[optind-1], &invert, &optind, 0);
170		end = optarg = argv[optind-1];
171		realminfo->id = strtoul(optarg, &end, 0);
172		if (end != optarg && (*end == '/' || *end == '\0')) {
173			if (*end == '/')
174				realminfo->mask = strtoul(end+1, &end, 0);
175			else
176				realminfo->mask = 0xffffffff;
177			if (*end != '\0' || end == optarg)
178				exit_error(PARAMETER_PROBLEM,
179					   "Bad realm value `%s'", optarg);
180		} else {
181			id = realm_name2id(optarg);
182			if (id == -1)
183				exit_error(PARAMETER_PROBLEM,
184					   "Realm `%s' not found", optarg);
185			realminfo->id = (u_int32_t)id;
186			realminfo->mask = 0xffffffff;
187		}
188		if (invert)
189			realminfo->invert = 1;
190		*flags = 1;
191		break;
192
193	default:
194		return 0;
195	}
196	return 1;
197}
198
199static void
200print_realm(unsigned long id, unsigned long mask, int numeric)
201{
202	const char* name = NULL;
203
204	if (mask != 0xffffffff)
205		printf("0x%lx/0x%lx ", id, mask);
206	else {
207		if (numeric == 0)
208			name = realm_id2name(id);
209		if (name)
210			printf("%s ", name);
211		else
212			printf("0x%lx ", id);
213	}
214}
215
216/* Prints out the matchinfo. */
217static void
218print(const struct ipt_ip *ip,
219      const struct ipt_entry_match *match,
220      int numeric)
221{
222	struct ipt_realm_info *ri = (struct ipt_realm_info *) match->data;
223
224	if (ri->invert)
225		printf("! ");
226
227	printf("realm ");
228	print_realm(ri->id, ri->mask, numeric);
229}
230
231
232/* Saves the union ipt_matchinfo in parsable form to stdout. */
233static void
234save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
235{
236	struct ipt_realm_info *ri = (struct ipt_realm_info *) match->data;
237
238	if (ri->invert)
239		printf("! ");
240
241	printf("--realm ");
242	print_realm(ri->id, ri->mask, 0);
243}
244
245/* Final check; must have specified --mark. */
246static void
247final_check(unsigned int flags)
248{
249	if (!flags)
250		exit_error(PARAMETER_PROBLEM,
251			   "realm match: You must specify `--realm'");
252}
253
254static struct iptables_match realm = { NULL,
255	.name		= "realm",
256	.version	= IPTABLES_VERSION,
257	.size		= IPT_ALIGN(sizeof(struct ipt_realm_info)),
258	.userspacesize	= IPT_ALIGN(sizeof(struct ipt_realm_info)),
259	.help		= &help,
260	.parse		= &parse,
261	.final_check	= &final_check,
262	.print		= &print,
263	.save		= &save,
264	.extra_opts	= opts
265};
266
267void _init(void)
268{
269	register_match(&realm);
270}
271