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/>.
22 #include <sys/types.h>
23 #include <arpa/inet.h>
25 #include <linux/netfilter_ipv4/ip_tables.h>
26 #include <linux/netfilter/nf_nat.h>
27 #include <linux/netfilter/xt_addrtype.h>
28 #include <libiptc/libiptc.h>
33 DEFINE_TRIVIAL_CLEANUP_FUNC(struct xtc_handle*, iptc_free);
35 static int entry_fill_basics(
36 struct ipt_entry *entry,
38 const char *in_interface,
39 const union in_addr_union *source,
40 unsigned source_prefixlen,
41 const char *out_interface,
42 const union in_addr_union *destination,
43 unsigned destination_prefixlen) {
47 if (out_interface && strlen(out_interface) >= IFNAMSIZ)
50 if (in_interface && strlen(in_interface) >= IFNAMSIZ)
53 entry->ip.proto = protocol;
56 strcpy(entry->ip.iniface, in_interface);
57 memset(entry->ip.iniface_mask, 0xFF, strlen(in_interface)+1);
60 entry->ip.src = source->in;
61 in_addr_prefixlen_to_netmask(&entry->ip.smsk, source_prefixlen);
65 strcpy(entry->ip.outiface, out_interface);
66 memset(entry->ip.outiface_mask, 0xFF, strlen(out_interface)+1);
69 entry->ip.dst = destination->in;
70 in_addr_prefixlen_to_netmask(&entry->ip.dmsk, destination_prefixlen);
76 int fw_add_masquerade(
80 const union in_addr_union *source,
81 unsigned source_prefixlen,
82 const char *out_interface,
83 const union in_addr_union *destination,
84 unsigned destination_prefixlen) {
86 _cleanup_(iptc_freep) struct xtc_handle *h = NULL;
87 struct ipt_entry *entry, *mask;
88 struct ipt_entry_target *t;
90 struct nf_nat_ipv4_multi_range_compat *mr;
96 if (protocol != 0 && protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)
103 sz = XT_ALIGN(sizeof(struct ipt_entry)) +
104 XT_ALIGN(sizeof(struct ipt_entry_target)) +
105 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
107 /* Put together the entry we want to add or remove */
109 entry->next_offset = sz;
110 entry->target_offset = XT_ALIGN(sizeof(struct ipt_entry));
111 r = entry_fill_basics(entry, protocol, NULL, source, source_prefixlen, out_interface, destination, destination_prefixlen);
115 /* Fill in target part */
116 t = ipt_get_target(entry);
118 XT_ALIGN(sizeof(struct ipt_entry_target)) +
119 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
120 strncpy(t->u.user.name, "MASQUERADE", sizeof(t->u.user.name));
121 mr = (struct nf_nat_ipv4_multi_range_compat*) t->data;
124 /* Create a search mask entry */
126 memset(mask, 0xFF, sz);
129 if (iptc_check_entry("POSTROUTING", entry, (unsigned char*) mask, h))
131 if (errno != ENOENT) /* if other error than not existing yet, fail */
134 if (!iptc_insert_entry("POSTROUTING", entry, 0, h))
137 if (!iptc_delete_entry("POSTROUTING", entry, (unsigned char*) mask, h)) {
138 if (errno == ENOENT) /* if it's already gone, all is good! */
151 int fw_add_local_dnat(
155 const char *in_interface,
156 const union in_addr_union *source,
157 unsigned source_prefixlen,
158 const union in_addr_union *destination,
159 unsigned destination_prefixlen,
161 const union in_addr_union *remote,
162 uint16_t remote_port,
163 const union in_addr_union *previous_remote) {
166 _cleanup_(iptc_freep) struct xtc_handle *h = NULL;
167 struct ipt_entry *entry, *mask;
168 struct ipt_entry_target *t;
169 struct ipt_entry_match *m;
170 struct xt_addrtype_info_v1 *at;
171 struct nf_nat_ipv4_multi_range_compat *mr;
175 assert(add || !previous_remote);
180 if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)
186 if (remote_port <= 0)
189 h = iptc_init("nat");
193 sz = XT_ALIGN(sizeof(struct ipt_entry)) +
194 XT_ALIGN(sizeof(struct ipt_entry_match)) +
195 XT_ALIGN(sizeof(struct xt_addrtype_info_v1)) +
196 XT_ALIGN(sizeof(struct ipt_entry_target)) +
197 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
199 if (protocol == IPPROTO_TCP)
200 msz = XT_ALIGN(sizeof(struct ipt_entry_match)) +
201 XT_ALIGN(sizeof(struct xt_tcp));
203 msz = XT_ALIGN(sizeof(struct ipt_entry_match)) +
204 XT_ALIGN(sizeof(struct xt_udp));
208 /* Fill in basic part */
210 entry->next_offset = sz;
211 entry->target_offset =
212 XT_ALIGN(sizeof(struct ipt_entry)) +
213 XT_ALIGN(sizeof(struct ipt_entry_match)) +
214 XT_ALIGN(sizeof(struct xt_addrtype_info_v1)) +
216 r = entry_fill_basics(entry, protocol, in_interface, source, source_prefixlen, NULL, destination, destination_prefixlen);
220 /* Fill in first match */
221 m = (struct ipt_entry_match*) ((uint8_t*) entry + XT_ALIGN(sizeof(struct ipt_entry)));
222 m->u.match_size = msz;
223 if (protocol == IPPROTO_TCP) {
226 strncpy(m->u.user.name, "tcp", sizeof(m->u.user.name));
227 tcp = (struct xt_tcp*) m->data;
228 tcp->dpts[0] = tcp->dpts[1] = local_port;
230 tcp->spts[1] = 0xFFFF;
235 strncpy(m->u.user.name, "udp", sizeof(m->u.user.name));
236 udp = (struct xt_udp*) m->data;
237 udp->dpts[0] = udp->dpts[1] = local_port;
239 udp->spts[1] = 0xFFFF;
242 /* Fill in second match */
243 m = (struct ipt_entry_match*) ((uint8_t*) entry + XT_ALIGN(sizeof(struct ipt_entry)) + msz);
245 XT_ALIGN(sizeof(struct ipt_entry_match)) +
246 XT_ALIGN(sizeof(struct xt_addrtype_info_v1));
247 strncpy(m->u.user.name, "addrtype", sizeof(m->u.user.name));
248 m->u.user.revision = 1;
249 at = (struct xt_addrtype_info_v1*) m->data;
250 at->dest = XT_ADDRTYPE_LOCAL;
252 /* Fill in target part */
253 t = ipt_get_target(entry);
255 XT_ALIGN(sizeof(struct ipt_entry_target)) +
256 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
257 strncpy(t->u.user.name, "DNAT", sizeof(t->u.user.name));
258 mr = (struct nf_nat_ipv4_multi_range_compat*) t->data;
260 mr->range[0].flags = NF_NAT_RANGE_PROTO_SPECIFIED|NF_NAT_RANGE_MAP_IPS;
261 mr->range[0].min_ip = mr->range[0].max_ip = remote->in.s_addr;
262 if (protocol == IPPROTO_TCP)
263 mr->range[0].min.tcp.port = mr->range[0].max.tcp.port = htons(remote_port);
265 mr->range[0].min.udp.port = mr->range[0].max.udp.port = htons(remote_port);
268 memset(mask, 0xFF, sz);
271 /* Add the PREROUTING rule, if it is missing so far */
272 if (!iptc_check_entry("PREROUTING", entry, (unsigned char*) mask, h)) {
276 if (!iptc_insert_entry("PREROUTING", entry, 0, h))
280 /* If a previous remote is set, remove its entry */
281 if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) {
282 mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr;
284 if (!iptc_delete_entry("PREROUTING", entry, (unsigned char*) mask, h)) {
289 mr->range[0].min_ip = mr->range[0].max_ip = remote->in.s_addr;
292 /* Add the OUTPUT rule, if it is missing so far */
295 /* Don't apply onto loopback addresses */
297 entry->ip.dst.s_addr = htobe32(0x7F000000);
298 entry->ip.dmsk.s_addr = htobe32(0xFF000000);
299 entry->ip.invflags = IPT_INV_DSTIP;
302 if (!iptc_check_entry("OUTPUT", entry, (unsigned char*) mask, h)) {
306 if (!iptc_insert_entry("OUTPUT", entry, 0, h))
310 /* If a previous remote is set, remove its entry */
311 if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) {
312 mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr;
314 if (!iptc_delete_entry("OUTPUT", entry, (unsigned char*) mask, h)) {
321 if (!iptc_delete_entry("PREROUTING", entry, (unsigned char*) mask, h)) {
328 entry->ip.dst.s_addr = htobe32(0x7F000000);
329 entry->ip.dmsk.s_addr = htobe32(0xFF000000);
330 entry->ip.invflags = IPT_INV_DSTIP;
333 if (!iptc_delete_entry("OUTPUT", entry, (unsigned char*) mask, h)) {