helpers: make the proto field as a list rather than one option
[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_LIST("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_proto(const struct fw3_cthelper *helper)
51 {
52         struct fw3_protocol     *proto;
53
54         if (list_empty(&helper->proto))
55                 return false;
56
57         list_for_each_entry(proto, &helper->proto, list)
58         {
59                 if (!proto->protocol || proto->any || proto->invert)
60                         return false;
61         }
62
63         return true;
64 }
65
66 static bool
67 check_cthelper(struct fw3_state *state, struct fw3_cthelper *helper, struct uci_element *e)
68 {
69         if (!helper->name || !*helper->name)
70         {
71                 warn_section("helper", helper, e, "must have a name assigned");
72         }
73         else if (!helper->module || !*helper->module)
74         {
75                 warn_section("helper", helper, e, "must have a module assigned");
76         }
77         else if (!check_cthelper_proto(helper))
78         {
79                 warn_section("helper", helper, e, "must specify a protocol");
80         }
81         else if (helper->port.set && helper->port.invert)
82         {
83                 warn_section("helper", helper, e, "must not specify negated ports");
84         }
85         else
86         {
87                 return true;
88         }
89
90         return false;
91 }
92
93 static struct fw3_cthelper *
94 fw3_alloc_cthelper(struct fw3_state *state)
95 {
96         struct fw3_cthelper *helper;
97
98         helper = calloc(1, sizeof(*helper));
99         if (!helper)
100                 return NULL;
101
102         helper->enabled = true;
103         helper->family  = FW3_FAMILY_ANY;
104         INIT_LIST_HEAD(&helper->proto);
105
106         list_add_tail(&helper->list, &state->cthelpers);
107
108         return helper;
109 }
110
111 static void
112 load_cthelpers(struct fw3_state *state, struct uci_package *p)
113 {
114         struct fw3_cthelper *helper;
115         struct uci_section *s;
116         struct uci_element *e;
117
118         uci_foreach_element(&p->sections, e)
119         {
120                 s = uci_to_section(e);
121
122                 if (strcmp(s->type, "helper"))
123                         continue;
124
125                 helper = fw3_alloc_cthelper(state);
126
127                 if (!helper)
128                         continue;
129
130                 if (!fw3_parse_options(helper, fw3_cthelper_opts, s))
131                         warn_elem(e, "has invalid options");
132
133                 if (!check_cthelper(state, helper, e))
134                         fw3_free_cthelper(helper);
135         }
136 }
137
138 void
139 fw3_load_cthelpers(struct fw3_state *state, struct uci_package *p)
140 {
141         struct uci_package *hp = NULL;
142         FILE *fp;
143
144         INIT_LIST_HEAD(&state->cthelpers);
145
146         fp = fopen(FW3_HELPERCONF, "r");
147
148         if (fp) {
149                 uci_import(state->uci, fp, "fw3_ct_helpers", &hp, true);
150                 fclose(fp);
151
152                 if (hp)
153                         load_cthelpers(state, hp);
154         }
155
156         load_cthelpers(state, p);
157 }
158
159 struct fw3_cthelper *
160 fw3_lookup_cthelper(struct fw3_state *state, const char *name)
161 {
162         struct fw3_cthelper *h;
163
164         if (list_empty(&state->cthelpers))
165                 return NULL;
166
167         list_for_each_entry(h, &state->cthelpers, list)
168         {
169                 if (strcasecmp(h->name, name))
170                         continue;
171
172                 return h;
173         }
174
175         return NULL;
176 }
177
178 bool
179 fw3_cthelper_check_proto(const struct fw3_cthelper *h, const struct fw3_protocol *proto)
180 {
181         struct fw3_protocol     *p;
182
183         list_for_each_entry(p, &h->proto, list)
184         {
185                 if (p->protocol == proto->protocol)
186                         return true;
187         }
188
189         return false;
190 }
191
192 struct fw3_cthelper *
193 fw3_lookup_cthelper_by_proto_port(struct fw3_state *state,
194                                   struct fw3_protocol *proto,
195                                   struct fw3_port *port)
196 {
197         struct fw3_cthelper *h;
198
199         if (list_empty(&state->cthelpers))
200                 return NULL;
201
202         if (!proto || !proto->protocol || proto->any || proto->invert)
203                 return NULL;
204
205         if (port && port->invert)
206                 return NULL;
207
208         list_for_each_entry(h, &state->cthelpers, list)
209         {
210                 if (!h->enabled)
211                         continue;
212
213                 if (!fw3_cthelper_check_proto(h, proto))
214                         continue;
215
216                 if (h->port.set && (!port || !port->set))
217                         continue;
218
219                 if (!h->port.set && (!port || !port->set))
220                         return h;
221
222                 if (h->port.set && port && port->set &&
223                     h->port.port_min <= port->port_min &&
224                     h->port.port_max >= port->port_max)
225                     return h;
226         }
227
228         return NULL;
229 }
230
231 static void
232 print_helper_rule(struct fw3_ipt_handle *handle, struct fw3_cthelper *helper,
233                   struct fw3_zone *zone, struct fw3_protocol *proto)
234 {
235         struct fw3_ipt_rule *r;
236
237         r = fw3_ipt_rule_create(handle, proto, NULL, NULL, NULL, NULL);
238
239         if (helper->description && *helper->description)
240                 fw3_ipt_rule_comment(r, helper->description);
241         else
242                 fw3_ipt_rule_comment(r, helper->name);
243
244         fw3_ipt_rule_sport_dport(r, NULL, &helper->port);
245         fw3_ipt_rule_target(r, "CT");
246         fw3_ipt_rule_addarg(r, false, "--helper", helper->name);
247         fw3_ipt_rule_replace(r, "zone_%s_helper", zone->name);
248 }
249
250 static void
251 expand_helper_rule(struct fw3_ipt_handle *handle, struct fw3_cthelper *helper,
252                   struct fw3_zone *zone)
253 {
254         struct fw3_protocol *proto;
255
256         list_for_each_entry(proto, &helper->proto, list)
257                 print_helper_rule(handle, helper, zone, proto);
258 }
259
260 void
261 fw3_print_cthelpers(struct fw3_ipt_handle *handle, struct fw3_state *state,
262                     struct fw3_zone *zone)
263 {
264         struct fw3_cthelper *helper;
265         struct fw3_cthelpermatch *match;
266
267         if (handle->table != FW3_TABLE_RAW)
268                 return;
269
270         if (!fw3_is_family(zone, handle->family))
271                 return;
272
273         if (list_empty(&zone->cthelpers))
274         {
275                 if (zone->masq || !zone->auto_helper)
276                         return;
277
278                 if (list_empty(&state->cthelpers))
279                         return;
280
281                 info("     - Using automatic conntrack helper attachment");
282
283                 list_for_each_entry(helper, &state->cthelpers, list)
284                 {
285                         if (!helper || !helper->enabled)
286                                 continue;
287
288                         if (!fw3_is_family(helper, handle->family))
289                                 continue;
290
291                         if (!test_module(helper))
292                                 continue;
293
294                         expand_helper_rule(handle, helper, zone);
295                 }
296         }
297         else
298         {
299                 list_for_each_entry(match, &zone->cthelpers, list)
300                 {
301                         helper = match->ptr;
302
303                         if (!helper || !helper->enabled)
304                                 continue;
305
306                         if (!fw3_is_family(helper, handle->family))
307                                 continue;
308
309                         if (!test_module(helper))
310                         {
311                                 info("     ! Conntrack module '%s' for helper '%s' is not loaded",
312                                      helper->module, helper->name);
313                                 continue;
314                         }
315
316                         expand_helper_rule(handle, helper, zone);
317                 }
318         }
319 }