Line data Source code
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 0 : DEFINE_TRIVIAL_CLEANUP_FUNC(struct xtc_handle*, iptc_free);
34 :
35 0 : 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 0 : assert(entry);
46 :
47 0 : if (out_interface && strlen(out_interface) >= IFNAMSIZ)
48 0 : return -EINVAL;
49 :
50 0 : if (in_interface && strlen(in_interface) >= IFNAMSIZ)
51 0 : return -EINVAL;
52 :
53 0 : entry->ip.proto = protocol;
54 :
55 0 : if (in_interface) {
56 0 : strcpy(entry->ip.iniface, in_interface);
57 0 : memset(entry->ip.iniface_mask, 0xFF, strlen(in_interface)+1);
58 : }
59 0 : if (source) {
60 0 : entry->ip.src = source->in;
61 0 : in_addr_prefixlen_to_netmask(&entry->ip.smsk, source_prefixlen);
62 : }
63 :
64 0 : if (out_interface) {
65 0 : strcpy(entry->ip.outiface, out_interface);
66 0 : memset(entry->ip.outiface_mask, 0xFF, strlen(out_interface)+1);
67 : }
68 0 : if (destination) {
69 0 : entry->ip.dst = destination->in;
70 0 : in_addr_prefixlen_to_netmask(&entry->ip.dmsk, destination_prefixlen);
71 : }
72 :
73 0 : return 0;
74 : }
75 :
76 0 : 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 0 : _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 0 : if (af != AF_INET)
94 0 : return -EOPNOTSUPP;
95 :
96 0 : if (protocol != 0 && protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)
97 0 : return -EOPNOTSUPP;
98 :
99 0 : h = iptc_init("nat");
100 0 : if (!h)
101 0 : return -errno;
102 :
103 0 : 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 0 : entry = alloca0(sz);
109 0 : entry->next_offset = sz;
110 0 : entry->target_offset = XT_ALIGN(sizeof(struct ipt_entry));
111 0 : r = entry_fill_basics(entry, protocol, NULL, source, source_prefixlen, out_interface, destination, destination_prefixlen);
112 0 : if (r < 0)
113 0 : return r;
114 :
115 : /* Fill in target part */
116 0 : t = ipt_get_target(entry);
117 0 : t->u.target_size =
118 : XT_ALIGN(sizeof(struct ipt_entry_target)) +
119 : XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
120 0 : strncpy(t->u.user.name, "MASQUERADE", sizeof(t->u.user.name));
121 0 : mr = (struct nf_nat_ipv4_multi_range_compat*) t->data;
122 0 : mr->rangesize = 1;
123 :
124 : /* Create a search mask entry */
125 0 : mask = alloca(sz);
126 0 : memset(mask, 0xFF, sz);
127 :
128 0 : if (add) {
129 0 : if (iptc_check_entry("POSTROUTING", entry, (unsigned char*) mask, h))
130 0 : return 0;
131 0 : if (errno != ENOENT) /* if other error than not existing yet, fail */
132 0 : return -errno;
133 :
134 0 : if (!iptc_insert_entry("POSTROUTING", entry, 0, h))
135 0 : return -errno;
136 : } else {
137 0 : if (!iptc_delete_entry("POSTROUTING", entry, (unsigned char*) mask, h)) {
138 0 : if (errno == ENOENT) /* if it's already gone, all is good! */
139 0 : return 0;
140 :
141 0 : return -errno;
142 : }
143 : }
144 :
145 0 : if (!iptc_commit(h))
146 0 : return -errno;
147 :
148 0 : return 0;
149 : }
150 :
151 0 : 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 0 : _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 0 : assert(add || !previous_remote);
176 :
177 0 : if (af != AF_INET)
178 0 : return -EOPNOTSUPP;
179 :
180 0 : if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)
181 0 : return -EOPNOTSUPP;
182 :
183 0 : if (local_port <= 0)
184 0 : return -EINVAL;
185 :
186 0 : if (remote_port <= 0)
187 0 : return -EINVAL;
188 :
189 0 : h = iptc_init("nat");
190 0 : if (!h)
191 0 : return -errno;
192 :
193 0 : 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 0 : if (protocol == IPPROTO_TCP)
200 0 : msz = XT_ALIGN(sizeof(struct ipt_entry_match)) +
201 : XT_ALIGN(sizeof(struct xt_tcp));
202 : else
203 0 : msz = XT_ALIGN(sizeof(struct ipt_entry_match)) +
204 : XT_ALIGN(sizeof(struct xt_udp));
205 :
206 0 : sz += msz;
207 :
208 : /* Fill in basic part */
209 0 : entry = alloca0(sz);
210 0 : entry->next_offset = sz;
211 0 : 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 0 : r = entry_fill_basics(entry, protocol, in_interface, source, source_prefixlen, NULL, destination, destination_prefixlen);
217 0 : if (r < 0)
218 0 : return r;
219 :
220 : /* Fill in first match */
221 0 : m = (struct ipt_entry_match*) ((uint8_t*) entry + XT_ALIGN(sizeof(struct ipt_entry)));
222 0 : m->u.match_size = msz;
223 0 : if (protocol == IPPROTO_TCP) {
224 : struct xt_tcp *tcp;
225 :
226 0 : strncpy(m->u.user.name, "tcp", sizeof(m->u.user.name));
227 0 : tcp = (struct xt_tcp*) m->data;
228 0 : tcp->dpts[0] = tcp->dpts[1] = local_port;
229 0 : tcp->spts[0] = 0;
230 0 : tcp->spts[1] = 0xFFFF;
231 :
232 : } else {
233 : struct xt_udp *udp;
234 :
235 0 : strncpy(m->u.user.name, "udp", sizeof(m->u.user.name));
236 0 : udp = (struct xt_udp*) m->data;
237 0 : udp->dpts[0] = udp->dpts[1] = local_port;
238 0 : udp->spts[0] = 0;
239 0 : udp->spts[1] = 0xFFFF;
240 : }
241 :
242 : /* Fill in second match */
243 0 : m = (struct ipt_entry_match*) ((uint8_t*) entry + XT_ALIGN(sizeof(struct ipt_entry)) + msz);
244 0 : m->u.match_size =
245 : XT_ALIGN(sizeof(struct ipt_entry_match)) +
246 : XT_ALIGN(sizeof(struct xt_addrtype_info_v1));
247 0 : strncpy(m->u.user.name, "addrtype", sizeof(m->u.user.name));
248 0 : m->u.user.revision = 1;
249 0 : at = (struct xt_addrtype_info_v1*) m->data;
250 0 : at->dest = XT_ADDRTYPE_LOCAL;
251 :
252 : /* Fill in target part */
253 0 : t = ipt_get_target(entry);
254 0 : t->u.target_size =
255 : XT_ALIGN(sizeof(struct ipt_entry_target)) +
256 : XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
257 0 : strncpy(t->u.user.name, "DNAT", sizeof(t->u.user.name));
258 0 : mr = (struct nf_nat_ipv4_multi_range_compat*) t->data;
259 0 : mr->rangesize = 1;
260 0 : mr->range[0].flags = NF_NAT_RANGE_PROTO_SPECIFIED|NF_NAT_RANGE_MAP_IPS;
261 0 : mr->range[0].min_ip = mr->range[0].max_ip = remote->in.s_addr;
262 0 : if (protocol == IPPROTO_TCP)
263 0 : mr->range[0].min.tcp.port = mr->range[0].max.tcp.port = htons(remote_port);
264 : else
265 0 : mr->range[0].min.udp.port = mr->range[0].max.udp.port = htons(remote_port);
266 :
267 0 : mask = alloca0(sz);
268 0 : memset(mask, 0xFF, sz);
269 :
270 0 : if (add) {
271 : /* Add the PREROUTING rule, if it is missing so far */
272 0 : if (!iptc_check_entry("PREROUTING", entry, (unsigned char*) mask, h)) {
273 0 : if (errno != ENOENT)
274 0 : return -EINVAL;
275 :
276 0 : if (!iptc_insert_entry("PREROUTING", entry, 0, h))
277 0 : return -errno;
278 : }
279 :
280 : /* If a previous remote is set, remove its entry */
281 0 : if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) {
282 0 : mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr;
283 :
284 0 : if (!iptc_delete_entry("PREROUTING", entry, (unsigned char*) mask, h)) {
285 0 : if (errno != ENOENT)
286 0 : return -errno;
287 : }
288 :
289 0 : 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 0 : if (!in_interface) {
294 :
295 : /* Don't apply onto loopback addresses */
296 0 : if (!destination) {
297 0 : entry->ip.dst.s_addr = htobe32(0x7F000000);
298 0 : entry->ip.dmsk.s_addr = htobe32(0xFF000000);
299 0 : entry->ip.invflags = IPT_INV_DSTIP;
300 : }
301 :
302 0 : if (!iptc_check_entry("OUTPUT", entry, (unsigned char*) mask, h)) {
303 0 : if (errno != ENOENT)
304 0 : return -errno;
305 :
306 0 : if (!iptc_insert_entry("OUTPUT", entry, 0, h))
307 0 : return -errno;
308 : }
309 :
310 : /* If a previous remote is set, remove its entry */
311 0 : if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) {
312 0 : mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr;
313 :
314 0 : if (!iptc_delete_entry("OUTPUT", entry, (unsigned char*) mask, h)) {
315 0 : if (errno != ENOENT)
316 0 : return -errno;
317 : }
318 : }
319 : }
320 : } else {
321 0 : if (!iptc_delete_entry("PREROUTING", entry, (unsigned char*) mask, h)) {
322 0 : if (errno != ENOENT)
323 0 : return -errno;
324 : }
325 :
326 0 : if (!in_interface) {
327 0 : if (!destination) {
328 0 : entry->ip.dst.s_addr = htobe32(0x7F000000);
329 0 : entry->ip.dmsk.s_addr = htobe32(0xFF000000);
330 0 : entry->ip.invflags = IPT_INV_DSTIP;
331 : }
332 :
333 0 : if (!iptc_delete_entry("OUTPUT", entry, (unsigned char*) mask, h)) {
334 0 : if (errno != ENOENT)
335 0 : return -errno;
336 : }
337 : }
338 : }
339 :
340 0 : if (!iptc_commit(h))
341 0 : return -errno;
342 :
343 0 : return 0;
344 : }
|