Only initialize extensions we actually use
[project/firewall3.git] / iptables.c
1 /*
2 * firewall3 - 3rd OpenWrt UCI firewall implementation
3 *
4 * Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include "iptables.h"
20
21
22 static struct option base_opts[] = {
23 { .name = "match", .has_arg = 1, .val = 'm' },
24 { .name = "jump", .has_arg = 1, .val = 'j' },
25 { .name = "append", .has_arg = 1, .val = 'A' },
26 { NULL }
27 };
28
29 static struct xtables_globals xtg = {
30 .option_offset = 0,
31 .program_version = "4",
32 .orig_opts = base_opts,
33 };
34
35 static struct xtables_globals xtg6 = {
36 .option_offset = 0,
37 .program_version = "6",
38 .orig_opts = base_opts,
39 };
40
41 /* Required by certain extensions like SNAT and DNAT */
42 int kernel_version;
43
44 void
45 get_kernel_version(void)
46 {
47 static struct utsname uts;
48 int x = 0, y = 0, z = 0;
49
50 if (uname(&uts) == -1)
51 sprintf(uts.release, "3.0.0");
52
53 sscanf(uts.release, "%d.%d.%d", &x, &y, &z);
54 kernel_version = LINUX_VERSION(x, y, z);
55 }
56
57 static void fw3_init_extensions(void)
58 {
59 libip6t_icmp6_init();
60 libip6t_LOG_init();
61 libipt_DNAT_init();
62 libipt_icmp_init();
63 libipt_LOG_init();
64 libipt_MASQUERADE_init();
65 libipt_REDIRECT_init();
66 libipt_REJECT_init();
67 libipt_SNAT_init();
68 libxt_comment_init();
69 libxt_conntrack_init();
70 libxt_CT_init();
71 libxt_limit_init();
72 libxt_mac_init();
73 libxt_mark_init();
74 libxt_MARK_init();
75 libxt_set_init();
76 libxt_SET_init();
77 libxt_standard_init();
78 libxt_TCPMSS_init();
79 libxt_tcp_init();
80 libxt_time_init();
81 libxt_udp_init();
82 }
83
84 struct fw3_ipt_handle *
85 fw3_ipt_open(enum fw3_family family, enum fw3_table table)
86 {
87 struct fw3_ipt_handle *h;
88
89 h = fw3_alloc(sizeof(*h));
90
91 xtables_init();
92
93 if (family == FW3_FAMILY_V6)
94 {
95 h->family = FW3_FAMILY_V6;
96 h->table = table;
97 h->handle = ip6tc_init(fw3_flag_names[table]);
98
99 xtables_set_params(&xtg6);
100 xtables_set_nfproto(NFPROTO_IPV6);
101 }
102 else
103 {
104 h->family = FW3_FAMILY_V4;
105 h->table = table;
106 h->handle = iptc_init(fw3_flag_names[table]);
107
108 xtables_set_params(&xtg);
109 xtables_set_nfproto(NFPROTO_IPV4);
110 }
111
112 if (!h->handle)
113 {
114 free(h);
115 return NULL;
116 }
117
118 xtables_pending_matches = NULL;
119 xtables_pending_targets = NULL;
120
121 xtables_matches = NULL;
122 xtables_targets = NULL;
123
124 fw3_init_extensions();
125
126 return h;
127 }
128
129 static void
130 debug(struct fw3_ipt_handle *h, const char *fmt, ...)
131 {
132 va_list ap;
133
134 printf("%s -t %s ", (h->family == FW3_FAMILY_V6) ? "ip6tables" : "iptables",
135 fw3_flag_names[h->table]);
136
137 va_start(ap, fmt);
138 vprintf(fmt, ap);
139 va_end(ap);
140 }
141
142 void
143 fw3_ipt_set_policy(struct fw3_ipt_handle *h, const char *chain,
144 enum fw3_flag policy)
145 {
146 if (fw3_pr_debug)
147 debug(h, "-P %s %s\n", chain, fw3_flag_names[policy]);
148
149 if (h->family == FW3_FAMILY_V6)
150 ip6tc_set_policy(chain, fw3_flag_names[policy], NULL, h->handle);
151 else
152 iptc_set_policy(chain, fw3_flag_names[policy], NULL, h->handle);
153 }
154
155 void
156 fw3_ipt_delete_chain(struct fw3_ipt_handle *h, const char *chain)
157 {
158 if (fw3_pr_debug)
159 {
160 debug(h, "-F %s\n", chain);
161 debug(h, "-X %s\n", chain);
162 }
163
164 if (h->family == FW3_FAMILY_V6)
165 {
166 if (ip6tc_flush_entries(chain, h->handle))
167 ip6tc_delete_chain(chain, h->handle);
168 }
169 else
170 {
171 if (iptc_flush_entries(chain, h->handle))
172 iptc_delete_chain(chain, h->handle);
173 }
174 }
175
176 void
177 fw3_ipt_delete_rules(struct fw3_ipt_handle *h, const char *target)
178 {
179 unsigned int num;
180 const struct ipt_entry *e;
181 const struct ip6t_entry *e6;
182 const char *chain;
183 const char *t;
184 bool found;
185
186 if (h->family == FW3_FAMILY_V6)
187 {
188 for (chain = ip6tc_first_chain(h->handle);
189 chain != NULL;
190 chain = ip6tc_next_chain(h->handle))
191 {
192 do {
193 found = false;
194
195 for (num = 0, e6 = ip6tc_first_rule(chain, h->handle);
196 e6 != NULL;
197 num++, e6 = ip6tc_next_rule(e6, h->handle))
198 {
199 t = ip6tc_get_target(e6, h->handle);
200
201 if (*t && !strcmp(t, target))
202 {
203 if (fw3_pr_debug)
204 debug(h, "-D %s %u\n", chain, num + 1);
205
206 ip6tc_delete_num_entry(chain, num, h->handle);
207 found = true;
208 break;
209 }
210 }
211 } while (found);
212 }
213 }
214 else
215 {
216 for (chain = iptc_first_chain(h->handle);
217 chain != NULL;
218 chain = iptc_next_chain(h->handle))
219 {
220 do {
221 found = false;
222
223 for (num = 0, e = iptc_first_rule(chain, h->handle);
224 e != NULL;
225 num++, e = iptc_next_rule(e, h->handle))
226 {
227 t = iptc_get_target(e, h->handle);
228
229 if (*t && !strcmp(t, target))
230 {
231 if (fw3_pr_debug)
232 debug(h, "-D %s %u\n", chain, num + 1);
233
234 iptc_delete_num_entry(chain, num, h->handle);
235 found = true;
236 break;
237 }
238 }
239 } while (found);
240 }
241 }
242 }
243
244 void
245 fw3_ipt_create_chain(struct fw3_ipt_handle *h, const char *fmt, ...)
246 {
247 char buf[32];
248 va_list ap;
249
250 va_start(ap, fmt);
251 vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
252 va_end(ap);
253
254 if (fw3_pr_debug)
255 debug(h, "-N %s\n", buf);
256
257 iptc_create_chain(buf, h->handle);
258 }
259
260 void
261 fw3_ipt_flush(struct fw3_ipt_handle *h)
262 {
263 const char *chain;
264
265 if (h->family == FW3_FAMILY_V6)
266 {
267 for (chain = ip6tc_first_chain(h->handle);
268 chain != NULL;
269 chain = ip6tc_next_chain(h->handle))
270 {
271 ip6tc_flush_entries(chain, h->handle);
272 }
273
274 for (chain = ip6tc_first_chain(h->handle);
275 chain != NULL;
276 chain = ip6tc_next_chain(h->handle))
277 {
278 ip6tc_delete_chain(chain, h->handle);
279 }
280 }
281 else
282 {
283 for (chain = iptc_first_chain(h->handle);
284 chain != NULL;
285 chain = iptc_next_chain(h->handle))
286 {
287 iptc_flush_entries(chain, h->handle);
288 }
289
290 for (chain = iptc_first_chain(h->handle);
291 chain != NULL;
292 chain = iptc_next_chain(h->handle))
293 {
294 iptc_delete_chain(chain, h->handle);
295 }
296 }
297 }
298
299 void
300 fw3_ipt_commit(struct fw3_ipt_handle *h)
301 {
302 int rv;
303
304 if (h->family == FW3_FAMILY_V6)
305 {
306 rv = ip6tc_commit(h->handle);
307 if (!rv)
308 fprintf(stderr, "ip6tc_commit(): %s\n", ip6tc_strerror(errno));
309 }
310 else
311 {
312 rv = iptc_commit(h->handle);
313 if (!rv)
314 fprintf(stderr, "iptc_commit(): %s\n", iptc_strerror(errno));
315 }
316
317 free(h);
318 }
319
320 struct fw3_ipt_rule *
321 fw3_ipt_rule_new(struct fw3_ipt_handle *h)
322 {
323 struct fw3_ipt_rule *r;
324
325 r = fw3_alloc(sizeof(*r));
326
327 r->h = h;
328 r->argv = fw3_alloc(sizeof(char *));
329 r->argv[r->argc++] = "fw3";
330
331 return r;
332 }
333
334
335 static bool
336 is_chain(struct fw3_ipt_handle *h, const char *name)
337 {
338 if (h->family == FW3_FAMILY_V6)
339 return ip6tc_is_chain(name, h->handle);
340 else
341 return iptc_is_chain(name, h->handle);
342 }
343
344 static char *
345 get_protoname(struct fw3_ipt_rule *r)
346 {
347 const struct xtables_pprot *pp;
348
349 if (r->protocol)
350 for (pp = xtables_chain_protos; pp->name; pp++)
351 if (pp->num == r->protocol)
352 return (char *)pp->name;
353
354 return NULL;
355 }
356
357 static struct xtables_match *
358 find_match(struct fw3_ipt_rule *r, const char *name)
359 {
360 return xtables_find_match(name, XTF_TRY_LOAD, &r->matches);
361 }
362
363 static void
364 init_match(struct fw3_ipt_rule *r, struct xtables_match *m, bool no_clone)
365 {
366 size_t s;
367 struct xtables_globals *g;
368
369 if (!m)
370 return;
371
372 s = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size;
373
374 m->m = fw3_alloc(s);
375 strcpy(m->m->u.user.name, m->real_name ? m->real_name : m->name);
376 m->m->u.user.revision = m->revision;
377 m->m->u.match_size = s;
378
379 /* free previous userspace data */
380 if (m->udata_size)
381 {
382 free(m->udata);
383 m->udata = fw3_alloc(m->udata_size);
384 }
385
386 if (m->init)
387 m->init(m->m);
388
389 /* don't merge options if no_clone is set and this match is a clone */
390 if (no_clone && (m == m->next))
391 return;
392
393 /* merge option table */
394 g = (r->h->family == FW3_FAMILY_V6) ? &xtg6 : &xtg;
395
396 if (m->x6_options)
397 g->opts = xtables_options_xfrm(g->orig_opts, g->opts,
398 m->x6_options, &m->option_offset);
399
400 if (m->extra_opts)
401 g->opts = xtables_merge_options(g->orig_opts, g->opts,
402 m->extra_opts, &m->option_offset);
403 }
404
405 static bool
406 need_protomatch(struct fw3_ipt_rule *r, const char *pname)
407 {
408 if (!pname)
409 return false;
410
411 if (!xtables_find_match(pname, XTF_DONT_LOAD, NULL))
412 return true;
413
414 return !r->protocol_loaded;
415 }
416
417 static struct xtables_match *
418 load_protomatch(struct fw3_ipt_rule *r)
419 {
420 const char *pname = get_protoname(r);
421
422 if (!need_protomatch(r, pname))
423 return NULL;
424
425 return find_match(r, pname);
426 }
427
428 static struct xtables_target *
429 get_target(struct fw3_ipt_rule *r, const char *name)
430 {
431 size_t s;
432 struct xtables_target *t;
433 struct xtables_globals *g;
434
435 bool chain = is_chain(r->h, name);
436
437 if (chain)
438 t = xtables_find_target(XT_STANDARD_TARGET, XTF_LOAD_MUST_SUCCEED);
439 else
440 t = xtables_find_target(name, XTF_TRY_LOAD);
441
442 if (!t)
443 return NULL;
444
445 s = XT_ALIGN(sizeof(struct xt_entry_target)) + t->size;
446 t->t = fw3_alloc(s);
447
448 if (!t->real_name)
449 strcpy(t->t->u.user.name, name);
450 else
451 strcpy(t->t->u.user.name, t->real_name);
452
453 t->t->u.user.revision = t->revision;
454 t->t->u.target_size = s;
455
456 if (t->udata_size)
457 {
458 free(t->udata);
459 t->udata = fw3_alloc(t->udata_size);
460 }
461
462 if (t->init)
463 t->init(t->t);
464
465 /* merge option table */
466 g = (r->h->family == FW3_FAMILY_V6) ? &xtg6 : &xtg;
467
468 if (t->x6_options)
469 g->opts = xtables_options_xfrm(g->orig_opts, g->opts,
470 t->x6_options, &t->option_offset);
471 else
472 g->opts = xtables_merge_options(g->orig_opts, g->opts,
473 t->extra_opts, &t->option_offset);
474
475 r->target = t;
476
477 return t;
478 }
479
480 void
481 fw3_ipt_rule_proto(struct fw3_ipt_rule *r, struct fw3_protocol *proto)
482 {
483 uint32_t pr;
484
485 if (!proto || proto->any)
486 return;
487
488 pr = proto->protocol;
489
490 if (r->h->family == FW3_FAMILY_V6)
491 {
492 if (pr == 1)
493 pr = 58;
494
495 r->e6.ipv6.proto = pr;
496 r->e6.ipv6.flags |= IP6T_F_PROTO;
497
498 if (proto->invert)
499 r->e6.ipv6.invflags |= XT_INV_PROTO;
500 }
501 else
502 {
503 r->e.ip.proto = pr;
504
505 if (proto->invert)
506 r->e.ip.invflags |= XT_INV_PROTO;
507 }
508
509 r->protocol = pr;
510 }
511
512 void
513 fw3_ipt_rule_in_out(struct fw3_ipt_rule *r,
514 struct fw3_device *in, struct fw3_device *out)
515 {
516 if (r->h->family == FW3_FAMILY_V6)
517 {
518 if (in && !in->any)
519 {
520 xtables_parse_interface(in->name, r->e6.ipv6.iniface,
521 r->e6.ipv6.iniface_mask);
522
523 if (in->invert)
524 r->e6.ipv6.invflags |= IP6T_INV_VIA_IN;
525 }
526
527 if (out && !out->any)
528 {
529 xtables_parse_interface(out->name, r->e6.ipv6.outiface,
530 r->e6.ipv6.outiface_mask);
531
532 if (out->invert)
533 r->e6.ipv6.invflags |= IP6T_INV_VIA_OUT;
534 }
535 }
536 else
537 {
538 if (in && !in->any)
539 {
540 xtables_parse_interface(in->name, r->e.ip.iniface,
541 r->e.ip.iniface_mask);
542
543 if (in->invert)
544 r->e.ip.invflags |= IPT_INV_VIA_IN;
545 }
546
547 if (out && !out->any)
548 {
549 xtables_parse_interface(out->name, r->e.ip.outiface,
550 r->e.ip.outiface_mask);
551
552 if (out->invert)
553 r->e.ip.invflags |= IPT_INV_VIA_OUT;
554 }
555 }
556 }
557
558
559 static void
560 ip4prefix2mask(int prefix, struct in_addr *mask)
561 {
562 mask->s_addr = htonl(~((1 << (32 - prefix)) - 1));
563 }
564
565 static void
566 ip6prefix2mask(int prefix, struct in6_addr *mask)
567 {
568 char *p = (char *)mask;
569
570 if (prefix > 0)
571 {
572 memset(p, 0xff, prefix / 8);
573 memset(p + (prefix / 8) + 1, 0, (128 - prefix) / 8);
574 p[prefix / 8] = 0xff << (8 - (prefix & 7));
575 }
576 else
577 {
578 memset(mask, 0, sizeof(*mask));
579 }
580 }
581
582 void
583 fw3_ipt_rule_src_dest(struct fw3_ipt_rule *r,
584 struct fw3_address *src, struct fw3_address *dest)
585 {
586 int i;
587
588 if ((src && src->range) || (dest && dest->range))
589 {
590 fw3_ipt_rule_addarg(r, false, "-m", "iprange");
591 }
592
593 if (src && src->set)
594 {
595 if (src->range)
596 {
597 fw3_ipt_rule_addarg(r, src->invert, "--src-range",
598 fw3_address_to_string(src, false));
599 }
600 else if (r->h->family == FW3_FAMILY_V6)
601 {
602 r->e6.ipv6.src = src->address.v6;
603 ip6prefix2mask(src->mask, &r->e6.ipv6.smsk);
604
605 for (i = 0; i < 4; i++)
606 r->e6.ipv6.src.s6_addr32[i] &= r->e6.ipv6.smsk.s6_addr32[i];
607
608 if (src->invert)
609 r->e6.ipv6.invflags |= IP6T_INV_SRCIP;
610 }
611 else
612 {
613 r->e.ip.src = src->address.v4;
614 ip4prefix2mask(src->mask, &r->e.ip.smsk);
615
616 r->e.ip.src.s_addr &= r->e.ip.smsk.s_addr;
617
618 if (src->invert)
619 r->e.ip.invflags |= IPT_INV_SRCIP;
620 }
621 }
622
623 if (dest && dest->set)
624 {
625 if (dest->range)
626 {
627 fw3_ipt_rule_addarg(r, dest->invert, "--dst-range",
628 fw3_address_to_string(dest, false));
629 }
630 else if (r->h->family == FW3_FAMILY_V6)
631 {
632 r->e6.ipv6.dst = dest->address.v6;
633 ip6prefix2mask(dest->mask, &r->e6.ipv6.dmsk);
634
635 for (i = 0; i < 4; i++)
636 r->e6.ipv6.dst.s6_addr32[i] &= r->e6.ipv6.dmsk.s6_addr32[i];
637
638 if (dest->invert)
639 r->e6.ipv6.invflags |= IP6T_INV_DSTIP;
640 }
641 else
642 {
643 r->e.ip.dst = dest->address.v4;
644 ip4prefix2mask(dest->mask, &r->e.ip.dmsk);
645
646 r->e.ip.dst.s_addr &= r->e.ip.dmsk.s_addr;
647
648 if (dest->invert)
649 r->e.ip.invflags |= IPT_INV_DSTIP;
650 }
651 }
652 }
653
654 void
655 fw3_ipt_rule_sport_dport(struct fw3_ipt_rule *r,
656 struct fw3_port *sp, struct fw3_port *dp)
657 {
658 char buf[sizeof("65535:65535\0")];
659
660 if ((!sp || !sp->set) && (!dp || !dp->set))
661 return;
662
663 if (!get_protoname(r))
664 return;
665
666 if (sp && sp->set)
667 {
668 if (sp->port_min == sp->port_max)
669 sprintf(buf, "%u", sp->port_min);
670 else
671 sprintf(buf, "%u:%u", sp->port_min, sp->port_max);
672
673 fw3_ipt_rule_addarg(r, sp->invert, "--sport", buf);
674 }
675
676 if (dp && dp->set)
677 {
678 if (dp->port_min == dp->port_max)
679 sprintf(buf, "%u", dp->port_min);
680 else
681 sprintf(buf, "%u:%u", dp->port_min, dp->port_max);
682
683 fw3_ipt_rule_addarg(r, dp->invert, "--dport", buf);
684 }
685 }
686
687 void
688 fw3_ipt_rule_mac(struct fw3_ipt_rule *r, struct fw3_mac *mac)
689 {
690 if (!mac)
691 return;
692
693 fw3_ipt_rule_addarg(r, false, "-m", "mac");
694 fw3_ipt_rule_addarg(r, mac->invert, "--mac-source", ether_ntoa(&mac->mac));
695 }
696
697 void
698 fw3_ipt_rule_icmptype(struct fw3_ipt_rule *r, struct fw3_icmptype *icmp)
699 {
700 char buf[sizeof("255/255\0")];
701
702 if (!icmp)
703 return;
704
705 if (r->h->family == FW3_FAMILY_V6)
706 {
707 if (icmp->code6_min == 0 && icmp->code6_max == 0xFF)
708 sprintf(buf, "%u", icmp->type6);
709 else
710 sprintf(buf, "%u/%u", icmp->type6, icmp->code6_min);
711
712 fw3_ipt_rule_addarg(r, icmp->invert, "--icmpv6-type", buf);
713 }
714 else
715 {
716 if (icmp->code_min == 0 && icmp->code_max == 0xFF)
717 sprintf(buf, "%u", icmp->type);
718 else
719 sprintf(buf, "%u/%u", icmp->type, icmp->code_min);
720
721 fw3_ipt_rule_addarg(r, icmp->invert, "--icmp-type", buf);
722 }
723 }
724
725 void
726 fw3_ipt_rule_limit(struct fw3_ipt_rule *r, struct fw3_limit *limit)
727 {
728 char buf[sizeof("-4294967296/second\0")];
729
730 if (!limit || limit->rate <= 0)
731 return;
732
733 fw3_ipt_rule_addarg(r, false, "-m", "limit");
734
735 sprintf(buf, "%u/%s", limit->rate, fw3_limit_units[limit->unit]);
736 fw3_ipt_rule_addarg(r, limit->invert, "--limit", buf);
737
738 if (limit->burst > 0)
739 {
740 sprintf(buf, "%u", limit->burst);
741 fw3_ipt_rule_addarg(r, limit->invert, "--limit-burst", buf);
742 }
743 }
744
745 void
746 fw3_ipt_rule_ipset(struct fw3_ipt_rule *r, struct fw3_ipset *ipset,
747 bool invert)
748 {
749 char buf[sizeof("dst,dst,dst\0")];
750 char *p = buf;
751
752 struct fw3_ipset_datatype *type;
753
754 if (!ipset)
755 return;
756
757 list_for_each_entry(type, &ipset->datatypes, list)
758 {
759 if (p > buf)
760 *p++ = ',';
761
762 p += sprintf(p, "%s", type->dest ? "dst" : "src");
763 }
764
765 fw3_ipt_rule_addarg(r, false, "-m", "set");
766
767 fw3_ipt_rule_addarg(r, invert, "--match-set",
768 ipset->external ? ipset->external : ipset->name);
769
770 fw3_ipt_rule_addarg(r, false, buf, NULL);
771 }
772
773 void
774 fw3_ipt_rule_time(struct fw3_ipt_rule *r, struct fw3_time *time)
775 {
776 int i;
777 struct tm empty = { 0 };
778
779 char buf[84]; /* sizeof("1,2,3,...,30,31\0") */
780 char *p;
781
782 bool d1 = memcmp(&time->datestart, &empty, sizeof(empty));
783 bool d2 = memcmp(&time->datestop, &empty, sizeof(empty));
784
785 if (!d1 && !d2 && !time->timestart && !time->timestop &&
786 !(time->monthdays & 0xFFFFFFFE) && !(time->weekdays & 0xFE))
787 {
788 return;
789 }
790
791 fw3_ipt_rule_addarg(r, false, "-m", "time");
792
793 if (time->utc)
794 fw3_ipt_rule_addarg(r, false, "--utc", NULL);
795
796 if (d1)
797 {
798 strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &time->datestart);
799 fw3_ipt_rule_addarg(r, false, "--datestart", buf);
800 }
801
802 if (d2)
803 {
804 strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &time->datestop);
805 fw3_ipt_rule_addarg(r, false, "--datestop", buf);
806 }
807
808 if (time->timestart)
809 {
810 sprintf(buf, "%02d:%02d:%02d",
811 time->timestart / 3600,
812 time->timestart % 3600 / 60,
813 time->timestart % 60);
814
815 fw3_ipt_rule_addarg(r, false, "--timestart", buf);
816 }
817
818 if (time->timestop)
819 {
820 sprintf(buf, "%02d:%02d:%02d",
821 time->timestop / 3600,
822 time->timestop % 3600 / 60,
823 time->timestop % 60);
824
825 fw3_ipt_rule_addarg(r, false, "--timestop", buf);
826 }
827
828 if (time->monthdays & 0xFFFFFFFE)
829 {
830 for (i = 1, p = buf; i < 32; i++)
831 {
832 if (hasbit(time->monthdays, i))
833 {
834 if (p > buf)
835 *p++ = ',';
836
837 p += sprintf(p, "%u", i);
838 }
839 }
840
841 fw3_ipt_rule_addarg(r, hasbit(time->monthdays, 0), "--monthdays", buf);
842 }
843
844 if (time->weekdays & 0xFE)
845 {
846 for (i = 1, p = buf; i < 8; i++)
847 {
848 if (hasbit(time->weekdays, i))
849 {
850 if (p > buf)
851 *p++ = ',';
852
853 p += sprintf(p, "%u", i);
854 }
855 }
856
857 fw3_ipt_rule_addarg(r, hasbit(time->weekdays, 0), "--weekdays", buf);
858 }
859 }
860
861 void
862 fw3_ipt_rule_mark(struct fw3_ipt_rule *r, struct fw3_mark *mark)
863 {
864 char buf[sizeof("0xFFFFFFFF/0xFFFFFFFF\0")];
865
866 if (!mark || !mark->set)
867 return;
868
869 if (mark->mask < 0xFFFFFFFF)
870 sprintf(buf, "0x%x/0x%x", mark->mark, mark->mask);
871 else
872 sprintf(buf, "0x%x", mark->mark);
873
874 fw3_ipt_rule_addarg(r, false, "-m", "mark");
875 fw3_ipt_rule_addarg(r, mark->invert, "--mark", buf);
876 }
877
878 void
879 fw3_ipt_rule_comment(struct fw3_ipt_rule *r, const char *fmt, ...)
880 {
881 va_list ap;
882 char buf[256];
883
884 if (!fmt || !*fmt)
885 return;
886
887 va_start(ap, fmt);
888 vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
889 va_end(ap);
890
891 fw3_ipt_rule_addarg(r, false, "-m", "comment");
892 fw3_ipt_rule_addarg(r, false, "--comment", buf);
893 }
894
895 void
896 fw3_ipt_rule_extra(struct fw3_ipt_rule *r, const char *extra)
897 {
898 char *p, **tmp, *s;
899
900 if (!extra || !*extra)
901 return;
902
903 s = fw3_strdup(extra);
904
905 for (p = strtok(s, " \t"); p; p = strtok(NULL, " \t"))
906 {
907 tmp = realloc(r->argv, (r->argc + 1) * sizeof(*r->argv));
908
909 if (!tmp)
910 break;
911
912 r->argv = tmp;
913 r->argv[r->argc++] = fw3_strdup(p);
914 }
915
916 free(s);
917 }
918
919 static void
920 rule_print6(struct ip6t_entry *e)
921 {
922 char buf[INET6_ADDRSTRLEN];
923 char *pname;
924
925 if (e->ipv6.flags & IP6T_F_PROTO)
926 {
927 if (e->ipv6.flags & XT_INV_PROTO)
928 printf(" !");
929
930 pname = get_protoname(container_of(e, struct fw3_ipt_rule, e6));
931
932 if (pname)
933 printf(" -p %s", pname);
934 else
935 printf(" -p %u", e->ipv6.proto);
936 }
937
938 if (e->ipv6.iniface[0])
939 {
940 if (e->ipv6.flags & IP6T_INV_VIA_IN)
941 printf(" !");
942
943 printf(" -i %s", e->ipv6.iniface);
944 }
945
946 if (e->ipv6.outiface[0])
947 {
948 if (e->ipv6.flags & IP6T_INV_VIA_OUT)
949 printf(" !");
950
951 printf(" -o %s", e->ipv6.outiface);
952 }
953
954 if (memcmp(&e->ipv6.src, &in6addr_any, sizeof(struct in6_addr)))
955 {
956 if (e->ipv6.flags & IP6T_INV_SRCIP)
957 printf(" !");
958
959 printf(" -s %s/%u", inet_ntop(AF_INET6, &e->ipv6.src, buf, sizeof(buf)),
960 xtables_ip6mask_to_cidr(&e->ipv6.smsk));
961 }
962
963 if (memcmp(&e->ipv6.dst, &in6addr_any, sizeof(struct in6_addr)))
964 {
965 if (e->ipv6.flags & IP6T_INV_DSTIP)
966 printf(" !");
967
968 printf(" -d %s/%u", inet_ntop(AF_INET6, &e->ipv6.dst, buf, sizeof(buf)),
969 xtables_ip6mask_to_cidr(&e->ipv6.dmsk));
970 }
971 }
972
973 static void
974 rule_print4(struct ipt_entry *e)
975 {
976 struct in_addr in_zero = { 0 };
977 char buf[sizeof("255.255.255.255\0")];
978 char *pname;
979
980 if (e->ip.proto)
981 {
982 if (e->ip.flags & XT_INV_PROTO)
983 printf(" !");
984
985 pname = get_protoname(container_of(e, struct fw3_ipt_rule, e));
986
987 if (pname)
988 printf(" -p %s", pname);
989 else
990 printf(" -p %u", e->ip.proto);
991 }
992
993 if (e->ip.iniface[0])
994 {
995 if (e->ip.flags & IPT_INV_VIA_IN)
996 printf(" !");
997
998 printf(" -i %s", e->ip.iniface);
999 }
1000
1001 if (e->ip.outiface[0])
1002 {
1003 if (e->ip.flags & IPT_INV_VIA_OUT)
1004 printf(" !");
1005
1006 printf(" -o %s", e->ip.outiface);
1007 }
1008
1009 if (memcmp(&e->ip.src, &in_zero, sizeof(struct in_addr)))
1010 {
1011 if (e->ip.flags & IPT_INV_SRCIP)
1012 printf(" !");
1013
1014 printf(" -s %s/%u", inet_ntop(AF_INET, &e->ip.src, buf, sizeof(buf)),
1015 xtables_ipmask_to_cidr(&e->ip.smsk));
1016 }
1017
1018 if (memcmp(&e->ip.dst, &in_zero, sizeof(struct in_addr)))
1019 {
1020 if (e->ip.flags & IPT_INV_DSTIP)
1021 printf(" !");
1022
1023 printf(" -d %s/%u", inet_ntop(AF_INET, &e->ip.dst, buf, sizeof(buf)),
1024 xtables_ipmask_to_cidr(&e->ip.dmsk));
1025 }
1026 }
1027
1028 static void
1029 rule_print(struct fw3_ipt_rule *r, const char *chain)
1030 {
1031 struct xtables_rule_match *rm;
1032 struct xtables_match *m;
1033 struct xtables_target *t;
1034
1035 debug(r->h, "-A %s", chain);
1036
1037 if (r->h->family == FW3_FAMILY_V6)
1038 rule_print6(&r->e6);
1039 else
1040 rule_print4(&r->e);
1041
1042 for (rm = r->matches; rm; rm = rm->next)
1043 {
1044 m = rm->match;
1045 printf(" -m %s", m->alias ? m->alias(m->m) : m->m->u.user.name);
1046
1047 if (m->save)
1048 m->save(&r->e.ip, m->m);
1049 }
1050
1051 if (r->target)
1052 {
1053 t = r->target;
1054 printf(" -j %s", t->alias ? t->alias(t->t) : t->t->u.user.name);
1055
1056 if (t->save)
1057 t->save(&r->e.ip, t->t);
1058 }
1059
1060 printf("\n");
1061 }
1062
1063 static bool
1064 parse_option(struct fw3_ipt_rule *r, int optc, bool inv)
1065 {
1066 struct xtables_rule_match *m;
1067 struct xtables_match *em;
1068
1069 /* is a target option */
1070 if (r->target && (r->target->parse || r->target->x6_parse) &&
1071 optc >= r->target->option_offset &&
1072 optc < (r->target->option_offset + 256))
1073 {
1074 xtables_option_tpcall(optc, r->argv, inv, r->target, &r->e);
1075 return false;
1076 }
1077
1078 /* try to dispatch argument to one of the match parsers */
1079 for (m = r->matches; m; m = m->next)
1080 {
1081 em = m->match;
1082
1083 if (m->completed || (!em->parse && !em->x6_parse))
1084 continue;
1085
1086 if (optc < em->option_offset ||
1087 optc >= (em->option_offset + 256))
1088 continue;
1089
1090 xtables_option_mpcall(optc, r->argv, inv, em, &r->e);
1091 return false;
1092 }
1093
1094 /* unhandled option, might belong to a protocol match */
1095 if ((em = load_protomatch(r)) != NULL)
1096 {
1097 init_match(r, em, false);
1098
1099 r->protocol_loaded = true;
1100 optind--;
1101
1102 return true;
1103 }
1104
1105 if (optc == ':')
1106 fprintf(stderr, "parse_option(): option '%s' needs argument\n",
1107 r->argv[optind-1]);
1108
1109 if (optc == '?')
1110 fprintf(stderr, "parse_option(): unknown option '%s'\n",
1111 r->argv[optind-1]);
1112
1113 return false;
1114 }
1115
1116 void
1117 fw3_ipt_rule_addarg(struct fw3_ipt_rule *r, bool inv,
1118 const char *k, const char *v)
1119 {
1120 int n;
1121 char **tmp;
1122
1123 if (!k)
1124 return;
1125
1126 n = inv + !!k + !!v;
1127 tmp = realloc(r->argv, (r->argc + n) * sizeof(*tmp));
1128
1129 if (!tmp)
1130 return;
1131
1132 r->argv = tmp;
1133
1134 if (inv)
1135 r->argv[r->argc++] = fw3_strdup("!");
1136
1137 r->argv[r->argc++] = fw3_strdup(k);
1138
1139 if (v)
1140 r->argv[r->argc++] = fw3_strdup(v);
1141 }
1142
1143 void
1144 fw3_ipt_rule_append(struct fw3_ipt_rule *r, const char *fmt, ...)
1145 {
1146 size_t s;
1147 struct xtables_rule_match *m;
1148 struct xtables_match *em;
1149 struct xtables_target *et;
1150 struct xtables_globals *g;
1151 struct ipt_entry *e;
1152 struct ip6t_entry *e6;
1153
1154 int i, optc;
1155 bool inv = false;
1156 char buf[32];
1157 va_list ap;
1158
1159 va_start(ap, fmt);
1160 vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
1161 va_end(ap);
1162
1163 g = (r->h->family == FW3_FAMILY_V6) ? &xtg6 : &xtg;
1164 g->opts = g->orig_opts;
1165
1166 optind = 0;
1167 opterr = 0;
1168
1169 while ((optc = getopt_long(r->argc, r->argv, "m:j:", g->opts, NULL)) != -1)
1170 {
1171 switch (optc)
1172 {
1173 case 'm':
1174 em = find_match(r, optarg);
1175
1176 if (!em)
1177 {
1178 fprintf(stderr, "fw3_ipt_rule_append(): Can't find match '%s'\n", optarg);
1179 return;
1180 }
1181
1182 init_match(r, em, true);
1183 break;
1184
1185 case 'j':
1186 et = get_target(r, optarg);
1187
1188 if (!et)
1189 {
1190 fprintf(stderr, "fw3_ipt_rule_append(): Can't find target '%s'\n", optarg);
1191 return;
1192 }
1193
1194 break;
1195
1196 case 1:
1197 if ((optarg[0] == '!') && (optarg[1] == '\0'))
1198 {
1199 inv = true;
1200 continue;
1201 }
1202
1203 fprintf(stderr, "fw3_ipt_rule_append(): Bad argument '%s'\n", optarg);
1204 return;
1205
1206 default:
1207 if (parse_option(r, optc, inv))
1208 continue;
1209 break;
1210 }
1211
1212 inv = false;
1213 }
1214
1215 for (m = r->matches; m; m = m->next)
1216 xtables_option_mfcall(m->match);
1217
1218 if (r->target)
1219 xtables_option_tfcall(r->target);
1220
1221 if (fw3_pr_debug)
1222 rule_print(r, buf);
1223
1224 if (r->h->family == FW3_FAMILY_V6)
1225 {
1226 s = XT_ALIGN(sizeof(struct ip6t_entry));
1227
1228 for (m = r->matches; m; m = m->next)
1229 s += m->match->m->u.match_size;
1230
1231 e6 = fw3_alloc(s + r->target->t->u.target_size);
1232
1233 memcpy(e6, &r->e6, sizeof(struct ip6t_entry));
1234
1235 e6->target_offset = s;
1236 e6->next_offset = s + r->target->t->u.target_size;
1237
1238 s = 0;
1239
1240 for (m = r->matches; m; m = m->next)
1241 {
1242 memcpy(e6->elems + s, m->match->m, m->match->m->u.match_size);
1243 s += m->match->m->u.match_size;
1244 }
1245
1246 memcpy(e6->elems + s, r->target->t, r->target->t->u.target_size);
1247 ip6tc_append_entry(buf, e6, r->h->handle);
1248 free(e6);
1249 }
1250 else
1251 {
1252 s = XT_ALIGN(sizeof(struct ipt_entry));
1253
1254 for (m = r->matches; m; m = m->next)
1255 s += m->match->m->u.match_size;
1256
1257 e = fw3_alloc(s + r->target->t->u.target_size);
1258
1259 memcpy(e, &r->e, sizeof(struct ipt_entry));
1260
1261 e->target_offset = s;
1262 e->next_offset = s + r->target->t->u.target_size;
1263
1264 s = 0;
1265
1266 for (m = r->matches; m; m = m->next)
1267 {
1268 memcpy(e->elems + s, m->match->m, m->match->m->u.match_size);
1269 s += m->match->m->u.match_size;
1270 }
1271
1272 memcpy(e->elems + s, r->target->t, r->target->t->u.target_size);
1273
1274 if (!iptc_append_entry(buf, e, r->h->handle))
1275 fprintf(stderr, "iptc_append_entry(): %s\n", iptc_strerror(errno));
1276
1277 free(e);
1278 }
1279
1280 for (i = 1; i < r->argc; i++)
1281 free(r->argv[i]);
1282
1283 free(r->argv);
1284
1285 xtables_rule_matches_free(&r->matches);
1286
1287 free(r->target->t);
1288 free(r);
1289
1290 /* reset all targets and matches */
1291 for (em = xtables_matches; em; em = em->next)
1292 em->mflags = 0;
1293
1294 for (et = xtables_targets; et; et = et->next)
1295 {
1296 et->tflags = 0;
1297 et->used = 0;
1298 }
1299
1300 xtables_free_opts(1);
1301 }
1302
1303 struct fw3_ipt_rule *
1304 fw3_ipt_rule_create(struct fw3_ipt_handle *handle, struct fw3_protocol *proto,
1305 struct fw3_device *in, struct fw3_device *out,
1306 struct fw3_address *src, struct fw3_address *dest)
1307 {
1308 struct fw3_ipt_rule *r;
1309
1310 r = fw3_ipt_rule_new(handle);
1311
1312 fw3_ipt_rule_proto(r, proto);
1313 fw3_ipt_rule_in_out(r, in, out);
1314 fw3_ipt_rule_src_dest(r, src, dest);
1315
1316 return r;
1317 }