Skip to content

Commit f50a524

Browse files
committed
helpers: implement explicit CT helper assignment support
Implement support for explicit per-zone conntrack helper assignment in the raw table in order to compensate for the now disabled automatic helper assignment in recent Linux kernels. This commit adds, along with the required infrastructure, a new per- zone uci option "helper" which can be used to tie one or more CT helpers to a given zone. For example the following configuration: config zone option name lan option network lan list helper ftp list helper sip ... will assign the FTP and SIP conntrack helpers as specified in /usr/share/fw3/helpers.conf to traffic originating from the LAN zone. Additionally, a new boolean option "auto_helper" has been defined for both "config defaults" and "config zone" sections, with the former option overruling the latter. When the default true "option auto_helper" is set, all available helpers are automatically attached to each non-masq zone (i.e. "lan" by default). When one or more "list helper" options are specified, the zone has masquerading enabled or "auto_helper" is set to false, then the automatic helper attachment is disabled for the corresponding zone. Furthermore, this commit introduces support for a new 'HELPER' target in "config rule" sections, along with "option helper" to match helper traffic and "option set_helper" to assign CT helpers to a stream. Finally, "config redirect" sections support "option helper" too now, which causes fw3 to emit helper setting rules for forwarded DNAT traffic. When "option helper" is not defined for a redirect and when the global option "auto_helper" is not disabled, fw3 will pick a suitable helper based on the destination protocol and port and assign it to DNATed traffic. Signed-off-by: Jo-Philipp Wich <[email protected]>
1 parent 503db4a commit f50a524

17 files changed

+809
-31
lines changed

CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ ENDIF()
2626
FIND_PATH(uci_include_dir uci.h)
2727
INCLUDE_DIRECTORIES(${uci_include_dir})
2828

29-
ADD_EXECUTABLE(firewall3 main.c options.c defaults.c zones.c forwards.c rules.c redirects.c snats.c utils.c ubus.c ipsets.c includes.c iptables.c)
29+
ADD_EXECUTABLE(firewall3 main.c options.c defaults.c zones.c forwards.c rules.c redirects.c snats.c utils.c ubus.c ipsets.c includes.c iptables.c helpers.c)
3030
TARGET_LINK_LIBRARIES(firewall3 uci ubox ubus xtables m dl ${iptc_libs} ${ext_libs})
3131

3232
SET(CMAKE_INSTALL_PREFIX /usr)

defaults.c

+2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ const struct fw3_option fw3_flag_opts[] = {
5454
FW3_OPT("accept_redirects", bool, defaults, accept_redirects),
5555
FW3_OPT("accept_source_route", bool, defaults, accept_source_route),
5656

57+
FW3_OPT("auto_helper", bool, defaults, auto_helper),
5758
FW3_OPT("custom_chains", bool, defaults, custom_chains),
5859
FW3_OPT("disable_ipv6", bool, defaults, disable_ipv6),
5960

@@ -93,6 +94,7 @@ fw3_load_defaults(struct fw3_state *state, struct uci_package *p)
9394
defs->tcp_syncookies = true;
9495
defs->tcp_window_scaling = true;
9596
defs->custom_chains = true;
97+
defs->auto_helper = true;
9698

9799
uci_foreach_element(&p->sections, e)
98100
{

helpers.c

+277
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
/*
2+
* firewall3 - 3rd OpenWrt UCI firewall implementation
3+
*
4+
* Copyright (C) 2018 Jo-Philipp Wich <[email protected]>
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+
}

helpers.conf

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
config helper
2+
option name 'amanda'
3+
option description 'Amanda backup and archiving proto'
4+
option module 'nf_conntrack_amanda'
5+
option family 'any'
6+
option proto 'udp'
7+
option port '10080'
8+
9+
config helper
10+
option name 'ftp'
11+
option description 'FTP passive connection tracking'
12+
option module 'nf_conntrack_ftp'
13+
option family 'any'
14+
option proto 'tcp'
15+
option port '21'
16+
17+
config helper
18+
option name 'RAS'
19+
option description 'RAS proto tracking'
20+
option module 'nf_conntrack_h323'
21+
option family 'any'
22+
option proto 'udp'
23+
option port '1719'
24+
25+
config helper
26+
option name 'Q.931'
27+
option description 'Q.931 proto tracking'
28+
option module 'nf_conntrack_h323'
29+
option family 'any'
30+
option proto 'tcp'
31+
option port '1720'
32+
33+
config helper
34+
option name 'irc'
35+
option description 'IRC DCC connection tracking'
36+
option module 'nf_conntrack_irc'
37+
option family 'ipv4'
38+
option proto 'tcp'
39+
option port '6667'
40+
41+
config helper
42+
option name 'netbios-ns'
43+
option description 'NetBIOS name service broadcast tracking'
44+
option module 'nf_conntrack_netbios_ns'
45+
option family 'ipv4'
46+
option proto 'udp'
47+
option port '137'
48+
49+
config helper
50+
option name 'pptp'
51+
option description 'PPTP VPN connection tracking'
52+
option module 'nf_conntrack_pptp'
53+
option family 'ipv4'
54+
option proto 'tcp'
55+
option port '1723'
56+
57+
config helper
58+
option name 'sane'
59+
option description 'SANE scanner connection tracking'
60+
option module 'nf_conntrack_sane'
61+
option family 'any'
62+
option proto 'tcp'
63+
option port '6566'
64+
65+
config helper
66+
option name 'sip'
67+
option description 'SIP VoIP connection tracking'
68+
option module 'nf_conntrack_sip'
69+
option family 'any'
70+
option proto 'udp'
71+
option port '5060'
72+
73+
config helper
74+
option name 'snmp'
75+
option description 'SNMP monitoring connection tracking'
76+
option module 'nf_conntrack_snmp'
77+
option family 'ipv4'
78+
option proto 'udp'
79+
option port '161'
80+
81+
config helper
82+
option name 'tftp'
83+
option description 'TFTP connection tracking'
84+
option module 'nf_conntrack_tftp'
85+
option family 'any'
86+
option proto 'udp'
87+
option port '69'

0 commit comments

Comments
 (0)