Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dnsdist: Use 65535 instead of 255 to block all types via eBPF #15199

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions contrib/xdp-filter.ebpf.src
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
5 changes: 3 additions & 2 deletions contrib/xdp.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions pdns/dnsdistdist/bpf-filter.ebpf.src
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down
4 changes: 2 additions & 2 deletions pdns/dnsdistdist/bpf-filter.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::pair<ComboAddress, uint64_t>> getAddrStats();
std::vector<std::pair<Netmask, CounterAndActionValue>> getRangeRule();
Expand Down
2 changes: 1 addition & 1 deletion pdns/dnsdistdist/bpf-filter.main.ebpf
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
2 changes: 1 addition & 1 deletion pdns/dnsdistdist/bpf-filter.qname.ebpf
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
6 changes: 3 additions & 3 deletions pdns/dnsdistdist/dnsdist-lua-bindings.cc
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck)
luaCtx.registerFunction<void (std::shared_ptr<BPFFilter>::*)(const DNSName& qname, boost::optional<uint16_t> qtype, boost::optional<uint32_t> action)>("blockQName", [](const std::shared_ptr<BPFFilter>& bpf, const DNSName& qname, boost::optional<uint16_t> qtype, boost::optional<uint32_t> 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{};

Expand All @@ -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);
}
});

Expand Down Expand Up @@ -630,7 +630,7 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck)
});
luaCtx.registerFunction<void (std::shared_ptr<BPFFilter>::*)(const DNSName& qname, boost::optional<uint16_t> qtype)>("unblockQName", [](const std::shared_ptr<BPFFilter>& bpf, const DNSName& qname, boost::optional<uint16_t> qtype) {
if (bpf) {
return bpf->unblock(qname, qtype ? *qtype : 255);
return bpf->unblock(qname, qtype ? *qtype : 65535);
}
});

Expand Down
12 changes: 8 additions & 4 deletions pdns/dnsdistdist/docs/advanced/ebpf.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
14 changes: 10 additions & 4 deletions pdns/dnsdistdist/docs/reference/ebpf.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,20 @@ 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.

:param string Netmask: The ip range to block, allow or truncate
: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
Expand All @@ -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.

Expand Down
2 changes: 2 additions & 0 deletions pdns/dnsdistdist/docs/upgrade_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
--------------

Expand Down
32 changes: 31 additions & 1 deletion regression-tests.dnsdist/test_EBPF.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"})
Expand Down Expand Up @@ -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"))')
Expand Down