track used family for 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 unknown 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                         setbit(rule->_dest->dst_flags, rule->target);
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         const char *target;
194
195         switch(rule->target)
196         {
197         case FW3_TARGET_ACCEPT:
198         case FW3_TARGET_DROP:
199         case FW3_TARGET_NOTRACK:
200                 target = fw3_flag_names[rule->target];
201                 break;
202
203         default:
204                 target = fw3_flag_names[FW3_TARGET_REJECT];
205                 break;
206         }
207
208         if (rule->dest.set && !rule->dest.any)
209                 fw3_pr(" -j zone_%s_dest_%s\n", rule->dest.name, target);
210         else if (rule->target == FW3_TARGET_REJECT)
211                 fw3_pr(" -j reject\n");
212         else
213                 fw3_pr(" -j %s\n", target);
214 }
215
216 static void
217 print_rule(enum fw3_table table, enum fw3_family family,
218            struct fw3_rule *rule, struct fw3_protocol *proto,
219            struct fw3_address *sip, struct fw3_address *dip,
220            struct fw3_port *sport, struct fw3_port *dport,
221            struct fw3_mac *mac, struct fw3_icmptype *icmptype)
222 {
223         if (!fw3_is_family(sip, family) || !fw3_is_family(dip, family))
224         {
225                 info("     ! Skipping due to different family of ip address");
226                 return;
227         }
228
229         if (proto->protocol == 58 && family == FW3_FAMILY_V4)
230         {
231                 info("     ! Skipping due to different family of protocol");
232                 return;
233         }
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         if (!fw3_is_family(rule->_src, family) ||
280             !fw3_is_family(rule->_dest, family))
281         {
282                 info("     ! Skipping due to different family of zone");
283                 return;
284         }
285
286         if (rule->_ipset)
287         {
288                 if (!fw3_is_family(rule->_ipset, family))
289                 {
290                         info("     ! Skipping due to different family in ipset");
291                         return;
292                 }
293
294                 setbit(rule->_ipset->flags, family);
295         }
296
297         list_for_each_entry(proto, &rule->proto, list)
298         {
299                 /* icmp / ipv6-icmp */
300                 if (proto->protocol == 1 || proto->protocol == 58)
301                 {
302                         sports = &empty;
303                         dports = &empty;
304                         icmptypes = &rule->icmp_type;
305                 }
306                 else
307                 {
308                         sports = &rule->port_src;
309                         dports = &rule->port_dest;
310                         icmptypes = &empty;
311                 }
312
313                 fw3_foreach(sip, &rule->ip_src)
314                 fw3_foreach(dip, &rule->ip_dest)
315                 fw3_foreach(sport, sports)
316                 fw3_foreach(dport, dports)
317                 fw3_foreach(mac, &rule->mac_src)
318                 fw3_foreach(icmptype, icmptypes)
319                         print_rule(table, family, rule, proto, sip, dip, sport, dport,
320                                    mac, icmptype);
321         }
322 }
323
324 void
325 fw3_print_rules(enum fw3_table table, enum fw3_family family,
326                 struct fw3_state *state)
327 {
328         int num = 0;
329         struct fw3_rule *rule;
330
331         list_for_each_entry(rule, &state->rules, list)
332                 expand_rule(table, family, rule, num++);
333 }
334
335 void
336 fw3_free_rule(struct fw3_rule *rule)
337 {
338         fw3_free_list(&rule->proto);
339
340         fw3_free_list(&rule->ip_src);
341         fw3_free_list(&rule->mac_src);
342         fw3_free_list(&rule->port_dest);
343
344         fw3_free_list(&rule->ip_dest);
345         fw3_free_list(&rule->port_dest);
346
347         fw3_free_list(&rule->icmp_type);
348
349         free(rule);
350 }