1
0
mirror of https://github.com/mfontanini/libtins synced 2026-01-23 02:35:57 +01:00

Add ICMPv6 Multicast Listener Query Messages support

This commit is contained in:
Matias Fontanini
2016-01-20 20:27:37 -08:00
parent 3d21ad7bec
commit dc1a5a6982
3 changed files with 266 additions and 8 deletions

View File

@@ -527,7 +527,7 @@ public:
typedef std::vector<ipaddress_type> sources_type; typedef std::vector<ipaddress_type> sources_type;
typedef std::vector<uint8_t> aux_data_type; typedef std::vector<uint8_t> 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); multicast_address_record(const uint8_t* buffer, uint32_t total_sz);
void serialize(uint8_t* buffer, uint32_t total_sz) const; void serialize(uint8_t* buffer, uint32_t total_sz) const;
@@ -544,6 +544,12 @@ public:
*/ */
typedef std::list<multicast_address_record> multicast_address_records_list; typedef std::list<multicast_address_record> multicast_address_records_list;
/*
* The type used to store all source address (from Multicast
* Listener Query messages) in a packet
*/
typedef std::list<ipaddress_type> sources_list;
/** /**
* \brief Constructs an ICMPv6 object. * \brief Constructs an ICMPv6 object.
* *
@@ -634,13 +640,21 @@ public:
} }
/** /**
* \brief Getter for the hop_limit field. * \brief Getter for the hop limit field.
* \return The stored hop_limit field value. * \return The stored hop limit field value.
*/ */
uint8_t hop_limit() const { uint8_t hop_limit() const {
return header_.u_nd_ra.hop_limit; 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. * \brief Getter for the router_pref field.
* \return The stored router_pref field value. * \return The stored router_pref field value.
@@ -713,6 +727,17 @@ public:
return dest_address_; 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. * \brief Getter for the ICMPv6 options.
* \return The stored options. * \return The stored options.
@@ -737,6 +762,46 @@ public:
return multicast_records_; 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 // Setters
/** /**
@@ -793,6 +858,12 @@ public:
*/ */
void hop_limit(uint8_t new_hop_limit); 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. * \brief Setter for the router_pref field.
* \param new_router_pref The new router_pref field value. * \param new_router_pref The new router_pref field value.
@@ -835,6 +906,15 @@ public:
*/ */
void dest_addr(const ipaddress_type& new_dest_addr); 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. * \brief Setter for the reachable_time field.
* \param new_reachable_time The new reachable_time field value. * \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); 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. * \brief Getter for the PDU's type.
* *
@@ -1421,9 +1529,17 @@ private:
struct { struct {
uint16_t reserved; uint16_t reserved;
uint16_t record_count; uint16_t record_count;
} mlrm2 ; } mlrm2;
}; };
} TINS_END_PACK; } 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 internal_add_option(const option& option);
void write_serialization(uint8_t* buffer, uint32_t total_sz, const PDU* parent); void write_serialization(uint8_t* buffer, uint32_t total_sz, const PDU* parent);
@@ -1458,14 +1574,18 @@ private:
} }
icmp6_header header_; icmp6_header header_;
ipaddress_type target_address_, dest_address_; ipaddress_type target_address_;
ipaddress_type dest_address_;
ipaddress_type multicast_address_;
options_type options_; options_type options_;
uint32_t options_size_; uint32_t options_size_;
uint32_t reach_time_, retrans_timer_; uint32_t reach_time_, retrans_timer_;
multicast_address_records_list multicast_records_; multicast_address_records_list multicast_records_;
multicast_listener_query_message_fields mlqm_;
sources_list sources_;
ICMPExtensionsStructure extensions_; ICMPExtensionsStructure extensions_;
}; };
}
} // Tins
#endif // TINS_ICMPV6_H #endif // TINS_ICMPV6_H

View File

@@ -75,6 +75,16 @@ ICMPv6::ICMPv6(const uint8_t* buffer, uint32_t total_sz)
stream.skip(multicast_records_.back().size()); stream.skip(multicast_records_.back().size());
} }
} }
else if (type() == MGM_QUERY) {
stream.read(multicast_address_);
stream.read(mlqm_);
int sources_count = stream.read_be<uint16_t>();
while (sources_count--) {
ipaddress_type address;
stream.read(address);
sources_.push_back(address);
}
}
// Retrieve options // Retrieve options
if (has_options()) { if (has_options()) {
parse_options(stream); 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; 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) { void ICMPv6::router_pref(small_uint<2> new_router_pref) {
header_.u_nd_ra.router_pref = 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; 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) { void ICMPv6::target_addr(const ipaddress_type& new_target_addr) {
target_address_ = 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; 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 ICMPv6::header_size() const {
uint32_t extra = 0; uint32_t extra = 0;
if (type() == ROUTER_ADVERT) { if (type() == ROUTER_ADVERT) {
@@ -197,6 +231,10 @@ uint32_t ICMPv6::header_size() const {
extra += iter->size(); 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 + return sizeof(header_) + options_size_ + extra +
(has_target_addr() ? ipaddress_type::address_size : 0) + (has_target_addr() ? ipaddress_type::address_size : 0) +
(has_dest_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()); stream.skip(iter->size());
} }
} }
else if (type() == MGM_QUERY) {
stream.write(multicast_address_);
stream.write(mlqm_);
stream.write_be<uint16_t>(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) { for (options_type::const_iterator it = options_.begin(); it != options_.end(); ++it) {
write_option(*it, stream); 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(); 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 it's lower than 128, we need to padd enough zeroes to make it 128 bytes long
if (inner_pdu_size < 128) { if (inner_pdu_size < 128) {
memset(extensions_ptr + inner_pdu_size, 0, memset(extensions_ptr + inner_pdu_size, 0, 128 - inner_pdu_size);
128 - inner_pdu_size);
inner_pdu_size = 128; inner_pdu_size = 128;
} }
else { else {

View File

@@ -20,6 +20,7 @@ public:
static const uint8_t packet_with_extensions[]; static const uint8_t packet_with_extensions[];
static const uint8_t packet_with_extensions_and_length[]; static const uint8_t packet_with_extensions_and_length[];
static const uint8_t mld2_icmpv6_layer[]; static const uint8_t mld2_icmpv6_layer[];
static const uint8_t mlqm_icmpv6_layer[];
void test_equals(const ICMPv6& icmp1, const ICMPv6& icmp2); 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 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) { TEST_F(ICMPv6Test, Constructor) {
ICMPv6 icmp; ICMPv6 icmp;
EXPECT_EQ(icmp.type(), ICMPv6::ECHO_REQUEST); 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) { TEST_F(ICMPv6Test, Type) {
ICMPv6 icmp; ICMPv6 icmp;
icmp.type(ICMPv6::MLD2_REPORT); icmp.type(ICMPv6::MLD2_REPORT);
@@ -516,6 +555,59 @@ TEST_F(ICMPv6Test, DNSSearchList) {
EXPECT_EQ(data.domains, output.domains); 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) { TEST_F(ICMPv6Test, SpoofedOptions) {
ICMPv6 pdu; ICMPv6 pdu;
uint8_t a[] = { 1,2,3,4,5,6 }; uint8_t a[] = { 1,2,3,4,5,6 };