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