helpers: fix the set_helper in the rule structure
[project/firewall3.git] / forwards.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 "forwards.h"
20
21
22 const struct fw3_option fw3_forward_opts[] = {
23         FW3_OPT("enabled",             bool,     forward,     enabled),
24
25         FW3_OPT("name",                string,   forward,     name),
26         FW3_OPT("family",              family,   forward,     family),
27
28         FW3_OPT("src",                 device,   forward,     src),
29         FW3_OPT("dest",                device,   forward,     dest),
30
31         { }
32 };
33
34 static bool
35 check_forward(struct fw3_state *state, struct fw3_forward *forward, struct uci_element *e)
36 {
37         if (!forward->enabled)
38                 return false;
39
40         if (forward->src.invert || forward->dest.invert)
41         {
42                 warn_section("forward", forward, e, "must not have inverted 'src' or 'dest' options");
43                 return false;
44         }
45         else if (forward->src.set && !forward->src.any &&
46                  !(forward->_src = fw3_lookup_zone(state, forward->src.name)))
47         {
48                 warn_section("forward", forward, e, "refers to not existing zone '%s'",
49                                 forward->src.name);
50                 return false;
51         }
52         else if (forward->dest.set && !forward->dest.any &&
53                  !(forward->_dest = fw3_lookup_zone(state, forward->dest.name)))
54         {
55                 warn_section("forward", forward, e, "refers to not existing zone '%s'",
56                                 forward->dest.name);
57                 return false;
58         }
59
60         /* NB: forward family... */
61         if (forward->_dest)
62         {
63                 fw3_setbit(forward->_dest->flags[0], FW3_FLAG_ACCEPT);
64                 fw3_setbit(forward->_dest->flags[1], FW3_FLAG_ACCEPT);
65         }
66
67         return true;
68 }
69
70 static struct fw3_forward *
71 fw3_alloc_forward(struct fw3_state *state)
72 {
73         struct fw3_forward *forward;
74
75         forward = calloc(1, sizeof(*forward));
76         if (!forward)
77                 return NULL;
78
79         forward->enabled = true;
80
81         list_add_tail(&forward->list, &state->forwards);
82
83         return forward;
84 }
85
86 void
87 fw3_load_forwards(struct fw3_state *state, struct uci_package *p,
88                 struct blob_attr *a)
89 {
90         struct uci_section *s;
91         struct uci_element *e;
92         struct fw3_forward *forward;
93         struct blob_attr *entry;
94         unsigned rem;
95
96         INIT_LIST_HEAD(&state->forwards);
97
98         blob_for_each_attr(entry, a, rem)
99         {
100                 const char *type;
101                 const char *name = "ubus forward";
102
103                 if (!fw3_attr_parse_name_type(entry, &name, &type))
104                         continue;
105
106                 if (strcmp(type, "forwarding"))
107                         continue;
108
109                 forward = fw3_alloc_forward(state);
110                 if (!forward)
111                         continue;
112
113                 if (!fw3_parse_blob_options(forward, fw3_forward_opts, entry, name))
114                 {
115                         warn_section("forward", forward, NULL, "skipped due to invalid options");
116                         fw3_free_forward(forward);
117                         continue;
118                 }
119
120                 if (!check_forward(state, forward, NULL))
121                         fw3_free_forward(forward);
122         }
123
124         uci_foreach_element(&p->sections, e)
125         {
126                 s = uci_to_section(e);
127
128                 if (strcmp(s->type, "forwarding"))
129                         continue;
130
131                 forward = fw3_alloc_forward(state);
132                 if (!forward)
133                         continue;
134
135                 if (!fw3_parse_options(forward, fw3_forward_opts, s))
136                         warn_elem(e, "has invalid options");
137
138                 if (!check_forward(state, forward, e))
139                         fw3_free_forward(forward);
140         }
141 }
142
143
144 static void
145 append_chain(struct fw3_ipt_rule *r, struct fw3_forward *forward)
146 {
147         if (forward->src.any || !forward->src.set)
148                 fw3_ipt_rule_append(r, "FORWARD");
149         else
150                 fw3_ipt_rule_append(r, "zone_%s_forward", forward->src.name);
151 }
152
153 static void set_target(struct fw3_ipt_rule *r, struct fw3_forward *forward)
154 {
155         if (forward->dest.any || !forward->dest.set)
156                 fw3_ipt_rule_target(r, "ACCEPT");
157         else
158                 fw3_ipt_rule_target(r, "zone_%s_dest_ACCEPT", forward->dest.name);
159 }
160
161 static void
162 print_forward(struct fw3_ipt_handle *handle, struct fw3_forward *forward)
163 {
164         const char *s, *d;
165         struct fw3_ipt_rule *r;
166
167         if (handle->table != FW3_TABLE_FILTER)
168                 return;
169
170         if (!fw3_is_family(forward, handle->family))
171                 return;
172
173         s = forward->_src  ? forward->_src->name  : "*";
174         d = forward->_dest ? forward->_dest->name : "*";
175
176         info("   * Forward '%s' -> '%s'", s, d);
177
178         if (!fw3_is_family(forward->_src, handle->family) ||
179             !fw3_is_family(forward->_dest, handle->family))
180         {
181                 info("     ! Skipping due to different family of zone");
182                 return;
183         }
184
185         r = fw3_ipt_rule_new(handle);
186         fw3_ipt_rule_comment(r, "Zone %s to %s forwarding policy", s, d);
187         set_target(r, forward);
188         append_chain(r, forward);
189 }
190
191 void
192 fw3_print_forwards(struct fw3_ipt_handle *handle, struct fw3_state *state)
193 {
194         struct fw3_forward *forward;
195
196         list_for_each_entry(forward, &state->forwards, list)
197                 print_forward(handle, forward);
198 }