From: Francis Dupont Date: Sat, 23 Dec 2017 19:31:48 +0000 (+0100) Subject: [5404] checkpoint: adding 4o6 support X-Git-Tag: trac5458a_base~19^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4fb30e285512a6b64571bebb14ed16e9b9b81cb1;p=thirdparty%2Fkea.git [5404] checkpoint: adding 4o6 support --- diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h index 6b6b3bde18..390ae19d4d 100644 --- a/src/bin/dhcp6/dhcp6_srv.h +++ b/src/bin/dhcp6/dhcp6_srv.h @@ -754,6 +754,10 @@ protected: void setStatusCode(boost::shared_ptr& container, const OptionPtr& status); +public: + + /// Used for DHCPv4-over-DHCPv6 too. + /// @brief Check if the last relay added a relay-source-port option. /// /// @param query DHCPv6 message to be checked. diff --git a/src/bin/dhcp6/dhcp6to4_ipc.cc b/src/bin/dhcp6/dhcp6to4_ipc.cc index 7ec9397973..71759732f3 100644 --- a/src/bin/dhcp6/dhcp6to4_ipc.cc +++ b/src/bin/dhcp6/dhcp6to4_ipc.cc @@ -89,7 +89,8 @@ void Dhcp6to4Ipc::handler() { // getType() always returns the type of internal message. uint8_t msg_type = buf[0]; if ((msg_type == DHCPV6_RELAY_FORW) || (msg_type == DHCPV6_RELAY_REPL)) { - pkt->setRemotePort(DHCP6_SERVER_PORT); + uint16_t relay_port = Dhcpv6Srv::testRelaySourcePort(pkt); + pkt->setRemotePort(relay_port ? relay_port : DHCP6_SERVER_PORT); } else { pkt->setRemotePort(DHCP6_CLIENT_PORT); } diff --git a/src/lib/dhcp/dhcp6.h b/src/lib/dhcp/dhcp6.h index 78c8c07380..7b67badbb6 100644 --- a/src/lib/dhcp/dhcp6.h +++ b/src/lib/dhcp/dhcp6.h @@ -280,6 +280,7 @@ static const uint32_t ENTERPRISE_ID_ISC = 2495; codes for the ISC vendor specific options used in 4o6 */ static const uint16_t ISC_V6_4O6_INTERFACE = 60000; static const uint16_t ISC_V6_4O6_SRC_ADDRESS = 60001; +static const uint16_t ISC_V6_4O6_SRC_PORT = 60002; /* Offsets into IA_*'s where Option spaces commence. */ static const uint16_t IA_NA_OFFSET = 12; /* IAID, T1, T2, all 4 octets each */ diff --git a/src/lib/dhcp/std_option_defs.h b/src/lib/dhcp/std_option_defs.h index 47d9c2256a..e3db039e58 100644 --- a/src/lib/dhcp/std_option_defs.h +++ b/src/lib/dhcp/std_option_defs.h @@ -476,7 +476,9 @@ const OptionDefParams ISC_V6_OPTION_DEFINITIONS[] = { { "4o6-interface", ISC_V6_4O6_INTERFACE, OPT_STRING_TYPE, false, NO_RECORD_DEF, "" }, { "4o6-source-address", ISC_V6_4O6_SRC_ADDRESS, OPT_IPV6_ADDRESS_TYPE, - false, NO_RECORD_DEF, "" } + false, NO_RECORD_DEF, "" }, + { "4o6-source-port", ISC_V6_4O6_SRC_PORT, OPT_UINT16_TYPE, false, + NO_RECORD_DEF, "" } }; const int ISC_V6_OPTION_DEFINITIONS_SIZE = diff --git a/src/lib/dhcpsrv/dhcp4o6_ipc.cc b/src/lib/dhcpsrv/dhcp4o6_ipc.cc index 86dfafc378..031c805956 100644 --- a/src/lib/dhcpsrv/dhcp4o6_ipc.cc +++ b/src/lib/dhcpsrv/dhcp4o6_ipc.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2015-2017 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -191,14 +192,29 @@ Pkt6Ptr Dhcp4o6IpcBase::receive() { "or has incorrect type)"); } + // Get the option holding source port. + OptionPtr sport = option_vendor->getOption(ISC_V6_4O6_SRC_PORT); + if (!sport || (sport->len() - sport->getHeaderLen() != 2)) { + LOG_WARN(dhcpsrv_logger, DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET) + .arg("no source port suboption"); + isc_throw(Dhcp4o6IpcError, + "malformed packet (source port suboption missing " + "or has incorrect length)"); + } + const OptionBuffer& sport_buf = sport->getData(); + uint16_t sport_value = static_cast(sport_buf[0]) << 8; + sport_value |= static_cast(sport_buf[1]); + // Update the packet. pkt->setRemoteAddr(srcs->readAddress()); + pkt->setRemotePort(sport_value); pkt->setIface(iface->getName()); pkt->setIndex(iface->getIndex()); // Remove options that have been added by the IPC sender. static_cast(option_vendor->delOption(ISC_V6_4O6_INTERFACE)); static_cast(option_vendor->delOption(ISC_V6_4O6_SRC_ADDRESS)); + static_cast(option_vendor->delOption(ISC_V6_4O6_SRC_PORT)); // If there are no more options, the IPC sender has probably created the // vendor option, in which case we should remove it here. @@ -243,6 +259,9 @@ void Dhcp4o6IpcBase::send(const Pkt6Ptr& pkt) { option_vendor->addOption(Option6AddrLstPtr(new Option6AddrLst( ISC_V6_4O6_SRC_ADDRESS, pkt->getRemoteAddr()))); + option_vendor->addOption(OptionUint16Ptr(new OptionUint16(Option::V6, + ISC_V6_4O6_SRC_PORT, + pkt->getRemotePort()))); // Get packet content OutputBuffer& buf = pkt->getBuffer(); buf.clear(); diff --git a/src/lib/dhcpsrv/dhcp4o6_ipc.h b/src/lib/dhcpsrv/dhcp4o6_ipc.h index fd7c4d8c16..d67f2d2176 100644 --- a/src/lib/dhcpsrv/dhcp4o6_ipc.h +++ b/src/lib/dhcpsrv/dhcp4o6_ipc.h @@ -1,4 +1,4 @@ -// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2015-2017 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -54,10 +54,10 @@ public: /// the original DHCPv4 query message sent by the client. This /// information is known by the DHCPv6 server and needs to be conveyed /// to the DHCPv4 server. The IPC conveys it in the -/// @c ISC_V6_4O6_INTERFACE and @c ISC_V6_4O6_SRC_ADDRESS options -/// within the Vendor Specific Information option, with ISC -/// enterprise id. These options are added by the IPC sender and removed -/// by the IPC receiver. +/// @c ISC_V6_4O6_INTERFACE, @c ISC_V6_4O6_SRC_ADDRESS and @c +/// ISC_V6_4O6_SRC_PORT options within the Vendor Specific Information +/// option, with ISC enterprise id. These options are added by the IPC +/// sender and removed by the IPC receiver. class Dhcp4o6IpcBase : public boost::noncopyable { public: @@ -105,11 +105,11 @@ public: /// @brief Send message over IPC. /// - /// The IPC uses @c ISC_V6_4O6_INTERFACE and @c ISC_V6_4O6_SRC_ADDRESS - /// options conveyed within the Vendor Specific Information option, with - /// ISC enterprise id, to communicate the client remote address and the - /// interface on which the DHCPv4 query was received. These options will - /// be removed by the receiver. + /// The IPC uses @c ISC_V6_4O6_INTERFACE, @c ISC_V6_4O6_SRC_ADDRESS + /// and @c ISC_V6_4O6_SRC_PORT options conveyed within the Vendor + /// Specific Information option, with ISC enterprise id, to communicate + /// the client remote address and the interface on which the DHCPv4 query + /// was received. These options will be removed by the receiver. /// /// @param pkt Pointer to a DHCPv6 message with interface and remote /// address. diff --git a/src/lib/dhcpsrv/tests/dhcp4o6_ipc_unittest.cc b/src/lib/dhcpsrv/tests/dhcp4o6_ipc_unittest.cc index 07ac8bd326..84b6d10ae2 100644 --- a/src/lib/dhcpsrv/tests/dhcp4o6_ipc_unittest.cc +++ b/src/lib/dhcpsrv/tests/dhcp4o6_ipc_unittest.cc @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -173,6 +174,10 @@ Dhcp4o6IpcBaseTest::createDHCPv4o6Message(uint16_t msg_type, // right address. pkt->setRemoteAddr(IOAddress(concatenate("2001:db8:1::", postfix))); + // The remote port of the sender of the DHCPv6 packet is carried + // between the servers in the dedicated option. + pkt->setRemotePort(10000 + (postfix % 1000)); + // Determine the endpoint type using the message type. TestIpc::EndpointType src = (msg_type == DHCPV6_DHCPV4_QUERY) ? TestIpc::ENDPOINT_TYPE_V6 : TestIpc::ENDPOINT_TYPE_V4; @@ -286,6 +291,9 @@ Dhcp4o6IpcBaseTest::testSendReceive(uint16_t iterations_num, EXPECT_EQ(concatenate("2001:db8:1::", i), pkt_received->getRemoteAddr().toText()); + // Check that the port conveyed is correct. + EXPECT_EQ(10000 + (i % 1000), pkt_received->getRemotePort()); + // Check that encapsulated DHCPv4 message has been received. EXPECT_TRUE(pkt_received->getOption(D6O_DHCPV4_MSG)); @@ -320,6 +328,7 @@ Dhcp4o6IpcBaseTest::testReceiveError(const Pkt6Ptr& pkt) { pkt->setIface("eth0"); pkt->setRemoteAddr(IOAddress("2001:db8:1::1")); + pkt->setRemotePort(TEST_PORT); pkt->addOption(createDHCPv4MsgOption(TestIpc::ENDPOINT_TYPE_V6)); OutputBuffer& buf = pkt->getBuffer(); @@ -476,6 +485,10 @@ TEST_F(Dhcp4o6IpcBaseTest, receiveWithoutInterfaceOption) { Option6AddrLstPtr(new Option6AddrLst(ISC_V6_4O6_SRC_ADDRESS, IOAddress("2001:db8:1::1"))) ); + option_vendor->addOption( + OptionUint16Ptr(new OptionUint16(Option::V6, + ISC_V6_4O6_SRC_PORT, + TEST_PORT))); pkt->addOption(option_vendor); testReceiveError(pkt); @@ -495,6 +508,10 @@ TEST_F(Dhcp4o6IpcBaseTest, receiveWithInvalidInterface) { Option6AddrLstPtr(new Option6AddrLst(ISC_V6_4O6_SRC_ADDRESS, IOAddress("2001:db8:1::1"))) ); + option_vendor->addOption( + OptionUint16Ptr(new OptionUint16(Option::V6, + ISC_V6_4O6_SRC_PORT, + TEST_PORT))); pkt->addOption(option_vendor); testReceiveError(pkt); @@ -510,6 +527,28 @@ TEST_F(Dhcp4o6IpcBaseTest, receiveWithoutSourceAddressOption) { option_vendor->addOption( OptionStringPtr(new OptionString(Option::V6, ISC_V6_4O6_INTERFACE, "eth0"))); + option_vendor->addOption( + OptionUint16Ptr(new OptionUint16(Option::V6, + ISC_V6_4O6_SRC_PORT, + TEST_PORT))); + + pkt->addOption(option_vendor); + testReceiveError(pkt); +} + +// This test verifies that receiving packet over the IPC fails when the +// source port option is not present. +TEST_F(Dhcp4o6IpcBaseTest, receiveWithoutSourcePortOption) { + Pkt6Ptr pkt(new Pkt6(DHCPV6_DHCPV4_QUERY, 0)); + OptionVendorPtr option_vendor(new OptionVendor(Option::V6, + ENTERPRISE_ID_ISC)); + option_vendor->addOption( + OptionStringPtr(new OptionString(Option::V6, ISC_V6_4O6_INTERFACE, + "eth0"))); + option_vendor->addOption( + Option6AddrLstPtr(new Option6AddrLst(ISC_V6_4O6_SRC_ADDRESS, + IOAddress("2001:db8:1::1"))) + ); pkt->addOption(option_vendor); testReceiveError(pkt);