libxt_connlabel.c revision ccbf6b6448a4210432b76fd4660798705b05f8c4
1#include <errno.h>
2#include <stdbool.h>
3#include <string.h>
4#include <stdio.h>
5#include <stdint.h>
6#include <xtables.h>
7#include <linux/netfilter/xt_connlabel.h>
8
9enum {
10	O_LABEL = 0,
11	O_SET = 1,
12};
13
14#define CONNLABEL_CFG "/etc/xtables/connlabel.conf"
15
16static void connlabel_mt_help(void)
17{
18	puts(
19"connlabel match options:\n"
20"[!] --label name     Match if label has been set on connection\n"
21"    --set            Set label on connection");
22}
23
24static const struct xt_option_entry connlabel_mt_opts[] = {
25	{.name = "label", .id = O_LABEL, .type = XTTYPE_STRING,
26	 .min = 1, .flags = XTOPT_MAND|XTOPT_INVERT},
27	{.name = "set", .id = O_SET, .type = XTTYPE_NONE},
28	XTOPT_TABLEEND,
29};
30
31static int
32xtables_parse_connlabel_numerical(const char *s, char **end)
33{
34	uintmax_t value;
35
36	if (!xtables_strtoul(s, end, &value, 0, XT_CONNLABEL_MAXBIT))
37		return -1;
38	return value;
39}
40
41static bool is_space_posix(int c)
42{
43	return c == ' ' || c == '\f' || c == '\r' || c == '\t' || c == '\v';
44}
45
46static char * trim_label(char *label)
47{
48	char *end;
49
50	while (is_space_posix(*label))
51		label++;
52	end = strchr(label, '\n');
53	if (end)
54		*end = 0;
55	else
56		end = strchr(label, '\0');
57	end--;
58
59	while (is_space_posix(*end) && end > label) {
60		*end = 0;
61		end--;
62	}
63
64	return *label ? label : NULL;
65}
66
67static void
68xtables_get_connlabel(uint16_t bit, char *buf, size_t len)
69{
70	FILE *fp = fopen(CONNLABEL_CFG, "r");
71	char label[1024];
72	char *end;
73
74	if (!fp)
75		goto error;
76
77	while (fgets(label, sizeof(label), fp)) {
78		int tmp;
79
80		if (label[0] == '#')
81			continue;
82		tmp = xtables_parse_connlabel_numerical(label, &end);
83		if (tmp < 0 || tmp < (int) bit)
84			continue;
85		if (tmp > (int) bit)
86			break;
87
88		end = trim_label(end);
89		if (!end)
90			continue;
91		snprintf(buf, len, "%s", end);
92		fclose(fp);
93		return;
94	}
95	fclose(fp);
96 error:
97	snprintf(buf, len, "%u", (unsigned int) bit);
98}
99
100
101static uint16_t xtables_parse_connlabel(const char *s)
102{
103	FILE *fp = fopen(CONNLABEL_CFG, "r");
104	char label[1024];
105	char *end;
106	int bit;
107
108	if (!fp)
109		xtables_error(PARAMETER_PROBLEM, "label '%s': could not open '%s': %s",
110						s, CONNLABEL_CFG, strerror(errno));
111
112	while (fgets(label, sizeof(label), fp)) {
113		if (label[0] == '#' || !strstr(label, s))
114			continue;
115		bit = xtables_parse_connlabel_numerical(label, &end);
116		if (bit < 0)
117			continue;
118
119		end = trim_label(end);
120		if (!end)
121			continue;
122		if (strcmp(end, s) == 0) {
123			fclose(fp);
124			return bit;
125		}
126	}
127	fclose(fp);
128	xtables_error(PARAMETER_PROBLEM, "label '%s' not found in config file %s",
129					s, CONNLABEL_CFG);
130}
131
132static void connlabel_mt_parse(struct xt_option_call *cb)
133{
134	struct xt_connlabel_mtinfo *info = cb->data;
135	int tmp;
136
137	xtables_option_parse(cb);
138
139	switch (cb->entry->id) {
140	case O_LABEL:
141		tmp = xtables_parse_connlabel_numerical(cb->arg, NULL);
142		info->bit = tmp < 0 ? xtables_parse_connlabel(cb->arg) : tmp;
143
144		if (cb->invert)
145			info->options |= XT_CONNLABEL_OP_INVERT;
146		break;
147	case O_SET:
148		info->options |= XT_CONNLABEL_OP_SET;
149		break;
150	}
151
152}
153
154static void
155connlabel_mt_print_op(const struct xt_connlabel_mtinfo *info, const char *prefix)
156{
157	if (info->options & XT_CONNLABEL_OP_SET)
158		printf(" %sset", prefix);
159}
160
161static void
162connlabel_mt_print(const void *ip, const struct xt_entry_match *match, int numeric)
163{
164	const struct xt_connlabel_mtinfo *info = (const void *)match->data;
165	char buf[1024];
166
167	printf(" connlabel");
168	if (info->options & XT_CONNLABEL_OP_INVERT)
169		printf(" !");
170	if (numeric) {
171		printf(" %u", info->bit);
172	} else {
173		xtables_get_connlabel(info->bit, buf, sizeof(buf));
174		printf(" '%s'", buf);
175	}
176	connlabel_mt_print_op(info, "");
177}
178
179static void
180connlabel_mt_save(const void *ip, const struct xt_entry_match *match)
181{
182	const struct xt_connlabel_mtinfo *info = (const void *)match->data;
183	char buf[1024];
184
185	if (info->options & XT_CONNLABEL_OP_INVERT)
186		printf(" !");
187
188	xtables_get_connlabel(info->bit, buf, sizeof(buf));
189	printf(" --label \"%s\"", buf);
190
191	connlabel_mt_print_op(info, "--");
192}
193
194static struct xtables_match connlabel_mt_reg = {
195	.family        = NFPROTO_UNSPEC,
196	.name          = "connlabel",
197	.version       = XTABLES_VERSION,
198	.size          = XT_ALIGN(sizeof(struct xt_connlabel_mtinfo)),
199	.userspacesize = offsetof(struct xt_connlabel_mtinfo, bit),
200	.help          = connlabel_mt_help,
201	.print         = connlabel_mt_print,
202	.save          = connlabel_mt_save,
203	.x6_parse      = connlabel_mt_parse,
204	.x6_options    = connlabel_mt_opts,
205};
206
207void _init(void)
208{
209	xtables_register_match(&connlabel_mt_reg);
210}
211