Module: OpenASN::BinaryFormat

Defined in:
lib/openasn/binary_format.rb

Overview

Reader for the OASN v1 artifact format and the OORG v1 org-names sidecar. The byte-exact spec lives in the data repo: https://github.com/openasn/openasn/blob/main/FORMAT.md — this file is an independent implementation of that document and must never drift from it. All integers big-endian. Readers REJECT unknown format versions on purpose: half-understanding an artifact is worse than keeping last-good data.

Defined Under Namespace

Modules: BaseLayer, OverlayLayer Classes: ArraysBase, ArraysOverlay, OrgIndex, PackedBase, PackedOverlay

Constant Summary collapse

MAGIC =
"OASN"
ORG_MAGIC =
"OORG"
FORMAT_VERSION =
0x01
HEADER_SIZE =
32
ORG_HEADER_SIZE =
16
FLAG_BAD_ASN =

flags (u16) bit layout — see FORMAT.md

1 << 8
FLAG_VPN_PROVIDER =
1 << 9
FLAG_MOBILE =
1 << 10
FLAG_ENTERPRISE_GW =
1 << 11
FLAG_CDN =
1 << 12
FLAG_HOSTING_EXTRA =
1 << 13
CATEGORY_MASK =
0x000F
ROLE_SHIFT =
4
ROLE_MASK =
0x00F0
CATEGORIES =
[nil, "isp", "hosting", "business", "education_research", "government_admin"].freeze
ROLES =
[nil, "tier1_transit", "major_transit", "midsize_transit",
"access_provider", "content_network", "stub"].freeze
FLAG_NAMES =
{
  FLAG_BAD_ASN => :bad_asn,
  FLAG_VPN_PROVIDER => :vpn_provider,
  FLAG_MOBILE => :mobile_carrier,
  FLAG_ENTERPRISE_GW => :enterprise_gw,
  FLAG_CDN => :cdn,
  FLAG_HOSTING_EXTRA => :hosting_extra
}.freeze

Class Method Summary collapse

Class Method Details

.addr_size(family) ⇒ Object



52
# File 'lib/openasn/binary_format.rb', line 52

def addr_size(family)        = family == :ipv4 ? 4 : 16

.base_rec_size(family) ⇒ Object



53
# File 'lib/openasn/binary_format.rb', line 53

def base_rec_size(family)    = family == :ipv4 ? 14 : 38

.category_name(flags) ⇒ Object



45
# File 'lib/openasn/binary_format.rb', line 45

def category_name(flags) = CATEGORIES[flags & CATEGORY_MASK]

.flag_names(flags) ⇒ Object



48
49
50
# File 'lib/openasn/binary_format.rb', line 48

def flag_names(flags)
  FLAG_NAMES.filter_map { |bit, name| name if flags.anybits?(bit) }
end

.overlay_rec_size(family) ⇒ Object



54
# File 'lib/openasn/binary_format.rb', line 54

def overlay_rec_size(family) = family == :ipv4 ? 8 : 32

.pack_addr(int, family) ⇒ Object



56
57
58
59
60
61
62
# File 'lib/openasn/binary_format.rb', line 56

def pack_addr(int, family)
  if family == :ipv4
    [int].pack("N")
  else
    [int >> 64, int & 0xFFFF_FFFF_FFFF_FFFF].pack("Q>Q>")
  end
end

.parse_artifact(bytes, mode: :packed) ⇒ Object

Parse a full OASN artifact into its layers. -> { family:, build_ts:, base: BaseLayer, vpn: OverlayLayer, dc: OverlayLayer, relay: OverlayLayer }

Raises:



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/openasn/binary_format.rb', line 66

def parse_artifact(bytes, mode: :packed)
  raise FormatError, "not an OASN artifact (bad magic)" unless bytes[0, 4] == MAGIC

  version = bytes.getbyte(4)
  raise FormatError, "unsupported OASN format_version #{version} (this gem speaks v#{FORMAT_VERSION}); update the openasn gem" unless version == FORMAT_VERSION

  family = bytes.getbyte(5) == 0x04 ? :ipv4 : :ipv6
  build_ts = bytes[8, 8].unpack1("Q>")
  base_n, vpn_n, dc_n, relay_n = bytes[16, 16].unpack("NNNN")

  brec = base_rec_size(family)
  orec = overlay_rec_size(family)
  expected = HEADER_SIZE + base_n * brec + (vpn_n + dc_n + relay_n) * orec
  raise FormatError, "artifact truncated or padded: #{bytes.bytesize} bytes, header implies #{expected}" unless bytes.bytesize == expected

  offset = HEADER_SIZE
  base  = bytes[offset, base_n * brec];  offset += base_n * brec
  vpn   = bytes[offset, vpn_n * orec];   offset += vpn_n * orec
  dc    = bytes[offset, dc_n * orec];    offset += dc_n * orec
  relay = bytes[offset, relay_n * orec]

  {
    family: family, build_ts: build_ts,
    base: BaseLayer.build(base, family, mode),
    vpn: OverlayLayer.build(vpn, family, mode),
    dc: OverlayLayer.build(dc, family, mode),
    relay: OverlayLayer.build(relay, family, mode)
  }
end

.role_name(flags) ⇒ Object



46
# File 'lib/openasn/binary_format.rb', line 46

def role_name(flags)     = ROLES[(flags & ROLE_MASK) >> ROLE_SHIFT]