]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/firewall-util.c
tree-wide: sort includes
[thirdparty/systemd.git] / src / shared / firewall-util.c
CommitLineData
76917807
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2015 Lennart Poettering
7
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.
12
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.
17
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/>.
20***/
21
76917807
LP
22#include <arpa/inet.h>
23#include <net/if.h>
cf0fbc49 24#include <sys/types.h>
76917807
LP
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>
29
b5efdb8a 30#include "alloc-util.h"
12c2884c 31#include "firewall-util.h"
b5efdb8a 32#include "util.h"
76917807
LP
33
34DEFINE_TRIVIAL_CLEANUP_FUNC(struct xtc_handle*, iptc_free);
35
36static int entry_fill_basics(
37 struct ipt_entry *entry,
38 int protocol,
39 const char *in_interface,
40 const union in_addr_union *source,
41 unsigned source_prefixlen,
42 const char *out_interface,
43 const union in_addr_union *destination,
44 unsigned destination_prefixlen) {
45
46 assert(entry);
47
48 if (out_interface && strlen(out_interface) >= IFNAMSIZ)
49 return -EINVAL;
50
51 if (in_interface && strlen(in_interface) >= IFNAMSIZ)
52 return -EINVAL;
53
54 entry->ip.proto = protocol;
55
56 if (in_interface) {
57 strcpy(entry->ip.iniface, in_interface);
58 memset(entry->ip.iniface_mask, 0xFF, strlen(in_interface)+1);
59 }
60 if (source) {
61 entry->ip.src = source->in;
62 in_addr_prefixlen_to_netmask(&entry->ip.smsk, source_prefixlen);
63 }
64
65 if (out_interface) {
66 strcpy(entry->ip.outiface, out_interface);
67 memset(entry->ip.outiface_mask, 0xFF, strlen(out_interface)+1);
68 }
69 if (destination) {
70 entry->ip.dst = destination->in;
71 in_addr_prefixlen_to_netmask(&entry->ip.dmsk, destination_prefixlen);
72 }
73
74 return 0;
75}
76
77int fw_add_masquerade(
78 bool add,
79 int af,
80 int protocol,
81 const union in_addr_union *source,
82 unsigned source_prefixlen,
83 const char *out_interface,
84 const union in_addr_union *destination,
85 unsigned destination_prefixlen) {
86
87 _cleanup_(iptc_freep) struct xtc_handle *h = NULL;
88 struct ipt_entry *entry, *mask;
89 struct ipt_entry_target *t;
90 size_t sz;
91 struct nf_nat_ipv4_multi_range_compat *mr;
92 int r;
93
94 if (af != AF_INET)
15411c0c 95 return -EOPNOTSUPP;
76917807
LP
96
97 if (protocol != 0 && protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)
15411c0c 98 return -EOPNOTSUPP;
76917807
LP
99
100 h = iptc_init("nat");
101 if (!h)
102 return -errno;
103
104 sz = XT_ALIGN(sizeof(struct ipt_entry)) +
105 XT_ALIGN(sizeof(struct ipt_entry_target)) +
106 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
107
108 /* Put together the entry we want to add or remove */
109 entry = alloca0(sz);
110 entry->next_offset = sz;
111 entry->target_offset = XT_ALIGN(sizeof(struct ipt_entry));
112 r = entry_fill_basics(entry, protocol, NULL, source, source_prefixlen, out_interface, destination, destination_prefixlen);
113 if (r < 0)
114 return r;
115
116 /* Fill in target part */
117 t = ipt_get_target(entry);
118 t->u.target_size =
119 XT_ALIGN(sizeof(struct ipt_entry_target)) +
120 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
121 strncpy(t->u.user.name, "MASQUERADE", sizeof(t->u.user.name));
122 mr = (struct nf_nat_ipv4_multi_range_compat*) t->data;
123 mr->rangesize = 1;
124
125 /* Create a search mask entry */
126 mask = alloca(sz);
127 memset(mask, 0xFF, sz);
128
129 if (add) {
130 if (iptc_check_entry("POSTROUTING", entry, (unsigned char*) mask, h))
131 return 0;
132 if (errno != ENOENT) /* if other error than not existing yet, fail */
133 return -errno;
134
135 if (!iptc_insert_entry("POSTROUTING", entry, 0, h))
136 return -errno;
137 } else {
138 if (!iptc_delete_entry("POSTROUTING", entry, (unsigned char*) mask, h)) {
139 if (errno == ENOENT) /* if it's already gone, all is good! */
140 return 0;
141
142 return -errno;
143 }
144 }
145
146 if (!iptc_commit(h))
147 return -errno;
148
149 return 0;
150}
151
152int fw_add_local_dnat(
153 bool add,
154 int af,
155 int protocol,
156 const char *in_interface,
157 const union in_addr_union *source,
158 unsigned source_prefixlen,
159 const union in_addr_union *destination,
160 unsigned destination_prefixlen,
161 uint16_t local_port,
162 const union in_addr_union *remote,
163 uint16_t remote_port,
164 const union in_addr_union *previous_remote) {
165
166
167 _cleanup_(iptc_freep) struct xtc_handle *h = NULL;
168 struct ipt_entry *entry, *mask;
169 struct ipt_entry_target *t;
170 struct ipt_entry_match *m;
171 struct xt_addrtype_info_v1 *at;
172 struct nf_nat_ipv4_multi_range_compat *mr;
173 size_t sz, msz;
174 int r;
175
176 assert(add || !previous_remote);
177
178 if (af != AF_INET)
15411c0c 179 return -EOPNOTSUPP;
76917807
LP
180
181 if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)
15411c0c 182 return -EOPNOTSUPP;
76917807
LP
183
184 if (local_port <= 0)
185 return -EINVAL;
186
187 if (remote_port <= 0)
188 return -EINVAL;
189
190 h = iptc_init("nat");
191 if (!h)
192 return -errno;
193
194 sz = XT_ALIGN(sizeof(struct ipt_entry)) +
195 XT_ALIGN(sizeof(struct ipt_entry_match)) +
196 XT_ALIGN(sizeof(struct xt_addrtype_info_v1)) +
197 XT_ALIGN(sizeof(struct ipt_entry_target)) +
198 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
199
200 if (protocol == IPPROTO_TCP)
201 msz = XT_ALIGN(sizeof(struct ipt_entry_match)) +
202 XT_ALIGN(sizeof(struct xt_tcp));
203 else
204 msz = XT_ALIGN(sizeof(struct ipt_entry_match)) +
205 XT_ALIGN(sizeof(struct xt_udp));
206
207 sz += msz;
208
209 /* Fill in basic part */
210 entry = alloca0(sz);
211 entry->next_offset = sz;
212 entry->target_offset =
213 XT_ALIGN(sizeof(struct ipt_entry)) +
214 XT_ALIGN(sizeof(struct ipt_entry_match)) +
215 XT_ALIGN(sizeof(struct xt_addrtype_info_v1)) +
216 msz;
217 r = entry_fill_basics(entry, protocol, in_interface, source, source_prefixlen, NULL, destination, destination_prefixlen);
218 if (r < 0)
219 return r;
220
221 /* Fill in first match */
222 m = (struct ipt_entry_match*) ((uint8_t*) entry + XT_ALIGN(sizeof(struct ipt_entry)));
223 m->u.match_size = msz;
224 if (protocol == IPPROTO_TCP) {
225 struct xt_tcp *tcp;
226
227 strncpy(m->u.user.name, "tcp", sizeof(m->u.user.name));
228 tcp = (struct xt_tcp*) m->data;
229 tcp->dpts[0] = tcp->dpts[1] = local_port;
230 tcp->spts[0] = 0;
231 tcp->spts[1] = 0xFFFF;
232
233 } else {
234 struct xt_udp *udp;
235
236 strncpy(m->u.user.name, "udp", sizeof(m->u.user.name));
237 udp = (struct xt_udp*) m->data;
238 udp->dpts[0] = udp->dpts[1] = local_port;
239 udp->spts[0] = 0;
240 udp->spts[1] = 0xFFFF;
241 }
242
243 /* Fill in second match */
244 m = (struct ipt_entry_match*) ((uint8_t*) entry + XT_ALIGN(sizeof(struct ipt_entry)) + msz);
245 m->u.match_size =
246 XT_ALIGN(sizeof(struct ipt_entry_match)) +
247 XT_ALIGN(sizeof(struct xt_addrtype_info_v1));
248 strncpy(m->u.user.name, "addrtype", sizeof(m->u.user.name));
249 m->u.user.revision = 1;
250 at = (struct xt_addrtype_info_v1*) m->data;
251 at->dest = XT_ADDRTYPE_LOCAL;
252
253 /* Fill in target part */
254 t = ipt_get_target(entry);
255 t->u.target_size =
256 XT_ALIGN(sizeof(struct ipt_entry_target)) +
257 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
258 strncpy(t->u.user.name, "DNAT", sizeof(t->u.user.name));
259 mr = (struct nf_nat_ipv4_multi_range_compat*) t->data;
260 mr->rangesize = 1;
261 mr->range[0].flags = NF_NAT_RANGE_PROTO_SPECIFIED|NF_NAT_RANGE_MAP_IPS;
262 mr->range[0].min_ip = mr->range[0].max_ip = remote->in.s_addr;
263 if (protocol == IPPROTO_TCP)
264 mr->range[0].min.tcp.port = mr->range[0].max.tcp.port = htons(remote_port);
265 else
266 mr->range[0].min.udp.port = mr->range[0].max.udp.port = htons(remote_port);
267
268 mask = alloca0(sz);
269 memset(mask, 0xFF, sz);
270
271 if (add) {
272 /* Add the PREROUTING rule, if it is missing so far */
273 if (!iptc_check_entry("PREROUTING", entry, (unsigned char*) mask, h)) {
274 if (errno != ENOENT)
275 return -EINVAL;
276
277 if (!iptc_insert_entry("PREROUTING", entry, 0, h))
278 return -errno;
279 }
280
281 /* If a previous remote is set, remove its entry */
282 if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) {
283 mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr;
284
285 if (!iptc_delete_entry("PREROUTING", entry, (unsigned char*) mask, h)) {
286 if (errno != ENOENT)
287 return -errno;
288 }
289
290 mr->range[0].min_ip = mr->range[0].max_ip = remote->in.s_addr;
291 }
292
293 /* Add the OUTPUT rule, if it is missing so far */
294 if (!in_interface) {
295
296 /* Don't apply onto loopback addresses */
297 if (!destination) {
298 entry->ip.dst.s_addr = htobe32(0x7F000000);
299 entry->ip.dmsk.s_addr = htobe32(0xFF000000);
300 entry->ip.invflags = IPT_INV_DSTIP;
301 }
302
303 if (!iptc_check_entry("OUTPUT", entry, (unsigned char*) mask, h)) {
304 if (errno != ENOENT)
305 return -errno;
306
307 if (!iptc_insert_entry("OUTPUT", entry, 0, h))
308 return -errno;
309 }
310
311 /* If a previous remote is set, remove its entry */
312 if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) {
313 mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr;
314
315 if (!iptc_delete_entry("OUTPUT", entry, (unsigned char*) mask, h)) {
316 if (errno != ENOENT)
317 return -errno;
318 }
319 }
320 }
321 } else {
322 if (!iptc_delete_entry("PREROUTING", entry, (unsigned char*) mask, h)) {
323 if (errno != ENOENT)
324 return -errno;
325 }
326
327 if (!in_interface) {
328 if (!destination) {
329 entry->ip.dst.s_addr = htobe32(0x7F000000);
330 entry->ip.dmsk.s_addr = htobe32(0xFF000000);
331 entry->ip.invflags = IPT_INV_DSTIP;
332 }
333
334 if (!iptc_delete_entry("OUTPUT", entry, (unsigned char*) mask, h)) {
335 if (errno != ENOENT)
336 return -errno;
337 }
338 }
339 }
340
341 if (!iptc_commit(h))
342 return -errno;
343
344 return 0;
345}