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