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 strcpy(entry
->ip
.outiface
, out_interface
);
80 memset(entry
->ip
.outiface_mask
, 0xFF, strlen(out_interface
)+1);
83 entry
->ip
.dst
= destination
->in
;
84 in_addr_prefixlen_to_netmask(&entry
->ip
.dmsk
, destination_prefixlen
);
90 int fw_add_masquerade(
94 const union in_addr_union
*source
,
95 unsigned source_prefixlen
,
96 const char *out_interface
,
97 const union in_addr_union
*destination
,
98 unsigned destination_prefixlen
) {
100 _cleanup_(iptc_freep
) struct xtc_handle
*h
= NULL
;
101 struct ipt_entry
*entry
, *mask
;
102 struct ipt_entry_target
*t
;
104 struct nf_nat_ipv4_multi_range_compat
*mr
;
110 if (protocol
!= 0 && protocol
!= IPPROTO_TCP
&& protocol
!= IPPROTO_UDP
)
113 h
= iptc_init("nat");
117 sz
= XT_ALIGN(sizeof(struct ipt_entry
)) +
118 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
119 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
121 /* Put together the entry we want to add or remove */
123 entry
->next_offset
= sz
;
124 entry
->target_offset
= XT_ALIGN(sizeof(struct ipt_entry
));
125 r
= entry_fill_basics(entry
, protocol
, NULL
, source
, source_prefixlen
, out_interface
, destination
, destination_prefixlen
);
129 /* Fill in target part */
130 t
= ipt_get_target(entry
);
132 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
133 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
134 strncpy(t
->u
.user
.name
, "MASQUERADE", sizeof(t
->u
.user
.name
));
135 mr
= (struct nf_nat_ipv4_multi_range_compat
*) t
->data
;
138 /* Create a search mask entry */
140 memset(mask
, 0xFF, sz
);
143 if (iptc_check_entry("POSTROUTING", entry
, (unsigned char*) mask
, h
))
145 if (errno
!= ENOENT
) /* if other error than not existing yet, fail */
148 if (!iptc_insert_entry("POSTROUTING", entry
, 0, h
))
151 if (!iptc_delete_entry("POSTROUTING", entry
, (unsigned char*) mask
, h
)) {
152 if (errno
== ENOENT
) /* if it's already gone, all is good! */
165 int fw_add_local_dnat(
169 const char *in_interface
,
170 const union in_addr_union
*source
,
171 unsigned source_prefixlen
,
172 const union in_addr_union
*destination
,
173 unsigned destination_prefixlen
,
175 const union in_addr_union
*remote
,
176 uint16_t remote_port
,
177 const union in_addr_union
*previous_remote
) {
180 _cleanup_(iptc_freep
) struct xtc_handle
*h
= NULL
;
181 struct ipt_entry
*entry
, *mask
;
182 struct ipt_entry_target
*t
;
183 struct ipt_entry_match
*m
;
184 struct xt_addrtype_info_v1
*at
;
185 struct nf_nat_ipv4_multi_range_compat
*mr
;
189 assert(add
|| !previous_remote
);
194 if (protocol
!= IPPROTO_TCP
&& protocol
!= IPPROTO_UDP
)
200 if (remote_port
<= 0)
203 h
= iptc_init("nat");
207 sz
= XT_ALIGN(sizeof(struct ipt_entry
)) +
208 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
209 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
)) +
210 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
211 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
213 if (protocol
== IPPROTO_TCP
)
214 msz
= XT_ALIGN(sizeof(struct ipt_entry_match
)) +
215 XT_ALIGN(sizeof(struct xt_tcp
));
217 msz
= XT_ALIGN(sizeof(struct ipt_entry_match
)) +
218 XT_ALIGN(sizeof(struct xt_udp
));
222 /* Fill in basic part */
224 entry
->next_offset
= sz
;
225 entry
->target_offset
=
226 XT_ALIGN(sizeof(struct ipt_entry
)) +
227 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
228 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
)) +
230 r
= entry_fill_basics(entry
, protocol
, in_interface
, source
, source_prefixlen
, NULL
, destination
, destination_prefixlen
);
234 /* Fill in first match */
235 m
= (struct ipt_entry_match
*) ((uint8_t*) entry
+ XT_ALIGN(sizeof(struct ipt_entry
)));
236 m
->u
.match_size
= msz
;
237 if (protocol
== IPPROTO_TCP
) {
240 strncpy(m
->u
.user
.name
, "tcp", sizeof(m
->u
.user
.name
));
241 tcp
= (struct xt_tcp
*) m
->data
;
242 tcp
->dpts
[0] = tcp
->dpts
[1] = local_port
;
244 tcp
->spts
[1] = 0xFFFF;
249 strncpy(m
->u
.user
.name
, "udp", sizeof(m
->u
.user
.name
));
250 udp
= (struct xt_udp
*) m
->data
;
251 udp
->dpts
[0] = udp
->dpts
[1] = local_port
;
253 udp
->spts
[1] = 0xFFFF;
256 /* Fill in second match */
257 m
= (struct ipt_entry_match
*) ((uint8_t*) entry
+ XT_ALIGN(sizeof(struct ipt_entry
)) + msz
);
259 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
260 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
));
261 strncpy(m
->u
.user
.name
, "addrtype", sizeof(m
->u
.user
.name
));
262 m
->u
.user
.revision
= 1;
263 at
= (struct xt_addrtype_info_v1
*) m
->data
;
264 at
->dest
= XT_ADDRTYPE_LOCAL
;
266 /* Fill in target part */
267 t
= ipt_get_target(entry
);
269 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
270 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
271 strncpy(t
->u
.user
.name
, "DNAT", sizeof(t
->u
.user
.name
));
272 mr
= (struct nf_nat_ipv4_multi_range_compat
*) t
->data
;
274 mr
->range
[0].flags
= NF_NAT_RANGE_PROTO_SPECIFIED
|NF_NAT_RANGE_MAP_IPS
;
275 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= remote
->in
.s_addr
;
276 if (protocol
== IPPROTO_TCP
)
277 mr
->range
[0].min
.tcp
.port
= mr
->range
[0].max
.tcp
.port
= htons(remote_port
);
279 mr
->range
[0].min
.udp
.port
= mr
->range
[0].max
.udp
.port
= htons(remote_port
);
282 memset(mask
, 0xFF, sz
);
285 /* Add the PREROUTING rule, if it is missing so far */
286 if (!iptc_check_entry("PREROUTING", entry
, (unsigned char*) mask
, h
)) {
290 if (!iptc_insert_entry("PREROUTING", entry
, 0, h
))
294 /* If a previous remote is set, remove its entry */
295 if (previous_remote
&& previous_remote
->in
.s_addr
!= remote
->in
.s_addr
) {
296 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= previous_remote
->in
.s_addr
;
298 if (!iptc_delete_entry("PREROUTING", entry
, (unsigned char*) mask
, h
)) {
303 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= remote
->in
.s_addr
;
306 /* Add the OUTPUT rule, if it is missing so far */
309 /* Don't apply onto loopback addresses */
311 entry
->ip
.dst
.s_addr
= htobe32(0x7F000000);
312 entry
->ip
.dmsk
.s_addr
= htobe32(0xFF000000);
313 entry
->ip
.invflags
= IPT_INV_DSTIP
;
316 if (!iptc_check_entry("OUTPUT", entry
, (unsigned char*) mask
, h
)) {
320 if (!iptc_insert_entry("OUTPUT", entry
, 0, h
))
324 /* If a previous remote is set, remove its entry */
325 if (previous_remote
&& previous_remote
->in
.s_addr
!= remote
->in
.s_addr
) {
326 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= previous_remote
->in
.s_addr
;
328 if (!iptc_delete_entry("OUTPUT", entry
, (unsigned char*) mask
, h
)) {
335 if (!iptc_delete_entry("PREROUTING", entry
, (unsigned char*) mask
, h
)) {
342 entry
->ip
.dst
.s_addr
= htobe32(0x7F000000);
343 entry
->ip
.dmsk
.s_addr
= htobe32(0xFF000000);
344 entry
->ip
.invflags
= IPT_INV_DSTIP
;
347 if (!iptc_delete_entry("OUTPUT", entry
, (unsigned char*) mask
, h
)) {