= `netx` Notes
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
:source-highlighter: rouge
:docinfo: shared

[id="4in6"]
== IPv4 Addresses as IPv6
There are, effectively, two RFC-specified ways of representing an IPv4 address in IPv6 notation/address space/context.

Per https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5[RFC 4291 § 2.5.5^]:

* _IPv4-Compatible IPv6 Address_ (https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.1[RFC 4291 § 2.5.5.1^]) -- `::/96`
** e.g. `::192.0.2.10`
* _IPv4-Mapped IPv6 Address_ (https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2[RFC 4291 § 2.5.5.2^]) -- `::ffff:0:0/96`
** e.g. `::ffff:192.0.2.10`

The former is considered deprecated, whereas the latter is considered the modern proper representation.
Some naïve or legacy software, however, still use the former.

An IPv6 address is *128 bits* (hence a prefix length/mask of `/128` representing a complete address), or *16 bytes* (`128 / 8`).

An IPv4 address is *32 bits* (hence a prefix length/mask of `/32` representing a complete address), or *4 bytes* (`32 / 8`).

This is why both the "IPv4-compatible" and "IPv4-mapped" IPv6 addresses are within a reserved prefix length of 96 bits (`/96`) - it leaves a remaining
*32 bits* (`128 - 96`) for a complete IPv4 address.

[IMPORTANT]
====
The IPv4-Compatible and IPv4-Mapped prefixes *are not* intended for routing!

They are used for *syntactic/contextual* use.

For routing/actual IPv4 <=> IPv6 translation, see https://datatracker.ietf.org/doc/html/rfc6052[RFC 6052^] and https://datatracker.ietf.org/doc/html/rfc8215[RFC 8215^].

This library may add supporting functions for these _translation_ prefixes at a later time.
====

[id="4in6_addr"]
=== Addresses
While https://pkg.go.dev/net/netip#ParseAddr[`net/netip.ParseAddr`^] (or https://pkg.go.dev/net/netip#MustParseAddr[`net/netip.MustParseAddr`^]) will handle
*parsing* the "IPv4-Compatible" format just fine, it will treat it *as* an IPv6 address -- not an "IPv6-wrapped/IPv6-mapped IPv4" address.

[%collapsible]
.For example...
====
[source,go]
----
package main

import (
	"fmt"
	"net/netip"
)

func main() {
	fmt.Printf(
		"::192.0.2.10 -> %s\n",
		netip.MustParseAddr("::192.0.2.10").String(),
	)
}
----

will print:

[source,text]
----
::192.0.2.10 -> ::c000:20a
----
====

For addresses, this is an easy enough fix:

. Take the IPv6 as a byte slice, `b` (`b = ip4in6.AsSlice()`)
. Slice out the last 4 bytes as `b2` (`b2 = b[12:16]`)
. Parse `b2` as an IPv4 address (`ip4 = netip.FromSlice(b2)`)

For the visual learners, the original address (`::192.0.2.10` or, more properly, `::c000:20a`) can be represented in multiple ways.

[%collapsible]
.For instance...
====
* A "canonical"/shorted IPv6 address: `::c000:20a`
* A fully "exploded" IPv6 address: `0000:0000:0000:0000:0000:0000:c000:020a`
* Hex bytes:
** `00:00:00:00:00:00:00:00:00:00:00:00:C0:00:02:0A`
** `0x000000000000000000000000C000020A`
** Via `xxd(1)`:
+
[source,text]
----
# xxd -c4 -g 1
00000000: 00 00 00 00  ....
00000004: 00 00 00 00  ....
00000008: 00 00 00 00  ....
0000000c: c0 00 02 0a  ....
----
** Via `hexdump(1)`:
+
[source,text]
----
# hexdump -v -e '"%08_ax: " 4/1 "0x%02X ""\n"'
00000000: 0x00 0x00 0x00 0x00
00000004: 0x00 0x00 0x00 0x00
00000008: 0x00 0x00 0x00 0x00
0000000c: 0xC0 0x00 0x02 0x0A
----
* A Go byte slice:
** Either as explicit `byte`:
+
[source,go]
----
[]byte{
	0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00,
	0xC0, 0x00, 0x02, 0x0A,
}
----
** Or as `uint8` (a `byte` is, quite literally, https://go.dev/ref/spec#Numeric_types[just a type alias for `uint8`^]):
+
[source,go]
----
[]byte{
	uint8(0), uint8(0), uint8(0), uint8(0),
	uint8(0), uint8(0), uint8(0), uint8(0),
	uint8(0), uint8(0), uint8(0), uint8(0),
	uint8(192), uint8(0), uint8(2), uint8(10),
}
----
====

When seeing it represented as a slice of `uint8`, some things should start to click mentally looking at the last
four bytes.

An IPv4 address can then be derived by taking the last four bytes (`[]byte{...}[12:16]`):

* `[]byte{0xC0, 0x00, 0x02, 0x0A}`
* `[]byte(uint8(192), uint8(0), uint8(2), uint8(10))`

As you may have guessed by now, this is the byte format of the IPv4 address `192.0.2.10`.

The `netx.UnmapAddr()` function handles this cleanly.

'''

If you instead prefer to think in _prefixing/masking_, the last four bytes are *32 bits* (`8 * 4`) -- the size of an IPv4 address.

The "compatible" mapping is '::/96', and an IPv6 address is *128 bits* (*16 bytes*, `128 / 8`) total -- `128 - 96 == 32 bits`, or *4 bytes*.

[id="4in6_pfx"]
=== Prefixes
Prefixes are a little more tricky.

Prefixing for IPv4-Compatible/IPv4-Mapped IPv6 addresses works a bit oddly because generally the "4-in-6" addresses are
meant to be purely representative - a way to represent IPv4 addresses in IPv6 implementations.

But there are cases where one needs a *prefix* represented.
This leads to the awkward case where an IPv4 prefix length (e.g. `/24`) needs to be represented as an IPv6 prefix length (e.g. `/120`).

While this module (`netx`) does offer helper functions to calculate this (`netx.IP4MapPfx6to4()`, `netx.IP4MapPfx4to6()`),
the `netx.UnmapPfx()` function is offered as a convenience function that handles the translation from an IPv6 representation
of an IPv4 prefix into a fully "native" IPv4 prefix.

[NOTE]
====
`netx.IP4MapPfx4to6(len4 uint8)` simply takes the prefix length `len4`, and (if `len4 <= 32`) adds `96`.

`netx.IP4MapPfx6to4(len6 uint8)` simply takes the prefix length `len6`, and (if `len6 >= 96`) subtracts `96`.
====

This is achieved by:

. Taking the address from the prefix (`ip4in6 = pfx4in6.Addr()`)
.. And <<4in6_addr, converting to native IPv4>> (`ip4 = netx.UnmapAddr(ip4in6)`)
. Taking the IPv6 prefix length (`len6 = pfx4in6.Bits()`)
.. And converting it to an IPv4 prefix length (`len4 = netx.IP4MapPfx6to4(uint8(len6))`)
. Then re-assembling (`pfx4 = netip.PrefixFrom(ip4, int(len4))`)


[id="misc"]
== Miscellaneous

[id="misc_unspec"]
=== Unspecified Address
There are two "unspecified" addresses for each family:

* IPv4
** `0.0.0.0/0`
** `0.0.0.0/32`
* IPv6
** `::/0`
** `::/128`

For each family, the `/0` prefix length 
