From b413f85092c7118bf44be202cafa0e6d737c1d20 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Wed, 17 Dec 2014 16:44:14 +0100 Subject: [PATCH] [3553] MAC extraction from DOCSIS options implemented. --- src/lib/dhcp/dhcp4.h | 4 ++ src/lib/dhcp/docsis3_option_defs.h | 9 ++++- src/lib/dhcp/hwaddr.cc | 3 +- src/lib/dhcp/hwaddr.h | 11 +++++- src/lib/dhcp/pkt.cc | 22 ++++++++++- src/lib/dhcp/pkt.h | 26 +++++++++++++ src/lib/dhcp/pkt4.h | 22 +++++++++++ src/lib/dhcp/pkt6.cc | 59 +++++++++++++++++++++++++++++- src/lib/dhcp/pkt6.h | 35 +++++++++++++++++- 9 files changed, 184 insertions(+), 7 deletions(-) diff --git a/src/lib/dhcp/dhcp4.h b/src/lib/dhcp/dhcp4.h index 2b2440512c..5b69f9df23 100644 --- a/src/lib/dhcp/dhcp4.h +++ b/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 diff --git a/src/lib/dhcp/docsis3_option_defs.h b/src/lib/dhcp/docsis3_option_defs.h index 6031f8d611..174508eece 100644 --- a/src/lib/dhcp/docsis3_option_defs.h +++ b/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. }; diff --git a/src/lib/dhcp/hwaddr.cc b/src/lib/dhcp/hwaddr.cc index 5d76ec723d..8c773e1fca 100644 --- a/src/lib/dhcp/hwaddr.cc +++ b/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) { diff --git a/src/lib/dhcp/hwaddr.h b/src/lib/dhcp/hwaddr.h index 3aa2046a1c..3776e9b0dc 100644 --- a/src/lib/dhcp/hwaddr.h +++ b/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; /// @} diff --git a/src/lib/dhcp/pkt.cc b/src/lib/dhcp/pkt.cc index bb6cd9f6ef..31ea660401 100755 --- a/src/lib/dhcp/pkt.cc +++ b/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()); diff --git a/src/lib/dhcp/pkt.h b/src/lib/dhcp/pkt.h index 5fd5056b87..8a8e6c46b1 100755 --- a/src/lib/dhcp/pkt.h +++ b/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_; diff --git a/src/lib/dhcp/pkt4.h b/src/lib/dhcp/pkt4.h index ac88e3ccd6..7938ecb1d8 100755 --- a/src/lib/dhcp/pkt4.h +++ b/src/lib/dhcp/pkt4.h @@ -414,6 +414,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 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. @@ -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_; diff --git a/src/lib/dhcp/pkt6.cc b/src/lib/dhcp/pkt6.cc index 69e3f1faac..73735325f4 100755 --- a/src/lib/dhcp/pkt6.cc +++ b/src/lib/dhcp/pkt6.cc @@ -15,7 +15,10 @@ #include #include #include +#include +#include #include +#include #include #include #include @@ -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()); } } diff --git a/src/lib/dhcp/pkt6.h b/src/lib/dhcp/pkt6.h index 9a598f9d1d..78986a026f 100755 --- a/src/lib/dhcp/pkt6.h +++ b/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. /// -- 2.47.3