add debug flag to monitor fw3_pr() calls, set policies to drop during reload
[project/firewall3.git] / rules.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 "rules.h"
20
21
22 const struct fw3_option fw3_rule_opts[] = {
23         FW3_OPT("name",                string,    rule,     name),
24         FW3_OPT("family",              family,    rule,     family),
25
26         FW3_OPT("src",                 device,    rule,     src),
27         FW3_OPT("dest",                device,    rule,     dest),
28
29         FW3_OPT("ipset",               device,    rule,     ipset),
30
31         FW3_LIST("proto",              protocol,  rule,     proto),
32
33         FW3_LIST("src_ip",             address,   rule,     ip_src),
34         FW3_LIST("src_mac",            mac,       rule,     mac_src),
35         FW3_LIST("src_port",           port,      rule,     port_src),
36
37         FW3_LIST("dest_ip",            address,   rule,     ip_dest),
38         FW3_LIST("dest_port",          port,      rule,     port_dest),
39
40         FW3_LIST("icmp_type",          icmptype,  rule,     icmp_type),
41         FW3_OPT("extra",               string,    rule,     extra),
42
43         FW3_OPT("limit",               limit,     rule,     limit),
44         FW3_OPT("limit_burst",         int,       rule,     limit.burst),
45
46         FW3_OPT("utc_time",            bool,      rule,     time.utc),
47         FW3_OPT("start_date",          date,      rule,     time.datestart),
48         FW3_OPT("stop_date",           date,      rule,     time.datestop),
49         FW3_OPT("start_time",          time,      rule,     time.timestart),
50         FW3_OPT("stop_time",           time,      rule,     time.timestop),
51         FW3_OPT("weekdays",            weekdays,  rule,     time.weekdays),
52         FW3_OPT("monthdays",           monthdays, rule,     time.monthdays),
53
54         FW3_OPT("target",              target,    rule,     target),
55
56         { }
57 };
58
59
60 void
61 fw3_load_rules(struct fw3_state *state, struct uci_package *p)
62 {
63         struct uci_section *s;
64         struct uci_element *e;
65         struct fw3_rule *rule;
66
67         INIT_LIST_HEAD(&state->rules);
68
69         uci_foreach_element(&p->sections, e)
70         {
71                 s = uci_to_section(e);
72
73                 if (strcmp(s->type, "rule"))
74                         continue;
75
76                 rule = malloc(sizeof(*rule));
77
78                 if (!rule)
79                         continue;
80
81                 memset(rule, 0, sizeof(*rule));
82
83                 INIT_LIST_HEAD(&rule->proto);
84
85                 INIT_LIST_HEAD(&rule->ip_src);
86                 INIT_LIST_HEAD(&rule->mac_src);
87                 INIT_LIST_HEAD(&rule->port_src);
88
89                 INIT_LIST_HEAD(&rule->ip_dest);
90                 INIT_LIST_HEAD(&rule->port_dest);
91
92                 INIT_LIST_HEAD(&rule->icmp_type);
93
94                 fw3_parse_options(rule, fw3_rule_opts, s);
95
96                 if (rule->src.invert || rule->dest.invert)
97                 {
98                         warn_elem(e, "must not have inverted 'src' or 'dest' options");
99                         fw3_free_rule(rule);
100                         continue;
101                 }
102                 else if (rule->src.set && !rule->src.any &&
103                          !(rule->_src = fw3_lookup_zone(state, rule->src.name, false)))
104                 {
105                         warn_elem(e, "refers to not existing zone '%s'", rule->src.name);
106                         fw3_free_rule(rule);
107                         continue;
108                 }
109                 else if (rule->dest.set && !rule->dest.any &&
110                          !(rule->_dest = fw3_lookup_zone(state, rule->dest.name, false)))
111                 {
112                         warn_elem(e, "refers to not existing zone '%s'", rule->dest.name);
113                         fw3_free_rule(rule);
114                         continue;
115                 }
116                 else if (rule->ipset.set && state->disable_ipsets)
117                 {
118                         warn_elem(e, "skipped due to disabled ipset support");
119                         fw3_free_rule(rule);
120                         continue;
121                 }
122                 else if (rule->ipset.set && !rule->ipset.any &&
123                          !(rule->_ipset = fw3_lookup_ipset(state, rule->ipset.name, false)))
124                 {
125                         warn_elem(e, "refers to unknown ipset '%s'", rule->ipset.name);
126                         fw3_free_rule(rule);
127                         continue;
128                 }
129
130                 if (!rule->_src && rule->target == FW3_TARGET_NOTRACK)
131                 {
132                         warn_elem(e, "is set to target NOTRACK but has no source assigned");
133                         fw3_free_rule(rule);
134                         continue;
135                 }
136
137                 if (!rule->_src && !rule->_dest && !rule->src.any && !rule->dest.any)
138                 {
139                         warn_elem(e, "has neither a source nor a destination zone assigned "
140                                      "- assuming an output rule");
141                 }
142
143                 if (rule->target == FW3_TARGET_UNSPEC)
144                 {
145                         warn_elem(e, "has no target specified, defaulting to REJECT");
146                         rule->target = FW3_TARGET_REJECT;
147                 }
148                 else if (rule->target > FW3_TARGET_NOTRACK)
149                 {
150                         warn_elem(e, "has invalid target specified, defaulting to REJECT");
151                         rule->target = FW3_TARGET_REJECT;
152                 }
153
154                 if (rule->_dest)
155                         setbit(rule->_dest->dst_flags, rule->target);
156
157                 list_add_tail(&rule->list, &state->rules);
158                 continue;
159         }
160 }
161
162
163 static void
164 print_chain(struct fw3_rule *rule)
165 {
166         char chain[256];
167
168         sprintf(chain, "delegate_output");
169
170         if (rule->target == FW3_TARGET_NOTRACK)
171         {
172                 sprintf(chain, "zone_%s_notrack", rule->src.name);
173         }
174         else
175         {
176                 if (rule->src.set)
177                 {
178                         if (!rule->src.any)
179                         {
180                                 if (rule->dest.set)
181                                         sprintf(chain, "zone_%s_forward", rule->src.name);
182                                 else
183                                         sprintf(chain, "zone_%s_input", rule->src.name);
184                         }
185                         else
186                         {
187                                 if (rule->dest.set)
188                                         sprintf(chain, "delegate_forward");
189                                 else
190                                         sprintf(chain, "delegate_input");
191                         }
192                 }
193
194                 if (rule->dest.set && !rule->src.set)
195                         sprintf(chain, "zone_%s_output", rule->dest.name);
196         }
197
198         fw3_pr("-A %s", chain);
199 }
200
201 static void print_target(struct fw3_rule *rule)
202 {
203         const char *target;
204
205         switch(rule->target)
206         {
207         case FW3_TARGET_ACCEPT:
208         case FW3_TARGET_DROP:
209         case FW3_TARGET_NOTRACK:
210                 target = fw3_flag_names[rule->target];
211                 break;
212
213         default:
214                 target = fw3_flag_names[FW3_TARGET_REJECT];
215                 break;
216         }
217
218         if (rule->dest.set && !rule->dest.any)
219                 fw3_pr(" -j zone_%s_dest_%s\n", rule->dest.name, target);
220         else if (rule->target == FW3_TARGET_REJECT)
221                 fw3_pr(" -j reject\n");
222         else
223                 fw3_pr(" -j %s\n", target);
224 }
225
226 static void
227 print_rule(enum fw3_table table, enum fw3_family family,
228            struct fw3_rule *rule, struct fw3_protocol *proto,
229            struct fw3_address *sip, struct fw3_address *dip,
230            struct fw3_port *sport, struct fw3_port *dport,
231            struct fw3_mac *mac, struct fw3_icmptype *icmptype)
232 {
233         if (!fw3_is_family(sip, family) || !fw3_is_family(dip, family))
234         {
235                 info("     ! Skipping due to different family of ip address");
236                 return;
237         }
238
239         if (proto->protocol == 58 && family == FW3_FAMILY_V4)
240         {
241                 info("     ! Skipping due to different family of protocol");
242                 return;
243         }
244
245         print_chain(rule);
246         fw3_format_ipset(rule->_ipset, rule->ipset.invert);
247         fw3_format_protocol(proto, family);
248         fw3_format_src_dest(sip, dip);
249         fw3_format_sport_dport(sport, dport);
250         fw3_format_icmptype(icmptype, family);
251         fw3_format_mac(mac);
252         fw3_format_limit(&rule->limit);
253         fw3_format_time(&rule->time);
254         fw3_format_extra(rule->extra);
255         fw3_format_comment(rule->name);
256         print_target(rule);
257 }
258
259 static void
260 expand_rule(enum fw3_table table, enum fw3_family family,
261             struct fw3_rule *rule, int num)
262 {
263         struct fw3_protocol *proto;
264         struct fw3_address *sip;
265         struct fw3_address *dip;
266         struct fw3_port *sport;
267         struct fw3_port *dport;
268         struct fw3_mac *mac;
269         struct fw3_icmptype *icmptype;
270
271         struct list_head *sports = NULL;
272         struct list_head *dports = NULL;
273         struct list_head *icmptypes = NULL;
274
275         struct list_head empty;
276         INIT_LIST_HEAD(&empty);
277
278         if (!fw3_is_family(rule, family))
279                 return;
280
281         if ((table == FW3_TABLE_RAW && rule->target != FW3_TARGET_NOTRACK) ||
282             (table != FW3_TABLE_FILTER))
283                 return;
284
285         if (rule->name)
286                 info("   * Rule '%s'", rule->name);
287         else
288                 info("   * Rule #%u", num);
289
290         if (!fw3_is_family(rule->_src, family) ||
291             !fw3_is_family(rule->_dest, family))
292         {
293                 info("     ! Skipping due to different family of zone");
294                 return;
295         }
296
297         if (rule->_ipset)
298         {
299                 if (!fw3_is_family(rule->_ipset, family))
300                 {
301                         info("     ! Skipping due to different family in ipset");
302                         return;
303                 }
304
305                 setbit(rule->_ipset->flags, family);
306         }
307
308         list_for_each_entry(proto, &rule->proto, list)
309         {
310                 /* icmp / ipv6-icmp */
311                 if (proto->protocol == 1 || proto->protocol == 58)
312                 {
313                         sports = &empty;
314                         dports = &empty;
315                         icmptypes = &rule->icmp_type;
316                 }
317                 else
318                 {
319                         sports = &rule->port_src;
320                         dports = &rule->port_dest;
321                         icmptypes = &empty;
322                 }
323
324                 fw3_foreach(sip, &rule->ip_src)
325                 fw3_foreach(dip, &rule->ip_dest)
326                 fw3_foreach(sport, sports)
327                 fw3_foreach(dport, dports)
328                 fw3_foreach(mac, &rule->mac_src)
329                 fw3_foreach(icmptype, icmptypes)
330                         print_rule(table, family, rule, proto, sip, dip, sport, dport,
331                                    mac, icmptype);
332         }
333 }
334
335 void
336 fw3_print_rules(enum fw3_table table, enum fw3_family family,
337                 struct fw3_state *state)
338 {
339         int num = 0;
340         struct fw3_rule *rule;
341
342         list_for_each_entry(rule, &state->rules, list)
343                 expand_rule(table, family, rule, num++);
344 }