1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 /* Temporary work-around for broken glibc vs. linux kernel header definitions
4 * This is already fixed upstream, remove this when distributions have updated.
13 #include <sys/socket.h>
19 #include <linux/netfilter_ipv4/ip_tables.h>
20 #include <linux/netfilter/nf_nat.h>
21 #include <linux/netfilter/xt_addrtype.h>
22 #include <libiptc/libiptc.h>
24 #include "alloc-util.h"
25 #include "firewall-util.h"
26 #include "in-addr-util.h"
28 #include "socket-util.h"
30 DEFINE_TRIVIAL_CLEANUP_FUNC(struct xtc_handle
*, iptc_free
);
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_add_masquerade(
86 const union in_addr_union
*source
,
87 unsigned source_prefixlen
,
88 const char *out_interface
,
89 const union in_addr_union
*destination
,
90 unsigned destination_prefixlen
) {
92 static const xt_chainlabel chain
= "POSTROUTING";
93 _cleanup_(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
;
103 if (!IN_SET(protocol
, 0, IPPROTO_TCP
, IPPROTO_UDP
))
106 h
= iptc_init("nat");
110 sz
= XT_ALIGN(sizeof(struct ipt_entry
)) +
111 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
112 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
114 /* Put together the entry we want to add or remove */
116 entry
->next_offset
= sz
;
117 entry
->target_offset
= XT_ALIGN(sizeof(struct ipt_entry
));
118 r
= entry_fill_basics(entry
, protocol
, NULL
, source
, source_prefixlen
, out_interface
, destination
, destination_prefixlen
);
122 /* Fill in target part */
123 t
= ipt_get_target(entry
);
125 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
126 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
127 strncpy(t
->u
.user
.name
, "MASQUERADE", sizeof(t
->u
.user
.name
));
128 mr
= (struct nf_nat_ipv4_multi_range_compat
*) t
->data
;
131 /* Create a search mask entry */
133 memset(mask
, 0xFF, sz
);
136 if (iptc_check_entry(chain
, entry
, (unsigned char*) mask
, h
))
138 if (errno
!= ENOENT
) /* if other error than not existing yet, fail */
141 if (!iptc_insert_entry(chain
, entry
, 0, h
))
144 if (!iptc_delete_entry(chain
, entry
, (unsigned char*) mask
, h
)) {
145 if (errno
== ENOENT
) /* if it's already gone, all is good! */
158 int fw_add_local_dnat(
162 const char *in_interface
,
163 const union in_addr_union
*source
,
164 unsigned source_prefixlen
,
165 const union in_addr_union
*destination
,
166 unsigned destination_prefixlen
,
168 const union in_addr_union
*remote
,
169 uint16_t remote_port
,
170 const union in_addr_union
*previous_remote
) {
172 static const xt_chainlabel chain_pre
= "PREROUTING", chain_output
= "OUTPUT";
173 _cleanup_(iptc_freep
) struct xtc_handle
*h
= NULL
;
174 struct ipt_entry
*entry
, *mask
;
175 struct ipt_entry_target
*t
;
176 struct ipt_entry_match
*m
;
177 struct xt_addrtype_info_v1
*at
;
178 struct nf_nat_ipv4_multi_range_compat
*mr
;
182 assert(add
|| !previous_remote
);
187 if (!IN_SET(protocol
, IPPROTO_TCP
, IPPROTO_UDP
))
193 if (remote_port
<= 0)
196 h
= iptc_init("nat");
200 sz
= XT_ALIGN(sizeof(struct ipt_entry
)) +
201 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
202 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
)) +
203 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
204 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
206 if (protocol
== IPPROTO_TCP
)
207 msz
= XT_ALIGN(sizeof(struct ipt_entry_match
)) +
208 XT_ALIGN(sizeof(struct xt_tcp
));
210 msz
= XT_ALIGN(sizeof(struct ipt_entry_match
)) +
211 XT_ALIGN(sizeof(struct xt_udp
));
215 /* Fill in basic part */
217 entry
->next_offset
= sz
;
218 entry
->target_offset
=
219 XT_ALIGN(sizeof(struct ipt_entry
)) +
220 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
221 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
)) +
223 r
= entry_fill_basics(entry
, protocol
, in_interface
, source
, source_prefixlen
, NULL
, destination
, destination_prefixlen
);
227 /* Fill in first match */
228 m
= (struct ipt_entry_match
*) ((uint8_t*) entry
+ XT_ALIGN(sizeof(struct ipt_entry
)));
229 m
->u
.match_size
= msz
;
230 if (protocol
== IPPROTO_TCP
) {
233 strncpy(m
->u
.user
.name
, "tcp", sizeof(m
->u
.user
.name
));
234 tcp
= (struct xt_tcp
*) m
->data
;
235 tcp
->dpts
[0] = tcp
->dpts
[1] = local_port
;
237 tcp
->spts
[1] = 0xFFFF;
242 strncpy(m
->u
.user
.name
, "udp", sizeof(m
->u
.user
.name
));
243 udp
= (struct xt_udp
*) m
->data
;
244 udp
->dpts
[0] = udp
->dpts
[1] = local_port
;
246 udp
->spts
[1] = 0xFFFF;
249 /* Fill in second match */
250 m
= (struct ipt_entry_match
*) ((uint8_t*) entry
+ XT_ALIGN(sizeof(struct ipt_entry
)) + msz
);
252 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
253 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
));
254 strncpy(m
->u
.user
.name
, "addrtype", sizeof(m
->u
.user
.name
));
255 m
->u
.user
.revision
= 1;
256 at
= (struct xt_addrtype_info_v1
*) m
->data
;
257 at
->dest
= XT_ADDRTYPE_LOCAL
;
259 /* Fill in target part */
260 t
= ipt_get_target(entry
);
262 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
263 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
264 strncpy(t
->u
.user
.name
, "DNAT", sizeof(t
->u
.user
.name
));
265 mr
= (struct nf_nat_ipv4_multi_range_compat
*) t
->data
;
267 mr
->range
[0].flags
= NF_NAT_RANGE_PROTO_SPECIFIED
|NF_NAT_RANGE_MAP_IPS
;
268 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= remote
->in
.s_addr
;
269 if (protocol
== IPPROTO_TCP
)
270 mr
->range
[0].min
.tcp
.port
= mr
->range
[0].max
.tcp
.port
= htobe16(remote_port
);
272 mr
->range
[0].min
.udp
.port
= mr
->range
[0].max
.udp
.port
= htobe16(remote_port
);
275 memset(mask
, 0xFF, sz
);
278 /* Add the PREROUTING rule, if it is missing so far */
279 if (!iptc_check_entry(chain_pre
, entry
, (unsigned char*) mask
, h
)) {
283 if (!iptc_insert_entry(chain_pre
, entry
, 0, h
))
287 /* If a previous remote is set, remove its entry */
288 if (previous_remote
&& previous_remote
->in
.s_addr
!= remote
->in
.s_addr
) {
289 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= previous_remote
->in
.s_addr
;
291 if (!iptc_delete_entry(chain_pre
, entry
, (unsigned char*) mask
, h
)) {
296 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= remote
->in
.s_addr
;
299 /* Add the OUTPUT rule, if it is missing so far */
302 /* Don't apply onto loopback addresses */
304 entry
->ip
.dst
.s_addr
= htobe32(0x7F000000);
305 entry
->ip
.dmsk
.s_addr
= htobe32(0xFF000000);
306 entry
->ip
.invflags
= IPT_INV_DSTIP
;
309 if (!iptc_check_entry(chain_output
, entry
, (unsigned char*) mask
, h
)) {
313 if (!iptc_insert_entry(chain_output
, entry
, 0, h
))
317 /* If a previous remote is set, remove its entry */
318 if (previous_remote
&& previous_remote
->in
.s_addr
!= remote
->in
.s_addr
) {
319 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= previous_remote
->in
.s_addr
;
321 if (!iptc_delete_entry(chain_output
, entry
, (unsigned char*) mask
, h
)) {
328 if (!iptc_delete_entry(chain_pre
, entry
, (unsigned char*) mask
, h
)) {
335 entry
->ip
.dst
.s_addr
= htobe32(0x7F000000);
336 entry
->ip
.dmsk
.s_addr
= htobe32(0xFF000000);
337 entry
->ip
.invflags
= IPT_INV_DSTIP
;
340 if (!iptc_delete_entry(chain_output
, entry
, (unsigned char*) mask
, h
)) {