/* 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_FULL(struct xtc_handle*, iptc_free, NULL);
+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,
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;
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)) +
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;
}
}
- if (!iptc_commit(h))
+ if (!sym_iptc_commit(h))
return -errno;
return 0;
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;
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)) +
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;
}
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;
}
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;
}
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;
}
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;
+}