1
0
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:
Matias Fontanini
2016-01-01 10:24:45 -08:00
parent 2b7714500c
commit d7e0d17154
3 changed files with 166 additions and 1 deletions

View File

@@ -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_;
};
}

View File

@@ -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

View File

@@ -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);