2 This file is part of systemd.
4 Copyright 2015 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 /* Temporary work-around for broken glibc vs. linux kernel header definitions
21 * This is already fixed upstream, remove this when distributions have updated.
26 #include <arpa/inet.h>
31 #include <sys/socket.h>
37 #include <linux/netfilter_ipv4/ip_tables.h>
38 #include <linux/netfilter/nf_nat.h>
39 #include <linux/netfilter/xt_addrtype.h>
40 #include <libiptc/libiptc.h>
42 #include "alloc-util.h"
43 #include "firewall-util.h"
44 #include "in-addr-util.h"
46 #include "socket-util.h"
48 DEFINE_TRIVIAL_CLEANUP_FUNC(struct xtc_handle
*, iptc_free
);
50 static int entry_fill_basics(
51 struct ipt_entry
*entry
,
53 const char *in_interface
,
54 const union in_addr_union
*source
,
55 unsigned source_prefixlen
,
56 const char *out_interface
,
57 const union in_addr_union
*destination
,
58 unsigned destination_prefixlen
) {
62 if (out_interface
&& !ifname_valid(out_interface
))
64 if (in_interface
&& !ifname_valid(in_interface
))
67 entry
->ip
.proto
= protocol
;
70 strcpy(entry
->ip
.iniface
, in_interface
);
71 memset(entry
->ip
.iniface_mask
, 0xFF, strlen(in_interface
)+1);
74 entry
->ip
.src
= source
->in
;
75 in_addr_prefixlen_to_netmask(&entry
->ip
.smsk
, source_prefixlen
);
79 size_t l
= strlen(out_interface
);
80 assert(l
< sizeof entry
->ip
.outiface
&& l
< sizeof entry
->ip
.outiface_mask
);
82 strcpy(entry
->ip
.outiface
, out_interface
);
83 memset(entry
->ip
.outiface_mask
, 0xFF, l
+ 1);
86 entry
->ip
.dst
= destination
->in
;
87 in_addr_prefixlen_to_netmask(&entry
->ip
.dmsk
, destination_prefixlen
);
93 int fw_add_masquerade(
97 const union in_addr_union
*source
,
98 unsigned source_prefixlen
,
99 const char *out_interface
,
100 const union in_addr_union
*destination
,
101 unsigned destination_prefixlen
) {
103 _cleanup_(iptc_freep
) struct xtc_handle
*h
= NULL
;
104 struct ipt_entry
*entry
, *mask
;
105 struct ipt_entry_target
*t
;
107 struct nf_nat_ipv4_multi_range_compat
*mr
;
113 if (protocol
!= 0 && protocol
!= IPPROTO_TCP
&& protocol
!= IPPROTO_UDP
)
116 h
= iptc_init("nat");
120 sz
= XT_ALIGN(sizeof(struct ipt_entry
)) +
121 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
122 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
124 /* Put together the entry we want to add or remove */
126 entry
->next_offset
= sz
;
127 entry
->target_offset
= XT_ALIGN(sizeof(struct ipt_entry
));
128 r
= entry_fill_basics(entry
, protocol
, NULL
, source
, source_prefixlen
, out_interface
, destination
, destination_prefixlen
);
132 /* Fill in target part */
133 t
= ipt_get_target(entry
);
135 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
136 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
137 strncpy(t
->u
.user
.name
, "MASQUERADE", sizeof(t
->u
.user
.name
));
138 mr
= (struct nf_nat_ipv4_multi_range_compat
*) t
->data
;
141 /* Create a search mask entry */
143 memset(mask
, 0xFF, sz
);
146 if (iptc_check_entry("POSTROUTING", entry
, (unsigned char*) mask
, h
))
148 if (errno
!= ENOENT
) /* if other error than not existing yet, fail */
151 if (!iptc_insert_entry("POSTROUTING", entry
, 0, h
))
154 if (!iptc_delete_entry("POSTROUTING", entry
, (unsigned char*) mask
, h
)) {
155 if (errno
== ENOENT
) /* if it's already gone, all is good! */
168 int fw_add_local_dnat(
172 const char *in_interface
,
173 const union in_addr_union
*source
,
174 unsigned source_prefixlen
,
175 const union in_addr_union
*destination
,
176 unsigned destination_prefixlen
,
178 const union in_addr_union
*remote
,
179 uint16_t remote_port
,
180 const union in_addr_union
*previous_remote
) {
183 _cleanup_(iptc_freep
) struct xtc_handle
*h
= NULL
;
184 struct ipt_entry
*entry
, *mask
;
185 struct ipt_entry_target
*t
;
186 struct ipt_entry_match
*m
;
187 struct xt_addrtype_info_v1
*at
;
188 struct nf_nat_ipv4_multi_range_compat
*mr
;
192 assert(add
|| !previous_remote
);
197 if (protocol
!= IPPROTO_TCP
&& protocol
!= IPPROTO_UDP
)
203 if (remote_port
<= 0)
206 h
= iptc_init("nat");
210 sz
= XT_ALIGN(sizeof(struct ipt_entry
)) +
211 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
212 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
)) +
213 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
214 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
216 if (protocol
== IPPROTO_TCP
)
217 msz
= XT_ALIGN(sizeof(struct ipt_entry_match
)) +
218 XT_ALIGN(sizeof(struct xt_tcp
));
220 msz
= XT_ALIGN(sizeof(struct ipt_entry_match
)) +
221 XT_ALIGN(sizeof(struct xt_udp
));
225 /* Fill in basic part */
227 entry
->next_offset
= sz
;
228 entry
->target_offset
=
229 XT_ALIGN(sizeof(struct ipt_entry
)) +
230 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
231 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
)) +
233 r
= entry_fill_basics(entry
, protocol
, in_interface
, source
, source_prefixlen
, NULL
, destination
, destination_prefixlen
);
237 /* Fill in first match */
238 m
= (struct ipt_entry_match
*) ((uint8_t*) entry
+ XT_ALIGN(sizeof(struct ipt_entry
)));
239 m
->u
.match_size
= msz
;
240 if (protocol
== IPPROTO_TCP
) {
243 strncpy(m
->u
.user
.name
, "tcp", sizeof(m
->u
.user
.name
));
244 tcp
= (struct xt_tcp
*) m
->data
;
245 tcp
->dpts
[0] = tcp
->dpts
[1] = local_port
;
247 tcp
->spts
[1] = 0xFFFF;
252 strncpy(m
->u
.user
.name
, "udp", sizeof(m
->u
.user
.name
));
253 udp
= (struct xt_udp
*) m
->data
;
254 udp
->dpts
[0] = udp
->dpts
[1] = local_port
;
256 udp
->spts
[1] = 0xFFFF;
259 /* Fill in second match */
260 m
= (struct ipt_entry_match
*) ((uint8_t*) entry
+ XT_ALIGN(sizeof(struct ipt_entry
)) + msz
);
262 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
263 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
));
264 strncpy(m
->u
.user
.name
, "addrtype", sizeof(m
->u
.user
.name
));
265 m
->u
.user
.revision
= 1;
266 at
= (struct xt_addrtype_info_v1
*) m
->data
;
267 at
->dest
= XT_ADDRTYPE_LOCAL
;
269 /* Fill in target part */
270 t
= ipt_get_target(entry
);
272 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
273 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
274 strncpy(t
->u
.user
.name
, "DNAT", sizeof(t
->u
.user
.name
));
275 mr
= (struct nf_nat_ipv4_multi_range_compat
*) t
->data
;
277 mr
->range
[0].flags
= NF_NAT_RANGE_PROTO_SPECIFIED
|NF_NAT_RANGE_MAP_IPS
;
278 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= remote
->in
.s_addr
;
279 if (protocol
== IPPROTO_TCP
)
280 mr
->range
[0].min
.tcp
.port
= mr
->range
[0].max
.tcp
.port
= htons(remote_port
);
282 mr
->range
[0].min
.udp
.port
= mr
->range
[0].max
.udp
.port
= htons(remote_port
);
285 memset(mask
, 0xFF, sz
);
288 /* Add the PREROUTING rule, if it is missing so far */
289 if (!iptc_check_entry("PREROUTING", entry
, (unsigned char*) mask
, h
)) {
293 if (!iptc_insert_entry("PREROUTING", entry
, 0, h
))
297 /* If a previous remote is set, remove its entry */
298 if (previous_remote
&& previous_remote
->in
.s_addr
!= remote
->in
.s_addr
) {
299 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= previous_remote
->in
.s_addr
;
301 if (!iptc_delete_entry("PREROUTING", entry
, (unsigned char*) mask
, h
)) {
306 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= remote
->in
.s_addr
;
309 /* Add the OUTPUT rule, if it is missing so far */
312 /* Don't apply onto loopback addresses */
314 entry
->ip
.dst
.s_addr
= htobe32(0x7F000000);
315 entry
->ip
.dmsk
.s_addr
= htobe32(0xFF000000);
316 entry
->ip
.invflags
= IPT_INV_DSTIP
;
319 if (!iptc_check_entry("OUTPUT", entry
, (unsigned char*) mask
, h
)) {
323 if (!iptc_insert_entry("OUTPUT", entry
, 0, h
))
327 /* If a previous remote is set, remove its entry */
328 if (previous_remote
&& previous_remote
->in
.s_addr
!= remote
->in
.s_addr
) {
329 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= previous_remote
->in
.s_addr
;
331 if (!iptc_delete_entry("OUTPUT", entry
, (unsigned char*) mask
, h
)) {
338 if (!iptc_delete_entry("PREROUTING", entry
, (unsigned char*) mask
, h
)) {
345 entry
->ip
.dst
.s_addr
= htobe32(0x7F000000);
346 entry
->ip
.dmsk
.s_addr
= htobe32(0xFF000000);
347 entry
->ip
.invflags
= IPT_INV_DSTIP
;
350 if (!iptc_delete_entry("OUTPUT", entry
, (unsigned char*) mask
, h
)) {