Commit 0b5b2461 authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[3549] Pkt::getMAC(HWADDR_SOURCE_IPV6_LINK_LOCAL) implemented.

parent 0a83a583
......@@ -143,11 +143,11 @@ Pkt::getMAC(uint32_t hw_addr_src) {
// Method 2: Extracted from DUID-LLT or DUID-LL
// Method 3: Extracted from source IPv6 link-local address
if (hw_addr_src & MAC_SOURCE_IPV6_LINK_LOCAL) {
if (hw_addr_src & HWADDR_SOURCE_IPV6_LINK_LOCAL) {
mac = getMACFromSrcLinkLocalAddr();
if (mac) {
return (mac);
} else if (hw_addr_src == MAC_SOURCE_IPV6_LINK_LOCAL) {
} else if (hw_addr_src == HWADDR_SOURCE_IPV6_LINK_LOCAL) {
// If we're interested only in link-local addr as source of that
// info, there's no point in trying other options.
return (HWAddrPtr());
......@@ -169,12 +169,13 @@ Pkt::getMAC(uint32_t hw_addr_src) {
}
HWAddrPtr
Pkt::getMACFromSrcLinkLocalAddr() {
if (!remote_addr_.isV6LinkLocal()) {
Pkt::getMACfromIPv6(const isc::asiolink::IOAddress& addr) {
if (!addr.isV6LinkLocal()) {
return (HWAddrPtr());
}
std::vector<uint8_t> bin = remote_addr_.toBytes();
std::vector<uint8_t> bin = addr.toBytes();
// Double check that it's of appropriate size
if ((bin.size() != isc::asiolink::V6ADDRESS_LEN) ||
......
......@@ -62,7 +62,7 @@ public:
/// Extracted from IPv6 link-local address. Not 100% reliable, as the
/// client can use different IID other than EUI-64, e.g. Windows supports
/// RFC4941 and uses random values instead of EUI-64.
//static const uint32_t HWADDR_SOURCE_IPV6_LINK_LOCAL = 0x0004;
static const uint32_t HWADDR_SOURCE_IPV6_LINK_LOCAL = 0x0004;
/// Get it from RFC6939 option. (A relay agent can insert client link layer
/// address option). Note that a skilled attacker can fake that by sending
......@@ -505,7 +505,7 @@ protected:
/// @brief Attempts to obtain MAC address from source link-local IPv6 address
///
/// This method is called from getMAC(MAC_SOURCE_IPV6_LINK_LOCAL) and should
/// This method is called from getMAC(HWADDR_SOURCE_IPV6_LINK_LOCAL) and should
/// not be called directly. It is not 100% reliable. The source IPv6 address
/// does not necessarily have to be link-local (may be global or ULA) and
/// even if it's link-local, it doesn't necessarily be based on EUI-64. For
......@@ -515,10 +515,28 @@ protected:
/// For direct message, it attempts to use remote_addr_ field. For relayed
/// message, it uses peer-addr of the first relay.
///
/// This method is not applicable to IPv4.
/// @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 not applicable to DHCPv4.
///
/// @return hardware address (or NULL)
virtual HWAddrPtr getMACFromSrcLinkLocalAddr() = 0;
/// @brief Attempts to convert IPv6 address into MAC.
///
/// Utility method that attempts to convert link-local IPv6 address to the
/// MAC address. That works only for link-local IPv6 addresses that are
/// based on EUI-64.
///
/// @note This method uses hardware type of the interface the packet was
/// received on. If you have multiple access technologies in your network
/// (e.g. client connected to WiFi that relayed the traffic to the server
/// over Ethernet), hardware type may be invalid.
///
/// @param hardware address (or NULL)
HWAddrPtr getMACFromSrcLinkLocalAddr();
/// @param addr IPv6 address to be converted
/// @return hardware address (or NULL)
HWAddrPtr
getMACfromIPv6(const isc::asiolink::IOAddress& addr);
/// Transaction-id (32 bits for v4, 24 bits for v6)
uint32_t transid_;
......
......@@ -390,6 +390,18 @@ protected:
uint8_t
DHCPTypeToBootpType(uint8_t dhcpType);
/// @brief No-op
///
/// This method returns hardware address generated from the IPv6 link-local
/// address. As there is no IPv4-equivalent, it always returns NULL.
/// We need this stub implementation here, to keep all the get hardware
/// address logic in the base class.
///
/// @return always NULL
virtual HWAddrPtr getMACFromSrcLinkLocalAddr() {
return (HWAddrPtr());
}
/// local HW address (dst if receiving packet, src if sending packet)
HWAddrPtr local_hwaddr_;
......
......@@ -542,5 +542,17 @@ void Pkt6::copyRelayInfo(const Pkt6Ptr& question) {
}
}
HWAddrPtr
Pkt6::getMACFromSrcLinkLocalAddr() {
if (relay_info_.empty()) {
// This is a direct message, use source address
return (getMACfromIPv6(remote_addr_));
}
// This is a relayed message, get the peer-addr from the first relay-forw
return (getMACfromIPv6(relay_info_[relay_info_.size() - 1].peeraddr_));
}
} // end of isc::dhcp namespace
} // end of isc namespace
......@@ -268,17 +268,33 @@ public:
/// @param question client's packet
void copyRelayInfo(const Pkt6Ptr& question);
/// relay information
/// @biref Relay information.
///
/// this is a public field. Otherwise we hit one of the two problems:
/// This is a public field. Otherwise we hit one of the two problems:
/// we return reference to an internal field (and that reference could
/// be potentially used past Pkt6 object lifetime causing badness) or
/// we return a copy (which is inefficient and also causes any updates
/// to be impossible). Therefore public field is considered the best
/// (or least bad) solution.
///
/// This vector is arranged in the order packet is encapsulated, i.e.
/// relay[0] was the outermort encapsulation (relay closest to the server),
/// relay[last] was the innermost encapsulation (relay closest to the
/// client).
std::vector<RelayInfo> relay_info_;
protected:
/// @brief Attempts to generate MAC/Hardware address from IPv6 link-local
/// address.
///
/// This method uses source IPv6 address for direct messages and the
/// peeraddr or the first relay that saw that packet. It may fail if the
/// address is not link-local or does not use EUI-64 identifier.
///
/// @return Hardware address (or NULL)
virtual HWAddrPtr getMACFromSrcLinkLocalAddr();
/// @brief Builds on wire packet for TCP transmission.
///
/// @todo This function is not implemented yet.
......
......@@ -910,8 +910,8 @@ TEST_F(Pkt6Test, getMAC) {
}
// Test checks whether getMACFromIPv6LinkLocal() returns the hardware (MAC)
// address properly.
TEST_F(Pkt6Test, getMACFromIPv6LinkLocal) {
// address properly (for direct message).
TEST_F(Pkt6Test, getMACFromIPv6LinkLocal_direct) {
Pkt6 pkt(DHCPV6_ADVERTISE, 1234);
// Let's get the first interface
......@@ -941,4 +941,101 @@ TEST_F(Pkt6Test, getMACFromIPv6LinkLocal) {
EXPECT_EQ(tmp.str(), found->toText(true));
}
// Test checks whether getMACFromIPv6LinkLocal() returns the hardware (MAC)
// address properly (for relayed message).
TEST_F(Pkt6Test, getMACFromIPv6LinkLocal_singleRelay) {
// Let's create a Solicit first...
Pkt6 pkt(DHCPV6_SOLICIT, 1234);
// ... and pretend it was relayed by a single relay.
Pkt6::RelayInfo info;
pkt.addRelayInfo(info);
ASSERT_EQ(1, pkt.relay_info_.size());
// Let's get the first interface
Iface* iface = IfaceMgr::instance().getIface(1);
ASSERT_TRUE(iface);
// and set source interface data properly. getMACFromIPv6LinkLocal attempts
// to use source interface to obtain hardware type
pkt.setIface(iface->getName());
pkt.setIndex(iface->getIndex());
IOAddress global("2001:db8::204:06ff:fe08:0a:0c"); // global address
IOAddress linklocal_noneui64("fe80::0204:0608:0a0c:0e10"); // no fffe
IOAddress linklocal_eui64("fe80::204:06ff:fe08:0a0c"); // valid EUI-64
// If received from a global address, this method should fail
pkt.relay_info_[0].peeraddr_ = global;
EXPECT_FALSE(pkt.getMAC(Pkt::HWADDR_SOURCE_IPV6_LINK_LOCAL));
// If received from a link-local that does not use EUI-64, it shoul fail
pkt.relay_info_[0].peeraddr_ = linklocal_noneui64;
EXPECT_FALSE(pkt.getMAC(Pkt::HWADDR_SOURCE_IPV6_LINK_LOCAL));
// If received from link-local that is EUI-64 based, it should succeed
pkt.relay_info_[0].peeraddr_ = linklocal_eui64;
HWAddrPtr found = pkt.getMAC(Pkt::HWADDR_SOURCE_IPV6_LINK_LOCAL);
ASSERT_TRUE(found);
stringstream tmp;
tmp << "hwtype=" << (int)iface->getHWType() << " 02:04:06:08:0a:0c";
EXPECT_EQ(tmp.str(), found->toText(true));
}
// Test checks whether getMACFromIPv6LinkLocal() returns the hardware (MAC)
// address properly (for a message relayed multiple times).
TEST_F(Pkt6Test, getMACFromIPv6LinkLocal_multiRelay) {
// Let's create a Solicit first...
Pkt6 pkt(DHCPV6_SOLICIT, 1234);
// ... and pretend it was relayed via 3 relays. Keep in mind that the relays
// are stored in relay_info_ in the encapsulation order rather than in
// traverse order. The following simulates:
// client --- relay1 --- relay2 --- relay3 --- server
IOAddress linklocal1("fe80::ff:fe00:1"); // valid EUI-64
IOAddress linklocal2("fe80::ff:fe00:2"); // valid EUI-64
IOAddress linklocal3("fe80::ff:fe00:3"); // valid EUI-64
// Let's add info about relay3. This was the last relay, so it added the
// outermost encapsulation layer, so it was parsed first during reception.
// Its peer-addr field contains an address of relay2, so it's useless for
// this method.
Pkt6::RelayInfo info;
info.peeraddr_ = linklocal3;
pkt.addRelayInfo(info);
// Now add info about relay2. Its peer-addr contains an address of the
// previous relay (relay1). Still useless for us.
info.peeraddr_ = linklocal2;
pkt.addRelayInfo(info);
// Finally add the first relay. This is the relay that received the packet
// from the client directly, so its peer-addr field contains an address of
// the client. The method should get that address and build MAC from it.
info.peeraddr_ = linklocal1;
pkt.addRelayInfo(info);
ASSERT_EQ(3, pkt.relay_info_.size());
// Let's get the first interface
Iface* iface = IfaceMgr::instance().getIface(1);
ASSERT_TRUE(iface);
// and set source interface data properly. getMACFromIPv6LinkLocal attempts
// to use source interface to obtain hardware type
pkt.setIface(iface->getName());
pkt.setIndex(iface->getIndex());
// The method should return MAC based on the first relay that was closest
HWAddrPtr found = pkt.getMAC(Pkt::HWADDR_SOURCE_IPV6_LINK_LOCAL);
ASSERT_TRUE(found);
// Let's check the info now.
stringstream tmp;
tmp << "hwtype=" << iface->getHWType() << " 00:00:00:00:00:01";
EXPECT_EQ(tmp.str(), found->toText(true));
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment