diff --git a/src/lib/dhcp/pkt.cc b/src/lib/dhcp/pkt.cc index 1af44ba989..f9590425e8 100644 --- a/src/lib/dhcp/pkt.cc +++ b/src/lib/dhcp/pkt.cc @@ -140,6 +140,7 @@ Pkt::getMAC(uint32_t hw_addr_src) { if (hw_addr_src & HWAddr::HWADDR_SOURCE_RAW) { mac = getRemoteHWAddr(); if (mac) { + mac->source_ = HWAddr::HWADDR_SOURCE_RAW; return (mac); } else if (hw_addr_src == HWAddr::HWADDR_SOURCE_RAW) { // If we're interested only in RAW sockets as source of that info, @@ -228,47 +229,45 @@ Pkt::getMAC(uint32_t hw_addr_src) { HWAddrPtr Pkt::getMACFromIPv6(const isc::asiolink::IOAddress& addr) { + HWAddrPtr mac; - if (!addr.isV6LinkLocal()) { - return (HWAddrPtr()); + if (addr.isV6LinkLocal()) { + std::vector bin = addr.toBytes(); + + // Double check that it's of appropriate size + if ((bin.size() == isc::asiolink::V6ADDRESS_LEN) && + // Check that it's link-local (starts with fe80). + (bin[0] == 0xfe) && (bin[1] == 0x80) && + // Check that u bit is set and g is clear. + // See Section 2.5.1 of RFC2373 for details. + ((bin[8] & 3) == 2) && + // And that the IID is of EUI-64 type. + (bin[11] == 0xff) && (bin[12] == 0xfe)) { + + // Remove 8 most significant bytes + bin.erase(bin.begin(), bin.begin() + 8); + + // Ok, we're down to EUI-64 only now: XX:XX:XX:ff:fe:XX:XX:XX + bin.erase(bin.begin() + 3, bin.begin() + 5); + + // MAC-48 to EUI-64 involves inverting u bit (see explanation + // in Section 2.5.1 of RFC2373). We need to revert that. + bin[0] = bin[0] ^ 2; + + // Let's get the interface this packet was received on. + // We need it to get hardware type + IfacePtr iface = IfaceMgr::instance().getIface(iface_); + uint16_t hwtype = 0; // not specified + if (iface) { + hwtype = iface->getHWType(); + } + + mac.reset(new HWAddr(bin, hwtype)); + mac->source_ = HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL; + } } - std::vector bin = addr.toBytes(); - - // Double check that it's of appropriate size - if ((bin.size() != isc::asiolink::V6ADDRESS_LEN) || - - // Check that it's link-local (starts with fe80). - (bin[0] != 0xfe) || (bin[1] != 0x80) || - - // Check that u bit is set and g is clear. See Section 2.5.1 of RFC2373 - // for details. - ((bin[8] & 3) != 2) || - - // And that the IID is of EUI-64 type. - (bin[11] != 0xff) || (bin[12] != 0xfe)) { - return (HWAddrPtr()); - } - - // Remove 8 most significant bytes - bin.erase(bin.begin(), bin.begin() + 8); - - // Ok, we're down to EUI-64 only now: XX:XX:XX:ff:fe:XX:XX:XX - bin.erase(bin.begin() + 3, bin.begin() + 5); - - // MAC-48 to EUI-64 involves inverting u bit (see explanation in Section - // 2.5.1 of RFC2373). We need to revert that. - bin[0] = bin[0] ^ 2; - - // Let's get the interface this packet was received on. We need it to get - // hardware type - IfacePtr iface = IfaceMgr::instance().getIface(iface_); - uint16_t hwtype = 0; // not specified - if (iface) { - hwtype = iface->getHWType(); - } - - return (HWAddrPtr(new HWAddr(bin, hwtype))); + return (mac); } }; diff --git a/src/lib/dhcp/pkt6.cc b/src/lib/dhcp/pkt6.cc index 322224717c..d2d8cac576 100644 --- a/src/lib/dhcp/pkt6.cc +++ b/src/lib/dhcp/pkt6.cc @@ -453,9 +453,10 @@ Pkt6::unpackTCP() { HWAddrPtr Pkt6::getMACFromDUID() { + HWAddrPtr mac; OptionPtr opt_duid = getOption(D6O_CLIENTID); if (!opt_duid) { - return (HWAddrPtr()); + return (mac); } uint8_t hlen = opt_duid->getData().size(); @@ -470,31 +471,33 @@ Pkt6::getMACFromDUID() { { // 2 bytes of duid type, 2 bytes of hardware type and at least // 1 byte of actual identification - if (duid_data.size() < 5) { - // This duid is truncated. We can't extract anything from it. - return (HWAddrPtr()); + if (duid_data.size() >= 5) { + uint16_t hwtype = util::readUint16(&duid_data[2], + duid_data.size() - 2); + mac.reset(new HWAddr(&duid_data[4], duid_data.size() - 4, hwtype)); } - - uint16_t hwtype = util::readUint16(&duid_data[2], duid_data.size() - 2); - return (HWAddrPtr(new HWAddr(&duid_data[4], duid_data.size() - 4, - hwtype))); + break; } case DUID::DUID_LLT: { // 2 bytes of duid type, 2 bytes of hardware, 4 bytes for timestamp, // and at least 1 byte of actual identification - if (duid_data.size() < 9) { - // This duid is truncated. We can't extract anything from it. - return (HWAddrPtr()); + if (duid_data.size() >= 9) { + uint16_t hwtype = util::readUint16(&duid_data[2], + duid_data.size() - 2); + mac.reset(new HWAddr(&duid_data[8], duid_data.size() - 8, hwtype)); } - - uint16_t hwtype = util::readUint16(&duid_data[2], duid_data.size() - 2); - return (HWAddrPtr(new HWAddr(&duid_data[8], duid_data.size() - 8, - hwtype))); + break; } default: - return (HWAddrPtr()); + break; } + + if (mac) { + mac->source_ = HWAddr::HWADDR_SOURCE_DUID; + } + + return (mac); } std::string @@ -705,116 +708,113 @@ Pkt6::getMACFromSrcLinkLocalAddr() { HWAddrPtr Pkt6::getMACFromIPv6RelayOpt() { - if (relay_info_.empty()) { - // This is a direct message - return (HWAddrPtr()); - } - // RFC6969 Section 6: Look for the client_linklayer_addr option on the - // relay agent closest to the client - OptionPtr opt = getAnyRelayOption(D6O_CLIENT_LINKLAYER_ADDR, RELAY_GET_FIRST); - if (opt) { - const OptionBuffer data = opt->getData(); - if (data.size() < 3) { - // This client link address option is truncated. It's supposed to be - // 2 bytes of link-layer type followed by link-layer address. - return (HWAddrPtr()); - } + HWAddrPtr mac; - // +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()); + // This is not a direct message + if (!relay_info_.empty()) { + // RFC6969 Section 6: Look for the client_linklayer_addr option on the + // relay agent closest to the client + OptionPtr opt = getAnyRelayOption(D6O_CLIENT_LINKLAYER_ADDR, + RELAY_GET_FIRST); + if (opt) { + const OptionBuffer data = opt->getData(); + // This client link address option is supposed to be + // 2 bytes of link-layer type followed by link-layer address. + if (data.size() >= 3) { + // +2, -2 means to skip the initial 2 bytes which are + // hwaddress type + mac.reset(new HWAddr(&data[0] + 2, data.size() - 2, + opt->getUint16())); + + mac->source_ = HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION; + } + } } + + return mac; } HWAddrPtr Pkt6::getMACFromDocsisModem() { + HWAddrPtr mac; OptionVendorPtr vendor = boost::dynamic_pointer_cast< OptionVendor>(getOption(D6O_VENDOR_OPTS)); // Check if this is indeed DOCSIS3 environment - if (!vendor || vendor->getVendorId() != VENDOR_ID_CABLE_LABS) { - return (HWAddrPtr()); + if (vendor && vendor->getVendorId() == VENDOR_ID_CABLE_LABS) { + // If it is, try to get device-id option + OptionPtr device_id = vendor->getOption(DOCSIS3_V6_DEVICE_ID); + if (device_id) { + // If the option contains any data, use it as MAC address + if (!device_id->getData().empty()) { + mac.reset(new HWAddr(device_id->getData(), HTYPE_DOCSIS)); + mac->source_ = HWAddr::HWADDR_SOURCE_DOCSIS_MODEM; + } + } } - // If it is, try to get device-id option - OptionPtr device_id = vendor->getOption(DOCSIS3_V6_DEVICE_ID); - if (!device_id) { - return (HWAddrPtr()); - } - - // If the option contains any data, use it as MAC address - if (!device_id->getData().empty()) { - return (HWAddrPtr(new HWAddr(device_id->getData(), HTYPE_DOCSIS))); - } else { - return (HWAddrPtr()); - } + return mac; } 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()); + HWAddrPtr mac; + + // If the message passed through a CMTS, there'll + // CMTS-specific options in it. + if (!relay_info_.empty()) { + OptionVendorPtr vendor = boost::dynamic_pointer_cast< + OptionVendor>(getAnyRelayOption(D6O_VENDOR_OPTS, + RELAY_SEARCH_FROM_CLIENT)); + + // Check if this is indeed DOCSIS3 environment + if (vendor && vendor->getVendorId() == VENDOR_ID_CABLE_LABS) { + // Try to get cable modem mac + OptionPtr cm_mac = vendor->getOption(DOCSIS3_V6_CMTS_CM_MAC); + + // If the option contains any data, use it as MAC address + if (cm_mac && !cm_mac->getData().empty()) { + mac.reset(new HWAddr(cm_mac->getData(), HTYPE_DOCSIS)); + mac->source_ = HWAddr::HWADDR_SOURCE_DOCSIS_CMTS; + } + } } - OptionVendorPtr vendor = boost::dynamic_pointer_cast< - OptionVendor>(getAnyRelayOption(D6O_VENDOR_OPTS, - RELAY_SEARCH_FROM_CLIENT)); - - // Check if this is indeed DOCSIS3 environment - if (!vendor || vendor->getVendorId() != 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()); - } - - // If the option contains any data, use it as MAC address - if (!cm_mac->getData().empty()) { - return (HWAddrPtr(new HWAddr(cm_mac->getData(), HTYPE_DOCSIS))); - } else { - return (HWAddrPtr()); - } + return (mac); } HWAddrPtr Pkt6::getMACFromRemoteIdRelayOption() { - if (relay_info_.empty()) { - // This is a direct message - return (HWAddrPtr()); + HWAddrPtr mac; + + // If this is relayed message + if (!relay_info_.empty()) { + // Get remote-id option from a relay agent closest to the client + OptionPtr opt = getAnyRelayOption(D6O_REMOTE_ID, RELAY_GET_FIRST); + if (opt) { + const OptionBuffer data = opt->getData(); + // This remote-id option is supposed to be 4 bytes of + // of enterprise-number followed by remote-id. + if (data.size() >= 5) { + // Let's get the interface this packet was received on. + // We need it to get the hardware type. + IfacePtr iface = IfaceMgr::instance().getIface(iface_); + uint16_t hwtype = 0; // not specified + + // If we get the interface HW type, great! If not, + // let's not panic. + if (iface) { + hwtype = iface->getHWType(); + } + + // Skip the initial 4 bytes which are enterprise-number. + mac.reset(new HWAddr(&data[0] + 4, data.size() - 4, hwtype)); + mac->source_ = HWAddr::HWADDR_SOURCE_REMOTE_ID; + } + } } - // Get remote-id option from a relay agent closest to the client - OptionPtr opt = getAnyRelayOption(D6O_REMOTE_ID, RELAY_GET_FIRST); - if (opt) { - const OptionBuffer data = opt->getData(); - if (data.size() < 5) { - // This remote-id option is truncated. It's supposed to be - // 4 bytes of enterprise-number followed by remote-id. - return (HWAddrPtr()); - } - - // Let's get the interface this packet was received on. We need it to get - // the hardware type. - IfacePtr iface = IfaceMgr::instance().getIface(iface_); - uint16_t hwtype = 0; // not specified - - // If we get the interface HW type, great! If not, let's not panic. - if (iface) { - hwtype = iface->getHWType(); - } - - // Skip the initial 4 bytes which are enterprise-number. - return (HWAddrPtr(new HWAddr(&data[0] + 4, data.size() - 4, hwtype))); - } else { - return (HWAddrPtr()); - } + return (mac); } } // end of isc::dhcp namespace diff --git a/src/lib/dhcp/tests/pkt6_unittest.cc b/src/lib/dhcp/tests/pkt6_unittest.cc index 580f69a355..b8da3eca36 100644 --- a/src/lib/dhcp/tests/pkt6_unittest.cc +++ b/src/lib/dhcp/tests/pkt6_unittest.cc @@ -406,7 +406,7 @@ TEST_F(Pkt6Test, unpackVendorMalformed) { orig.push_back(3); orig.push_back(4); orig.push_back(1); // suboption type=0x101 - orig.push_back(1); + orig.push_back(1); orig.push_back(0); // suboption length=3 orig.push_back(3); orig.push_back(102); // data="foo" @@ -428,7 +428,7 @@ TEST_F(Pkt6Test, unpackVendorMalformed) { shortv[len_index] = 20; Pkt6Ptr too_short_vendor_pkt(new Pkt6(&shortv[0], shortv.size())); EXPECT_NO_THROW(too_short_vendor_pkt->unpack()); - + // Truncated option header is not accepted vector shorth = orig; shorth.resize(orig.size() - 4); @@ -996,10 +996,17 @@ TEST_F(Pkt6Test, getMAC) { // Let's check if setting IPv6 address improves the situation. IOAddress linklocal_eui64("fe80::204:06ff:fe08:0a0c"); pkt.setRemoteAddr(linklocal_eui64); - EXPECT_TRUE(pkt.getMAC(HWAddr::HWADDR_SOURCE_ANY)); - EXPECT_TRUE(pkt.getMAC(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL)); - EXPECT_TRUE(pkt.getMAC(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL | - HWAddr::HWADDR_SOURCE_RAW)); + HWAddrPtr mac; + ASSERT_TRUE(mac = pkt.getMAC(HWAddr::HWADDR_SOURCE_ANY)); + EXPECT_EQ(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL, mac->source_); + + ASSERT_TRUE(mac = pkt.getMAC(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL)); + EXPECT_EQ(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL, mac->source_); + + ASSERT_TRUE(mac = pkt.getMAC(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL | + HWAddr::HWADDR_SOURCE_RAW)); + EXPECT_EQ(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL, mac->source_); + pkt.setRemoteAddr(IOAddress("::")); // Let's invent a MAC @@ -1011,10 +1018,15 @@ TEST_F(Pkt6Test, getMAC) { pkt.setRemoteHWAddr(dummy_hwaddr); // Now we should be able to get something - ASSERT_TRUE(pkt.getMAC(HWAddr::HWADDR_SOURCE_ANY)); + ASSERT_TRUE(mac = pkt.getMAC(HWAddr::HWADDR_SOURCE_ANY)); + EXPECT_EQ(HWAddr::HWADDR_SOURCE_RAW, mac->source_); + ASSERT_TRUE(pkt.getMAC(HWAddr::HWADDR_SOURCE_RAW)); + EXPECT_EQ(HWAddr::HWADDR_SOURCE_RAW, mac->source_); + EXPECT_TRUE(pkt.getMAC(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL | HWAddr::HWADDR_SOURCE_RAW)); + EXPECT_EQ(HWAddr::HWADDR_SOURCE_RAW, mac->source_); // Check that the returned MAC is indeed the expected one ASSERT_TRUE(*dummy_hwaddr == *pkt.getMAC(HWAddr::HWADDR_SOURCE_ANY)); @@ -1050,6 +1062,7 @@ TEST_F(Pkt6Test, getMACFromIPv6LinkLocal_direct) { pkt.setRemoteAddr(linklocal_eui64); HWAddrPtr found = pkt.getMAC(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL); ASSERT_TRUE(found); + EXPECT_EQ(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL, found->source_); stringstream tmp; tmp << "hwtype=" << (int)iface->getHWType() << " f0:04:06:08:0a:0c"; @@ -1097,6 +1110,7 @@ TEST_F(Pkt6Test, getMACFromIPv6LinkLocal_singleRelay) { stringstream tmp; tmp << "hwtype=" << (int)iface->getHWType() << " f0:04:06:08:0a:0c"; EXPECT_EQ(tmp.str(), found->toText(true)); + EXPECT_EQ(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL, found->source_); } // Test checks whether getMACFromIPv6LinkLocal() returns the hardware (MAC) @@ -1151,6 +1165,7 @@ TEST_F(Pkt6Test, getMACFromIPv6LinkLocal_multiRelay) { stringstream tmp; tmp << "hwtype=" << iface->getHWType() << " 00:00:00:00:00:01"; EXPECT_EQ(tmp.str(), found->toText(true)); + EXPECT_EQ(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL, found->source_); } // Test checks whether getMACFromIPv6RelayOpt() returns the hardware (MAC) @@ -1184,6 +1199,7 @@ TEST_F(Pkt6Test, getMACFromIPv6RelayOpt_singleRelay) { stringstream tmp; tmp << "hwtype=1 0a:1b:0b:01:ca:fe"; EXPECT_EQ(tmp.str(), found->toText(true)); + EXPECT_EQ(HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION, found->source_); } // Test checks whether getMACFromIPv6RelayOpt() returns the hardware (MAC) @@ -1232,6 +1248,7 @@ TEST_F(Pkt6Test, getMACFromIPv6RelayOpt_multipleRelay) { stringstream tmp; tmp << "hwtype=1 fa:30:0b:fa:c0:fe"; EXPECT_EQ(tmp.str(), found->toText(true)); + EXPECT_EQ(HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION,found->source_); } TEST_F(Pkt6Test, getMACFromDUID) { @@ -1271,6 +1288,7 @@ TEST_F(Pkt6Test, getMACFromDUID) { HWAddrPtr mac = pkt.getMAC(HWAddr::HWADDR_SOURCE_DUID); ASSERT_TRUE(mac); EXPECT_EQ("hwtype=7 0a:0b:0c:0d:0e:0f:10", mac->toText(true)); + EXPECT_EQ(HWAddr::HWADDR_SOURCE_DUID, mac->source_); // Let's test DUID-LL. This should work. ASSERT_TRUE(pkt.delOption(D6O_CLIENTID)); @@ -1278,6 +1296,7 @@ TEST_F(Pkt6Test, getMACFromDUID) { mac = pkt.getMAC(HWAddr::HWADDR_SOURCE_DUID); ASSERT_TRUE(mac); EXPECT_EQ("hwtype=11 0a:0b:0c:0d:0e", mac->toText(true)); + EXPECT_EQ(HWAddr::HWADDR_SOURCE_DUID, mac->source_); // Finally, let's try DUID-EN. This should fail, as EN type does not // contain any MAC address information. @@ -1303,6 +1322,7 @@ TEST_F(Pkt6Test, getMAC_DOCSIS_Modem) { // Let's check the info. EXPECT_EQ("hwtype=1 10:0d:7f:00:07:88", found->toText(true)); + EXPECT_EQ(HWAddr::HWADDR_SOURCE_DOCSIS_MODEM, found->source_); // Now let's remove the option OptionVendorPtr vendor = boost::dynamic_pointer_cast< @@ -1331,6 +1351,7 @@ TEST_F(Pkt6Test, getMAC_DOCSIS_CMTS) { // Let's check the info. EXPECT_EQ("hwtype=1 20:e5:2a:b8:15:14", found->toText(true)); + EXPECT_EQ(HWAddr::HWADDR_SOURCE_DOCSIS_CMTS, found->source_); // Now let's remove the suboption 1026 that is inserted by the // relay. @@ -1393,6 +1414,7 @@ TEST_F(Pkt6Test, getMACFromRemoteIdRelayOption) { tmp << "hwtype=" << (int)iface->getHWType() << " 0a:0b:0c:0d:0e:0f"; EXPECT_EQ(tmp.str(), mac->toText(true)); + EXPECT_EQ(HWAddr::HWADDR_SOURCE_REMOTE_ID, mac->source_); } // This test verifies that a solicit that passed through two relays is parsed