#define D6O_CLT_TIME 46 /* RFC5007 */
#define D6O_LQ_RELAY_DATA 47 /* RFC5007 */
#define D6O_LQ_CLIENT_LINK 48 /* RFC5007 */
+#define D6O_CLIENT_LINKLAYER_ADDR 79 /* RFC6939 */
/*
* Status Codes, from RFC 3315 section 24.4, and RFC 3633, 5007.
}
// Method 4: From client link-layer address option inserted by a relay
+ if (hw_addr_src & HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION) {
+ mac = getMACFromIPv6RelayOpt();
+ if (mac) {
+ return (mac);
+ } else if (hw_addr_src == HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION) {
+ // If we're interested only in RFC6939 link layer address as source
+ // of that info, there's no point in trying other options.
+ return (HWAddrPtr());
+ }
+ }
// Method 5: From remote-id option inserted by a relay
/// address option). Note that a skilled attacker can fake that by sending
/// his request relayed, so the legitimate relay will think it's a second
/// relay.
- //static const uint32_t HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION = 0x0008;
+ static const uint32_t HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION = 0x0008;
/// A relay can insert remote-id. In some deployments it contains a MAC
/// address (RFC4649).
/// @return hardware address (or NULL)
virtual HWAddrPtr getMACFromSrcLinkLocalAddr() = 0;
+ /// @brief Attempts to obtain MAC address from relay option
+ /// client-linklayer-addr
+ ///
+ /// This method is called from getMAC(HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION)
+ /// and should not be called directly. It will extract the client's
+ // MAC/Hardware address from option client_linklayer_addr (RFC6939)
+ // inserted by the relay agent closest to the client.
+ /// If this method fails, it will return NULL.
+ ///
+ /// @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 getMACFromIPv6RelayOpt() = 0;
+
/// @brief Attempts to convert IPv6 address into MAC.
///
/// Utility method that attempts to convert link-local IPv6 address to the
return (HWAddrPtr());
}
+ /// @brief No-op
+ ///
+ /// This method returns hardware address extracted from an IPv6 relay agent.
+ /// option. 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 getMACFromIPv6RelayOpt() {
+ return (HWAddrPtr());
+ }
+
/// local HW address (dst if receiving packet, src if sending packet)
HWAddrPtr local_hwaddr_;
return (getMACFromIPv6(relay_info_[relay_info_.size() - 1].peeraddr_));
}
+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) {
+ return (HWAddrPtr(new HWAddr(&(opt->getData())[2], (opt->len()-opt->getHeaderLen()-2),
+ opt->getUint16())));
+ }
+ else {
+ return (HWAddrPtr());
+ }
+}
} // end of isc::dhcp namespace
} // end of isc namespace
/// @return Hardware address (or NULL)
virtual HWAddrPtr getMACFromSrcLinkLocalAddr();
+ /// @brief Extract MAC/Hardware address from client link-layer address
+ // option inserted by a relay agent (RFC6939).
+ ///
+ /// This method extracts the client's hardware address from the
+ // client-linklayer-addr option inserted by the relay agent closest to
+ // the client.
+ ///
+ /// @return Hardware address (or NULL)
+ virtual HWAddrPtr getMACFromIPv6RelayOpt();
+
/// @brief Builds on wire packet for TCP transmission.
///
/// @todo This function is not implemented yet.
{ "lq-relay-data", D6O_LQ_RELAY_DATA, OPT_RECORD_TYPE, false,
RECORD_DEF(LQ_RELAY_DATA_RECORDS), "" },
{ "lq-client-link", D6O_LQ_CLIENT_LINK, OPT_IPV6_ADDRESS_TYPE, true,
- NO_RECORD_DEF, "" }
+ NO_RECORD_DEF, "" },
+ { "client-linklayer-addr", D6O_CLIENT_LINKLAYER_ADDR, OPT_BINARY_TYPE, false,
+ NO_RECORD_DEF, "" }
// @todo There is still a bunch of options for which we have to provide
// definitions but we don't do it because they are not really
EXPECT_EQ(tmp.str(), found->toText(true));
}
+// Test checks whether getMACFromIPv6RelayOpt() returns the hardware (MAC)
+// address properly from a single relayed message.
+TEST_F(Pkt6Test, getMACFromIPv6RelayOpt_singleRelay) {
+
+ // Let's create a Solicit first...
+ Pkt6 pkt(DHCPV6_SOLICIT, 1234);
+
+ // Packets that are not relayed should fail
+ EXPECT_FALSE(pkt.getMAC(Pkt::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION));
+
+ // Now pretend it was relayed by a single relay.
+ Pkt6::RelayInfo info;
+
+ // generate options with code 79 and client link layer address
+ const char opt_data[] = {
+ 0x00, 0x01, // Ethertype
+ 0x0a, 0x1b, 0x0b, 0x01, 0xca, 0xfe // MAC
+ };
+ OptionPtr relay_opt(new Option(Option::V6, 79,
+ OptionBuffer(opt_data, opt_data + sizeof(opt_data))));
+ info.options_.insert(make_pair(relay_opt->getType(), relay_opt));
+
+ pkt.addRelayInfo(info);
+ ASSERT_EQ(1, pkt.relay_info_.size());
+
+ HWAddrPtr found = pkt.getMAC(Pkt::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION);
+ ASSERT_TRUE(found);
+
+ stringstream tmp;
+ tmp << "hwtype=1 0a:1b:0b:01:ca:fe";
+ EXPECT_EQ(tmp.str(), found->toText(true));
+}
+
+// Test checks whether getMACFromIPv6RelayOpt() returns the hardware (MAC)
+// address properly from a message relayed by multiple servers.
+TEST_F(Pkt6Test, getMACFromIPv6RelayOpt_multipleRelay) {
+
+ // Let's create a Solicit first...
+ Pkt6 pkt(DHCPV6_SOLICIT, 1234);
+
+ // Now pretend it was relayed two times. The relay closest to the server
+ // adds link-layer-address information against the RFC, the process fails.
+ Pkt6::RelayInfo info1;
+ char opt_data[] = {
+ 0x00, 0x01, // Ethertype
+ 0x1a, 0x30, 0x0b, 0xfa, 0xc0, 0xfe // MAC
+ };
+ OptionPtr relay_opt1(new Option(Option::V6, 79,
+ OptionBuffer(opt_data, opt_data + sizeof(opt_data))));
+
+ info1.options_.insert(make_pair(relay_opt1->getType(), relay_opt1));
+ pkt.addRelayInfo(info1);
+
+ // Second relay, closest to the client has not implemented RFC6939
+ Pkt6::RelayInfo info2;
+ pkt.addRelayInfo(info2);
+ ASSERT_EQ(2, pkt.relay_info_.size());
+
+ EXPECT_FALSE(pkt.getMAC(Pkt::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION));
+
+ // Let's envolve the packet with a third relay (now the closest to the client)
+ // that inserts the correct client_linklayer_addr option.
+ Pkt6::RelayInfo info3;
+ // We reuse the option and modify the MAC to be sure we get the right address
+ opt_data[2] = 0xfa;
+ OptionPtr relay_opt3(new Option(Option::V6, 79,
+ OptionBuffer(opt_data, opt_data + sizeof(opt_data))));
+ info3.options_.insert(make_pair(relay_opt3->getType(), relay_opt3));
+ pkt.addRelayInfo(info3);
+ ASSERT_EQ(3, pkt.relay_info_.size());
+
+ // Now extract the MAC address from the relayed option
+ HWAddrPtr found = pkt.getMAC(Pkt::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION);
+ ASSERT_TRUE(found);
+
+ stringstream tmp;
+ tmp << "hwtype=1 fa:30:0b:fa:c0:fe";
+ EXPECT_EQ(tmp.str(), found->toText(true));
+}
}