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