From d7e0d17154a0d242b8b025fed6fd0fe1c17ffee4 Mon Sep 17 00:00:00 2001 From: Matias Fontanini Date: Fri, 1 Jan 2016 10:24:45 -0800 Subject: [PATCH] Add ICMPv6 multicast listener report message structure --- include/tins/icmpv6.h | 44 +++++++++++++++++++++++++ src/icmpv6.cpp | 75 ++++++++++++++++++++++++++++++++++++++++++- tests/src/icmpv6.cpp | 48 +++++++++++++++++++++++++++ 3 files changed, 166 insertions(+), 1 deletion(-) diff --git a/include/tins/icmpv6.h b/include/tins/icmpv6.h index 3b810e2..395e280 100644 --- a/include/tins/icmpv6.h +++ b/include/tins/icmpv6.h @@ -521,6 +521,30 @@ public: static new_advert_interval_type from_option(const option &opt); }; + /** + * The type used to represent a multicast address record + */ + struct multicast_address_record { + typedef std::vector sources_type; + typedef std::vector aux_data_type; + + multicast_address_record(uint8_t type) : type(type) { } + + multicast_address_record(const uint8_t* buffer, uint32_t total_sz); + void serialize(uint8_t* buffer, uint32_t total_sz) const; + uint32_t size() const; + + uint8_t type; + ipaddress_type multicast_address; + sources_type sources; + aux_data_type aux_data; + }; + + /* + * The type used to store all multicast address records in a packet + */ + typedef std::list multicast_address_records_list; + /** * \brief Constructs an ICMPv6 object. * @@ -707,6 +731,13 @@ public: return _header.rfc4884.length; } + /** + * \brief Getter for the multicast address records field + */ + const multicast_address_records_list& multicast_address_records() const { + return multicast_records_; + } + // Setters /** @@ -817,6 +848,13 @@ public: */ void retransmit_timer(uint32_t new_retrans_timer); + /** + * \brief Setter for the multicast address records field. + * + * This field is only valid if the type of this PDU is MLD2_REPORT + */ + void multicast_address_records(const multicast_address_records_list& records); + /** * \brief Getter for the PDU's type. * @@ -1374,6 +1412,11 @@ private: uint8_t length; uint8_t unused[3]; } rfc4884; + // Multicast Listener Report Message (mld2) + struct { + uint16_t reserved; + uint16_t record_count; + } mlrm2 ; }; } TINS_END_PACK; @@ -1411,6 +1454,7 @@ private: options_type _options; uint32_t _options_size; uint32_t reach_time, retrans_timer; + multicast_address_records_list multicast_records_; ICMPExtensionsStructure extensions_; }; } diff --git a/src/icmpv6.cpp b/src/icmpv6.cpp index ad679d5..b48827a 100644 --- a/src/icmpv6.cpp +++ b/src/icmpv6.cpp @@ -63,6 +63,15 @@ ICMPv6::ICMPv6(const uint8_t *buffer, uint32_t total_sz) reach_time = stream.read(); retrans_timer = stream.read(); } + else if (type() == MLD2_REPORT) { + uint16_t record_count = Endian::be_to_host(_header.mlrm2.record_count); + for (uint16_t i = 0; i < record_count; ++i) { + multicast_records_.push_back( + multicast_address_record(stream.pointer(), stream.size()) + ); + stream.skip(multicast_records_.back().size()); + } + } // Retrieve options if (has_options()) { parse_options(stream); @@ -161,6 +170,10 @@ void ICMPv6::retransmit_timer(uint32_t new_retrans_timer) { retrans_timer = Endian::host_to_be(new_retrans_timer); } +void ICMPv6::multicast_address_records(const multicast_address_records_list& records) { + multicast_records_ = records; +} + void ICMPv6::target_addr(const ipaddress_type &new_target_addr) { _target_address = new_target_addr; } @@ -171,8 +184,16 @@ void ICMPv6::dest_addr(const ipaddress_type &new_dest_addr) { uint32_t ICMPv6::header_size() const { uint32_t extra = 0; - if(type() == ROUTER_ADVERT) + if(type() == ROUTER_ADVERT) { extra = sizeof(uint32_t) * 2; + } + else if (type() == MLD2_REPORT) { + typedef multicast_address_records_list::const_iterator iterator; + for (iterator iter = multicast_records_.begin(); + iter != multicast_records_.end(); ++iter) { + extra += iter->size(); + } + } return sizeof(_header) + _options_size + extra + (has_target_addr() ? ipaddress_type::address_size : 0) + (has_dest_addr() ? ipaddress_type::address_size : 0); @@ -225,6 +246,10 @@ void ICMPv6::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU * } // Initially set checksum to 0, we'll calculate it at the end _header.cksum = 0; + // Update the MLRM record count before writing the header + if (type() == MLD2_REPORT) { + _header.mlrm2.record_count = Endian::host_to_be(multicast_records_.size()); + } stream.write(_header); if (has_target_addr()) { @@ -237,6 +262,14 @@ void ICMPv6::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU * stream.write(reach_time); stream.write(retrans_timer); } + else if (type() == MLD2_REPORT) { + typedef multicast_address_records_list::const_iterator iterator; + for (iterator iter = multicast_records_.begin(); + iter != multicast_records_.end(); ++iter) { + iter->serialize(stream.pointer(), stream.size()); + stream.skip(iter->size()); + } + } for(options_type::const_iterator it = _options.begin(); it != _options.end(); ++it) { write_option(*it, stream); } @@ -999,5 +1032,45 @@ ICMPv6::new_advert_interval_type ICMPv6::new_advert_interval_type::from_option(c output.interval = Endian::be_to_host(output.interval); return output; } + +// multicast_address_record + +ICMPv6::multicast_address_record::multicast_address_record(const uint8_t* buffer, + uint32_t total_sz) +{ + InputMemoryStream stream(buffer, total_sz); + stream.read(type); + int aux_data_len = stream.read() * sizeof(uint32_t); + int sources_count = Endian::be_to_host(stream.read()); + stream.read(multicast_address); + while (sources_count--) { + sources.push_back(stream.read()); + } + if (!stream.can_read(aux_data_len)) { + throw malformed_packet(); + } + aux_data.assign(stream.pointer(), stream.pointer() + aux_data_len); } +void ICMPv6::multicast_address_record::serialize(uint8_t* buffer, uint32_t total_sz) const +{ + OutputMemoryStream stream(buffer, total_sz); + stream.write(type); + stream.write(aux_data.size() / sizeof(uint32_t)); + stream.write(Endian::host_to_be(sources.size())); + stream.write(multicast_address); + for (size_t i = 0; i < sources.size(); ++i) { + stream.write(sources[i]); + } + stream.write(aux_data.begin(), aux_data.end()); +} + +uint32_t ICMPv6::multicast_address_record::size() const +{ + return sizeof(uint8_t) * 2 + sizeof(uint16_t) + ipaddress_type::address_size + + sources.size() * ipaddress_type::address_size + + aux_data.size(); +} + +} // Tins + diff --git a/tests/src/icmpv6.cpp b/tests/src/icmpv6.cpp index 36449f3..1a17a29 100644 --- a/tests/src/icmpv6.cpp +++ b/tests/src/icmpv6.cpp @@ -19,6 +19,7 @@ public: static const uint8_t expected_packet2[]; static const uint8_t packet_with_extensions[]; static const uint8_t packet_with_extensions_and_length[]; + static const uint8_t mld2_icmpv6_layer[]; void test_equals(const ICMPv6 &icmp1, const ICMPv6 &icmp2); }; @@ -63,6 +64,15 @@ const uint8_t ICMPv6Test::packet_with_extensions_and_length[] = { 0, 197, 95, 0, 8, 1, 1, 24, 150, 1, 1 }; +const uint8_t ICMPv6Test::mld2_icmpv6_layer[] = { + 143, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0, 8, 255, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 255, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 255, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 255, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 255, 0, 0, 9, 222, 173, 190, 239, 190, 173, 254, 237 +}; + TEST_F(ICMPv6Test, Constructor) { ICMPv6 icmp; EXPECT_EQ(icmp.type(), ICMPv6::ECHO_REQUEST); @@ -118,6 +128,44 @@ TEST_F(ICMPv6Test, ConstructorFromBuffer2) { EXPECT_EQ(opt->data_size(), 30U); } +TEST_F(ICMPv6Test, ConstructorFromBuffer_MLD2_Layer) { + ICMPv6 icmp(mld2_icmpv6_layer, sizeof(mld2_icmpv6_layer)); + + ICMPv6::multicast_address_records_list records = icmp.multicast_address_records(); + ASSERT_EQ(1, records.size()); + ICMPv6::multicast_address_record r = *records.begin(); + EXPECT_EQ(1, r.type); + std::vector aux_data; + aux_data.push_back(0xde); + aux_data.push_back(0xad); + aux_data.push_back(0xbe); + aux_data.push_back(0xef); + aux_data.push_back(0xbe); + aux_data.push_back(0xad); + aux_data.push_back(0xfe); + aux_data.push_back(0xed); + EXPECT_EQ(aux_data, r.aux_data); + std::vector sources; + sources.push_back("::"); + sources.push_back("ff02::1"); + sources.push_back("::"); + sources.push_back("ff02::1"); + sources.push_back("ff02::1"); + sources.push_back("ff02::2"); + sources.push_back("::1"); + sources.push_back("ff02::1:ff00:9"); + EXPECT_EQ(sources, r.sources); + + PDU::serialization_type buffer = icmp.serialize(); + EXPECT_EQ( + PDU::serialization_type( + mld2_icmpv6_layer, + mld2_icmpv6_layer + sizeof(mld2_icmpv6_layer) + ), + buffer + ); +} + TEST_F(ICMPv6Test, Type) { ICMPv6 icmp; icmp.type(ICMPv6::MLD2_REPORT);