mirror of
https://github.com/mfontanini/libtins
synced 2026-01-23 02:35:57 +01:00
Add ICMPv6 multicast listener report message structure
This commit is contained in:
@@ -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<ipaddress_type> sources_type;
|
||||
typedef std::vector<uint8_t> 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_record> 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_;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -63,6 +63,15 @@ ICMPv6::ICMPv6(const uint8_t *buffer, uint32_t total_sz)
|
||||
reach_time = stream.read<uint32_t>();
|
||||
retrans_timer = stream.read<uint32_t>();
|
||||
}
|
||||
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<uint16_t>(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<uint8_t>() * sizeof(uint32_t);
|
||||
int sources_count = Endian::be_to_host(stream.read<uint16_t>());
|
||||
stream.read(multicast_address);
|
||||
while (sources_count--) {
|
||||
sources.push_back(stream.read<ipaddress_type>());
|
||||
}
|
||||
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<uint8_t>(aux_data.size() / sizeof(uint32_t));
|
||||
stream.write(Endian::host_to_be<uint16_t>(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
|
||||
|
||||
|
||||
@@ -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<uint8_t> 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<IPv6Address> 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);
|
||||
|
||||
Reference in New Issue
Block a user