firewall3: check the return value of fw3_parse_options()
[project/firewall3.git] / rules.c
1 /*
2  * firewall3 - 3rd OpenWrt UCI firewall implementation
3  *
4  *   Copyright (C) 2013 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
36         FW3_LIST("proto",              protocol,  rule,     proto),
37
38         FW3_LIST("src_ip",             network,   rule,     ip_src),
39         FW3_LIST("src_mac",            mac,       rule,     mac_src),
40         FW3_LIST("src_port",           port,      rule,     port_src),
41
42         FW3_LIST("dest_ip",            network,   rule,     ip_dest),
43         FW3_LIST("dest_port",          port,      rule,     port_dest),
44
45         FW3_LIST("icmp_type",          icmptype,  rule,     icmp_type),
46         FW3_OPT("extra",               string,    rule,     extra),
47
48         FW3_OPT("limit",               limit,     rule,     limit),
49         FW3_OPT("limit_burst",         int,       rule,     limit.burst),
50
51         FW3_OPT("utc_time",            bool,      rule,     time.utc),
52         FW3_OPT("start_date",          date,      rule,     time.datestart),
53         FW3_OPT("stop_date",           date,      rule,     time.datestop),
54         FW3_OPT("start_time",          time,      rule,     time.timestart),
55         FW3_OPT("stop_time",           time,      rule,     time.timestop),
56         FW3_OPT("weekdays",            weekdays,  rule,     time.weekdays),
57         FW3_OPT("monthdays",           monthdays, rule,     time.monthdays),
58
59         FW3_OPT("mark",                mark,      rule,     mark),
60         FW3_OPT("set_mark",            mark,      rule,     set_mark),
61         FW3_OPT("set_xmark",           mark,      rule,     set_xmark),
62
63         FW3_OPT("target",              target,    rule,     target),
64
65         { }
66 };
67
68
69 static bool
70 need_src_action_chain(struct fw3_rule *r)
71 {
72         return (r->_src && r->_src->log && (r->target > FW3_FLAG_ACCEPT));
73 }
74
75 static struct fw3_rule*
76 alloc_rule(struct fw3_state *state)
77 {
78         struct fw3_rule *rule = calloc(1, sizeof(*rule));
79
80         if (rule) {
81                 INIT_LIST_HEAD(&rule->proto);
82
83                 INIT_LIST_HEAD(&rule->ip_src);
84                 INIT_LIST_HEAD(&rule->mac_src);
85                 INIT_LIST_HEAD(&rule->port_src);
86
87                 INIT_LIST_HEAD(&rule->ip_dest);
88                 INIT_LIST_HEAD(&rule->port_dest);
89
90                 INIT_LIST_HEAD(&rule->icmp_type);
91
92                 list_add_tail(&rule->list, &state->rules);
93                 rule->enabled = true;
94         }
95
96         return rule;
97 }
98
99 #define warn_rule(r, e, fmt, ...)                                                                       \
100         do {                                                                                                                    \
101                 if (e)                                                                                                          \
102                         warn_elem(e, fmt, ##__VA_ARGS__);                                               \
103                 else                                                                                                            \
104                         warn("Warning: ubus rule (%s) " fmt,                                    \
105                              (r && r->name) ? r->name : "?", ##__VA_ARGS__);    \
106         } while(0)
107
108 static bool
109 check_rule(struct fw3_state *state, struct fw3_rule *r, struct uci_element *e)
110 {
111         if (!r->enabled)
112                 return false;
113
114         if (r->src.invert || r->dest.invert)
115         {
116                 warn_rule(r, e, "must not have inverted 'src' or 'dest' options");
117                 return false;
118         }
119         else if (r->src.set && !r->src.any &&
120                  !(r->_src = fw3_lookup_zone(state, r->src.name)))
121         {
122                 warn_rule(r, e, "refers to not existing zone '%s'", r->src.name);
123                 return false;
124         }
125         else if (r->dest.set && !r->dest.any &&
126                  !(r->_dest = fw3_lookup_zone(state, r->dest.name)))
127         {
128                 warn_rule(r, e, "refers to not existing zone '%s'", r->dest.name);
129                 return false;
130         }
131         else if (r->ipset.set && state->disable_ipsets)
132         {
133                 warn_rule(r, e, "skipped due to disabled ipset support");
134                 return false;
135         }
136         else if (r->ipset.set &&
137                  !(r->ipset.ptr = fw3_lookup_ipset(state, r->ipset.name)))
138         {
139                 warn_rule(r, e, "refers to unknown ipset '%s'", r->ipset.name);
140                 return false;
141         }
142
143         if (!r->_src && r->target == FW3_FLAG_NOTRACK)
144         {
145                 warn_rule(r, e, "is set to target NOTRACK but has no source assigned");
146                 return false;
147         }
148
149         if (!r->set_mark.set && !r->set_xmark.set &&
150             r->target == FW3_FLAG_MARK)
151         {
152                 warn_rule(r, e, "is set to target MARK but specifies neither "
153                                 "'set_mark' nor 'set_xmark' option");
154                 return false;
155         }
156
157         if (r->_dest && r->target == FW3_FLAG_MARK)
158         {
159                 warn_rule(r, e, "must not specify 'dest' for MARK target");
160                 return false;
161         }
162
163         if (r->set_mark.invert || r->set_xmark.invert)
164         {
165                 warn_rule(r, e, "must not have inverted 'set_mark' or 'set_xmark'");
166                 return false;
167         }
168
169         if (!r->_src && !r->_dest && !r->src.any && !r->dest.any)
170         {
171                 warn_rule(r, e, "has neither a source nor a destination zone assigned "
172                                 "- assuming an output r");
173         }
174
175         if (list_empty(&r->proto))
176         {
177                 warn_rule(r, e, "does not specify a protocol, assuming TCP+UDP");
178                 fw3_parse_protocol(&r->proto, "tcpudp", true);
179         }
180
181         if (r->target == FW3_FLAG_UNSPEC)
182         {
183                 warn_rule(r, e, "has no target specified, defaulting to REJECT");
184                 r->target = FW3_FLAG_REJECT;
185         }
186         else if (r->target > FW3_FLAG_MARK)
187         {
188                 warn_rule(r, e, "has invalid target specified, defaulting to REJECT");
189                 r->target = FW3_FLAG_REJECT;
190         }
191
192         /* NB: r family... */
193         if (r->_dest)
194         {
195                 fw3_setbit(r->_dest->flags[0], r->target);
196                 fw3_setbit(r->_dest->flags[1], r->target);
197         }
198         else if (need_src_action_chain(r))
199         {
200                 fw3_setbit(r->_src->flags[0], fw3_to_src_target(r->target));
201                 fw3_setbit(r->_src->flags[1], fw3_to_src_target(r->target));
202         }
203
204         return true;
205 }
206
207 void
208 fw3_load_rules(struct fw3_state *state, struct uci_package *p,
209                 struct blob_attr *a)
210 {
211         struct uci_section *s;
212         struct uci_element *e;
213         struct fw3_rule *rule;
214         struct blob_attr *entry, *opt;
215         unsigned rem, orem;
216
217         INIT_LIST_HEAD(&state->rules);
218
219         blob_for_each_attr(entry, a, rem) {
220                 const char *type = NULL;
221                 const char *name = "ubus rule";
222                 blobmsg_for_each_attr(opt, entry, orem)
223                         if (!strcmp(blobmsg_name(opt), "type"))
224                                 type = blobmsg_get_string(opt);
225                         else if (!strcmp(blobmsg_name(opt), "name"))
226                                 name = blobmsg_get_string(opt);
227
228                 if (!type || strcmp(type, "rule"))
229                         continue;
230
231                 if (!(rule = alloc_rule(state)))
232                         continue;
233
234                 if (!fw3_parse_blob_options(rule, fw3_rule_opts, entry, name))
235                 {
236                         warn_rule(rule, NULL, "skipped due to invalid options\n");
237                         fw3_free_rule(rule);
238                         continue;
239                 }
240
241                 if (!check_rule(state, rule, NULL))
242                         fw3_free_rule(rule);
243         }
244
245         uci_foreach_element(&p->sections, e)
246         {
247                 s = uci_to_section(e);
248
249                 if (strcmp(s->type, "rule"))
250                         continue;
251
252                 if (!(rule = alloc_rule(state)))
253                         continue;
254
255                 if (!fw3_parse_options(rule, fw3_rule_opts, s))
256                 {
257                         warn_elem(e, "skipped due to invalid options");
258                         fw3_free_rule(rule);
259                         continue;
260                 }
261
262                 if (!check_rule(state, rule, e))
263                         fw3_free_rule(rule);
264         }
265 }
266
267
268 static void
269 append_chain(struct fw3_ipt_rule *r, struct fw3_rule *rule)
270 {
271         char chain[32];
272
273         snprintf(chain, sizeof(chain), "OUTPUT");
274
275         if (rule->target == FW3_FLAG_NOTRACK)
276         {
277                 snprintf(chain, sizeof(chain), "zone_%s_notrack", rule->src.name);
278         }
279         else if (rule->target == FW3_FLAG_MARK && (rule->_src || rule->src.any))
280         {
281                 snprintf(chain, sizeof(chain), "PREROUTING");
282         }
283         else
284         {
285                 if (rule->src.set)
286                 {
287                         if (!rule->src.any)
288                         {
289                                 if (rule->dest.set)
290                                         snprintf(chain, sizeof(chain), "zone_%s_forward",
291                                                  rule->src.name);
292                                 else
293                                         snprintf(chain, sizeof(chain), "zone_%s_input",
294                                                  rule->src.name);
295                         }
296                         else
297                         {
298                                 if (rule->dest.set)
299                                         snprintf(chain, sizeof(chain), "FORWARD");
300                                 else
301                                         snprintf(chain, sizeof(chain), "INPUT");
302                         }
303                 }
304
305                 if (rule->dest.set && !rule->src.set)
306                 {
307                         if (rule->dest.any)
308                                 snprintf(chain, sizeof(chain), "OUTPUT");
309                         else
310                                 snprintf(chain, sizeof(chain), "zone_%s_output",
311                                          rule->dest.name);
312                 }
313         }
314
315         fw3_ipt_rule_append(r, chain);
316 }
317
318 static void set_target(struct fw3_ipt_rule *r, struct fw3_rule *rule)
319 {
320         const char *name;
321         struct fw3_mark *mark;
322         char buf[sizeof("0xFFFFFFFF/0xFFFFFFFF\0")];
323
324         switch(rule->target)
325         {
326         case FW3_FLAG_MARK:
327                 name = rule->set_mark.set ? "--set-mark" : "--set-xmark";
328                 mark = rule->set_mark.set ? &rule->set_mark : &rule->set_xmark;
329                 sprintf(buf, "0x%x/0x%x", mark->mark, mark->mask);
330
331                 fw3_ipt_rule_target(r, "MARK");
332                 fw3_ipt_rule_addarg(r, false, name, buf);
333                 return;
334
335         case FW3_FLAG_NOTRACK:
336                 fw3_ipt_rule_target(r, "CT");
337                 fw3_ipt_rule_addarg(r, false, "--notrack", NULL);
338                 return;
339
340         case FW3_FLAG_ACCEPT:
341         case FW3_FLAG_DROP:
342                 name = fw3_flag_names[rule->target];
343                 break;
344
345         default:
346                 name = fw3_flag_names[FW3_FLAG_REJECT];
347                 break;
348         }
349
350         if (rule->dest.set && !rule->dest.any)
351                 fw3_ipt_rule_target(r, "zone_%s_dest_%s", rule->dest.name, name);
352         else if (need_src_action_chain(rule))
353                 fw3_ipt_rule_target(r, "zone_%s_src_%s", rule->src.name, name);
354         else if (strcmp(name, "REJECT"))
355                 fw3_ipt_rule_target(r, name);
356         else
357                 fw3_ipt_rule_target(r, "reject");
358 }
359
360 static void
361 set_comment(struct fw3_ipt_rule *r, const char *name, int num)
362 {
363         if (name)
364                 fw3_ipt_rule_comment(r, name);
365         else
366                 fw3_ipt_rule_comment(r, "@rule[%u]", num);
367 }
368
369 static void
370 print_rule(struct fw3_ipt_handle *handle, struct fw3_state *state,
371            struct fw3_rule *rule, int num, struct fw3_protocol *proto,
372            struct fw3_address *sip, struct fw3_address *dip,
373            struct fw3_port *sport, struct fw3_port *dport,
374            struct fw3_mac *mac, struct fw3_icmptype *icmptype)
375 {
376         struct fw3_ipt_rule *r;
377
378         if (!fw3_is_family(sip, handle->family) ||
379             !fw3_is_family(dip, handle->family))
380         {
381                 if ((sip && !sip->resolved) || (dip && !dip->resolved))
382                         info("     ! Skipping due to different family of ip address");
383
384                 return;
385         }
386
387         if (proto->protocol == 58 && handle->family == FW3_FAMILY_V4)
388         {
389                 info("     ! Skipping due to different family of protocol");
390                 return;
391         }
392
393         r = fw3_ipt_rule_create(handle, proto, NULL, NULL, sip, dip);
394         fw3_ipt_rule_sport_dport(r, sport, dport);
395         fw3_ipt_rule_device(r, rule->device, rule->direction_out);
396         fw3_ipt_rule_icmptype(r, icmptype);
397         fw3_ipt_rule_mac(r, mac);
398         fw3_ipt_rule_ipset(r, &rule->ipset);
399         fw3_ipt_rule_limit(r, &rule->limit);
400         fw3_ipt_rule_time(r, &rule->time);
401         fw3_ipt_rule_mark(r, &rule->mark);
402         set_target(r, rule);
403         fw3_ipt_rule_extra(r, rule->extra);
404         set_comment(r, rule->name, num);
405         append_chain(r, rule);
406 }
407
408 static void
409 expand_rule(struct fw3_ipt_handle *handle, struct fw3_state *state,
410             struct fw3_rule *rule, int num)
411 {
412         struct fw3_protocol *proto;
413         struct fw3_address *sip;
414         struct fw3_address *dip;
415         struct fw3_port *sport;
416         struct fw3_port *dport;
417         struct fw3_mac *mac;
418         struct fw3_icmptype *icmptype;
419
420         struct list_head *sports = NULL;
421         struct list_head *dports = NULL;
422         struct list_head *icmptypes = NULL;
423
424         struct list_head empty;
425         INIT_LIST_HEAD(&empty);
426
427         if (!fw3_is_family(rule, handle->family))
428                 return;
429
430         if ((rule->target == FW3_FLAG_NOTRACK && handle->table != FW3_TABLE_RAW) ||
431             (rule->target == FW3_FLAG_MARK && handle->table != FW3_TABLE_MANGLE) ||
432                 (rule->target < FW3_FLAG_NOTRACK && handle->table != FW3_TABLE_FILTER))
433                 return;
434
435         if (rule->name)
436                 info("   * Rule '%s'", rule->name);
437         else
438                 info("   * Rule #%u", num);
439
440         if (!fw3_is_family(rule->_src, handle->family) ||
441             !fw3_is_family(rule->_dest, handle->family))
442         {
443                 info("     ! Skipping due to different family of zone");
444                 return;
445         }
446
447         if (rule->ipset.ptr)
448         {
449                 if (!fw3_is_family(rule->ipset.ptr, handle->family))
450                 {
451                         info("     ! Skipping due to different family in ipset");
452                         return;
453                 }
454
455                 if (!fw3_check_ipset(rule->ipset.ptr))
456                 {
457                         info("     ! Skipping due to missing ipset '%s'",
458                              rule->ipset.ptr->external
459                                         ? rule->ipset.ptr->external : rule->ipset.ptr->name);
460                         return;
461                 }
462
463                 set(rule->ipset.ptr->flags, handle->family, handle->family);
464         }
465
466         list_for_each_entry(proto, &rule->proto, list)
467         {
468                 /* icmp / ipv6-icmp */
469                 if (proto->protocol == 1 || proto->protocol == 58)
470                 {
471                         sports = &empty;
472                         dports = &empty;
473                         icmptypes = &rule->icmp_type;
474                 }
475                 else
476                 {
477                         sports = &rule->port_src;
478                         dports = &rule->port_dest;
479                         icmptypes = &empty;
480                 }
481
482                 fw3_foreach(sip, &rule->ip_src)
483                 fw3_foreach(dip, &rule->ip_dest)
484                 fw3_foreach(sport, sports)
485                 fw3_foreach(dport, dports)
486                 fw3_foreach(mac, &rule->mac_src)
487                 fw3_foreach(icmptype, icmptypes)
488                         print_rule(handle, state, rule, num, proto, sip, dip,
489                                    sport, dport, mac, icmptype);
490         }
491 }
492
493 void
494 fw3_print_rules(struct fw3_ipt_handle *handle, struct fw3_state *state)
495 {
496         int num = 0;
497         struct fw3_rule *rule;
498
499         list_for_each_entry(rule, &state->rules, list)
500                 expand_rule(handle, state, rule, num++);
501 }