1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2015 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include <arpa/inet.h>
29 #include <sys/socket.h>
30 #include <linux/netfilter_ipv4/ip_tables.h>
31 #include <linux/netfilter/nf_nat.h>
32 #include <linux/netfilter/xt_addrtype.h>
33 #include <libiptc/libiptc.h>
35 #include "alloc-util.h"
36 #include "firewall-util.h"
37 #include "in-addr-util.h"
40 DEFINE_TRIVIAL_CLEANUP_FUNC(struct xtc_handle
*, iptc_free
);
42 static int entry_fill_basics(
43 struct ipt_entry
*entry
,
45 const char *in_interface
,
46 const union in_addr_union
*source
,
47 unsigned source_prefixlen
,
48 const char *out_interface
,
49 const union in_addr_union
*destination
,
50 unsigned destination_prefixlen
) {
54 if (out_interface
&& strlen(out_interface
) >= IFNAMSIZ
)
57 if (in_interface
&& strlen(in_interface
) >= IFNAMSIZ
)
60 entry
->ip
.proto
= protocol
;
63 strcpy(entry
->ip
.iniface
, in_interface
);
64 memset(entry
->ip
.iniface_mask
, 0xFF, strlen(in_interface
)+1);
67 entry
->ip
.src
= source
->in
;
68 in_addr_prefixlen_to_netmask(&entry
->ip
.smsk
, source_prefixlen
);
72 strcpy(entry
->ip
.outiface
, out_interface
);
73 memset(entry
->ip
.outiface_mask
, 0xFF, strlen(out_interface
)+1);
76 entry
->ip
.dst
= destination
->in
;
77 in_addr_prefixlen_to_netmask(&entry
->ip
.dmsk
, destination_prefixlen
);
83 int fw_add_masquerade(
87 const union in_addr_union
*source
,
88 unsigned source_prefixlen
,
89 const char *out_interface
,
90 const union in_addr_union
*destination
,
91 unsigned destination_prefixlen
) {
93 _cleanup_(iptc_freep
) struct xtc_handle
*h
= NULL
;
94 struct ipt_entry
*entry
, *mask
;
95 struct ipt_entry_target
*t
;
97 struct nf_nat_ipv4_multi_range_compat
*mr
;
103 if (protocol
!= 0 && protocol
!= IPPROTO_TCP
&& protocol
!= IPPROTO_UDP
)
106 h
= iptc_init("nat");
110 sz
= XT_ALIGN(sizeof(struct ipt_entry
)) +
111 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
112 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
114 /* Put together the entry we want to add or remove */
116 entry
->next_offset
= sz
;
117 entry
->target_offset
= XT_ALIGN(sizeof(struct ipt_entry
));
118 r
= entry_fill_basics(entry
, protocol
, NULL
, source
, source_prefixlen
, out_interface
, destination
, destination_prefixlen
);
122 /* Fill in target part */
123 t
= ipt_get_target(entry
);
125 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
126 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
127 strncpy(t
->u
.user
.name
, "MASQUERADE", sizeof(t
->u
.user
.name
));
128 mr
= (struct nf_nat_ipv4_multi_range_compat
*) t
->data
;
131 /* Create a search mask entry */
133 memset(mask
, 0xFF, sz
);
136 if (iptc_check_entry("POSTROUTING", entry
, (unsigned char*) mask
, h
))
138 if (errno
!= ENOENT
) /* if other error than not existing yet, fail */
141 if (!iptc_insert_entry("POSTROUTING", entry
, 0, h
))
144 if (!iptc_delete_entry("POSTROUTING", entry
, (unsigned char*) mask
, h
)) {
145 if (errno
== ENOENT
) /* if it's already gone, all is good! */
158 int fw_add_local_dnat(
162 const char *in_interface
,
163 const union in_addr_union
*source
,
164 unsigned source_prefixlen
,
165 const union in_addr_union
*destination
,
166 unsigned destination_prefixlen
,
168 const union in_addr_union
*remote
,
169 uint16_t remote_port
,
170 const union in_addr_union
*previous_remote
) {
173 _cleanup_(iptc_freep
) struct xtc_handle
*h
= NULL
;
174 struct ipt_entry
*entry
, *mask
;
175 struct ipt_entry_target
*t
;
176 struct ipt_entry_match
*m
;
177 struct xt_addrtype_info_v1
*at
;
178 struct nf_nat_ipv4_multi_range_compat
*mr
;
182 assert(add
|| !previous_remote
);
187 if (protocol
!= IPPROTO_TCP
&& protocol
!= IPPROTO_UDP
)
193 if (remote_port
<= 0)
196 h
= iptc_init("nat");
200 sz
= XT_ALIGN(sizeof(struct ipt_entry
)) +
201 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
202 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
)) +
203 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
204 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
206 if (protocol
== IPPROTO_TCP
)
207 msz
= XT_ALIGN(sizeof(struct ipt_entry_match
)) +
208 XT_ALIGN(sizeof(struct xt_tcp
));
210 msz
= XT_ALIGN(sizeof(struct ipt_entry_match
)) +
211 XT_ALIGN(sizeof(struct xt_udp
));
215 /* Fill in basic part */
217 entry
->next_offset
= sz
;
218 entry
->target_offset
=
219 XT_ALIGN(sizeof(struct ipt_entry
)) +
220 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
221 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
)) +
223 r
= entry_fill_basics(entry
, protocol
, in_interface
, source
, source_prefixlen
, NULL
, destination
, destination_prefixlen
);
227 /* Fill in first match */
228 m
= (struct ipt_entry_match
*) ((uint8_t*) entry
+ XT_ALIGN(sizeof(struct ipt_entry
)));
229 m
->u
.match_size
= msz
;
230 if (protocol
== IPPROTO_TCP
) {
233 strncpy(m
->u
.user
.name
, "tcp", sizeof(m
->u
.user
.name
));
234 tcp
= (struct xt_tcp
*) m
->data
;
235 tcp
->dpts
[0] = tcp
->dpts
[1] = local_port
;
237 tcp
->spts
[1] = 0xFFFF;
242 strncpy(m
->u
.user
.name
, "udp", sizeof(m
->u
.user
.name
));
243 udp
= (struct xt_udp
*) m
->data
;
244 udp
->dpts
[0] = udp
->dpts
[1] = local_port
;
246 udp
->spts
[1] = 0xFFFF;
249 /* Fill in second match */
250 m
= (struct ipt_entry_match
*) ((uint8_t*) entry
+ XT_ALIGN(sizeof(struct ipt_entry
)) + msz
);
252 XT_ALIGN(sizeof(struct ipt_entry_match
)) +
253 XT_ALIGN(sizeof(struct xt_addrtype_info_v1
));
254 strncpy(m
->u
.user
.name
, "addrtype", sizeof(m
->u
.user
.name
));
255 m
->u
.user
.revision
= 1;
256 at
= (struct xt_addrtype_info_v1
*) m
->data
;
257 at
->dest
= XT_ADDRTYPE_LOCAL
;
259 /* Fill in target part */
260 t
= ipt_get_target(entry
);
262 XT_ALIGN(sizeof(struct ipt_entry_target
)) +
263 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat
));
264 strncpy(t
->u
.user
.name
, "DNAT", sizeof(t
->u
.user
.name
));
265 mr
= (struct nf_nat_ipv4_multi_range_compat
*) t
->data
;
267 mr
->range
[0].flags
= NF_NAT_RANGE_PROTO_SPECIFIED
|NF_NAT_RANGE_MAP_IPS
;
268 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= remote
->in
.s_addr
;
269 if (protocol
== IPPROTO_TCP
)
270 mr
->range
[0].min
.tcp
.port
= mr
->range
[0].max
.tcp
.port
= htons(remote_port
);
272 mr
->range
[0].min
.udp
.port
= mr
->range
[0].max
.udp
.port
= htons(remote_port
);
275 memset(mask
, 0xFF, sz
);
278 /* Add the PREROUTING rule, if it is missing so far */
279 if (!iptc_check_entry("PREROUTING", entry
, (unsigned char*) mask
, h
)) {
283 if (!iptc_insert_entry("PREROUTING", entry
, 0, h
))
287 /* If a previous remote is set, remove its entry */
288 if (previous_remote
&& previous_remote
->in
.s_addr
!= remote
->in
.s_addr
) {
289 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= previous_remote
->in
.s_addr
;
291 if (!iptc_delete_entry("PREROUTING", entry
, (unsigned char*) mask
, h
)) {
296 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= remote
->in
.s_addr
;
299 /* Add the OUTPUT rule, if it is missing so far */
302 /* Don't apply onto loopback addresses */
304 entry
->ip
.dst
.s_addr
= htobe32(0x7F000000);
305 entry
->ip
.dmsk
.s_addr
= htobe32(0xFF000000);
306 entry
->ip
.invflags
= IPT_INV_DSTIP
;
309 if (!iptc_check_entry("OUTPUT", entry
, (unsigned char*) mask
, h
)) {
313 if (!iptc_insert_entry("OUTPUT", entry
, 0, h
))
317 /* If a previous remote is set, remove its entry */
318 if (previous_remote
&& previous_remote
->in
.s_addr
!= remote
->in
.s_addr
) {
319 mr
->range
[0].min_ip
= mr
->range
[0].max_ip
= previous_remote
->in
.s_addr
;
321 if (!iptc_delete_entry("OUTPUT", entry
, (unsigned char*) mask
, h
)) {
328 if (!iptc_delete_entry("PREROUTING", entry
, (unsigned char*) mask
, h
)) {
335 entry
->ip
.dst
.s_addr
= htobe32(0x7F000000);
336 entry
->ip
.dmsk
.s_addr
= htobe32(0xFF000000);
337 entry
->ip
.invflags
= IPT_INV_DSTIP
;
340 if (!iptc_delete_entry("OUTPUT", entry
, (unsigned char*) mask
, h
)) {