Keep the colleague's microbu-esp32c5 tree in this repository

obu-firmware builds against vanetza-idf from microbu-esp32c5/external, but
that tree was gitignored, so a clone of this repository could not build the
firmware it ships. It is now committed here as ordinary files in its own
folder, microbu-esp32c5/: the colleague's commit cf4b99f plus the V2X2MAP
bridge's signature verification (--trust) used on the bench. Nothing is
fetched from or pushed to the colleague's repository; this repository and
its remotes carry everything. The folder's own .gitignore keeps build output,
downloaded components and private key material out, as it did there; the
committed file set is identical to that repository's tracked files.

The ESP32-C5 is still flashed from obu-firmware/, which only takes
vanetza-idf from microbu-esp32c5/, so the two stay separate folders.
FLASHING.md says how to take a newer version of the colleague's tree (copy
it over the folder, rebuild, test, commit).
This commit is contained in:
Ashin Walpola
2026-09-23 17:46:40 +02:00
parent 2f60623e18
commit 0e9525162d
9881 changed files with 1582523 additions and 17 deletions
@@ -0,0 +1,26 @@
set(CXX_SOURCES
buffer_packet.cpp
chunk_packet.cpp
cohesive_packet.cpp
ethernet_header.cpp
mac_address.cpp
packet_variant.cpp
packet.cpp
proxy_header.cpp
)
set(CXX_SOURCES_POSIX
io_vector.cpp
sockaddr.cpp
)
option(VANETZA_NET_WITH_POSIX "Build network module with POSIX extension" ${UNIX})
if(VANETZA_NET_WITH_POSIX)
list(APPEND CXX_SOURCES ${CXX_SOURCES_POSIX})
endif()
add_vanetza_component(net ${CXX_SOURCES})
target_link_libraries(net PUBLIC common)
add_test_subdirectory(tests)
@@ -0,0 +1,44 @@
#include "buffer_packet.hpp"
namespace vanetza
{
BufferPacket::BufferPacket()
{
}
void BufferPacket::swap(OsiLayer layer, ByteBuffer& replacement)
{
ByteBuffer& stored = mBuffers[layer];
stored.swap(replacement);
}
const ByteBuffer& BufferPacket::operator[](OsiLayer layer) const
{
auto match = mBuffers.find(layer);
if (match == mBuffers.end()) {
static const ByteBuffer scEmptyBuffer;
return scEmptyBuffer;
} else {
return match->second;
}
}
std::size_t BufferPacket::size() const
{
std::size_t packet_size = 0;
for (const auto& it : *this) {
packet_size += it.second.size();
}
return packet_size;
}
void BufferPacket::clear()
{
for (auto& it : mBuffers) {
it.second.clear();
}
}
} // namespace vanetza
@@ -0,0 +1,35 @@
#ifndef BUFFER_PACKET_HPP_QYLSJSX5
#define BUFFER_PACKET_HPP_QYLSJSX5
#include <vanetza/common/byte_buffer.hpp>
#include <vanetza/net/osi_layer.hpp>
#include <cstddef>
#include <map>
namespace vanetza
{
class BufferPacket
{
public:
typedef std::map<OsiLayer, ByteBuffer> map_t;
BufferPacket();
void swap(OsiLayer layer, ByteBuffer&);
const ByteBuffer& operator[](OsiLayer layer) const;
ByteBuffer& operator[](OsiLayer layer) { return mBuffers[layer]; }
std::size_t size() const;
void clear();
map_t::iterator begin() { return mBuffers.begin(); }
map_t::iterator end() { return mBuffers.end(); }
map_t::const_iterator begin() const { return mBuffers.begin(); }
map_t::const_iterator end() const { return mBuffers.end(); }
private:
map_t mBuffers;
};
} // namespace vanetza
#endif /* BUFFER_PACKET_HPP_QYLSJSX5 */
@@ -0,0 +1,87 @@
#include "chunk_packet.hpp"
#include "cohesive_packet.hpp"
#include <cassert>
namespace vanetza
{
static const ByteBufferConvertible empty_byte_buffer_convertible;
ChunkPacket::ChunkPacket()
{
}
ChunkPacket::ChunkPacket(const ChunkPacket& other)
{
for (auto& layer : other.m_layers) {
m_layers.insert(layer);
}
}
ChunkPacket& ChunkPacket::operator=(const ChunkPacket& other)
{
ChunkPacket tmp = other;
m_layers.swap(tmp.m_layers);
return *this;
}
ByteBufferConvertible& ChunkPacket::layer(OsiLayer layer)
{
return m_layers[layer];
}
const ByteBufferConvertible& ChunkPacket::layer(OsiLayer layer) const
{
auto found = m_layers.find(layer);
if (found != m_layers.end()) {
assert(found->first == layer);
return found->second;
} else {
return empty_byte_buffer_convertible;
}
}
std::size_t ChunkPacket::size() const
{
std::size_t size = 0;
for(auto& it : m_layers)
{
size += it.second.size();
}
return size;
}
std::size_t ChunkPacket::size(OsiLayer from, OsiLayer to) const
{
assert(from <= to);
std::size_t size = 0;
for (auto& layer : m_layers) {
if (layer.first >= from && layer.first <= to) {
size += layer.second.size();
}
}
return size;
}
ChunkPacket ChunkPacket::extract(OsiLayer from, OsiLayer to)
{
ChunkPacket result;
for (auto layer : osi_layer_range(from, to)) {
using namespace std;
swap(result[layer], (*this)[layer]);
}
return result;
}
ChunkPacket& ChunkPacket::merge(ChunkPacket& source, OsiLayer from, OsiLayer to)
{
for (auto layer : osi_layer_range(from, to)) {
(*this)[layer] = std::move(source[layer]);
source[layer] = empty_byte_buffer_convertible;
}
return *this;
}
} // namespace vanetza
@@ -0,0 +1,92 @@
#ifndef CHUNK_PACKET_HPP_AT4GYSLD
#define CHUNK_PACKET_HPP_AT4GYSLD
#include <vanetza/common/byte_buffer_convertible.hpp>
#include <vanetza/net/osi_layer.hpp>
#include <cstddef>
#include <map>
namespace vanetza
{
/**
* \brief ChunckPacket is a packet consisting of several memory chunks
*
* ChunkPacket is the preferred packet type when it is getting assembled step by step.
* Each layer can easily add further bytes without caring about other layers at all.
*/
class ChunkPacket
{
public:
ChunkPacket();
// copy semantics
ChunkPacket(const ChunkPacket&);
ChunkPacket& operator=(const ChunkPacket&);
// move semantics
ChunkPacket(ChunkPacket&&) = default;
ChunkPacket& operator=(ChunkPacket&&) = default;
/**
* Access ByteBufferConvertible of specific layer
* \param layer ol Access this layer's data
* \return ByteBufferConvertible, might be empty
*/
ByteBufferConvertible& layer(OsiLayer ol);
/** \copydoc ChunkPacket::layer */
const ByteBufferConvertible& layer(OsiLayer ol) const;
/** \copydoc ChunkPacket::layer */
inline ByteBufferConvertible& operator[](OsiLayer ol)
{
return layer(ol);
}
/** \copydoc ChunkPacket::layer */
inline const ByteBufferConvertible& operator[](OsiLayer ol) const
{
return layer(ol);
}
/**
* Get size of whole payload
* \return payload size in bytes
*/
std::size_t size() const;
/**
* Get size of payload from specified layer range
* \param from start counting including this layer
* \param to stop counting including this layer
* \return payload size in bytes
*/
std::size_t size(OsiLayer from, OsiLayer to) const;
/**
* Extract a range of layers from this packet to a new one.
* The respective layers of this ChunkPacket are empty afterwards.
* \param from start at this layer (inclusive)
* \param to stop at this layer (inclusive)
* \return new packet containing layers of specified range
*/
ChunkPacket extract(OsiLayer from, OsiLayer to);
/**
* Merge layers from another packet
* \param packet source packet (layers will be moved from there)
* \param from start at this layer (inclusive)
* \param to stop at this layer (inclusive)
* \return reference to this packet
*/
ChunkPacket& merge(ChunkPacket& packet, OsiLayer from, OsiLayer to);
private:
typedef std::map<OsiLayer, ByteBufferConvertible> map_type;
map_type m_layers;
};
} // namespace vanetza
#endif /* CHUNK_PACKET_HPP_AT4GYSLD */
@@ -0,0 +1,136 @@
#include "cohesive_packet.hpp"
#include <cassert>
#include <iterator>
namespace vanetza
{
constexpr unsigned layer_index(OsiLayer layer)
{
return static_cast<unsigned>(layer);
}
static_assert(layer_index(min_osi_layer()) == 1, "Lowest OSI layer index broken");
CohesivePacket::CohesivePacket(const ByteBuffer& buffer, OsiLayer layer) :
m_buffer(buffer)
{
reset_iterators(layer);
}
CohesivePacket::CohesivePacket(ByteBuffer&& buffer, OsiLayer layer) :
m_buffer(std::move(buffer))
{
reset_iterators(layer);
}
CohesivePacket::CohesivePacket(const CohesivePacket& other) :
m_buffer(other.m_buffer)
{
rebuild_iterators(other);
}
CohesivePacket& CohesivePacket::operator=(const CohesivePacket& other)
{
m_buffer = other.m_buffer;
rebuild_iterators(other);
return *this;
}
auto CohesivePacket::operator[](OsiLayer layer) const -> buffer_const_range
{
return get(layer_index(layer));
}
auto CohesivePacket::operator[](OsiLayer layer) -> buffer_range
{
return get(layer_index(layer));
}
void CohesivePacket::set_boundary(OsiLayer layer, unsigned bytes)
{
const unsigned layer_idx = layer_index(layer);
assert(get(layer_idx).size() >= bytes);
m_iterators[layer_idx] = m_iterators[layer_idx - 1] + bytes;
}
void CohesivePacket::trim(OsiLayer from, unsigned bytes)
{
if (size(from, max_osi_layer()) > bytes) {
const auto from_idx = layer_index(from) - 1;
const auto max_idx = layer_index(max_osi_layer());
m_iterators[max_idx] = m_iterators[from_idx] + bytes;
assert(&m_iterators.back() == &m_iterators[max_idx]);
assert(m_iterators.back() >= m_iterators.front());
for (auto idx = from_idx; idx < max_idx; ++idx) {
if (m_iterators[idx] > m_iterators[max_idx]) {
m_iterators[idx] = m_iterators[max_idx];
}
}
}
assert(size(from, max_osi_layer()) <= bytes);
}
std::size_t CohesivePacket::size() const
{
return std::distance(m_iterators.front(), m_iterators.back());
}
std::size_t CohesivePacket::size(OsiLayer single_layer) const
{
return get(layer_index(single_layer)).size();
}
std::size_t CohesivePacket::size(OsiLayer from, OsiLayer to) const
{
auto begin = m_iterators[layer_index(from) - 1];
auto end = m_iterators[layer_index(to)];
auto dist = std::distance(begin, end);
return dist < 0 ? 0 : dist;
}
void CohesivePacket::reset_iterators(OsiLayer ins_layer)
{
unsigned layer_idx = 0;
const unsigned ins_layer_idx = layer_index(ins_layer);
for (unsigned i = 0; i < ins_layer_idx; ++i) {
m_iterators[layer_idx++] = m_buffer.begin();
}
const unsigned max_layer_idx = layer_index(max_osi_layer());
for (unsigned i = ins_layer_idx; i <= max_layer_idx; ++i) {
m_iterators[layer_idx++] = m_buffer.end();
}
assert(m_iterators.size() == layer_idx);
}
void CohesivePacket::rebuild_iterators(const CohesivePacket& other)
{
assert(m_buffer.size() == other.m_buffer.size());
m_iterators.front() = m_buffer.begin();
auto next = m_iterators.front();
for (unsigned i = 1; i < m_iterators.size(); ++i) {
next += other.m_iterators[i] - other.m_iterators[i - 1];
m_iterators[i] = next;
}
}
auto CohesivePacket::get(unsigned layer_idx) -> buffer_range
{
assert(layer_idx > 0);
assert(layer_idx < m_iterators.size());
return buffer_range(m_iterators[layer_idx - 1], m_iterators[layer_idx]);
}
auto CohesivePacket::get(unsigned layer_idx) const -> buffer_const_range
{
assert(layer_idx > 0);
assert(layer_idx < m_iterators.size());
return buffer_const_range(m_iterators[layer_idx - 1], m_iterators[layer_idx]);
}
} // namespace vanetza
@@ -0,0 +1,99 @@
#ifndef COHESIVE_PACKET_HPP_VG2XKSCV
#define COHESIVE_PACKET_HPP_VG2XKSCV
#include <vanetza/common/byte_buffer.hpp>
#include <vanetza/net/osi_layer.hpp>
#include <boost/range/iterator_range.hpp>
#include <array>
namespace vanetza
{
/**
* A cohesive packet is stored in contiguous memory
*/
class CohesivePacket
{
public:
typedef boost::iterator_range<ByteBuffer::iterator> buffer_range;
typedef boost::iterator_range<ByteBuffer::const_iterator> buffer_const_range;
/**
* Create packet from buffer and assign all bytes to given layer
* \param buffer copy data from this buffer
* \param layer all bytes belong to this layer (at least at first)
*/
CohesivePacket(const ByteBuffer& buffer, OsiLayer layer);
CohesivePacket(ByteBuffer&& buffer, OsiLayer layer);
CohesivePacket(const CohesivePacket&);
CohesivePacket& operator=(const CohesivePacket&);
CohesivePacket(CohesivePacket&&) = default;
CohesivePacket& operator=(CohesivePacket&&) = default;
/**
* Access a certain sub-range of packet data belonging to a specific layer
* \param layer requested layer data
* \return buffer range with data, might be empty
*/
buffer_const_range operator[](OsiLayer layer) const;
buffer_range operator[](OsiLayer layer);
/**
* Set boundary of layer data.
* Data beyond boundary belongs to next upper layer afterwards.
* \param layer set boundary of this layer
* \param bytes length of layer
* \note Never set boundary larger than previous layer length!
*/
void set_boundary(OsiLayer, unsigned bytes);
/**
* Trim size of packet, i.e. cut bytes at the end if too long.
* \param from start counting with this layer
* \param bytes target length in bytes
*/
void trim(OsiLayer from, unsigned bytes);
/**
* Get size of whole packet
* \return length in bytes
*/
std::size_t size() const;
/**
* Get size of a single layer in packet
* \param single_layer which layer has to be considered
* \return length in bytes
*/
std::size_t size(OsiLayer single_layer) const;
/**
* Get size of several layers
* \param from start counting with this layer
* \param to stop counting after this layer
* \return length in bytes
*/
std::size_t size(OsiLayer from, OsiLayer to) const;
/**
* Non-mutable access to internal byte buffer
* \return const byte buffer reference
*/
const ByteBuffer& buffer() const { return m_buffer; }
private:
void reset_iterators(OsiLayer layer);
void rebuild_iterators(const CohesivePacket&);
buffer_const_range get(unsigned idx) const;
buffer_range get(unsigned idx);
ByteBuffer m_buffer;
std::array<ByteBuffer::iterator, osi_layers.size() + 1> m_iterators;
};
} // namespace vanetza
#endif /* COHESIVE_PACKET_HPP_VG2XKSCV */
@@ -0,0 +1,51 @@
#include "ethernet_header.hpp"
#include <algorithm>
#include <cassert>
#include <stdexcept>
namespace vanetza
{
ByteBuffer create_ethernet_header(const MacAddress& dest, const MacAddress& src, uint16be_t proto)
{
ByteBuffer buffer;
buffer.reserve(EthernetHeader::length_bytes);
auto inserter = std::back_inserter(buffer);
std::copy(dest.octets.begin(), dest.octets.end(), inserter);
std::copy(src.octets.begin(), src.octets.end(), inserter);
uint16_t host_proto = proto.host();
inserter = (host_proto >> 8) & 0xff;
inserter = host_proto & 0xff;
assert(buffer.size() == EthernetHeader::length_bytes);
return buffer;
}
ByteBuffer create_ethernet_header(const EthernetHeader& hdr)
{
return create_ethernet_header(hdr.destination, hdr.source, hdr.type);
}
EthernetHeader decode_ethernet_header(ByteBuffer::const_iterator begin, ByteBuffer::const_iterator end)
{
EthernetHeader hdr;
const std::size_t buflen = std::distance(begin, end);
if (buflen < EthernetHeader::length_bytes) {
throw std::runtime_error("buffer is too short for EthernetHeader decoding");
} else {
std::copy_n(begin, MacAddress::length_bytes, hdr.destination.octets.begin());
begin += MacAddress::length_bytes;
std::copy_n(begin, MacAddress::length_bytes, hdr.source.octets.begin());
begin += MacAddress::length_bytes;
uint16_t proto = (begin[0] << 8) | begin[1];
hdr.type = host_cast(proto);
}
return hdr;
}
EthernetHeader decode_ethernet_header(const ByteBuffer& buf)
{
return decode_ethernet_header(buf.begin(), buf.end());
}
} // namespace vanetza
@@ -0,0 +1,57 @@
#ifndef ETHERNET_HEADER_HPP_
#define ETHERNET_HEADER_HPP_
#include <vanetza/common/byte_buffer.hpp>
#include <vanetza/common/byte_order.hpp>
#include <vanetza/net/mac_address.hpp>
#include <cstddef>
namespace vanetza
{
/**
* Get length of ethernet header in bytes
* \param header length in bytes
*/
constexpr std::size_t ethernet_header_length()
{
return 2 * sizeof(MacAddress::octets) + sizeof(uint16_t);
}
/**
* Link-level header of type Ethernet II
*
* EthernetHeader is suitable for Linux raw packet sockets, see man 7 packet
*/
class EthernetHeader
{
public:
using EtherType = uint16be_t;
static constexpr std::size_t length_bytes = ethernet_header_length();
MacAddress destination;
MacAddress source;
EtherType type;
};
/**
* Create a byte buffer containing an ethernet header
* \param dest Destination MAC address
* \param src Source MAC address
* \param proto EtherType number
*/
ByteBuffer create_ethernet_header(const MacAddress& dest, const MacAddress& src, uint16be_t proto);
ByteBuffer create_ethernet_header(const EthernetHeader&);
/**
* Parse ethernet header from byte buffer
* \param buffer byte buffer (throws exception if too short)
* \return ethernet header
*/
EthernetHeader decode_ethernet_header(const ByteBuffer&);
EthernetHeader decode_ethernet_header(ByteBuffer::const_iterator, ByteBuffer::const_iterator);
} // namespace vanetza
#endif // ETHERNET_HEADER_HPP_
@@ -0,0 +1,39 @@
#include "io_vector.hpp"
#include "buffer_packet.hpp"
namespace vanetza
{
void IoVector::clear()
{
m_vector.clear();
}
void IoVector::append(const void* base, std::size_t length)
{
iovec node;
node.iov_base = const_cast<void*>(base);
node.iov_len = length;
m_vector.push_back(node);
}
void IoVector::append(const BufferPacket& packet)
{
for (auto& kv : packet) {
const ByteBuffer& buffer = kv.second;
append(buffer.data(), buffer.size());
}
}
std::size_t IoVector::length() const
{
return m_vector.size();
}
const iovec* IoVector::base() const
{
return m_vector.empty() ? nullptr : &m_vector[0];
}
} // namespace vanetza
@@ -0,0 +1,34 @@
#ifndef IO_VECTOR_HPP_A3ANMI8B
#define IO_VECTOR_HPP_A3ANMI8B
#include <vector>
#include <sys/socket.h>
#include <sys/types.h>
namespace vanetza
{
// forward declaration
class BufferPacket;
/**
* IoVector eaeses population of struct msghdr.
* struct msghdr is required for sendmsg() calls.
*/
class IoVector
{
public:
void append(const void* base, std::size_t length);
void append(const BufferPacket&);
void clear();
std::size_t length() const;
const iovec* base() const;
private:
std::vector<iovec> m_vector;
};
} // namespace vanetza
#endif /* IO_VECTOR_HPP_A3ANMI8B */
@@ -0,0 +1,102 @@
#include "mac_address.hpp"
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/io/ios_state.hpp>
#include <algorithm>
#include <cassert>
#include <cctype>
#include <iomanip>
#include <string>
#include <vector>
namespace vanetza
{
const MacAddress cBroadcastMacAddress = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
constexpr std::size_t MacAddress::length_bytes;
MacAddress::MacAddress()
{
std::fill_n(octets.begin(), octets.size(), 0x00);
}
MacAddress::MacAddress(std::initializer_list<uint8_t> args)
{
assert(args.size() == octets.size());
std::copy_n(args.begin(), std::min(args.size(), octets.size()), octets.begin());
}
bool operator==(const MacAddress& lhs, const MacAddress& rhs)
{
return (lhs.octets == rhs.octets);
}
bool operator<(const MacAddress& lhs, const MacAddress& rhs)
{
for (std::size_t i = 0; i < MacAddress::length_bytes; ++i) {
if (lhs.octets[i] < rhs.octets[i]) {
return true;
}
}
return false;
}
bool parse_mac_address(const std::string& str, MacAddress& addr)
{
bool parsed = false;
std::vector<std::string> octets;
boost::algorithm::split(octets, str, boost::algorithm::is_any_of(":"));
// lambda returning true if string consists of two hex digits
auto octet_checker = [](const std::string& str) {
return str.size() == 2 && std::isxdigit(str[0]) && std::isxdigit(str[1]);
};
if (octets.size() == addr.octets.size() && std::all_of(octets.begin(), octets.end(), octet_checker)) {
std::transform(octets.begin(), octets.end(), addr.octets.begin(),
[](const std::string& octet) {
return std::strtol(octet.c_str(), nullptr, 16);
});
parsed = true;
}
return parsed;
}
boost::optional<MacAddress> parse_mac_address(const std::string& str)
{
boost::optional<MacAddress> addr_result;
MacAddress addr_tmp;
if (parse_mac_address(str, addr_tmp)) {
addr_result.reset(addr_tmp);
}
return addr_result;
}
std::ostream& operator<<(std::ostream& os, const MacAddress& addr)
{
boost::io::ios_all_saver ifs(os);
os << std::hex << std::setfill('0');
os << std::setw(2) << unsigned(addr.octets[0]);
for (unsigned i = 1; i < addr.octets.size(); ++i) {
os << ":" << std::setw(2) << unsigned(addr.octets[i]);
}
return os;
}
void serialize(OutputArchive& ar, const MacAddress& addr)
{
for (std::uint8_t octet : addr.octets) {
ar << octet;
}
}
void deserialize(InputArchive& ar, MacAddress& addr)
{
for (std::uint8_t& octet : addr.octets) {
ar >> octet;
}
}
} // namespace vanetza
@@ -0,0 +1,104 @@
#ifndef MAC_ADDRESS_HPP_FDINBLBS
#define MAC_ADDRESS_HPP_FDINBLBS
#include <vanetza/common/serialization.hpp>
#include <boost/operators.hpp>
#include <boost/optional.hpp>
#include <array>
#include <cstddef>
#include <cstdint>
#include <initializer_list>
#include <functional>
#include <ostream>
#include <string>
namespace vanetza
{
class MacAddress : public boost::totally_ordered<MacAddress>
{
public:
static constexpr std::size_t length_bytes = 6;
MacAddress();
MacAddress(std::initializer_list<uint8_t> args);
MacAddress(const MacAddress&) = default;
MacAddress& operator=(const MacAddress&) = default;
MacAddress(MacAddress&&) = default;
MacAddress& operator=(MacAddress&&) = default;
std::array<uint8_t, length_bytes> octets;
};
extern const MacAddress cBroadcastMacAddress;
bool operator==(const MacAddress& lhs, const MacAddress& rhs);
bool operator<(const MacAddress& lhs, const MacAddress& rhs);
std::ostream& operator<<(std::ostream& os, const MacAddress&);
/**
* Try to parse MAC address from string
* \param str source string with "XX:XX:XX:XX:XX:XX" format
* \param addr pass parsed address by reference
* \return true if successfully parsed
*/
bool parse_mac_address(const std::string& str, MacAddress& addr);
/**
* Try to parse MAC address from string
* \param str source string with "XX:XX:XX:XX:XX:XX" format
* \return parsed address if successful
*/
boost::optional<MacAddress> parse_mac_address(const std::string& str);
/**
* Derive a MAC address from an arbitrary integral value.
* \param value used to derive MAC address, it's size does not matter
* \return New MAC address
*/
template<typename T>
MacAddress create_mac_address(T value)
{
MacAddress mac;
const std::size_t octets = std::min(mac.octets.size(), sizeof(T));
for (std::size_t i = 0; i < octets; ++i) {
mac.octets[i] = value >> (8 * (octets - i - 1)) & 0xff;
}
return mac;
}
/**
* Serialize MAC address
* \param out output sink
* \param addr MAC address
*/
void serialize(OutputArchive& out, const MacAddress& addr);
/**
* Deserialize MAC address
* \param in input source
* \param addr deserialize into this object
*/
void deserialize(InputArchive& in, MacAddress& addr);
} // namespace vanetza
// specialization
namespace std {
template<>
struct hash<vanetza::MacAddress>
{
size_t operator()(const vanetza::MacAddress& addr) const
{
size_t tmp = 0;
for (auto octet : addr.octets) {
tmp ^= hash<decltype(octet)>()(octet);
}
return tmp;
}
};
} // namespace std
#endif /* MAC_ADDRESS_HPP_FDINBLBS */
@@ -0,0 +1,80 @@
#ifndef OSI_LAYER_HPP_C4VTEZJP
#define OSI_LAYER_HPP_C4VTEZJP
#include <boost/range/iterator_range.hpp>
#include <array>
#include <cstdint>
namespace vanetza
{
enum class OsiLayer : uint8_t
{
Physical = 1,
Link = 2,
Network = 3,
Transport = 4,
Session = 5,
Presentation = 6,
Application = 7
};
constexpr OsiLayer min_osi_layer() { return OsiLayer::Physical; }
constexpr OsiLayer max_osi_layer() { return OsiLayer::Application; }
constexpr std::array<OsiLayer, 7> osi_layers {{
OsiLayer::Physical,
OsiLayer::Link,
OsiLayer::Network,
OsiLayer::Transport,
OsiLayer::Session,
OsiLayer::Presentation,
OsiLayer::Application
}};
/**
* Calculate distance between layers
* \param from start counting at this layer
* \param to stop counting here
* \return 0 if equal layers, positive if "to" is a higher layer, negative if "to" is below "from"
*/
constexpr int distance(OsiLayer from, OsiLayer to)
{
return static_cast<int>(to) - static_cast<int>(from);
}
constexpr std::size_t num_osi_layers(OsiLayer from, OsiLayer to)
{
return (from <= to ? distance(from, to) + 1 : 0);
}
template<OsiLayer FROM, OsiLayer TO>
std::array<OsiLayer, num_osi_layers(FROM, TO)> osi_layer_range()
{
static_assert(FROM <= TO, "FROM layer is above TO layer");
typedef typename std::underlying_type<OsiLayer>::type num_type;
num_type num = static_cast<num_type>(FROM);
std::array<OsiLayer, num_osi_layers(FROM, TO)> layers;
for (auto& layer : layers) {
layer = static_cast<OsiLayer>(num++);
}
return layers;
}
inline boost::iterator_range<decltype(osi_layers)::const_iterator>
osi_layer_range(OsiLayer from, OsiLayer to)
{
if (from <= to) {
auto begin = osi_layers.cbegin() + distance(min_osi_layer(), from);
auto end = osi_layers.cend() - distance(to, max_osi_layer());
return boost::make_iterator_range(begin, end);
} else {
return boost::make_iterator_range(osi_layers.cend(), osi_layers.cend());
}
}
} // namespace vanetza
#endif /* OSI_LAYER_HPP_C4VTEZJP */
@@ -0,0 +1,38 @@
#include <vanetza/net/packet.hpp>
namespace vanetza
{
std::unique_ptr<DownPacket> duplicate(const PacketVariant& packet)
{
struct duplication_visitor : public boost::static_visitor<>
{
void operator()(const CohesivePacket& packet)
{
m_duplicate.reset(new ChunkPacket());
for (auto layer : osi_layers) {
const auto source = packet[layer];
auto& dest = m_duplicate->layer(layer);
dest = ByteBuffer(source.begin(), source.end());
}
}
void operator()(const ChunkPacket& packet)
{
m_duplicate = duplicate(packet);
}
std::unique_ptr<ChunkPacket> m_duplicate;
};
duplication_visitor visitor;
boost::apply_visitor(visitor, packet);
return std::move(visitor.m_duplicate);
}
std::unique_ptr<DownPacket> duplicate(const DownPacket& packet)
{
return std::unique_ptr<DownPacket> { new DownPacket(packet) };
}
} // namespace vanetza
@@ -0,0 +1,21 @@
#ifndef PACKETS_HPP_XB5AOPWE
#define PACKETS_HPP_XB5AOPWE
#include <vanetza/net/packet_variant.hpp>
namespace vanetza
{
using DownPacket = ChunkPacket;
using UpPacket = PacketVariant;
/**
* Create clone of a packet
*/
std::unique_ptr<DownPacket> duplicate(const DownPacket&);
std::unique_ptr<DownPacket> duplicate(const UpPacket&);
} // namespace vanetza
#endif /* PACKETS_HPP_XB5AOPWE */
@@ -0,0 +1,195 @@
#include "packet_variant.hpp"
#include <boost/variant.hpp>
namespace vanetza
{
byte_view_range create_byte_view(const ChunkPacket& packet, OsiLayer layer)
{
return create_byte_view(packet[layer]);
}
byte_view_range create_byte_view(const CohesivePacket& packet, OsiLayer layer)
{
CohesivePacket::buffer_const_range range = packet[layer];
return byte_view_range { range.begin(), range.end() };
}
byte_view_range create_byte_view(const ChunkPacket& packet, OsiLayer from, OsiLayer to)
{
ByteBuffer buffer_range;
for (auto layer : osi_layer_range(from, to)) {
ByteBuffer buffer_layer;
packet[layer].convert(buffer_layer); // convert clears passed buffer (does not append)
buffer_range.insert(buffer_range.end(), buffer_layer.begin(), buffer_layer.end());
}
return create_byte_view(std::move(buffer_range));
}
byte_view_range create_byte_view(const CohesivePacket& packet, OsiLayer from, OsiLayer to)
{
ByteBuffer::const_iterator from_begin = packet[from].begin();
ByteBuffer::const_iterator to_end = packet[to].end();
return byte_view_range(from_begin, to_end);
}
void serialize(OutputArchive& oa, const ChunkPacket& packet)
{
ByteBuffer buf;
for (auto layer : osi_layers) {
buf.clear();
packet[layer].convert(buf);
oa.save_binary(buf.data(), buf.size());
}
}
void serialize(OutputArchive& oa, const CohesivePacket& packet)
{
oa.save_binary(packet.buffer().data(), packet.buffer().size());
}
} // namespace vanetza
namespace boost
{
using namespace vanetza;
std::size_t size(const PacketVariant& packet, OsiLayer layer)
{
struct size_visitor : public boost::static_visitor<std::size_t>
{
size_visitor(OsiLayer layer) : m_layer(layer) {}
std::size_t operator()(const CohesivePacket& packet)
{
return size(packet, m_layer);
}
std::size_t operator()(const ChunkPacket& packet)
{
return size(packet, m_layer);
}
OsiLayer m_layer;
};
size_visitor visitor(layer);
return boost::apply_visitor(visitor, packet);
}
std::size_t size(const PacketVariant& packet, OsiLayer from, OsiLayer to)
{
struct size_visitor : public boost::static_visitor<std::size_t>
{
size_visitor(OsiLayer from, OsiLayer to) : m_from(from), m_to(to) {}
std::size_t operator()(const CohesivePacket& packet)
{
return size(packet, m_from, m_to);
}
std::size_t operator()(const ChunkPacket& packet)
{
return size(packet, m_from, m_to);
}
OsiLayer m_from;
OsiLayer m_to;
};
size_visitor visitor(from, to);
return boost::apply_visitor(visitor, packet);
}
std::size_t size(const PacketVariant& packet)
{
struct size_visitor : public boost::static_visitor<std::size_t>
{
std::size_t operator()(const CohesivePacket& packet)
{
return packet.size();
}
std::size_t operator()(const ChunkPacket& packet)
{
return packet.size();
}
};
size_visitor visitor;
return boost::apply_visitor(visitor, packet);
}
byte_view_range create_byte_view(const PacketVariant& packet, OsiLayer layer)
{
struct payload_visitor : public boost::static_visitor<byte_view_range>
{
payload_visitor(OsiLayer layer) : m_layer(layer) {}
byte_view_range operator()(const CohesivePacket& packet)
{
return create_byte_view(packet, m_layer);
}
byte_view_range operator()(const ChunkPacket& packet)
{
return create_byte_view(packet, m_layer);
}
OsiLayer m_layer;
};
payload_visitor visitor(layer);
return boost::apply_visitor(visitor, packet);
}
byte_view_range create_byte_view(const PacketVariant& packet, OsiLayer from, OsiLayer to)
{
struct payload_visitor : public boost::static_visitor<byte_view_range>
{
payload_visitor(OsiLayer from, OsiLayer to) : m_from(from), m_to(to) {}
byte_view_range operator()(const CohesivePacket& packet)
{
return create_byte_view(packet, m_from, m_to);
}
byte_view_range operator()(const ChunkPacket& packet)
{
return create_byte_view(packet, m_from, m_to);
}
OsiLayer m_from;
OsiLayer m_to;
};
payload_visitor visitor(from, to);
return boost::apply_visitor(visitor, packet);
}
void serialize(OutputArchive& ar, const PacketVariant& packet)
{
struct serialize_visitor : public boost::static_visitor<>
{
serialize_visitor(OutputArchive& _oa) : oa(_oa) {}
void operator()(const ChunkPacket& packet)
{
serialize(oa, packet);
}
void operator()(const CohesivePacket& packet)
{
serialize(oa, packet);
}
OutputArchive& oa;
};
serialize_visitor visitor(ar);
boost::apply_visitor(visitor, packet);
}
} // namespace boost
@@ -0,0 +1,82 @@
#ifndef PACKET_VARIANT_HPP_LILZ0UWN
#define PACKET_VARIANT_HPP_LILZ0UWN
#include <vanetza/common/byte_view.hpp>
#include <vanetza/common/serialization.hpp>
#include <vanetza/net/chunk_packet.hpp>
#include <vanetza/net/cohesive_packet.hpp>
#include <boost/variant.hpp>
namespace vanetza
{
using PacketVariant = boost::variant<ChunkPacket, CohesivePacket>;
inline std::size_t
size(const CohesivePacket& packet, OsiLayer from, OsiLayer to)
{
return packet.size(from, to);
}
inline std::size_t
size(const CohesivePacket& packet, OsiLayer layer)
{
return packet.size(layer);
}
inline std::size_t
size(const ChunkPacket& packet, OsiLayer from, OsiLayer to)
{
return packet.size(from, to);
}
inline std::size_t
size(const ChunkPacket& packet, OsiLayer layer)
{
return packet[layer].size();
}
/**
* Create a view of a packet's bytes assigned to a certain layer
* \param packet
* \param layer
* \return a byte view, possibly empty
*/
byte_view_range create_byte_view(const ChunkPacket&, OsiLayer);
byte_view_range create_byte_view(const CohesivePacket&, OsiLayer);
/**
* Create a byte view for a sub-range of layers
* \param packet
* \param from first byte will be from this layer
* \param to last byte will be from this layer
* \return a byte view
*/
byte_view_range create_byte_view(const ChunkPacket& packet, OsiLayer from, OsiLayer to);
byte_view_range create_byte_view(const CohesivePacket& packet, OsiLayer from, OsiLayer to);
/**
* Serialize a whole packet from physical to application layer
* \param archive destination archive
* \param packet source packet
*/
void serialize(OutputArchive&, const ChunkPacket&);
void serialize(OutputArchive&, const CohesivePacket&);
} // namespace vanetza
namespace boost
{
// PacketVariant is only a type alias, so we need this quirk for proper name lookup
std::size_t size(const vanetza::PacketVariant&, vanetza::OsiLayer from, vanetza::OsiLayer to);
std::size_t size(const vanetza::PacketVariant&, vanetza::OsiLayer);
std::size_t size(const vanetza::PacketVariant&);
vanetza::byte_view_range create_byte_view(const vanetza::PacketVariant&, vanetza::OsiLayer);
vanetza::byte_view_range create_byte_view(const vanetza::PacketVariant&, vanetza::OsiLayer from, vanetza::OsiLayer to);
void serialize(vanetza::OutputArchive&, const vanetza::PacketVariant&);
} // namespace boost
#endif /* PACKET_VARIANT_HPP_LILZ0UWN */
@@ -0,0 +1,57 @@
#include "proxy_header.hpp"
#include <vanetza/common/byte_order.hpp>
#include <cassert>
namespace vanetza
{
double get_signal_power(const ProxyHeader& header)
{
double power = ntoh(header.signal_power);
power /= 256.0;
return power;
}
void set_signal_power(ProxyHeader& header, double dbm)
{
auto power = static_cast<decltype(ProxyHeader::signal_power)>(dbm * 256.0);
header.signal_power = hton(power);
}
access::AccessCategory get_access_category(const ProxyHeader& header)
{
return static_cast<access::AccessCategory>(header.access_category & 0x07);
}
void set_access_category(ProxyHeader& header, access::AccessCategory ac)
{
header.access_category = static_cast<uint8_t>(ac);
}
boost::iterator_range<const uint8_t*> get_payload(const ProxyHeader& header, std::size_t size)
{
const auto payload_offset = ntoh(header.payload_offset);
const auto payload_size = ntoh(header.payload_size);
assert(size >= payload_offset + payload_size);
assert(payload_offset >= sizeof(ProxyHeader));
const uint8_t* begin = reinterpret_cast<const uint8_t*>(&header);
const uint8_t* end = begin;
if (size >= payload_offset + payload_size) {
begin += payload_offset;
end = begin + payload_size;
}
return boost::make_iterator_range(begin, end);
}
void set_payload(ProxyHeader& header, std::size_t size)
{
header.payload_offset = hton<decltype(header.payload_offset)>(sizeof(ProxyHeader));
header.payload_size = hton<decltype(header.payload_size)>(size);
}
} // namespace vanetza
@@ -0,0 +1,48 @@
#ifndef PROXY_HEADER_HPP_JWTV3BDT
#define PROXY_HEADER_HPP_JWTV3BDT
#include <boost/range/iterator_range.hpp>
#include <vanetza/access/access_category.hpp>
#include <cstdint>
namespace vanetza
{
#ifndef _MSC_VER
// GCC and Clang support packed attribute
#define PACKED_STRUCT(name, block) struct name { block } __attribute__((__packed__));
#else
// MSVC variant
#define PACKED_STRUCT(name, block) \
__pragma(pack(push, 1)) \
struct name { block }; \
__pragma(pack(pop))
#endif
PACKED_STRUCT(ProxyHeader,
/** offset in bytes to payload's first byte, counting from packet start */
uint16_t payload_offset;
/** length of payload in bytes */
uint32_t payload_size;
/** signal power in dBm, fixed point format 8.8 = trunc(double * 256) */
int16_t signal_power;
/** access category (transmission only) */
uint8_t access_category;
)
static_assert(sizeof(ProxyHeader) == 9, "ProxyHeader has invalid size");
double get_signal_power(const ProxyHeader&);
void set_signal_power(ProxyHeader&, double dbm);
access::AccessCategory get_access_category(const ProxyHeader&);
void set_access_category(ProxyHeader&, access::AccessCategory);
boost::iterator_range<const uint8_t*> get_payload(const ProxyHeader&, std::size_t);
void set_payload(ProxyHeader&, std::size_t);
} // namespace vanetza
#endif /* PROXY_HEADER_HPP_JWTV3BDT */
@@ -0,0 +1,17 @@
#include "sockaddr.hpp"
#include "mac_address.hpp"
#include <algorithm>
#include <cassert>
#include <net/ethernet.h>
#include <netpacket/packet.h>
namespace vanetza
{
void assign(sockaddr_ll& sockaddr, const MacAddress& mac)
{
assert(ETHER_ADDR_LEN == mac.octets.size());
std::copy_n(mac.octets.begin(), mac.octets.size(), sockaddr.sll_addr);
}
} // namespace vanetza
@@ -0,0 +1,16 @@
#ifndef SOCKADDR_HPP_YPVTVXXP
#define SOCKADDR_HPP_YPVTVXXP
struct sockaddr_ll;
namespace vanetza
{
class MacAddress;
void assign(sockaddr_ll&, const MacAddress&);
} // namespace vanetza
#endif /* SOCKADDR_HPP_YPVTVXXP */
@@ -0,0 +1,48 @@
#ifndef SOCKET_TRAITS_HPP_
#define SOCKET_TRAITS_HPP_
#include <vanetza/net/osi_layer.hpp>
#include <type_traits>
namespace vanetza
{
template<typename SOCKET>
struct socket_layers
{
static const OsiLayer min = SOCKET::min_layer;
static const OsiLayer max = SOCKET::max_layer;
static_assert(min <= max, "Corrupt layer ordering");
};
namespace pdu_tags
{
struct pdu_tag {};
struct empty_pdu_tag : public pdu_tag {};
struct phy_tag : public pdu_tag {};
struct mk2_tag : public phy_tag {};
struct mac_tag : public pdu_tag {};
struct ethernet_tag : public mac_tag {};
} // namespace pdu_tags
template<typename SOCKET, OsiLayer LAYER>
struct socket_layer_pdu
{
typedef pdu_tags::empty_pdu_tag tag;
};
template<OsiLayer LAYER, typename TAG, class SOCKET>
bool constexpr pdu_match(const SOCKET&)
{
return std::is_same<TAG, typename socket_layer_pdu<SOCKET, LAYER>::tag>::value;
}
} // namespace vanetza
#endif // SOCKET_TRAITS_HPP_
@@ -0,0 +1,11 @@
include(UseGTest)
configure_gtest_directory(LINK_LIBRARIES net)
add_gtest(ChunkPacket chunk_packet.cpp)
add_gtest(CohesivePacket cohesive_packet.cpp)
add_gtest(MacAddress mac_address.cpp)
add_gtest(OsiLayer osi_layer.cpp)
if(VANETZA_NET_WITH_POSIX)
add_gtest(EthernetHeader ethernet_header.cpp)
endif()
@@ -0,0 +1,145 @@
#include <gtest/gtest.h>
#include <vanetza/net/chunk_packet.hpp>
using vanetza::ByteBuffer;
using vanetza::ChunkPacket;
using vanetza::OsiLayer;
TEST(ChunkPacket, ctor)
{
ChunkPacket packet;
EXPECT_EQ(0, packet.size());
}
TEST(ChunkPacket, size_from_to)
{
ChunkPacket packet;
packet[OsiLayer::Link] = ByteBuffer(8);
packet[OsiLayer::Session] = ByteBuffer(19);
packet[OsiLayer::Application] = ByteBuffer(5);
EXPECT_EQ(8, packet.size(OsiLayer::Link, OsiLayer::Link));
EXPECT_EQ(0, packet.size(OsiLayer::Network, OsiLayer::Transport));
EXPECT_EQ(27, packet.size(OsiLayer::Physical, OsiLayer::Session));
EXPECT_EQ(24, packet.size(OsiLayer::Session, OsiLayer::Application));
}
TEST(ChunkPacket, access)
{
const ByteBuffer data { 3, 8, 7, 5, 6 };
ChunkPacket packet;
packet[OsiLayer::Transport] = ByteBuffer { data };
EXPECT_EQ(data.size(), packet.size());
EXPECT_EQ(data.size(), packet[OsiLayer::Transport].size());
ByteBuffer tmp;
packet[OsiLayer::Transport].convert(tmp);
EXPECT_EQ(data, tmp);
}
TEST(ChunkPacket, copy)
{
ChunkPacket original;
original[OsiLayer::Physical] = ByteBuffer(12);
original[OsiLayer::Presentation] = ByteBuffer(34);
ChunkPacket copy { original };
EXPECT_EQ(46, copy.size());
EXPECT_EQ(12, copy[OsiLayer::Physical].size());
EXPECT_EQ(34, copy[OsiLayer::Presentation].size());
ChunkPacket tmp;
tmp = original;
EXPECT_EQ(46, tmp.size());
}
TEST(ChunkPacket, self_assignment)
{
ChunkPacket data;
data[OsiLayer::Physical] = ByteBuffer(20);
ASSERT_EQ(20, data.size());
data = data;
EXPECT_EQ(20, data.size());
}
TEST(ChunkPacket, extract)
{
std::array<ByteBuffer, 5> buffer;
for (unsigned i = 0; i < buffer.size(); ++i) {
ByteBuffer tmp;
tmp.resize(10, static_cast<ByteBuffer::value_type>(i));
buffer[i] = std::move(tmp);
}
ChunkPacket a;
a[OsiLayer::Link] = ByteBuffer { buffer[0] };
a[OsiLayer::Network] = ByteBuffer { buffer[1] };
a[OsiLayer::Transport] = ByteBuffer { buffer[2] };
a[OsiLayer::Session] = ByteBuffer { buffer[3] };
a[OsiLayer::Application] = ByteBuffer { buffer[4] };
EXPECT_EQ(50, a.size());
ChunkPacket b = a.extract(OsiLayer::Network, OsiLayer::Presentation);
EXPECT_EQ(20, a.size());
EXPECT_EQ(30, b.size());
ByteBuffer tmp;
a[OsiLayer::Link].convert(tmp);
EXPECT_EQ(buffer[0], tmp);
a[OsiLayer::Application].convert(tmp);
EXPECT_EQ(buffer[4], tmp);
b[OsiLayer::Network].convert(tmp);
EXPECT_EQ(buffer[1], tmp);
b[OsiLayer::Transport].convert(tmp);
EXPECT_EQ(buffer[2], tmp);
b[OsiLayer::Session].convert(tmp);
EXPECT_EQ(buffer[3], tmp);
}
TEST(ChunkPacket, merge)
{
std::array<ByteBuffer, 11> buffer;
for (unsigned i = 0; i < buffer.size(); ++i) {
ByteBuffer tmp;
tmp.resize(10, static_cast<ByteBuffer::value_type>(i));
buffer[i] = std::move(tmp);
}
ChunkPacket a;
a[OsiLayer::Physical] = ByteBuffer { buffer[0] };
a[OsiLayer::Link] = ByteBuffer { buffer[1] };
a[OsiLayer::Network] = ByteBuffer { buffer[2] };
a[OsiLayer::Transport] = ByteBuffer { buffer[3] };
a[OsiLayer::Session] = ByteBuffer { buffer[4] };
a[OsiLayer::Application] = ByteBuffer { buffer[5] };
ASSERT_EQ(60, a.size());
ChunkPacket b;
b[OsiLayer::Physical] = ByteBuffer { buffer[6] };
b[OsiLayer::Link] = ByteBuffer { buffer[7] };
b[OsiLayer::Network] = ByteBuffer { buffer[8] };
b[OsiLayer::Transport] = ByteBuffer { buffer[9] };
b[OsiLayer::Application] = ByteBuffer { buffer[10] };
ASSERT_EQ(50, b.size());
a.merge(b, OsiLayer::Link, OsiLayer::Presentation);
EXPECT_EQ(50, a.size());
ByteBuffer tmp;
a[OsiLayer::Physical].convert(tmp);
EXPECT_EQ(buffer[0], tmp);
a[OsiLayer::Link].convert(tmp);
EXPECT_EQ(buffer[7], tmp);
a[OsiLayer::Network].convert(tmp);
EXPECT_EQ(buffer[8], tmp);
a[OsiLayer::Transport].convert(tmp);
EXPECT_EQ(buffer[9], tmp);
a[OsiLayer::Session].convert(tmp);
EXPECT_TRUE(tmp.empty());
a[OsiLayer::Presentation].convert(tmp);
EXPECT_TRUE(tmp.empty());
a[OsiLayer::Application].convert(tmp);
EXPECT_EQ(buffer[5], tmp);
}
@@ -0,0 +1,100 @@
#include <gtest/gtest.h>
#include <vanetza/net/cohesive_packet.hpp>
#include <vanetza/common/byte_buffer.hpp>
using vanetza::ByteBuffer;
using vanetza::CohesivePacket;
using vanetza::OsiLayer;
TEST(CohesivePacket, ctor_and_size)
{
const ByteBuffer buffer(892);
CohesivePacket packet(buffer, OsiLayer::Transport);
EXPECT_EQ(892, packet.size());
EXPECT_EQ(892, packet.size(OsiLayer::Transport));
EXPECT_EQ(0, packet.size(OsiLayer::Network));
EXPECT_EQ(0, packet.size(OsiLayer::Session));
}
TEST(CohesivePacket, set_boundary)
{
CohesivePacket packet(ByteBuffer(512), OsiLayer::Link);
ASSERT_EQ(512, packet.size(OsiLayer::Link));
packet.set_boundary(OsiLayer::Link, 64);
EXPECT_EQ(64, packet.size(OsiLayer::Link));
EXPECT_EQ(512, packet.size());
ASSERT_EQ(448, packet.size(OsiLayer::Network));
packet.set_boundary(OsiLayer::Network, 128);
EXPECT_EQ(128, packet.size(OsiLayer::Network));
ASSERT_EQ(320, packet.size(OsiLayer::Transport));
packet.set_boundary(OsiLayer::Transport, 0);
EXPECT_EQ(0, packet.size(OsiLayer::Transport));
EXPECT_EQ(320, packet.size(OsiLayer::Session));
EXPECT_EQ(512, packet.size());
EXPECT_EQ(448, packet.size(OsiLayer::Network, OsiLayer::Application));
EXPECT_EQ(0, packet.size(OsiLayer::Application, OsiLayer::Network));
}
TEST(CohesivePacket, access)
{
ByteBuffer buffer;
for (unsigned i = 0; i < 512; ++i) {
buffer.push_back(i);
}
CohesivePacket packet(buffer, OsiLayer::Network);
auto net_range = packet[OsiLayer::Network];
ASSERT_EQ(512, net_range.size());
packet.set_boundary(OsiLayer::Network, 128);
auto transport_range = packet[OsiLayer::Transport];
ASSERT_EQ(384, transport_range.size());
ByteBuffer::value_type expected_byte = 128;
for (auto byte : transport_range) {
EXPECT_EQ(expected_byte, byte);
++expected_byte;
}
}
TEST(CohesivePacket, trim_simple_boundaries)
{
CohesivePacket packet(ByteBuffer(128), OsiLayer::Link);
EXPECT_EQ(128, packet.size());
packet.trim(OsiLayer::Physical, 100);
EXPECT_EQ(100, packet.size());
packet.trim(OsiLayer::Physical, 130);
EXPECT_EQ(100, packet.size());
}
TEST(CohesivePacket, trim_advanced_boundaries)
{
CohesivePacket packet(ByteBuffer(128), OsiLayer::Link);
packet.set_boundary(OsiLayer::Link, 12);
packet.set_boundary(OsiLayer::Network, 0);
packet.set_boundary(OsiLayer::Transport, 24);
packet.set_boundary(OsiLayer::Session, 48);
EXPECT_EQ(128, packet.size(OsiLayer::Link, OsiLayer::Presentation));
EXPECT_EQ(0, packet.size(OsiLayer::Physical));
EXPECT_EQ(12, packet.size(OsiLayer::Link));
EXPECT_EQ(0, packet.size(OsiLayer::Network));
EXPECT_EQ(24, packet.size(OsiLayer::Transport));
EXPECT_EQ(48, packet.size(OsiLayer::Session));
EXPECT_EQ(44, packet.size(OsiLayer::Presentation));
EXPECT_EQ(0, packet.size(OsiLayer::Application));
packet.trim(OsiLayer::Network, 24 + 48 + 20);
EXPECT_EQ(24 + 48 + 20, packet.size(OsiLayer::Network, OsiLayer::Application));
EXPECT_EQ(104, packet.size());
packet.trim(OsiLayer::Network, 24 + 8);
EXPECT_EQ(44, packet.size());
EXPECT_EQ(24, packet.size(OsiLayer::Transport));
EXPECT_EQ(8, packet.size(OsiLayer::Session));
EXPECT_EQ(0, packet.size(OsiLayer::Presentation, OsiLayer::Application));
}
@@ -0,0 +1,21 @@
#include <gtest/gtest.h>
#include <vanetza/common/byte_order.hpp>
#include <vanetza/net/ethernet_header.hpp>
#include <vanetza/net/mac_address.hpp>
using namespace vanetza;
TEST(EthernetHeader, create) {
MacAddress dest = { 0x12, 0x34, 0x01, 0x47, 0x43, 0x43 };
MacAddress src = { 0xfa, 0xbc, 0x8d, 0x43, 0xcb, 0x00 };
ByteBuffer header = create_ethernet_header(dest, src, host_cast<uint16_t>(0x3773));
ByteBuffer expected = { 0x12, 0x34, 0x01, 0x47, 0x43, 0x43, 0xfa, 0xbc, 0x8d, 0x43, 0xcb, 0x00, 0x37, 0x73 };
EXPECT_EQ(expected, header);
}
TEST(EthernetHeader, length) {
// magic constants are okay in unit tests
EXPECT_EQ(14, ethernet_header_length());
}
@@ -0,0 +1,96 @@
#include <gtest/gtest.h>
#include <vanetza/net/mac_address.hpp>
#include <sstream>
using namespace vanetza;
TEST(MacAddress, ctor) {
MacAddress empty;
EXPECT_EQ(empty.octets.size(), 6);
for (uint8_t octet : empty.octets) {
EXPECT_EQ(octet, 0x00);
}
MacAddress init = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
EXPECT_EQ(init.octets[0], 0x01);
EXPECT_EQ(init.octets[1], 0x02);
EXPECT_EQ(init.octets[2], 0x03);
EXPECT_EQ(init.octets[3], 0x04);
EXPECT_EQ(init.octets[4], 0x05);
EXPECT_EQ(init.octets[5], 0x06);
}
TEST(MacAddress, equality) {
MacAddress a = { 1, 2, 3, 4, 5, 6 };
MacAddress b = { 1, 2, 3, 4, 5, 7 };
MacAddress c = { 0, 2, 3, 4, 5, 6 };
MacAddress d = { 0, 2, 3, 4, 5, 6 };
EXPECT_NE(a, b);
EXPECT_NE(b, a);
EXPECT_NE(a, c);
EXPECT_NE(b, c);
EXPECT_EQ(a, a);
EXPECT_EQ(c, d);
}
TEST(MacAddress, less) {
MacAddress a = { 1, 2, 3, 4, 5, 6};
MacAddress b = { 1, 2, 4, 4, 5, 6};
MacAddress c = { 250, 0, 0, 0, 0, 0};
MacAddress d = { 0, 2, 3, 4, 5, 6};
MacAddress e = { 1, 2, 3, 4, 5, 5};
EXPECT_LT(a, b);
EXPECT_LT(a, b);
EXPECT_LT(d, e);
EXPECT_LT(e, a);
EXPECT_LT(c, d);
EXPECT_GT(b, a);
}
TEST(MacAddress, ostream) {
MacAddress mac = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
std::stringstream sout;
sout << mac;
EXPECT_EQ(sout.str(), "01:02:03:04:05:06");
}
TEST(MacAddress, parse) {
MacAddress result;
ASSERT_TRUE(!!parse_mac_address("01:02:03:04:05:06", result));
EXPECT_EQ(MacAddress({1, 2, 3, 4, 5, 6}), result);
EXPECT_TRUE(!!parse_mac_address("01:02:03:04:05:06"));
EXPECT_FALSE(!!parse_mac_address(":::::0102040506", result));
EXPECT_FALSE(!!parse_mac_address("foobarfoobarfoob", result));
EXPECT_FALSE(!!parse_mac_address("01:02:03:04:05", result));
EXPECT_FALSE(!!parse_mac_address("01:02:03:04:05:06:07", result));
}
TEST(MacAddress, create) {
MacAddress a = create_mac_address(static_cast<uint32_t>(0x12345678));
EXPECT_EQ(a, MacAddress({0x12, 0x34, 0x56, 0x78, 0x00, 0x00}));
MacAddress b = create_mac_address(static_cast<uint64_t>(0x1234567890abcdef));
EXPECT_EQ(b, MacAddress({0x56, 0x78, 0x90, 0xab, 0xcd, 0xef}));
MacAddress c = create_mac_address(static_cast<uint32_t>(0x1234));
EXPECT_EQ(c, MacAddress({0x00, 0x00, 0x12, 0x34, 0x00, 0x00}));
MacAddress d = create_mac_address(static_cast<uint16_t>(0x2156));
EXPECT_EQ(d, MacAddress({0x21, 0x56, 0x00, 0x00, 0x00, 0x00}));
}
TEST(MacAddress, hash) {
std::hash<MacAddress> hash_fn;
MacAddress a, b;
EXPECT_EQ(hash_fn(a), hash_fn(b));
MacAddress c = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
EXPECT_NE(hash_fn(a), hash_fn(c));
MacAddress d = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
EXPECT_NE(hash_fn(a), hash_fn(d));
}
@@ -0,0 +1,102 @@
#include <gtest/gtest.h>
#include <vanetza/net/osi_layer.hpp>
using namespace vanetza;
TEST(OsiLayer, ordering) {
EXPECT_LT(OsiLayer::Physical, OsiLayer::Link);
EXPECT_LT(OsiLayer::Link, OsiLayer::Network);
EXPECT_LT(OsiLayer::Network, OsiLayer::Transport);
EXPECT_LT(OsiLayer::Transport, OsiLayer::Session);
EXPECT_LT(OsiLayer::Session, OsiLayer::Presentation);
EXPECT_LT(OsiLayer::Presentation, OsiLayer::Application);
EXPECT_EQ(min_osi_layer(), OsiLayer::Physical);
EXPECT_EQ(max_osi_layer(), OsiLayer::Application);
}
TEST(OsiLayer, comparison) {
EXPECT_LT(OsiLayer::Physical, OsiLayer::Link);
EXPECT_GT(OsiLayer::Link, OsiLayer::Physical);
EXPECT_EQ(OsiLayer::Physical, OsiLayer::Physical);
EXPECT_NE(OsiLayer::Physical, OsiLayer::Link);
EXPECT_LE(OsiLayer::Physical, OsiLayer::Physical);
EXPECT_GE(OsiLayer::Link, OsiLayer::Link);
}
TEST(OsiLayer, list) {
auto list = osi_layers;
ASSERT_EQ(list.size(), 7);
auto it = list.begin();
EXPECT_EQ(OsiLayer::Physical, *it++);
EXPECT_EQ(OsiLayer::Link, *it++);
EXPECT_EQ(OsiLayer::Network, *it++);
EXPECT_EQ(OsiLayer::Transport, *it++);
EXPECT_EQ(OsiLayer::Session, *it++);
EXPECT_EQ(OsiLayer::Presentation, *it++);
EXPECT_EQ(OsiLayer::Application, *it++);
EXPECT_EQ(list.end(), it);
auto prev = list.begin();
for (it = prev + 1; it != list.end(); prev = it, ++it) {
EXPECT_LT(*prev, *it);
}
}
TEST(OsiLayer, num) {
EXPECT_EQ(7, num_osi_layers(min_osi_layer(), max_osi_layer()));
EXPECT_EQ(1, num_osi_layers(OsiLayer::Link, OsiLayer::Link));
EXPECT_EQ(2, num_osi_layers(OsiLayer::Link, OsiLayer::Network));
EXPECT_EQ(0, num_osi_layers(OsiLayer::Network, OsiLayer::Link));
}
TEST(OsiLayer, distance) {
EXPECT_EQ(0, distance(OsiLayer::Session, OsiLayer::Session));
EXPECT_EQ(1, distance(OsiLayer::Network, OsiLayer::Transport));
EXPECT_EQ(-1, distance(OsiLayer::Transport, OsiLayer::Network));
EXPECT_EQ(num_osi_layers(min_osi_layer(), max_osi_layer()) - 1,
distance(min_osi_layer(), max_osi_layer()));
}
TEST(OsiLayer, compile_time_range) {
const auto r1_ref = osi_layers;
ASSERT_EQ(7, r1_ref.size());
auto r1 = osi_layer_range<min_osi_layer(), max_osi_layer()>();
EXPECT_EQ(r1_ref.size(), r1.size());
for (unsigned i = 0; i < r1.size(); ++i) {
EXPECT_EQ(r1_ref[i], r1[i]);
}
auto r2 = osi_layer_range<OsiLayer::Link, OsiLayer::Transport>();
ASSERT_EQ(3, r2.size());
EXPECT_EQ(OsiLayer::Link, r2[0]);
EXPECT_EQ(OsiLayer::Network, r2[1]);
EXPECT_EQ(OsiLayer::Transport, r2[2]);
auto r3 = osi_layer_range<OsiLayer::Application, OsiLayer::Application>();
EXPECT_EQ(1, r3.size());
}
TEST(OsiLayer, run_time_range) {
const auto r1_ref = osi_layers;
ASSERT_EQ(7, r1_ref.size());
auto r1 = osi_layer_range(min_osi_layer(), max_osi_layer());
EXPECT_EQ(r1_ref.size(), r1.size());
for (unsigned i = 0; i < r1.size(); ++i) {
EXPECT_EQ(r1_ref[i], r1[i]);
}
auto r2 = osi_layer_range(OsiLayer::Link, OsiLayer::Transport);
ASSERT_EQ(3, r2.size());
EXPECT_EQ(OsiLayer::Link, r2[0]);
EXPECT_EQ(OsiLayer::Network, r2[1]);
EXPECT_EQ(OsiLayer::Transport, r2[2]);
auto r3 = osi_layer_range(OsiLayer::Application, OsiLayer::Application);
ASSERT_EQ(1, r3.size());
EXPECT_EQ(OsiLayer::Application, r3[0]);
auto r4 = osi_layer_range(OsiLayer::Application, OsiLayer::Transport);
EXPECT_EQ(0, r4.size());
}