////
 AnnNet Specification © 2025 by Brent Saner is licensed under Creative Commons Attribution-ShareAlike 4.0 International. To view a copy of this license, visit https://creativecommons.org/licenses/by-sa/4.0/
////

= AnnNet Specification
Brent Saner <bts@square-r00t.net>
Last rendered {localdatetime}
:doctype: book
:docinfo: shared
:data-uri:
:imagesdir: images
:sectlinks:
:sectnums:
:sectnumlevels: 7
:toc: preamble
:toc2: left
:idprefix:
:toclevels: 7
//:toclevels: 4
:source-highlighter: rouge
:docinfo: shared
:allmeta: _meta
:allrules: _rules

[id="license"]
== License

++++
include::LICENSE.html[]
++++

In a nutshell, this means any may:

* Use it in commercial/proprietary/internal works...
* Expand upon/change the specification...
** (As long as it is released under the same Creative Commons license)

As long as you attribute the original (this document). This can be as simple as something like:

====
Based on AnnNet version <protocol version> as found at https://annnet.io/.
====

More details certainly helps, though; you may want to mention the exact date you "forked" it, etc.

Please see the full text as collapsed above or https://creativecommons.org/licenses/by-sa/4.0/legalcode.en[the online version^] of the license for full legal copy.

NOTE: In the event of the embedded text in this document differing from the online version, the online version is assumed to take precedence as the valid license applicable to this work.

[id="lib"]
== Reference Library
The AnnNet Protocol is accompanied by a reference library for Golang, https://pkg.go.dev/r00t2.io/annnet["AnnNet"^] (https://git.r00t2.io/r00t2/go_annnet[_source_^]):

++++
<a href="https://pkg.go.dev/go.pkg.dev/r00t2.io/annnet">
    <img src="https://pkg.go.dev/badge/go.pkg.dev/r00t2.io/annnet.svg"
         alt="Go Reference">
</a>
<br />
<br />
++++

It contains a reference "client" (listener), https://pkg.go.dev/r00t2.io/annnet/annie[`annie`^] (https://git.r00t2.io/r00t2/go_annnet/src/branch/master/cmd/annie[_source_^]), and a reference "server" (announcer), https://pkg.go.dev/r00t2.io/annnet/anna[`anna`^] (https://git.r00t2.io/r00t2/go_annnet/src/branch/master/cmd/annie[_source_^]).

Additional reference libraries may be available in the future.

[id="disclaimer"]
== Disclaimer

[id="disc_proto"]
=== The AnnNet Protocol
This specification is provided as-is with no guarantees or obligations from this specification's author(s).

The author(s) is/are not held liable for any damages caused by implementation/use of this protocol.

The AnnNet specification is subject to change, but changes are not considered definitive nor effective until/unless tied with/to a specific released version.

[id="disc_ref"]
=== References
Many outside references are made throughout this document.

* The author(s) is/are not sponsored by, contribute to, nor condone any of the resources mentioned unless otherwise specified.
* The author(s) is/are not sponsored by, nor member(s),contributor(s), or sponsor of, nor condone any organizations or other entities listed or their actions unless otherwise specified.

References are suggested as-is with no guarantees or obligations from this specification's author(s).

[id="proto"]
== Protocol
The AnnNet (pronounced like the woman's name "Annette") protocol is an announcement/query protocol designed for unspecified hosts on a local network. It is a shorthand for __"the **Ann**ouncement **Net**work Protocol"__.

AnnNet is designed to operate with or without IP addressing being available, as it can operate on layer 2, layer 3, or both to unknown/unspecified destinations.

It is intended as a sort of "announcement" mechanism for sending messages to all hosts on a *locally-linked* (or *single-organization-controlled*) network (such as bootstrapping large-scale layer 3 services/clusters). It was designed with service/implementation-specific host discovery in mind, though other uses are possible.

It is recommended that it is only used *sparingly* for *small* messages (such as cluster *bootstrapping* for new members or initial cluster assembly). _Operational_ messages should occur on a well-defined transport layer (e.g. TCP). +
It is important to avoid the temptation of simply implementing ALL of a service's communication via AnnNet, as this can quite easily overwhelm the physical network's limitations as hosts participating in its communication grows. See the FAQ/FUQ entry <<fq_proto>> for suggestions on how to manage actual service communication instead.

[NOTE]
====
The https://en.wikipedia.org/wiki/OSI_model[OSI (Open Systems Interconnection) model^] is going to be referenced a fair bit in this document.

The linked Wikipedia article is a fantastic primer on it with a plethora of useful links, but if you'd like to read the authoritative material that'd be https://www.iso.org/standard/20269.html[ISO/IEC 7498-1:1994^].

ISO/IEC 7498-1:1994 is one of the free-as-in-beer-for-digital-copy ISO/IEC standards available, provided you have an ISO account -- which is also free to create and use. You can use the _Register_ link at the bottom of https://www.iso.org/webstore/login[ISO's Webstore login^] to create a new purchasing account.

[[tcpip_guide]]Another fantastic reference for the OSI model (and all things networking) is http://www.tcpipguide.com/[the TCP/IP Guide^] by Charles M. Kozierok. __(I am not sponsored by nor condone the material found in that re)__

It is available:

* In its entirety for free (-as-in-beer) http://www.tcpipguide.com/free/index.htm[online^] (if you don't mind the large amount of ads)
** Note that the online version may not map perfectly to the print/PDF versions, and may have different wording.
** I (Brent) highly recommend supporting the author by either using the donation links at the bottom of that page or purchasing/licensing a donwloaded PDF or print copy, see below.
* By purchasing a PDF download http://www.tcpipguide.com/la.htm[directly from the author^]
** Which also allows for http://www.tcpipguide.com/pricing.htm[bulk licensing^] of the e-book for orginaztional use.
* By purchasing a print and/or PDF copy https://nostarch.com/tcpip.htm[from No Starch Press^]
**  As expected with all other No Starch Press book purchases, a print copy comes with a free digital/e-book copy.

I (Brent) have a physical copy from No Starch Press. I find myself referencing the PDF much more often, as it has some very useful document linking within it, but moreso because the print copy is a *behemoth* and is very bulky for anything but a bookshelf and seated-at-a-table reading/research!

The OSI model is covered in the *TCP/IP Guide* in _Part I-2_ (chapters *5* to *7* inclusive), starting on _p.79_. +
If using the online version, _Part I-2_ is summarized/starts http://www.tcpipguide.com/free/t_TheOpenSystemInterconnectionOSIReferenceModel.htm[here^].
====

[id="spec"]
== Specification
AnnNet supports two "sub-protocols":

* <<spec_link>>
* <<spec_bcast>>

Both make use of the <<spec_msg_pfx>>, though in different places of the frame/packet depending on their implementation. See each sub-protocol below for details.

[id="spec_msg"]
=== AnnNet Message Format
The general format of an AnnNet message (excluding leading/surrounding/trailing frames, headers, etc.) is:

.AnnNet Message
[cols="^.^1s,^.^2s"]
|===
| <<spec_msg_pfx>> (5 Bytes) | <<spec_msg_payload>>
|===

[id="spec_msg_pfx"]
==== Standard Prefix
AnnNet defines a specific prefix that must be added before a message. While AnnNet does not dictate the contents of the *payload* in any way, all AnnNet messages must contain this prefix.

In <<spec_link>>, it immediately follows the EtherType value.

In <<spec_bcast>>, it immediately follows the IPv4/IPv6 header.

In its current form, this is:

.AnnNet Prefix
[cols="^.^1s,^.^1s,^.^1s,^.^1s,^.^1s"]
|===
4+| <<spec_msg_pfx_ver>> |
| <<spec_msg_pfx_ver_maj>> | <<spec_msg_pfx_ver_min>> | <<spec_msg_pfx_ver_patch>> | <<spec_msg_pfx_ver_flags>> | <<spec_msg_pfx_len>>
|===

[id="spec_msg_pfx_ver"]
===== Version

[cols="^.^1m,^.^3",options="header"]
|===
| Length (Octets/Bytes) | Type
| 4 | Big-Endian Unsigned 32-Bit Integer/Container
|===

The *Version* field matches the version of AnnNet being used.

Because AnnNet follows https://semver.org/[Semantic Versioning^] (v2.0.0) for its specification, this can either be treated as an abstract container (hereafter a "byte-slice version" in this spec) of individual bytes indicating each version sub-field or treated as a whole (hereafter a "single-value version" in this spec).

Thus for example, when determing how to parse the following fields from an AnnNet message if incompatible structuring changes are introduced, one can either:

* Perform a version comparison sequentially (e.g. via first parsing and comparing the <<spec_msg_pfx_ver_maj>>, and then moving on to the <<spec_msg_pfx_ver_min>>, and so forth) for a byte-slice version
* Or for a single-value version, a direct integer comparison -- for example, if a single-value *Version* is greater than or equal to `16777216` to check if it is greater than or equal to version `1.0.0` of AnnNet (`16777216` being equal to `0x01`, `0x00`, `0x00`, `0x00`, or more properly `0x01000000`, when big-endian decoded).

[TIP]
====
Bit-shifting can be used to extract individual version fields from a single-value version.

[%collapsible]
.Example in Go
=====
.`examples/extractversion.go`
[source,go]
----
include::examples/extractversion.go[]
----
=====
====

[id="spec_msg_pfx_ver_maj"]
====== Major

[cols="^.^1m,^.^3",options="header"]
|===
| Length (Octets/Bytes) | Type
| 1 | Unsigned 8-Bit Integer
|===

The *Major* is the first byte in *Version* (if interpreted as a byte slice and not a big-endian unsigned 32-bit integer).

[id="spec_msg_pfx_ver_min"]
====== Minor

[cols="^.^1m,^.^3",options="header"]
|===
| Length (Octets/Bytes) | Type
| 1 | Unsigned 8-Bit Integer
|===

The *Minor* is the second byte in *Version* (if interpreted as a byte slice and not a big-endian unsigned 32-bit integer).

[id="spec_msg_pfx_ver_patch"]
====== Patch

[cols="^.^1m,^.^3",options="header"]
|===
| Length (Octets/Bytes) | Type
| 1 | Unsigned 8-Bit Integer
|===

The *Patch* is the third byte in *Version* (if interpreted as a byte slice and not a big-endian unsigned 32-bit integer).

[NOTE]
====
The "patch" in SemVer may be more accurate to think of it as a "revision" than a "patch", but the term "revision" means something else in Semantic Version specification so "patch" is used here instead.
====

[id="spec_msg_pfx_ver_flags"]
====== Release Flag(s)

[cols="^.^1m,^.^3",options="header"]
|===
| Length (Octets/Bytes) | Type
| 1 | Byte/8-Bit Length Bitmask
|===

The *Release Flag(s)* field is the fourth byte in *Version*. It is not intended as an unsigned 8-bit integer but rather a https://en.wikipedia.org/wiki/Mask_(computing)[bitmasked^] byte.

The collection of all flags is coalesced into a single byte on the wire -- an unsigned 8-bit integer -- with each flag occupying a single bit.

The following flags are currently defined:

.AnnNet Flags
[cols="^.^1m,.^1m,^.^2",options="header"]
|===
| Integer ^.^| Flag | Name

| 0 | ANET_NONE | None; stable release.
| 1 | ANET_PREREL | Pre-release
| 2 | ANET_UNSTABLE | Unstable
| 4 | ANET_EXPERIMENT | Experimental
|===

Additionally, several "shorthand" flags are explicitly defined by the bit-wise ``OR``'d combination of other flags:

.AnnNet Combined Flags
[cols="^.^1m,.^1m,^.^3m,^.^2",options="header"]
|===
| Integer ^.^| Flag ^.^| Expression | Name

| 3 | ANET_BETA | ANET_PREREL \| ANET_UNSTABLE | Beta release
| 7 | ANET_ALPHA | ANET_BETA \| ANET_EXPERIMENT | Alpha release
|===

A flag's presence can be checked via a bit-wise `AND` against _flag_ being equal to _flag_.

[%collapsible]
.Example in Go
=====
.`examples/hasflag.go`
[source,go]
----
include::examples/hasflag.go[]
----
=====

[id="spec_msg_pfx_len"]
===== Payload Length

[cols="^.^1m,^.^3",options="header"]
|===
| Length (Octets/Bytes) | Type
| 4 | Big-Endian Unsigned 32-Bit Integer
|===

The *Payload Length* indicates the length of the <<spec_msg_payload>>, which is the arbitrary/implementation-specific data that follows the <<spec_msg_pfx>> (and thus the Payload Length is the last field in the Prefix).

It *must* be the length of the entirety of the payload that follows (but *not* including the size of the Prefix).

[NOTE]
====
`0` is a valid Payload Length, as is an empty <<spec_msg_payload>>. However, if Payload Length is specified as `0` but there *is* Payload data, that data must be considered *invalid* by listening implementations and _should not_ be read/__should be__ discarded (with the exception of *validation, testing, or abuse mitigation purposes*).
====

[id="spec_msg_payload"]
==== Payload
The *Payload* is the arbitrary data that follows the <<spec_msg_pfx>> in the <<spec_msg>>.

It is an arbitrary length (specified/bounded by <<spec_msg_pfx_len>>) of payload data.

[id="spec_link"]
=== AnnNet Link
*AnnNet Link* operates on OSI layer 2 as an https://en.wikipedia.org/wiki/EtherType[EtherType^] protocol.

[WARNING]
====
For obvious reasons, this is not going to play well with other EtherTypes (e.g. VLAN/802.1Q tagging).

Future versions of AnnNet may implement VLAN support.

If VLAN support is added, it will be done so in a dedicated major release.
====

It _may_ send to either a directed MAC/physical address of a host *or* to the broadcast MAC/PHY__(sical)__ address `ff:ff:ff:ff:ff:ff`.
Its source _must_ be the MAC/PHYS address of the network interface it is sending from.

It *must not* traverse physical network boundaries (can't, really, since it's layer 2).

It currently uses the `0x88b5` EtherType by default, as AnnNet is still in experimental stages and this EtherType is https://standards-oui.ieee.org/ethertype/eth.txt[reserved for prototyping^] per IEEE Std 802. (`0x88b6` is another viable option for your implementation, as it too is reserved by IEEE Std 802 for prototyping.) See also https://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml#ieee-802-numbers-1[IANA's registration list^].

If AnnNet is allocated a dedicated IEEE/IANA-registered EtherType, it will be implemented in a new major version release.

The <<spec_msg, AnnNet Message>> follows the EtherType value immediately:

.AnnNet Link Packet
[cols="^.^1s,^.^1s,^.^1s,^.^2s"]
|===
3+| Ethernet II Header (14 Bytes) |
| E:D | E:S | E:T | <<spec_msg, AnnNet Message>>
|===

Where:

E:D:: Destination MAC/PHY__(sical)__ Address _(6 Bytes)_
E:S:: Source MAC/PHY__(sical)__ Address _(6 Bytes)_
E:T:: EtherType (`0x88b5` or `0x88b6`) _(2 Bytes)_

[id="spec_bcast"]
=== AnnNet Broadcast
AnnNet Broadcast operates on layer 3, making use of <<spec_bcast_v4>> (EtherType `0x0800`) and/or <<spec_bcast_v6>> (EtherType `0x86dd`).

[id="spec_bcast_v4"]
==== IPv4
If IPv4, the AnnNet Broadcast sender _may_ send to:

* The IPv4 limited broadcast address `255.255.255.255/32` (RFC https://datatracker.ietf.org/doc/html/rfc6890#section-2.2.2[6890 § 2.2.2^], https://datatracker.ietf.org/doc/html/rfc8190#section-2.2[8190 § 2.2^])
* Or a direct address of a host, IPv4 link-local (also known as __Automatic Private IP Addressing/APIPA__, https://datatracker.ietf.org/doc/html/rfc3927[RFC 3927^]) or not.
** The target host/network *must* not traverse organization boundary (e.g. *must not* be routed directly across the Internet).

If using the `255.255.255.255/32` broadcast address, the source address *must* be a network-reachable address of the interface the message is sending from.

If using any other address, the source address *must* be an address reachable by the network/host the message is being sent to.

[id="spec_bcast_v4_msg"]
===== Message Format

The <<spec_msg, AnnNet Message>> follows the Options value immediately:

.AnnNet Broadcast Packet, IPv4
[cols="^.^1s,^.^1s,^.^1s,^.^1s,^.^1s,^.^1s,^.^1s,^.^1s,^.^1s,^.^1s,^.^1s,^.^1s,^.^1s,^.^1s,^.^1s,^.^1s,^.^1s,^.^2s"]
|===
3+| Ethernet II Header +
(>= 14 Bytes) 14+| IP Header (20-60 Bytes) |
3+| 2+| V/IHL +
(1 Byte) 2+| D/E +
(1 Byte) | | 2+| Fragmentation +
(2 Bytes) | | | | | | |

| E:D | E:S | E:T | VI:V | VI:I | DE:D | DE:E | IP:L | IP:I | IP:F | IP:R | IP:T | IP:P | IP:C | IP:S | IP:D | IP:O | <<spec_msg, AnnNet Message>>
|===

Where:

E:D:: Destination MAC/PHY__(sical)__ Address _(6 Bytes)_
E:S:: Source MAC/PHY__(sical)__ Address _(6 Bytes)_
E:T:: EtherType (`0x86dd`) _(>= 2 Bytes; may be prefixed with VLAN information etc.)_
VI:V:: Version _(4 Bits; see <<xtra_bitpacked_vihl,Addendum>>)_
VI:I:: Internet Header Length _(4 Bits; see <<xtra_bitpacked_vihl,Addendum>>)_
DE:D:: DSCP (Differentiated Services Code Point; QoS/traffic class) _(6 Bits; see <<xtra_bitpacked_de,Addendum>>)_
DE:E:: ECN (Explicit Congestion Notification) _(2 Bits; see <<xtra_bitpacked_de,Addendum>>)_
IP:L:: Total size of IP header + payload (not to be confused with <<spec_msg_pfx_len>>) _(2 Bytes)_
IP:I:: Identification (used in fragmentation) _(2 Bytes)_
IP:F:: IP Flags _(3 bits; see <<xtra_bitpacked_frag,Addendum>>)_
IP:R:: Fragmentation Offset _(13 Bits; see <<xtra_bitpacked_frag,Addendum>>)_
IP:T:: TTL _(1 Byte)_
IP:P:: <<spec_bcast_protonum, IP/Transport Protocol Number>> _(1 Byte)_
IP:C:: Header Checksum (RFC https://datatracker.ietf.org/doc/html/rfc1071[1071^], https://datatracker.ietf.org/doc/html/rfc1071[1141^], https://datatracker.ietf.org/doc/html/rfc1624[1624^]) _(2 Bytes)_
IP:S:: Source IPv4 Address _(32 Bits/4 Bytes)_
IP:D:: Destination IPv4 Address _(32 Bits/4 Bytes)_
IP:O:: Options + Null Padding (Optional) _(Variable Length, see <<xtra_pad_v4opts,Addendum>>)_

[id="spec_bcast_v6"]
==== IPv6
If IPv6, the AnnNet Broadcast sender _may_ send to:

* The multicast addresses (``ff0__x__``) with the following scopes (`scop`) (RFC https://datatracker.ietf.org/doc/html/rfc3513#section-2.7[3513 § 2.7^], https://datatracker.ietf.org/doc/html/rfc4291#section-2.7[4291 § 2.7^], https://datatracker.ietf.org/doc/html/rfc7346#section-2[7346 § 2], see also https://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xhtml[IANA IPv6 Multicast Registry^])
** *except* as reserved by other IANA/IETF/IESG-recognized protocols (i.e. `OSPFIGP`, `NDP`, et. al.):
*** Scope `1` (`0x0001`), _Interface-Local_
*** Scope `2` (`0x0002`), _Link-Local_
*** Scope `4` (`0x0004`), _Admin-Local_
*** Scope `5` (`0x0005`), _Site-Local_
*** Scope `8` (`0x0008`), _Organization-Local_
** A generally safe address would be `ff02::1` (_All Nodes, Link-Local_) but refer to https://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xhtml[the IANA registry^] to select the most appropriate multicast address(es).
* Or a direct IPv6 Link-Local Address (LLA) of a host (RFC https://datatracker.ietf.org/doc/html/rfc3513#section-2.5.6[3513 § 2.5.6^], https://datatracker.ietf.org/doc/html/rfc3927[3927^], https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.6[4291 § 2.5.6^], https://datatracker.ietf.org/doc/html/rfc7404[7404^] _(tangentially)_)
* Or a direct IPv6 Unique Local Address (ULA) of a host (https://datatracker.ietf.org/doc/html/rfc4193[RFC 4193^])

If using a multicast address, the source address *must* be a network-reachable address of the interface the message is sending from.

If using any other address, the source address *must* be an address reachable by the network/host the message is being sent to.

[id="spec_bcast_v6_msg"]
===== Message Format

The <<spec_msg, AnnNet Message>> follows the Destination Address value immediately:

.AnnNet Broadcast Packet, IPv6
[cols="^.^1s,^.^1s,^.^1s,^.^1s,^.^1s,^.^1s,^.^1s,^.^1s,^.^1s,^.^1s,^.^1s,^.^2s"]
|===
3+| Ethernet II Header +
(>= 14 Bytes) 8+| IP Header (40 Bytes) |
3+| 3+| V/TC/Flow (4 Bytes) 6+|

| E:D | E:S | E:T | VTF:V | VTF:T | VTF:F | IP:L | IP:N | IP:H | IP:S | IP:D | <<spec_msg, AnnNet Message>>
|===

Where:

E:D:: Destination MAC/PHY__(sical)__ Address _(6 Bytes)_
E:S:: Source MAC/PHY__(sical)__ Address _(6 Bytes)_
E:T:: EtherType (`0x86dd`) _(>= 2 Bytes; may be prefixed with VLAN information etc.)_
VTF:V:: Version _(4 Bits; see <<xtra_bitpacked_vtf,Addendum>>)_
VTF:T:: Traffic Class _(8 Bits)_ _(*not* 1 Byte; see <<xtra_bitpacked_vtf,Addendum>>)_
VTF:F:: Flow Label _(20 bits; see <<xtra_bitpacked,Addendum>>)_
IP:L:: IP Payload Length (not to be confused with <<spec_msg_pfx_len>>) _(2 Bytes)_
IP:N:: <<spec_bcast_protonum, Next Header>> (transport protocol number) _(1 Byte)_
IP:H:: Hop Limit _(1 Byte)_
IP:S:: Source IPv6 Address _(128 Bits/16 Bytes)_
IP:D:: Destination IPv6 Address _(128 Bits/16 Bytes)_

[id="spec_bcast_protonum"]
==== Internet Protocol Number
AnnNet currently uses the __IPv4 protocol__/__IPv6 Next Header__ protocol number `253` (`0xfd`) by default, as AnnNet is still in experimental stages and this protocol is https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml[reserved for experimentation^] per IANA. (`254`, `0xfe`, is another viable option for your implementation as it too is reserved by IANA for experimentation.)

If AnnNet is allocated a dedicated IANA-registered Protocol/Next Header, it will be implemented in a new major version release.

[id="limits"]
== Limitations
. AnnNet messages *must* fit into a single frame on the network it is being sent/received on. If message continuation or longer messages are wanted/desired, then it is up to the implementer to add a mechanism for this within their implementation's payload.

.. Likewise for checksumming; while most NICs will automatically handle the standard CRC32 https://en.wikipedia.org/wiki/Frame_check_sequence[Frame Check Sequence^] at the end of the entire frame (AnnNet Link is still Ethernet II), if you require checksumming within your application for *payloads* it must be accounted for by your application. If you are using hardware that doesn't automatically append/strip the FCS, your application must also account for that as well.

.. For AnnNet Broadcast over IPv4, ensure that you are including the 16-bit https://en.wikipedia.org/wiki/Internet_checksum["Internet checksum"^] of the header (see RFCs https://datatracker.ietf.org/doc/html/rfc1071[1071^], https://datatracker.ietf.org/doc/html/rfc1141[1141^], and https://datatracker.ietf.org/doc/html/rfc1624[1624^]). +
Most kernels can/will automatically insert this (e.g. on Linux, with `IPPROTO_RAW`/raw sockets with the `IP_HDRINCL` option, see https://man7.org/linux/man-pages/man7/raw.7.html[`raw(7)`^]), but if your implementation doesn't/can't use such a mechanism then you must do so yourself. +
IPv6 does not require a header checksum.

[id="fq"]
== (F)AQ/FUQ (Anticipated, Frequently Asked/Anticipated/Unasked Questions)

[id="fq_tcpip"]
=== Why do you reference the OSI model instead of the TCP/IP model? It's old/outdated/inaccurate.
Hold onto your seat.

Several reasons:

. The OSI model is standardized. There is only a *single*, *canonical*, *authoritative* "OSI model" when it comes to network models.
+
References are unambiguous. Boundaries between layers and their definitions are *mostly* clear. (And the ISO/IEC provides supplementals for any ambiguities that arise over the years.)
+
As for "the TCP/IP model", https://en.wikipedia.org/wiki/Internet_protocol_suite#Layering_evolution_and_representations_in_the_literature[which one are you referring to^]?
+
There's no less than *seven different models* that are referred to as "_**the** TCP/IP model_". (The linked-to article doesn't even mention e.g. the DoD model.) And they're all different in ways enough to make this confusing.

. The OSI model is more nuanced and detailed.
+
When one is talking about *IP protocols* and *service protocols* in the _same context_, things get very confusing very quickly. One can say "protocol" and it can apply to either ambiguously.
+
If instead one says "L2 protocol" or "layer 2 protocol" it's immediately clear that it operates without (IP) addressing, or "L3 protocol"/"layer 3 protocol" and it's immediately clear that a protocol that DOES use (IP) addressing is being referenced.

[id="fq_proto"]
=== Why Shouldn't I Use AnnNet for Regular Traffic?
On a purely technical level, there's nothing *stopping* you from doing so (aside from MTU windows, so you'd need to find some way of implementing message continuation, ordering, checksumming, etc. -- at that point, just use UDP multicast or something else).

But AnnNet is designed to be a lightweight announcement protocol for a _local_ (i.e. within the same subnet/link system) network.

It is designed to act like e.g.:

* https://datatracker.ietf.org/doc/html/rfc826[_An Ethernet Address Resolution Protocol ..._^] (`ARP`)
* https://datatracker.ietf.org/doc/html/rfc4861[_Neighbor Discovery for IP version 6 (IPv6)_^] (`ND`)
* Lease requests for:
** https://datatracker.ietf.org/doc/html/rfc2131[_Dynamic Host Configuration Protocol_^] (`DHCP`)
** https://datatracker.ietf.org/doc/html/rfc8415[_Dynamic Host Configuration Protocol for IPv6_ (`DHCPv6`)^]

etc., but with (very small amounts of) *arbitrary data* making it "plug-and-play" with >= L3 protocols/services' configuration.

It explicitly avoids defining any formation of payloads, message format, or the like -- because it is designed to be entirely agnostic of whatever use case it is implemented for.

However, AnnNet *strongly encourages* the use of https://wireproto.io/[WireProto^] for payload packing due to its minimal overhead, its strong hierarchical structuring, and its flexibility.

[WARNING]
====
Future versions of AnnNet **may _require_** use of WireProto (but WireProto itself is a very loose and flexible format, and would not add much overhead).

If this requirement is put in place, it will be in its own major revision.
====

If one needs a much more robust solution for anything *beyond* e.g. announcement messages, https://nats.io/[NATS^] may be useful for actual messaging between *known* members. (AnnNet would then be used in this case to provide a mechanism for _making members known/discovered_ with no explicit configuration.) +
[NOTE]
====
NATS also does not define any sort of payload format in its *encapsulated* payloads; most implementations tend to use https://protobuf.dev/[ProtoBuf/Protocol Buffers^], https://msgpack.org/index.html[MessagePack^], JSON, or the like.
====

[id="xtra"]
== Addendum/Errata/Administrivia

[id="xtra_bitpacked"]
=== Bitpacked Fields
Certain fields in IP headers are less than 1 byte or don't align to a clean byte boundary on their own, and therefore generally require bitwise operations to read/encapsulate meaningful values within them.

[id="xtra_bitpacked_vihl"]
==== IP Version, IHL (IPv4)
The IP version and IHL combined make up a single byte, with four-bit "nibbles" each.

[NOTE]
====
The IHL is calculated as the number of "32-bit words" in the IP header.

In other words, `IHL = (B * 8) / 32` (where `B` is the size of the IP header -- everything from "VI:V" to "IP:O" inclusive in <<spec_bcast_v4_msg>> -- in *bytes*).

Or, alternatively, `IHL = b / 32` (where `b` is the size of the IP header -- everything from "VI:V" to "IP:O" inclusive in <<spec_bcast_v4_msg>> -- in *bits*).

Since the V/IHL is a fixed size, this does *not* cause a recursion/chicken-egg problem.
====

To *create* the Version/IHL value, the Version (*always* `4` because IPv4) is packed as a big-endian 8-bit unsigned integer, and is then bit-shifted to the left by 4 bits and bitwise-``OR``'d with the IHL (as a big-endian 8-bit unsigned integer bitwise-`AND`-masked to the lower bits via `0x0f`/`15`).

To *retrieve* the Version and IHL, the value is bit-shifted to the *right* by 4 bits and cast as an 8-bit unsigned integer (this is the Version, `4`), and the original value is also bitwise-``AND``'d with the lower bits via `0x0f` (`15`) and cast as an unsigned 8-bit integer to get the IHL.

[%collapsible]
.Example in Go
====
.`examples/vihl.go`
[source,go]
----
include::examples/vihl.go[]
----
====

[id="xtra_bitpacked_de"]
==== DSCP, ECN (IPv4)
The DSCP and ECN fields are combined to make up a single byte with a 6-bit and 2-bit "nibble" each, respectively.

[NOTE]
====
The actual values for DSCP and ECN are sort of scattered about through several different RFCs.

Notable DSCP mentions are:

* https://datatracker.ietf.org/doc/html/rfc2474#section-4.2.2.1[RFC 2474 § 4.2.2.1^]
* https://datatracker.ietf.org/doc/html/rfc2475[RFC 2475^]
* https://datatracker.ietf.org/doc/html/rfc8837#section-5[RFC 8837 § 5^]

Notable ECN mentions are:

* https://datatracker.ietf.org/doc/html/rfc3168[RFC 3168^]
* https://datatracker.ietf.org/doc/html/rfc6040[RFC 6040^]
* https://datatracker.ietf.org/doc/html/rfc8311[RFC 8311^]
====

[%collapsible]
.Example in Go
====
.`examples/de.go`
[source,go]
----
include::examples/de.go[]
----
====

[id="xtra_bitpacked_frag"]
==== Flags, Fragmentation Offset (IPv4)
The (Fragmentation) Flags and Fragmentation Offset fields are combined to make up 2 bytes with a 3-bit and 13-bit "nibble"/"word" each, respectively.

The Fragmentation Flags and Offset are defined in https://datatracker.ietf.org/doc/html/rfc791#section-3.1[RFC 791 § 3.1^] and part of https://datatracker.ietf.org/doc/html/rfc815[RFC 815^].

[%collapsible]
.Example in Go
====
.`examples/frag.go`
[source,go]
----
include::examples/frag.go[]
----
====

[id="xtra_bitpacked_ip4opt_t"]
==== Option Type (IPv4)
The *Type* field of an <<xtra_pad_v4opts,IPv4 option>> is a single byte that consists of:

* A "Copy" flag _(1 Bit)_
* A "Class" flag _(2 Bits)_
* An Option "Number" _(5 Bits)_

[%collapsible]
.Example in Go
====
.`examples/v4optstype.go`
[source,go]
----
include::examples/v4optstype.go[]
----
====

[id="xtra_bitpacked_vtf"]
==== IP Version, Traffic Class, Flow Label (IPv6)
IPv6 thankfully only has one bitpacking in the header. Unfortunately, it's a triple-whammy.

These are mostly defined in:

* https://datatracker.ietf.org/doc/html/rfc8200#section-3[RFC 8200 § 3^] for the header format
* https://datatracker.ietf.org/doc/html/rfc8200#section-7[RFC 8200 § 7^] for the Traffic Class field
* https://datatracker.ietf.org/doc/html/rfc8200#section-6[RFC 8200 § 6^] for the Flow Label field

The IP Version takes up 4 bits (just as in IPv4, except it will always be `6` this time), the Traffic Class field takes up 8 bits, and Flow Labels takes up 20 bits for a total of 32 bits (4 bytes).

[%collapsible]
.Example in Go
====
.`examples/vtf.go`
[source,go]
----
include::examples/vtf.go[]
----
====


[id="xtra_pad"]
=== Padded Fields

[id="xtra_pad_v4opts"]
==== IPv4 Options
The IPv4 options, if specified, *must* align to a 32-bit/4-byte multiple size.
(In other words, its total length of bytes *must* be cleanly divisible by 4;
or said another way its total length of bits must be cleanly divisible by 32).

It uses null-byte (`0x00`) padding to achieve this.

[%collapsible]
.Example in Go
====
.`examples/v4optspad.go`
[source,go]
----
include::examples/v4optspad.go[]
----
====

For more extensive details, see https://datatracker.ietf.org/doc/html/rfc791#section-3.1[RFC 791 § 3.1^]
and the https://www.iana.org/assignments/ip-parameters/ip-parameters.xhtml[IANA Registered IP Paramaeters^]
(the *Value* column is what should be used as the *Type*, see <<xtra_bitpacked_ip4opt_t>>.

Each option (with two exceptions) is a TLV (<<xtra_pad_v4opts_t,type>> / <<xtra_pad_v4opts_l,length>> / <<xtra_pad_v4opts_v,value>>) structured field.

The two exceptions for the above are `EOOL` (`0x00`) and `NOP` (`0x01`), both of which occupy only a single byte -- they are *unvalued*, so they have no length or value field.

[id="xtra_pad_v4opts_t"]
===== Type
See <<xtra_bitpacked_ip4opt_t>> for details.

[id="xtra_pad_v4opts_l"]
===== Length
The *Length* is the length of this _entire_ option as an 8-bit unsigned integer.

In other words it includes the length of the <<xtra_pad_v4opts_t>> field, the <<xtra_pad_v4opts_l>> field (this field), 
*and* the <<xtra_pad_v4opts_v>> field together.

This is not present for `EOOL` (`0x00`) and `NOP` (`0x01`) (or any other "non-valued" options).

[id="xtra_pad_v4opts_v"]
===== Value
Value is the option's value. Its <<xtra_pad_v4opts_l>> is variable depending on the <<xtra_pad_v4opts_t>>.

This is not present for `EOOL` (`0x00`) and `NOP` (`0x01`) (or any other "non-valued" options).
