]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/firewall-util-iptables.c
hwdb: Add mapping for Xiaomi Mipad 2 bottom bezel capacitive buttons
[thirdparty/systemd.git] / src / shared / firewall-util-iptables.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 /* Make sure the net/if.h header is included before any linux/ one */
4 #include <net/if.h>
5 #include <arpa/inet.h>
6 #include <endian.h>
7 #include <errno.h>
8 #include <stddef.h>
9 #include <string.h>
10 #include <linux/if.h>
11 #include <linux/netfilter_ipv4/ip_tables.h>
12 #include <linux/netfilter/nf_nat.h>
13 #include <linux/netfilter/xt_addrtype.h>
14 #include <libiptc/libiptc.h>
15
16 #include "alloc-util.h"
17 #include "dlfcn-util.h"
18 #include "firewall-util.h"
19 #include "firewall-util-private.h"
20 #include "in-addr-util.h"
21 #include "macro.h"
22 #include "socket-util.h"
23
24 static DLSYM_FUNCTION(iptc_check_entry);
25 static DLSYM_FUNCTION(iptc_commit);
26 static DLSYM_FUNCTION(iptc_delete_entry);
27 static DLSYM_FUNCTION(iptc_free);
28 static DLSYM_FUNCTION(iptc_init);
29 static DLSYM_FUNCTION(iptc_insert_entry);
30 static DLSYM_FUNCTION(iptc_strerror);
31
32 static void *iptc_dl = NULL;
33
34 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct xtc_handle*, sym_iptc_free, NULL);
35
36 static 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 && !ifname_valid(out_interface))
49 return -EINVAL;
50 if (in_interface && !ifname_valid(in_interface))
51 return -EINVAL;
52
53 entry->ip.proto = protocol;
54
55 if (in_interface) {
56 size_t l;
57
58 l = strlen(in_interface);
59 assert(l < sizeof entry->ip.iniface);
60 assert(l < sizeof entry->ip.iniface_mask);
61
62 strcpy(entry->ip.iniface, in_interface);
63 memset(entry->ip.iniface_mask, 0xFF, l + 1);
64 }
65 if (source) {
66 entry->ip.src = source->in;
67 in4_addr_prefixlen_to_netmask(&entry->ip.smsk, source_prefixlen);
68 }
69
70 if (out_interface) {
71 size_t l = strlen(out_interface);
72 assert(l < sizeof entry->ip.outiface);
73 assert(l < sizeof entry->ip.outiface_mask);
74
75 strcpy(entry->ip.outiface, out_interface);
76 memset(entry->ip.outiface_mask, 0xFF, l + 1);
77 }
78 if (destination) {
79 entry->ip.dst = destination->in;
80 in4_addr_prefixlen_to_netmask(&entry->ip.dmsk, destination_prefixlen);
81 }
82
83 return 0;
84 }
85
86 int fw_iptables_add_masquerade(
87 bool add,
88 int af,
89 const union in_addr_union *source,
90 unsigned source_prefixlen) {
91
92 static const xt_chainlabel chain = "POSTROUTING";
93 _cleanup_(sym_iptc_freep) struct xtc_handle *h = NULL;
94 struct ipt_entry *entry, *mask;
95 struct ipt_entry_target *t;
96 size_t sz;
97 struct nf_nat_ipv4_multi_range_compat *mr;
98 int r, protocol = 0;
99 const char *out_interface = NULL;
100 const union in_addr_union *destination = NULL;
101 unsigned destination_prefixlen = 0;
102
103 if (af != AF_INET)
104 return -EOPNOTSUPP;
105
106 if (!source || source_prefixlen == 0)
107 return -EINVAL;
108
109 r = fw_iptables_init_nat(&h);
110 if (r < 0)
111 return r;
112
113 sz = XT_ALIGN(sizeof(struct ipt_entry)) +
114 XT_ALIGN(sizeof(struct ipt_entry_target)) +
115 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
116
117 /* Put together the entry we want to add or remove */
118 entry = alloca0(sz);
119 entry->next_offset = sz;
120 entry->target_offset = XT_ALIGN(sizeof(struct ipt_entry));
121 r = entry_fill_basics(entry, protocol, NULL, source, source_prefixlen, out_interface, destination, destination_prefixlen);
122 if (r < 0)
123 return r;
124
125 /* Fill in target part */
126 t = ipt_get_target(entry);
127 t->u.target_size =
128 XT_ALIGN(sizeof(struct ipt_entry_target)) +
129 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
130 strncpy(t->u.user.name, "MASQUERADE", sizeof(t->u.user.name));
131 mr = (struct nf_nat_ipv4_multi_range_compat*) t->data;
132 mr->rangesize = 1;
133
134 /* Create a search mask entry */
135 mask = alloca_safe(sz);
136 memset(mask, 0xFF, sz);
137
138 if (add) {
139 if (sym_iptc_check_entry(chain, entry, (unsigned char*) mask, h))
140 return 0;
141 if (errno != ENOENT) /* if other error than not existing yet, fail */
142 return -errno;
143
144 if (!sym_iptc_insert_entry(chain, entry, 0, h))
145 return -errno;
146 } else {
147 if (!sym_iptc_delete_entry(chain, entry, (unsigned char*) mask, h)) {
148 if (errno == ENOENT) /* if it's already gone, all is good! */
149 return 0;
150
151 return -errno;
152 }
153 }
154
155 if (!sym_iptc_commit(h))
156 return -errno;
157
158 return 0;
159 }
160
161 int fw_iptables_add_local_dnat(
162 bool add,
163 int af,
164 int protocol,
165 uint16_t local_port,
166 const union in_addr_union *remote,
167 uint16_t remote_port,
168 const union in_addr_union *previous_remote) {
169
170 static const xt_chainlabel chain_pre = "PREROUTING", chain_output = "OUTPUT";
171 _cleanup_(sym_iptc_freep) struct xtc_handle *h = NULL;
172 struct ipt_entry *entry, *mask;
173 struct ipt_entry_target *t;
174 struct ipt_entry_match *m;
175 struct xt_addrtype_info_v1 *at;
176 struct nf_nat_ipv4_multi_range_compat *mr;
177 size_t sz, msz;
178 int r;
179 const char *in_interface = NULL;
180 const union in_addr_union *source = NULL;
181 unsigned source_prefixlen = 0;
182 const union in_addr_union *destination = NULL;
183 unsigned destination_prefixlen = 0;
184
185 assert(add || !previous_remote);
186
187 if (af != AF_INET)
188 return -EOPNOTSUPP;
189
190 if (!IN_SET(protocol, IPPROTO_TCP, IPPROTO_UDP))
191 return -EOPNOTSUPP;
192
193 if (local_port <= 0)
194 return -EINVAL;
195
196 if (remote_port <= 0)
197 return -EINVAL;
198
199 r = fw_iptables_init_nat(&h);
200 if (r < 0)
201 return r;
202
203 sz = XT_ALIGN(sizeof(struct ipt_entry)) +
204 XT_ALIGN(sizeof(struct ipt_entry_match)) +
205 XT_ALIGN(sizeof(struct xt_addrtype_info_v1)) +
206 XT_ALIGN(sizeof(struct ipt_entry_target)) +
207 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
208
209 if (protocol == IPPROTO_TCP)
210 msz = XT_ALIGN(sizeof(struct ipt_entry_match)) +
211 XT_ALIGN(sizeof(struct xt_tcp));
212 else
213 msz = XT_ALIGN(sizeof(struct ipt_entry_match)) +
214 XT_ALIGN(sizeof(struct xt_udp));
215
216 sz += msz;
217
218 /* Fill in basic part */
219 entry = alloca0(sz);
220 entry->next_offset = sz;
221 entry->target_offset =
222 XT_ALIGN(sizeof(struct ipt_entry)) +
223 XT_ALIGN(sizeof(struct ipt_entry_match)) +
224 XT_ALIGN(sizeof(struct xt_addrtype_info_v1)) +
225 msz;
226 r = entry_fill_basics(entry, protocol, in_interface, source, source_prefixlen, NULL, destination, destination_prefixlen);
227 if (r < 0)
228 return r;
229
230 /* Fill in first match */
231 m = (struct ipt_entry_match*) ((uint8_t*) entry + XT_ALIGN(sizeof(struct ipt_entry)));
232 m->u.match_size = msz;
233 if (protocol == IPPROTO_TCP) {
234 struct xt_tcp *tcp;
235
236 strncpy(m->u.user.name, "tcp", sizeof(m->u.user.name));
237 tcp = (struct xt_tcp*) m->data;
238 tcp->dpts[0] = tcp->dpts[1] = local_port;
239 tcp->spts[0] = 0;
240 tcp->spts[1] = 0xFFFF;
241
242 } else {
243 struct xt_udp *udp;
244
245 strncpy(m->u.user.name, "udp", sizeof(m->u.user.name));
246 udp = (struct xt_udp*) m->data;
247 udp->dpts[0] = udp->dpts[1] = local_port;
248 udp->spts[0] = 0;
249 udp->spts[1] = 0xFFFF;
250 }
251
252 /* Fill in second match */
253 m = (struct ipt_entry_match*) ((uint8_t*) entry + XT_ALIGN(sizeof(struct ipt_entry)) + msz);
254 m->u.match_size =
255 XT_ALIGN(sizeof(struct ipt_entry_match)) +
256 XT_ALIGN(sizeof(struct xt_addrtype_info_v1));
257 strncpy(m->u.user.name, "addrtype", sizeof(m->u.user.name));
258 m->u.user.revision = 1;
259 at = (struct xt_addrtype_info_v1*) m->data;
260 at->dest = XT_ADDRTYPE_LOCAL;
261
262 /* Fill in target part */
263 t = ipt_get_target(entry);
264 t->u.target_size =
265 XT_ALIGN(sizeof(struct ipt_entry_target)) +
266 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
267 strncpy(t->u.user.name, "DNAT", sizeof(t->u.user.name));
268 mr = (struct nf_nat_ipv4_multi_range_compat*) t->data;
269 mr->rangesize = 1;
270 mr->range[0].flags = NF_NAT_RANGE_PROTO_SPECIFIED|NF_NAT_RANGE_MAP_IPS;
271 mr->range[0].min_ip = mr->range[0].max_ip = remote->in.s_addr;
272 if (protocol == IPPROTO_TCP)
273 mr->range[0].min.tcp.port = mr->range[0].max.tcp.port = htobe16(remote_port);
274 else
275 mr->range[0].min.udp.port = mr->range[0].max.udp.port = htobe16(remote_port);
276
277 mask = alloca0(sz);
278 memset(mask, 0xFF, sz);
279
280 if (add) {
281 /* Add the PREROUTING rule, if it is missing so far */
282 if (!sym_iptc_check_entry(chain_pre, entry, (unsigned char*) mask, h)) {
283 if (errno != ENOENT)
284 return -EINVAL;
285
286 if (!sym_iptc_insert_entry(chain_pre, entry, 0, h))
287 return -errno;
288 }
289
290 /* If a previous remote is set, remove its entry */
291 if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) {
292 mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr;
293
294 if (!sym_iptc_delete_entry(chain_pre, entry, (unsigned char*) mask, h)) {
295 if (errno != ENOENT)
296 return -errno;
297 }
298
299 mr->range[0].min_ip = mr->range[0].max_ip = remote->in.s_addr;
300 }
301
302 /* Add the OUTPUT rule, if it is missing so far */
303 if (!in_interface) {
304
305 /* Don't apply onto loopback addresses */
306 if (!destination) {
307 entry->ip.dst.s_addr = htobe32(0x7F000000);
308 entry->ip.dmsk.s_addr = htobe32(0xFF000000);
309 entry->ip.invflags = IPT_INV_DSTIP;
310 }
311
312 if (!sym_iptc_check_entry(chain_output, entry, (unsigned char*) mask, h)) {
313 if (errno != ENOENT)
314 return -errno;
315
316 if (!sym_iptc_insert_entry(chain_output, entry, 0, h))
317 return -errno;
318 }
319
320 /* If a previous remote is set, remove its entry */
321 if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) {
322 mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr;
323
324 if (!sym_iptc_delete_entry(chain_output, entry, (unsigned char*) mask, h)) {
325 if (errno != ENOENT)
326 return -errno;
327 }
328 }
329 }
330 } else {
331 if (!sym_iptc_delete_entry(chain_pre, entry, (unsigned char*) mask, h)) {
332 if (errno != ENOENT)
333 return -errno;
334 }
335
336 if (!in_interface) {
337 if (!destination) {
338 entry->ip.dst.s_addr = htobe32(0x7F000000);
339 entry->ip.dmsk.s_addr = htobe32(0xFF000000);
340 entry->ip.invflags = IPT_INV_DSTIP;
341 }
342
343 if (!sym_iptc_delete_entry(chain_output, entry, (unsigned char*) mask, h)) {
344 if (errno != ENOENT)
345 return -errno;
346 }
347 }
348 }
349
350 if (!sym_iptc_commit(h))
351 return -errno;
352
353 return 0;
354 }
355
356 static int dlopen_iptc(void) {
357 return dlopen_many_sym_or_warn(
358 &iptc_dl,
359 "libip4tc.so.2", LOG_DEBUG,
360 DLSYM_ARG(iptc_check_entry),
361 DLSYM_ARG(iptc_commit),
362 DLSYM_ARG(iptc_delete_entry),
363 DLSYM_ARG(iptc_free),
364 DLSYM_ARG(iptc_init),
365 DLSYM_ARG(iptc_insert_entry),
366 DLSYM_ARG(iptc_strerror));
367 }
368
369 int fw_iptables_init_nat(struct xtc_handle **ret) {
370 _cleanup_(sym_iptc_freep) struct xtc_handle *h = NULL;
371 int r;
372
373 r = dlopen_iptc();
374 if (r < 0)
375 return r;
376
377 h = sym_iptc_init("nat");
378 if (!h)
379 return log_debug_errno(errno, "Failed to init \"nat\" table: %s", sym_iptc_strerror(errno));
380
381 if (ret)
382 *ret = TAKE_PTR(h);
383
384 return 0;
385 }