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
&& l
< sizeof entry
->ip
.outiface_mask
);
83 strcpy(entry
->ip
.outiface
, out_interface
);
84 memset(entry
->ip
.outiface_mask
, 0xFF, l
+ 1);
87 entry
->ip
.dst
= destination
->in
;
88 in4_addr_prefixlen_to_netmask(&entry
->ip
.dmsk
, destination_prefixlen
);
94 int fw_add_masquerade(
98 const union in_addr_union
*source
,
99 unsigned source_prefixlen
,
100 const char *out_interface
,
101 const union in_addr_union
*destination
,
102 unsigned destination_prefixlen
) {
104 _cleanup_(iptc_freep
) struct xtc_handle
*h
= NULL
;
105 struct ipt_entry
*entry
, *mask
;
106 struct ipt_entry_target
*t
;
108 struct nf_nat_ipv4_multi_range_compat
*mr
;
114 if (!IN_SET(protocol
, 0, IPPROTO_TCP
, IPPROTO_UDP
))
117 h
= iptc_init("nat");
121 sz
= XT_ALIGN(sizeof(struct ipt_entry
)) +
122 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
123 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
125 /* Put together the entry we want to add or remove */
127 entry
->next_offset
= sz
;
128 entry
->target_offset
= XT_ALIGN(sizeof(struct ipt_entry
));
129 r
= entry_fill_basics(entry
, protocol
, NULL
, source
, source_prefixlen
, out_interface
, destination
, destination_prefixlen
);
133 /* Fill in target part */
134 t
= ipt_get_target(entry
);
136 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
137 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
138 strncpy(t
->u
.user
.name
, "MASQUERADE", sizeof(t
->u
.user
.name
));
139 mr
= (struct nf_nat_ipv4_multi_range_compat
*) t
->data
;
142 /* Create a search mask entry */
144 memset(mask
, 0xFF, sz
);
147 if (iptc_check_entry("POSTROUTING", entry
, (unsigned char*) mask
, h
))
149 if (errno
!= ENOENT
) /* if other error than not existing yet, fail */
152 if (!iptc_insert_entry("POSTROUTING", entry
, 0, h
))
155 if (!iptc_delete_entry("POSTROUTING", entry
, (unsigned char*) mask
, h
)) {
156 if (errno
== ENOENT
) /* if it's already gone, all is good! */
169 int fw_add_local_dnat(
173 const char *in_interface
,
174 const union in_addr_union
*source
,
175 unsigned source_prefixlen
,
176 const union in_addr_union
*destination
,
177 unsigned destination_prefixlen
,
179 const union in_addr_union
*remote
,
180 uint16_t remote_port
,
181 const union in_addr_union
*previous_remote
) {
184 _cleanup_(iptc_freep
) struct xtc_handle
*h
= NULL
;
185 struct ipt_entry
*entry
, *mask
;
186 struct ipt_entry_target
*t
;
187 struct ipt_entry_match
*m
;
188 struct xt_addrtype_info_v1
*at
;
189 struct nf_nat_ipv4_multi_range_compat
*mr
;
193 assert(add
|| !previous_remote
);
198 if (!IN_SET(protocol
, IPPROTO_TCP
, IPPROTO_UDP
))
204 if (remote_port
<= 0)
207 h
= iptc_init("nat");
211 sz
= XT_ALIGN(sizeof(struct ipt_entry
)) +
212 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
213 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
)) +
214 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
215 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
217 if (protocol
== IPPROTO_TCP
)
218 msz
= XT_ALIGN(sizeof(struct ipt_entry_match
)) +
219 XT_ALIGN(sizeof(struct xt_tcp
));
221 msz
= XT_ALIGN(sizeof(struct ipt_entry_match
)) +
222 XT_ALIGN(sizeof(struct xt_udp
));
226 /* Fill in basic part */
228 entry
->next_offset
= sz
;
229 entry
->target_offset
=
230 XT_ALIGN(sizeof(struct ipt_entry
)) +
231 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
232 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
)) +
234 r
= entry_fill_basics(entry
, protocol
, in_interface
, source
, source_prefixlen
, NULL
, destination
, destination_prefixlen
);
238 /* Fill in first match */
239 m
= (struct ipt_entry_match
*) ((uint8_t*) entry
+ XT_ALIGN(sizeof(struct ipt_entry
)));
240 m
->u
.match_size
= msz
;
241 if (protocol
== IPPROTO_TCP
) {
244 strncpy(m
->u
.user
.name
, "tcp", sizeof(m
->u
.user
.name
));
245 tcp
= (struct xt_tcp
*) m
->data
;
246 tcp
->dpts
[0] = tcp
->dpts
[1] = local_port
;
248 tcp
->spts
[1] = 0xFFFF;
253 strncpy(m
->u
.user
.name
, "udp", sizeof(m
->u
.user
.name
));
254 udp
= (struct xt_udp
*) m
->data
;
255 udp
->dpts
[0] = udp
->dpts
[1] = local_port
;
257 udp
->spts
[1] = 0xFFFF;
260 /* Fill in second match */
261 m
= (struct ipt_entry_match
*) ((uint8_t*) entry
+ XT_ALIGN(sizeof(struct ipt_entry
)) + msz
);
263 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
264 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
));
265 strncpy(m
->u
.user
.name
, "addrtype", sizeof(m
->u
.user
.name
));
266 m
->u
.user
.revision
= 1;
267 at
= (struct xt_addrtype_info_v1
*) m
->data
;
268 at
->dest
= XT_ADDRTYPE_LOCAL
;
270 /* Fill in target part */
271 t
= ipt_get_target(entry
);
273 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
274 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
275 strncpy(t
->u
.user
.name
, "DNAT", sizeof(t
->u
.user
.name
));
276 mr
= (struct nf_nat_ipv4_multi_range_compat
*) t
->data
;
278 mr
->range
[0].flags
= NF_NAT_RANGE_PROTO_SPECIFIED
|NF_NAT_RANGE_MAP_IPS
;
279 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= remote
->in
.s_addr
;
280 if (protocol
== IPPROTO_TCP
)
281 mr
->range
[0].min
.tcp
.port
= mr
->range
[0].max
.tcp
.port
= htons(remote_port
);
283 mr
->range
[0].min
.udp
.port
= mr
->range
[0].max
.udp
.port
= htons(remote_port
);
286 memset(mask
, 0xFF, sz
);
289 /* Add the PREROUTING rule, if it is missing so far */
290 if (!iptc_check_entry("PREROUTING", entry
, (unsigned char*) mask
, h
)) {
294 if (!iptc_insert_entry("PREROUTING", entry
, 0, h
))
298 /* If a previous remote is set, remove its entry */
299 if (previous_remote
&& previous_remote
->in
.s_addr
!= remote
->in
.s_addr
) {
300 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= previous_remote
->in
.s_addr
;
302 if (!iptc_delete_entry("PREROUTING", entry
, (unsigned char*) mask
, h
)) {
307 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= remote
->in
.s_addr
;
310 /* Add the OUTPUT rule, if it is missing so far */
313 /* Don't apply onto loopback addresses */
315 entry
->ip
.dst
.s_addr
= htobe32(0x7F000000);
316 entry
->ip
.dmsk
.s_addr
= htobe32(0xFF000000);
317 entry
->ip
.invflags
= IPT_INV_DSTIP
;
320 if (!iptc_check_entry("OUTPUT", entry
, (unsigned char*) mask
, h
)) {
324 if (!iptc_insert_entry("OUTPUT", entry
, 0, h
))
328 /* If a previous remote is set, remove its entry */
329 if (previous_remote
&& previous_remote
->in
.s_addr
!= remote
->in
.s_addr
) {
330 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= previous_remote
->in
.s_addr
;
332 if (!iptc_delete_entry("OUTPUT", entry
, (unsigned char*) mask
, h
)) {
339 if (!iptc_delete_entry("PREROUTING", entry
, (unsigned char*) mask
, h
)) {
346 entry
->ip
.dst
.s_addr
= htobe32(0x7F000000);
347 entry
->ip
.dmsk
.s_addr
= htobe32(0xFF000000);
348 entry
->ip
.invflags
= IPT_INV_DSTIP
;
351 if (!iptc_delete_entry("OUTPUT", entry
, (unsigned char*) mask
, h
)) {