Branch data 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 [ - + ]: 28 : 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 : 12 : 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 : 12 : _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 [ - + ]: 12 : if (af != AF_INET)
102 : 0 : return -EOPNOTSUPP;
103 : :
104 [ + - - + ]: 12 : if (!IN_SET(protocol, 0, IPPROTO_TCP, IPPROTO_UDP))
105 : 0 : return -EOPNOTSUPP;
106 : :
107 : 12 : h = iptc_init("nat");
108 [ + - ]: 12 : if (!h)
109 : 12 : 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 : 16 : 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 : 16 : _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 [ + + - + ]: 16 : assert(add || !previous_remote);
184 : :
185 [ - + ]: 16 : if (af != AF_INET)
186 : 0 : return -EOPNOTSUPP;
187 : :
188 [ + - - + ]: 16 : if (!IN_SET(protocol, IPPROTO_TCP, IPPROTO_UDP))
189 : 0 : return -EOPNOTSUPP;
190 : :
191 [ - + ]: 16 : if (local_port <= 0)
192 : 0 : return -EINVAL;
193 : :
194 [ - + ]: 16 : if (remote_port <= 0)
195 : 0 : return -EINVAL;
196 : :
197 : 16 : h = iptc_init("nat");
198 [ + - ]: 16 : if (!h)
199 : 16 : 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 : : }
|