Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions include/tins/ip.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,11 @@ class TINS_API IP : public PDU {
*/
typedef std::vector<option> options_type;

/**
* The type used to store the bytes after the last parsed option but before the end the IP header.
*/
typedef std::vector<uint8_t> post_options_type;

/**
* \brief Extracts metadata for this protocol based on the buffer provided
*
Expand Down Expand Up @@ -416,6 +421,14 @@ class TINS_API IP : public PDU {
return options_;
}

/**
* \brief Getter for the IP options.
* \return The stored options.
*/
const post_options_type& post_option_bytes() const {
return post_option_bytes_;
}

/* Setters */

/**
Expand Down Expand Up @@ -508,6 +521,22 @@ class TINS_API IP : public PDU {
*/
void version(small_uint<4> ver);

/**
* \brief Sets the post option bytes.
*
* \param b The post_options_type to be set to
*/
void post_option_bytes(const post_options_type& b);

/**
* \brief Adds a byte to the post option bytes.
*
* The byte is append
*
* \param b The byte to be added
*/
void add_post_option_byte(uint8_t b);

/**
* \brief Adds an IP option.
*
Expand Down Expand Up @@ -769,6 +798,7 @@ class TINS_API IP : public PDU {
options_type::const_iterator search_option_iterator(option_identifier id) const;
options_type::iterator search_option_iterator(option_identifier id);

post_options_type post_option_bytes_;
options_type options_;
ip_header header_;
};
Expand Down
2 changes: 1 addition & 1 deletion include/tins/ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ class TINS_API IPv6 : public PDU {
void write_serialization(uint8_t* buffer, uint32_t total_sz);
void set_last_next_header(uint8_t value);
uint32_t calculate_headers_size() const;
static void write_header(const ext_header& header, Memory::OutputMemoryStream& stream);
static void write_header(const ext_header& header, Memory::OutputMemoryStream& stream, ExtensionHeader type);
static bool is_extension_header(uint8_t header_id);
static uint32_t get_padding_size(const ext_header& header);
static std::vector<header_option_type> parse_header_options(const uint8_t* data, size_t size);
Expand Down
24 changes: 18 additions & 6 deletions src/ip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,12 @@ IP::IP(const uint8_t* buffer, uint32_t total_sz) {
}
}
else if (opt_type == END) {
// If the end option found, we're done
options_.push_back(option(opt_type));
// If the end option is found, we're done parsing options, however there may still be data after the end option
if (TINS_UNLIKELY(stream.pointer() != options_end)) {
// Make sure we found the END option at the end of the options list
throw malformed_packet();
while (stream.pointer() < options_end) {
add_post_option_byte(stream.read<uint8_t>());
}
}
break;
}
Expand Down Expand Up @@ -314,6 +316,14 @@ uint16_t IP::stream_identifier() const {
return opt->to<uint16_t>();
}

void IP::post_option_bytes(const IP::post_options_type& b) {
post_option_bytes_ = b;
}

void IP::add_post_option_byte(const uint8_t b) {
post_option_bytes_.push_back(b);
}

void IP::add_option(const option& opt) {
options_.push_back(opt);
}
Expand Down Expand Up @@ -374,7 +384,7 @@ void IP::write_option(const option& opt, OutputMemoryStream& stream) {
// Virtual method overriding

uint32_t IP::header_size() const {
return sizeof(header_) + pad_options_size(calculate_options_size());
return sizeof(header_) + pad_options_size(calculate_options_size() + post_option_bytes_.size());
}

PacketSender::SocketType pdu_type_to_sender_type(PDU::PDUType type) {
Expand Down Expand Up @@ -459,9 +469,11 @@ void IP::write_serialization(uint8_t* buffer, uint32_t total_sz) {
write_option(*it, stream);
}
const uint32_t options_size = calculate_options_size();
const uint32_t padded_options_size = pad_options_size(options_size);
stream.write(post_option_bytes_.begin(), post_option_bytes_.end());
const uint32_t not_padded_options_size = options_size + post_option_bytes_.size();
const uint32_t padded_options_size = pad_options_size(not_padded_options_size);
// Add option padding
stream.fill(padded_options_size - options_size, 0);
stream.fill(padded_options_size - not_padded_options_size, 0);

uint32_t check = Utils::do_checksum(buffer, stream.pointer());
while (check >> 16) {
Expand Down
19 changes: 12 additions & 7 deletions src/ipv6.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ IPv6::IPv6(const uint8_t* buffer, uint32_t total_sz) {
const uint8_t ext_type = stream.read<uint8_t>();
// every ext header is at least 8 bytes long
// minus one, from the next_header field.
const uint32_t ext_size = (static_cast<uint32_t>(stream.read<uint8_t>()) + 1) * 8;
const uint32_t ext_size = current_header == AUTHENTICATION ? (static_cast<uint32_t>(stream.read<uint8_t>()) + 2) * 4 : (static_cast<uint32_t>(stream.read<uint8_t>()) + 1) * 8;
const uint32_t payload_size = ext_size - sizeof(uint8_t) * 2;
if (!stream.can_read(payload_size)) {
throw malformed_packet();
Expand Down Expand Up @@ -359,8 +359,10 @@ void IPv6::write_serialization(uint8_t* buffer, uint32_t total_sz) {
}
payload_length(static_cast<uint16_t>(total_sz - sizeof(header_)));
stream.write(header_);
auto prev_nh = header_.next_header;
for (headers_type::const_iterator it = ext_headers_.begin(); it != ext_headers_.end(); ++it) {
write_header(*it, stream);
write_header(*it, stream, (ExtensionHeader)prev_nh);
prev_nh = it->option();
}
// Restore our original header types
for (size_t i = 0; i < ext_headers_.size(); ++i) {
Expand Down Expand Up @@ -425,19 +427,22 @@ uint32_t IPv6::calculate_headers_size() const {
uint32_t output = 0;
for (const_iterator iter = ext_headers_.begin(); iter != ext_headers_.end(); ++iter) {
output += static_cast<uint32_t>(iter->data_size() + sizeof(uint8_t) * 2);
output += get_padding_size(*iter);

bool may_need_padding = iter->option() != AUTHENTICATION;
if (may_need_padding)
output += get_padding_size(*iter);
}
return output;
}

void IPv6::write_header(const ext_header& header, OutputMemoryStream& stream) {
const uint8_t length = header.length_field() / 8;
void IPv6::write_header(const ext_header& header, OutputMemoryStream& stream, ExtensionHeader type) {
const uint8_t length = type == AUTHENTICATION ? ( (2 + header.length_field()) / 4 - 2) : ( (2 + header.length_field()) / 8 - 1);
stream.write(header.option());
stream.write(length);
stream.write(header.data_ptr(), header.data_size());
// Append padding
stream.fill(get_padding_size(header), 0);
bool may_need_padding = type != AUTHENTICATION;
if (may_need_padding)
stream.fill(get_padding_size(header), 0);
}

} // Tins
45 changes: 45 additions & 0 deletions tests/src/ip_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ using namespace Tins;
#define TINS_DEFAULT_TEST_IP "127.0.0.1"
#endif

std::vector<uint8_t> HexToBytes(const std::string& hex) {
std::vector<uint8_t> bytes;

for (unsigned int i = 0; i < hex.length(); i += 2) {
std::string byteString = hex.substr(i, 2);
uint8_t byte = (uint8_t) strtol(byteString.c_str(), NULL, 16);
bytes.push_back(byte);
}

return bytes;
}

class IPTest : public testing::Test {
public:
static const uint8_t expected_packet[], fragmented_packet[],
Expand Down Expand Up @@ -883,3 +895,36 @@ TEST_F(IPTest, ParseSerializeOptions) {
const vector<uint8_t> buffer(options_packet, options_packet + sizeof(options_packet));
EXPECT_EQ(buffer, serialized);
}

TEST_F(IPTest, EndOfOptionsBeforeEndOfHeader) {
// IPv4 Header with 16 bytes of options where the first byte of options is the EOL option/ UDP (8 bytes)
std::string ip_hex_data = "4900002c0001000040110d52ac10080eac100835"
"000000000001010a0000ffffffffffff"
"271000500008701a";
std::vector<uint8_t> ip_data_vector = HexToBytes(ip_hex_data);
const uint8_t *ip_data = ip_data_vector.data();

IP packet(ip_data, ip_data_vector.size());

const IP::options_type &options = packet.options();
EXPECT_EQ(options.size(), 1);
EXPECT_EQ(options[0].option().number, IP::END);
const IP::post_options_type &post_option_bytes = packet.post_option_bytes();
const IP::post_options_type expected_post_option_bytes = HexToBytes("0000000001010a0000ffffffffffff");
EXPECT_EQ(post_option_bytes, post_option_bytes);
}

TEST_F(IPTest, EndOfOptionsBeforeEndOfHeaderSerialize) {
// IPv4 Header with 16 bytes of options where the first byte of options is the EOL option/ UDP (8 bytes)
std::string ip_hex_data = "4900002c0001000040110d52ac10080eac100835"
"000000000001010a0000ffffffffffff"
"271000500008701a";
std::vector<uint8_t> ip_data_vector = HexToBytes(ip_hex_data);
const uint8_t *ip_data = ip_data_vector.data();
IP packet(ip_data, ip_data_vector.size());

auto serialized = packet.serialize();

EXPECT_EQ(serialized.size(), ip_data_vector.size());
EXPECT_EQ(serialized, ip_data_vector);
}
Loading