From: Christopher Zimmermann Date: Sat, 11 Jan 2020 14:58:55 +0000 (+0100) Subject: Port ipset to BSD pf tables X-Git-Tag: release-1.21.0rc1~48^2~8^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7d76e84953a1753529219bd8e33412f26ba721d8;p=thirdparty%2Funbound.git Port ipset to BSD pf tables --- diff --git a/config.h.in b/config.h.in index bd9b38bc0..0b1f8053a 100644 --- a/config.h.in +++ b/config.h.in @@ -371,6 +371,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_NETTLE_EDDSA_H +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_PFVAR_H + /* Use libnss for crypto */ #undef HAVE_NSS diff --git a/configure b/configure index fb1ce374e..6e40fa0f8 100755 --- 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 + #include + +" +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 diff --git a/configure.ac b/configure.ac index f96a24ef2..09bdf7c57 100644 --- a/configure.ac +++ b/configure.ac @@ -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 + #include + ]) ;; no|*) # nothing diff --git a/ipset/ipset.c b/ipset/ipset.c index bf4cbea47..347512406 100755 --- a/ipset/ipset.c +++ b/ipset/ipset.c @@ -17,9 +17,21 @@ #include "sldns/wire2str.h" #include "sldns/parseutil.h" +#include + +#ifdef HAVE_NET_PFVAR_H +#include +#include +#include +#include +#include +typedef intptr_t filter_dev; +#else #include #include #include +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) { diff --git a/ipset/ipset.h b/ipset/ipset.h index 6273ec789..66a4378b6 100755 --- a/ipset/ipset.h +++ b/ipset/ipset.h @@ -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,