1/* Shared library add-on to iptables to add devgroup matching support.
2 *
3 * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
4 */
5#include <stdio.h>
6#include <string.h>
7#include <stdlib.h>
8#include <errno.h>
9#include <xtables.h>
10#include <linux/netfilter/xt_devgroup.h>
11
12static void devgroup_help(void)
13{
14	printf(
15"devgroup match options:\n"
16"[!] --src-group value[/mask]	Match device group of incoming device\n"
17"[!] --dst-group value[/mask]	Match device group of outgoing device\n"
18		);
19}
20
21enum {
22	O_SRC_GROUP = 0,
23	O_DST_GROUP,
24};
25
26static const struct xt_option_entry devgroup_opts[] = {
27	{.name = "src-group", .id = O_SRC_GROUP, .type = XTTYPE_STRING,
28	 .flags = XTOPT_INVERT},
29	{.name = "dst-group", .id = O_DST_GROUP, .type = XTTYPE_STRING,
30	 .flags = XTOPT_INVERT},
31	XTOPT_TABLEEND,
32};
33
34/* array of devgroups from /etc/iproute2/group_map */
35static struct xtables_lmap *devgroups;
36
37static void devgroup_init(struct xt_entry_match *match)
38{
39	const char file[] = "/etc/iproute2/group_map";
40	devgroups = xtables_lmap_init(file);
41	if (devgroups == NULL && errno != ENOENT)
42		fprintf(stderr, "Warning: %s: %s\n", file, strerror(errno));
43}
44
45static void devgroup_parse(struct xt_option_call *cb)
46{
47	struct xt_devgroup_info *info = cb->data;
48	unsigned int id;
49	char *end;
50
51	xtables_option_parse(cb);
52	switch (cb->entry->id) {
53	case O_SRC_GROUP:
54		info->src_group = strtoul(cb->arg, &end, 0);
55		if (end != cb->arg && (*end == '/' || *end == '\0')) {
56			if (*end == '/')
57				info->src_mask = strtoul(end+1, &end, 0);
58			else
59				info->src_mask = 0xffffffff;
60			if (*end != '\0' || end == cb->arg)
61				xtables_error(PARAMETER_PROBLEM,
62					      "Bad src-group value `%s'",
63					      cb->arg);
64		} else {
65			id = xtables_lmap_name2id(devgroups, cb->arg);
66			if (id == -1)
67				xtables_error(PARAMETER_PROBLEM,
68					      "Device group `%s' not found",
69					      cb->arg);
70			info->src_group = id;
71			info->src_mask  = 0xffffffff;
72		}
73		info->flags |= XT_DEVGROUP_MATCH_SRC;
74		if (cb->invert)
75			info->flags |= XT_DEVGROUP_INVERT_SRC;
76		break;
77	case O_DST_GROUP:
78		info->dst_group = strtoul(cb->arg, &end, 0);
79		if (end != cb->arg && (*end == '/' || *end == '\0')) {
80			if (*end == '/')
81				info->dst_mask = strtoul(end+1, &end, 0);
82			else
83				info->dst_mask = 0xffffffff;
84			if (*end != '\0' || end == cb->arg)
85				xtables_error(PARAMETER_PROBLEM,
86					      "Bad dst-group value `%s'",
87					      cb->arg);
88		} else {
89			id = xtables_lmap_name2id(devgroups, cb->arg);
90			if (id == -1)
91				xtables_error(PARAMETER_PROBLEM,
92					      "Device group `%s' not found",
93					      cb->arg);
94			info->dst_group = id;
95			info->dst_mask  = 0xffffffff;
96		}
97		info->flags |= XT_DEVGROUP_MATCH_DST;
98		if (cb->invert)
99			info->flags |= XT_DEVGROUP_INVERT_DST;
100		break;
101	}
102}
103
104static void
105print_devgroup(unsigned int id, unsigned int mask, int numeric)
106{
107	const char *name = NULL;
108
109	if (mask != 0xffffffff)
110		printf("0x%x/0x%x", id, mask);
111	else {
112		if (numeric == 0)
113			name = xtables_lmap_id2name(devgroups, id);
114		if (name)
115			printf("%s", name);
116		else
117			printf("0x%x", id);
118	}
119}
120
121static void devgroup_show(const char *pfx, const struct xt_devgroup_info *info,
122			  int numeric)
123{
124	if (info->flags & XT_DEVGROUP_MATCH_SRC) {
125		if (info->flags & XT_DEVGROUP_INVERT_SRC)
126			printf(" !");
127		printf(" %ssrc-group ", pfx);
128		print_devgroup(info->src_group, info->src_mask, numeric);
129	}
130
131	if (info->flags & XT_DEVGROUP_MATCH_DST) {
132		if (info->flags & XT_DEVGROUP_INVERT_DST)
133			printf(" !");
134		printf(" %sdst-group ", pfx);
135		print_devgroup(info->src_group, info->src_mask, numeric);
136	}
137}
138
139static void devgroup_print(const void *ip, const struct xt_entry_match *match,
140                        int numeric)
141{
142	const struct xt_devgroup_info *info = (const void *)match->data;
143
144	devgroup_show("", info, numeric);
145}
146
147static void devgroup_save(const void *ip, const struct xt_entry_match *match)
148{
149	const struct xt_devgroup_info *info = (const void *)match->data;
150
151	devgroup_show("--", info, 0);
152}
153
154static void devgroup_check(struct xt_fcheck_call *cb)
155{
156	if (cb->xflags == 0)
157		xtables_error(PARAMETER_PROBLEM,
158			      "devgroup match: You must specify either "
159			      "'--src-group' or '--dst-group'");
160}
161
162static struct xtables_match devgroup_mt_reg = {
163	.name		= "devgroup",
164	.version	= XTABLES_VERSION,
165	.family		= NFPROTO_UNSPEC,
166	.size		= XT_ALIGN(sizeof(struct xt_devgroup_info)),
167	.userspacesize	= XT_ALIGN(sizeof(struct xt_devgroup_info)),
168	.init		= devgroup_init,
169	.help		= devgroup_help,
170	.print		= devgroup_print,
171	.save		= devgroup_save,
172	.x6_parse	= devgroup_parse,
173	.x6_fcheck	= devgroup_check,
174	.x6_options	= devgroup_opts,
175};
176
177void _init(void)
178{
179	xtables_register_match(&devgroup_mt_reg);
180}
181