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_groupspec(const char *arg, unsigned int *group,
46				     unsigned int *mask)
47{
48	char *end;
49	bool ok;
50
51	ok = xtables_strtoui(arg, &end, group, 0, UINT32_MAX);
52	if (ok && (*end == '/' || *end == '\0')) {
53		if (*end == '/')
54			ok = xtables_strtoui(end + 1, NULL, mask,
55			                     0, UINT32_MAX);
56		else
57			*mask = ~0U;
58		if (!ok)
59			xtables_error(PARAMETER_PROBLEM,
60				      "Bad group value \"%s\"", arg);
61	} else {
62		*group = xtables_lmap_name2id(devgroups, arg);
63		if (*group == -1)
64			xtables_error(PARAMETER_PROBLEM,
65				      "Device group \"%s\" not found", arg);
66		*mask = ~0U;
67	}
68}
69
70static void devgroup_parse(struct xt_option_call *cb)
71{
72	struct xt_devgroup_info *info = cb->data;
73	unsigned int id, mask;
74
75	xtables_option_parse(cb);
76	switch (cb->entry->id) {
77	case O_SRC_GROUP:
78		devgroup_parse_groupspec(cb->arg, &id, &mask);
79		info->src_group = id;
80		info->src_mask  = mask;
81		info->flags |= XT_DEVGROUP_MATCH_SRC;
82		if (cb->invert)
83			info->flags |= XT_DEVGROUP_INVERT_SRC;
84		break;
85	case O_DST_GROUP:
86		devgroup_parse_groupspec(cb->arg, &id, &mask);
87		info->dst_group = id;
88		info->dst_mask  = mask;
89		info->flags |= XT_DEVGROUP_MATCH_DST;
90		if (cb->invert)
91			info->flags |= XT_DEVGROUP_INVERT_DST;
92		break;
93	}
94}
95
96static void
97print_devgroup(unsigned int id, unsigned int mask, int numeric)
98{
99	const char *name = NULL;
100
101	if (mask != 0xffffffff)
102		printf("0x%x/0x%x", id, mask);
103	else {
104		if (numeric == 0)
105			name = xtables_lmap_id2name(devgroups, id);
106		if (name)
107			printf("%s", name);
108		else
109			printf("0x%x", id);
110	}
111}
112
113static void devgroup_show(const char *pfx, const struct xt_devgroup_info *info,
114			  int numeric)
115{
116	if (info->flags & XT_DEVGROUP_MATCH_SRC) {
117		if (info->flags & XT_DEVGROUP_INVERT_SRC)
118			printf(" !");
119		printf(" %ssrc-group ", pfx);
120		print_devgroup(info->src_group, info->src_mask, numeric);
121	}
122
123	if (info->flags & XT_DEVGROUP_MATCH_DST) {
124		if (info->flags & XT_DEVGROUP_INVERT_DST)
125			printf(" !");
126		printf(" %sdst-group ", pfx);
127		print_devgroup(info->src_group, info->src_mask, numeric);
128	}
129}
130
131static void devgroup_print(const void *ip, const struct xt_entry_match *match,
132                        int numeric)
133{
134	const struct xt_devgroup_info *info = (const void *)match->data;
135
136	devgroup_show("", info, numeric);
137}
138
139static void devgroup_save(const void *ip, const struct xt_entry_match *match)
140{
141	const struct xt_devgroup_info *info = (const void *)match->data;
142
143	devgroup_show("--", info, 0);
144}
145
146static void devgroup_check(struct xt_fcheck_call *cb)
147{
148	if (cb->xflags == 0)
149		xtables_error(PARAMETER_PROBLEM,
150			      "devgroup match: You must specify either "
151			      "'--src-group' or '--dst-group'");
152}
153
154static struct xtables_match devgroup_mt_reg = {
155	.name		= "devgroup",
156	.version	= XTABLES_VERSION,
157	.family		= NFPROTO_UNSPEC,
158	.size		= XT_ALIGN(sizeof(struct xt_devgroup_info)),
159	.userspacesize	= XT_ALIGN(sizeof(struct xt_devgroup_info)),
160	.init		= devgroup_init,
161	.help		= devgroup_help,
162	.print		= devgroup_print,
163	.save		= devgroup_save,
164	.x6_parse	= devgroup_parse,
165	.x6_fcheck	= devgroup_check,
166	.x6_options	= devgroup_opts,
167};
168
169void _init(void)
170{
171	xtables_register_match(&devgroup_mt_reg);
172}
173