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

Added PPPoE.

This commit is contained in:
Matias Fontanini
2013-04-07 20:57:43 -03:00
parent f2a5f73337
commit f7f5a9bc9a
13 changed files with 973 additions and 50 deletions

View File

@@ -13,6 +13,7 @@ AM_CXXFLAGS = -Wall -pedantic -I@LIBTINS_INCLUDE_DIR@
libtins_la_SOURCES=src/arp.cpp \
src/bootp.cpp \
src/pppoe.cpp \
src/crypto.cpp \
src/dhcp.cpp \
src/dhcpv6.cpp \
@@ -57,6 +58,7 @@ libtins_include_HEADERS = include/internals.h \
include/dns_record.h \
include/eapol.h \
include/tcp_stream.h \
include/pppoe.h \
include/ipv6.h \
include/icmpv6.h \
include/endianness.h \

View File

@@ -100,16 +100,16 @@ am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" \
LTLIBRARIES = $(lib_LTLIBRARIES)
libtins_la_LIBADD =
am__dirstamp = $(am__leading_dot)dirstamp
am_libtins_la_OBJECTS = src/arp.lo src/bootp.lo src/crypto.lo \
src/dhcp.lo src/dhcpv6.lo src/dns.lo src/dns_record.lo \
src/dot11.lo src/dot3.lo src/dot1q.lo src/eapol.lo \
src/ethernetII.lo src/icmp.lo src/icmpv6.lo src/internals.lo \
src/ip.lo src/ip_address.lo src/ipv6.lo src/ipv6_address.lo \
src/llc.lo src/loopback.lo src/network_interface.lo \
src/packet_sender.lo src/packet_writer.lo src/pdu.lo \
src/radiotap.lo src/rawpdu.lo src/rsn_information.lo \
src/sll.lo src/snap.lo src/sniffer.lo src/tcp.lo \
src/tcp_stream.lo src/udp.lo src/utils.lo
am_libtins_la_OBJECTS = src/arp.lo src/bootp.lo src/pppoe.lo \
src/crypto.lo src/dhcp.lo src/dhcpv6.lo src/dns.lo \
src/dns_record.lo src/dot11.lo src/dot3.lo src/dot1q.lo \
src/eapol.lo src/ethernetII.lo src/icmp.lo src/icmpv6.lo \
src/internals.lo src/ip.lo src/ip_address.lo src/ipv6.lo \
src/ipv6_address.lo src/llc.lo src/loopback.lo \
src/network_interface.lo src/packet_sender.lo \
src/packet_writer.lo src/pdu.lo src/radiotap.lo src/rawpdu.lo \
src/rsn_information.lo src/sll.lo src/snap.lo src/sniffer.lo \
src/tcp.lo src/tcp_stream.lo src/udp.lo src/utils.lo
libtins_la_OBJECTS = $(am_libtins_la_OBJECTS)
libtins_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
@@ -285,6 +285,7 @@ libtins_la_LDFLAGS = -version-info @LIBTINS_VERSION@
AM_CXXFLAGS = -Wall -pedantic -I@LIBTINS_INCLUDE_DIR@
libtins_la_SOURCES = src/arp.cpp \
src/bootp.cpp \
src/pppoe.cpp \
src/crypto.cpp \
src/dhcp.cpp \
src/dhcpv6.cpp \
@@ -329,6 +330,7 @@ libtins_include_HEADERS = include/internals.h \
include/dns_record.h \
include/eapol.h \
include/tcp_stream.h \
include/pppoe.h \
include/ipv6.h \
include/icmpv6.h \
include/endianness.h \
@@ -446,6 +448,7 @@ src/$(DEPDIR)/$(am__dirstamp):
@: > src/$(DEPDIR)/$(am__dirstamp)
src/arp.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
src/bootp.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
src/pppoe.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
src/crypto.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
src/dhcp.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
src/dhcpv6.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
@@ -538,6 +541,8 @@ mostlyclean-compile:
-rm -f src/packet_writer.lo
-rm -f src/pdu.$(OBJEXT)
-rm -f src/pdu.lo
-rm -f src/pppoe.$(OBJEXT)
-rm -f src/pppoe.lo
-rm -f src/radiotap.$(OBJEXT)
-rm -f src/radiotap.lo
-rm -f src/rawpdu.$(OBJEXT)
@@ -587,6 +592,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/packet_sender.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/packet_writer.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/pdu.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/pppoe.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/radiotap.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/rawpdu.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/rsn_information.Plo@am__quote@

View File

@@ -84,6 +84,7 @@ namespace Tins {
VLAN = 0x8100, /* IEEE 802.1Q VLAN tagging */
IPX = 0x8137, /* IPX */
IPV6 = 0x86dd, /* IP protocol version 6 */
PPPOED = 0x8863, /* PPPoE Discovery */
EAPOL = 0x888e, /* EAPOL */
LOOPBACK = 0x9000 /* used to test interfaces */
};

View File

@@ -41,6 +41,7 @@ public:
* This PDU's flag.
*/
static const PDU::PDUType pdu_flag = PDU::DOT1Q;
/**
* Default constructor
*/
@@ -177,6 +178,7 @@ public:
private:
void write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent);
TINS_BEGIN_PACK
struct dot1q_hdr {
#if TINS_IS_BIG_ENDIAN
uint16_t priority:3,
@@ -190,7 +192,7 @@ private:
idL:8;
uint16_t type;
#endif
};
} TINS_END_PACK;
static uint16_t get_id(const dot1q_hdr *hdr);

View File

@@ -40,6 +40,11 @@
namespace Tins {
class PacketSender;
/**
* The type used to store several PDU option values.
*/
typedef std::vector<uint8_t> byte_array;
/** \brief Base class for protocol data units.
*
@@ -55,7 +60,7 @@ namespace Tins {
/**
* The type that will be returned when serializing PDUs.
*/
typedef std::vector<uint8_t> serialization_type;
typedef byte_array serialization_type;
/**
* \brief Enum which identifies each type of PDU.
@@ -107,7 +112,8 @@ namespace Tins {
ICMPv6,
SLL,
DHCPv6,
DOT1Q
DOT1Q,
PPPOE
};
/** \brief PDU constructor

View File

@@ -93,6 +93,25 @@ public:
}
/**
* \brief Constructs a PDUOption from iterators, which
* indicate the data to be stored in it.
*
* The length parameter indicates the contents of the length field
* when this option is serialized. Note that this can be different
* to std::distance(start, end).
*
* \param opt The option type.
* \param length The length of this option.
* \param start The beginning of the option data.
* \param end The end of the option data.
*/
template<typename ForwardIterator>
PDUOption(option_type opt, size_t length, ForwardIterator start, ForwardIterator end)
: option_(opt), size_(length), value_(start, end) {
}
/**
* Retrieves this option's type.
* \return uint8_t containing this option's size.
@@ -123,9 +142,22 @@ public:
}
/**
* Retrieves the length of this option's data.
* \brief Retrieves the length of this option's data.
*
* This is the actual size of the data.
*/
size_t data_size() const {
return value_.size();
}
/**
* \brief Retrieves the data length field.
*
* This may be different to the actual size of the data.
*
* \sa data_size.
*/
size_t length_field() const {
return size_;
}
private:

430
include/pppoe.h Normal file
View File

@@ -0,0 +1,430 @@
/*
* Copyright (c) 2012, Nasel
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef TINS_PPPoE_H
#define TINS_PPPoE_H
#include <list>
#include <string>
#include <vector>
#include "pdu.h"
#include "endianness.h"
#include "small_uint.h"
#include "pdu_option.h"
#include "cxxstd.h"
namespace Tins {
class PPPoE : public PDU {
public:
/**
* The tag types enum.
*/
enum tag_identifiers {
END_OF_LIST = 0,
SERVICE_NAME = 0x101,
#if TINS_IS_LITTLE_ENDIAN
AC_NAME = 0x201,
HOST_UNIQ = 0x301,
AC_COOKIE = 0x401,
VENDOR_SPECIFIC = 0x501,
RELAY_SESSION_ID = 0x101,
SERVICE_NAME_ERROR = 0x201,
AC_SYSTEM_ERROR = 0x202,
GENERIC_ERROR = 0x302
#else
AC_NAME = 0x102,
HOST_UNIQ = 0x103,
AC_COOKIE = 0x104,
VENDOR_SPECIFIC = 0x105,
RELAY_SESSION_ID = 0x110,
SERVICE_NAME_ERROR = 0x201,
AC_SYSTEM_ERROR = 0x202,
GENERIC_ERROR = 0x203
#endif
};
/**
* The type used to store a TLV option.
*/
typedef PDUOption<tag_identifiers> pppoe_tag;
/**
* The type used to store the options.
*/
typedef std::list<pppoe_tag> tags_type;
/**
* The type used to store the Vendor-Specific tag's value.
*/
struct vendor_spec_type {
typedef std::vector<uint8_t> data_type;
uint32_t vendor_id;
data_type data;
vendor_spec_type(uint32_t vendor_id = 0, const data_type &data = data_type())
: vendor_id(vendor_id), data(data) { }
};
/**
* This PDU's flag.
*/
static const PDU::PDUType pdu_flag = PDU::PPPOE;
/**
* \brief Default constructor.
*
* This sets the version and type fields to 0x1.
*/
PPPoE();
/**
* \brief Constructor which creates an PPPoE object from a buffer.
*
* \param buffer The buffer from which this PDU will be constructed.
* \param total_sz The total size of the buffer.
*/
PPPoE(const uint8_t *buffer, uint32_t total_sz);
// Getters
/**
* \brief Getter for the version field.
* \return The stored version field value.
*/
small_uint<4> version() const {
return _header.version;
}
/**
* \brief Getter for the type field.
* \return The stored type field value.
*/
small_uint<4> type() const {
return _header.type;
}
/**
* \brief Getter for the code field.
* \return The stored code field value.
*/
uint8_t code() const {
return _header.code;
}
/**
* \brief Getter for the session_id field.
* \return The stored session_id field value.
*/
uint16_t session_id() const {
return Endian::be_to_host(_header.session_id);
}
/**
* \brief Getter for the payload_length field.
* \return The stored payload_length field value.
*/
uint16_t payload_length() const {
return Endian::be_to_host(_header.payload_length);
}
/**
* \brief Returns the header size.
*
* This metod overrides PDU::header_size. \sa PDU::header_size
*/
uint32_t header_size() const;
/**
* \brief Returns the list of tags.
*/
const tags_type &tags() const {
return _tags;
}
/**
* \sa PDU::clone
*/
PPPoE *clone() const {
return new PPPoE(*this);
}
const pppoe_tag *search_tag(tag_identifiers identifier) const;
/**
* \brief Getter for the PDU's type.
* \sa PDU::pdu_type
*/
PDUType pdu_type() const { return pdu_flag; }
// Setters
/**
* \brief Setter for the version field.
* \param new_version The new version field value.
*/
void version(small_uint<4> new_version);
/**
* \brief Setter for the type field.
* \param new_type The new type field value.
*/
void type(small_uint<4> new_type);
/**
* \brief Setter for the code field.
* \param new_code The new code field value.
*/
void code(uint8_t new_code);
/**
* \brief Setter for the session_id field.
* \param new_session_id The new session_id field value.
*/
void session_id(uint16_t new_session_id);
/**
* \brief Setter for the payload_length field.
* \param new_payload_length The new payload_length field value.
*/
void payload_length(uint16_t new_payload_length);
/**
* \brief Adds a PPPoE tag.
*
* \param option The option to be added.
*/
void add_tag(const pppoe_tag &option);
#if TINS_IS_CXX11
/**
* \brief Adds a PPPoE tag.
*
* This move-constructs the option.
*
* \param option The option to be added.
*/
void add_tag(pppoe_tag &&option);
#endif
// Option setters
/**
* \brief Adds an end-of-list tag.
*/
void end_of_list();
/**
* \brief Adds a service-name tag.
*
* \param value The service name.
*/
void service_name(const std::string &value);
/**
* \brief Adds a AC-name tag.
*
* \param value The AC name.
*/
void ac_name(const std::string &value);
/**
* \brief Adds a host-uniq tag.
*
* \param value The tag's value.
*/
void host_uniq(const byte_array &value);
/**
* \brief Adds a AC-Cookie tag.
*
* \param value The tag's value.
*/
void ac_cookie(const byte_array &value);
/**
* \brief Adds a Vendor-Specific tag.
*
* \param value The tag's value.
*/
void vendor_specific(const vendor_spec_type &value);
/**
* \brief Adds a Relay-Session-Id tag.
*
* \param value The tag's value.
*/
void relay_session_id(const byte_array &value);
/**
* \brief Adds a Service-Name-Error tag.
*
* \param value The tag's value.
*/
void service_name_error(const std::string &value);
/**
* \brief Adds a AC-System-Error tag.
*
* \param value The tag's value.
*/
void ac_system_error(const std::string &value);
/**
* \brief Adds a Generic-Error tag.
*
* \param value The tag's value.
*/
void generic_error(const std::string &value);
// Option getters
/**
* \brief Getter for the service-name tag.
*
* This method will throw an option_not_found exception if the
* option is not found.
*/
std::string service_name() const;
/**
* \brief Getter for the AC-name tag.
*
* This method will throw an option_not_found exception if the
* option is not found.
*/
std::string ac_name() const;
/**
* \brief Getter for the host-uniq tag.
*
* This method will throw an option_not_found exception if the
* option is not found.
*/
byte_array host_uniq() const;
/**
* \brief Getter for the AC-Cookie tag.
*
* This method will throw an option_not_found exception if the
* option is not found.
*/
byte_array ac_cookie() const;
/**
* \brief Getter for the Vendor-Specific tag.
*
* This method will throw an option_not_found exception if the
* option is not found.
*/
vendor_spec_type vendor_specific() const;
/**
* \brief Getter for the Vendor-Specific tag.
*
* This method will throw an option_not_found exception if the
* option is not found.
*/
byte_array relay_session_id() const;
/**
* \brief Getter for the Service-Name-Error tag.
*
* This method will throw an option_not_found exception if the
* option is not found.
*/
std::string service_name_error() const;
/**
* \brief Getter for the AC-System-Error tag.
*
* This method will throw an option_not_found exception if the
* option is not found.
*/
std::string ac_system_error() const;
/**
* \brief Getter for the Generic-Error tag.
*
* This method will throw an option_not_found exception if the
* option is not found.
*/
std::string generic_error() const;
private:
void write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *);
template<typename T>
void add_tag_iterable(tag_identifiers id, const T &data) {
add_tag(
pppoe_tag(
id,
data.begin(),
data.end()
)
);
}
template<typename T>
T retrieve_tag_iterable(tag_identifiers id) const {
const pppoe_tag *tag = search_tag(id);
if(!tag)
throw option_not_found();
return T(tag->data_ptr(), tag->data_ptr() + tag->data_size());
}
template<template <typename> class Functor>
const pppoe_tag *safe_search_tag(tag_identifiers opt, uint32_t size) const {
const pppoe_tag *option = search_tag(opt);
if(!option || Functor<uint32_t>()(option->data_size(), size))
throw option_not_found();
return option;
}
TINS_BEGIN_PACK
struct pppoe_hdr {
#if TINS_IS_LITTLE_ENDIAN
uint8_t version:4,
type:4;
uint8_t code;
#else
uint16_t version:4,
type:4,
code:8;
#endif
uint16_t session_id;
uint16_t payload_length;
} TINS_END_PACK;
pppoe_hdr _header;
tags_type _tags;
uint16_t _tags_size;
};
}
#endif // TINS_PPPoE_H

View File

@@ -65,5 +65,6 @@
#include "timestamp.h"
#include "sll.h"
#include "dhcpv6.h"
#include "pppoe.h"
#endif // TINS_TINS_H

View File

@@ -38,6 +38,7 @@
#include "eapol.h"
#include "rawpdu.h"
#include "dot1q.h"
#include "pppoe.h"
using std::string;
@@ -76,6 +77,8 @@ Tins::PDU *pdu_from_flag(Constants::Ethernet::e flag, const uint8_t *buffer,
return new Tins::IPv6(buffer, size);
case Tins::Constants::Ethernet::ARP:
return new Tins::ARP(buffer, size);
case Tins::Constants::Ethernet::PPPOED:
return new Tins::PPPoE(buffer, size);
case Tins::Constants::Ethernet::EAPOL:
return Tins::EAPOL::from_bytes(buffer, size);
case Tins::Constants::Ethernet::VLAN:
@@ -100,6 +103,8 @@ Tins::PDU *pdu_from_flag(PDU::PDUType type, const uint8_t *buffer, uint32_t size
return new Tins::IEEE802_3(buffer, size);
case Tins::PDU::RADIOTAP:
return new Tins::RadioTap(buffer, size);
case Tins::PDU::PPPOE:
return new Tins::PPPoE(buffer, size);
case Tins::PDU::DOT11:
case Tins::PDU::DOT11_ACK:
case Tins::PDU::DOT11_ASSOC_REQ:
@@ -138,6 +143,8 @@ Constants::Ethernet::e pdu_flag_to_ether_type(PDU::PDUType flag) {
return Constants::Ethernet::ARP;
case PDU::DOT1Q:
return Constants::Ethernet::VLAN;
case PDU::PPPOE:
return Constants::Ethernet::PPPOED;
default:
return Constants::Ethernet::UNKNOWN;
}

View File

@@ -88,45 +88,26 @@ IP::IP(const uint8_t *buffer, uint32_t total_sz)
option_identifier opt_type;
memcpy(&opt_type, ptr_buffer, sizeof(uint8_t));
ptr_buffer++;
switch (opt_type.number) {
if(opt_type.number > NOOP) {
/* Multibyte options with length as second byte */
case SEC:
case LSSR:
case TIMESTAMP:
case EXTSEC:
case RR:
case SID:
case SSRR:
case MTUPROBE:
case MTUREPLY:
case EIP:
case TR:
case ADDEXT:
case RTRALT:
case SDB:
case DPS:
case UMP:
case QS:
if(ptr_buffer == buffer || *ptr_buffer == 0)
throw std::runtime_error(msg);
{
const uint8_t data_size = *ptr_buffer - 2;
if(data_size > 0) {
ptr_buffer++;
if(buffer - ptr_buffer < data_size)
throw std::runtime_error(msg);
_ip_options.push_back(ip_option(opt_type, ptr_buffer, ptr_buffer + data_size));
}
else
_ip_options.push_back(ip_option(opt_type));
}
if(ptr_buffer == buffer || *ptr_buffer == 0)
throw std::runtime_error(msg);
ptr_buffer += _ip_options.back().data_size() + 1;
break;
default:
const uint8_t data_size = *ptr_buffer - 2;
if(data_size > 0) {
ptr_buffer++;
if(buffer - ptr_buffer < data_size)
throw std::runtime_error(msg);
_ip_options.push_back(ip_option(opt_type, ptr_buffer, ptr_buffer + data_size));
}
else
_ip_options.push_back(ip_option(opt_type));
break;
ptr_buffer += _ip_options.back().data_size() + 1;
}
else {
_ip_options.push_back(ip_option(opt_type));
break;
}
_options_size += _ip_options.back().data_size() + 2;
}

245
src/pppoe.cpp Normal file
View File

@@ -0,0 +1,245 @@
/*
* Copyright (c) 2012, Nasel
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifdef TINS_DEBUG
#include <cassert>
#endif
#include <cstring>
#include "pppoe.h"
namespace Tins {
PPPoE::PPPoE()
: _header(), _tags_size()
{
version(1);
type(1);
}
PPPoE::PPPoE(const uint8_t *buffer, uint32_t total_sz)
: _tags_size()
{
const char *err_msg = "Not enough size for a PPPoE";
if(total_sz < sizeof(_header))
throw std::runtime_error(err_msg);
std::memcpy(&_header, buffer, sizeof(_header));
buffer += sizeof(_header);
total_sz -= sizeof(_header);
const uint8_t *end = buffer + total_sz;
while(buffer < end) {
if(buffer + sizeof(uint32_t) * 2 > end)
throw std::runtime_error(err_msg);
uint16_t opt_type = *(const uint16_t*)buffer;
uint16_t opt_len = *(const uint16_t*)(buffer + sizeof(uint16_t));
buffer += sizeof(uint16_t) * 2;
total_sz -= sizeof(uint16_t) * 2;
if(Endian::be_to_host(opt_len) > total_sz)
throw std::runtime_error(err_msg);
add_tag(
pppoe_tag(
static_cast<tag_identifiers>(opt_type),
Endian::be_to_host(opt_len),
buffer
)
);
buffer += Endian::be_to_host(opt_len);
total_sz -= Endian::be_to_host(opt_len);
}
}
const PPPoE::pppoe_tag *PPPoE::search_tag(tag_identifiers identifier) const {
for(tags_type::const_iterator it = _tags.begin(); it != _tags.end(); ++it) {
if(it->option() == identifier)
return &*it;
}
return 0;
}
void PPPoE::version(small_uint<4> new_version) {
_header.version = new_version;
}
void PPPoE::type(small_uint<4> new_type) {
_header.type = new_type;
}
void PPPoE::code(uint8_t new_code) {
_header.code = new_code;
}
void PPPoE::session_id(uint16_t new_session_id) {
_header.session_id = Endian::host_to_be(new_session_id);
}
void PPPoE::payload_length(uint16_t new_payload_length) {
_header.payload_length = Endian::host_to_be(new_payload_length);
}
uint32_t PPPoE::header_size() const {
return sizeof(_header) + _tags_size;
}
void PPPoE::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *)
{
#ifdef TINS_DEBUG
assert(total_sz == sizeof(_header) + _tags_size);
#endif
std::memcpy(buffer, &_header, sizeof(_header));
if(_tags_size > 0)
((pppoe_hdr*)buffer)->payload_length = Endian::host_to_be(_tags_size);
buffer += sizeof(_header);
for(tags_type::const_iterator it = _tags.begin(); it != _tags.end(); ++it) {
*(uint16_t*)buffer = it->option();
*(uint16_t*)(buffer + sizeof(uint16_t)) = Endian::host_to_be<uint16_t>(it->length_field());
std::copy(
it->data_ptr(),
it->data_ptr() + it->data_size(),
buffer + sizeof(uint16_t) * 2
);
buffer += sizeof(uint16_t) * 2 + it->data_size();
}
}
void PPPoE::add_tag(const pppoe_tag &option) {
_tags_size += option.data_size() + sizeof(uint16_t) * 2;
_tags.push_back(option);
}
#if TINS_IS_CXX11
void PPPoE::add_tag(pppoe_tag &&option) {
_tags_size += option.data_size() + sizeof(uint16_t) * 2;
_tags.push_back(std::move(option));
}
#endif
// *********************** Setters *************************
void PPPoE::end_of_list() {
add_tag(
END_OF_LIST
);
}
void PPPoE::service_name(const std::string &value) {
add_tag_iterable(SERVICE_NAME, value);
}
void PPPoE::ac_name(const std::string &value) {
add_tag_iterable(AC_NAME, value);
}
void PPPoE::host_uniq(const byte_array &value) {
add_tag_iterable(HOST_UNIQ, value);
}
void PPPoE::ac_cookie(const byte_array &value) {
add_tag_iterable(AC_COOKIE, value);
}
void PPPoE::vendor_specific(const vendor_spec_type &value) {
std::vector<uint8_t> buffer(sizeof(uint32_t) + value.data.size());
*(uint32_t*)&buffer[0] = Endian::host_to_be(value.vendor_id);
std::copy(
value.data.begin(),
value.data.end(),
buffer.begin() + sizeof(uint32_t)
);
add_tag(
pppoe_tag(
VENDOR_SPECIFIC,
buffer.begin(),
buffer.end()
)
);
}
void PPPoE::relay_session_id(const byte_array &value) {
add_tag_iterable(RELAY_SESSION_ID, value);
}
void PPPoE::service_name_error(const std::string &value) {
add_tag_iterable(SERVICE_NAME_ERROR, value);
}
void PPPoE::ac_system_error(const std::string &value) {
add_tag_iterable(AC_SYSTEM_ERROR, value);
}
void PPPoE::generic_error(const std::string &value) {
add_tag_iterable(GENERIC_ERROR, value);
}
// *********************** Getters *************************
std::string PPPoE::service_name() const {
return retrieve_tag_iterable<std::string>(SERVICE_NAME);
}
std::string PPPoE::ac_name() const {
return retrieve_tag_iterable<std::string>(AC_NAME);
}
byte_array PPPoE::host_uniq() const {
return retrieve_tag_iterable<byte_array>(HOST_UNIQ);
}
byte_array PPPoE::ac_cookie() const {
return retrieve_tag_iterable<byte_array>(AC_COOKIE);
}
PPPoE::vendor_spec_type PPPoE::vendor_specific() const {
const pppoe_tag *tag = safe_search_tag<std::less>(
VENDOR_SPECIFIC, sizeof(uint32_t)
);
vendor_spec_type output;
output.vendor_id = Endian::be_to_host(*(const uint32_t*)tag->data_ptr());
output.data.assign(
tag->data_ptr() + sizeof(uint32_t),
tag->data_ptr() + tag->data_size()
);
return output;
}
byte_array PPPoE::relay_session_id() const {
return retrieve_tag_iterable<byte_array>(RELAY_SESSION_ID);
}
std::string PPPoE::service_name_error() const {
return retrieve_tag_iterable<std::string>(SERVICE_NAME_ERROR);
}
std::string PPPoE::ac_system_error() const {
return retrieve_tag_iterable<std::string>(AC_SYSTEM_ERROR);
}
std::string PPPoE::generic_error() const {
return retrieve_tag_iterable<std::string>(GENERIC_ERROR);
}
} //namespace Tins

View File

@@ -721,6 +721,23 @@
../include/hw_address.h:
../include/ip_address.h:
../src/pppoe.o: ../src/pppoe.cpp ../include/pppoe.h ../include/pdu.h \
../include/macros.h ../include/endianness.h ../include/small_uint.h \
../include/pdu_option.h ../include/cxxstd.h
../include/pppoe.h:
../include/pdu.h:
../include/macros.h:
../include/endianness.h:
../include/small_uint.h:
../include/pdu_option.h:
../include/cxxstd.h:
../src/radiotap.o: ../src/radiotap.cpp ../include/macros.h \
../include/radiotap.h ../include/macros.h ../include/pdu.h \
../include/endianness.h ../include/network_interface.h \
@@ -2074,6 +2091,23 @@ src/pdu.o: src/pdu.cpp ../include/ip.h ../include/pdu.h \
../include/packet.h:
../include/timestamp.h:
src/pppoe.o: src/pppoe.cpp ../include/pppoe.h ../include/pdu.h \
../include/macros.h ../include/endianness.h ../include/small_uint.h \
../include/pdu_option.h ../include/cxxstd.h
../include/pppoe.h:
../include/pdu.h:
../include/macros.h:
../include/endianness.h:
../include/small_uint.h:
../include/pdu_option.h:
../include/cxxstd.h:
src/radiotap.o: src/radiotap.cpp ../include/radiotap.h \
../include/macros.h ../include/pdu.h ../include/endianness.h \
../include/network_interface.h ../include/hw_address.h \

176
tests/src/pppoe.cpp Normal file
View File

@@ -0,0 +1,176 @@
#include <gtest/gtest.h>
#include <cstring>
#include <string>
#include <stdint.h>
#include "pppoe.h"
#include "ethernetII.h"
using namespace std;
using namespace Tins;
class PPPoETest : public testing::Test {
public:
static const uint8_t expected_packet[];
};
const uint8_t PPPoETest::expected_packet[] = {
17, 9, 0, 0, 0, 16, 1, 1, 0, 0, 1, 2, 0, 0, 1, 3, 0, 4, 97, 98, 99, 100
};
TEST_F(PPPoETest, DefaultConstructor) {
PPPoE pdu;
EXPECT_EQ(1, pdu.version());
EXPECT_EQ(1, pdu.type());
EXPECT_EQ(0, pdu.code());
EXPECT_EQ(0, pdu.session_id());
EXPECT_EQ(0, pdu.payload_length());
}
TEST_F(PPPoETest, ConstructorFromBuffer) {
PPPoE pdu(expected_packet, sizeof(expected_packet));
EXPECT_EQ(1, pdu.version());
EXPECT_EQ(1, pdu.type());
EXPECT_EQ(0x09, pdu.code());
EXPECT_EQ(0, pdu.session_id());
EXPECT_EQ(16, pdu.payload_length());
EXPECT_EQ(3, pdu.tags().size());
EXPECT_EQ("", pdu.service_name());
ASSERT_TRUE(pdu.search_tag(PPPoE::SERVICE_NAME));
}
TEST_F(PPPoETest, StackedOnEthernet) {
EthernetII eth = EthernetII() / PPPoE();
PDU::serialization_type buffer = eth.serialize();
EthernetII eth2(&buffer[0], buffer.size());
ASSERT_TRUE(eth2.find_pdu<PPPoE>());
}
TEST_F(PPPoETest, Serialize) {
PPPoE pdu(expected_packet, sizeof(expected_packet));
PPPoE::serialization_type buffer = pdu.serialize();
EXPECT_EQ(
PPPoE::serialization_type(expected_packet, expected_packet + sizeof(expected_packet)),
buffer
);
}
TEST_F(PPPoETest, Version) {
PPPoE pdu;
pdu.version(6);
EXPECT_EQ(6, pdu.version());
}
TEST_F(PPPoETest, Type) {
PPPoE pdu;
pdu.type(6);
EXPECT_EQ(6, pdu.type());
}
TEST_F(PPPoETest, Code) {
PPPoE pdu;
pdu.code(0x7a);
EXPECT_EQ(0x7a, pdu.code());
}
TEST_F(PPPoETest, SessionID) {
PPPoE pdu;
pdu.session_id(0x9182);
EXPECT_EQ(0x9182, pdu.session_id());
}
TEST_F(PPPoETest, PayloadLength) {
PPPoE pdu;
pdu.payload_length(0x9182);
EXPECT_EQ(0x9182, pdu.payload_length());
}
TEST_F(PPPoETest, ServiceName) {
PPPoE pdu;
pdu.service_name("carlos");
EXPECT_EQ("carlos", pdu.service_name());
}
TEST_F(PPPoETest, ACName) {
PPPoE pdu;
pdu.ac_name("carlos");
EXPECT_EQ("carlos", pdu.ac_name());
}
TEST_F(PPPoETest, HostUniq) {
PPPoE pdu;
uint8_t a[] = { 1,2,3,4,5,6 };
byte_array data(a, a + sizeof(a));
pdu.host_uniq(data);
EXPECT_EQ(data, pdu.host_uniq());
}
TEST_F(PPPoETest, ACCookie) {
PPPoE pdu;
uint8_t a[] = { 1,2,3,4,5,6 };
byte_array data(a, a + sizeof(a));
pdu.ac_cookie(data);
EXPECT_EQ(data, pdu.ac_cookie());
}
TEST_F(PPPoETest, VendorSpecific) {
PPPoE pdu;
uint8_t a[] = { 1,2,3,4,5,6 };
PPPoE::vendor_spec_type output, data(
0x9283f78,
PPPoE::vendor_spec_type::data_type(a, a + sizeof(a))
);
pdu.vendor_specific(data);
output = pdu.vendor_specific();
EXPECT_EQ(data.data, output.data);
EXPECT_EQ(data.vendor_id, output.vendor_id);
}
TEST_F(PPPoETest, RelaySessionID) {
PPPoE pdu;
uint8_t a[] = { 1,2,3,4,5,6 };
byte_array data(a, a + sizeof(a));
pdu.relay_session_id(data);
EXPECT_EQ(data, pdu.relay_session_id());
}
TEST_F(PPPoETest, ServiceNameError) {
{
PPPoE pdu;
pdu.service_name_error("carlos");
EXPECT_EQ("carlos", pdu.service_name_error());
}
{
PPPoE pdu;
pdu.service_name_error("");
EXPECT_EQ("", pdu.service_name_error());
}
}
TEST_F(PPPoETest, ACSystemError) {
PPPoE pdu;
pdu.ac_system_error("carlos");
EXPECT_EQ("carlos", pdu.ac_system_error());
}
TEST_F(PPPoETest, GenericError) {
PPPoE pdu;
pdu.generic_error("carlos");
EXPECT_EQ("carlos", pdu.generic_error());
}
TEST_F(PPPoETest, SpoofedOptions) {
PPPoE pdu;
uint8_t a[] = { 1,2,3,4,5,6 };
pdu.add_tag(
PPPoE::pppoe_tag(PPPoE::VENDOR_SPECIFIC, 65000, a, a + sizeof(a))
);
pdu.add_tag(
PPPoE::pppoe_tag(PPPoE::VENDOR_SPECIFIC, 65000, a, a + sizeof(a))
);
pdu.add_tag(
PPPoE::pppoe_tag(PPPoE::VENDOR_SPECIFIC, 65000, a, a + sizeof(a))
);
// probably we'd expect it to crash if it's not working, valgrind plx
EXPECT_EQ(3, pdu.tags().size());
}