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