1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 /* Temporary work-around for broken glibc vs. linux kernel header definitions
4 * This is already fixed upstream, remove this when distributions have updated.
18 #include <linux/netfilter_ipv4/ip_tables.h>
19 #include <linux/netfilter/nf_nat.h>
20 #include <linux/netfilter/xt_addrtype.h>
21 #include <libiptc/libiptc.h>
23 #include "alloc-util.h"
24 #include "firewall-util.h"
25 #include "firewall-util-private.h"
26 #include "in-addr-util.h"
28 #include "socket-util.h"
30 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct xtc_handle
*, iptc_free
, NULL
);
32 static int entry_fill_basics(
33 struct ipt_entry
*entry
,
35 const char *in_interface
,
36 const union in_addr_union
*source
,
37 unsigned source_prefixlen
,
38 const char *out_interface
,
39 const union in_addr_union
*destination
,
40 unsigned destination_prefixlen
) {
44 if (out_interface
&& !ifname_valid(out_interface
))
46 if (in_interface
&& !ifname_valid(in_interface
))
49 entry
->ip
.proto
= protocol
;
54 l
= strlen(in_interface
);
55 assert(l
< sizeof entry
->ip
.iniface
);
56 assert(l
< sizeof entry
->ip
.iniface_mask
);
58 strcpy(entry
->ip
.iniface
, in_interface
);
59 memset(entry
->ip
.iniface_mask
, 0xFF, l
+ 1);
62 entry
->ip
.src
= source
->in
;
63 in4_addr_prefixlen_to_netmask(&entry
->ip
.smsk
, source_prefixlen
);
67 size_t l
= strlen(out_interface
);
68 assert(l
< sizeof entry
->ip
.outiface
);
69 assert(l
< sizeof entry
->ip
.outiface_mask
);
71 strcpy(entry
->ip
.outiface
, out_interface
);
72 memset(entry
->ip
.outiface_mask
, 0xFF, l
+ 1);
75 entry
->ip
.dst
= destination
->in
;
76 in4_addr_prefixlen_to_netmask(&entry
->ip
.dmsk
, destination_prefixlen
);
82 int fw_iptables_add_masquerade(
85 const union in_addr_union
*source
,
86 unsigned source_prefixlen
) {
88 static const xt_chainlabel chain
= "POSTROUTING";
89 _cleanup_(iptc_freep
) struct xtc_handle
*h
= NULL
;
90 struct ipt_entry
*entry
, *mask
;
91 struct ipt_entry_target
*t
;
93 struct nf_nat_ipv4_multi_range_compat
*mr
;
95 const char *out_interface
= NULL
;
96 const union in_addr_union
*destination
= NULL
;
97 unsigned destination_prefixlen
= 0;
102 if (!source
|| source_prefixlen
== 0)
105 h
= iptc_init("nat");
109 sz
= XT_ALIGN(sizeof(struct ipt_entry
)) +
110 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
111 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
113 /* Put together the entry we want to add or remove */
115 entry
->next_offset
= sz
;
116 entry
->target_offset
= XT_ALIGN(sizeof(struct ipt_entry
));
117 r
= entry_fill_basics(entry
, protocol
, NULL
, source
, source_prefixlen
, out_interface
, destination
, destination_prefixlen
);
121 /* Fill in target part */
122 t
= ipt_get_target(entry
);
124 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
125 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
126 strncpy(t
->u
.user
.name
, "MASQUERADE", sizeof(t
->u
.user
.name
));
127 mr
= (struct nf_nat_ipv4_multi_range_compat
*) t
->data
;
130 /* Create a search mask entry */
132 memset(mask
, 0xFF, sz
);
135 if (iptc_check_entry(chain
, entry
, (unsigned char*) mask
, h
))
137 if (errno
!= ENOENT
) /* if other error than not existing yet, fail */
140 if (!iptc_insert_entry(chain
, entry
, 0, h
))
143 if (!iptc_delete_entry(chain
, entry
, (unsigned char*) mask
, h
)) {
144 if (errno
== ENOENT
) /* if it's already gone, all is good! */
157 int fw_iptables_add_local_dnat(
162 const union in_addr_union
*remote
,
163 uint16_t remote_port
,
164 const union in_addr_union
*previous_remote
) {
166 static const xt_chainlabel chain_pre
= "PREROUTING", chain_output
= "OUTPUT";
167 _cleanup_(iptc_freep
) struct xtc_handle
*h
= NULL
;
168 struct ipt_entry
*entry
, *mask
;
169 struct ipt_entry_target
*t
;
170 struct ipt_entry_match
*m
;
171 struct xt_addrtype_info_v1
*at
;
172 struct nf_nat_ipv4_multi_range_compat
*mr
;
175 const char *in_interface
= NULL
;
176 const union in_addr_union
*source
= NULL
;
177 unsigned source_prefixlen
= 0;
178 const union in_addr_union
*destination
= NULL
;
179 unsigned destination_prefixlen
= 0;
181 assert(add
|| !previous_remote
);
186 if (!IN_SET(protocol
, IPPROTO_TCP
, IPPROTO_UDP
))
192 if (remote_port
<= 0)
195 h
= iptc_init("nat");
199 sz
= XT_ALIGN(sizeof(struct ipt_entry
)) +
200 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
201 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
)) +
202 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
203 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
205 if (protocol
== IPPROTO_TCP
)
206 msz
= XT_ALIGN(sizeof(struct ipt_entry_match
)) +
207 XT_ALIGN(sizeof(struct xt_tcp
));
209 msz
= XT_ALIGN(sizeof(struct ipt_entry_match
)) +
210 XT_ALIGN(sizeof(struct xt_udp
));
214 /* Fill in basic part */
216 entry
->next_offset
= sz
;
217 entry
->target_offset
=
218 XT_ALIGN(sizeof(struct ipt_entry
)) +
219 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
220 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
)) +
222 r
= entry_fill_basics(entry
, protocol
, in_interface
, source
, source_prefixlen
, NULL
, destination
, destination_prefixlen
);
226 /* Fill in first match */
227 m
= (struct ipt_entry_match
*) ((uint8_t*) entry
+ XT_ALIGN(sizeof(struct ipt_entry
)));
228 m
->u
.match_size
= msz
;
229 if (protocol
== IPPROTO_TCP
) {
232 strncpy(m
->u
.user
.name
, "tcp", sizeof(m
->u
.user
.name
));
233 tcp
= (struct xt_tcp
*) m
->data
;
234 tcp
->dpts
[0] = tcp
->dpts
[1] = local_port
;
236 tcp
->spts
[1] = 0xFFFF;
241 strncpy(m
->u
.user
.name
, "udp", sizeof(m
->u
.user
.name
));
242 udp
= (struct xt_udp
*) m
->data
;
243 udp
->dpts
[0] = udp
->dpts
[1] = local_port
;
245 udp
->spts
[1] = 0xFFFF;
248 /* Fill in second match */
249 m
= (struct ipt_entry_match
*) ((uint8_t*) entry
+ XT_ALIGN(sizeof(struct ipt_entry
)) + msz
);
251 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
252 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
));
253 strncpy(m
->u
.user
.name
, "addrtype", sizeof(m
->u
.user
.name
));
254 m
->u
.user
.revision
= 1;
255 at
= (struct xt_addrtype_info_v1
*) m
->data
;
256 at
->dest
= XT_ADDRTYPE_LOCAL
;
258 /* Fill in target part */
259 t
= ipt_get_target(entry
);
261 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
262 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
263 strncpy(t
->u
.user
.name
, "DNAT", sizeof(t
->u
.user
.name
));
264 mr
= (struct nf_nat_ipv4_multi_range_compat
*) t
->data
;
266 mr
->range
[0].flags
= NF_NAT_RANGE_PROTO_SPECIFIED
|NF_NAT_RANGE_MAP_IPS
;
267 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= remote
->in
.s_addr
;
268 if (protocol
== IPPROTO_TCP
)
269 mr
->range
[0].min
.tcp
.port
= mr
->range
[0].max
.tcp
.port
= htobe16(remote_port
);
271 mr
->range
[0].min
.udp
.port
= mr
->range
[0].max
.udp
.port
= htobe16(remote_port
);
274 memset(mask
, 0xFF, sz
);
277 /* Add the PREROUTING rule, if it is missing so far */
278 if (!iptc_check_entry(chain_pre
, entry
, (unsigned char*) mask
, h
)) {
282 if (!iptc_insert_entry(chain_pre
, entry
, 0, h
))
286 /* If a previous remote is set, remove its entry */
287 if (previous_remote
&& previous_remote
->in
.s_addr
!= remote
->in
.s_addr
) {
288 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= previous_remote
->in
.s_addr
;
290 if (!iptc_delete_entry(chain_pre
, entry
, (unsigned char*) mask
, h
)) {
295 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= remote
->in
.s_addr
;
298 /* Add the OUTPUT rule, if it is missing so far */
301 /* Don't apply onto loopback addresses */
303 entry
->ip
.dst
.s_addr
= htobe32(0x7F000000);
304 entry
->ip
.dmsk
.s_addr
= htobe32(0xFF000000);
305 entry
->ip
.invflags
= IPT_INV_DSTIP
;
308 if (!iptc_check_entry(chain_output
, entry
, (unsigned char*) mask
, h
)) {
312 if (!iptc_insert_entry(chain_output
, entry
, 0, h
))
316 /* If a previous remote is set, remove its entry */
317 if (previous_remote
&& previous_remote
->in
.s_addr
!= remote
->in
.s_addr
) {
318 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= previous_remote
->in
.s_addr
;
320 if (!iptc_delete_entry(chain_output
, entry
, (unsigned char*) mask
, h
)) {
327 if (!iptc_delete_entry(chain_pre
, entry
, (unsigned char*) mask
, h
)) {
334 entry
->ip
.dst
.s_addr
= htobe32(0x7F000000);
335 entry
->ip
.dmsk
.s_addr
= htobe32(0xFF000000);
336 entry
->ip
.invflags
= IPT_INV_DSTIP
;
339 if (!iptc_delete_entry(chain_output
, entry
, (unsigned char*) mask
, h
)) {