8cad0b3459754f08eb7d5cecd028598456472570
[project/firewall3.git] / helpers.c
1 /*
2  * firewall3 - 3rd OpenWrt UCI firewall implementation
3  *
4  *   Copyright (C) 2018 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 "helpers.h"
20
21
22 const struct fw3_option fw3_cthelper_opts[] = {
23         FW3_OPT("enabled",     bool,     cthelper, enabled),
24         FW3_OPT("name",        string,   cthelper, name),
25         FW3_OPT("module",      string,   cthelper, module),
26         FW3_OPT("description", string,   cthelper, description),
27         FW3_OPT("family",      family,   cthelper, family),
28         FW3_OPT("proto",       protocol, cthelper, proto),
29         FW3_OPT("port",        port,     cthelper, port),
30
31         { }
32 };
33
34
35 static bool
36 test_module(struct fw3_cthelper *helper)
37 {
38         struct stat s;
39         char path[sizeof("/sys/module/nf_conntrack_xxxxxxxxxxxxxxxx")];
40
41         snprintf(path, sizeof(path), "/sys/module/%s", helper->module);
42
43         if (stat(path, &s) || !S_ISDIR(s.st_mode))
44                 return false;
45
46         return true;
47 }
48
49 static bool
50 check_cthelper(struct fw3_state *state, struct fw3_cthelper *helper, struct uci_element *e)
51 {
52         if (!helper->name || !*helper->name)
53         {
54                 warn_section("helper", helper, e, "must have a name assigned");
55         }
56         else if (!helper->module || !*helper->module)
57         {
58                 warn_section("helper", helper, e, "must have a module assigned");
59         }
60         else if (!helper->proto.protocol || helper->proto.any || helper->proto.invert)
61         {
62                 warn_section("helper", helper, e, "must specify a protocol");
63         }
64         else if (helper->port.set && helper->port.invert)
65         {
66                 warn_section("helper", helper, e, "must not specify negated ports");
67         }
68         else
69         {
70                 return true;
71         }
72
73         return false;
74 }
75
76 static struct fw3_cthelper *
77 fw3_alloc_cthelper(struct fw3_state *state)
78 {
79         struct fw3_cthelper *helper;
80
81         helper = calloc(1, sizeof(*helper));
82         if (!helper)
83                 return NULL;
84
85         helper->enabled = true;
86         helper->family  = FW3_FAMILY_ANY;
87
88         list_add_tail(&helper->list, &state->cthelpers);
89
90         return helper;
91 }
92
93 static void
94 load_cthelpers(struct fw3_state *state, struct uci_package *p)
95 {
96         struct fw3_cthelper *helper;
97         struct uci_section *s;
98         struct uci_element *e;
99
100         uci_foreach_element(&p->sections, e)
101         {
102                 s = uci_to_section(e);
103
104                 if (strcmp(s->type, "helper"))
105                         continue;
106
107                 helper = fw3_alloc_cthelper(state);
108
109                 if (!helper)
110                         continue;
111
112                 if (!fw3_parse_options(helper, fw3_cthelper_opts, s))
113                         warn_elem(e, "has invalid options");
114
115                 if (!check_cthelper(state, helper, e))
116                         fw3_free_cthelper(helper);
117         }
118 }
119
120 void
121 fw3_load_cthelpers(struct fw3_state *state, struct uci_package *p)
122 {
123         struct uci_package *hp = NULL;
124         FILE *fp;
125
126         INIT_LIST_HEAD(&state->cthelpers);
127
128         fp = fopen(FW3_HELPERCONF, "r");
129
130         if (fp) {
131                 uci_import(state->uci, fp, "fw3_ct_helpers", &hp, true);
132                 fclose(fp);
133
134                 if (hp)
135                         load_cthelpers(state, hp);
136         }
137
138         load_cthelpers(state, p);
139 }
140
141 struct fw3_cthelper *
142 fw3_lookup_cthelper(struct fw3_state *state, const char *name)
143 {
144         struct fw3_cthelper *h;
145
146         if (list_empty(&state->cthelpers))
147                 return NULL;
148
149         list_for_each_entry(h, &state->cthelpers, list)
150         {
151                 if (strcasecmp(h->name, name))
152                         continue;
153
154                 return h;
155         }
156
157         return NULL;
158 }
159
160 struct fw3_cthelper *
161 fw3_lookup_cthelper_by_proto_port(struct fw3_state *state,
162                                   struct fw3_protocol *proto,
163                                   struct fw3_port *port)
164 {
165         struct fw3_cthelper *h;
166
167         if (list_empty(&state->cthelpers))
168                 return NULL;
169
170         if (!proto || !proto->protocol || proto->any || proto->invert)
171                 return NULL;
172
173         if (port && port->invert)
174                 return NULL;
175
176         list_for_each_entry(h, &state->cthelpers, list)
177         {
178                 if (!h->enabled)
179                         continue;
180
181                 if (h->proto.protocol != proto->protocol)
182                         continue;
183
184                 if (h->port.set && (!port || !port->set))
185                         continue;
186
187                 if (!h->port.set && (!port || !port->set))
188                         return h;
189
190                 if (h->port.set && port && port->set &&
191                     h->port.port_min <= port->port_min &&
192                     h->port.port_max >= port->port_max)
193                     return h;
194         }
195
196         return NULL;
197 }
198
199 static void
200 print_helper_rule(struct fw3_ipt_handle *handle, struct fw3_cthelper *helper,
201                   struct fw3_zone *zone)
202 {
203         struct fw3_ipt_rule *r;
204
205         r = fw3_ipt_rule_create(handle, &helper->proto, NULL, NULL, NULL, NULL);
206
207         if (helper->description && *helper->description)
208                 fw3_ipt_rule_comment(r, helper->description);
209         else
210                 fw3_ipt_rule_comment(r, helper->name);
211
212         fw3_ipt_rule_sport_dport(r, NULL, &helper->port);
213         fw3_ipt_rule_target(r, "CT");
214         fw3_ipt_rule_addarg(r, false, "--helper", helper->name);
215         fw3_ipt_rule_replace(r, "zone_%s_helper", zone->name);
216 }
217
218 void
219 fw3_print_cthelpers(struct fw3_ipt_handle *handle, struct fw3_state *state,
220                     struct fw3_zone *zone)
221 {
222         struct fw3_cthelper *helper;
223         struct fw3_cthelpermatch *match;
224
225         if (handle->table != FW3_TABLE_RAW)
226                 return;
227
228         if (!fw3_is_family(zone, handle->family))
229                 return;
230
231         if (list_empty(&zone->cthelpers))
232         {
233                 if (zone->masq || !zone->auto_helper)
234                         return;
235
236                 if (list_empty(&state->cthelpers))
237                         return;
238
239                 info("     - Using automatic conntrack helper attachment");
240
241                 list_for_each_entry(helper, &state->cthelpers, list)
242                 {
243                         if (!helper || !helper->enabled)
244                                 continue;
245
246                         if (!fw3_is_family(helper, handle->family))
247                                 continue;
248
249                         if (!test_module(helper))
250                                 continue;
251
252                         print_helper_rule(handle, helper, zone);
253                 }
254         }
255         else
256         {
257                 list_for_each_entry(match, &zone->cthelpers, list)
258                 {
259                         helper = match->ptr;
260
261                         if (!helper || !helper->enabled)
262                                 continue;
263
264                         if (!fw3_is_family(helper, handle->family))
265                                 continue;
266
267                         if (!test_module(helper))
268                         {
269                                 info("     ! Conntrack module '%s' for helper '%s' is not loaded",
270                                      helper->module, helper->name);
271                                 continue;
272                         }
273
274                         print_helper_rule(handle, helper, zone);
275                 }
276         }
277 }