]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/firewall-util.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / shared / firewall-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
76917807
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2015 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
2dc95d98
ZJS
21/* Temporary work-around for broken glibc vs. linux kernel header definitions
22 * This is already fixed upstream, remove this when distributions have updated.
23 */
08ce521f
LP
24#define _NET_IF_H 1
25
a8fbdf54 26#include <alloca.h>
76917807 27#include <arpa/inet.h>
a8fbdf54
TA
28#include <endian.h>
29#include <errno.h>
a8fbdf54
TA
30#include <stddef.h>
31#include <string.h>
32#include <sys/socket.h>
08ce521f 33#include <net/if.h>
08ce521f 34#ifndef IFNAMSIZ
6f270e6b 35#define IFNAMSIZ 16
08ce521f 36#endif
6f270e6b 37#include <linux/if.h>
76917807
LP
38#include <linux/netfilter_ipv4/ip_tables.h>
39#include <linux/netfilter/nf_nat.h>
40#include <linux/netfilter/xt_addrtype.h>
41#include <libiptc/libiptc.h>
42
b5efdb8a 43#include "alloc-util.h"
12c2884c 44#include "firewall-util.h"
a8fbdf54
TA
45#include "in-addr-util.h"
46#include "macro.h"
d31645ad 47#include "socket-util.h"
76917807
LP
48
49DEFINE_TRIVIAL_CLEANUP_FUNC(struct xtc_handle*, iptc_free);
50
51static int entry_fill_basics(
52 struct ipt_entry *entry,
53 int protocol,
54 const char *in_interface,
55 const union in_addr_union *source,
56 unsigned source_prefixlen,
57 const char *out_interface,
58 const union in_addr_union *destination,
59 unsigned destination_prefixlen) {
60
61 assert(entry);
62
d31645ad 63 if (out_interface && !ifname_valid(out_interface))
76917807 64 return -EINVAL;
d31645ad 65 if (in_interface && !ifname_valid(in_interface))
76917807
LP
66 return -EINVAL;
67
68 entry->ip.proto = protocol;
69
70 if (in_interface) {
71 strcpy(entry->ip.iniface, in_interface);
72 memset(entry->ip.iniface_mask, 0xFF, strlen(in_interface)+1);
73 }
74 if (source) {
75 entry->ip.src = source->in;
5a941f5f 76 in4_addr_prefixlen_to_netmask(&entry->ip.smsk, source_prefixlen);
76917807
LP
77 }
78
79 if (out_interface) {
f2850127
ZJS
80 size_t l = strlen(out_interface);
81 assert(l < sizeof entry->ip.outiface && l < sizeof entry->ip.outiface_mask);
82
76917807 83 strcpy(entry->ip.outiface, out_interface);
f2850127 84 memset(entry->ip.outiface_mask, 0xFF, l + 1);
76917807
LP
85 }
86 if (destination) {
87 entry->ip.dst = destination->in;
5a941f5f 88 in4_addr_prefixlen_to_netmask(&entry->ip.dmsk, destination_prefixlen);
76917807
LP
89 }
90
91 return 0;
92}
93
94int fw_add_masquerade(
95 bool add,
96 int af,
97 int protocol,
98 const union in_addr_union *source,
99 unsigned source_prefixlen,
100 const char *out_interface,
101 const union in_addr_union *destination,
102 unsigned destination_prefixlen) {
103
104 _cleanup_(iptc_freep) struct xtc_handle *h = NULL;
105 struct ipt_entry *entry, *mask;
106 struct ipt_entry_target *t;
107 size_t sz;
108 struct nf_nat_ipv4_multi_range_compat *mr;
109 int r;
110
111 if (af != AF_INET)
15411c0c 112 return -EOPNOTSUPP;
76917807 113
4c701096 114 if (!IN_SET(protocol, 0, IPPROTO_TCP, IPPROTO_UDP))
15411c0c 115 return -EOPNOTSUPP;
76917807
LP
116
117 h = iptc_init("nat");
118 if (!h)
119 return -errno;
120
121 sz = XT_ALIGN(sizeof(struct ipt_entry)) +
122 XT_ALIGN(sizeof(struct ipt_entry_target)) +
123 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
124
125 /* Put together the entry we want to add or remove */
126 entry = alloca0(sz);
127 entry->next_offset = sz;
128 entry->target_offset = XT_ALIGN(sizeof(struct ipt_entry));
129 r = entry_fill_basics(entry, protocol, NULL, source, source_prefixlen, out_interface, destination, destination_prefixlen);
130 if (r < 0)
131 return r;
132
133 /* Fill in target part */
134 t = ipt_get_target(entry);
135 t->u.target_size =
136 XT_ALIGN(sizeof(struct ipt_entry_target)) +
137 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
138 strncpy(t->u.user.name, "MASQUERADE", sizeof(t->u.user.name));
139 mr = (struct nf_nat_ipv4_multi_range_compat*) t->data;
140 mr->rangesize = 1;
141
142 /* Create a search mask entry */
143 mask = alloca(sz);
144 memset(mask, 0xFF, sz);
145
146 if (add) {
147 if (iptc_check_entry("POSTROUTING", entry, (unsigned char*) mask, h))
148 return 0;
149 if (errno != ENOENT) /* if other error than not existing yet, fail */
150 return -errno;
151
152 if (!iptc_insert_entry("POSTROUTING", entry, 0, h))
153 return -errno;
154 } else {
155 if (!iptc_delete_entry("POSTROUTING", entry, (unsigned char*) mask, h)) {
156 if (errno == ENOENT) /* if it's already gone, all is good! */
157 return 0;
158
159 return -errno;
160 }
161 }
162
163 if (!iptc_commit(h))
164 return -errno;
165
166 return 0;
167}
168
169int fw_add_local_dnat(
170 bool add,
171 int af,
172 int protocol,
173 const char *in_interface,
174 const union in_addr_union *source,
175 unsigned source_prefixlen,
176 const union in_addr_union *destination,
177 unsigned destination_prefixlen,
178 uint16_t local_port,
179 const union in_addr_union *remote,
180 uint16_t remote_port,
181 const union in_addr_union *previous_remote) {
182
183
184 _cleanup_(iptc_freep) struct xtc_handle *h = NULL;
185 struct ipt_entry *entry, *mask;
186 struct ipt_entry_target *t;
187 struct ipt_entry_match *m;
188 struct xt_addrtype_info_v1 *at;
189 struct nf_nat_ipv4_multi_range_compat *mr;
190 size_t sz, msz;
191 int r;
192
193 assert(add || !previous_remote);
194
195 if (af != AF_INET)
15411c0c 196 return -EOPNOTSUPP;
76917807 197
4c701096 198 if (!IN_SET(protocol, IPPROTO_TCP, IPPROTO_UDP))
15411c0c 199 return -EOPNOTSUPP;
76917807
LP
200
201 if (local_port <= 0)
202 return -EINVAL;
203
204 if (remote_port <= 0)
205 return -EINVAL;
206
207 h = iptc_init("nat");
208 if (!h)
209 return -errno;
210
211 sz = XT_ALIGN(sizeof(struct ipt_entry)) +
212 XT_ALIGN(sizeof(struct ipt_entry_match)) +
213 XT_ALIGN(sizeof(struct xt_addrtype_info_v1)) +
214 XT_ALIGN(sizeof(struct ipt_entry_target)) +
215 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
216
217 if (protocol == IPPROTO_TCP)
218 msz = XT_ALIGN(sizeof(struct ipt_entry_match)) +
219 XT_ALIGN(sizeof(struct xt_tcp));
220 else
221 msz = XT_ALIGN(sizeof(struct ipt_entry_match)) +
222 XT_ALIGN(sizeof(struct xt_udp));
223
224 sz += msz;
225
226 /* Fill in basic part */
227 entry = alloca0(sz);
228 entry->next_offset = sz;
229 entry->target_offset =
230 XT_ALIGN(sizeof(struct ipt_entry)) +
231 XT_ALIGN(sizeof(struct ipt_entry_match)) +
232 XT_ALIGN(sizeof(struct xt_addrtype_info_v1)) +
233 msz;
234 r = entry_fill_basics(entry, protocol, in_interface, source, source_prefixlen, NULL, destination, destination_prefixlen);
235 if (r < 0)
236 return r;
237
238 /* Fill in first match */
239 m = (struct ipt_entry_match*) ((uint8_t*) entry + XT_ALIGN(sizeof(struct ipt_entry)));
240 m->u.match_size = msz;
241 if (protocol == IPPROTO_TCP) {
242 struct xt_tcp *tcp;
243
244 strncpy(m->u.user.name, "tcp", sizeof(m->u.user.name));
245 tcp = (struct xt_tcp*) m->data;
246 tcp->dpts[0] = tcp->dpts[1] = local_port;
247 tcp->spts[0] = 0;
248 tcp->spts[1] = 0xFFFF;
249
250 } else {
251 struct xt_udp *udp;
252
253 strncpy(m->u.user.name, "udp", sizeof(m->u.user.name));
254 udp = (struct xt_udp*) m->data;
255 udp->dpts[0] = udp->dpts[1] = local_port;
256 udp->spts[0] = 0;
257 udp->spts[1] = 0xFFFF;
258 }
259
260 /* Fill in second match */
261 m = (struct ipt_entry_match*) ((uint8_t*) entry + XT_ALIGN(sizeof(struct ipt_entry)) + msz);
262 m->u.match_size =
263 XT_ALIGN(sizeof(struct ipt_entry_match)) +
264 XT_ALIGN(sizeof(struct xt_addrtype_info_v1));
265 strncpy(m->u.user.name, "addrtype", sizeof(m->u.user.name));
266 m->u.user.revision = 1;
267 at = (struct xt_addrtype_info_v1*) m->data;
268 at->dest = XT_ADDRTYPE_LOCAL;
269
270 /* Fill in target part */
271 t = ipt_get_target(entry);
272 t->u.target_size =
273 XT_ALIGN(sizeof(struct ipt_entry_target)) +
274 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
275 strncpy(t->u.user.name, "DNAT", sizeof(t->u.user.name));
276 mr = (struct nf_nat_ipv4_multi_range_compat*) t->data;
277 mr->rangesize = 1;
278 mr->range[0].flags = NF_NAT_RANGE_PROTO_SPECIFIED|NF_NAT_RANGE_MAP_IPS;
279 mr->range[0].min_ip = mr->range[0].max_ip = remote->in.s_addr;
280 if (protocol == IPPROTO_TCP)
281 mr->range[0].min.tcp.port = mr->range[0].max.tcp.port = htons(remote_port);
282 else
283 mr->range[0].min.udp.port = mr->range[0].max.udp.port = htons(remote_port);
284
285 mask = alloca0(sz);
286 memset(mask, 0xFF, sz);
287
288 if (add) {
289 /* Add the PREROUTING rule, if it is missing so far */
290 if (!iptc_check_entry("PREROUTING", entry, (unsigned char*) mask, h)) {
291 if (errno != ENOENT)
292 return -EINVAL;
293
294 if (!iptc_insert_entry("PREROUTING", entry, 0, h))
295 return -errno;
296 }
297
298 /* If a previous remote is set, remove its entry */
299 if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) {
300 mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr;
301
302 if (!iptc_delete_entry("PREROUTING", entry, (unsigned char*) mask, h)) {
303 if (errno != ENOENT)
304 return -errno;
305 }
306
307 mr->range[0].min_ip = mr->range[0].max_ip = remote->in.s_addr;
308 }
309
310 /* Add the OUTPUT rule, if it is missing so far */
311 if (!in_interface) {
312
313 /* Don't apply onto loopback addresses */
314 if (!destination) {
315 entry->ip.dst.s_addr = htobe32(0x7F000000);
316 entry->ip.dmsk.s_addr = htobe32(0xFF000000);
317 entry->ip.invflags = IPT_INV_DSTIP;
318 }
319
320 if (!iptc_check_entry("OUTPUT", entry, (unsigned char*) mask, h)) {
321 if (errno != ENOENT)
322 return -errno;
323
324 if (!iptc_insert_entry("OUTPUT", entry, 0, h))
325 return -errno;
326 }
327
328 /* If a previous remote is set, remove its entry */
329 if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) {
330 mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr;
331
332 if (!iptc_delete_entry("OUTPUT", entry, (unsigned char*) mask, h)) {
333 if (errno != ENOENT)
334 return -errno;
335 }
336 }
337 }
338 } else {
339 if (!iptc_delete_entry("PREROUTING", entry, (unsigned char*) mask, h)) {
340 if (errno != ENOENT)
341 return -errno;
342 }
343
344 if (!in_interface) {
345 if (!destination) {
346 entry->ip.dst.s_addr = htobe32(0x7F000000);
347 entry->ip.dmsk.s_addr = htobe32(0xFF000000);
348 entry->ip.invflags = IPT_INV_DSTIP;
349 }
350
351 if (!iptc_delete_entry("OUTPUT", entry, (unsigned char*) mask, h)) {
352 if (errno != ENOENT)
353 return -errno;
354 }
355 }
356 }
357
358 if (!iptc_commit(h))
359 return -errno;
360
361 return 0;
362}