diff --git a/contrib/xdp-filter.ebpf.src b/contrib/xdp-filter.ebpf.src index f25ba2c08e38..31275d2ff738 100644 --- a/contrib/xdp-filter.ebpf.src +++ b/contrib/xdp-filter.ebpf.src @@ -143,8 +143,8 @@ static inline struct map_value* check_qname(struct cursor* c) return value; } - // check with Qtype 255 (*) - qkey.qtype = 255; + // check with Qtype 65535 (*) + qkey.qtype = 65535; return qnamefilter.lookup(&qkey); } diff --git a/contrib/xdp.py b/contrib/xdp.py index e5fb69ce40a5..5365e8e971f1 100644 --- a/contrib/xdp.py +++ b/contrib/xdp.py @@ -7,8 +7,9 @@ from bcc import BPF # Constants -QTYPES = {'LOC': 29, - '*': 255, +QTYPES = {'*': 65535, + 'LOC': 29, + 'ANY': 255, 'IXFR': 251, 'UINFO': 100, 'NSEC3': 50, diff --git a/pdns/dnsdistdist/bpf-filter.ebpf.src b/pdns/dnsdistdist/bpf-filter.ebpf.src index 9f5866906876..2f77ba03004c 100644 --- a/pdns/dnsdistdist/bpf-filter.ebpf.src +++ b/pdns/dnsdistdist/bpf-filter.ebpf.src @@ -389,7 +389,7 @@ int bpf_qname_filter(struct __sk_buff *skb) struct QNameValue* qvalue = qnamefilter.lookup(&qkey); if (qvalue && - (qvalue->qtype == 255 || qtype == qvalue->qtype)) { + (qvalue->qtype == 65535 || qtype == qvalue->qtype)) { __sync_fetch_and_add(&qvalue->counter, 1); return 0; } @@ -479,7 +479,7 @@ int bpf_dns_filter(struct __sk_buff *skb) { struct QNameValue* qvalue = qnamefilter.lookup(&qkey); if (qvalue && - (qvalue->qtype == 255 || qtype == qvalue->qtype)) { + (qvalue->qtype == 65535 || qtype == qvalue->qtype)) { __sync_fetch_and_add(&qvalue->counter, 1); return 0; } diff --git a/pdns/dnsdistdist/bpf-filter.hh b/pdns/dnsdistdist/bpf-filter.hh index 46b7eed9dc1b..eaca10e19479 100644 --- a/pdns/dnsdistdist/bpf-filter.hh +++ b/pdns/dnsdistdist/bpf-filter.hh @@ -90,10 +90,10 @@ public: void removeSocket(int sock); void block(const ComboAddress& addr, MatchAction action); void addRangeRule(const Netmask& address, bool force, BPFFilter::MatchAction action); - void block(const DNSName& qname, MatchAction action, uint16_t qtype = 255); + void block(const DNSName& qname, MatchAction action, uint16_t qtype = 65535); void unblock(const ComboAddress& addr); void rmRangeRule(const Netmask& address); - void unblock(const DNSName& qname, uint16_t qtype = 255); + void unblock(const DNSName& qname, uint16_t qtype = 65535); std::vector> getAddrStats(); std::vector> getRangeRule(); diff --git a/pdns/dnsdistdist/bpf-filter.main.ebpf b/pdns/dnsdistdist/bpf-filter.main.ebpf index 4b42a2ec6b71..ef0e85843436 100644 --- a/pdns/dnsdistdist/bpf-filter.main.ebpf +++ b/pdns/dnsdistdist/bpf-filter.main.ebpf @@ -104,7 +104,7 @@ BPF_RAW_INSN(BPF_JMP|BPF_CALL,0,0,0,BPF_FUNC_map_lookup_elem), BPF_MOV64_IMM(BPF_REG_7,2147483647), BPF_JMP_IMM(BPF_JEQ,BPF_REG_0,0,7), BPF_LDX_MEM(BPF_H,BPF_REG_1,BPF_REG_0,8), -BPF_JMP_IMM(BPF_JEQ,BPF_REG_1,255,2), +BPF_JMP_IMM(BPF_JEQ,BPF_REG_1,65535,2), BPF_ALU64_IMM(BPF_AND,BPF_REG_6,65535), BPF_JMP_REG(BPF_JNE,BPF_REG_1,BPF_REG_6,3), BPF_MOV64_IMM(BPF_REG_1,1), diff --git a/pdns/dnsdistdist/bpf-filter.qname.ebpf b/pdns/dnsdistdist/bpf-filter.qname.ebpf index c7d6094c0872..120e0620e2b7 100644 --- a/pdns/dnsdistdist/bpf-filter.qname.ebpf +++ b/pdns/dnsdistdist/bpf-filter.qname.ebpf @@ -4085,7 +4085,7 @@ BPF_RAW_INSN(BPF_JMP|BPF_CALL,0,0,0,BPF_FUNC_map_lookup_elem), BPF_MOV64_IMM(BPF_REG_1,2147483647), BPF_JMP_IMM(BPF_JEQ,BPF_REG_0,0,7), BPF_LDX_MEM(BPF_H,BPF_REG_2,BPF_REG_0,8), -BPF_JMP_IMM(BPF_JEQ,BPF_REG_2,255,2), +BPF_JMP_IMM(BPF_JEQ,BPF_REG_2,65535,2), BPF_ALU64_IMM(BPF_AND,BPF_REG_6,65535), BPF_JMP_REG(BPF_JNE,BPF_REG_6,BPF_REG_2,3), BPF_MOV64_IMM(BPF_REG_1,1), diff --git a/pdns/dnsdistdist/dnsdist-lua-bindings.cc b/pdns/dnsdistdist/dnsdist-lua-bindings.cc index 8f966b1a124f..a7b5458f2f46 100644 --- a/pdns/dnsdistdist/dnsdist-lua-bindings.cc +++ b/pdns/dnsdistdist/dnsdist-lua-bindings.cc @@ -579,7 +579,7 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) luaCtx.registerFunction::*)(const DNSName& qname, boost::optional qtype, boost::optional action)>("blockQName", [](const std::shared_ptr& bpf, const DNSName& qname, boost::optional qtype, boost::optional action) { if (bpf) { if (!action) { - return bpf->block(qname, BPFFilter::MatchAction::Drop, qtype ? *qtype : 255); + return bpf->block(qname, BPFFilter::MatchAction::Drop, qtype ? *qtype : 65535); } BPFFilter::MatchAction match{}; @@ -596,7 +596,7 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) default: throw std::runtime_error("Unsupported action for BPFFilter::blockQName"); } - return bpf->block(qname, match, qtype ? *qtype : 255); + return bpf->block(qname, match, qtype ? *qtype : 65535); } }); @@ -630,7 +630,7 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) }); luaCtx.registerFunction::*)(const DNSName& qname, boost::optional qtype)>("unblockQName", [](const std::shared_ptr& bpf, const DNSName& qname, boost::optional qtype) { if (bpf) { - return bpf->unblock(qname, qtype ? *qtype : 255); + return bpf->unblock(qname, qtype ? *qtype : 65535); } }); diff --git a/pdns/dnsdistdist/docs/advanced/ebpf.rst b/pdns/dnsdistdist/docs/advanced/ebpf.rst index 5b76b91b98e8..f82eea95f3d9 100644 --- a/pdns/dnsdistdist/docs/advanced/ebpf.rst +++ b/pdns/dnsdistdist/docs/advanced/ebpf.rst @@ -31,16 +31,20 @@ The BPF filter can be used to block incoming queries manually:: > bpf = newBPFFilter({ipv4MaxItems=1024, ipv6MaxItems=1024, qnamesMaxItems=1024}) > bpf:attachToAllBinds() > bpf:block(newCA("2001:DB8::42")) - > bpf:blockQName(newDNSName("evildomain.com"), 255) + > bpf:blockQName(newDNSName("evildomain.com"), 65535) > bpf:getStats() [2001:DB8::42]: 0 - evildomain.com. 255: 0 + evildomain.com. 65535: 0 > bpf:unblock(newCA("2001:DB8::42")) - > bpf:unblockQName(newDNSName("evildomain.com"), 255) + > bpf:unblockQName(newDNSName("evildomain.com"), 65535) > bpf:getStats() +.. note:: + Before 2.0.0 the value used to block queries for all types was 255. This was changed because it prevented blocking only queries for the ``ANY`` (255) qtype. + The :meth:`BPFFilter:blockQName` method can be used to block queries based on the exact qname supplied, in a case-insensitive way, and an optional qtype. -Using the 255 (ANY) qtype will block all queries for the qname, regardless of the qtype. +Using the ``65535`` value for the qtype will block all queries for the qname, regardless of the qtype. + Contrary to source address filtering, qname filtering only works over UDP. TCP qname filtering can be done the usual way:: addAction(AndRule({TCPRule(true), QNameSuffixRule("evildomain.com")}), DropAction()) diff --git a/pdns/dnsdistdist/docs/reference/ebpf.rst b/pdns/dnsdistdist/docs/reference/ebpf.rst index 6e5902f46f13..0f95511d5cd4 100644 --- a/pdns/dnsdistdist/docs/reference/ebpf.rst +++ b/pdns/dnsdistdist/docs/reference/ebpf.rst @@ -87,7 +87,7 @@ These are all the functions, objects and methods related to the :doc:`../advance .. versionadded:: 1.8.0 - Block all IP addresses in this range. + Block all IP addresses in this range. DNSDist eBPF code first checks if an exact IP match is found, then if a range matches, and finally if a DNSName does. @@ -95,9 +95,12 @@ These are all the functions, objects and methods related to the :doc:`../advance :param int action: set ``action`` to ``0`` to allow a range, set ``action`` to ``1`` to block a range, set ``action`` to ``2`` to truncate a range. :param bool force: When ``force`` is set to true, DNSDist always accepts adding a new item to BPF maps, even if the item to be added may already be included in the larger network range. - .. method:: BPFFilter:blockQName(name [, qtype=255]) + .. method:: BPFFilter:blockQName(name [, qtype=65535]) - Block queries for this exact qname. An optional qtype can be used, defaults to 255. + .. versionchanged:: 2.0.0 + Before 2.0.0 the value used to block queries for all types was 255. It also used to be the default value. This was changed because it prevented blocking only queries for the ``ANY`` (255) qtype. + + Block queries for this exact qname. An optional qtype can be used, defaults to 65535 which blocks queries for all types. :param DNSName name: The name to block :param int qtype: QType to block @@ -124,7 +127,10 @@ These are all the functions, objects and methods related to the :doc:`../advance List all range rule. - .. method:: BPFFilter:unblockQName(name [, qtype=255]) + .. method:: BPFFilter:unblockQName(name [, qtype=65535]) + + .. versionchanged:: 2.0.0 + Before 2.0.0 the value used to block queries for all types was 255. It also used to be the default value. This was changed because it prevented blocking only queries for the ``ANY`` (255) qtype. Remove this qname from the block list. diff --git a/pdns/dnsdistdist/docs/upgrade_guide.rst b/pdns/dnsdistdist/docs/upgrade_guide.rst index 427be84a3e24..cecd03104a72 100644 --- a/pdns/dnsdistdist/docs/upgrade_guide.rst +++ b/pdns/dnsdistdist/docs/upgrade_guide.rst @@ -9,6 +9,8 @@ Upgrade Guide :func:`showTLSContexts` has been renamed to :func:`showTLSFrontends`. :func:`getTLSContext` and the associated :class:`TLSContext` have been removed, please use :func:`getTLSFrontend` and the associated :class:`TLSFrontend` instead. +Our eBPF filtering code no longer treats the ``255``/``ANY`` qtype as a special value intended to block queries for all types, and will only block ``ANY`` queries instead. The reserved ``65535`` value now can be used to block queries for all qtypes. + 1.8.x to 1.9.0 -------------- diff --git a/regression-tests.dnsdist/test_EBPF.py b/regression-tests.dnsdist/test_EBPF.py index e8ca41178858..26d8912f68f9 100644 --- a/regression-tests.dnsdist/test_EBPF.py +++ b/regression-tests.dnsdist/test_EBPF.py @@ -31,7 +31,8 @@ class TestSimpleEBPF(DNSDistTest): bpf = newBPFFilter({ipv4MaxItems=10, ipv6MaxItems=10, qnamesMaxItems=10}) setDefaultBPFFilter(bpf) - bpf:blockQName(newDNSName("blocked.ebpf.tests.powerdns.com."), 255) + bpf:blockQName(newDNSName("blocked.ebpf.tests.powerdns.com."), 65535) + bpf:blockQName(newDNSName("blocked-any-only.ebpf.tests.powerdns.com."), 255) addTLSLocal("127.0.0.1:%d", "%s", "%s", { provider="openssl" }) addDOHLocal("127.0.0.1:%d", "%s", "%s", {"/"}, {library="nghttp2"}) @@ -95,6 +96,35 @@ def testQNameBlocked(self): receivedResponse.id = response.id self.assertEqual(response, receivedResponse) + def testQNameBlockedOnylForAny(self): + # unblock 127.0.0.1, just in case + self.sendConsoleCommand('bpf:unblock(newCA("127.0.0.1"))') + + name = 'blocked-any-only.ebpf.tests.powerdns.com.' + query = dns.message.make_query(name, 'ANY', 'IN', use_edns=False) + + # ANY should be blocked over Do53 UDP + for method in ["sendUDPQuery"]: + sender = getattr(self, method) + (_, receivedResponse) = sender(query, response=None, useQueue=False, timeout=0.5) + self.assertEqual(receivedResponse, None) + + query = dns.message.make_query(name, 'A', 'IN', use_edns=False) + response = dns.message.make_response(query) + rrset = dns.rrset.from_text(name, + 3600, + dns.rdataclass.IN, + dns.rdatatype.A, + '127.0.0.1') + response.answer.append(rrset) + # but A should NOT be blocked + for method in ["sendUDPQuery"]: + sender = getattr(self, method) + (receivedQuery, receivedResponse) = sender(query, response, timeout=1) + receivedQuery.id = query.id + self.assertEqual(query, receivedQuery) + self.assertEqual(response, receivedResponse) + def testClientIPBlocked(self): # block 127.0.0.1 self.sendConsoleCommand('bpf:block(newCA("127.0.0.1"))')