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