1/* Shared library add-on to iptables to add bridge port matching support. */
2#include <stdio.h>
3#include <string.h>
4#include <stdlib.h>
5#include <getopt.h>
6#include <ctype.h>
7#include <ip6tables.h>
8#include <linux/netfilter_ipv6/ip6t_physdev.h>
9#if defined(__GLIBC__) && __GLIBC__ == 2
10#include <net/ethernet.h>
11#else
12#include <linux/if_ether.h>
13#endif
14
15static void
16help(void)
17{
18	printf(
19"physdev v%s options:\n"
20" --physdev-in [!] input name[+]		bridge port name ([+] for wildcard)\n"
21" --physdev-out [!] output name[+]	bridge port name ([+] for wildcard)\n"
22" [!] --physdev-is-in			arrived on a bridge device\n"
23" [!] --physdev-is-out			will leave on a bridge device\n"
24" [!] --physdev-is-bridged		it's a bridged packet\n"
25"\n", IPTABLES_VERSION);
26}
27
28static struct option opts[] = {
29	{ "physdev-in", 1, 0, '1' },
30	{ "physdev-out", 1, 0, '2' },
31	{ "physdev-is-in", 0, 0, '3' },
32	{ "physdev-is-out", 0, 0, '4' },
33	{ "physdev-is-bridged", 0, 0, '5' },
34	{0}
35};
36
37static void
38init(struct ip6t_entry_match *m, unsigned int *nfcache)
39{
40}
41
42static int
43parse(int c, char **argv, int invert, unsigned int *flags,
44      const struct ip6t_entry *entry,
45      unsigned int *nfcache,
46      struct ip6t_entry_match **match)
47{
48	struct ip6t_physdev_info *info =
49		(struct ip6t_physdev_info*)(*match)->data;
50
51	switch (c) {
52	case '1':
53		if (*flags & IP6T_PHYSDEV_OP_IN)
54			goto multiple_use;
55		check_inverse(optarg, &invert, &optind, 0);
56		parse_interface(argv[optind-1], info->physindev,
57				(unsigned char *)info->in_mask);
58		if (invert)
59			info->invert |= IP6T_PHYSDEV_OP_IN;
60		info->bitmask |= IP6T_PHYSDEV_OP_IN;
61		*flags |= IP6T_PHYSDEV_OP_IN;
62		break;
63
64	case '2':
65		if (*flags & IP6T_PHYSDEV_OP_OUT)
66			goto multiple_use;
67		check_inverse(optarg, &invert, &optind, 0);
68		parse_interface(argv[optind-1], info->physoutdev,
69				(unsigned char *)info->out_mask);
70		if (invert)
71			info->invert |= IP6T_PHYSDEV_OP_OUT;
72		info->bitmask |= IP6T_PHYSDEV_OP_OUT;
73		*flags |= IP6T_PHYSDEV_OP_OUT;
74		break;
75
76	case '3':
77		if (*flags & IP6T_PHYSDEV_OP_ISIN)
78			goto multiple_use;
79		check_inverse(optarg, &invert, &optind, 0);
80		info->bitmask |= IP6T_PHYSDEV_OP_ISIN;
81		if (invert)
82			info->invert |= IP6T_PHYSDEV_OP_ISIN;
83		*flags |= IP6T_PHYSDEV_OP_ISIN;
84		break;
85
86	case '4':
87		if (*flags & IP6T_PHYSDEV_OP_ISOUT)
88			goto multiple_use;
89		check_inverse(optarg, &invert, &optind, 0);
90		info->bitmask |= IP6T_PHYSDEV_OP_ISOUT;
91		if (invert)
92			info->invert |= IP6T_PHYSDEV_OP_ISOUT;
93		*flags |= IP6T_PHYSDEV_OP_ISOUT;
94		break;
95
96	case '5':
97		if (*flags & IP6T_PHYSDEV_OP_BRIDGED)
98			goto multiple_use;
99		check_inverse(optarg, &invert, &optind, 0);
100		if (invert)
101			info->invert |= IP6T_PHYSDEV_OP_BRIDGED;
102		*flags |= IP6T_PHYSDEV_OP_BRIDGED;
103		info->bitmask |= IP6T_PHYSDEV_OP_BRIDGED;
104		break;
105
106	default:
107		return 0;
108	}
109
110	return 1;
111multiple_use:
112	exit_error(PARAMETER_PROBLEM,
113	   "multiple use of the same physdev option is not allowed");
114
115}
116
117static void final_check(unsigned int flags)
118{
119	if (flags == 0)
120		exit_error(PARAMETER_PROBLEM, "PHYSDEV: no physdev option specified");
121}
122
123static void
124print(const struct ip6t_ip6 *ip,
125      const struct ip6t_entry_match *match,
126      int numeric)
127{
128	struct ip6t_physdev_info *info =
129		(struct ip6t_physdev_info*)match->data;
130
131	printf("PHYSDEV match");
132	if (info->bitmask & IP6T_PHYSDEV_OP_ISIN)
133		printf("%s --physdev-is-in",
134		       info->invert & IP6T_PHYSDEV_OP_ISIN ? " !":"");
135	if (info->bitmask & IP6T_PHYSDEV_OP_IN)
136		printf("%s --physdev-in %s",
137		(info->invert & IP6T_PHYSDEV_OP_IN) ? " !":"", info->physindev);
138
139	if (info->bitmask & IP6T_PHYSDEV_OP_ISOUT)
140		printf("%s --physdev-is-out",
141		       info->invert & IP6T_PHYSDEV_OP_ISOUT ? " !":"");
142	if (info->bitmask & IP6T_PHYSDEV_OP_OUT)
143		printf("%s --physdev-out %s",
144		(info->invert & IP6T_PHYSDEV_OP_OUT) ? " !":"", info->physoutdev);
145	if (info->bitmask & IP6T_PHYSDEV_OP_BRIDGED)
146		printf("%s --physdev-is-bridged",
147		       info->invert & IP6T_PHYSDEV_OP_BRIDGED ? " !":"");
148	printf(" ");
149}
150
151static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
152{
153	struct ip6t_physdev_info *info =
154		(struct ip6t_physdev_info*)match->data;
155
156	if (info->bitmask & IP6T_PHYSDEV_OP_ISIN)
157		printf("%s --physdev-is-in",
158		       info->invert & IP6T_PHYSDEV_OP_ISIN ? " !":"");
159	if (info->bitmask & IP6T_PHYSDEV_OP_IN)
160		printf("%s --physdev-in %s",
161		(info->invert & IP6T_PHYSDEV_OP_IN) ? " !":"", info->physindev);
162
163	if (info->bitmask & IP6T_PHYSDEV_OP_ISOUT)
164		printf("%s --physdev-is-out",
165		       info->invert & IP6T_PHYSDEV_OP_ISOUT ? " !":"");
166	if (info->bitmask & IP6T_PHYSDEV_OP_OUT)
167		printf("%s --physdev-out %s",
168		(info->invert & IP6T_PHYSDEV_OP_OUT) ? " !":"", info->physoutdev);
169	if (info->bitmask & IP6T_PHYSDEV_OP_BRIDGED)
170		printf("%s --physdev-is-bridged",
171		       info->invert & IP6T_PHYSDEV_OP_BRIDGED ? " !":"");
172	printf(" ");
173}
174
175static struct ip6tables_match physdev = {
176	.name		= "physdev",
177	.version	= IPTABLES_VERSION,
178	.size		= IP6T_ALIGN(sizeof(struct ip6t_physdev_info)),
179	.userspacesize	= IP6T_ALIGN(sizeof(struct ip6t_physdev_info)),
180	.help		= &help,
181	.init		= &init,
182	.parse		= &parse,
183	.final_check	= &final_check,
184	.print		= &print,
185	.save		= &save,
186	.extra_opts	= opts,
187};
188
189void _init(void)
190{
191	register_match6(&physdev);
192}
193