libipt_realm.c revision 4a0fbe37a9879ade6a6bf99ab105316284eb4102
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 <xtables.h>
15#include <linux/netfilter_ipv4/ipt_realm.h>
16
17static void realm_help(void)
18{
19	printf(
20"realm match options:\n"
21"[!] --realm value[/mask]\n"
22"				Match realm\n");
23}
24
25static const struct option realm_opts[] = {
26	{ "realm", 1, NULL, '1' },
27	{ .name = NULL }
28};
29
30struct realmname {
31	int	id;
32	char*	name;
33	int	len;
34	struct realmname* next;
35};
36
37/* array of realms from /etc/iproute2/rt_realms */
38static struct realmname *realms;
39/* 1 if loading failed */
40static int rdberr;
41
42static void load_realms(void)
43{
44	const char* rfnm = "/etc/iproute2/rt_realms";
45	char buf[512];
46	FILE *fil;
47	char *cur, *nxt;
48	int id;
49	struct realmname *oldnm = NULL, *newnm = NULL;
50
51	fil = fopen(rfnm, "r");
52	if (!fil) {
53		rdberr = 1;
54		return;
55	}
56
57	while (fgets(buf, sizeof(buf), fil)) {
58		cur = buf;
59		while ((*cur == ' ') || (*cur == '\t'))
60			cur++;
61		if ((*cur == '#') || (*cur == '\n') || (*cur == 0))
62			continue;
63
64		/* iproute2 allows hex and dec format */
65		errno = 0;
66		id = strtoul(cur, &nxt, strncmp(cur, "0x", 2) ? 10 : 16);
67		if ((nxt == cur) || errno)
68			continue;
69
70		/* same boundaries as in iproute2 */
71		if (id < 0 || id > 255)
72			continue;
73		cur = nxt;
74
75		if (!isspace(*cur))
76			continue;
77		while ((*cur == ' ') || (*cur == '\t'))
78			cur++;
79		if ((*cur == '#') || (*cur == '\n') || (*cur == 0))
80			continue;
81		nxt = cur;
82		while ((*nxt != 0) && !isspace(*nxt))
83			nxt++;
84		if (nxt == cur)
85			continue;
86
87		/* found valid data */
88		newnm = malloc(sizeof(struct realmname));
89		if (newnm == NULL) {
90			perror("libipt_realm: malloc failed");
91			exit(1);
92		}
93		newnm->id = id;
94		newnm->len = nxt - cur;
95		newnm->name = malloc(newnm->len + 1);
96		if (newnm->name == NULL) {
97			perror("libipt_realm: malloc failed");
98			exit(1);
99		}
100		strncpy(newnm->name, cur, newnm->len);
101		newnm->name[newnm->len] = 0;
102		newnm->next = NULL;
103
104		if (oldnm)
105			oldnm->next = newnm;
106		else
107			realms = newnm;
108		oldnm = newnm;
109	}
110
111	fclose(fil);
112}
113
114/* get realm id for name, -1 if error/not found */
115static int realm_name2id(const char* name)
116{
117	struct realmname* cur;
118
119	if ((realms == NULL) && (rdberr == 0))
120		load_realms();
121	cur = realms;
122	if (cur == NULL)
123		return -1;
124	while (cur) {
125		if (!strncmp(name, cur->name, cur->len + 1))
126			return cur->id;
127		cur = cur->next;
128	}
129	return -1;
130}
131
132/* get realm name for id, NULL if error/not found */
133static const char *realm_id2name(int id)
134{
135	struct realmname* cur;
136
137	if ((realms == NULL) && (rdberr == 0))
138		load_realms();
139	cur = realms;
140	if (cur == NULL)
141		return NULL;
142	while (cur) {
143		if (id == cur->id)
144			return cur->name;
145		cur = cur->next;
146	}
147	return NULL;
148}
149
150static int realm_parse(int c, char **argv, int invert, unsigned int *flags,
151                       const void *entry, struct xt_entry_match **match)
152{
153	struct ipt_realm_info *realminfo = (struct ipt_realm_info *)(*match)->data;
154	int id;
155
156	switch (c) {
157		char *end;
158	case '1':
159		xtables_check_inverse(argv[optind-1], &invert, &optind, 0);
160		end = optarg = argv[optind-1];
161		realminfo->id = strtoul(optarg, &end, 0);
162		if (end != optarg && (*end == '/' || *end == '\0')) {
163			if (*end == '/')
164				realminfo->mask = strtoul(end+1, &end, 0);
165			else
166				realminfo->mask = 0xffffffff;
167			if (*end != '\0' || end == optarg)
168				xtables_error(PARAMETER_PROBLEM,
169					   "Bad realm value `%s'", optarg);
170		} else {
171			id = realm_name2id(optarg);
172			if (id == -1)
173				xtables_error(PARAMETER_PROBLEM,
174					   "Realm `%s' not found", optarg);
175			realminfo->id = id;
176			realminfo->mask = 0xffffffff;
177		}
178		if (invert)
179			realminfo->invert = 1;
180		*flags = 1;
181		break;
182
183	default:
184		return 0;
185	}
186	return 1;
187}
188
189static void
190print_realm(unsigned long id, unsigned long mask, int numeric)
191{
192	const char* name = NULL;
193
194	if (mask != 0xffffffff)
195		printf("0x%lx/0x%lx ", id, mask);
196	else {
197		if (numeric == 0)
198			name = realm_id2name(id);
199		if (name)
200			printf("%s ", name);
201		else
202			printf("0x%lx ", id);
203	}
204}
205
206static void realm_print(const void *ip, const struct xt_entry_match *match,
207                        int numeric)
208{
209	const struct ipt_realm_info *ri = (const void *)match->data;
210
211	if (ri->invert)
212		printf("! ");
213
214	printf("realm ");
215	print_realm(ri->id, ri->mask, numeric);
216}
217
218static void realm_save(const void *ip, const struct xt_entry_match *match)
219{
220	const struct ipt_realm_info *ri = (const void *)match->data;
221
222	if (ri->invert)
223		printf("! ");
224
225	printf("--realm ");
226	print_realm(ri->id, ri->mask, 0);
227}
228
229static void realm_check(unsigned int flags)
230{
231	if (!flags)
232		xtables_error(PARAMETER_PROBLEM,
233			   "realm match: You must specify `--realm'");
234}
235
236static struct xtables_match realm_mt_reg = {
237	.name		= "realm",
238	.version	= XTABLES_VERSION,
239	.family		= NFPROTO_IPV4,
240	.size		= XT_ALIGN(sizeof(struct ipt_realm_info)),
241	.userspacesize	= XT_ALIGN(sizeof(struct ipt_realm_info)),
242	.help		= realm_help,
243	.parse		= realm_parse,
244	.final_check	= realm_check,
245	.print		= realm_print,
246	.save		= realm_save,
247	.extra_opts	= realm_opts,
248};
249
250void _init(void)
251{
252	xtables_register_match(&realm_mt_reg);
253}
254