2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-30 05:27:55 +00:00

[4247] Pkt6 MAC extraction now sets HWAddr::source_ to the appropriate value

src/lib/dhcp/pkt.cc
    Pkt::getMAC(uint32_t hw_addr_src)
        sets HwAddr.source_ to HWADDR_SOURCE_RAW if appropriate

    Pkt::getMACFromIPv6(const isc::asiolink::IOAddress& addr)
        sets HwAddr.source_ to HWADDR_SOURCE_IPV6_LINK_LOCAL

src/lib/dhcp/pkt6.cc
    Pkt6::getMACFromDUID()
        sets HwAddr.source_ to HWADDR_SOURCE_DUID

    Pkt6::getMACFromIPv6RelayOpt()
        sets HwAddr.source_ to HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION;

    Pkt6::getMACFromDocsisModem()         sets HwAddr.source_ to HWADDR_SOURCE_DOCSIS_MODEM;

    Pkt6::getMACFromDocsisCMTS()
        sets HwAddr.source_ to HWADDR_SOURCE_DOCSIS_CMTS;

    Pkt6::getMACFromRemoteIdRelayOption()
        sets HwAddr.source_ to HWADDR_SOURCE_REMOTE_ID;

src/lib/dhcp/tests/pkt6_unittest.cc
    Added checks for correct HWAddr::source_ values to the following tests:
    TEST_F(Pkt6Test, getMAC)
    TEST_F(Pkt6Test, getMAC)
    TEST_F(Pkt6Test, getMACFromIPv6LinkLocal_direct)
    TEST_F(Pkt6Test, getMACFromIPv6LinkLocal_singleRelay)
    TEST_F(Pkt6Test, getMACFromIPv6LinkLocal_multiRelay)
    TEST_F(Pkt6Test, getMACFromIPv6RelayOpt_singleRelay)
    TEST_F(Pkt6Test, getMACFromIPv6RelayOpt_multipleRelay)
    TEST_F(Pkt6Test, getMACFromDUID)
    TEST_F(Pkt6Test, getMACFromDUID)
    TEST_F(Pkt6Test, getMAC_DOCSIS_Modem)
    TEST_F(Pkt6Test, getMAC_DOCSIS_CMTS)
    TEST_F(Pkt6Test, getMACFromRemoteIdRelayOption)
This commit is contained in:
Thomas Markwalder 2015-12-15 14:19:50 -05:00
parent ed80773fde
commit d7433e8d89
3 changed files with 168 additions and 147 deletions

View File

@ -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<uint8_t> 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<uint8_t> 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);
}
};

View File

@ -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

View File

@ -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<uint8_t> 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