]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
Port ipset to BSD pf tables
authorChristopher Zimmermann <madroach@gmerlin.de>
Sat, 11 Jan 2020 14:58:55 +0000 (15:58 +0100)
committerChristopher Zimmermann <madroach@gmerlin.de>
Sun, 10 May 2020 20:30:25 +0000 (22:30 +0200)
config.h.in
configure
configure.ac
ipset/ipset.c
ipset/ipset.h

index bd9b38bc05bbc52244ed7c1c24703ad5ccb3b588..0b1f8053ade0c36c0a0544931de9a9f487c2ba60 100644 (file)
 /* Define to 1 if you have the <nettle/eddsa.h> header file. */
 #undef HAVE_NETTLE_EDDSA_H
 
+/* Define to 1 if you have the <net/pfvar.h> header file. */
+#undef HAVE_NET_PFVAR_H
+
 /* Use libnss for crypto */
 #undef HAVE_NSS
 
index fb1ce374e918b23e64ff3a04f2d72ee194ee467e..6e40fa0f86536f3c734b64c54312c397a9036db7 100755 (executable)
--- a/configure
+++ b/configure
@@ -21376,7 +21376,22 @@ $as_echo "#define USE_IPSET 1" >>confdefs.h
                IPSET_OBJ="ipset.lo"
 
 
-               # mnl
+               # BSD's pf
+               for ac_header in net/pfvar.h
+do :
+  ac_fn_c_check_header_compile "$LINENO" "net/pfvar.h" "ac_cv_header_net_pfvar_h" "
+                 #include <netinet/in.h>
+                 #include <net/if.h>
+
+"
+if test "x$ac_cv_header_net_pfvar_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_NET_PFVAR_H 1
+_ACEOF
+
+else
+
+                 # mnl
 
 # Check whether --with-libmnl was given.
 if test "${with_libmnl+set}" = set; then :
@@ -21385,28 +21400,34 @@ else
    withval="yes"
 fi
 
-               found_libmnl="no"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libmnl" >&5
+                 found_libmnl="no"
+                 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libmnl" >&5
 $as_echo_n "checking for libmnl... " >&6; }
-               if test x_$withval = x_ -o x_$withval = x_yes; then
-                       withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr"
-               fi
-               for dir in $withval ; do
-                       if test -f "$dir/include/libmnl/libmnl.h"; then
-                               found_libmnl="yes"
-                                                               if test "$dir" != "/usr"; then
-                                       CPPFLAGS="$CPPFLAGS -I$dir/include"
-                                       LDFLAGS="$LDFLAGS -L$dir/lib"
-                               fi
-                               { $as_echo "$as_me:${as_lineno-$LINENO}: result: found in $dir" >&5
+                 if test x_$withval = x_ -o x_$withval = x_yes; then
+                         withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr"
+                 fi
+                 for dir in $withval ; do
+                         if test -f "$dir/include/libmnl/libmnl.h"; then
+                                 found_libmnl="yes"
+                                                                 if test "$dir" != "/usr"; then
+                                         CPPFLAGS="$CPPFLAGS -I$dir/include"
+                                         LDFLAGS="$LDFLAGS -L$dir/lib"
+                                 fi
+                                 { $as_echo "$as_me:${as_lineno-$LINENO}: result: found in $dir" >&5
 $as_echo "found in $dir" >&6; }
-                               LIBS="$LIBS -lmnl"
-                               break;
-                       fi
-               done
-               if test x_$found_libmnl != x_yes; then
-                       as_fn_error $? "Could not find libmnl, libmnl.h" "$LINENO" 5
-               fi
+                                 LIBS="$LIBS -lmnl"
+                                 break;
+                         fi
+                 done
+                 if test x_$found_libmnl != x_yes
+                 then
+                         as_fn_error $? "Could not find libmnl, libmnl.h" "$LINENO" 5
+                 fi
+
+fi
+
+done
+
                ;;
     no|*)
        # nothing
index f96a24ef2dea547a9f173ace889ff0e5b8bd9927..09bdf7c57286df037935f4ded94767a5bb9b00c4 100644 (file)
@@ -1756,31 +1756,38 @@ case "$enable_ipset" in
                IPSET_OBJ="ipset.lo"
                AC_SUBST(IPSET_OBJ)
 
-               # mnl
-               AC_ARG_WITH(libmnl, AC_HELP_STRING([--with-libmnl=path],
-                       [specify explicit path for libmnl.]),
-                       [ ],[ withval="yes" ])
-               found_libmnl="no"
-               AC_MSG_CHECKING(for libmnl)
-               if test x_$withval = x_ -o x_$withval = x_yes; then
-                       withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr"
-               fi
-               for dir in $withval ; do
-                       if test -f "$dir/include/libmnl/libmnl.h"; then
-                               found_libmnl="yes"
-                               dnl assume /usr is in default path.
-                               if test "$dir" != "/usr"; then
-                                       CPPFLAGS="$CPPFLAGS -I$dir/include"
-                                       LDFLAGS="$LDFLAGS -L$dir/lib"
-                               fi
-                               AC_MSG_RESULT(found in $dir)
-                               LIBS="$LIBS -lmnl"
-                               break;
-                       fi
-               done
-               if test x_$found_libmnl != x_yes; then
-                       AC_ERROR([Could not find libmnl, libmnl.h])
-               fi
+               # BSD's pf
+               AC_CHECK_HEADERS([net/pfvar.h], [], [
+                 # mnl
+                 AC_ARG_WITH(libmnl, AC_HELP_STRING([--with-libmnl=path],
+                         [specify explicit path for libmnl.]),
+                         [ ],[ withval="yes" ])
+                 found_libmnl="no"
+                 AC_MSG_CHECKING(for libmnl)
+                 if test x_$withval = x_ -o x_$withval = x_yes; then
+                         withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr"
+                 fi
+                 for dir in $withval ; do
+                         if test -f "$dir/include/libmnl/libmnl.h"; then
+                                 found_libmnl="yes"
+                                 dnl assume /usr is in default path.
+                                 if test "$dir" != "/usr"; then
+                                         CPPFLAGS="$CPPFLAGS -I$dir/include"
+                                         LDFLAGS="$LDFLAGS -L$dir/lib"
+                                 fi
+                                 AC_MSG_RESULT(found in $dir)
+                                 LIBS="$LIBS -lmnl"
+                                 break;
+                         fi
+                 done
+                 if test x_$found_libmnl != x_yes
+                 then
+                         AC_ERROR([Could not find libmnl, libmnl.h])
+                 fi
+               ], [
+                 #include <netinet/in.h>
+                 #include <net/if.h>
+               ])
                ;;
     no|*)
        # nothing
index bf4cbea477b33ea55f63f49580c5a3496eccf8c6..347512406fed84d9394cc710559bc9a35ca643ee 100755 (executable)
 #include "sldns/wire2str.h"
 #include "sldns/parseutil.h"
 
+#include <stdint.h>
+
+#ifdef HAVE_NET_PFVAR_H
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <net/pfvar.h>
+typedef intptr_t filter_dev;
+#else
 #include <libmnl/libmnl.h>
 #include <linux/netfilter/nfnetlink.h>
 #include <linux/netfilter/ipset/ip_set.h>
+typedef struct mnl_socket * filter_dev;
+#endif
 
 #define BUFF_LEN 256
 
@@ -41,24 +53,95 @@ static int error_response(struct module_qstate* qstate, int id, int rcode) {
        return 0;
 }
 
-static struct mnl_socket * open_mnl_socket() {
-       struct mnl_socket *mnl;
+#ifdef HAVE_NET_PFVAR_H
+static void * open_filter() {
+       filter_dev dev;
 
-       mnl = mnl_socket_open(NETLINK_NETFILTER);
-       if (!mnl) {
+       dev = open("/dev/pf", O_RDWR);
+       if (dev == -1) {
+               log_err("open(\"/dev/pf\") failed");
+               return NULL;
+       }
+       else
+               return (void *)dev;
+}
+#else
+static void * open_filter() {
+       filter_dev dev;
+
+       dev = mnl_socket_open(NETLINK_NETFILTER);
+       if (!dev) {
                log_err("ipset: could not open netfilter.");
                return NULL;
        }
 
-       if (mnl_socket_bind(mnl, 0, MNL_SOCKET_AUTOPID) < 0) {
-               mnl_socket_close(mnl);
+       if (mnl_socket_bind(dev, 0, MNL_SOCKET_AUTOPID) < 0) {
+               mnl_socket_close(dev);
                log_err("ipset: could not bind netfilter.");
                return NULL;
        }
-       return mnl;
+       return dev;
 }
+#endif
+
+#ifdef HAVE_NET_PFVAR_H
+static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, int af) {
+       struct pfioc_table io;
+       struct pfr_addr addr;
+       const char *p;
+       int i;
+
+       bzero(&io, sizeof(io));
+       bzero(&addr, sizeof(addr));
+
+       p = strrchr(setname, '/');
+       if (p) {
+               i = p - setname;
+               if (i >= PATH_MAX) {
+                       errno = ENAMETOOLONG;
+                       return -1;
+               }
+               memcpy(io.pfrio_table.pfrt_anchor, setname, i);
+               if (i < PATH_MAX)
+                       io.pfrio_table.pfrt_anchor[i] = '\0';
+               p++;
+       }
+       else
+               p = setname;
 
-static int add_to_ipset(struct mnl_socket *mnl, const char *setname, const void *ipaddr, int af) {
+       if (strlen(p) >= PF_TABLE_NAME_SIZE) {
+               errno = ENAMETOOLONG;
+               return -1;
+       }
+       strlcpy(io.pfrio_table.pfrt_name, p, PF_TABLE_NAME_SIZE);
+
+       io.pfrio_buffer = &addr;
+       io.pfrio_size = 1;
+       io.pfrio_esize = sizeof(addr);
+
+       switch (af) {
+               case AF_INET:
+                       addr.pfra_ip4addr = *(struct in_addr *)ipaddr;
+                       addr.pfra_net = 32;
+                       break;
+               case AF_INET6:
+                       addr.pfra_ip6addr = *(struct in6_addr *)ipaddr;
+                       addr.pfra_net = 128;
+                       break;
+               default:
+               errno = EAFNOSUPPORT;
+               return -1;
+       }
+       addr.pfra_af = af;
+
+       if (ioctl(dev, DIOCRADDADDRS, &io) == -1) {
+               log_err("ioctl failed: %s", strerror(errno));
+               return -1;
+       }
+       return 0;
+}
+#else
+static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, int af) {
        struct nlmsghdr *nlh;
        struct nfgenmsg *nfg;
        struct nlattr *nested[2];
@@ -91,14 +174,15 @@ static int add_to_ipset(struct mnl_socket *mnl, const char *setname, const void
        mnl_attr_nest_end(nlh, nested[1]);
        mnl_attr_nest_end(nlh, nested[0]);
 
-       if (mnl_socket_sendto(mnl, nlh, nlh->nlmsg_len) < 0) {
+       if (mnl_socket_sendto(dev, nlh, nlh->nlmsg_len) < 0) {
                return -1;
        }
        return 0;
 }
+#endif
 
 static void
-ipset_add_rrset_data(struct ipset_env *ie, struct mnl_socket *mnl,
+ipset_add_rrset_data(struct ipset_env *ie,
        struct packed_rrset_data *d, const char* setname, int af,
        const char* dname)
 {
@@ -123,12 +207,16 @@ ipset_add_rrset_data(struct ipset_env *ie, struct mnl_socket *mnl,
                                        snprintf(ip, sizeof(ip), "(inet_ntop_error)");
                                verbose(VERB_QUERY, "ipset: add %s to %s for %s", ip, setname, dname);
                        }
-                       ret = add_to_ipset(mnl, setname, rr_data + 2, af);
+                       ret = add_to_ipset((filter_dev)ie->dev, setname, rr_data + 2, af);
                        if (ret < 0) {
                                log_err("ipset: could not add %s into %s", dname, setname);
 
-                               mnl_socket_close(mnl);
-                               ie->mnl = NULL;
+#if HAVE_NET_PFVAR_H
+                               /* don't close as we might not be able to open again due to dropped privs */
+#else
+                               mnl_socket_close((filter_dev)ie->dev);
+                               ie->dev = NULL;
+#endif
                                break;
                        }
                }
@@ -137,7 +225,7 @@ ipset_add_rrset_data(struct ipset_env *ie, struct mnl_socket *mnl,
 
 static int
 ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie,
-       struct mnl_socket *mnl, struct ub_packed_rrset_key *rrset,
+       struct ub_packed_rrset_key *rrset,
        const char *setname, int af)
 {
        static char dname[BUFF_LEN];
@@ -158,13 +246,16 @@ ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie,
 
        for (p = env->cfg->local_zones_ipset; p; p = p->next) {
                plen = strlen(p->str);
+               if (p->str[plen - 1] == '.') {
+                       plen--;
+               }
 
                if (dlen >= plen) {
                        s = dname + (dlen - plen);
 
                        if (strncasecmp(p->str, s, plen) == 0) {
                                d = (struct packed_rrset_data*)rrset->entry.data;
-                               ipset_add_rrset_data(ie, mnl, d, setname,
+                               ipset_add_rrset_data(ie, d, setname,
                                        af, dname);
                                break;
                        }
@@ -174,8 +265,6 @@ ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie,
 }
 
 static int ipset_update(struct module_env *env, struct dns_msg *return_msg, struct ipset_env *ie) {
-       struct mnl_socket *mnl;
-
        size_t i;
 
        const char *setname;
@@ -184,17 +273,17 @@ static int ipset_update(struct module_env *env, struct dns_msg *return_msg, stru
 
        int af;
 
-
-       mnl = (struct mnl_socket *)ie->mnl;
-       if (!mnl) {
+#ifdef HAVE_NET_PFVAR_H
+#else
+       if (!ie->dev) {
                // retry to create mnl socket
-               mnl = open_mnl_socket();
-               if (!mnl) {
+               ie->dev = open_filter();
+               if (!ie->dev) {
+                       log_warn("ipset open_filter failed");
                        return -1;
                }
-
-               ie->mnl = mnl;
        }
+#endif
 
        for (i = 0; i < return_msg->rep->rrset_count; ++i) {
                setname = NULL;
@@ -203,18 +292,18 @@ static int ipset_update(struct module_env *env, struct dns_msg *return_msg, stru
 
                if (rrset->rk.type == htons(LDNS_RR_TYPE_A)) {
                        af = AF_INET;
-                       if ((ie->v4_enabled == 1)) {
+                       if (ie->v4_enabled == 1) {
                                setname = ie->name_v4;
                        }
                } else {
                        af = AF_INET6;
-                       if ((ie->v6_enabled == 1)) {
+                       if (ie->v6_enabled == 1) {
                                setname = ie->name_v6;
                        }
                }
 
                if (setname) {
-                       if(ipset_check_zones_for_rrset(env, ie, mnl, rrset,
+                       if(ipset_check_zones_for_rrset(env, ie, rrset,
                                setname, af) == -1)
                                return -1;
                }
@@ -223,10 +312,10 @@ static int ipset_update(struct module_env *env, struct dns_msg *return_msg, stru
        return 0;
 }
 
-int ipset_setup(struct module_env* env, int id) {
+int ipset_init(struct module_env* env, int id) {
        struct ipset_env *ipset_env;
 
-       ipset_env = (struct ipset_env *)calloc(1, sizeof(struct ipset_env));
+       ipset_env = (struct ipset_env *)malloc(sizeof(struct ipset_env));
        if (!ipset_env) {
                log_err("malloc failure");
                return 0;
@@ -234,7 +323,28 @@ int ipset_setup(struct module_env* env, int id) {
 
        env->modinfo[id] = (void *)ipset_env;
 
-       ipset_env->mnl = NULL;
+#ifdef HAVE_NET_PFVAR_H
+       ipset_env->dev = open_filter();
+       if (!ipset_env->dev) {
+               log_err("ipset open_filter failed");
+               return 0;
+       }
+#else
+       ipset_env->dev = NULL;
+#endif
+       return 1;
+}
+
+int ipset_deinit(struct module_env* env, int id) {
+       struct ipset_env *ipset_env = env->modinfo[id];
+       close((filter_dev)ipset_env->dev);
+       free(ipset_env);
+       env->modinfo[id] = NULL;
+       return 1;
+}
+
+int ipset_setup(struct module_env* env, int id) {
+       struct ipset_env *ipset_env = env->modinfo[id];
 
        ipset_env->name_v4 = env->cfg->ipset_name_v4;
        ipset_env->name_v6 = env->cfg->ipset_name_v6;
@@ -251,7 +361,7 @@ int ipset_setup(struct module_env* env, int id) {
 }
 
 void ipset_desetup(struct module_env *env, int id) {
-       struct mnl_socket *mnl;
+       filter_dev dev;
        struct ipset_env *ipset_env;
 
        if (!env || !env->modinfo[id]) {
@@ -260,10 +370,14 @@ void ipset_desetup(struct module_env *env, int id) {
 
        ipset_env = (struct ipset_env *)env->modinfo[id];
 
-       mnl = (struct mnl_socket *)ipset_env->mnl;
-       if (mnl) {
-               mnl_socket_close(mnl);
-               ipset_env->mnl = NULL;
+       dev = (filter_dev)ipset_env->dev;
+       if (dev) {
+#if HAVE_NET_PFVAR_H
+               close(dev);
+#else
+               mnl_socket_close(dev);
+#endif
+               ipset_env->dev = NULL;
        }
 
        free(ipset_env);
@@ -373,8 +487,8 @@ size_t ipset_get_mem(struct module_env *env, int id) {
  */
 static struct module_func_block ipset_block = {
        "ipset",
-       &module_dummy_init, &module_dummy_init, &ipset_setup, &ipset_desetup, &ipset_operate,
-       &ipset_inform_super, &ipset_clear, &ipset_get_mem
+       &ipset_init, &ipset_deinit, &ipset_setup, &ipset_desetup,
+       &ipset_operate, &ipset_inform_super, &ipset_clear, &ipset_get_mem
 };
 
 struct module_func_block * ipset_get_funcblock(void) {
index 6273ec789931a7c5d204987187c21047ef79d53a..66a4378b628708857736858e1c25d97514225b3f 100755 (executable)
@@ -37,7 +37,7 @@ extern "C" {
 #endif
 
 struct ipset_env {
-    void* mnl;
+       void* dev;
 
        int v4_enabled;
        int v6_enabled;
@@ -51,8 +51,12 @@ struct ipset_qstate {
 };
 
 /** Init the ipset module */
-int ipset_setup(struct module_env* env, int id);
+int ipset_init(struct module_env* env, int id);
 /** Deinit the ipset module */
+int ipset_deinit(struct module_env* env, int id);
+/** Setup the ipset module */
+int ipset_setup(struct module_env* env, int id);
+/** Desetup the ipset module */
 void ipset_desetup(struct module_env* env, int id);
 /** Operate on an event on a query (in qstate). */
 void ipset_operate(struct module_qstate* qstate, enum module_ev event,