From 01a7b812df592a665fa80be0df02817ab78d4ec4 Mon Sep 17 00:00:00 2001 From: Santiago Alessandri Date: Mon, 19 Mar 2012 12:04:55 -0300 Subject: [PATCH] LLC almost works --- include/ieee802-3.h | 221 ++++++++++++++++++++++++++++++++++++++++++++ include/llc.h | 92 +++++++++++++----- include/pdu.h | 1 + src/ieee802-3.cpp | 169 +++++++++++++++++++++++++++++++++ src/llc.cpp | 85 ++++++++++------- tests/src/llc.cpp | 202 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 710 insertions(+), 60 deletions(-) create mode 100644 include/ieee802-3.h create mode 100644 src/ieee802-3.cpp create mode 100644 tests/src/llc.cpp diff --git a/include/ieee802-3.h b/include/ieee802-3.h new file mode 100644 index 0000000..7abb48e --- /dev/null +++ b/include/ieee802-3.h @@ -0,0 +1,221 @@ +/* + * libtins is a net packet wrapper library for crafting and + * interpreting sniffed packets. + * + * Copyright (C) 2011 Nasel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __IEEE802_3_H +#define __IEEE802_3_H + +#include +#include + +#include "pdu.h" +#include "utils.h" + +namespace Tins { + + /** + * \brief Class representing an Ethernet II PDU. + */ + class IEEE802_3 : public PDU { + + public: + + /** + * \brief Represents the IEEE802_3 broadcast address. + */ + static const uint8_t* BROADCAST; + + /** + * \brief IEEE802_3 hardware address size. + */ + static const unsigned ADDR_SIZE = 6; + + /** + * \brief Constructor for creating an IEEE802_3 PDU + * + * Constructor that builds an IEEE802_3 PDU taking the interface name, + * destination's and source's MAC. + * + * \param iface string containing the interface's name from where to send the packet. + * \param dst_hw_addr uint8_t array of 6 bytes containing the destination's MAC(optional). + * \param src_hw_addr uint8_t array of 6 bytes containing the source's MAC(optional). + * \param child PDU* with the PDU contained by the ethernet PDU (optional). + */ + IEEE802_3(const std::string& iface, const uint8_t* dst_hw_addr = 0, const uint8_t* src_hw_addr = 0, PDU* child = 0) throw (std::runtime_error); + + /** + * \brief Constructor for creating an IEEE802_3 PDU + * + * Constructor that builds an IEEE802_3 PDU taking the interface index, + * destination's and source's MAC. + * + * \param iface_index const uint32_t with the interface's index from where to send the packet. + * \param dst_hw_addr uint8_t array of 6 bytes containing the destination's MAC(optional). + * \param src_hw_addr uint8_t array of 6 bytes containing the source's MAC(optional). + * \param child PDU* with the PDU contained by the ethernet PDU (optional). + */ + IEEE802_3(uint32_t iface_index, const uint8_t* dst_hw_addr = 0, const uint8_t* src_hw_addr = 0, PDU* child = 0); + + /** + * \brief Constructor which creates an IEEE802_3 object from a buffer and adds all identifiable + * PDUs found in the buffer as children of this one. + * \param buffer The buffer from which this PDU will be constructed. + * \param total_sz The total size of the buffer. + */ + IEEE802_3(const uint8_t *buffer, uint32_t total_sz); + + /* Getters */ + /** + * \brief Getter for the destination's mac address. + * + * \return Returns the destination's mac address as a constant uint8_t pointer. + */ + inline const uint8_t* dst_addr() const { return _eth.dst_mac; } + + /** + * \brief Getter for the source's mac address. + * + * \return Returns the source's mac address as a constant uint8_t pointer. + */ + inline const uint8_t* src_addr() const { return _eth.src_mac; } + + /** + * \brief Getter for the interface. + * + * \return Returns the interface's index as an uint32_t. + */ + inline uint32_t iface() const { return this->_iface_index; } + + /** + * \brief Getter for the length field. + * \return The length field value. + */ + inline uint16_t length() const { return Utils::net_to_host_s(_eth.length); }; + + /* Setters */ + + /** + * \brief Setter for the destination's MAC. + * + * \param new_dst_mac uint8_t array of 6 bytes containing the new destination's MAC. + */ + void dst_addr(const uint8_t* new_dst_mac); + + /** + * \brief Setter for the source's MAC. + * + * \param new_src_mac uint8_t array of 6 bytes containing the new source's MAC. + */ + void src_addr(const uint8_t* new_src_mac); + + /** + * \brief Setter for the interface. + * + * \param new_iface_index uint32_t containing the new interface index. + */ + void iface(uint32_t new_iface_index); + + /** + * \brief Setter for the interface. + * + * \param new_iface string reference containing the new interface name. + */ + void iface(const std::string& new_iface) throw (std::runtime_error); + + /** + * \brief Setter for the length field. + * + * \param new_length uint16_t with the new value of the length field. + */ + void length(uint16_t new_length); + + /* Virtual methods */ + /** + * \brief Returns the IEEE802_3 frame's header length. + * + * \return An uint32_t with the header's size. + * \sa PDU::header_size() + */ + uint32_t header_size() const; + + /** + * \sa PDU::send() + */ + bool send(PacketSender* sender); + + /** \brief Check wether ptr points to a valid response for this PDU. + * + * \sa PDU::matches_response + * \param ptr The pointer to the buffer. + * \param total_sz The size of the buffer. + */ + bool matches_response(uint8_t *ptr, uint32_t total_sz); + + /** \brief Receives a matching response for this packet. + * + * \sa PDU::recv_response + * \param sender The packet sender which will receive the packet. + */ + PDU *recv_response(PacketSender *sender); + + /** + * \brief Getter for the PDU's type. + * \sa PDU::pdu_type + */ + PDUType pdu_type() const { return PDU::IEEE802_3; } + + /** \brief Clones this pdu, filling the corresponding header with data + * extracted from a buffer. + * + * \param ptr The pointer to the from from which the data will be extracted. + * \param total_sz The size of the buffer. + * \return The cloned PDU. + * \sa PDU::clone_packet + */ + PDU *clone_packet(const uint8_t *ptr, uint32_t total_sz); + + /** + * \brief Clones this PDU. + * + * \sa PDU::clone_pdu + */ + PDU *clone_pdu() const; + private: + /** + * Struct that represents the Ethernet II header + */ + struct ethhdr { + uint8_t dst_mac[ADDR_SIZE]; + uint8_t src_mac[ADDR_SIZE]; + uint16_t length; + } __attribute__((__packed__)); + + void copy_fields(const IEEE802_3 *other); + void write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent); + + + ethhdr _eth; + uint32_t _iface_index; + }; + +}; + + +#endif diff --git a/include/llc.h b/include/llc.h index fa20d4c..0815378 100644 --- a/include/llc.h +++ b/include/llc.h @@ -58,32 +58,38 @@ namespace Tins { */ enum ModifierFunctions { UI = 0x00, - XID = 0xAC, - TEST = 0xE0, - SABME = 0x6C, - DISC = 0x40, - UA = 0x60, - DM = 0x0C, - FRMR = 0x84 + XID = 0x1D, + TEST = 0x07, + SABME = 0x1E, + DISC = 0x02, + UA = 0x06, + DM = 0x18, + FRMR = 0x11 }; /** * \brief LLC Supervisory functions */ enum SupervisoryFunctions { - RECEIVE_READY = 0x00, - REJECT = 0x08, - RECEIVE_NOT_READY = 0x04 + RECEIVE_READY = 0, + REJECT = 2, + RECEIVE_NOT_READY = 1 }; /** * \brief Creates an instance of LLC - * This constructor sets the dsap and ssap fields to 0xaa, and - * the id field to 3. * \param child The child PDU.(optional) */ LLC(PDU *child = 0); + /** + * \brief Creates an instance of LLC, setting the dsap and ssap. + * The control field is set all to 0. + * @param dsap The dsap value to be set. + * @param ssap The ssap value to be set. + */ + LLC(uint8_t dsap, uint8_t ssap, PDU* child = 0); + /** * \brief Constructor which creates a LLC object from a buffer and adds all identifiable * PDUs found in the buffer as children of this one. @@ -209,7 +215,7 @@ namespace Tins { * \brief Getter for the LLC frame format type. * \return The LLC frame format. */ - inline uint8_t type() {return control_field & 0x03; } + inline uint8_t type() {return _type; } /** * \brief Getter for sender send sequence number. @@ -217,7 +223,7 @@ namespace Tins { * \return The sender send sequence number if format is INFORMATION else 0. */ inline uint8_t send_seq_number() { - return (type() == INFORMATION) ? (control_field & 0x00FE) >> 1 : 0; + return (type() == INFORMATION) ? (control_field.info.send_seq_num) : 0; } /** @@ -227,10 +233,14 @@ namespace Tins { * INFORMATION or SUPERVISORY else 0. */ inline uint8_t receive_seq_number() { - if (type() == INFORMATION || type() == SUPERVISORY) - return (control_field & 0xFE00) >> 9; - else - return 0; + switch (type()) { + case INFORMATION: + return control_field.info.recv_seq_num; + case SUPERVISORY: + return control_field.super.recv_seq_num; + case UNNUMBERED: + return 0; + } } /** @@ -238,10 +248,14 @@ namespace Tins { * \return Whether the poll/final flag is set. */ inline bool poll_final() { - if (type() == UNNUMBERED) - return control_field & 0x10; - else - return control_field & 0x0100; + switch (type()) { + case UNNUMBERED: + return control_field.unnumbered.poll_final_bit; + case INFORMATION: + return control_field.info.poll_final_bit; + case SUPERVISORY: + return control_field.super.poll_final_bit; + } } /** @@ -251,7 +265,7 @@ namespace Tins { */ inline uint8_t supervisory_function() { if (type() == SUPERVISORY) - return control_field & 0x0C; + return control_field.super.supervisory_func; return 0; } @@ -262,7 +276,7 @@ namespace Tins { */ inline uint8_t modifier_function() { if (type() == UNNUMBERED) - return control_field & 0xEC; + return (control_field.unnumbered.mod_func1 << 3) + control_field.unnumbered.mod_func2; return 0; } @@ -297,12 +311,40 @@ namespace Tins { uint8_t ssap; } __attribute__((__packed__)); + struct info_control_field { + uint16_t + type_bit:1, + send_seq_num:7, + poll_final_bit:1, + recv_seq_num:7; + } __attribute__((__packed__)); + + struct super_control_field { + uint16_t type_bit:2, + supervisory_func:2, + unused:4, + poll_final_bit:1, + recv_seq_num:7; + } __attribute__((__packed__)); + + struct un_control_field { + uint8_t type_bits:2, + mod_func1:2, + poll_final_bit:1, + mod_func2:3; + } __attribute__((__packed__)); + void copy_fields(const LLC *other); void write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent); llchdr _header; uint8_t control_field_length; - uint16_t control_field; + union { + info_control_field info; + super_control_field super; + un_control_field unnumbered; + } control_field; + Format _type; uint8_t information_field_length; std::list > information_fields; }; diff --git a/include/pdu.h b/include/pdu.h index 51bf505..bdd03c8 100644 --- a/include/pdu.h +++ b/include/pdu.h @@ -52,6 +52,7 @@ namespace Tins { enum PDUType { RAW, ETHERNET_II, + IEEE802_3, RADIOTAP, DOT11, DOT11_ACK, diff --git a/src/ieee802-3.cpp b/src/ieee802-3.cpp new file mode 100644 index 0000000..3eb4baf --- /dev/null +++ b/src/ieee802-3.cpp @@ -0,0 +1,169 @@ +/* + * libtins is a net packet wrapper library for crafting and + * interpreting sniffed packets. + * + * Copyright (C) 2011 Nasel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#ifndef WIN32 + #include + #include +#endif +#include "ieee802-3.h" +#include "llc.h" +#include "utils.h" + +const uint8_t* Tins::IEEE802_3::BROADCAST = (const uint8_t*)"\xff\xff\xff\xff\xff\xff"; +const uint32_t Tins::IEEE802_3::ADDR_SIZE; + +Tins::IEEE802_3::IEEE802_3(const std::string& iface, const uint8_t* dst_hw_addr, const uint8_t* src_hw_addr, PDU* child) throw (std::runtime_error) : PDU(ETHERTYPE_IP, child) { + memset(&_eth, 0, sizeof(ethhdr)); + if(dst_hw_addr) + this->dst_addr(dst_hw_addr); + if(src_hw_addr) + this->src_addr(src_hw_addr); + this->iface(iface); + this->_eth.length = 0; + +} + +Tins::IEEE802_3::IEEE802_3(uint32_t iface_index, const uint8_t* dst_hw_addr, const uint8_t* src_hw_addr, PDU* child) : PDU(ETHERTYPE_IP, child) { + memset(&_eth, 0, sizeof(ethhdr)); + if(dst_hw_addr) + this->dst_addr(dst_hw_addr); + if(src_hw_addr) + this->src_addr(src_hw_addr); + this->iface(iface_index); + this->_eth.length = 0; +} + +Tins::IEEE802_3::IEEE802_3(const uint8_t *buffer, uint32_t total_sz) : PDU(ETHERTYPE_IP) { + if(total_sz < sizeof(ethhdr)) + throw std::runtime_error("Not enough size for an ethernetII header in the buffer."); + memcpy(&_eth, buffer, sizeof(ethhdr)); + PDU *next = 0; + buffer += sizeof(ethhdr); + total_sz -= sizeof(ethhdr); + if(total_sz) { + next = new Tins::LLC(buffer, total_sz); + inner_pdu(next); + } +} + +void Tins::IEEE802_3::dst_addr(const uint8_t* new_dst_mac) { + memcpy(_eth.dst_mac, new_dst_mac, sizeof(_eth.dst_mac)); +} + +void Tins::IEEE802_3::src_addr(const uint8_t* new_src_mac) { + memcpy(_eth.src_mac, new_src_mac, sizeof(_eth.src_mac)); +} + +void Tins::IEEE802_3::iface(uint32_t new_iface_index) { + _iface_index = new_iface_index; +} + +void Tins::IEEE802_3::iface(const std::string& new_iface) throw (std::runtime_error) { + if (!Tins::Utils::interface_id(new_iface, this->_iface_index)) { + throw std::runtime_error("Invalid interface name!"); + } +} + +void Tins::IEEE802_3::length(uint16_t new_length) { + this->_eth.length = Utils::net_to_host_s(new_length); +} + +uint32_t Tins::IEEE802_3::header_size() const { + return sizeof(ethhdr); +} + +bool Tins::IEEE802_3::send(PacketSender* sender) { + struct sockaddr_ll addr; + + memset(&addr, 0, sizeof(struct sockaddr_ll)); + + addr.sll_family = Utils::net_to_host_s(PF_PACKET); + addr.sll_protocol = Utils::net_to_host_s(ETH_P_ALL); + addr.sll_halen = ADDR_SIZE; + addr.sll_ifindex = _iface_index; + memcpy(&(addr.sll_addr), _eth.dst_mac, ADDR_SIZE); + + return sender->send_l2(this, (struct sockaddr*)&addr, (uint32_t)sizeof(addr)); +} + +bool Tins::IEEE802_3::matches_response(uint8_t *ptr, uint32_t total_sz) { + if(total_sz < sizeof(ethhdr)) + return false; + ethhdr *eth_ptr = (ethhdr*)ptr; + if(!memcmp(eth_ptr->dst_mac, _eth.src_mac, ADDR_SIZE)) { + return true; + } + return false; +} + +void Tins::IEEE802_3::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent) { + uint32_t my_sz = header_size(); + bool set_length = _eth.length == 0; + assert(total_sz >= my_sz); + + if (set_length) + _eth.length = Utils::net_to_host_s(size() - sizeof(_eth)); + + memcpy(buffer, &_eth, sizeof(ethhdr)); + + if (set_length) + _eth.length = 0; +} + +Tins::PDU *Tins::IEEE802_3::recv_response(PacketSender *sender) { + struct sockaddr_ll addr; + memset(&addr, 0, sizeof(struct sockaddr_ll)); + + addr.sll_family = Utils::net_to_host_s(PF_PACKET); + addr.sll_protocol = Utils::net_to_host_s(ETH_P_802_3); + addr.sll_halen = ADDR_SIZE; + addr.sll_ifindex = _iface_index; + memcpy(&(addr.sll_addr), _eth.dst_mac, ADDR_SIZE); + + return sender->recv_l2(this, (struct sockaddr*)&addr, (uint32_t)sizeof(addr)); +} + +Tins::PDU *Tins::IEEE802_3::clone_packet(const uint8_t *ptr, uint32_t total_sz) { + if(total_sz < sizeof(_eth)) + return 0; + PDU *child = 0, *cloned; + if(total_sz > sizeof(_eth)) { + if((child = PDU::clone_inner_pdu(ptr + sizeof(_eth), total_sz - sizeof(_eth))) == 0) + return 0; + } + cloned = new IEEE802_3(ptr, std::min(total_sz, (uint32_t)sizeof(_eth))); + cloned->inner_pdu(child); + return cloned; +} + +void Tins::IEEE802_3::copy_fields(const IEEE802_3 *other) { + memcpy(&_eth, &other->_eth, sizeof(_eth)); + _iface_index = other->_iface_index; +} + +Tins::PDU *Tins::IEEE802_3::clone_pdu() const { + IEEE802_3 *new_pdu = new IEEE802_3(_iface_index); + new_pdu->copy_fields(this); + return new_pdu; +} diff --git a/src/llc.cpp b/src/llc.cpp index 90d9cf3..98b55e4 100644 --- a/src/llc.cpp +++ b/src/llc.cpp @@ -38,10 +38,18 @@ using Tins::LLC; const uint8_t LLC::GLOBAL_DSAP_ADDR = 0xFF; const uint8_t LLC::NULL_ADDR = 0x00; -LLC::LLC(PDU *child) : PDU(0xff, child) { +LLC::LLC(PDU *child) : PDU(0xff, child), _type(LLC::INFORMATION) { memset(&_header, 0, sizeof(llchdr)); control_field_length = 2; - control_field = 0; + memset(&control_field, 0, sizeof(control_field)); + information_field_length = 0; +} + +LLC::LLC(uint8_t dsap, uint8_t ssap, PDU *child) : PDU(0xff, child), _type(LLC::INFORMATION) { + _header.dsap = dsap; + _header.ssap = ssap; + control_field_length = 2; + memset(&control_field, 0, sizeof(control_field)); information_field_length = 0; } @@ -52,15 +60,16 @@ LLC::LLC(const uint8_t *buffer, uint32_t total_sz) : PDU(0xff) { buffer += sizeof(_header); total_sz -= sizeof(_header); if ((buffer[0] & 0x03) == LLC::UNNUMBERED) { - control_field_length = 1; - control_field = buffer[0]; + type(LLC::UNNUMBERED); + std::memcpy(&control_field.unnumbered, buffer, sizeof(un_control_field)); buffer++; total_sz -= 1; //TODO: Create information fields if corresponding. } else { + type((Format)(buffer[0] & 0x03)); control_field_length = 2; - control_field = Utils::net_to_host_s(((uint16_t*)buffer)[0]); + std::memcpy(&control_field.info, buffer, sizeof(un_control_field)); buffer += 2; total_sz -= 2; } @@ -104,20 +113,19 @@ void LLC::ssap(uint8_t new_ssap) { } void LLC::type(LLC::Format type) { + _type = type; switch (type) { case LLC::INFORMATION: control_field_length = 2; - control_field &= 0xFFFE; + control_field.info.type_bit = 0; break; case LLC::SUPERVISORY: control_field_length = 2; - control_field &= 0xFFFD; - control_field |= type; + control_field.super.type_bit = 1; break; case LLC::UNNUMBERED: control_field_length = 1; - control_field &= 0xFD; - control_field |= type; + control_field.unnumbered.type_bits = 3; break; } } @@ -125,48 +133,48 @@ void LLC::type(LLC::Format type) { void LLC::send_seq_number(uint8_t seq_number) { if (type() != LLC::INFORMATION) return; - control_field &= 0xFF01; - control_field |= seq_number; + control_field.info.send_seq_num = seq_number; } void LLC::receive_seq_number(uint8_t seq_number) { - if (type() == LLC::UNNUMBERED) - return; - uint16_t to_mask = seq_number <<= 9; - control_field &= 0x01FF; - control_field |= to_mask; + switch (type()) { + case LLC::UNNUMBERED: + return; + case LLC::INFORMATION: + control_field.info.recv_seq_num = seq_number; + break; + case LLC::SUPERVISORY: + control_field.super.recv_seq_num = seq_number; + break; + } } void LLC::poll_final(bool value) { - uint16_t mask = 0; switch (type()) { case LLC::UNNUMBERED: - mask = 0xFFEF; + control_field.unnumbered.poll_final_bit = value; break; case LLC::INFORMATION: + control_field.info.poll_final_bit = value; + return; case LLC::SUPERVISORY: - mask = 0xFEFF; + control_field.super.poll_final_bit = value; break; } - if (value) - control_field |= ~mask; - else - control_field &= mask; } void LLC::supervisory_function(LLC::SupervisoryFunctions new_func) { if (type() != LLC::SUPERVISORY) return; - control_field &= 0xFFF3; - control_field |= new_func; + control_field.super.supervisory_func = new_func; } void LLC::modifier_function(LLC::ModifierFunctions mod_func) { if (type() != LLC::UNNUMBERED) return; - control_field &= 0xEC; - control_field |= mod_func; + control_field.unnumbered.mod_func1 = mod_func >> 3; + control_field.unnumbered.mod_func2 = mod_func & 0x07; } void LLC::add_xid_information(uint8_t xid_id, uint8_t llc_type_class, uint8_t receive_window) { @@ -214,14 +222,21 @@ void LLC::write_serialization(uint8_t *buffer, uint32_t total_sz, const Tins::PD assert(total_sz >= header_size()); std::memcpy(buffer, &_header, sizeof(_header)); buffer += sizeof(_header); - if (control_field_length == 1) { - *buffer = (uint8_t)control_field; - buffer++; - } - else { - *((uint16_t*)buffer) = Utils::net_to_host_s(control_field); - buffer += 2; + switch (type()) { + case LLC::UNNUMBERED: + std::memcpy(buffer, &(control_field.unnumbered), sizeof(un_control_field)); + buffer += sizeof(un_control_field); + break; + case LLC::INFORMATION: + std::memcpy(buffer, &(control_field.info), sizeof(info_control_field)); + buffer += sizeof(info_control_field); + break; + case LLC::SUPERVISORY: + std::memcpy(buffer, &(control_field.super), sizeof(super_control_field)); + buffer += sizeof(super_control_field); + break; } + for (list >::iterator it = information_fields.begin(); it != information_fields.end(); it++) { std::memcpy(buffer, it->second, it->first); buffer += it->first; diff --git a/tests/src/llc.cpp b/tests/src/llc.cpp new file mode 100644 index 0000000..189b52f --- /dev/null +++ b/tests/src/llc.cpp @@ -0,0 +1,202 @@ +#include +#include +#include +#include + +#include "llc.h" + +using namespace Tins; +using namespace std; + +class LLCTest : public testing::Test { +public: + static const uint8_t expected_packet[]; + static const uint8_t from_buffer_unnumbered[]; + static const uint8_t from_buffer_info[]; + static const uint8_t from_buffer_super[]; + + //void test_equals(const IP &ip1, const IP &ip2); +}; + +const uint8_t LLCTest::expected_packet[] = {}; +const uint8_t LLCTest::from_buffer_info[] = {'\xfe', '\x48', '\x3c', '\x3b'}; +const uint8_t LLCTest::from_buffer_super[] = {'\x4b', '\x19', '\x05', '\x3a'}; +const uint8_t LLCTest::from_buffer_unnumbered[] = {'\xaa', '\x17', '\xcf'}; + + +TEST_F(LLCTest, DefaultConstructor) { + LLC llc; + EXPECT_EQ(llc.ssap(), 0); + EXPECT_EQ(llc.dsap(), 0); + EXPECT_EQ(llc.type(), LLC::INFORMATION); + EXPECT_EQ(llc.header_size(), 4); + EXPECT_EQ(llc.pdu_type(), PDU::LLC); +} + +TEST_F(LLCTest, ParamsConstructor) { + LLC llc(0xAD, 0x16); + EXPECT_EQ(llc.dsap(), 0xAD); + EXPECT_EQ(llc.ssap(), 0x16); + EXPECT_EQ(llc.type(), LLC::INFORMATION); + EXPECT_EQ(llc.header_size(), 4); + EXPECT_EQ(llc.pdu_type(), PDU::LLC); +} + +TEST_F(LLCTest, Group) { + LLC llc; + llc.group(true); + EXPECT_TRUE(llc.group()); + llc.group(false); + EXPECT_FALSE(llc.group()); +} + +TEST_F(LLCTest, Dsap) { + LLC llc; + llc.dsap(0xaa); + EXPECT_EQ(llc.dsap(), 0xaa); + llc.dsap(0x01); + EXPECT_EQ(llc.dsap(), 0x01); +} + +TEST_F(LLCTest, Command) { + LLC llc; + llc.command(true); + EXPECT_TRUE(llc.command()); + llc.command(false); + EXPECT_FALSE(llc.command()); +} + +TEST_F(LLCTest, Ssap) { + LLC llc; + llc.ssap(0xaa); + EXPECT_EQ(llc.ssap(), 0xaa); + llc.ssap(0x01); + EXPECT_EQ(llc.ssap(), 0x01); +} + +TEST_F(LLCTest, Type) { + LLC llc; + llc.type(LLC::INFORMATION); + EXPECT_EQ(llc.type(), LLC::INFORMATION); + llc.type(LLC::SUPERVISORY); + EXPECT_EQ(llc.type(), LLC::SUPERVISORY); + llc.type(LLC::UNNUMBERED); + EXPECT_EQ(llc.type(), LLC::UNNUMBERED); +} + +TEST_F(LLCTest, HeadSize) { + LLC llc; + llc.type(LLC::INFORMATION); + EXPECT_EQ(llc.header_size(), 4); + llc.type(LLC::SUPERVISORY); + EXPECT_EQ(llc.header_size(), 4); + llc.type(LLC::UNNUMBERED); + EXPECT_EQ(llc.header_size(), 3); +} + +TEST_F(LLCTest, SendSeqNumber) { + LLC llc; + llc.type(LLC::INFORMATION); + llc.send_seq_number(18); + EXPECT_EQ(18, llc.send_seq_number()); + llc.send_seq_number(127); + EXPECT_EQ(127, llc.send_seq_number()); + llc.type(LLC::SUPERVISORY); + EXPECT_EQ(0, llc.send_seq_number()); + llc.type(LLC::UNNUMBERED); + EXPECT_EQ(0, llc.send_seq_number()); +} + +TEST_F(LLCTest, ReceiveSeqNumber) { + LLC llc; + llc.type(LLC::INFORMATION); + llc.receive_seq_number(18); + EXPECT_EQ(18, llc.receive_seq_number()); + llc.send_seq_number(127); + EXPECT_EQ(127, llc.receive_seq_number()); + llc.type(LLC::SUPERVISORY); + llc.receive_seq_number(19); + EXPECT_EQ(19, llc.receive_seq_number()); + llc.send_seq_number(127); + EXPECT_EQ(127, llc.receive_seq_number()); + llc.type(LLC::UNNUMBERED); + EXPECT_EQ(0, llc.receive_seq_number()); + +} + +TEST_F(LLCTest, PollFinal) { + LLC llc; + llc.type(LLC::INFORMATION); + llc.poll_final(true); + EXPECT_TRUE(llc.poll_final()); + llc.poll_final(false); + EXPECT_FALSE(llc.poll_final()); + llc.type(LLC::SUPERVISORY); + llc.poll_final(true); + EXPECT_TRUE(llc.poll_final()); + llc.poll_final(false); + EXPECT_FALSE(llc.poll_final()); + llc.type(LLC::UNNUMBERED); + llc.poll_final(true); + EXPECT_TRUE(llc.poll_final()); + llc.poll_final(false); + EXPECT_FALSE(llc.poll_final()); +} + +TEST_F(LLCTest, SupervisoryFunction) { + LLC llc; + llc.type(LLC::INFORMATION); + EXPECT_EQ(0, llc.supervisory_function()); + llc.type(LLC::SUPERVISORY); + llc.supervisory_function(LLC::RECEIVE_NOT_READY); + EXPECT_EQ(LLC::RECEIVE_NOT_READY, llc.supervisory_function()); + llc.supervisory_function(LLC::RECEIVE_READY); + EXPECT_EQ(LLC::RECEIVE_READY, llc.supervisory_function()); + llc.type(LLC::UNNUMBERED); + EXPECT_EQ(0, llc.supervisory_function()); +} + +TEST_F(LLCTest, ModifierFunction) { + LLC llc; + llc.type(LLC::INFORMATION); + EXPECT_EQ(0, llc.modifier_function()); + llc.type(LLC::SUPERVISORY); + EXPECT_EQ(0, llc.modifier_function()); + llc.type(LLC::UNNUMBERED); + llc.modifier_function(LLC::TEST); + EXPECT_EQ(LLC::TEST, llc.modifier_function()); + llc.modifier_function(LLC::XID); + EXPECT_EQ(LLC::XID, llc.modifier_function()); +} + +TEST_F(LLCTest, ConstructorFromBuffer) { + LLC llc(LLCTest::from_buffer_info, 4); + EXPECT_EQ(llc.type(), LLC::INFORMATION); + EXPECT_EQ(llc.header_size(), 4); + EXPECT_EQ(llc.dsap(), 0xFE); + EXPECT_EQ(llc.ssap(), 0x48); + EXPECT_FALSE(llc.group()); + EXPECT_TRUE(llc.command()); + EXPECT_TRUE(llc.poll_final()); + EXPECT_EQ(llc.send_seq_number(), 30); + EXPECT_EQ(llc.receive_seq_number(), 29); + + LLC llc_super(LLCTest::from_buffer_super, 4); + EXPECT_EQ(llc_super.header_size(), 4); + EXPECT_EQ(llc_super.dsap(), 0x4B); + EXPECT_EQ(llc_super.ssap(), 0x19); + EXPECT_TRUE(llc_super.group()); + EXPECT_FALSE(llc_super.command()); + EXPECT_FALSE(llc_super.poll_final()); + EXPECT_EQ(llc_super.receive_seq_number(), 29); + EXPECT_EQ(llc_super.supervisory_function(), LLC::RECEIVE_NOT_READY); + + LLC llc_unnum(LLCTest::from_buffer_unnumbered, 3); + EXPECT_EQ(llc_unnum.header_size(), 3); + EXPECT_EQ(llc_unnum.dsap(), 0xaa); + EXPECT_EQ(llc_unnum.ssap(), 0x17); + EXPECT_FALSE(llc_unnum.group()); + EXPECT_FALSE(llc_unnum.command()); + EXPECT_FALSE(llc_unnum.poll_final()); + EXPECT_EQ(llc_unnum.modifier_function(), LLC::SABME); +}