• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/firewall3/rules.c

  1 /*
  2  * firewall3 - 3rd OpenWrt UCI firewall implementation
  3  *
  4  *   Copyright (C) 2013-2018 Jo-Philipp Wich <jo@mein.io>
  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 "rules.h"
 20 
 21 
 22 const struct fw3_option fw3_rule_opts[] = {
 23         FW3_OPT("enabled",             bool,      rule,     enabled),
 24 
 25         FW3_OPT("name",                string,    rule,     name),
 26         FW3_OPT("family",              family,    rule,     family),
 27 
 28         FW3_OPT("src",                 device,    rule,     src),
 29         FW3_OPT("dest",                device,    rule,     dest),
 30 
 31         FW3_OPT("device",              string,    rule,     device),
 32         FW3_OPT("direction",           direction, rule,     direction_out),
 33 
 34         FW3_OPT("ipset",               setmatch,  rule,     ipset),
 35         FW3_OPT("helper",              cthelper,  rule,     helper),
 36         FW3_OPT("set_helper",          cthelper,  rule,     set_helper),
 37 
 38         FW3_LIST("proto",              protocol,  rule,     proto),
 39 
 40         FW3_LIST("src_ip",             network,   rule,     ip_src),
 41         FW3_LIST("src_mac",            mac,       rule,     mac_src),
 42         FW3_LIST("src_port",           port,      rule,     port_src),
 43 
 44         FW3_LIST("dest_ip",            network,   rule,     ip_dest),
 45         FW3_LIST("dest_port",          port,      rule,     port_dest),
 46 
 47         FW3_LIST("icmp_type",          icmptype,  rule,     icmp_type),
 48         FW3_OPT("extra",               string,    rule,     extra),
 49 
 50         FW3_OPT("limit",               limit,     rule,     limit),
 51         FW3_OPT("limit_burst",         int,       rule,     limit.burst),
 52 
 53         FW3_OPT("utc_time",            bool,      rule,     time.utc),
 54         FW3_OPT("start_date",          date,      rule,     time.datestart),
 55         FW3_OPT("stop_date",           date,      rule,     time.datestop),
 56         FW3_OPT("start_time",          time,      rule,     time.timestart),
 57         FW3_OPT("stop_time",           time,      rule,     time.timestop),
 58         FW3_OPT("weekdays",            weekdays,  rule,     time.weekdays),
 59         FW3_OPT("monthdays",           monthdays, rule,     time.monthdays),
 60 
 61         FW3_OPT("mark",                mark,      rule,     mark),
 62         FW3_OPT("set_mark",            mark,      rule,     set_mark),
 63         FW3_OPT("set_xmark",           mark,      rule,     set_xmark),
 64 
 65         FW3_OPT("dscp",                dscp,      rule,     dscp),
 66         FW3_OPT("set_dscp",            dscp,      rule,     set_dscp),
 67 
 68         FW3_OPT("target",              target,    rule,     target),
 69 
 70         { }
 71 };
 72 
 73 
 74 static bool
 75 need_src_action_chain(struct fw3_rule *r)
 76 {
 77         return (r->_src && r->_src->log && (r->target > FW3_FLAG_ACCEPT));
 78 }
 79 
 80 static struct fw3_rule*
 81 alloc_rule(struct fw3_state *state)
 82 {
 83         struct fw3_rule *rule = calloc(1, sizeof(*rule));
 84 
 85         if (rule) {
 86                 INIT_LIST_HEAD(&rule->proto);
 87 
 88                 INIT_LIST_HEAD(&rule->ip_src);
 89                 INIT_LIST_HEAD(&rule->mac_src);
 90                 INIT_LIST_HEAD(&rule->port_src);
 91 
 92                 INIT_LIST_HEAD(&rule->ip_dest);
 93                 INIT_LIST_HEAD(&rule->port_dest);
 94 
 95                 INIT_LIST_HEAD(&rule->icmp_type);
 96 
 97                 list_add_tail(&rule->list, &state->rules);
 98                 rule->enabled = true;
 99         }
100 
101         return rule;
102 }
103 
104 static bool
105 check_rule(struct fw3_state *state, struct fw3_rule *r, struct uci_element *e)
106 {
107         if (!r->enabled)
108                 return false;
109 
110         if (r->src.invert || r->dest.invert)
111         {
112                 warn_section("rule", r, e, "must not have inverted 'src' or 'dest' options");
113                 return false;
114         }
115         else if (r->src.set && !r->src.any &&
116                  !(r->_src = fw3_lookup_zone(state, r->src.name)))
117         {
118                 warn_section("rule", r, e, "refers to not existing zone '%s'", r->src.name);
119                 return false;
120         }
121         else if (r->dest.set && !r->dest.any &&
122                  !(r->_dest = fw3_lookup_zone(state, r->dest.name)))
123         {
124                 warn_section("rule", r, e, "refers to not existing zone '%s'", r->dest.name);
125                 return false;
126         }
127         else if (r->ipset.set && state->disable_ipsets)
128         {
129                 warn_section("rule", r, e, "skipped due to disabled ipset support");
130                 return false;
131         }
132         else if (r->ipset.set &&
133                  !(r->ipset.ptr = fw3_lookup_ipset(state, r->ipset.name)))
134         {
135                 warn_section("rule", r, e, "refers to unknown ipset '%s'", r->ipset.name);
136                 return false;
137         }
138         else if (r->helper.set &&
139                  !(r->helper.ptr = fw3_lookup_cthelper(state, r->helper.name)))
140         {
141                 warn_section("rule", r, e, "refers to unknown CT helper '%s'", r->helper.name);
142                 return false;
143         }
144         else if (r->set_helper.set &&
145                  !(r->set_helper.ptr = fw3_lookup_cthelper(state, r->set_helper.name)))
146         {
147                 warn_section("rule", r, e, "refers to unknown CT helper '%s'", r->set_helper.name);
148                 return false;
149         }
150 
151         if (!r->_src && (r->target == FW3_FLAG_NOTRACK || r->target == FW3_FLAG_HELPER))
152         {
153                 warn_section("rule", r, e, "is set to target %s but has no source assigned",
154                              fw3_flag_names[r->target]);
155                 return false;
156         }
157 
158         if (!r->set_mark.set && !r->set_xmark.set &&
159             r->target == FW3_FLAG_MARK)
160         {
161                 warn_section("rule", r, e, "is set to target MARK but specifies neither "
162                                            "'set_mark' nor 'set_xmark' option");
163                 return false;
164         }
165 
166         if (!r->set_dscp.set && r->target == FW3_FLAG_DSCP)
167         {
168                 warn_section("rule", r, e, "is set to target DSCP but specifies no "
169                                            "'set_dscp' option");
170                 return false;
171         }
172 
173         if (r->set_mark.invert || r->set_xmark.invert || r->set_dscp.invert)
174         {
175                 warn_section("rule", r, e, "must not have inverted 'set_mark', "
176                                            "'set_xmark' or 'set_dscp'");
177                 return false;
178         }
179 
180         if (!r->set_helper.set && r->target == FW3_FLAG_HELPER)
181         {
182                 warn_section("rule", r, e, "is set to target HELPER but specifies "
183                                            "no 'set_helper' option");
184                 return false;
185         }
186 
187         if (r->set_helper.invert && r->target == FW3_FLAG_HELPER)
188         {
189                 warn_section("rule", r, e, "must not have inverted 'set_helper' option");
190                 return false;
191         }
192 
193         if (!r->_src && !r->_dest && !r->src.any && !r->dest.any)
194         {
195                 warn_section("rule", r, e, "has neither a source nor a destination zone assigned "
196                                 "- assuming an output rule");
197         }
198 
199         if (list_empty(&r->proto))
200         {
201                 warn_section("rule", r, e, "does not specify a protocol, assuming TCP+UDP");
202                 fw3_parse_protocol(&r->proto, "tcpudp", true);
203         }
204 
205         if (r->target == FW3_FLAG_UNSPEC)
206         {
207                 warn_section("rule", r, e, "has no target specified, defaulting to REJECT");
208                 r->target = FW3_FLAG_REJECT;
209         }
210         else if (r->target > FW3_FLAG_DSCP)
211         {
212                 warn_section("rule", r, e, "has invalid target specified, defaulting to REJECT");
213                 r->target = FW3_FLAG_REJECT;
214         }
215 
216         /* NB: r family... */
217         if (r->_dest)
218         {
219                 fw3_setbit(r->_dest->flags[0], r->target);
220                 fw3_setbit(r->_dest->flags[1], r->target);
221         }
222         else if (need_src_action_chain(r))
223         {
224                 fw3_setbit(r->_src->flags[0], fw3_to_src_target(r->target));
225                 fw3_setbit(r->_src->flags[1], fw3_to_src_target(r->target));
226         }
227 
228         return true;
229 }
230 
231 void
232 fw3_load_rules(struct fw3_state *state, struct uci_package *p,
233                 struct blob_attr *a)
234 {
235         struct uci_section *s;
236         struct uci_element *e;
237         struct fw3_rule *rule;
238         struct blob_attr *entry;
239         unsigned rem;
240 
241         INIT_LIST_HEAD(&state->rules);
242 
243         blob_for_each_attr(entry, a, rem) {
244                 const char *type;
245                 const char *name = "ubus rule";
246 
247                 if (!fw3_attr_parse_name_type(entry, &name, &type))
248                         continue;
249 
250                 if (strcmp(type, "rule"))
251                         continue;
252 
253                 if (!(rule = alloc_rule(state)))
254                         continue;
255 
256                 if (!fw3_parse_blob_options(rule, fw3_rule_opts, entry, name))
257                 {
258                         warn_section("rule", rule, NULL, "skipped due to invalid options");
259                         fw3_free_rule(rule);
260                         continue;
261                 }
262 
263                 if (!check_rule(state, rule, NULL))
264                         fw3_free_rule(rule);
265         }
266 
267         uci_foreach_element(&p->sections, e)
268         {
269                 s = uci_to_section(e);
270 
271                 if (strcmp(s->type, "rule"))
272                         continue;
273 
274                 if (!(rule = alloc_rule(state)))
275                         continue;
276 
277                 if (!fw3_parse_options(rule, fw3_rule_opts, s))
278                 {
279                         warn_elem(e, "skipped due to invalid options");
280                         fw3_free_rule(rule);
281                         continue;
282                 }
283 
284                 if (!check_rule(state, rule, e))
285                         fw3_free_rule(rule);
286         }
287 }
288 
289 
290 static void
291 append_chain(struct fw3_ipt_rule *r, struct fw3_rule *rule)
292 {
293         char chain[32];
294 
295         snprintf(chain, sizeof(chain), "OUTPUT");
296 
297         if (rule->target == FW3_FLAG_NOTRACK)
298         {
299                 snprintf(chain, sizeof(chain), "zone_%s_notrack", rule->src.name);
300         }
301         else if (rule->target == FW3_FLAG_HELPER)
302         {
303                 snprintf(chain, sizeof(chain), "zone_%s_helper", rule->src.name);
304         }
305         else if (rule->target == FW3_FLAG_MARK || rule->target == FW3_FLAG_DSCP)
306         {
307                 if ((rule->_dest && rule->_src) ||
308                     (rule->dest.any && rule->src.any))
309                         snprintf(chain, sizeof(chain), "FORWARD");
310                 else if (rule->src.any && rule->_dest)
311                         snprintf(chain, sizeof(chain), "POSTROUTING");
312                 else if (rule->dest.any && rule->_src)
313                         snprintf(chain, sizeof(chain), "PREROUTING");
314                 else if (!rule->dest.set && rule->src.set)
315                         snprintf(chain, sizeof(chain), "INPUT");
316                 else /* if (!rule->src.set) */
317                         snprintf(chain, sizeof(chain), "OUTPUT");
318         }
319         else
320         {
321                 if (rule->src.set)
322                 {
323                         if (!rule->src.any)
324                         {
325                                 if (rule->dest.set)
326                                         snprintf(chain, sizeof(chain), "zone_%s_forward",
327                                                  rule->src.name);
328                                 else
329                                         snprintf(chain, sizeof(chain), "zone_%s_input",
330                                                  rule->src.name);
331                         }
332                         else
333                         {
334                                 if (rule->dest.set)
335                                         snprintf(chain, sizeof(chain), "FORWARD");
336                                 else
337                                         snprintf(chain, sizeof(chain), "INPUT");
338                         }
339                 }
340 
341                 if (rule->dest.set && !rule->src.set)
342                 {
343                         if (rule->dest.any)
344                                 snprintf(chain, sizeof(chain), "OUTPUT");
345                         else
346                                 snprintf(chain, sizeof(chain), "zone_%s_output",
347                                          rule->dest.name);
348                 }
349         }
350 
351         fw3_ipt_rule_append(r, chain);
352 }
353 
354 static void set_target(struct fw3_ipt_rule *r, struct fw3_rule *rule)
355 {
356         const char *name;
357         struct fw3_mark *mark;
358         char buf[sizeof("0xFFFFFFFF/0xFFFFFFFF")];
359 
360         switch(rule->target)
361         {
362         case FW3_FLAG_MARK:
363                 name = rule->set_mark.set ? "--set-mark" : "--set-xmark";
364                 mark = rule->set_mark.set ? &rule->set_mark : &rule->set_xmark;
365                 snprintf(buf, sizeof(buf), "0x%x/0x%x", mark->mark, mark->mask);
366 
367                 fw3_ipt_rule_target(r, "MARK");
368                 fw3_ipt_rule_addarg(r, false, name, buf);
369                 return;
370 
371         case FW3_FLAG_DSCP:
372                 snprintf(buf, sizeof(buf), "0x%x", rule->set_dscp.dscp);
373 
374                 fw3_ipt_rule_target(r, "DSCP");
375                 fw3_ipt_rule_addarg(r, false, "--set-dscp", buf);
376                 return;
377 
378         case FW3_FLAG_NOTRACK:
379                 fw3_ipt_rule_target(r, "CT");
380                 fw3_ipt_rule_addarg(r, false, "--notrack", NULL);
381                 return;
382 
383         case FW3_FLAG_HELPER:
384                 fw3_ipt_rule_target(r, "CT");
385                 fw3_ipt_rule_addarg(r, false, "--helper", rule->set_helper.ptr->name);
386                 return;
387 
388         case FW3_FLAG_ACCEPT:
389         case FW3_FLAG_DROP:
390                 name = fw3_flag_names[rule->target];
391                 break;
392 
393         default:
394                 name = fw3_flag_names[FW3_FLAG_REJECT];
395                 break;
396         }
397 
398         if (rule->dest.set && !rule->dest.any)
399                 fw3_ipt_rule_target(r, "zone_%s_dest_%s", rule->dest.name, name);
400         else if (need_src_action_chain(rule))
401                 fw3_ipt_rule_target(r, "zone_%s_src_%s", rule->src.name, name);
402         else if (strcmp(name, "REJECT"))
403                 fw3_ipt_rule_target(r, name);
404         else
405                 fw3_ipt_rule_target(r, "reject");
406 }
407 
408 static void
409 set_comment(struct fw3_ipt_rule *r, const char *name, int num)
410 {
411         if (name)
412                 fw3_ipt_rule_comment(r, name);
413         else
414                 fw3_ipt_rule_comment(r, "@rule[%u]", num);
415 }
416 
417 static void
418 print_rule(struct fw3_ipt_handle *handle, struct fw3_state *state,
419            struct fw3_rule *rule, int num, struct fw3_protocol *proto,
420            struct fw3_address *sip, struct fw3_address *dip,
421            struct fw3_port *sport, struct fw3_port *dport,
422            struct fw3_mac *mac, struct fw3_icmptype *icmptype)
423 {
424         struct fw3_ipt_rule *r;
425         struct fw3_device *idev, *odev;
426         struct list_head empty, *idevices, *odevices;
427         INIT_LIST_HEAD(&empty);
428         idevices = odevices = &empty;
429 
430         if (!fw3_is_family(sip, handle->family) ||
431             !fw3_is_family(dip, handle->family))
432         {
433                 if ((sip && !sip->resolved) || (dip && !dip->resolved))
434                         info("     ! Skipping due to different family of ip address");
435 
436                 return;
437         }
438 
439         if (!fw3_is_family(sip, handle->family) ||
440             !fw3_is_family(dip, handle->family))
441         {
442                 if ((sip && !sip->resolved) || (dip && !dip->resolved))
443                         info("     ! Skipping due to different family of ip address");
444 
445                 return;
446         }
447 
448         if (!fw3_is_family(sip, handle->family) ||
449             !fw3_is_family(dip, handle->family))
450         {
451                 if ((sip && !sip->resolved) || (dip && !dip->resolved))
452                         info("     ! Skipping due to different family of ip address");
453 
454                 return;
455         }
456 
457         if (proto->protocol == 58 && handle->family == FW3_FAMILY_V4)
458         {
459                 info("     ! Skipping protocol %s due to different family",
460                      fw3_protoname(proto));
461                 return;
462         }
463 
464         if (rule->helper.ptr &&
465             !fw3_cthelper_check_proto(rule->helper.ptr, proto))
466         {
467                 info("     ! Skipping protocol %s since helper '%s' does not support it",
468                      fw3_protoname(proto), rule->helper.ptr->name);
469                 return;
470         }
471 
472         if (rule->set_helper.ptr &&
473             !fw3_cthelper_check_proto(rule->set_helper.ptr, proto))
474         {
475                 info("     ! Skipping protocol %s since helper '%s' does not support it",
476                      fw3_protoname(proto), rule->helper.ptr->name);
477                 return;
478         }
479 
480         if (rule->target == FW3_FLAG_DSCP || rule->target == FW3_FLAG_MARK)
481         {
482                 if (rule->_src)
483                         idevices = &rule->_src->devices;
484                 if (rule->_dest)
485                         odevices = &rule->_dest->devices;
486         }
487 
488         fw3_foreach(idev, idevices)
489         fw3_foreach(odev, odevices)
490         {
491                 r = fw3_ipt_rule_create(handle, proto, idev, odev, sip, dip);
492                 fw3_ipt_rule_sport_dport(r, sport, dport);
493                 fw3_ipt_rule_device(r, rule->device, rule->direction_out);
494                 fw3_ipt_rule_icmptype(r, icmptype);
495                 fw3_ipt_rule_mac(r, mac);
496                 fw3_ipt_rule_ipset(r, &rule->ipset);
497                 fw3_ipt_rule_helper(r, &rule->helper);
498                 fw3_ipt_rule_limit(r, &rule->limit);
499                 fw3_ipt_rule_time(r, &rule->time);
500                 fw3_ipt_rule_mark(r, &rule->mark);
501                 fw3_ipt_rule_dscp(r, &rule->dscp);
502                 set_target(r, rule);
503                 fw3_ipt_rule_extra(r, rule->extra);
504                 set_comment(r, rule->name, num);
505                 append_chain(r, rule);
506         }
507 }
508 
509 static void
510 expand_rule(struct fw3_ipt_handle *handle, struct fw3_state *state,
511             struct fw3_rule *rule, int num)
512 {
513         struct fw3_protocol *proto;
514         struct fw3_address *sip;
515         struct fw3_address *dip;
516         struct fw3_port *sport;
517         struct fw3_port *dport;
518         struct fw3_mac *mac;
519         struct fw3_icmptype *icmptype;
520 
521         struct list_head *sports = NULL;
522         struct list_head *dports = NULL;
523         struct list_head *icmptypes = NULL;
524 
525         struct list_head empty;
526         INIT_LIST_HEAD(&empty);
527 
528         if (!fw3_is_family(rule, handle->family))
529                 return;
530 
531         if ((rule->target == FW3_FLAG_NOTRACK && handle->table != FW3_TABLE_RAW) ||
532             (rule->target == FW3_FLAG_HELPER && handle->table != FW3_TABLE_RAW)  ||
533             (rule->target == FW3_FLAG_MARK && handle->table != FW3_TABLE_MANGLE) ||
534             (rule->target == FW3_FLAG_DSCP && handle->table != FW3_TABLE_MANGLE) ||
535                 (rule->target < FW3_FLAG_NOTRACK && handle->table != FW3_TABLE_FILTER))
536                 return;
537 
538         if (rule->name)
539                 info("   * Rule '%s'", rule->name);
540         else
541                 info("   * Rule #%u", num);
542 
543         if (!fw3_is_family(rule->_src, handle->family) ||
544             !fw3_is_family(rule->_dest, handle->family))
545         {
546                 info("     ! Skipping due to different family of zone");
547                 return;
548         }
549 
550         if (rule->ipset.ptr)
551         {
552                 if (!fw3_is_family(rule->ipset.ptr, handle->family))
553                 {
554                         info("     ! Skipping due to different family in ipset");
555                         return;
556                 }
557 
558                 if (!fw3_check_ipset(rule->ipset.ptr))
559                 {
560                         info("     ! Skipping due to missing ipset '%s'",
561                              rule->ipset.ptr->external
562                                         ? rule->ipset.ptr->external : rule->ipset.ptr->name);
563                         return;
564                 }
565 
566                 set(rule->ipset.ptr->flags, handle->family, handle->family);
567         }
568 
569         if (rule->helper.ptr && !fw3_is_family(rule->helper.ptr, handle->family))
570         {
571                 info("     ! Skipping due to unsupported family of CT helper");
572                 return;
573         }
574 
575         if (rule->set_helper.ptr && !fw3_is_family(rule->set_helper.ptr, handle->family))
576         {
577                 info("     ! Skipping due to unsupported family of CT helper");
578                 return;
579         }
580 
581         list_for_each_entry(proto, &rule->proto, list)
582         {
583                 /* icmp / ipv6-icmp */
584                 if (proto->protocol == 1 || proto->protocol == 58)
585                 {
586                         sports = &empty;
587                         dports = &empty;
588                         icmptypes = &rule->icmp_type;
589                 }
590                 else
591                 {
592                         sports = &rule->port_src;
593                         dports = &rule->port_dest;
594                         icmptypes = &empty;
595                 }
596 
597                 fw3_foreach(sip, &rule->ip_src)
598                 fw3_foreach(dip, &rule->ip_dest)
599                 fw3_foreach(sport, sports)
600                 fw3_foreach(dport, dports)
601                 fw3_foreach(mac, &rule->mac_src)
602                 fw3_foreach(icmptype, icmptypes)
603                         print_rule(handle, state, rule, num, proto, sip, dip,
604                                    sport, dport, mac, icmptype);
605         }
606 }
607 
608 void
609 fw3_print_rules(struct fw3_ipt_handle *handle, struct fw3_state *state)
610 {
611         int num = 0;
612         struct fw3_rule *rule;
613 
614         list_for_each_entry(rule, &state->rules, list)
615                 expand_rule(handle, state, rule, num++);
616 }
617 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt