]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Add optional masks to KeyValueLookupKeySourceIP 9244/head
authorRemi Gacogne <remi.gacogne@powerdns.com>
Wed, 17 Jun 2020 09:02:36 +0000 (11:02 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Wed, 17 Jun 2020 09:05:33 +0000 (11:05 +0200)
pdns/dnsdist-console.cc
pdns/dnsdistdist/dnsdist-kvs.cc
pdns/dnsdistdist/dnsdist-kvs.hh
pdns/dnsdistdist/dnsdist-lua-bindings-kvs.cc
pdns/dnsdistdist/docs/reference/kvs.rst
pdns/dnsdistdist/docs/rules-actions.rst
pdns/dnsdistdist/test-dnsdistkvs_cc.cc

index bb9d6998ba95b99b9bc3f86d41191466c33d02b7..9b3255db48eb116430412153c966619f86ef0e54 100644 (file)
@@ -419,7 +419,7 @@ const std::vector<ConsoleKeyword> 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'" },
index 090a17c2e2e3e95745dee345f210d9ddb1ba21bf..3b60dfc2acd0bda2636c4acaaf46b895c16a5588 100644 (file)
 std::vector<std::string> KeyValueLookupKeySourceIP::getKeys(const ComboAddress& addr)
 {
   std::vector<std::string> result;
+  ComboAddress truncated(addr);
 
-  if (addr.sin4.sin_family == AF_INET) {
-    result.emplace_back(reinterpret_cast<const char*>(&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<const char*>(&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<const char*>(&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<const char*>(&truncated.sin6.sin6_addr.s6_addr), sizeof(truncated.sin6.sin6_addr.s6_addr));
   }
 
   return result;
index 997de05a2b086e15358da49c4fba01f9fd29af9f..f0968b08ab14b971e69d0acaf9097785f9be6fe7 100644 (file)
@@ -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<std::string> getKeys(const ComboAddress& addr);
 
   std::vector<std::string> 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
index a026cfb11b4d30a318c9f880423e6a1342ee1c1b..3bc185056ed0809afc5fb6210c9c2168b50361ca 100644 (file)
@@ -26,8 +26,8 @@
 void setupLuaBindingsKVS(bool client)
 {
   /* Key Value Store objects */
-  g_lua.writeFunction("KeyValueLookupKeySourceIP", []() {
-    return std::shared_ptr<KeyValueLookupKey>(new KeyValueLookupKeySourceIP());
+  g_lua.writeFunction("KeyValueLookupKeySourceIP", [](boost::optional<uint8_t> v4Mask, boost::optional<uint8_t> v6Mask) {
+    return std::shared_ptr<KeyValueLookupKey>(new KeyValueLookupKeySourceIP(v4Mask.get_value_or(32), v6Mask.get_value_or(128)));
   });
   g_lua.writeFunction("KeyValueLookupKeyQName", [](boost::optional<bool> wireFormat) {
     return std::shared_ptr<KeyValueLookupKey>(new KeyValueLookupKeyQName(wireFormat ? *wireFormat : true));
@@ -65,7 +65,7 @@ void setupLuaBindingsKVS(bool client)
 
     if (keyVar.type() == typeid(ComboAddress)) {
       const auto ca = boost::get<ComboAddress>(&keyVar);
-      KeyValueLookupKeySourceIP lookup;
+      KeyValueLookupKeySourceIP lookup(32, 128);
       for (const auto& key : lookup.getKeys(*ca)) {
         if (kvs->getValue(key, result)) {
           return result;
index 47e69945824f3b23c9d09f66d3cad84d678ea7ad..8519c8828501eb2094c153be8dc0c8ec805e8c2c 100644 (file)
@@ -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
index b1ff0bbf27c6827c8ddb29374710bdd55738740d..b13df7968f5ca779f777f2483446eda7cc458ae2 100644 (file)
@@ -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`).
index 41516d756af7b8d761d04ed1afd84be2bc661d1f..9322f61dad69a0a312fcb3bb32463b001fd90614 100644 (file)
@@ -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<KeyValueStore>& kvs, const ComboAddress& lc, const ComboAddress& rem, const DNSQuestion& dq, const DNSName& plaintextDomain)
 {
   /* source IP */
   {
-    auto lookupKey = make_unique<KeyValueLookupKeySourceIP>();
+    auto lookupKey = make_unique<KeyValueLookupKeySourceIP>(32, 128);
     std::string value;
     /* local address is not in the db, remote is */
     BOOST_CHECK_EQUAL(kvs->getValue(std::string(reinterpret_cast<const char*>(&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<KeyValueStore>& kvs, const ComboAddress&
     }
   }
 
+  /* masked source IP */
+  {
+    auto lookupKey = make_unique<KeyValueLookupKeySourceIP>(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<const char*>(&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<const char*>(&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<const char*>(&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<const char*>(&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<const char*>(&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<const char*>(&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();