libxt_policy.c revision bf97128c7262f17a02fec41cdae75b472ba77f88
1ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta/* Shared library add-on to iptables to add policy support. */
2ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta#include <stdio.h>
3ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta#include <netdb.h>
4ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta#include <string.h>
5ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta#include <stdlib.h>
6ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta#include <syslog.h>
7ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta#include <getopt.h>
8ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta#include <netdb.h>
9ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta#include <errno.h>
10ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta#include <sys/socket.h>
11ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta#include <netinet/in.h>
12ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta#include <arpa/inet.h>
13ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta#include <xtables.h>
14ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta
15ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta#include <linux/netfilter/xt_policy.h>
16ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta
17ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta/*
18ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta * HACK: global pointer to current matchinfo for making
19ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta * final checks and adjustments in final_check.
20ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta */
21ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Guptastatic struct xt_policy_info *policy_info;
22ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta
23ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Guptastatic void policy_help(void)
24ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta{
25ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	printf(
26ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta"policy match options:\n"
27ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta"  --dir in|out			match policy applied during decapsulation/\n"
28ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta"				policy to be applied during encapsulation\n"
29ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta"  --pol none|ipsec		match policy\n"
30ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta"  --strict 			match entire policy instead of single element\n"
31ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta"				at any position\n"
32ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta"[!] --reqid reqid		match reqid\n"
33ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta"[!] --spi spi			match SPI\n"
34ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta"[!] --proto proto		match protocol (ah/esp/ipcomp)\n"
35ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta"[!] --mode mode 		match mode (transport/tunnel)\n"
36ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta"[!] --tunnel-src addr/mask	match tunnel source\n"
37ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta"[!] --tunnel-dst addr/mask	match tunnel destination\n"
38ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta"  --next 			begin next element in policy\n");
39ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta}
40ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta
41ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Guptastatic const struct option policy_opts[] =
42ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta{
43ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	{
44ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.name		= "dir",
45ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.has_arg	= 1,
46ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.val		= '1',
47ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	},
48ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	{
49ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.name		= "pol",
50ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.has_arg	= 1,
51ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.val		= '2',
52ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	},
53ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	{
54ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.name		= "strict",
55ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.val		= '3'
56ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	},
57ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	{
58ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.name		= "reqid",
59ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.has_arg	= 1,
60ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.val		= '4',
61ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	},
62ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	{
63ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.name		= "spi",
64ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.has_arg	= 1,
65ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.val		= '5'
66ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	},
67ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	{
68ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.name		= "tunnel-src",
69ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.has_arg	= 1,
70ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.val		= '6'
71ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	},
72ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	{
73ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.name		= "tunnel-dst",
74ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.has_arg	= 1,
75ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.val		= '7'
76ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	},
77ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	{
78ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.name		= "proto",
79ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.has_arg	= 1,
80ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.val		= '8'
81ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	},
82ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	{
83ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.name		= "mode",
84ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.has_arg	= 1,
85ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.val		= '9'
86ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	},
87ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	{
88ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.name		= "next",
89ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		.val		= 'a'
90ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	},
91ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	{ .name = NULL }
92ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta};
93ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta
94ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Guptastatic int parse_direction(char *s)
95ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta{
96ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	if (strcmp(s, "in") == 0)
97ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		return XT_POLICY_MATCH_IN;
98ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	if (strcmp(s, "out") == 0)
99ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		return XT_POLICY_MATCH_OUT;
100ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	xtables_error(PARAMETER_PROBLEM, "policy_match: invalid dir \"%s\"", s);
101ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta}
102ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta
103ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Guptastatic int parse_policy(char *s)
104ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta{
105ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	if (strcmp(s, "none") == 0)
106ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		return XT_POLICY_MATCH_NONE;
107ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	if (strcmp(s, "ipsec") == 0)
108ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		return 0;
109ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	xtables_error(PARAMETER_PROBLEM, "policy match: invalid policy \"%s\"", s);
110ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta}
111ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta
112ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Guptastatic int parse_mode(char *s)
113ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta{
114ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	if (strcmp(s, "transport") == 0)
115ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		return XT_POLICY_MODE_TRANSPORT;
116ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	if (strcmp(s, "tunnel") == 0)
117ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		return XT_POLICY_MODE_TUNNEL;
118ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	xtables_error(PARAMETER_PROBLEM, "policy match: invalid mode \"%s\"", s);
119ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta}
120ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta
121ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Guptastatic int policy_parse(int c, char **argv, int invert, unsigned int *flags,
122ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta                        struct xt_policy_info *info, uint8_t family)
123ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta{
124ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	struct xt_policy_elem *e = &info->pol[info->len];
125ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	struct in_addr *addr = NULL, mask;
126ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	struct in6_addr *addr6 = NULL, mask6;
127ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	unsigned int naddr = 0, num;
128ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	int mode;
129ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta
130ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	xtables_check_inverse(optarg, &invert, &optind, 0, argv);
131ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta
132ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	switch (c) {
133ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	case '1':
134ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		if (info->flags & (XT_POLICY_MATCH_IN | XT_POLICY_MATCH_OUT))
135ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta			xtables_error(PARAMETER_PROBLEM,
136ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta			           "policy match: double --dir option");
137ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		if (invert)
138ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta			xtables_error(PARAMETER_PROBLEM,
139ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta			           "policy match: can't invert --dir option");
140ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta
141ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		info->flags |= parse_direction(optarg);
142ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		break;
143ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	case '2':
144ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		if (invert)
145ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta			xtables_error(PARAMETER_PROBLEM,
146ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta			           "policy match: can't invert --policy option");
147ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta
148ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		info->flags |= parse_policy(optarg);
149ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		break;
150ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	case '3':
151ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		if (info->flags & XT_POLICY_MATCH_STRICT)
152ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta			xtables_error(PARAMETER_PROBLEM,
153ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta			           "policy match: double --strict option");
154ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta
155ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		if (invert)
156ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta			xtables_error(PARAMETER_PROBLEM,
157ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta			           "policy match: can't invert --strict option");
158ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta
159ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		info->flags |= XT_POLICY_MATCH_STRICT;
160ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		break;
161ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	case '4':
162ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		if (e->match.reqid)
163ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta			xtables_error(PARAMETER_PROBLEM,
164ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta			           "policy match: double --reqid option");
165ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta
166ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		e->match.reqid = 1;
167ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		e->invert.reqid = invert;
168ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		if (!xtables_strtoui(optarg, NULL, &num, 0, UINT32_MAX))
169ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta			xtables_param_act(XTF_BAD_VALUE, "policy", "--spi", optarg);
170ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		e->reqid = num;
171ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		break;
172ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	case '5':
173ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		if (e->match.spi)
174ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta			xtables_error(PARAMETER_PROBLEM,
175ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta			           "policy match: double --spi option");
176ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta
177ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		e->match.spi = 1;
178ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		e->invert.spi = invert;
179ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		if (!xtables_strtoui(optarg, NULL, &num, 0, UINT32_MAX))
180ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta			xtables_param_act(XTF_BAD_VALUE, "policy", "--spi", optarg);
181ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		e->spi = num;
182ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		break;
183ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta	case '6':
184ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		if (e->match.saddr)
185ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta			xtables_error(PARAMETER_PROBLEM,
186ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta			           "policy match: double --tunnel-src option");
187ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta
188ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		if (family == NFPROTO_IPV6)
189ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta			xtables_ip6parse_any(optarg, &addr6, &mask6, &naddr);
190ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		else
191ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta			xtables_ipparse_any(optarg, &addr, &mask, &naddr);
192ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		if (naddr > 1)
193ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta			xtables_error(PARAMETER_PROBLEM,
194ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta			           "policy match: name resolves to multiple IPs");
195ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta
196ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		e->match.saddr = 1;
197ce24765fe7620c34e8d88ed4f826c8a6917582b2Hemant Gupta		e->invert.saddr = invert;
198		if (family == NFPROTO_IPV6) {
199			memcpy(&e->saddr.a6, addr6, sizeof(*addr6));
200			memcpy(&e->smask.a6, &mask6, sizeof(mask6));
201		} else {
202			e->saddr.a4 = addr[0];
203			e->smask.a4 = mask;
204		}
205                break;
206	case '7':
207		if (e->match.daddr)
208			xtables_error(PARAMETER_PROBLEM,
209			           "policy match: double --tunnel-dst option");
210
211		if (family == NFPROTO_IPV6)
212			xtables_ip6parse_any(optarg, &addr6, &mask6, &naddr);
213		else
214			xtables_ipparse_any(optarg, &addr, &mask, &naddr);
215		if (naddr > 1)
216			xtables_error(PARAMETER_PROBLEM,
217			           "policy match: name resolves to multiple IPs");
218
219		e->match.daddr = 1;
220		e->invert.daddr = invert;
221		if (family == NFPROTO_IPV6) {
222			memcpy(&e->daddr.a6, addr6, sizeof(*addr6));
223			memcpy(&e->dmask.a6, &mask6, sizeof(mask6));
224		} else {
225			e->daddr.a4 = addr[0];
226			e->dmask.a4 = mask;
227		}
228		break;
229	case '8':
230		if (e->match.proto)
231			xtables_error(PARAMETER_PROBLEM,
232			           "policy match: double --proto option");
233
234		e->proto = xtables_parse_protocol(optarg);
235		if (e->proto != IPPROTO_AH && e->proto != IPPROTO_ESP &&
236		    e->proto != IPPROTO_COMP)
237			xtables_error(PARAMETER_PROBLEM,
238			           "policy match: protocol must ah/esp/ipcomp");
239		e->match.proto = 1;
240		e->invert.proto = invert;
241		break;
242	case '9':
243		if (e->match.mode)
244			xtables_error(PARAMETER_PROBLEM,
245			           "policy match: double --mode option");
246
247		mode = parse_mode(optarg);
248		e->match.mode = 1;
249		e->invert.mode = invert;
250		e->mode = mode;
251		break;
252	case 'a':
253		if (invert)
254			xtables_error(PARAMETER_PROBLEM,
255			           "policy match: can't invert --next option");
256
257		if (++info->len == XT_POLICY_MAX_ELEM)
258			xtables_error(PARAMETER_PROBLEM,
259			           "policy match: maximum policy depth reached");
260		break;
261	default:
262		return 0;
263	}
264
265	policy_info = info;
266	return 1;
267}
268
269static int policy4_parse(int c, char **argv, int invert, unsigned int *flags,
270                         const void *entry, struct xt_entry_match **match)
271{
272	return policy_parse(c, argv, invert, flags, (void *)(*match)->data,
273	       NFPROTO_IPV4);
274}
275
276static int policy6_parse(int c, char **argv, int invert, unsigned int *flags,
277                        const void *entry, struct xt_entry_match **match)
278{
279	return policy_parse(c, argv, invert, flags, (void *)(*match)->data,
280	       NFPROTO_IPV6);
281}
282
283static void policy_check(unsigned int flags)
284{
285	struct xt_policy_info *info = policy_info;
286	struct xt_policy_elem *e;
287	int i;
288
289	if (info == NULL)
290		xtables_error(PARAMETER_PROBLEM,
291		           "policy match: no parameters given");
292
293	if (!(info->flags & (XT_POLICY_MATCH_IN | XT_POLICY_MATCH_OUT)))
294		xtables_error(PARAMETER_PROBLEM,
295		           "policy match: neither --in nor --out specified");
296
297	if (info->flags & XT_POLICY_MATCH_NONE) {
298		if (info->flags & XT_POLICY_MATCH_STRICT)
299			xtables_error(PARAMETER_PROBLEM,
300			           "policy match: policy none but --strict given");
301
302		if (info->len != 0)
303			xtables_error(PARAMETER_PROBLEM,
304			           "policy match: policy none but policy given");
305	} else
306		info->len++;	/* increase len by 1, no --next after last element */
307
308	if (!(info->flags & XT_POLICY_MATCH_STRICT) && info->len > 1)
309		xtables_error(PARAMETER_PROBLEM,
310		           "policy match: multiple elements but no --strict");
311
312	for (i = 0; i < info->len; i++) {
313		e = &info->pol[i];
314
315		if (info->flags & XT_POLICY_MATCH_STRICT &&
316		    !(e->match.reqid || e->match.spi || e->match.saddr ||
317		      e->match.daddr || e->match.proto || e->match.mode))
318			xtables_error(PARAMETER_PROBLEM,
319			           "policy match: empty policy element");
320
321		if ((e->match.saddr || e->match.daddr)
322		    && ((e->mode == XT_POLICY_MODE_TUNNEL && e->invert.mode) ||
323		        (e->mode == XT_POLICY_MODE_TRANSPORT && !e->invert.mode)))
324			xtables_error(PARAMETER_PROBLEM,
325			           "policy match: --tunnel-src/--tunnel-dst "
326			           "is only valid in tunnel mode");
327	}
328}
329
330static void print_mode(const char *prefix, u_int8_t mode, int numeric)
331{
332	printf("%smode ", prefix);
333
334	switch (mode) {
335	case XT_POLICY_MODE_TRANSPORT:
336		printf("transport ");
337		break;
338	case XT_POLICY_MODE_TUNNEL:
339		printf("tunnel ");
340		break;
341	default:
342		printf("??? ");
343		break;
344	}
345}
346
347static void print_proto(const char *prefix, u_int8_t proto, int numeric)
348{
349	struct protoent *p = NULL;
350
351	printf("%sproto ", prefix);
352	if (!numeric)
353		p = getprotobynumber(proto);
354	if (p != NULL)
355		printf("%s ", p->p_name);
356	else
357		printf("%u ", proto);
358}
359
360#define PRINT_INVERT(x)		\
361do {				\
362	if (x)			\
363		printf("! ");	\
364} while(0)
365
366static void print_entry(const char *prefix, const struct xt_policy_elem *e,
367                        bool numeric, uint8_t family)
368{
369	if (e->match.reqid) {
370		PRINT_INVERT(e->invert.reqid);
371		printf("%sreqid %u ", prefix, e->reqid);
372	}
373	if (e->match.spi) {
374		PRINT_INVERT(e->invert.spi);
375		printf("%sspi 0x%x ", prefix, e->spi);
376	}
377	if (e->match.proto) {
378		PRINT_INVERT(e->invert.proto);
379		print_proto(prefix, e->proto, numeric);
380	}
381	if (e->match.mode) {
382		PRINT_INVERT(e->invert.mode);
383		print_mode(prefix, e->mode, numeric);
384	}
385	if (e->match.daddr) {
386		PRINT_INVERT(e->invert.daddr);
387		if (family == NFPROTO_IPV6)
388			printf("%stunnel-dst %s%s ", prefix,
389			       xtables_ip6addr_to_numeric(&e->daddr.a6),
390			       xtables_ip6mask_to_numeric(&e->dmask.a6));
391		else
392			printf("%stunnel-dst %s%s ", prefix,
393			       xtables_ipaddr_to_numeric(&e->daddr.a4),
394			       xtables_ipmask_to_numeric(&e->dmask.a4));
395	}
396	if (e->match.saddr) {
397		PRINT_INVERT(e->invert.saddr);
398		if (family == NFPROTO_IPV6)
399			printf("%stunnel-src %s%s ", prefix,
400			       xtables_ip6addr_to_numeric(&e->saddr.a6),
401			       xtables_ip6mask_to_numeric(&e->smask.a6));
402		else
403			printf("%stunnel-src %s%s ", prefix,
404			       xtables_ipaddr_to_numeric(&e->saddr.a4),
405			       xtables_ipmask_to_numeric(&e->smask.a4));
406	}
407}
408
409static void print_flags(char *prefix, const struct xt_policy_info *info)
410{
411	if (info->flags & XT_POLICY_MATCH_IN)
412		printf("%sdir in ", prefix);
413	else
414		printf("%sdir out ", prefix);
415
416	if (info->flags & XT_POLICY_MATCH_NONE)
417		printf("%spol none ", prefix);
418	else
419		printf("%spol ipsec ", prefix);
420
421	if (info->flags & XT_POLICY_MATCH_STRICT)
422		printf("%sstrict ", prefix);
423}
424
425static void policy4_print(const void *ip, const struct xt_entry_match *match,
426                          int numeric)
427{
428	const struct xt_policy_info *info = (void *)match->data;
429	unsigned int i;
430
431	printf("policy match ");
432	print_flags("", info);
433	for (i = 0; i < info->len; i++) {
434		if (info->len > 1)
435			printf("[%u] ", i);
436		print_entry("", &info->pol[i], numeric, NFPROTO_IPV4);
437	}
438}
439
440static void policy6_print(const void *ip, const struct xt_entry_match *match,
441                          int numeric)
442{
443	const struct xt_policy_info *info = (void *)match->data;
444	unsigned int i;
445
446	printf("policy match ");
447	print_flags("", info);
448	for (i = 0; i < info->len; i++) {
449		if (info->len > 1)
450			printf("[%u] ", i);
451		print_entry("", &info->pol[i], numeric, NFPROTO_IPV6);
452	}
453}
454
455static void policy4_save(const void *ip, const struct xt_entry_match *match)
456{
457	const struct xt_policy_info *info = (void *)match->data;
458	unsigned int i;
459
460	print_flags("--", info);
461	for (i = 0; i < info->len; i++) {
462		print_entry("--", &info->pol[i], false, NFPROTO_IPV4);
463		if (i + 1 < info->len)
464			printf("--next ");
465	}
466}
467
468static void policy6_save(const void *ip, const struct xt_entry_match *match)
469{
470	const struct xt_policy_info *info = (void *)match->data;
471	unsigned int i;
472
473	print_flags("--", info);
474	for (i = 0; i < info->len; i++) {
475		print_entry("--", &info->pol[i], false, NFPROTO_IPV6);
476		if (i + 1 < info->len)
477			printf("--next ");
478	}
479}
480
481static struct xtables_match policy_mt_reg[] = {
482	{
483		.name          = "policy",
484		.version       = XTABLES_VERSION,
485		.family        = NFPROTO_IPV4,
486		.size          = XT_ALIGN(sizeof(struct xt_policy_info)),
487		.userspacesize = XT_ALIGN(sizeof(struct xt_policy_info)),
488		.help          = policy_help,
489		.parse         = policy4_parse,
490		.final_check   = policy_check,
491		.print         = policy4_print,
492		.save          = policy4_save,
493		.extra_opts    = policy_opts,
494	},
495	{
496		.name          = "policy",
497		.version       = XTABLES_VERSION,
498		.family        = NFPROTO_IPV6,
499		.size          = XT_ALIGN(sizeof(struct xt_policy_info)),
500		.userspacesize = XT_ALIGN(sizeof(struct xt_policy_info)),
501		.help          = policy_help,
502		.parse         = policy6_parse,
503		.final_check   = policy_check,
504		.print         = policy6_print,
505		.save          = policy6_save,
506		.extra_opts    = policy_opts,
507	},
508};
509
510void _init(void)
511{
512	xtables_register_matches(policy_mt_reg, ARRAY_SIZE(policy_mt_reg));
513}
514