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