1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 /* Make sure the net/if.h header is included before any linux/ one */
11 #include <linux/netfilter_ipv4/ip_tables.h>
12 #include <linux/netfilter/nf_nat.h>
13 #include <linux/netfilter/xt_addrtype.h>
14 #include <libiptc/libiptc.h>
16 #include "alloc-util.h"
17 #include "dlfcn-util.h"
18 #include "firewall-util.h"
19 #include "firewall-util-private.h"
20 #include "in-addr-util.h"
22 #include "socket-util.h"
24 static DLSYM_FUNCTION(iptc_check_entry
);
25 static DLSYM_FUNCTION(iptc_commit
);
26 static DLSYM_FUNCTION(iptc_delete_entry
);
27 static DLSYM_FUNCTION(iptc_free
);
28 static DLSYM_FUNCTION(iptc_init
);
29 static DLSYM_FUNCTION(iptc_insert_entry
);
30 static DLSYM_FUNCTION(iptc_strerror
);
32 static void *iptc_dl
= NULL
;
34 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct xtc_handle
*, sym_iptc_free
, NULL
);
36 static int entry_fill_basics(
37 struct ipt_entry
*entry
,
39 const char *in_interface
,
40 const union in_addr_union
*source
,
41 unsigned source_prefixlen
,
42 const char *out_interface
,
43 const union in_addr_union
*destination
,
44 unsigned destination_prefixlen
) {
48 if (out_interface
&& !ifname_valid(out_interface
))
50 if (in_interface
&& !ifname_valid(in_interface
))
53 entry
->ip
.proto
= protocol
;
58 l
= strlen(in_interface
);
59 assert(l
< sizeof entry
->ip
.iniface
);
60 assert(l
< sizeof entry
->ip
.iniface_mask
);
62 strcpy(entry
->ip
.iniface
, in_interface
);
63 memset(entry
->ip
.iniface_mask
, 0xFF, l
+ 1);
66 entry
->ip
.src
= source
->in
;
67 in4_addr_prefixlen_to_netmask(&entry
->ip
.smsk
, source_prefixlen
);
71 size_t l
= strlen(out_interface
);
72 assert(l
< sizeof entry
->ip
.outiface
);
73 assert(l
< sizeof entry
->ip
.outiface_mask
);
75 strcpy(entry
->ip
.outiface
, out_interface
);
76 memset(entry
->ip
.outiface_mask
, 0xFF, l
+ 1);
79 entry
->ip
.dst
= destination
->in
;
80 in4_addr_prefixlen_to_netmask(&entry
->ip
.dmsk
, destination_prefixlen
);
86 int fw_iptables_add_masquerade(
89 const union in_addr_union
*source
,
90 unsigned source_prefixlen
) {
92 static const xt_chainlabel chain
= "POSTROUTING";
93 _cleanup_(sym_iptc_freep
) struct xtc_handle
*h
= NULL
;
94 struct ipt_entry
*entry
, *mask
;
95 struct ipt_entry_target
*t
;
97 struct nf_nat_ipv4_multi_range_compat
*mr
;
99 const char *out_interface
= NULL
;
100 const union in_addr_union
*destination
= NULL
;
101 unsigned destination_prefixlen
= 0;
106 if (!source
|| source_prefixlen
== 0)
109 r
= fw_iptables_init_nat(&h
);
113 sz
= XT_ALIGN(sizeof(struct ipt_entry
)) +
114 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
115 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
117 /* Put together the entry we want to add or remove */
119 entry
->next_offset
= sz
;
120 entry
->target_offset
= XT_ALIGN(sizeof(struct ipt_entry
));
121 r
= entry_fill_basics(entry
, protocol
, NULL
, source
, source_prefixlen
, out_interface
, destination
, destination_prefixlen
);
125 /* Fill in target part */
126 t
= ipt_get_target(entry
);
128 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
129 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
130 strncpy(t
->u
.user
.name
, "MASQUERADE", sizeof(t
->u
.user
.name
));
131 mr
= (struct nf_nat_ipv4_multi_range_compat
*) t
->data
;
134 /* Create a search mask entry */
135 mask
= alloca_safe(sz
);
136 memset(mask
, 0xFF, sz
);
139 if (sym_iptc_check_entry(chain
, entry
, (unsigned char*) mask
, h
))
141 if (errno
!= ENOENT
) /* if other error than not existing yet, fail */
144 if (!sym_iptc_insert_entry(chain
, entry
, 0, h
))
147 if (!sym_iptc_delete_entry(chain
, entry
, (unsigned char*) mask
, h
)) {
148 if (errno
== ENOENT
) /* if it's already gone, all is good! */
155 if (!sym_iptc_commit(h
))
161 int fw_iptables_add_local_dnat(
166 const union in_addr_union
*remote
,
167 uint16_t remote_port
,
168 const union in_addr_union
*previous_remote
) {
170 static const xt_chainlabel chain_pre
= "PREROUTING", chain_output
= "OUTPUT";
171 _cleanup_(sym_iptc_freep
) struct xtc_handle
*h
= NULL
;
172 struct ipt_entry
*entry
, *mask
;
173 struct ipt_entry_target
*t
;
174 struct ipt_entry_match
*m
;
175 struct xt_addrtype_info_v1
*at
;
176 struct nf_nat_ipv4_multi_range_compat
*mr
;
179 const char *in_interface
= NULL
;
180 const union in_addr_union
*source
= NULL
;
181 unsigned source_prefixlen
= 0;
182 const union in_addr_union
*destination
= NULL
;
183 unsigned destination_prefixlen
= 0;
185 assert(add
|| !previous_remote
);
190 if (!IN_SET(protocol
, IPPROTO_TCP
, IPPROTO_UDP
))
196 if (remote_port
<= 0)
199 r
= fw_iptables_init_nat(&h
);
203 sz
= XT_ALIGN(sizeof(struct ipt_entry
)) +
204 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
205 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
)) +
206 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
207 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
209 if (protocol
== IPPROTO_TCP
)
210 msz
= XT_ALIGN(sizeof(struct ipt_entry_match
)) +
211 XT_ALIGN(sizeof(struct xt_tcp
));
213 msz
= XT_ALIGN(sizeof(struct ipt_entry_match
)) +
214 XT_ALIGN(sizeof(struct xt_udp
));
218 /* Fill in basic part */
220 entry
->next_offset
= sz
;
221 entry
->target_offset
=
222 XT_ALIGN(sizeof(struct ipt_entry
)) +
223 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
224 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
)) +
226 r
= entry_fill_basics(entry
, protocol
, in_interface
, source
, source_prefixlen
, NULL
, destination
, destination_prefixlen
);
230 /* Fill in first match */
231 m
= (struct ipt_entry_match
*) ((uint8_t*) entry
+ XT_ALIGN(sizeof(struct ipt_entry
)));
232 m
->u
.match_size
= msz
;
233 if (protocol
== IPPROTO_TCP
) {
236 strncpy(m
->u
.user
.name
, "tcp", sizeof(m
->u
.user
.name
));
237 tcp
= (struct xt_tcp
*) m
->data
;
238 tcp
->dpts
[0] = tcp
->dpts
[1] = local_port
;
240 tcp
->spts
[1] = 0xFFFF;
245 strncpy(m
->u
.user
.name
, "udp", sizeof(m
->u
.user
.name
));
246 udp
= (struct xt_udp
*) m
->data
;
247 udp
->dpts
[0] = udp
->dpts
[1] = local_port
;
249 udp
->spts
[1] = 0xFFFF;
252 /* Fill in second match */
253 m
= (struct ipt_entry_match
*) ((uint8_t*) entry
+ XT_ALIGN(sizeof(struct ipt_entry
)) + msz
);
255 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
256 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
));
257 strncpy(m
->u
.user
.name
, "addrtype", sizeof(m
->u
.user
.name
));
258 m
->u
.user
.revision
= 1;
259 at
= (struct xt_addrtype_info_v1
*) m
->data
;
260 at
->dest
= XT_ADDRTYPE_LOCAL
;
262 /* Fill in target part */
263 t
= ipt_get_target(entry
);
265 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
266 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
267 strncpy(t
->u
.user
.name
, "DNAT", sizeof(t
->u
.user
.name
));
268 mr
= (struct nf_nat_ipv4_multi_range_compat
*) t
->data
;
270 mr
->range
[0].flags
= NF_NAT_RANGE_PROTO_SPECIFIED
|NF_NAT_RANGE_MAP_IPS
;
271 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= remote
->in
.s_addr
;
272 if (protocol
== IPPROTO_TCP
)
273 mr
->range
[0].min
.tcp
.port
= mr
->range
[0].max
.tcp
.port
= htobe16(remote_port
);
275 mr
->range
[0].min
.udp
.port
= mr
->range
[0].max
.udp
.port
= htobe16(remote_port
);
278 memset(mask
, 0xFF, sz
);
281 /* Add the PREROUTING rule, if it is missing so far */
282 if (!sym_iptc_check_entry(chain_pre
, entry
, (unsigned char*) mask
, h
)) {
286 if (!sym_iptc_insert_entry(chain_pre
, entry
, 0, h
))
290 /* If a previous remote is set, remove its entry */
291 if (previous_remote
&& previous_remote
->in
.s_addr
!= remote
->in
.s_addr
) {
292 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= previous_remote
->in
.s_addr
;
294 if (!sym_iptc_delete_entry(chain_pre
, entry
, (unsigned char*) mask
, h
)) {
299 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= remote
->in
.s_addr
;
302 /* Add the OUTPUT rule, if it is missing so far */
305 /* Don't apply onto loopback addresses */
307 entry
->ip
.dst
.s_addr
= htobe32(0x7F000000);
308 entry
->ip
.dmsk
.s_addr
= htobe32(0xFF000000);
309 entry
->ip
.invflags
= IPT_INV_DSTIP
;
312 if (!sym_iptc_check_entry(chain_output
, entry
, (unsigned char*) mask
, h
)) {
316 if (!sym_iptc_insert_entry(chain_output
, entry
, 0, h
))
320 /* If a previous remote is set, remove its entry */
321 if (previous_remote
&& previous_remote
->in
.s_addr
!= remote
->in
.s_addr
) {
322 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= previous_remote
->in
.s_addr
;
324 if (!sym_iptc_delete_entry(chain_output
, entry
, (unsigned char*) mask
, h
)) {
331 if (!sym_iptc_delete_entry(chain_pre
, entry
, (unsigned char*) mask
, h
)) {
338 entry
->ip
.dst
.s_addr
= htobe32(0x7F000000);
339 entry
->ip
.dmsk
.s_addr
= htobe32(0xFF000000);
340 entry
->ip
.invflags
= IPT_INV_DSTIP
;
343 if (!sym_iptc_delete_entry(chain_output
, entry
, (unsigned char*) mask
, h
)) {
350 if (!sym_iptc_commit(h
))
356 static int dlopen_iptc(void) {
357 return dlopen_many_sym_or_warn(
359 "libip4tc.so.2", LOG_DEBUG
,
360 DLSYM_ARG(iptc_check_entry
),
361 DLSYM_ARG(iptc_commit
),
362 DLSYM_ARG(iptc_delete_entry
),
363 DLSYM_ARG(iptc_free
),
364 DLSYM_ARG(iptc_init
),
365 DLSYM_ARG(iptc_insert_entry
),
366 DLSYM_ARG(iptc_strerror
));
369 int fw_iptables_init_nat(struct xtc_handle
**ret
) {
370 _cleanup_(sym_iptc_freep
) struct xtc_handle
*h
= NULL
;
377 h
= sym_iptc_init("nat");
379 return log_debug_errno(errno
, "Failed to init \"nat\" table: %s", sym_iptc_strerror(errno
));