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/>.
21 #include <arpa/inet.h>
27 #include <sys/socket.h>
28 #include <linux/netfilter_ipv4/ip_tables.h>
29 #include <linux/netfilter/nf_nat.h>
30 #include <linux/netfilter/xt_addrtype.h>
31 #include <libiptc/libiptc.h>
33 #include "alloc-util.h"
34 #include "firewall-util.h"
35 #include "in-addr-util.h"
38 DEFINE_TRIVIAL_CLEANUP_FUNC(struct xtc_handle
*, iptc_free
);
40 static int entry_fill_basics(
41 struct ipt_entry
*entry
,
43 const char *in_interface
,
44 const union in_addr_union
*source
,
45 unsigned source_prefixlen
,
46 const char *out_interface
,
47 const union in_addr_union
*destination
,
48 unsigned destination_prefixlen
) {
52 if (out_interface
&& strlen(out_interface
) >= IFNAMSIZ
)
55 if (in_interface
&& strlen(in_interface
) >= IFNAMSIZ
)
58 entry
->ip
.proto
= protocol
;
61 strcpy(entry
->ip
.iniface
, in_interface
);
62 memset(entry
->ip
.iniface_mask
, 0xFF, strlen(in_interface
)+1);
65 entry
->ip
.src
= source
->in
;
66 in_addr_prefixlen_to_netmask(&entry
->ip
.smsk
, source_prefixlen
);
70 strcpy(entry
->ip
.outiface
, out_interface
);
71 memset(entry
->ip
.outiface_mask
, 0xFF, strlen(out_interface
)+1);
74 entry
->ip
.dst
= destination
->in
;
75 in_addr_prefixlen_to_netmask(&entry
->ip
.dmsk
, destination_prefixlen
);
81 int fw_add_masquerade(
85 const union in_addr_union
*source
,
86 unsigned source_prefixlen
,
87 const char *out_interface
,
88 const union in_addr_union
*destination
,
89 unsigned destination_prefixlen
) {
91 _cleanup_(iptc_freep
) struct xtc_handle
*h
= NULL
;
92 struct ipt_entry
*entry
, *mask
;
93 struct ipt_entry_target
*t
;
95 struct nf_nat_ipv4_multi_range_compat
*mr
;
101 if (protocol
!= 0 && protocol
!= IPPROTO_TCP
&& protocol
!= IPPROTO_UDP
)
104 h
= iptc_init("nat");
108 sz
= XT_ALIGN(sizeof(struct ipt_entry
)) +
109 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
110 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
112 /* Put together the entry we want to add or remove */
114 entry
->next_offset
= sz
;
115 entry
->target_offset
= XT_ALIGN(sizeof(struct ipt_entry
));
116 r
= entry_fill_basics(entry
, protocol
, NULL
, source
, source_prefixlen
, out_interface
, destination
, destination_prefixlen
);
120 /* Fill in target part */
121 t
= ipt_get_target(entry
);
123 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
124 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
125 strncpy(t
->u
.user
.name
, "MASQUERADE", sizeof(t
->u
.user
.name
));
126 mr
= (struct nf_nat_ipv4_multi_range_compat
*) t
->data
;
129 /* Create a search mask entry */
131 memset(mask
, 0xFF, sz
);
134 if (iptc_check_entry("POSTROUTING", entry
, (unsigned char*) mask
, h
))
136 if (errno
!= ENOENT
) /* if other error than not existing yet, fail */
139 if (!iptc_insert_entry("POSTROUTING", entry
, 0, h
))
142 if (!iptc_delete_entry("POSTROUTING", entry
, (unsigned char*) mask
, h
)) {
143 if (errno
== ENOENT
) /* if it's already gone, all is good! */
156 int fw_add_local_dnat(
160 const char *in_interface
,
161 const union in_addr_union
*source
,
162 unsigned source_prefixlen
,
163 const union in_addr_union
*destination
,
164 unsigned destination_prefixlen
,
166 const union in_addr_union
*remote
,
167 uint16_t remote_port
,
168 const union in_addr_union
*previous_remote
) {
171 _cleanup_(iptc_freep
) struct xtc_handle
*h
= NULL
;
172 struct ipt_entry
*entry
, *mask
;
173 struct ipt_entry_target
*t
;
174 struct ipt_entry_match
*m
;
175 struct xt_addrtype_info_v1
*at
;
176 struct nf_nat_ipv4_multi_range_compat
*mr
;
180 assert(add
|| !previous_remote
);
185 if (protocol
!= IPPROTO_TCP
&& protocol
!= IPPROTO_UDP
)
191 if (remote_port
<= 0)
194 h
= iptc_init("nat");
198 sz
= XT_ALIGN(sizeof(struct ipt_entry
)) +
199 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
200 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
)) +
201 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
202 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
204 if (protocol
== IPPROTO_TCP
)
205 msz
= XT_ALIGN(sizeof(struct ipt_entry_match
)) +
206 XT_ALIGN(sizeof(struct xt_tcp
));
208 msz
= XT_ALIGN(sizeof(struct ipt_entry_match
)) +
209 XT_ALIGN(sizeof(struct xt_udp
));
213 /* Fill in basic part */
215 entry
->next_offset
= sz
;
216 entry
->target_offset
=
217 XT_ALIGN(sizeof(struct ipt_entry
)) +
218 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
219 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
)) +
221 r
= entry_fill_basics(entry
, protocol
, in_interface
, source
, source_prefixlen
, NULL
, destination
, destination_prefixlen
);
225 /* Fill in first match */
226 m
= (struct ipt_entry_match
*) ((uint8_t*) entry
+ XT_ALIGN(sizeof(struct ipt_entry
)));
227 m
->u
.match_size
= msz
;
228 if (protocol
== IPPROTO_TCP
) {
231 strncpy(m
->u
.user
.name
, "tcp", sizeof(m
->u
.user
.name
));
232 tcp
= (struct xt_tcp
*) m
->data
;
233 tcp
->dpts
[0] = tcp
->dpts
[1] = local_port
;
235 tcp
->spts
[1] = 0xFFFF;
240 strncpy(m
->u
.user
.name
, "udp", sizeof(m
->u
.user
.name
));
241 udp
= (struct xt_udp
*) m
->data
;
242 udp
->dpts
[0] = udp
->dpts
[1] = local_port
;
244 udp
->spts
[1] = 0xFFFF;
247 /* Fill in second match */
248 m
= (struct ipt_entry_match
*) ((uint8_t*) entry
+ XT_ALIGN(sizeof(struct ipt_entry
)) + msz
);
250 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
251 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
));
252 strncpy(m
->u
.user
.name
, "addrtype", sizeof(m
->u
.user
.name
));
253 m
->u
.user
.revision
= 1;
254 at
= (struct xt_addrtype_info_v1
*) m
->data
;
255 at
->dest
= XT_ADDRTYPE_LOCAL
;
257 /* Fill in target part */
258 t
= ipt_get_target(entry
);
260 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
261 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
262 strncpy(t
->u
.user
.name
, "DNAT", sizeof(t
->u
.user
.name
));
263 mr
= (struct nf_nat_ipv4_multi_range_compat
*) t
->data
;
265 mr
->range
[0].flags
= NF_NAT_RANGE_PROTO_SPECIFIED
|NF_NAT_RANGE_MAP_IPS
;
266 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= remote
->in
.s_addr
;
267 if (protocol
== IPPROTO_TCP
)
268 mr
->range
[0].min
.tcp
.port
= mr
->range
[0].max
.tcp
.port
= htons(remote_port
);
270 mr
->range
[0].min
.udp
.port
= mr
->range
[0].max
.udp
.port
= htons(remote_port
);
273 memset(mask
, 0xFF, sz
);
276 /* Add the PREROUTING rule, if it is missing so far */
277 if (!iptc_check_entry("PREROUTING", entry
, (unsigned char*) mask
, h
)) {
281 if (!iptc_insert_entry("PREROUTING", entry
, 0, h
))
285 /* If a previous remote is set, remove its entry */
286 if (previous_remote
&& previous_remote
->in
.s_addr
!= remote
->in
.s_addr
) {
287 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= previous_remote
->in
.s_addr
;
289 if (!iptc_delete_entry("PREROUTING", entry
, (unsigned char*) mask
, h
)) {
294 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= remote
->in
.s_addr
;
297 /* Add the OUTPUT rule, if it is missing so far */
300 /* Don't apply onto loopback addresses */
302 entry
->ip
.dst
.s_addr
= htobe32(0x7F000000);
303 entry
->ip
.dmsk
.s_addr
= htobe32(0xFF000000);
304 entry
->ip
.invflags
= IPT_INV_DSTIP
;
307 if (!iptc_check_entry("OUTPUT", entry
, (unsigned char*) mask
, h
)) {
311 if (!iptc_insert_entry("OUTPUT", entry
, 0, h
))
315 /* If a previous remote is set, remove its entry */
316 if (previous_remote
&& previous_remote
->in
.s_addr
!= remote
->in
.s_addr
) {
317 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= previous_remote
->in
.s_addr
;
319 if (!iptc_delete_entry("OUTPUT", entry
, (unsigned char*) mask
, h
)) {
326 if (!iptc_delete_entry("PREROUTING", entry
, (unsigned char*) mask
, h
)) {
333 entry
->ip
.dst
.s_addr
= htobe32(0x7F000000);
334 entry
->ip
.dmsk
.s_addr
= htobe32(0xFF000000);
335 entry
->ip
.invflags
= IPT_INV_DSTIP
;
338 if (!iptc_delete_entry("OUTPUT", entry
, (unsigned char*) mask
, h
)) {