1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2015 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 /* Temporary work-around for broken glibc vs. linux kernel header definitions
22 * This is already fixed upstream, remove this when distributions have updated.
27 #include <arpa/inet.h>
32 #include <sys/socket.h>
38 #include <linux/netfilter_ipv4/ip_tables.h>
39 #include <linux/netfilter/nf_nat.h>
40 #include <linux/netfilter/xt_addrtype.h>
41 #include <libiptc/libiptc.h>
43 #include "alloc-util.h"
44 #include "firewall-util.h"
45 #include "in-addr-util.h"
47 #include "socket-util.h"
49 DEFINE_TRIVIAL_CLEANUP_FUNC(struct xtc_handle
*, iptc_free
);
51 static int entry_fill_basics(
52 struct ipt_entry
*entry
,
54 const char *in_interface
,
55 const union in_addr_union
*source
,
56 unsigned source_prefixlen
,
57 const char *out_interface
,
58 const union in_addr_union
*destination
,
59 unsigned destination_prefixlen
) {
63 if (out_interface
&& !ifname_valid(out_interface
))
65 if (in_interface
&& !ifname_valid(in_interface
))
68 entry
->ip
.proto
= protocol
;
71 strcpy(entry
->ip
.iniface
, in_interface
);
72 memset(entry
->ip
.iniface_mask
, 0xFF, strlen(in_interface
)+1);
75 entry
->ip
.src
= source
->in
;
76 in4_addr_prefixlen_to_netmask(&entry
->ip
.smsk
, source_prefixlen
);
80 size_t l
= strlen(out_interface
);
81 assert(l
< sizeof entry
->ip
.outiface
);
82 assert(l
< sizeof entry
->ip
.outiface_mask
);
84 strcpy(entry
->ip
.outiface
, out_interface
);
85 memset(entry
->ip
.outiface_mask
, 0xFF, l
+ 1);
88 entry
->ip
.dst
= destination
->in
;
89 in4_addr_prefixlen_to_netmask(&entry
->ip
.dmsk
, destination_prefixlen
);
95 int fw_add_masquerade(
99 const union in_addr_union
*source
,
100 unsigned source_prefixlen
,
101 const char *out_interface
,
102 const union in_addr_union
*destination
,
103 unsigned destination_prefixlen
) {
105 _cleanup_(iptc_freep
) struct xtc_handle
*h
= NULL
;
106 struct ipt_entry
*entry
, *mask
;
107 struct ipt_entry_target
*t
;
109 struct nf_nat_ipv4_multi_range_compat
*mr
;
115 if (!IN_SET(protocol
, 0, IPPROTO_TCP
, IPPROTO_UDP
))
118 h
= iptc_init("nat");
122 sz
= XT_ALIGN(sizeof(struct ipt_entry
)) +
123 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
124 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
126 /* Put together the entry we want to add or remove */
128 entry
->next_offset
= sz
;
129 entry
->target_offset
= XT_ALIGN(sizeof(struct ipt_entry
));
130 r
= entry_fill_basics(entry
, protocol
, NULL
, source
, source_prefixlen
, out_interface
, destination
, destination_prefixlen
);
134 /* Fill in target part */
135 t
= ipt_get_target(entry
);
137 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
138 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
139 strncpy(t
->u
.user
.name
, "MASQUERADE", sizeof(t
->u
.user
.name
));
140 mr
= (struct nf_nat_ipv4_multi_range_compat
*) t
->data
;
143 /* Create a search mask entry */
145 memset(mask
, 0xFF, sz
);
148 if (iptc_check_entry("POSTROUTING", entry
, (unsigned char*) mask
, h
))
150 if (errno
!= ENOENT
) /* if other error than not existing yet, fail */
153 if (!iptc_insert_entry("POSTROUTING", entry
, 0, h
))
156 if (!iptc_delete_entry("POSTROUTING", entry
, (unsigned char*) mask
, h
)) {
157 if (errno
== ENOENT
) /* if it's already gone, all is good! */
170 int fw_add_local_dnat(
174 const char *in_interface
,
175 const union in_addr_union
*source
,
176 unsigned source_prefixlen
,
177 const union in_addr_union
*destination
,
178 unsigned destination_prefixlen
,
180 const union in_addr_union
*remote
,
181 uint16_t remote_port
,
182 const union in_addr_union
*previous_remote
) {
185 _cleanup_(iptc_freep
) struct xtc_handle
*h
= NULL
;
186 struct ipt_entry
*entry
, *mask
;
187 struct ipt_entry_target
*t
;
188 struct ipt_entry_match
*m
;
189 struct xt_addrtype_info_v1
*at
;
190 struct nf_nat_ipv4_multi_range_compat
*mr
;
194 assert(add
|| !previous_remote
);
199 if (!IN_SET(protocol
, IPPROTO_TCP
, IPPROTO_UDP
))
205 if (remote_port
<= 0)
208 h
= iptc_init("nat");
212 sz
= XT_ALIGN(sizeof(struct ipt_entry
)) +
213 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
214 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
)) +
215 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
216 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
218 if (protocol
== IPPROTO_TCP
)
219 msz
= XT_ALIGN(sizeof(struct ipt_entry_match
)) +
220 XT_ALIGN(sizeof(struct xt_tcp
));
222 msz
= XT_ALIGN(sizeof(struct ipt_entry_match
)) +
223 XT_ALIGN(sizeof(struct xt_udp
));
227 /* Fill in basic part */
229 entry
->next_offset
= sz
;
230 entry
->target_offset
=
231 XT_ALIGN(sizeof(struct ipt_entry
)) +
232 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
233 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
)) +
235 r
= entry_fill_basics(entry
, protocol
, in_interface
, source
, source_prefixlen
, NULL
, destination
, destination_prefixlen
);
239 /* Fill in first match */
240 m
= (struct ipt_entry_match
*) ((uint8_t*) entry
+ XT_ALIGN(sizeof(struct ipt_entry
)));
241 m
->u
.match_size
= msz
;
242 if (protocol
== IPPROTO_TCP
) {
245 strncpy(m
->u
.user
.name
, "tcp", sizeof(m
->u
.user
.name
));
246 tcp
= (struct xt_tcp
*) m
->data
;
247 tcp
->dpts
[0] = tcp
->dpts
[1] = local_port
;
249 tcp
->spts
[1] = 0xFFFF;
254 strncpy(m
->u
.user
.name
, "udp", sizeof(m
->u
.user
.name
));
255 udp
= (struct xt_udp
*) m
->data
;
256 udp
->dpts
[0] = udp
->dpts
[1] = local_port
;
258 udp
->spts
[1] = 0xFFFF;
261 /* Fill in second match */
262 m
= (struct ipt_entry_match
*) ((uint8_t*) entry
+ XT_ALIGN(sizeof(struct ipt_entry
)) + msz
);
264 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
265 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
));
266 strncpy(m
->u
.user
.name
, "addrtype", sizeof(m
->u
.user
.name
));
267 m
->u
.user
.revision
= 1;
268 at
= (struct xt_addrtype_info_v1
*) m
->data
;
269 at
->dest
= XT_ADDRTYPE_LOCAL
;
271 /* Fill in target part */
272 t
= ipt_get_target(entry
);
274 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
275 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
276 strncpy(t
->u
.user
.name
, "DNAT", sizeof(t
->u
.user
.name
));
277 mr
= (struct nf_nat_ipv4_multi_range_compat
*) t
->data
;
279 mr
->range
[0].flags
= NF_NAT_RANGE_PROTO_SPECIFIED
|NF_NAT_RANGE_MAP_IPS
;
280 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= remote
->in
.s_addr
;
281 if (protocol
== IPPROTO_TCP
)
282 mr
->range
[0].min
.tcp
.port
= mr
->range
[0].max
.tcp
.port
= htons(remote_port
);
284 mr
->range
[0].min
.udp
.port
= mr
->range
[0].max
.udp
.port
= htons(remote_port
);
287 memset(mask
, 0xFF, sz
);
290 /* Add the PREROUTING rule, if it is missing so far */
291 if (!iptc_check_entry("PREROUTING", entry
, (unsigned char*) mask
, h
)) {
295 if (!iptc_insert_entry("PREROUTING", entry
, 0, h
))
299 /* If a previous remote is set, remove its entry */
300 if (previous_remote
&& previous_remote
->in
.s_addr
!= remote
->in
.s_addr
) {
301 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= previous_remote
->in
.s_addr
;
303 if (!iptc_delete_entry("PREROUTING", entry
, (unsigned char*) mask
, h
)) {
308 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= remote
->in
.s_addr
;
311 /* Add the OUTPUT rule, if it is missing so far */
314 /* Don't apply onto loopback addresses */
316 entry
->ip
.dst
.s_addr
= htobe32(0x7F000000);
317 entry
->ip
.dmsk
.s_addr
= htobe32(0xFF000000);
318 entry
->ip
.invflags
= IPT_INV_DSTIP
;
321 if (!iptc_check_entry("OUTPUT", entry
, (unsigned char*) mask
, h
)) {
325 if (!iptc_insert_entry("OUTPUT", entry
, 0, h
))
329 /* If a previous remote is set, remove its entry */
330 if (previous_remote
&& previous_remote
->in
.s_addr
!= remote
->in
.s_addr
) {
331 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= previous_remote
->in
.s_addr
;
333 if (!iptc_delete_entry("OUTPUT", entry
, (unsigned char*) mask
, h
)) {
340 if (!iptc_delete_entry("PREROUTING", entry
, (unsigned char*) mask
, h
)) {
347 entry
->ip
.dst
.s_addr
= htobe32(0x7F000000);
348 entry
->ip
.dmsk
.s_addr
= htobe32(0xFF000000);
349 entry
->ip
.invflags
= IPT_INV_DSTIP
;
352 if (!iptc_delete_entry("OUTPUT", entry
, (unsigned char*) mask
, h
)) {