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