]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/shared/firewall-util-iptables.c
tree-wide: make sure net/if.h is included before any linux/ header
[thirdparty/systemd.git] / src / shared / firewall-util-iptables.c
index fec3000e751e5a3037920874912a9cd6ceca41e6..044d2d074474c91891e4458e075c19477908a4aa 100644 (file)
@@ -1,19 +1,12 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-/* Temporary work-around for broken glibc vs. linux kernel header definitions
- * This is already fixed upstream, remove this when distributions have updated.
- */
-#define _NET_IF_H 1
-
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
 #include <arpa/inet.h>
 #include <endian.h>
 #include <errno.h>
 #include <stddef.h>
 #include <string.h>
-#include <net/if.h>
-#ifndef IFNAMSIZ
-#define IFNAMSIZ 16
-#endif
 #include <linux/if.h>
 #include <linux/netfilter_ipv4/ip_tables.h>
 #include <linux/netfilter/nf_nat.h>
 #include <libiptc/libiptc.h>
 
 #include "alloc-util.h"
+#include "dlfcn-util.h"
 #include "firewall-util.h"
 #include "firewall-util-private.h"
 #include "in-addr-util.h"
 #include "macro.h"
 #include "socket-util.h"
 
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct xtc_handle*, iptc_free);
+static DLSYM_FUNCTION(iptc_check_entry);
+static DLSYM_FUNCTION(iptc_commit);
+static DLSYM_FUNCTION(iptc_delete_entry);
+static DLSYM_FUNCTION(iptc_free);
+static DLSYM_FUNCTION(iptc_init);
+static DLSYM_FUNCTION(iptc_insert_entry);
+static DLSYM_FUNCTION(iptc_strerror);
+
+static void *iptc_dl = NULL;
+
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct xtc_handle*, sym_iptc_free, NULL);
 
 static int entry_fill_basics(
                 struct ipt_entry *entry,
@@ -86,7 +90,7 @@ int fw_iptables_add_masquerade(
                 unsigned source_prefixlen) {
 
         static const xt_chainlabel chain = "POSTROUTING";
-        _cleanup_(iptc_freep) struct xtc_handle *h = NULL;
+        _cleanup_(sym_iptc_freep) struct xtc_handle *h = NULL;
         struct ipt_entry *entry, *mask;
         struct ipt_entry_target *t;
         size_t sz;
@@ -102,9 +106,9 @@ int fw_iptables_add_masquerade(
         if (!source || source_prefixlen == 0)
                 return -EINVAL;
 
-        h = iptc_init("nat");
-        if (!h)
-                return -errno;
+        r = fw_iptables_init_nat(&h);
+        if (r < 0)
+                return r;
 
         sz = XT_ALIGN(sizeof(struct ipt_entry)) +
              XT_ALIGN(sizeof(struct ipt_entry_target)) +
@@ -128,19 +132,19 @@ int fw_iptables_add_masquerade(
         mr->rangesize = 1;
 
         /* Create a search mask entry */
-        mask = alloca(sz);
+        mask = alloca_safe(sz);
         memset(mask, 0xFF, sz);
 
         if (add) {
-                if (iptc_check_entry(chain, entry, (unsigned char*) mask, h))
+                if (sym_iptc_check_entry(chain, entry, (unsigned char*) mask, h))
                         return 0;
                 if (errno != ENOENT) /* if other error than not existing yet, fail */
                         return -errno;
 
-                if (!iptc_insert_entry(chain, entry, 0, h))
+                if (!sym_iptc_insert_entry(chain, entry, 0, h))
                         return -errno;
         } else {
-                if (!iptc_delete_entry(chain, entry, (unsigned char*) mask, h)) {
+                if (!sym_iptc_delete_entry(chain, entry, (unsigned char*) mask, h)) {
                         if (errno == ENOENT) /* if it's already gone, all is good! */
                                 return 0;
 
@@ -148,7 +152,7 @@ int fw_iptables_add_masquerade(
                 }
         }
 
-        if (!iptc_commit(h))
+        if (!sym_iptc_commit(h))
                 return -errno;
 
         return 0;
@@ -164,7 +168,7 @@ int fw_iptables_add_local_dnat(
                 const union in_addr_union *previous_remote) {
 
         static const xt_chainlabel chain_pre = "PREROUTING", chain_output = "OUTPUT";
-        _cleanup_(iptc_freep) struct xtc_handle *h = NULL;
+        _cleanup_(sym_iptc_freep) struct xtc_handle *h = NULL;
         struct ipt_entry *entry, *mask;
         struct ipt_entry_target *t;
         struct ipt_entry_match *m;
@@ -192,9 +196,9 @@ int fw_iptables_add_local_dnat(
         if (remote_port <= 0)
                 return -EINVAL;
 
-        h = iptc_init("nat");
-        if (!h)
-                return -errno;
+        r = fw_iptables_init_nat(&h);
+        if (r < 0)
+                return r;
 
         sz = XT_ALIGN(sizeof(struct ipt_entry)) +
              XT_ALIGN(sizeof(struct ipt_entry_match)) +
@@ -275,11 +279,11 @@ int fw_iptables_add_local_dnat(
 
         if (add) {
                 /* Add the PREROUTING rule, if it is missing so far */
-                if (!iptc_check_entry(chain_pre, entry, (unsigned char*) mask, h)) {
+                if (!sym_iptc_check_entry(chain_pre, entry, (unsigned char*) mask, h)) {
                         if (errno != ENOENT)
                                 return -EINVAL;
 
-                        if (!iptc_insert_entry(chain_pre, entry, 0, h))
+                        if (!sym_iptc_insert_entry(chain_pre, entry, 0, h))
                                 return -errno;
                 }
 
@@ -287,7 +291,7 @@ int fw_iptables_add_local_dnat(
                 if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) {
                         mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr;
 
-                        if (!iptc_delete_entry(chain_pre, entry, (unsigned char*) mask, h)) {
+                        if (!sym_iptc_delete_entry(chain_pre, entry, (unsigned char*) mask, h)) {
                                 if (errno != ENOENT)
                                         return -errno;
                         }
@@ -305,11 +309,11 @@ int fw_iptables_add_local_dnat(
                                 entry->ip.invflags = IPT_INV_DSTIP;
                         }
 
-                        if (!iptc_check_entry(chain_output, entry, (unsigned char*) mask, h)) {
+                        if (!sym_iptc_check_entry(chain_output, entry, (unsigned char*) mask, h)) {
                                 if (errno != ENOENT)
                                         return -errno;
 
-                                if (!iptc_insert_entry(chain_output, entry, 0, h))
+                                if (!sym_iptc_insert_entry(chain_output, entry, 0, h))
                                         return -errno;
                         }
 
@@ -317,14 +321,14 @@ int fw_iptables_add_local_dnat(
                         if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) {
                                 mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr;
 
-                                if (!iptc_delete_entry(chain_output, entry, (unsigned char*) mask, h)) {
+                                if (!sym_iptc_delete_entry(chain_output, entry, (unsigned char*) mask, h)) {
                                         if (errno != ENOENT)
                                                 return -errno;
                                 }
                         }
                 }
         } else {
-                if (!iptc_delete_entry(chain_pre, entry, (unsigned char*) mask, h)) {
+                if (!sym_iptc_delete_entry(chain_pre, entry, (unsigned char*) mask, h)) {
                         if (errno != ENOENT)
                                 return -errno;
                 }
@@ -336,15 +340,46 @@ int fw_iptables_add_local_dnat(
                                 entry->ip.invflags = IPT_INV_DSTIP;
                         }
 
-                        if (!iptc_delete_entry(chain_output, entry, (unsigned char*) mask, h)) {
+                        if (!sym_iptc_delete_entry(chain_output, entry, (unsigned char*) mask, h)) {
                                 if (errno != ENOENT)
                                         return -errno;
                         }
                 }
         }
 
-        if (!iptc_commit(h))
+        if (!sym_iptc_commit(h))
                 return -errno;
 
         return 0;
 }
+
+static int dlopen_iptc(void) {
+        return dlopen_many_sym_or_warn(
+                        &iptc_dl,
+                        "libip4tc.so.2", LOG_DEBUG,
+                        DLSYM_ARG(iptc_check_entry),
+                        DLSYM_ARG(iptc_commit),
+                        DLSYM_ARG(iptc_delete_entry),
+                        DLSYM_ARG(iptc_free),
+                        DLSYM_ARG(iptc_init),
+                        DLSYM_ARG(iptc_insert_entry),
+                        DLSYM_ARG(iptc_strerror));
+}
+
+int fw_iptables_init_nat(struct xtc_handle **ret) {
+        _cleanup_(sym_iptc_freep) struct xtc_handle *h = NULL;
+        int r;
+
+        r = dlopen_iptc();
+        if (r < 0)
+                return r;
+
+        h = sym_iptc_init("nat");
+        if (!h)
+                return log_debug_errno(errno, "Failed to init \"nat\" table: %s", sym_iptc_strerror(errno));
+
+        if (ret)
+                *ret = TAKE_PTR(h);
+
+        return 0;
+}