From: Remi Gacogne Date: Wed, 17 Jun 2020 09:02:36 +0000 (+0200) Subject: dnsdist: Add optional masks to KeyValueLookupKeySourceIP X-Git-Tag: dnsdist-1.5.0-rc3^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a6bee8afb40255d93b8a18f524eef99c452f701a;p=thirdparty%2Fpdns.git dnsdist: Add optional masks to KeyValueLookupKeySourceIP --- diff --git a/pdns/dnsdist-console.cc b/pdns/dnsdist-console.cc index bb9d6998ba..9b3255db48 100644 --- a/pdns/dnsdist-console.cc +++ b/pdns/dnsdist-console.cc @@ -419,7 +419,7 @@ const std::vector g_consoleKeywords{ { "inClientStartup", true, "", "returns true during console client parsing of configuration" }, { "includeDirectory", true, "path", "include configuration files from `path`" }, { "KeyValueLookupKeyQName", true, "[wireFormat]", "Return a new KeyValueLookupKey object that, when passed to KeyValueStoreLookupAction or KeyValueStoreLookupRule, will return the qname of the query, either in wire format (default) or in plain text if 'wireFormat' is false" }, - { "KeyValueLookupKeySourceIP", true, "", "Return a new KeyValueLookupKey object that, when passed to KeyValueStoreLookupAction or KeyValueStoreLookupRule, will return the source IP of the client in network byte-order." }, + { "KeyValueLookupKeySourceIP", true, "[v4Mask [, v6Mask]]", "Return a new KeyValueLookupKey object that, when passed to KeyValueStoreLookupAction or KeyValueStoreLookupRule, will return the (possibly bitmasked) source IP of the client in network byte-order." }, { "KeyValueLookupKeySuffix", true, "[minLabels [,wireFormat]]", "Return a new KeyValueLookupKey object that, when passed to KeyValueStoreLookupAction or KeyValueStoreLookupRule, will return a vector of keys based on the labels of the qname in DNS wire format or plain text" }, { "KeyValueLookupKeyTag", true, "tag", "Return a new KeyValueLookupKey object that, when passed to KeyValueStoreLookupAction or KeyValueStoreLookupRule, will return the value of the corresponding tag for this query, if it exists" }, { "KeyValueStoreLookupAction", true, "kvs, lookupKey, destinationTag", "does a lookup into the key value store referenced by 'kvs' using the key returned by 'lookupKey', and storing the result if any into the tag named 'destinationTag'" }, diff --git a/pdns/dnsdistdist/dnsdist-kvs.cc b/pdns/dnsdistdist/dnsdist-kvs.cc index 090a17c2e2..3b60dfc2ac 100644 --- a/pdns/dnsdistdist/dnsdist-kvs.cc +++ b/pdns/dnsdistdist/dnsdist-kvs.cc @@ -28,12 +28,15 @@ std::vector KeyValueLookupKeySourceIP::getKeys(const ComboAddress& addr) { std::vector result; + ComboAddress truncated(addr); - if (addr.sin4.sin_family == AF_INET) { - result.emplace_back(reinterpret_cast(&addr.sin4.sin_addr.s_addr), sizeof(addr.sin4.sin_addr.s_addr)); + if (truncated.isIPv4()) { + truncated.truncate(d_v4Mask); + result.emplace_back(reinterpret_cast(&truncated.sin4.sin_addr.s_addr), sizeof(truncated.sin4.sin_addr.s_addr)); } - else if (addr.sin4.sin_family == AF_INET6) { - result.emplace_back(reinterpret_cast(&addr.sin6.sin6_addr.s6_addr), sizeof(addr.sin6.sin6_addr.s6_addr)); + else if (truncated.isIPv6()) { + truncated.truncate(d_v6Mask); + result.emplace_back(reinterpret_cast(&truncated.sin6.sin6_addr.s6_addr), sizeof(truncated.sin6.sin6_addr.s6_addr)); } return result; diff --git a/pdns/dnsdistdist/dnsdist-kvs.hh b/pdns/dnsdistdist/dnsdist-kvs.hh index 997de05a2b..f0968b08ab 100644 --- a/pdns/dnsdistdist/dnsdist-kvs.hh +++ b/pdns/dnsdistdist/dnsdist-kvs.hh @@ -36,6 +36,10 @@ public: class KeyValueLookupKeySourceIP: public KeyValueLookupKey { public: + KeyValueLookupKeySourceIP(uint8_t v4Mask, uint8_t v6Mask): d_v4Mask(v4Mask), d_v6Mask(v6Mask) + { + } + std::vector getKeys(const ComboAddress& addr); std::vector getKeys(const DNSQuestion& dq) override @@ -45,8 +49,11 @@ public: std::string toString() const override { - return "source IP"; + return "source IP (masked to " + std::to_string(d_v4Mask) + " (v4) / " + std::to_string(d_v6Mask) + " (v6) bits)"; } +private: + uint8_t d_v4Mask; + uint8_t d_v6Mask; }; class KeyValueLookupKeyQName: public KeyValueLookupKey diff --git a/pdns/dnsdistdist/dnsdist-lua-bindings-kvs.cc b/pdns/dnsdistdist/dnsdist-lua-bindings-kvs.cc index a026cfb11b..3bc185056e 100644 --- a/pdns/dnsdistdist/dnsdist-lua-bindings-kvs.cc +++ b/pdns/dnsdistdist/dnsdist-lua-bindings-kvs.cc @@ -26,8 +26,8 @@ void setupLuaBindingsKVS(bool client) { /* Key Value Store objects */ - g_lua.writeFunction("KeyValueLookupKeySourceIP", []() { - return std::shared_ptr(new KeyValueLookupKeySourceIP()); + g_lua.writeFunction("KeyValueLookupKeySourceIP", [](boost::optional v4Mask, boost::optional v6Mask) { + return std::shared_ptr(new KeyValueLookupKeySourceIP(v4Mask.get_value_or(32), v6Mask.get_value_or(128))); }); g_lua.writeFunction("KeyValueLookupKeyQName", [](boost::optional wireFormat) { return std::shared_ptr(new KeyValueLookupKeyQName(wireFormat ? *wireFormat : true)); @@ -65,7 +65,7 @@ void setupLuaBindingsKVS(bool client) if (keyVar.type() == typeid(ComboAddress)) { const auto ca = boost::get(&keyVar); - KeyValueLookupKeySourceIP lookup; + KeyValueLookupKeySourceIP lookup(32, 128); for (const auto& key : lookup.getKeys(*ca)) { if (kvs->getValue(key, result)) { return result; diff --git a/pdns/dnsdistdist/docs/reference/kvs.rst b/pdns/dnsdistdist/docs/reference/kvs.rst index 47e6994582..8519c88285 100644 --- a/pdns/dnsdistdist/docs/reference/kvs.rst +++ b/pdns/dnsdistdist/docs/reference/kvs.rst @@ -3,8 +3,6 @@ Key Value Store functions and objects These are all the functions, objects and methods related to the CDB and LMDB key value stores. -As of 1.4.0, the CDB and LMDB code is considered experimental. - A lookup into a key value store can be done via the :func:`KeyValueStoreLookupRule` rule or the :func:`KeyValueStoreLookupAction` action, using the usual selectors to match the incoming queries for which the lookup should be done. @@ -81,12 +79,18 @@ If the value found in the LMDB database for the key '\\8powerdns\\3com\\0' was ' :param bool wireFormat: Whether to do the lookup in wire format (default) or in plain text -.. function:: KeyValueLookupKeySourceIP() -> KeyValueLookupKey +.. function:: KeyValueLookupKeySourceIP([v4mask [, v6mask]]) -> KeyValueLookupKey .. versionadded:: 1.4.0 + .. versionchanged:: 1.5.0 + Optional parameters ``v4mask`` and ``v6mask`` added. + Return a new KeyValueLookupKey object that, when passed to :func:`KeyValueStoreLookupAction` or :func:`KeyValueStoreLookupRule`, will return the source IP of the client in network byte-order. + :param int v4mask: Mask applied to IPv4 addresses. Default is 32 (the whole address) + :param int v6mask: Mask applied to IPv6 addresses. Default is 128 (the whole address) + .. function:: KeyValueLookupKeySuffix([minLabels [, wireFormat]]) -> KeyValueLookupKey .. versionadded:: 1.4.0 diff --git a/pdns/dnsdistdist/docs/rules-actions.rst b/pdns/dnsdistdist/docs/rules-actions.rst index b1ff0bbf27..b13df7968f 100644 --- a/pdns/dnsdistdist/docs/rules-actions.rst +++ b/pdns/dnsdistdist/docs/rules-actions.rst @@ -606,8 +606,6 @@ These ``DNSRule``\ s be one of the following items: .. versionadded:: 1.4.0 - As of 1.4.0, this code is considered experimental. - Return true if the key returned by 'lookupKey' exists in the key value store referenced by 'kvs'. The store can be a CDB (:func:`newCDBKVStore`) or a LMDB database (:func:`newLMDBKVStore`). The key can be based on the qname (:func:`KeyValueLookupKeyQName` and :func:`KeyValueLookupKeySuffix`), @@ -1025,8 +1023,6 @@ The following actions exist. .. versionadded:: 1.4.0 - As of 1.4.0, this code is considered experimental. - Does a lookup into the key value store referenced by 'kvs' using the key returned by 'lookupKey', and storing the result if any into the tag named 'destinationTag'. The store can be a CDB (:func:`newCDBKVStore`) or a LMDB database (:func:`newLMDBKVStore`). diff --git a/pdns/dnsdistdist/test-dnsdistkvs_cc.cc b/pdns/dnsdistdist/test-dnsdistkvs_cc.cc index 41516d756a..9322f61dad 100644 --- a/pdns/dnsdistdist/test-dnsdistkvs_cc.cc +++ b/pdns/dnsdistdist/test-dnsdistkvs_cc.cc @@ -7,11 +7,14 @@ #include "dnsdist-kvs.hh" #if defined(HAVE_LMDB) || defined(HAVE_CDB) +static const ComboAddress v4ToMask("203.0.113.255"); +static const ComboAddress v6ToMask("2001:db8:ff:ff:ff:ff:ff:ff"); + static void doKVSChecks(std::unique_ptr& kvs, const ComboAddress& lc, const ComboAddress& rem, const DNSQuestion& dq, const DNSName& plaintextDomain) { /* source IP */ { - auto lookupKey = make_unique(); + auto lookupKey = make_unique(32, 128); std::string value; /* local address is not in the db, remote is */ BOOST_CHECK_EQUAL(kvs->getValue(std::string(reinterpret_cast(&lc.sin4.sin_addr.s_addr), sizeof(lc.sin4.sin_addr.s_addr)), value), false); @@ -27,6 +30,27 @@ static void doKVSChecks(std::unique_ptr& kvs, const ComboAddress& } } + /* masked source IP */ + { + auto lookupKey = make_unique(25, 65); + + auto keys = lookupKey->getKeys(v4ToMask); + BOOST_CHECK_EQUAL(keys.size(), 1U); + for (const auto& key : keys) { + std::string value; + BOOST_CHECK_EQUAL(kvs->getValue(key, value), true); + BOOST_CHECK_EQUAL(value, "this is the value for the masked v4 addr"); + } + + keys = lookupKey->getKeys(v6ToMask); + BOOST_CHECK_EQUAL(keys.size(), 1U); + for (const auto& key : keys) { + std::string value; + BOOST_CHECK_EQUAL(kvs->getValue(key, value), true); + BOOST_CHECK_EQUAL(value, "this is the value for the masked v6 addr"); + } + } + const DNSName subdomain = DNSName("sub") + *dq.qname; const DNSName notPDNS("not-powerdns.com."); @@ -221,6 +245,10 @@ BOOST_AUTO_TEST_CASE(test_LMDB) { gettime(&expiredTime); DNSQuestion dq(&qname, qtype, qclass, qname.wirelength(), &lc, &rem, &dh, bufferSize, queryLen, isTcp, &queryRealTime); + ComboAddress v4Masked(v4ToMask); + ComboAddress v6Masked(v6ToMask); + v4Masked.truncate(25); + v6Masked.truncate(65); const string dbPath("/tmp/test_lmdb.XXXXXX"); { @@ -228,6 +256,8 @@ BOOST_AUTO_TEST_CASE(test_LMDB) { auto transaction = env.getRWTransaction(); auto dbi = transaction->openDB("db-name", MDB_CREATE); transaction->put(dbi, MDBInVal(std::string(reinterpret_cast(&rem.sin4.sin_addr.s_addr), sizeof(rem.sin4.sin_addr.s_addr))), MDBInVal("this is the value for the remote addr")); + transaction->put(dbi, MDBInVal(std::string(reinterpret_cast(&v4Masked.sin4.sin_addr.s_addr), sizeof(v4Masked.sin4.sin_addr.s_addr))), MDBInVal("this is the value for the masked v4 addr")); + transaction->put(dbi, MDBInVal(std::string(reinterpret_cast(&v6Masked.sin6.sin6_addr.s6_addr), sizeof(v6Masked.sin6.sin6_addr.s6_addr))), MDBInVal("this is the value for the masked v6 addr")); transaction->put(dbi, MDBInVal(qname.toDNSStringLC()), MDBInVal("this is the value for the qname")); transaction->put(dbi, MDBInVal(plaintextDomain.toStringRootDot()), MDBInVal("this is the value for the plaintext domain")); transaction->commit(); @@ -273,6 +303,10 @@ BOOST_AUTO_TEST_CASE(test_CDB) { gettime(&expiredTime); DNSQuestion dq(&qname, qtype, qclass, qname.wirelength(), &lc, &rem, &dh, bufferSize, queryLen, isTcp, &queryRealTime); + ComboAddress v4Masked(v4ToMask); + ComboAddress v6Masked(v6ToMask); + v4Masked.truncate(25); + v6Masked.truncate(65); char db[] = "/tmp/test_cdb.XXXXXX"; { @@ -280,6 +314,8 @@ BOOST_AUTO_TEST_CASE(test_CDB) { BOOST_REQUIRE(fd >= 0); CDBWriter writer(fd); BOOST_REQUIRE(writer.addEntry(std::string(reinterpret_cast(&rem.sin4.sin_addr.s_addr), sizeof(rem.sin4.sin_addr.s_addr)), "this is the value for the remote addr")); + BOOST_REQUIRE(writer.addEntry(std::string(reinterpret_cast(&v4Masked.sin4.sin_addr.s_addr), sizeof(v4Masked.sin4.sin_addr.s_addr)), "this is the value for the masked v4 addr")); + BOOST_REQUIRE(writer.addEntry(std::string(reinterpret_cast(&v6Masked.sin6.sin6_addr.s6_addr), sizeof(v6Masked.sin6.sin6_addr.s6_addr)), "this is the value for the masked v6 addr")); BOOST_REQUIRE(writer.addEntry(qname.toDNSStringLC(), "this is the value for the qname")); BOOST_REQUIRE(writer.addEntry(plaintextDomain.toStringRootDot(), "this is the value for the plaintext domain")); writer.close();