helpers: implement explicit CT helper assignment support
[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 }