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

Add remove_option member to IP, TCP, Dot11, ICMPv6, DHCP and DHCPv6

This commit is contained in:
Matias Fontanini
2015-08-17 15:19:03 -07:00
parent ab61907a06
commit 6dec68128d
19 changed files with 386 additions and 59 deletions

View File

@@ -210,6 +210,17 @@ namespace Tins {
_options.push_back(std::move(opt));
}
#endif
/**
* \brief Removes a DHCP option.
*
* If there are multiple options of the given type, only the first one
* will be removed.
*
* \param type The type of the option to be removed.
* \return true if the option was removed, false otherwise.
*/
bool remove_option(OptionTypes type);
/**
* \brief Searchs for an option that matchs the given flag.
@@ -501,6 +512,8 @@ namespace Tins {
void internal_add_option(const option &opt);
serialization_type serialize_list(const std::vector<ipaddress_type> &ip_list);
options_type::const_iterator search_option_iterator(OptionTypes opt) const;
options_type::iterator search_option_iterator(OptionTypes opt);
options_type _options;
uint32_t _size;

View File

@@ -816,15 +816,26 @@ public:
void add_option(const option &opt);
/**
* \brief Searchs for an option that matchs the given flag.
* \brief Removes a DHCPv6 option.
*
* If there are multiple options of the given type, only the first one
* will be removed.
*
* \param type The type of the option to be removed.
* \return true if the option was removed, false otherwise.
*/
bool remove_option(OptionTypes type);
/**
* \brief Searchs for an option that matchs the given type.
*
* If the option is not found, a null pointer is returned.
* Deleting the returned pointer will result in <b>undefined
* behaviour</b>.
*
* \param id The option identifier to be searched.
* \param type The option identifier to be searched.
*/
const option *search_option(OptionTypes id) const;
const option *search_option(OptionTypes type) const;
// PDU stuff
@@ -859,6 +870,8 @@ public:
private:
void write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *);
uint8_t* write_option(const option &option, uint8_t* buffer) const;
options_type::const_iterator search_option_iterator(OptionTypes type) const;
options_type::iterator search_option_iterator(OptionTypes type);
template<template <typename> class Functor>
const option *safe_search_option(OptionTypes opt, uint32_t size) const {

View File

@@ -409,15 +409,26 @@ public:
}
#endif
/**
* \brief Removes a Dot11 option.
*
* If there are multiple options of the given type, only the first one
* will be removed.
*
* \param type The type of the option to be removed.
* \return true if the option was removed, false otherwise.
*/
bool remove_option(OptionTypes type);
/**
* \brief Looks up a tagged option in the option list.
*
* The returned pointer <b>must not</b> be free'd.
*
* \param opt The option identifier.
* \param type The option identifier.
* \return The option found, or 0 if no such option has been set.
*/
const option *search_option(OptionTypes opt) const;
const option *search_option(OptionTypes type) const;
/**
* \brief Getter for the PDU's type.
@@ -511,6 +522,8 @@ private:
void internal_add_option(const option &opt);
void write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent);
options_type::const_iterator search_option_iterator(OptionTypes type) const;
options_type::iterator search_option_iterator(OptionTypes type);
ieee80211_header _header;

View File

@@ -853,6 +853,17 @@ public:
}
#endif
/**
* \brief Removes an ICMPv6 option.
*
* If there are multiple options of the given type, only the first one
* will be removed.
*
* \param type The type of the option to be removed.
* \return true if the option was removed, false otherwise.
*/
bool remove_option(OptionTypes type);
/**
* \brief Returns the header size.
*
@@ -877,9 +888,9 @@ public:
* Deleting the returned pointer will result in <b>undefined
* behaviour</b>.
*
* \param id The option identifier to be searched.
* \param type The option identifier to be searched.
*/
const option *search_option(OptionTypes id) const;
const option *search_option(OptionTypes type) const;
/**
* \sa PDU::clone
@@ -1309,6 +1320,8 @@ private:
void parse_options(const uint8_t *&buffer, uint32_t &total_sz);
void add_addr_list(uint8_t type, const addr_list_type &value);
addr_list_type search_addr_list(OptionTypes type) const;
options_type::const_iterator search_option_iterator(OptionTypes type) const;
options_type::iterator search_option_iterator(OptionTypes type);
template<template <typename> class Functor>
const option *safe_search_option(OptionTypes opt, uint32_t size) const {

View File

@@ -81,7 +81,7 @@ namespace Tins {
END = 0,
NOOP = 1,
SEC = 2,
LSSR = 3,
LSRR = 3,
TIMESTAMP = 4,
EXTSEC = 5,
RR = 7,
@@ -443,6 +443,17 @@ namespace Tins {
}
#endif
/**
* \brief Removes an IP option.
*
* If there are multiple options of the given type, only the first one
* will be removed.
*
* \param type The type of the option to be removed.
* \return true if the option was removed, false otherwise.
*/
bool remove_option(option_identifier id);
/**
* \brief Searchs for an option that matchs the given flag.
*
@@ -653,11 +664,15 @@ namespace Tins {
void add_route_option(option_identifier id, const generic_route_option_type &data);
generic_route_option_type search_route_option(option_identifier id) const;
void checksum(uint16_t new_check);
options_type::const_iterator search_option_iterator(option_identifier id) const;
options_type::iterator search_option_iterator(option_identifier id);
void update_padded_options_size();
iphdr _ip;
uint16_t _options_size, _padded_options_size;
options_type _ip_options;
};
}
} // Tins
#endif // TINS_IP_H

View File

@@ -523,5 +523,25 @@ private:
data_type* big_buffer_ptr;
} payload_;
};
namespace Internals {
/*
* \cond
*/
template <typename Option>
struct option_type_equality_comparator {
option_type_equality_comparator(typename Option::option_type type) : type(type) { }
bool operator()(const Option& opt) const {
return opt.option() == type;
}
typename Option::option_type type;
};
/*
* \endcond
*/
} // Internals
} // namespace Tins
#endif // TINS_PDU_OPTION_H

View File

@@ -467,6 +467,17 @@ namespace Tins {
}
#endif
/**
* \brief Removes a TCP option.
*
* If there are multiple options of the given type, only the first one
* will be removed.
*
* \param type The type of the option to be removed.
* \return true if the option was removed, false otherwise.
*/
bool remove_option(OptionTypes type);
/**
* \brief Returns the header size.
*
@@ -494,11 +505,11 @@ namespace Tins {
PDUType pdu_type() const { return PDU::TCP; }
/**
* \brief Searchs for an option that matchs the given flag.
* \param opt_flag The flag to be searched.
* \brief Searchs for an option that matchs the given type.
* \param type The option type to be searched.
* \return A pointer to the option, or 0 if it was not found.
*/
const option *search_option(OptionTypes opt) const;
const option *search_option(OptionTypes type) const;
/**
* \sa PDU::clone
@@ -568,6 +579,9 @@ namespace Tins {
void internal_add_option(const option &option);
void write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent);
void checksum(uint16_t new_check);
void update_options_size();
options_type::const_iterator search_option_iterator(OptionTypes type) const;
options_type::iterator search_option_iterator(OptionTypes type);
uint8_t *write_option(const option &opt, uint8_t *buffer);
@@ -575,6 +589,6 @@ namespace Tins {
uint16_t _options_size, _total_options_size;
options_type _options;
};
}
} // Tins
#endif // TINS_TCP_H

View File

@@ -33,11 +33,13 @@
#include "endianness.h"
#include "dhcp.h"
#include "ethernetII.h"
#include "internals.h"
#include "exceptions.h"
using std::string;
using std::list;
using std::runtime_error;
using std::find_if;
namespace Tins {
// Magic cookie: uint32_t.
@@ -89,12 +91,30 @@ void DHCP::internal_add_option(const option &opt) {
_size += static_cast<uint32_t>(opt.data_size() + (sizeof(uint8_t) << 1));
}
const DHCP::option *DHCP::search_option(OptionTypes opt) const {
for(options_type::const_iterator it = _options.begin(); it != _options.end(); ++it) {
if(it->option() == opt)
return &(*it);
bool DHCP::remove_option(OptionTypes type) {
options_type::iterator iter = search_option_iterator(type);
if (iter == _options.end()) {
return false;
}
return 0;
_size -= static_cast<uint32_t>(iter->data_size() + (sizeof(uint8_t) << 1));
_options.erase(iter);
return true;
}
const DHCP::option *DHCP::search_option(OptionTypes opt) const {
// Search for the iterator. If we found something, return it, otherwise return nullptr.
options_type::const_iterator iter = search_option_iterator(opt);
return (iter != _options.end()) ? &*iter : 0;
}
DHCP::options_type::const_iterator DHCP::search_option_iterator(OptionTypes opt) const {
Internals::option_type_equality_comparator<option> comparator(opt);
return find_if(_options.begin(), _options.end(), comparator);
}
DHCP::options_type::iterator DHCP::search_option_iterator(OptionTypes opt) {
Internals::option_type_equality_comparator<option> comparator(opt);
return find_if(_options.begin(), _options.end(), comparator);
}
void DHCP::type(Flags type) {

View File

@@ -32,6 +32,8 @@
#include "dhcpv6.h"
#include "exceptions.h"
using std::find_if;
namespace Tins {
DHCPv6::DHCPv6() : options_size() {
std::fill(header_data, header_data + sizeof(header_data), 0);
@@ -84,12 +86,30 @@ void DHCPv6::add_option(const option &opt) {
options_size += opt.data_size() + sizeof(uint16_t) * 2;
}
const DHCPv6::option *DHCPv6::search_option(OptionTypes id) const {
for(options_type::const_iterator it = options_.begin(); it != options_.end(); ++it) {
if(it->option() == static_cast<uint16_t>(id))
return &*it;
bool DHCPv6::remove_option(OptionTypes type) {
options_type::iterator iter = search_option_iterator(type);
if (iter == options_.end()) {
return false;
}
return 0;
options_size -= iter->data_size() + sizeof(uint16_t) * 2;
options_.erase(iter);
return true;
}
const DHCPv6::option *DHCPv6::search_option(OptionTypes type) const {
// Search for the iterator. If we found something, return it, otherwise return nullptr.
options_type::const_iterator iter = search_option_iterator(type);
return (iter != options_.end()) ? &*iter : 0;
}
DHCPv6::options_type::const_iterator DHCPv6::search_option_iterator(OptionTypes type) const {
Internals::option_type_equality_comparator<option> comparator(type);
return find_if(options_.begin(), options_.end(), comparator);
}
DHCPv6::options_type::iterator DHCPv6::search_option_iterator(OptionTypes type) {
Internals::option_type_equality_comparator<option> comparator(type);
return find_if(options_.begin(), options_.end(), comparator);
}
uint8_t* DHCPv6::write_option(const option &opt, uint8_t* buffer) const {
@@ -151,8 +171,9 @@ void DHCPv6::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *
buffer = link_addr.copy(buffer);
buffer = peer_addr.copy(buffer);
}
for(options_type::const_iterator it = options_.begin(); it != options_.end(); ++it)
for(options_type::const_iterator it = options_.begin(); it != options_.end(); ++it) {
buffer = write_option(*it, buffer);
}
}

View File

@@ -106,16 +106,35 @@ void Dot11::internal_add_option(const option &opt) {
_options_size += static_cast<uint32_t>(opt.data_size() + sizeof(uint8_t) * 2);
}
bool Dot11::remove_option(OptionTypes type) {
options_type::iterator iter = search_option_iterator(type);
if (iter == _options.end()) {
return false;
}
_options_size -= static_cast<uint32_t>(iter->data_size() + sizeof(uint8_t) * 2);
_options.erase(iter);
return true;
}
void Dot11::add_option(const option &opt) {
internal_add_option(opt);
_options.push_back(opt);
}
const Dot11::option *Dot11::search_option(OptionTypes opt) const {
for(std::list<option>::const_iterator it = _options.begin(); it != _options.end(); ++it)
if(it->option() == (uint8_t)opt)
return &(*it);
return 0;
const Dot11::option *Dot11::search_option(OptionTypes type) const {
// Search for the iterator. If we found something, return it, otherwise return nullptr.
options_type::const_iterator iter = search_option_iterator(type);
return (iter != _options.end()) ? &*iter : 0;
}
Dot11::options_type::const_iterator Dot11::search_option_iterator(OptionTypes type) const {
Internals::option_type_equality_comparator<option> comparator(static_cast<uint8_t>(type));
return find_if(_options.begin(), _options.end(), comparator);
}
Dot11::options_type::iterator Dot11::search_option_iterator(OptionTypes type) {
Internals::option_type_equality_comparator<option> comparator(static_cast<uint8_t>(type));
return find_if(_options.begin(), _options.end(), comparator);
}
void Dot11::protocol(small_uint<2> new_proto) {

View File

@@ -263,18 +263,36 @@ void ICMPv6::internal_add_option(const option &option) {
_options_size += static_cast<uint32_t>(option.data_size() + sizeof(uint8_t) * 2);
}
bool ICMPv6::remove_option(OptionTypes type) {
options_type::iterator iter = search_option_iterator(type);
if (iter == _options.end()) {
return false;
}
_options_size -= static_cast<uint32_t>(iter->data_size() + sizeof(uint8_t) * 2);
_options.erase(iter);
return true;
}
uint8_t *ICMPv6::write_option(const option &opt, uint8_t *buffer) {
*buffer++ = opt.option();
*buffer++ = static_cast<uint8_t>((opt.length_field() + sizeof(uint8_t) * 2) / 8);
return std::copy(opt.data_ptr(), opt.data_ptr() + opt.data_size(), buffer);
}
const ICMPv6::option *ICMPv6::search_option(OptionTypes id) const {
for(options_type::const_iterator it = _options.begin(); it != _options.end(); ++it) {
if(it->option() == id)
return &*it;
}
return 0;
const ICMPv6::option *ICMPv6::search_option(OptionTypes type) const {
// Search for the iterator. If we found something, return it, otherwise return nullptr.
options_type::const_iterator iter = search_option_iterator(type);
return (iter != _options.end()) ? &*iter : 0;
}
ICMPv6::options_type::const_iterator ICMPv6::search_option_iterator(OptionTypes type) const {
Internals::option_type_equality_comparator<option> comparator(type);
return find_if(_options.begin(), _options.end(), comparator);
}
ICMPv6::options_type::iterator ICMPv6::search_option_iterator(OptionTypes type) {
Internals::option_type_equality_comparator<option> comparator(type);
return find_if(_options.begin(), _options.end(), comparator);
}
// ********************************************************************

View File

@@ -297,18 +297,40 @@ void IP::add_option(const option &opt) {
_ip_options.push_back(opt);
}
void IP::internal_add_option(const option &opt) {
_options_size += static_cast<uint16_t>(1 + opt.data_size());
void IP::update_padded_options_size() {
uint8_t padding = _options_size % 4;
_padded_options_size = padding ? (_options_size - padding + 4) : _options_size;
}
const IP::option *IP::search_option(option_identifier id) const {
for(options_type::const_iterator it = _ip_options.begin(); it != _ip_options.end(); ++it) {
if(it->option() == id)
return &(*it);
void IP::internal_add_option(const option &opt) {
_options_size += static_cast<uint16_t>(1 + opt.data_size());
update_padded_options_size();
}
bool IP::remove_option(option_identifier id) {
options_type::iterator iter = search_option_iterator(id);
if (iter == _ip_options.end()) {
return false;
}
return 0;
_options_size -= static_cast<uint16_t>(1 + iter->data_size());
_ip_options.erase(iter);
update_padded_options_size();
return true;
}
const IP::option *IP::search_option(option_identifier id) const {
options_type::const_iterator iter = search_option_iterator(id);
return (iter != _ip_options.end()) ? &*iter : 0;
}
IP::options_type::const_iterator IP::search_option_iterator(option_identifier id) const {
Internals::option_type_equality_comparator<option> comparator(id);
return find_if(_ip_options.begin(), _ip_options.end(), comparator);
}
IP::options_type::iterator IP::search_option_iterator(option_identifier id) {
Internals::option_type_equality_comparator<option> comparator(id);
return find_if(_ip_options.begin(), _ip_options.end(), comparator);
}
uint8_t* IP::write_option(const option &opt, uint8_t* buffer) {

View File

@@ -28,6 +28,7 @@
*/
#include <cstring>
#include <algorithm>
#include <cassert>
#include "tcp.h"
#include "ip.h"
@@ -36,6 +37,9 @@
#include "rawpdu.h"
#include "utils.h"
#include "exceptions.h"
#include "internals.h"
using std::find_if;
namespace Tins {
@@ -321,12 +325,20 @@ void TCP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *par
}
}
const TCP::option *TCP::search_option(OptionTypes opt) const {
for(options_type::const_iterator it = _options.begin(); it != _options.end(); ++it) {
if(it->option() == opt)
return &(*it);
}
return 0;
const TCP::option *TCP::search_option(OptionTypes type) const {
// Search for the iterator. If we found something, return it, otherwise return nullptr.
options_type::const_iterator iter = search_option_iterator(type);
return (iter != _options.end()) ? &*iter : 0;
}
TCP::options_type::const_iterator TCP::search_option_iterator(OptionTypes type) const {
Internals::option_type_equality_comparator<option> comparator(type);
return find_if(_options.begin(), _options.end(), comparator);
}
TCP::options_type::iterator TCP::search_option_iterator(OptionTypes type) {
Internals::option_type_equality_comparator<option> comparator(type);
return find_if(_options.begin(), _options.end(), comparator);
}
/* options */
@@ -347,18 +359,35 @@ uint8_t *TCP::write_option(const option &opt, uint8_t *buffer) {
}
}
void TCP::update_options_size() {
uint8_t padding = _options_size & 3;
_total_options_size = (padding) ? (_options_size - padding + 4) : _options_size;
}
void TCP::internal_add_option(const option &opt) {
uint8_t padding;
_options_size += sizeof(uint8_t);
// SACK_OK contains length but not data....
if(opt.data_size() || opt.option() == SACK_OK)
_options_size += sizeof(uint8_t);
_options_size += static_cast<uint16_t>(opt.data_size());
padding = _options_size & 3;
_total_options_size = (padding) ? _options_size - padding + 4 : _options_size;
if(opt.data_size() || opt.option() == SACK_OK) {
_options_size += sizeof(uint8_t);
_options_size += static_cast<uint16_t>(opt.data_size());
}
update_options_size();
}
bool TCP::remove_option(OptionTypes type) {
options_type::iterator iter = search_option_iterator(type);
if (iter == _options.end()) {
return false;
}
_options_size -= sizeof(uint8_t);
// SACK_OK contains length but not data....
if(iter->data_size() || iter->option() == SACK_OK) {
_options_size -= sizeof(uint8_t);
_options_size -= static_cast<uint16_t>(iter->data_size());
}
_options.erase(iter);
update_options_size();
return true;
}
bool TCP::matches_response(const uint8_t *ptr, uint32_t total_sz) const {

View File

@@ -276,7 +276,7 @@ TEST_F(DHCPTest, ConstructorFromBuffer) {
DHCP dhcp1(expected_packet, sizeof(expected_packet));
std::vector<IPv4Address> routers, expected_routers;
expected_routers.push_back("192.168.0.1");
expected_routers.push_back("127.0.0.1");
expected_routers.push_back("127.0.0.1");
EXPECT_EQ(dhcp1.opcode(), DHCP::DISCOVER);
EXPECT_EQ(dhcp1.htype(), 1);
@@ -305,4 +305,15 @@ TEST_F(DHCPTest, Serialize) {
test_equals(dhcp1, dhcp2);
}
TEST_F(DHCPTest, RemoveOption) {
DHCP dhcp;
PDU::serialization_type old_buffer = dhcp.serialize();
dhcp.domain_name("libtins.github.io");
dhcp.server_identifier("192.168.0.1");
EXPECT_TRUE(dhcp.remove_option(DHCP::DOMAIN_NAME));
EXPECT_TRUE(dhcp.remove_option(DHCP::DHCP_SERVER_IDENTIFIER));
PDU::serialization_type new_buffer = dhcp.serialize();
EXPECT_EQ(old_buffer, new_buffer);
}

View File

@@ -334,3 +334,17 @@ TEST_F(DHCPv6Test, Client_Server_ID_DUIDEN) {
EXPECT_EQ(tmp.id, tmp2.id);
EXPECT_EQ(tmp.data, tmp2.data);
}
TEST_F(DHCPv6Test, RemoveOption) {
DHCPv6 dhcp;
PDU::serialization_type old_buffer = dhcp.serialize();
dhcp.server_unicast("fe00:0a9d:dd23::1");
dhcp.preference(12);
EXPECT_TRUE(dhcp.remove_option(DHCPv6::UNICAST));
EXPECT_TRUE(dhcp.remove_option(DHCPv6::PREFERENCE));
PDU::serialization_type new_buffer = dhcp.serialize();
EXPECT_EQ(old_buffer, new_buffer);
}

View File

@@ -407,7 +407,6 @@ TEST_F(Dot11BeaconTest, RSNInformationTest) {
EXPECT_EQ(rsn_info.akm_cyphers(), found.akm_cyphers());
}
TEST_F(Dot11BeaconTest, PCAPLoad1) {
const uint8_t buffer[] = {
128, 0, 0, 0, 255, 255, 255, 255, 255, 255, 244, 236, 56, 254, 77,
@@ -460,4 +459,18 @@ TEST_F(Dot11BeaconTest, Serialize) {
EXPECT_TRUE(std::equal(buffer.begin(), buffer.end(), expected_packet));
}
TEST_F(Dot11BeaconTest, RemoveOption) {
Dot11Beacon dot11;
PDU::serialization_type old_buffer = dot11.serialize();
dot11.challenge_text("libtins ftw");
dot11.power_constraint(0x1e);
EXPECT_TRUE(dot11.remove_option(Dot11::CHALLENGE_TEXT));
EXPECT_TRUE(dot11.remove_option(Dot11::POWER_CONSTRAINT));
PDU::serialization_type new_buffer = dot11.serialize();
EXPECT_EQ(old_buffer, new_buffer);
}
#endif // HAVE_DOT11

View File

@@ -469,3 +469,20 @@ TEST_F(ICMPv6Test, ChecksumCalculation) {
const ICMPv6& icmp = eth.rfind_pdu<ICMPv6>();
EXPECT_EQ(0x68bd, icmp.checksum());
}
TEST_F(ICMPv6Test, RemoveOption) {
ICMPv6 icmp;
PDU::serialization_type old_buffer = icmp.serialize();
ICMPv6::recursive_dns_type data(0x9283712);
data.servers.push_back("827d:adae::1");
data.servers.push_back("2929:1234:fefe::2");
icmp.recursive_dns_servers(data);
icmp.timestamp(0x2837d6aaa231ULL);
EXPECT_TRUE(icmp.remove_option(ICMPv6::TIMESTAMP));
EXPECT_TRUE(icmp.remove_option(ICMPv6::RECURSIVE_DNS_SERV));
PDU::serialization_type new_buffer = icmp.serialize();
EXPECT_EQ(old_buffer, new_buffer);
}

View File

@@ -695,3 +695,26 @@ TEST_F(IPTest, SpoofedOptions) {
EXPECT_EQ(3U, pdu.options().size());
EXPECT_EQ(pdu.serialize().size(), pdu.size());
}
TEST_F(IPTest, RemoveOption) {
IP ip(TINS_DEFAULT_TEST_IP);
PDU::serialization_type old_buffer = ip.serialize();
// Add a record route option
IP::record_route_type record_route(0x2d);
record_route.routes.push_back("192.168.2.3");
record_route.routes.push_back("192.168.5.1");
ip.record_route(record_route);
// Add a lsrr option
IP::lsrr_type lsrr(0x2d);
lsrr.routes.push_back("192.168.2.3");
lsrr.routes.push_back("192.168.5.1");
ip.lsrr(lsrr);
EXPECT_TRUE(ip.remove_option(IP::option_identifier(IP::LSRR, IP::CONTROL, 1)));
EXPECT_TRUE(ip.remove_option(IP::option_identifier(IP::RR, IP::CONTROL, 0)));
PDU::serialization_type new_buffer = ip.serialize();
EXPECT_EQ(old_buffer, new_buffer);
}

View File

@@ -263,3 +263,22 @@ TEST_F(TCPTest, SpoofedOptions) {
EXPECT_EQ(3U, pdu.options().size());
EXPECT_EQ(pdu.serialize().size(), pdu.size());
}
TEST_F(TCPTest, RemoveOption) {
TCP tcp(22, 987);
uint8_t a[] = { 1,2,3,4,5,6 };
// Add an option
tcp.mss(1400);
PDU::serialization_type old_buffer = tcp.serialize();
// Add options and remove them. The serializations before and after should be equal.
tcp.add_option(TCP::option(TCP::SACK, 250, a, a + sizeof(a)));
tcp.add_option(TCP::option(TCP::SACK_OK));
tcp.add_option(TCP::option(TCP::NOP));
EXPECT_TRUE(tcp.remove_option(TCP::SACK));
EXPECT_TRUE(tcp.remove_option(TCP::SACK_OK));
EXPECT_TRUE(tcp.remove_option(TCP::NOP));
PDU::serialization_type new_buffer = tcp.serialize();
EXPECT_EQ(old_buffer, new_buffer);
}