]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[3207] Replaced selected subnet with lease address in user check output
authorThomas Markwalder <tmark@isc.org>
Fri, 25 Oct 2013 10:31:05 +0000 (06:31 -0400)
committerThomas Markwalder <tmark@isc.org>
Fri, 25 Oct 2013 10:31:05 +0000 (06:31 -0400)
User check hook library's outcome output is now generated in pkt_send
callout allowing the output of lease address (or prefix) in place
of selected subnet.

src/hooks/dhcp/user_chk/pkt_send_co.cc

index 7be256284aa51a464126076027811148a88ba37d..4e9c6b61454bf3059af7cfd915ec2c78598d6e9e 100644 (file)
 
 #include <asiolink/io_address.h>
 #include <hooks/hooks.h>
-#include <dhcp/pkt4.h>
 #include <dhcp/dhcp6.h>
+#include <dhcp/option_custom.h>
+#include <dhcp/option6_ia.h>
+#include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_iaprefix.h>
+#include <dhcp/pkt4.h>
 #include <dhcp/pkt6.h>
 #include <user_chk.h>
 
@@ -30,64 +34,14 @@ using namespace std;
 // issues related to namespaces.
 extern "C" {
 
-/// @brief Adds an entry to the end of the user check outcome file.
-///
-/// Each user entry is written in an ini-like format, with one name-value pair
-/// per line as follows:
-///
-/// id_type=<id type>
-/// client=<id str>
-/// subnet=<subnet str>
-/// registered=<is registered>"
-///
-/// where:
-/// <id type> text label of the id type: "HW_ADDR" or "DUID"
-/// <id str> user's id formatted as either isc::dhcp::Hwaddr.toText() or
-/// isc::dhcp::DUID.toText()
-/// <subnet str> selected subnet formatted as isc::dhcp::Subnet4::toText() or
-/// isc::dhcp::Subnet6::toText() as appropriate.
-/// <is registered> "yes" or "no"
-///
-/// Sample IPv4 entry would like this:
-///
-/// @code
-/// id_type=DUID
-/// client=00:01:00:01:19:ef:e6:3b:00:0c:01:02:03:04
-/// subnet=2001:db8:2::/64
-/// registered=yes
-/// id_type=duid
-/// @endcode
-///
-/// Sample IPv4 entry would like this:
-///
-/// @code
-/// id_type=DUID
-/// id_type=HW_ADDR
-/// client=hwtype=1 00:0c:01:02:03:05
-/// subnet=152.77.5.0/24
-/// registered=no
-/// @endcode
-///
-/// @param id_type_str text label identify the id type
-/// @param id_val_str text representation of the user id
-/// @param subnet_str text representation  of the selected subnet
-/// @param registered boolean indicating if the user is registered or not
-void generate_output_record(const std::string& id_type_str,
+extern void generate_output_record(const std::string& id_type_str,
                             const std::string& id_val_str,
                             const std::string& addr_str,
-                            const bool& registered)
-{
-    user_chk_output << "id_type=" << id_type_str << std::endl
-                    << "client=" << id_val_str << std::endl
-                    << "addr=" << addr_str << std::endl
-                    << "registered=" << (registered ? "yes" : "no")
-                    << std::endl;
-
-    // @todo Flush is here to ensure output is immediate for demo purposes.
-    // Performance would generally dictate not using it.
-    flush(user_chk_output);
-}
-
+                            const bool& registered);
+extern std::string getV6AddrStr (Pkt6Ptr response);
+extern std::string getAddrStrIA_NA(OptionPtr options);
+extern std::string getAddrStrIA_PD(OptionPtr options);
+extern bool checkIAStatus(boost::shared_ptr<Option6IA>& ia_opt);
 
 /// @brief  This callout is called at the "pkt4_send" hook.
 ///
@@ -102,9 +56,22 @@ int pkt4_send(CalloutHandle& handle) {
         Pkt4Ptr response;
         handle.getArgument("response4", response);
 
+        // @todo do we need to filter on type?
+#if 0
+        uint8_t packet_type = response->getType();
+        std::cout << "PACKET TYPE IS: " <<
+                  static_cast<int>(packet_type) << std::endl;
+
+        if ((packet_type != DHCPOFFER) && (packet_type != DHCPACK)) {
+            std::cout << "SKIPPING PACKET TYPE IS: "
+                      << static_cast<int>(packet_type) << std::endl;
+            return (0);
+        }
+#endif
+
         // Get the user id saved from the query packet.
         HWAddrPtr hwaddr;
-        handle.setContext(query_user_id_label, hwaddr);
+        handle.getContext(query_user_id_label, hwaddr);
 
         // Get registered_user pointer.
         UserPtr registered_user;
@@ -123,9 +90,9 @@ int pkt4_send(CalloutHandle& handle) {
             generate_output_record(UserId::HW_ADDRESS_STR, hwaddr->toText(),
                                    addr.toText(), true);
         } else {
-            // add default options based 
+            // add default options based
             // then generate not registered output record
-            std::cout << "DHCP UserCheckHook : pkt4_send no registered_user" 
+            std::cout << "DHCP UserCheckHook : pkt4_send no registered_user"
                       << std::endl;
             // Add the outcome entry to the output file.
             generate_output_record(UserId::HW_ADDRESS_STR, hwaddr->toText(),
@@ -153,12 +120,32 @@ int pkt6_send(CalloutHandle& handle) {
         Pkt6Ptr response;
         handle.getArgument("response6", response);
 
-        // Fetch the lease address. @todo
-        isc::asiolink::IOAddress addr("0.0.0.0");
+        // @todo do we need to filter on type?
+#if 0
+        uint8_t packet_type = response->getType();
+        std::cout << "PACKET TYPE IS: " <<
+                  static_cast<int>(packet_type) << std::endl;
+
+        if ((packet_type != DHCPV6_ADVERTISE) &&
+            (packet_type != DHCPV6_REPLY)) {
+            std::cout << "SKIPPING PACKET TYPE IS: "
+                      << static_cast<int>(packet_type) << std::endl;
+            return (0);
+        }
+#endif
+
+        // Fetch the lease address as a string
+        std::string addr_str = getV6AddrStr(response);
+        if (addr_str.empty()) {
+            // packet did not contain an address, must be failed.
+            std::cout << "pkt6_send: Skipping packet address is blank"
+                      << std::endl;
+            return (0);
+        }
 
         // Get the user id saved from the query packet.
         DuidPtr duid;
-        handle.setContext(query_user_id_label, duid);
+        handle.getContext(query_user_id_label, duid);
 
         // Get registered_user pointer.
         UserPtr registered_user;
@@ -171,15 +158,15 @@ int pkt6_send(CalloutHandle& handle) {
                       << registered_user->getUserId() << std::endl;
             // Add the outcome entry to the output file.
             generate_output_record(UserId::DUID_STR, duid->toText(),
-                                   addr.toText(), true);
+                                   addr_str, true);
         } else {
-            // add default options based 
+            // add default options based
             // then generate not registered output record
-            std::cout << "DHCP UserCheckHook : pkt6_send no registered_user" 
+            std::cout << "DHCP UserCheckHook : pkt6_send no registered_user"
                       << std::endl;
             // Add the outcome entry to the output file.
             generate_output_record(UserId::DUID_STR, duid->toText(),
-                                   addr.toText(), false);
+                                   addr_str, false);
         }
     } catch (const std::exception& ex) {
         std::cout << "DHCP UserCheckHook : pkt6_send unexpected error: "
@@ -190,4 +177,162 @@ int pkt6_send(CalloutHandle& handle) {
     return (0);
 }
 
+/// @brief Adds an entry to the end of the user check outcome file.
+///
+/// Each user entry is written in an ini-like format, with one name-value pair
+/// per line as follows:
+///
+/// id_type=<id type>
+/// client=<id str>
+/// subnet=<subnet str>
+/// registered=<is registered>"
+///
+/// where:
+/// <id type> text label of the id type: "HW_ADDR" or "DUID"
+/// <id str> user's id formatted as either isc::dhcp::Hwaddr.toText() or
+/// isc::dhcp::DUID.toText()
+/// <subnet str> selected subnet formatted as isc::dhcp::Subnet4::toText() or
+/// isc::dhcp::Subnet6::toText() as appropriate.
+/// <is registered> "yes" or "no"
+///
+/// Sample IPv4 entry would like this:
+///
+/// @code
+/// id_type=DUID
+/// client=00:01:00:01:19:ef:e6:3b:00:0c:01:02:03:04
+/// subnet=2001:db8:2::/64
+/// registered=yes
+/// id_type=duid
+/// @endcode
+///
+/// Sample IPv4 entry would like this:
+///
+/// @code
+/// id_type=DUID
+/// id_type=HW_ADDR
+/// client=hwtype=1 00:0c:01:02:03:05
+/// subnet=152.77.5.0/24
+/// registered=no
+/// @endcode
+///
+/// @param id_type_str text label identify the id type
+/// @param id_val_str text representation of the user id
+/// @param subnet_str text representation  of the selected subnet
+/// @param registered boolean indicating if the user is registered or not
+void generate_output_record(const std::string& id_type_str,
+                            const std::string& id_val_str,
+                            const std::string& addr_str,
+                            const bool& registered)
+{
+    user_chk_output << "id_type=" << id_type_str << std::endl
+                    << "client=" << id_val_str << std::endl
+                    << "addr=" << addr_str << std::endl
+                    << "registered=" << (registered ? "yes" : "no")
+                    << std::endl;
+
+    // @todo Flush is here to ensure output is immediate for demo purposes.
+    // Performance would generally dictate not using it.
+    flush(user_chk_output);
+}
+
+/// @brief Stringify the lease address or prefix IPv6 response packet
+std::string getV6AddrStr (Pkt6Ptr response) {
+    OptionPtr tmp = response->getOption(D6O_IA_NA);
+    if (tmp) {
+        return(getAddrStrIA_NA(tmp));
+    }
+
+    // IA_NA not there so try IA_PD
+    tmp = response->getOption(D6O_IA_PD);
+    if (!tmp) {
+        isc_throw (isc::BadValue, "Response has neither IA_NA nor IA_PD");
+    }
+
+    return(getAddrStrIA_PD(tmp));
+}
+
+/// @brief Stringify the lease address in an D6O_IA_NA option set
+/// @todo fill out this out
+std::string getAddrStrIA_NA(OptionPtr options) {
+    boost::shared_ptr<Option6IA> ia =
+        boost::dynamic_pointer_cast<Option6IA>(options);
+
+    if (!ia) {
+        isc_throw (isc::BadValue, "D6O_IA_NA option invalid");
+    }
+
+    // If status indicates a failure return a blank string.
+    if (!checkIAStatus(ia)) {
+        return (std::string(""));
+    }
+
+    options = ia->getOption(D6O_IAADDR);
+    if (!options) {
+        isc_throw (isc::BadValue, "D6O_IAADDR option missing");
+    }
+
+    boost::shared_ptr<Option6IAAddr> addr_option;
+    addr_option  = boost::dynamic_pointer_cast<Option6IAAddr>(options);
+    if (!addr_option) {
+        isc_throw (isc::BadValue, "D6O_IAADDR Option6IAAddr missing");
+    }
+
+    isc::asiolink::IOAddress addr = addr_option->getAddress();
+    return (addr.toText());
+}
+
+/// @brief Stringify the delegated prefix in an D6O_IA_DP option set
+std::string getAddrStrIA_PD(OptionPtr options) {
+    boost::shared_ptr<Option6IA> ia =
+        boost::dynamic_pointer_cast<Option6IA>(options);
+
+    if (!ia) {
+        isc_throw (isc::BadValue, "D6O_IA_PD option invalid");
+    }
+
+    // If status indicates a failure return a blank string.
+    if (!checkIAStatus(ia)) {
+        return (std::string(""));
+    }
+
+    options = ia->getOption(D6O_IAPREFIX);
+    if (!options) {
+        isc_throw(isc::BadValue, "D60_IAPREFIX option is missing");
+    }
+
+    boost::shared_ptr<Option6IAPrefix> addr_option;
+    addr_option = boost::dynamic_pointer_cast<Option6IAPrefix>(options);
+    if (!addr_option) {
+        isc_throw (isc::BadValue, "D6O_IA_PD addr option bad");
+    }
+
+    isc::asiolink::IOAddress addr = addr_option->getAddress();
+    uint8_t prefix_len = addr_option->getLength();
+
+    stringstream buf;
+    buf << addr.toText() << "/" << static_cast<int>(prefix_len);
+    return (buf.str());
+}
+
+/// @brief Tests given IA option set for successful status.
+/// @todo fill out this out
+bool checkIAStatus(boost::shared_ptr<Option6IA>& ia) {
+    OptionCustomPtr status =
+            boost::dynamic_pointer_cast
+                <OptionCustom>(ia->getOption(D6O_STATUS_CODE));
+
+   // If a non-zero status is present, bail.
+   if (status) {
+        int status_val = status->readInteger<uint16_t>(0);
+        if (status_val != 0) {
+            std::cout << "SKIPPING PACKET STATUS is not success:"
+                      << status_val << std::endl;
+            return (false);
+        }
+    }
+
+    return (true);
+}
+
+
 } // end extern "C"