diff --git a/include/tins/icmpv6.h b/include/tins/icmpv6.h index 90021ff..5b7c097 100644 --- a/include/tins/icmpv6.h +++ b/include/tins/icmpv6.h @@ -527,7 +527,7 @@ public: typedef std::vector sources_type; typedef std::vector aux_data_type; - multicast_address_record(uint8_t type) : type(type) { } + multicast_address_record(uint8_t type = 0) : type(type) { } multicast_address_record(const uint8_t* buffer, uint32_t total_sz); void serialize(uint8_t* buffer, uint32_t total_sz) const; @@ -544,6 +544,12 @@ public: */ typedef std::list multicast_address_records_list; + /* + * The type used to store all source address (from Multicast + * Listener Query messages) in a packet + */ + typedef std::list sources_list; + /** * \brief Constructs an ICMPv6 object. * @@ -634,13 +640,21 @@ public: } /** - * \brief Getter for the hop_limit field. - * \return The stored hop_limit field value. + * \brief Getter for the hop limit field. + * \return The stored hop limit field value. */ uint8_t hop_limit() const { return header_.u_nd_ra.hop_limit; } + /** + * \brief Getter for the maximum response code field. + * \return The stored maximum response code field value. + */ + uint16_t maximum_response_code() const { + return Endian::be_to_host(header_.u_echo.identifier); + } + /** * \brief Getter for the router_pref field. * \return The stored router_pref field value. @@ -713,6 +727,17 @@ public: return dest_address_; } + /** + * \brief Getter for the multicast address field. + * + * Note that this field is only valid for Multicast Listener Query + * Message packets + * \return The stored multicast address field value. + */ + const ipaddress_type& multicast_addr() const { + return multicast_address_; + } + /** * \brief Getter for the ICMPv6 options. * \return The stored options. @@ -737,6 +762,46 @@ public: return multicast_records_; } + /** + * \brief Getter for the multicast address records field. + * + * Note that this field is only valid for Multicast Listener Query Message + * packets + */ + const sources_list& sources() const { + return sources_; + } + + /** + * \brief Getter for the Suppress Router-Side Processing field. + * + * Note that this field is only valid for Multicast Listener Query Message + * packets + */ + small_uint<1> supress() const { + return mlqm_.supress; + } + + /** + * \brief Getter for the Querier's Robustnes Variable field. + * + * Note that this field is only valid for Multicast Listener Query Message + * packets + */ + small_uint<3> qrv() const { + return mlqm_.qrv; + } + + /** + * \brief Getter for the Querier's Query Interval Code field. + * + * Note that this field is only valid for Multicast Listener Query Message + * packets + */ + uint8_t qqic() const { + return mlqm_.qqic; + } + // Setters /** @@ -793,6 +858,12 @@ public: */ void hop_limit(uint8_t new_hop_limit); + /** + * \brief Setter for the maximum response code field. + * \param new_hop_limit The new maximum response code field value. + */ + void maximum_response_code(uint16_t maximum_response_code); + /** * \brief Setter for the router_pref field. * \param new_router_pref The new router_pref field value. @@ -835,6 +906,15 @@ public: */ void dest_addr(const ipaddress_type& new_dest_addr); + /** + * \brief Setter for the multicast address field. + * + * Note that this field is only valid if the type is MGM_QUERY + * + * \param new_multicast_addr The new multicast address field value. + */ + void multicast_addr(const ipaddress_type& new_multicast_addr); + /** * \brief Setter for the reachable_time field. * \param new_reachable_time The new reachable_time field value. @@ -854,6 +934,34 @@ public: */ void multicast_address_records(const multicast_address_records_list& records); + /** + * \brief Setter for the sources field. + * + * This field is only valid if the type of this PDU is MGM_QUERY + */ + void sources(const sources_list& new_sources); + + /** + * \brief Setter for the supress field. + * + * This field is only valid if the type of this PDU is MGM_QUERY + */ + void supress(small_uint<1> value); + + /** + * \brief Setter for the Querier's Robustness Variable field. + * + * This field is only valid if the type of this PDU is MGM_QUERY + */ + void qrv(small_uint<3> value); + + /** + * \brief Setter for the Querier's Query Interval Code field. + * + * This field is only valid if the type of this PDU is MGM_QUERY + */ + void qqic(uint8_t value); + /** * \brief Getter for the PDU's type. * @@ -1421,9 +1529,17 @@ private: struct { uint16_t reserved; uint16_t record_count; - } mlrm2 ; + } mlrm2; }; } TINS_END_PACK; + + TINS_BEGIN_PACK + struct multicast_listener_query_message_fields { + uint8_t reserved:4, + supress:1, + qrv:3; + uint8_t qqic; + } TINS_END_PACK; void internal_add_option(const option& option); void write_serialization(uint8_t* buffer, uint32_t total_sz, const PDU* parent); @@ -1458,14 +1574,18 @@ private: } icmp6_header header_; - ipaddress_type target_address_, dest_address_; + ipaddress_type target_address_; + ipaddress_type dest_address_; + ipaddress_type multicast_address_; options_type options_; uint32_t options_size_; uint32_t reach_time_, retrans_timer_; multicast_address_records_list multicast_records_; + multicast_listener_query_message_fields mlqm_; + sources_list sources_; ICMPExtensionsStructure extensions_; }; -} +} // Tins #endif // TINS_ICMPV6_H diff --git a/src/icmpv6.cpp b/src/icmpv6.cpp index 10c97f5..5720224 100644 --- a/src/icmpv6.cpp +++ b/src/icmpv6.cpp @@ -75,6 +75,16 @@ ICMPv6::ICMPv6(const uint8_t* buffer, uint32_t total_sz) stream.skip(multicast_records_.back().size()); } } + else if (type() == MGM_QUERY) { + stream.read(multicast_address_); + stream.read(mlqm_); + int sources_count = stream.read_be(); + while (sources_count--) { + ipaddress_type address; + stream.read(address); + sources_.push_back(address); + } + } // Retrieve options if (has_options()) { parse_options(stream); @@ -145,6 +155,10 @@ void ICMPv6::hop_limit(uint8_t new_hop_limit) { header_.u_nd_ra.hop_limit = new_hop_limit; } +void ICMPv6::maximum_response_code(uint16_t maximum_response_code) { + header_.u_echo.identifier = Endian::host_to_be(maximum_response_code); +} + void ICMPv6::router_pref(small_uint<2> new_router_pref) { header_.u_nd_ra.router_pref = new_router_pref; } @@ -177,6 +191,22 @@ void ICMPv6::multicast_address_records(const multicast_address_records_list& rec multicast_records_ = records; } +void ICMPv6::sources(const sources_list& new_sources) { + sources_ = new_sources; +} + +void ICMPv6::supress(small_uint<1> value) { + mlqm_.supress = value; +} + +void ICMPv6::qrv(small_uint<3> value) { + mlqm_.qrv = value; +} + +void ICMPv6::qqic(uint8_t value) { + mlqm_.qqic = value; +} + void ICMPv6::target_addr(const ipaddress_type& new_target_addr) { target_address_ = new_target_addr; } @@ -185,6 +215,10 @@ void ICMPv6::dest_addr(const ipaddress_type& new_dest_addr) { dest_address_ = new_dest_addr; } +void ICMPv6::multicast_addr(const ipaddress_type& new_multicast_addr) { + multicast_address_ = new_multicast_addr; +} + uint32_t ICMPv6::header_size() const { uint32_t extra = 0; if (type() == ROUTER_ADVERT) { @@ -197,6 +231,10 @@ uint32_t ICMPv6::header_size() const { extra += iter->size(); } } + else if (type() == MGM_QUERY) { + extra += ipaddress_type::address_size + sizeof(mlqm_) + sizeof(uint16_t) + + ipaddress_type::address_size * sources_.size(); + } return sizeof(header_) + options_size_ + extra + (has_target_addr() ? ipaddress_type::address_size : 0) + (has_dest_addr() ? ipaddress_type::address_size : 0); @@ -275,6 +313,15 @@ void ICMPv6::write_serialization(uint8_t* buffer, uint32_t total_sz, const PDU* stream.skip(iter->size()); } } + else if (type() == MGM_QUERY) { + stream.write(multicast_address_); + stream.write(mlqm_); + stream.write_be(sources_.size()); + typedef sources_list::const_iterator iterator; + for (iterator iter = sources_.begin(); iter != sources_.end(); ++iter) { + stream.write(*iter); + } + } for (options_type::const_iterator it = options_.begin(); it != options_.end(); ++it) { write_option(*it, stream); } @@ -286,8 +333,7 @@ void ICMPv6::write_serialization(uint8_t* buffer, uint32_t total_sz, const PDU* uint32_t inner_pdu_size = get_adjusted_inner_pdu_size(); // If it's lower than 128, we need to padd enough zeroes to make it 128 bytes long if (inner_pdu_size < 128) { - memset(extensions_ptr + inner_pdu_size, 0, - 128 - inner_pdu_size); + memset(extensions_ptr + inner_pdu_size, 0, 128 - inner_pdu_size); inner_pdu_size = 128; } else { diff --git a/tests/src/icmpv6.cpp b/tests/src/icmpv6.cpp index 5406ade..1268095 100644 --- a/tests/src/icmpv6.cpp +++ b/tests/src/icmpv6.cpp @@ -20,6 +20,7 @@ public: static const uint8_t packet_with_extensions[]; static const uint8_t packet_with_extensions_and_length[]; static const uint8_t mld2_icmpv6_layer[]; + static const uint8_t mlqm_icmpv6_layer[]; void test_equals(const ICMPv6& icmp1, const ICMPv6& icmp2); }; @@ -73,6 +74,16 @@ const uint8_t ICMPv6Test::mld2_icmpv6_layer[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 255, 0, 0, 9, 222, 173, 190, 239, 190, 173, 254, 237 }; +// Multicast Listener Query Message +const uint8_t ICMPv6Test::mlqm_icmpv6_layer[] = { + 130, 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, + 8, 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 +}; + TEST_F(ICMPv6Test, Constructor) { ICMPv6 icmp; EXPECT_EQ(icmp.type(), ICMPv6::ECHO_REQUEST); @@ -166,6 +177,34 @@ TEST_F(ICMPv6Test, ConstructorFromBuffer_MLD2_Layer) { ); } +TEST_F(ICMPv6Test, ConstructorFromBuffer_MLQM_Layer) { + ICMPv6 icmp(mlqm_icmpv6_layer, sizeof(mlqm_icmpv6_layer)); + ICMPv6::sources_list 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, icmp.sources()); + EXPECT_EQ(0, icmp.supress()); + EXPECT_EQ(0, icmp.qrv()); + EXPECT_EQ(0, icmp.qqic()); + EXPECT_EQ(0, icmp.maximum_response_code()); + EXPECT_EQ(IPv6Address("ff02::1"), icmp.multicast_addr()); + + PDU::serialization_type buffer = icmp.serialize(); + EXPECT_EQ( + PDU::serialization_type( + mlqm_icmpv6_layer, + mlqm_icmpv6_layer + sizeof(mlqm_icmpv6_layer) + ), + buffer + ); +} + TEST_F(ICMPv6Test, Type) { ICMPv6 icmp; icmp.type(ICMPv6::MLD2_REPORT); @@ -516,6 +555,59 @@ TEST_F(ICMPv6Test, DNSSearchList) { EXPECT_EQ(data.domains, output.domains); } +TEST_F(ICMPv6Test, MLD2Fields) { + ICMPv6 icmp; + ICMPv6::multicast_address_records_list records; + ICMPv6::multicast_address_record r; + r.type = 1; + r.aux_data.push_back(0xde); + r.aux_data.push_back(0xad); + r.aux_data.push_back(0xbe); + r.aux_data.push_back(0xef); + r.aux_data.push_back(0xbe); + r.aux_data.push_back(0xad); + r.aux_data.push_back(0xfe); + r.aux_data.push_back(0xed); + r.sources.push_back("::"); + r.sources.push_back("ff02::1"); + r.sources.push_back("::"); + r.sources.push_back("ff02::1"); + r.sources.push_back("ff02::1"); + r.sources.push_back("ff02::2"); + r.sources.push_back("::1"); + r.sources.push_back("ff02::1:ff00:9"); + + records.push_back(r); + icmp.multicast_address_records(records); + ICMPv6::multicast_address_records_list stored_records; + stored_records = icmp.multicast_address_records(); + ASSERT_EQ(1, stored_records.size()); + + ICMPv6::multicast_address_record r2 = *stored_records.begin(); + EXPECT_EQ(r.type, r2.type); + EXPECT_EQ(r.sources, r2.sources); + EXPECT_EQ(r.aux_data, r2.aux_data); +} + +TEST_F(ICMPv6Test, MLQMFields) { + ICMPv6 icmp; + ICMPv6::sources_list sources; + sources.push_back("::"); + sources.push_back("ff02::1"); + icmp.sources(sources); + icmp.qrv(3); + icmp.maximum_response_code(0x928a); + icmp.supress(1); + icmp.qqic(0xa8); + icmp.multicast_addr("feed::beef"); + EXPECT_EQ(sources, icmp.sources()); + EXPECT_EQ(1, icmp.supress()); + EXPECT_EQ(3, icmp.qrv()); + EXPECT_EQ(0xa8, icmp.qqic()); + EXPECT_EQ(0x928a, icmp.maximum_response_code()); + EXPECT_EQ(IPv6Address("feed::beef"), icmp.multicast_addr()); +} + TEST_F(ICMPv6Test, SpoofedOptions) { ICMPv6 pdu; uint8_t a[] = { 1,2,3,4,5,6 };