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