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