Browse Source

[3553] MAC extraction from DOCSIS options implemented.

Tomek Mrugalski 10 years ago
parent
commit
b413f85092

+ 4 - 0
src/lib/dhcp/dhcp4.h

@@ -54,6 +54,10 @@ static const uint16_t BOOTP_BROADCAST = 32768L;
 /* Possible values for hardware type (htype) field... */
 enum HType {
     HTYPE_ETHER = 1,   /* Ethernet 10Mbps */
+    HTYPE_DOCSIS = 1,  /* the traffic captures we have from cable modems as well
+                          as this list by IANA: http://www.iana.org/assignments/
+                          arp-parameters/arp-parameters.xhtml suggest that
+                          Ethernet (1) should be used in DOCSIS environment. */
     HTYPE_IEEE802 = 6, /* IEEE 802.2 Token Ring */
     HTYPE_FDDI = 8     /* FDDI */
     /// TODO Add infiniband here

+ 8 - 1
src/lib/dhcp/docsis3_option_defs.h

@@ -42,9 +42,14 @@ const int DOCSIS3_V4_DEFS_SIZE  = sizeof(DOCSIS3_V4_DEFS) / sizeof(OptionDefPara
 #define DOCSIS3_V6_TFTP_SERVERS 32
 #define DOCSIS3_V6_CONFIG_FILE 33
 #define DOCSIS3_V6_SYSLOG_SERVERS 34
+#define DOCSIS3_V6_DEVICE_ID 36
 #define DOCSIS3_V6_TIME_SERVERS 37
 #define DOCSIS3_V6_TIME_OFFSET 38
 
+// The following DOCSIS3 options are inserted by the CMTS (which acts as
+// a relay agent)
+#define DOCSIS3_V6_CMTS_CM_MAC 1026
+
 /// @brief Definitions of standard DHCPv6 options.
 const OptionDefParams DOCSIS3_V6_DEFS[] = {
     { "oro",            DOCSIS3_V6_ORO, OPT_UINT16_TYPE, true, NO_RECORD_DEF, "" },
@@ -54,7 +59,9 @@ const OptionDefParams DOCSIS3_V6_DEFS[] = {
     { "time-servers",   DOCSIS3_V6_TIME_SERVERS, OPT_IPV6_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
     { "config-file",    DOCSIS3_V6_CONFIG_FILE, OPT_STRING_TYPE, false, NO_RECORD_DEF, "" },
     { "syslog-servers", DOCSIS3_V6_SYSLOG_SERVERS, OPT_IPV6_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
-    { "time-offset",    DOCSIS3_V6_TIME_OFFSET, OPT_INT32_TYPE, false, NO_RECORD_DEF, "" }
+    { "device-id",      DOCSIS3_V6_DEVICE_ID, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
+    { "time-offset",    DOCSIS3_V6_TIME_OFFSET, OPT_INT32_TYPE, false, NO_RECORD_DEF, "" },
+    { "cmts-cm-mac",    DOCSIS3_V6_CMTS_CM_MAC, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" }
     // @todo add definitions for all remaning options.
 };
 

+ 2 - 1
src/lib/dhcp/hwaddr.cc

@@ -35,7 +35,8 @@ const uint32_t HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL = 0x00000004;
 const uint32_t HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION = 0x00000008;
 const uint32_t HWAddr::HWADDR_SOURCE_REMOTE_ID = 0x00000010;
 const uint32_t HWAddr::HWADDR_SOURCE_SUBSCRIBER_ID = 0x00000020;
-const uint32_t HWAddr::HWADDR_SOURCE_DOCSIS = 0x00000040;
+const uint32_t HWAddr::HWADDR_SOURCE_DOCSIS_CMTS = 0x00000040;
+const uint32_t HWAddr::HWADDR_SOURCE_DOCSIS_MODEM = 0x00000080;
 
 HWAddr::HWAddr()
     :htype_(HTYPE_ETHER), source_(0) {

+ 9 - 2
src/lib/dhcp/hwaddr.h

@@ -76,8 +76,15 @@ public:
 
     /// A CMTS (acting as DHCP relay agent) that supports DOCSIS standard
     /// can insert DOCSIS options that contain client's MAC address.
-    /// Client in this context would be a cable modem.
-    static const uint32_t HWADDR_SOURCE_DOCSIS;
+    /// This specific option is suboption 1026 in vendor-class option with
+    /// vendor-id=4491. Client in this context would be a cable modem.
+    static const uint32_t HWADDR_SOURCE_DOCSIS_CMTS;
+
+    /// A cable modem (acting as DHCP client) that supports DOCSIS standard
+    /// can insert DOCSIS options that contain client's MAC address.
+    /// This specific option is suboption 36 in vendor-class option with
+    /// vendor-id=4491.
+    static const uint32_t HWADDR_SOURCE_DOCSIS_MODEM;
 
     /// @}
 

+ 21 - 1
src/lib/dhcp/pkt.cc

@@ -182,8 +182,28 @@ Pkt::getMAC(uint32_t hw_addr_src) {
     // Method 6: From subscriber-id option inserted by a relay
 
     // Method 7: From docsis options
+    if (hw_addr_src & HWADDR_SOURCE_DOCSIS_CMTS) {
+        mac = getMACFromDocsisCMTS();
+        if (mac) {
+            return (mac);
+        } else if (hw_addr_src == HWADDR_SOURCE_DOCSIS_CMTS) {
+            // If we're interested only in CMTS options as a source of that
+            // info, there's no point in trying other options.
+            return (HWAddrPtr());
+        }
+    }
 
-    /// @todo: add other MAC acquisition methods here
+    // Method 8: From docsis options
+    if (hw_addr_src & HWADDR_SOURCE_DOCSIS_MODEM) {
+        mac = getMACFromDocsisModem();
+        if (mac) {
+            return (mac);
+        } else if (hw_addr_src == HWADDR_SOURCE_DOCSIS_MODEM) {
+            // If we're interested only in CMTS options as a source of that
+            // info, there's no point in trying other options.
+            return (HWAddrPtr());
+        }
+    }
 
     // Ok, none of the methods were suitable. Return NULL.
     return (HWAddrPtr());

+ 26 - 0
src/lib/dhcp/pkt.h

@@ -523,6 +523,32 @@ protected:
     HWAddrPtr
     getMACFromIPv6(const isc::asiolink::IOAddress& addr);
 
+    /// @brief Attempts to extract MAC/Hardware address from DOCSIS options
+    ///        inserted by the modem itself.
+    ///
+    /// This is a generic mechanism for extracting hardware address from the
+    /// DOCSIS options.
+    ///
+    /// @note This is a pure virtual method and must be implemented in
+    /// the derived classes. The @c Pkt6 class have respective implementation.
+    /// This method is currently not implemented in DHCPv4.
+    ///
+    /// @return hardware address (if necessary DOCSIS suboptions are present)
+    virtual HWAddrPtr getMACFromDocsisModem() = 0;
+
+    /// @brief Attempts to extract MAC/Hardware address from DOCSIS options
+    ///        inserted by the CMTS (the relay agent)
+    ///
+    /// This is a generic mechanism for extracting hardware address from the
+    /// DOCSIS options.
+    ///
+    /// @note This is a pure virtual method and must be implemented in
+    /// the derived classes. The @c Pkt6 class have respective implementation.
+    /// This method is currently not implemented in DHCPv4.
+    ///
+    /// @return hardware address (if necessary DOCSIS suboptions are present)
+    virtual HWAddrPtr getMACFromDocsisCMTS() = 0;
+
     /// Transaction-id (32 bits for v4, 24 bits for v6)
     uint32_t transid_;
 

+ 22 - 0
src/lib/dhcp/pkt4.h

@@ -416,6 +416,17 @@ protected:
 
     /// @brief No-op
     ///
+    /// This is a DHCPv4 version of the function that attempts to extract
+    /// MAC address from the options inserted by a cable modem. It is currently
+    /// not implemented for v4.
+    ///
+    /// @return always NULL
+    virtual HWAddrPtr getMACFromDocsisModem() {
+        return (HWAddrPtr());
+    }
+
+    /// @brief No-op
+    ///
     /// This method returns hardware address extracted from DUID.
     /// Currently it is a no-op, even though there's RFC that defines how to
     /// use DUID in DHCPv4 (see RFC4361). We may implement it one day.
@@ -425,6 +436,17 @@ protected:
         return (HWAddrPtr());
     }
 
+    /// @brief No-op
+    ///
+    /// This is a DHCPv4 version of the function that attempts to extract
+    /// MAC address from the options inserted by a CMTS. It is currently
+    /// not implemented for v4.
+    ///
+    /// @return always NULL
+    virtual HWAddrPtr getMACFromDocsisCMTS() {
+        return (HWAddrPtr());
+    }
+
     /// local HW address (dst if receiving packet, src if sending packet)
     HWAddrPtr local_hwaddr_;
 

+ 58 - 1
src/lib/dhcp/pkt6.cc

@@ -15,7 +15,10 @@
 #include <dhcp/dhcp6.h>
 #include <dhcp/libdhcp++.h>
 #include <dhcp/option.h>
+#include <dhcp/option_int.h>
+#include <dhcp/option_vendor_class.h>
 #include <dhcp/pkt6.h>
+#include <dhcp/docsis3_option_defs.h>
 #include <util/io_utilities.h>
 #include <exceptions/exceptions.h>
 #include <dhcp/duid.h>
@@ -620,8 +623,62 @@ Pkt6::getMACFromIPv6RelayOpt() {
         // +2, -2 means to skip the initial 2 bytes which are hwaddress type
         return (HWAddrPtr(new HWAddr(&data[0] + 2, data.size() - 2,
                                      opt->getUint16())));
+    } else {
+        return (HWAddrPtr());
+    }
+}
+
+HWAddrPtr
+Pkt6::getMACFromDocsisModem() {
+    OptionUint32Ptr vendor = boost::dynamic_pointer_cast<
+        OptionUint32>(getOption(D6O_VENDOR_OPTS));
+
+    // Check if this is indeed DOCSIS3 environment
+    if (!vendor || vendor->getValue() != VENDOR_ID_CABLE_LABS) {
+        return (HWAddrPtr());
+    }
+
+    // If it is, try to get device-id option
+    OptionPtr device_id = vendor->getOption(DOCSIS3_V6_DEVICE_ID);
+    if (!device_id) {
+        return (HWAddrPtr());
+    }
+
+    OptionBuffer buf = device_id->getData();
+    if (buf.size() > 1) {
+        return (HWAddrPtr(new HWAddr(device_id->getData(), HTYPE_DOCSIS)));
+    } else {
+        return (HWAddrPtr());
     }
-    else {
+}
+
+HWAddrPtr
+Pkt6::getMACFromDocsisCMTS() {
+    if (relay_info_.empty()) {
+        // This message didn't pass through a CMTS, so there won't be any
+        // CMTS-specific options in it.
+        return (HWAddrPtr());
+    }
+
+    OptionUint32Ptr vendor = boost::dynamic_pointer_cast<
+        OptionUint32>(getAnyRelayOption(D6O_VENDOR_OPTS,
+                                        RELAY_SEARCH_FROM_CLIENT));
+
+    // Check if this is indeed DOCSIS3 environment
+    if (!vendor || vendor->getValue() != VENDOR_ID_CABLE_LABS) {
+        return (HWAddrPtr());
+    }
+
+    // If it is, try to get cable modem mac
+    OptionPtr cm_mac = vendor->getOption(DOCSIS3_V6_CMTS_CM_MAC);
+    if (!cm_mac) {
+        return (HWAddrPtr());
+    }
+
+    OptionBuffer buf = cm_mac->getData();
+    if (buf.size() > 1) {
+        return (HWAddrPtr(new HWAddr(cm_mac->getData(), HTYPE_DOCSIS)));
+    } else {
         return (HWAddrPtr());
     }
 }

+ 34 - 1
src/lib/dhcp/pkt6.h

@@ -305,9 +305,42 @@ protected:
     /// @return Hardware address (or NULL)
     virtual HWAddrPtr getMACFromIPv6RelayOpt();
 
+    /// @brief Extract MAC/Hardware address from client-id.
+    ///
+    /// This method attempts to extract MAC/Hardware address from DUID sent
+    /// as client-id. This method may fail, as only DUID-LLT and DUID-LL are
+    /// based on link-layer addresses. Client may use other valid DUID types
+    /// and this method will fail.
+    ///
+    /// @return Hardware address (or NULL)
     virtual HWAddrPtr getMACFromDUID();
 
-    HWAddrPtr hwaddr_;
+    /// @brief Attempts to extract MAC/Hardware address from DOCSIS options
+    ///        inserted by the modem itself.
+    ///
+    /// The mechanism extracts that information from DOCSIS option
+    /// (vendor-specific info, vendor-id=4491, suboption 36). Note that
+    /// in a DOCSIS capable network, the MAC address information is provided
+    /// several times. The first is specified by the modem itself. The second
+    /// is added by the CMTS, which acts as a relay agent. This method
+    /// attempts to extract the former. See @ref getMACFromDocsisCMTS
+    /// for a similar method that extracts from the CMTS (relay) options.
+    ///
+    /// @return hardware address (if DOCSIS suboption 36 is present)
+    virtual HWAddrPtr getMACFromDocsisModem();
+
+    /// @brief Attempts to extract MAC/Hardware address from DOCSIS options.
+    ///
+    /// The DHCPv6 mechanism extracts that information from DOCSIS option
+    /// (vendor-specific info, vendor-id=4491, suboption 1026). Note that
+    /// in a DOCSIS capable network, the MAC address information is provided
+    /// several times. The first is specified by the modem itself. The second
+    /// is added by the CMTS, which acts as a relay agent. This method
+    /// attempts to extract the latter. See @ref getMACFromDocsisModem
+    /// for a similar method that extracts from the modem (client) options.
+    ///
+    /// @return hardware address (if DOCSIS suboption 1026 is present)
+    virtual HWAddrPtr getMACFromDocsisCMTS();
 
     /// @brief Builds on wire packet for TCP transmission.
     ///