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