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