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