llarp/net/ip_packet.hpp

Namespaces

Name
llarp
[crypto.hpp]
llarp::net

Classes

Name
struct llarp::net::ip_header_le
struct llarp::net::ip_header_be
struct llarp::net::ipv6_header_preamble_le
struct llarp::net::ipv6_header_preamble_be
struct llarp::net::ipv6_header
struct llarp::net::IPPacket
an Packet
struct llarp::net::IPPacket::CompareSize
struct llarp::net::IPPacket::CompareOrder

Source code

#pragma once

#include <oxenc/endian.h>
#include <cstdint>
#include <llarp/ev/ev.hpp>
#include "net_int.hpp"
#include "net.hpp"
#include <llarp/util/buffer.hpp>
#include <llarp/util/time.hpp>
#include <memory>
#include <llarp/service/protocol_type.hpp>
#include <stdexcept>
#include <utility>

namespace llarp::net
{
  struct ip_header_le
  {
    uint8_t ihl : 4;
    uint8_t version : 4;
    uint8_t tos;
    uint16_t tot_len;
    uint16_t id;
    uint16_t frag_off;
    uint8_t ttl;
    uint8_t protocol;
    uint16_t check;
    uint32_t saddr;
    uint32_t daddr;
  };

  struct ip_header_be
  {
    uint8_t version : 4;
    uint8_t ihl : 4;
    uint8_t tos;
    uint16_t tot_len;
    uint16_t id;
    uint16_t frag_off;
    uint8_t ttl;
    uint8_t protocol;
    uint16_t check;
    uint32_t saddr;
    uint32_t daddr;
  };

  using ip_header = std::conditional_t<oxenc::little_endian, ip_header_le, ip_header_be>;

  static_assert(sizeof(ip_header) == 20);

  struct ipv6_header_preamble_le
  {
    unsigned char pad_small : 4;
    unsigned char version : 4;
    uint8_t pad[3];
  };

  struct ipv6_header_preamble_be
  {
    unsigned char version : 4;
    unsigned char pad_small : 4;
    uint8_t pad[3];
  };

  using ipv6_header_preamble =
      std::conditional_t<oxenc::little_endian, ipv6_header_preamble_le, ipv6_header_preamble_be>;

  static_assert(sizeof(ipv6_header_preamble) == 4);

  struct ipv6_header
  {
    union
    {
      ipv6_header_preamble preamble;
      uint32_t flowlabel;
    } preamble;

    uint16_t payload_len;
    uint8_t protocol;
    uint8_t hoplimit;
    in6_addr srcaddr;
    in6_addr dstaddr;
    llarp::nuint32_t
    FlowLabel() const;

    void
    FlowLabel(llarp::nuint32_t label);
  };

  static_assert(sizeof(ipv6_header) == 40);

  enum class IPProtocol : uint8_t
  {
    ICMP = 0x01,
    IGMP = 0x02,
    IPIP = 0x04,
    TCP = 0x06,
    UDP = 0x11,
    GRE = 0x2F,
    ICMP6 = 0x3A,
    OSFP = 0x59,
    PGM = 0x71,
  };

  std::string
  IPProtocolName(IPProtocol proto);

  IPProtocol
  ParseIPProtocol(std::string data);

  struct IPPacket
  {
    static constexpr size_t _max_size = 1500;
    llarp_time_t timestamp;
    std::vector<byte_t> _buf;

   public:
    IPPacket() : IPPacket{size_t{}}
    {}
    explicit IPPacket(size_t sz);
    explicit IPPacket(byte_view_t);
    IPPacket(std::vector<byte_t>&&);

    ~IPPacket() = default;

    static constexpr size_t MaxSize = _max_size;
    static constexpr size_t MinSize = 20;

    [[deprecated("deprecated because of llarp_buffer_t")]] static IPPacket
    UDP(nuint32_t srcaddr,
        nuint16_t srcport,
        nuint32_t dstaddr,
        nuint16_t dstport,
        const llarp_buffer_t& data)
    {
      return make_udp(srcaddr, srcport, dstaddr, dstport, data.copy());
    }

    static IPPacket
    make_udp(
        net::ipaddr_t srcaddr,
        net::port_t srcport,
        net::ipaddr_t dstaddr,
        net::port_t dstport,
        std::vector<byte_t> udp_body);

    static inline IPPacket
    make_udp(SockAddr src, SockAddr dst, std::variant<OwnedBuffer, std::vector<byte_t>> udp_body)
    {
      if (auto* vec = std::get_if<std::vector<byte_t>>(&udp_body))
        return make_udp(src.getIP(), src.port(), dst.getIP(), dst.port(), std::move(*vec));
      else if (auto* buf = std::get_if<OwnedBuffer>(&udp_body))
        return make_udp(src, dst, buf->copy());
      else
        return net::IPPacket{size_t{}};
    }

    static IPPacket
    make_icmp_reply(const net::IPPacket& pkt);

    [[deprecated("deprecated because of llarp_buffer_t")]] inline bool
    Load(const llarp_buffer_t& buf)
    {
      _buf = buf.copy();
      if (size() >= MinSize)
        return true;
      _buf.resize(0);
      return false;
    }

    [[deprecated("deprecated because of llarp_buffer_t")]] inline llarp_buffer_t
    ConstBuffer() const
    {
      return llarp_buffer_t{_buf};
    }

    inline std::vector<byte_t>
    steal()
    {
      std::vector<byte_t> buf;
      buf.resize(0);
      std::swap(_buf, buf);
      return buf;
    }

    inline void
    truncate(size_t sz)
    {
      _buf.resize(sz);
    }

    inline byte_t*
    data()
    {
      return _buf.data();
    }

    inline const byte_t*
    data() const
    {
      return _buf.data();
    }

    constexpr size_t
    capacity() const
    {
      return _max_size;
    }

    inline size_t
    size() const
    {
      return _buf.size();
    }

    inline bool
    empty() const
    {
      return _buf.empty();
    }

    byte_view_t
    view() const;

    inline size_t
    payload_offset() const
    {
      size_t offset{};
      if (IsV4())
      {
        offset = size_t{Header()->ihl} * 4;
      }
      else if (IsV6())
      {
        offset = 40;
      }
      return offset;
    }

    inline const byte_t&
    icmp_type() const
    {
      return view()[payload_offset()];
    }

    inline const byte_t&
    icmp_code() const
    {
      return view()[payload_offset() + 1];
    }

    inline byte_t&
    icmp_type()
    {
      return _buf[payload_offset()];
    }

    inline byte_t&
    icmp_code()
    {
      return _buf[payload_offset() + 1];
    }

    inline const uint16_t&
    icmp_checksum() const
    {
      const byte_t* ptr = view().data();
      ptr += payload_offset() + 2;
      return *reinterpret_cast<const uint16_t*>(ptr);
    }

    inline uint16_t*
    icmp_checksum()
    {
      byte_t* ptr = data();
      ptr += payload_offset() + 2;
      return reinterpret_cast<uint16_t*>(ptr);
    }

    struct CompareSize
    {
      bool
      operator()(const IPPacket& left, const IPPacket& right)
      {
        return left.size() < right.size();
      }
    };

    struct CompareOrder
    {
      bool
      operator()(const IPPacket& left, const IPPacket& right)
      {
        return left.timestamp < right.timestamp;
      }
    };

    inline ip_header*
    Header()
    {
      return reinterpret_cast<ip_header*>(data());
    }

    inline const ip_header*
    Header() const
    {
      return reinterpret_cast<const ip_header*>(data());
    }

    inline ipv6_header*
    HeaderV6()
    {
      return reinterpret_cast<ipv6_header*>(data());
    }

    inline const ipv6_header*
    HeaderV6() const
    {
      return reinterpret_cast<const ipv6_header*>(data());
    }

    inline int
    Version() const
    {
      return Header()->version;
    }

    inline byte_t
    protocol() const
    {
      if (IsV4())
        return Header()->protocol;
      else
        return HeaderV6()->protocol;
    }

    inline bool
    IsV4() const
    {
      return Version() == 4;
    }

    inline bool
    IsV6() const
    {
      return Version() == 6;
    }

    inline service::ProtocolType
    ServiceProtocol() const
    {
      if (IsV4())
        return service::ProtocolType::TrafficV4;
      if (IsV6())
        return service::ProtocolType::TrafficV6;

      return service::ProtocolType::Control;
    }

    inline int
    AF() const
    {
      if (IsV4())
        return AF_INET;
      if (IsV6())
        return AF_INET6;
      return AF_UNSPEC;
    }

    net::ipv6addr_t
    srcv6() const;

    net::ipv6addr_t
    dstv6() const;

    net::ipv4addr_t
    srcv4() const;

    net::ipv4addr_t
    dstv4() const;

    net::ipv6addr_t
    src4to6() const;

    net::ipv6addr_t
    dst4to6() const;

    net::ipv6addr_t
    src4to6Lan() const;

    net::ipv6addr_t
    dst4to6Lan() const;

    inline net::ipaddr_t
    srcaddr() const
    {
      if (IsV4())
        return srcv4();
      else if (IsV6())
        return srcv6();
      else
        throw std::runtime_error{"invalid ip packet"};
    }

    inline net::ipaddr_t
    dstaddr() const
    {
      if (IsV4())
        return dstv4();
      else if (IsV6())
        return dstv6();
      else
        throw std::runtime_error{"invalid ip packet"};
    }

    SockAddr
    src() const;

    SockAddr
    dst() const;

    std::optional<nuint16_t>
    DstPort() const;

    std::optional<nuint16_t>
    SrcPort() const;

    std::optional<std::pair<const char*, size_t>>
    L4Data() const;

    inline std::optional<OwnedBuffer>
    L4OwnedBuffer() const
    {
      if (auto data = L4Data())
        return OwnedBuffer{reinterpret_cast<const byte_t*>(data->first), data->second};
      return std::nullopt;
    }

    void
    UpdateIPv4Address(nuint32_t src, nuint32_t dst);

    void
    UpdateIPv6Address(
        net::ipv6addr_t src,
        net::ipv6addr_t dst,
        std::optional<net::flowlabel_t> flowlabel = std::nullopt);

    void
    ZeroAddresses(std::optional<net::flowlabel_t> flowlabel = std::nullopt);

    void
    ZeroSourceAddress(std::optional<net::flowlabel_t> flowlabel = std::nullopt);

    std::optional<IPPacket>
    MakeICMPUnreachable() const;

    std::function<void(net::IPPacket)> reply;
  };

  uint16_t
  ipchksum(const byte_t* buf, size_t sz, uint32_t sum = 0);

}  // namespace llarp::net

Updated on 2026-01-10 at 22:49:45 +0000