SHA256
1
0

include IPv4 Option padding and type packing/unpacking

This commit is contained in:
brent saner 2025-08-31 00:29:20 -04:00
parent e48754ae4b
commit cd5570d34b
Signed by: bts
GPG Key ID: 8C004C2F93481F6B
9 changed files with 586 additions and 24 deletions

15
.githooks/pre-commit/02-gofmt Executable file
View File

@ -0,0 +1,15 @@
#!/bin/bash
srcdir="${PWD}/examples"
if ! command -v gofmt &> /dev/null;
then
exit 0
fi
for f in $(find ${srcdir} -type f -iname "*.go");
do
gofmt -w "${f}"
git add "${f}"
done
echo "Reformatted examples"

View File

@ -270,7 +270,7 @@ A flag's presence can be checked via a bit-wise `AND` against _flag_ being equal
[%collapsible]
.Example in Go
=====
.`hasflag.go`
.`examples/hasflag.go`
[source,go]
----
include::examples/hasflag.go[]
@ -390,7 +390,7 @@ 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 (Optional) (See the https://www.iana.org/assignments/ip-parameters/ip-parameters.xhtml[IANA Registered IP Parameters^] for details) _(Variable Length)_
IP:O:: Options + Null Padding (Optional) _(Variable Length, see <<xtra_pad_v4opts,Addendum>>)_
[id="spec_bcast_v6"]
==== IPv6
@ -541,6 +541,7 @@ To *retrieve* the Version and IHL, the value is bit-shifted to the *right* by 4
[%collapsible]
.Example in Go
====
.`examples/vihl.go`
[source,go]
----
include::examples/vihl.go[]
@ -571,6 +572,7 @@ Notable ECN mentions are:
[%collapsible]
.Example in Go
====
.`examples/de.go`
[source,go]
----
include::examples/de.go[]
@ -586,12 +588,31 @@ The Fragmentation Flags and Offset are defined in https://datatracker.ietf.org/d
[%collapsible]
.Example in Go
====
.`examples/frag.go`
[source,go]
----
include::examples/frag.go[]
----
====
[id="xtra_bitpacked_ip4opt_t"]
==== IPv4 Option Type
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.
@ -607,8 +628,58 @@ The IP Version takes up 4 bits (just as in IPv4, except it will always be `6` th
[%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).

View File

@ -559,7 +559,7 @@ pre.rouge .gs {
<div class="details">
<span id="author" class="author">Brent Saner</span><br>
<span id="email" class="email"><a href="mailto:bts@square-r00t.net">bts@square-r00t.net</a></span><br>
<span id="revdate">Last rendered 2025-08-30 17:05:09 -0400</span>
<span id="revdate">Last rendered 2025-08-31 00:29:24 -0400</span>
</div>
<div id="toc" class="toc2">
<div id="toctitle">Table of Contents</div>
@ -625,7 +625,19 @@ pre.rouge .gs {
<li><a href="#xtra_bitpacked_vihl">8.1.1. IP Version, IHL (IPv4)</a></li>
<li><a href="#xtra_bitpacked_de">8.1.2. DSCP, ECN (IPv4)</a></li>
<li><a href="#xtra_bitpacked_frag">8.1.3. Flags, Fragmentation Offset (IPv4)</a></li>
<li><a href="#xtra_bitpacked_vtf">8.1.4. IP Version, Traffic Class, Flow Label (IPv6)</a></li>
<li><a href="#xtra_bitpacked_ip4opt_t">8.1.4. IPv4 Option Type</a></li>
<li><a href="#xtra_bitpacked_vtf">8.1.5. IP Version, Traffic Class, Flow Label (IPv6)</a></li>
</ul>
</li>
<li><a href="#xtra_pad">8.2. Padded Fields</a>
<ul class="sectlevel3">
<li><a href="#xtra_pad_v4opts">8.2.1. IPv4 Options</a>
<ul class="sectlevel4">
<li><a href="#xtra_pad_v4opts_t">8.2.1.1. Type</a></li>
<li><a href="#xtra_pad_v4opts_l">8.2.1.2. Length</a></li>
<li><a href="#xtra_pad_v4opts_v">8.2.1.3. Value</a></li>
</ul>
</li>
</ul>
</li>
</ul>
@ -1700,7 +1712,7 @@ If using the online version, <em>Part I-2</em> is summarized/starts <a href="htt
<summary class="title">Example in Go</summary>
<div class="content">
<div class="listingblock">
<div class="title"><code>hasflag.go</code></div>
<div class="title"><code>examples/hasflag.go</code></div>
<div class="content">
<pre class="rouge highlight"><code data-lang="go"><span class="k">package</span> <span class="n">main</span>
@ -2079,7 +2091,7 @@ Its source <em>must</em> be the MAC/PHYS address of the network interface it is
</dd>
<dt class="hdlist1">IP:O</dt>
<dd>
<p>Options (Optional) (See the <a href="https://www.iana.org/assignments/ip-parameters/ip-parameters.xhtml" target="_blank" rel="noopener">IANA Registered IP Parameters</a> for details) <em>(Variable Length)</em></p>
<p>Options + Null Padding (Optional) <em>(Variable Length, see <a href="#xtra_pad_v4opts">Addendum</a>)</em></p>
</dd>
</dl>
</div>
@ -2439,12 +2451,13 @@ IPv6 does not require a header checksum.</p>
<summary class="title">Example in Go</summary>
<div class="content">
<div class="listingblock">
<div class="title"><code>examples/vihl.go</code></div>
<div class="content">
<pre class="rouge highlight"><code data-lang="go"><span class="k">package</span> <span class="n">main</span>
<span class="k">import</span> <span class="p">(</span>
<span class="s">`fmt`</span>
<span class="s">`log`</span>
<span class="s">"fmt"</span>
<span class="s">"log"</span>
<span class="s">"strconv"</span>
<span class="p">)</span>
@ -2573,12 +2586,13 @@ IPv6 does not require a header checksum.</p>
<summary class="title">Example in Go</summary>
<div class="content">
<div class="listingblock">
<div class="title"><code>examples/de.go</code></div>
<div class="content">
<pre class="rouge highlight"><code data-lang="go"><span class="k">package</span> <span class="n">main</span>
<span class="k">import</span> <span class="p">(</span>
<span class="s">`fmt`</span>
<span class="s">`log`</span>
<span class="s">"fmt"</span>
<span class="s">"log"</span>
<span class="s">"strconv"</span>
<span class="p">)</span>
@ -2665,12 +2679,13 @@ IPv6 does not require a header checksum.</p>
<summary class="title">Example in Go</summary>
<div class="content">
<div class="listingblock">
<div class="title"><code>examples/frag.go</code></div>
<div class="content">
<pre class="rouge highlight"><code data-lang="go"><span class="k">package</span> <span class="n">main</span>
<span class="k">import</span> <span class="p">(</span>
<span class="s">`fmt`</span>
<span class="s">`log`</span>
<span class="s">"fmt"</span>
<span class="s">"log"</span>
<span class="s">"strconv"</span>
<span class="p">)</span>
@ -2747,7 +2762,147 @@ IPv6 does not require a header checksum.</p>
</details>
</div>
<div class="sect3">
<h4 id="xtra_bitpacked_vtf"><a class="link" href="#xtra_bitpacked_vtf">8.1.4. IP Version, Traffic Class, Flow Label (IPv6)</a></h4>
<h4 id="xtra_bitpacked_ip4opt_t"><a class="link" href="#xtra_bitpacked_ip4opt_t">8.1.4. IPv4 Option Type</a></h4>
<div class="paragraph">
<p>The <strong>Type</strong> field of an <a href="#xtra_pad_v4opts">IPv4 option</a> is a single byte that consists of:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>A "Copy" flag <em>(1 Bit)</em></p>
</li>
<li>
<p>A "Class" flag <em>(2 Bits)</em></p>
</li>
<li>
<p>An Option "Number" <em>(5 Bits)</em></p>
</li>
</ul>
</div>
<details>
<summary class="title">Example in Go</summary>
<div class="content">
<div class="listingblock">
<div class="title"><code>examples/v4optstype.go</code></div>
<div class="content">
<pre class="rouge highlight"><code data-lang="go"><span class="k">package</span> <span class="n">main</span>
<span class="k">import</span> <span class="p">(</span>
<span class="s">"fmt"</span>
<span class="s">"log"</span>
<span class="s">"strconv"</span>
<span class="p">)</span>
<span class="k">const</span> <span class="p">(</span>
<span class="n">ClassControl</span> <span class="kt">uint8</span> <span class="o">=</span> <span class="no">iota</span>
<span class="n">ClassReserved</span>
<span class="n">ClassDebug</span>
<span class="n">ClassReserved2</span>
<span class="p">)</span>
<span class="k">var</span> <span class="p">(</span>
<span class="n">classStr</span> <span class="k">map</span><span class="p">[</span><span class="kt">uint8</span><span class="p">]</span><span class="kt">string</span> <span class="o">=</span> <span class="k">map</span><span class="p">[</span><span class="kt">uint8</span><span class="p">]</span><span class="kt">string</span><span class="p">{</span>
<span class="n">ClassControl</span><span class="o">:</span> <span class="s">"Control"</span><span class="p">,</span>
<span class="n">ClassReserved</span><span class="o">:</span> <span class="s">"Reserved (1)"</span><span class="p">,</span>
<span class="n">ClassDebug</span><span class="o">:</span> <span class="s">"Debugging/Measurement"</span><span class="p">,</span>
<span class="n">ClassReserved2</span><span class="o">:</span> <span class="s">"Reserved (2)"</span><span class="p">,</span>
<span class="p">}</span>
<span class="p">)</span>
<span class="k">const</span> <span class="p">(</span>
<span class="c">/// This is the same type from examples/v4optspad.go.</span>
<span class="n">ip4OptTypBits</span> <span class="kt">string</span> <span class="o">=</span> <span class="s">"10010100"</span> <span class="c">// [1001 0100], or 148 (0x94)</span>
<span class="p">)</span>
<span class="k">var</span> <span class="p">(</span>
<span class="n">v4TypCpyOffset</span> <span class="kt">uint8</span> <span class="o">=</span> <span class="m">0x80</span> <span class="c">// 128</span>
<span class="n">v4TypCpyMask</span> <span class="kt">uint8</span> <span class="o">=</span> <span class="m">0x01</span> <span class="c">// Mask to 1 bit</span>
<span class="n">v4TypCpyPos</span> <span class="kt">uint8</span> <span class="o">=</span> <span class="m">8</span> <span class="o">-</span> <span class="n">v4TypCpyMask</span> <span class="c">// 7 or 0x07 (8 (bits) - mask = Shifted to 7th bit)</span>
<span class="n">v4TypClsOffset</span> <span class="kt">uint8</span> <span class="o">=</span> <span class="m">0x60</span> <span class="c">// 96</span>
<span class="n">v4TypClsMask</span> <span class="kt">uint8</span> <span class="o">=</span> <span class="m">0x05</span> <span class="c">// mask to 5 bits</span>
<span class="n">v4TypClsPos</span> <span class="kt">uint8</span> <span class="o">=</span> <span class="m">8</span> <span class="o">-</span> <span class="n">v4TypClsMask</span>
<span class="n">v4TypNumOffset</span> <span class="kt">uint8</span> <span class="o">=</span> <span class="m">0x1f</span> <span class="c">// 31</span>
<span class="p">)</span>
<span class="k">func</span> <span class="n">ToV4OptTyp</span><span class="p">(</span><span class="n">copied</span><span class="p">,</span> <span class="n">class</span><span class="p">,</span> <span class="n">num</span> <span class="kt">uint8</span><span class="p">)</span> <span class="p">(</span><span class="n">typ</span> <span class="kt">uint8</span><span class="p">)</span> <span class="p">{</span>
<span class="n">typ</span> <span class="o">=</span> <span class="p">((</span><span class="n">copied</span> <span class="o">&amp;</span> <span class="n">v4TypCpyMask</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="n">v4TypCpyPos</span><span class="p">)</span> <span class="o">|</span> <span class="p">((</span><span class="n">class</span> <span class="o">&amp;</span> <span class="n">v4TypClsMask</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="n">v4TypClsPos</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">num</span> <span class="o">&amp;</span> <span class="n">v4TypNumOffset</span><span class="p">)</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="k">func</span> <span class="n">FromV4OptTyp</span><span class="p">(</span><span class="n">typ</span> <span class="kt">uint8</span><span class="p">)</span> <span class="p">(</span><span class="n">copied</span><span class="p">,</span> <span class="n">class</span><span class="p">,</span> <span class="n">num</span> <span class="kt">uint8</span><span class="p">)</span> <span class="p">{</span>
<span class="n">copied</span> <span class="o">=</span> <span class="p">(</span><span class="n">typ</span> <span class="o">&amp;</span> <span class="n">v4TypCpyOffset</span><span class="p">)</span> <span class="o">&gt;&gt;</span> <span class="n">v4TypCpyPos</span>
<span class="n">class</span> <span class="o">=</span> <span class="p">(</span><span class="n">typ</span> <span class="o">&amp;</span> <span class="n">v4TypClsOffset</span><span class="p">)</span> <span class="o">&gt;&gt;</span> <span class="n">v4TypClsPos</span>
<span class="n">num</span> <span class="o">=</span> <span class="p">(</span><span class="n">typ</span> <span class="o">&amp;</span> <span class="n">v4TypNumOffset</span><span class="p">)</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
<span class="k">var</span> <span class="n">err</span> <span class="kt">error</span>
<span class="k">var</span> <span class="n">u64</span> <span class="kt">uint64</span>
<span class="k">var</span> <span class="n">typ</span> <span class="kt">uint8</span>
<span class="k">var</span> <span class="n">cpd</span> <span class="kt">uint8</span>
<span class="k">var</span> <span class="n">cls</span> <span class="kt">uint8</span>
<span class="k">var</span> <span class="n">num</span> <span class="kt">uint8</span>
<span class="c">// Given a type of ip4OptTypBits (see const at top)...</span>
<span class="k">if</span> <span class="n">u64</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">strconv</span><span class="o">.</span><span class="n">ParseUint</span><span class="p">(</span><span class="n">ip4OptTypBits</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="m">8</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">log</span><span class="o">.</span><span class="n">Panicln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">typ</span> <span class="o">=</span> <span class="kt">uint8</span><span class="p">(</span><span class="n">u64</span><span class="p">)</span>
<span class="c">// Prints:</span>
<span class="c">/*
Type is: 148 (0x0094)
*/</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s">"Type is:</span><span class="se">\t</span><span class="s">%d (%#04x)</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">typ</span><span class="p">,</span> <span class="n">typ</span><span class="p">)</span>
<span class="n">cpd</span><span class="p">,</span> <span class="n">cls</span><span class="p">,</span> <span class="n">num</span> <span class="o">=</span> <span class="n">FromV4OptTyp</span><span class="p">(</span><span class="n">typ</span><span class="p">)</span>
<span class="c">// Prints:</span>
<span class="c">/*
Copied: 1 (0x0001)
Class: 0 (0x0000)
Number: 20 (0x0014)
*/</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span>
<span class="s">"Copied:</span><span class="se">\t\t</span><span class="s">%d %#04x)</span><span class="se">\n</span><span class="s">"</span><span class="o">+</span>
<span class="s">"Class:</span><span class="se">\t\t</span><span class="s">%d (%#04x)</span><span class="se">\n</span><span class="s">"</span><span class="o">+</span>
<span class="s">"Number:</span><span class="se">\t\t</span><span class="s">%d (%#04x)</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
<span class="n">cpd</span><span class="p">,</span> <span class="n">cpd</span><span class="p">,</span>
<span class="n">cls</span><span class="p">,</span> <span class="n">cls</span><span class="p">,</span>
<span class="n">num</span><span class="p">,</span> <span class="n">num</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">typ</span> <span class="o">=</span> <span class="n">ToV4OptTyp</span><span class="p">(</span><span class="n">cpd</span><span class="p">,</span> <span class="n">cls</span><span class="p">,</span> <span class="n">num</span><span class="p">)</span>
<span class="c">// Prints:</span>
<span class="c">/*
Confirmed Type: 148 (0x94)
*/</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s">"Confirmed Type:</span><span class="se">\t</span><span class="s">%d (%#02x)</span><span class="se">\n\n</span><span class="s">"</span><span class="p">,</span> <span class="n">typ</span><span class="p">,</span> <span class="n">typ</span><span class="p">)</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"Class Name:"</span><span class="p">)</span>
<span class="c">// Prints:</span>
<span class="c">/*
Control
*/</span>
<span class="k">for</span> <span class="n">c</span><span class="p">,</span> <span class="n">cNm</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">classStr</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">c</span> <span class="o">==</span> <span class="n">cls</span> <span class="p">{</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="n">cNm</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre>
</div>
</div>
</div>
</details>
</div>
<div class="sect3">
<h4 id="xtra_bitpacked_vtf"><a class="link" href="#xtra_bitpacked_vtf">8.1.5. IP Version, Traffic Class, Flow Label (IPv6)</a></h4>
<div class="paragraph">
<p>IPv6 thankfully only has one bitpacking in the header. Unfortunately, it&#8217;s a triple-whammy.</p>
</div>
@ -2774,12 +2929,13 @@ IPv6 does not require a header checksum.</p>
<summary class="title">Example in Go</summary>
<div class="content">
<div class="listingblock">
<div class="title"><code>examples/vtf.go</code></div>
<div class="content">
<pre class="rouge highlight"><code data-lang="go"><span class="k">package</span> <span class="n">main</span>
<span class="k">import</span> <span class="p">(</span>
<span class="s">`fmt`</span>
<span class="s">`log`</span>
<span class="s">"fmt"</span>
<span class="s">"log"</span>
<span class="s">"strconv"</span>
<span class="p">)</span>
@ -2866,12 +3022,148 @@ IPv6 does not require a header checksum.</p>
</details>
</div>
</div>
<div class="sect2">
<h3 id="xtra_pad"><a class="link" href="#xtra_pad">8.2. Padded Fields</a></h3>
<div class="sect3">
<h4 id="xtra_pad_v4opts"><a class="link" href="#xtra_pad_v4opts">8.2.1. IPv4 Options</a></h4>
<div class="paragraph">
<p>The IPv4 options, if specified, <strong>must</strong> align to a 32-bit/4-byte multiple size.
(In other words, its total length of bytes <strong>must</strong> be cleanly divisible by 4;
or said another way its total length of bits must be cleanly divisible by 32).</p>
</div>
<div class="paragraph">
<p>It uses null-byte (<code>0x00</code>) padding to achieve this.</p>
</div>
<details>
<summary class="title">Example in Go</summary>
<div class="content">
<div class="listingblock">
<div class="title"><code>examples/v4optspad.go</code></div>
<div class="content">
<pre class="rouge highlight"><code data-lang="go"><span class="k">package</span> <span class="n">main</span>
<span class="k">import</span> <span class="p">(</span>
<span class="s">"bytes"</span>
<span class="s">"fmt"</span>
<span class="p">)</span>
<span class="k">const</span> <span class="p">(</span>
<span class="n">padVal</span> <span class="kt">uint8</span> <span class="o">=</span> <span class="m">0x00</span> <span class="c">// Padded with NUL/null-bytes</span>
<span class="n">alignLen</span> <span class="kt">int</span> <span class="o">=</span> <span class="m">4</span> <span class="c">// 4-Byte alignment</span>
<span class="p">)</span>
<span class="k">var</span> <span class="p">(</span>
<span class="c">// See the examples/v4opts.go for how these bytes are constructed.</span>
<span class="n">opts</span> <span class="p">[]</span><span class="kt">byte</span> <span class="o">=</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">{</span>
<span class="c">// This is an example.</span>
<span class="c">// Option 1</span>
<span class="m">0x94</span><span class="p">,</span> <span class="c">// Type 148, RTRALT (Router Alert) (RFC 2113)</span>
<span class="m">0x04</span><span class="p">,</span> <span class="c">// Length (4 bytes)</span>
<span class="m">0x00</span><span class="p">,</span> <span class="m">0x00</span><span class="p">,</span> <span class="c">// "Router shall examine packet"</span>
<span class="c">// EOOL</span>
<span class="m">0x00</span><span class="p">,</span>
<span class="c">// Padding will go here.</span>
<span class="p">}</span>
<span class="p">)</span>
<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
<span class="k">var</span> <span class="n">optLen</span> <span class="kt">int</span>
<span class="k">var</span> <span class="n">padLen</span> <span class="kt">int</span>
<span class="k">var</span> <span class="n">pad</span> <span class="p">[]</span><span class="kt">byte</span>
<span class="n">optLen</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">opts</span><span class="p">)</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"Before padding:"</span><span class="p">)</span>
<span class="c">// Prints:</span>
<span class="c">/*
0x9404000000
(Length: 5)
*/</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s">"%#02x</span><span class="se">\n</span><span class="s">(Length: %d)</span><span class="se">\n\n</span><span class="s">"</span><span class="p">,</span> <span class="n">opts</span><span class="p">,</span> <span class="n">optLen</span><span class="p">)</span>
<span class="c">/*
The remainder of the current length divided by
alignLen (4) (modulo) is subtracted from the alignLen
to determine how much must be added to reach the next
"boundary". It's then modulo'd *again* to rule out
currently being on an alignment bounary.
*/</span>
<span class="n">padLen</span> <span class="o">=</span> <span class="p">(</span><span class="n">alignLen</span> <span class="o">-</span> <span class="p">(</span><span class="n">optLen</span> <span class="o">%</span> <span class="n">alignLen</span><span class="p">))</span> <span class="o">%</span> <span class="n">alignLen</span>
<span class="c">// Prints:</span>
<span class="c">/*
Pad length needed: 3
*/</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s">"Pad length needed:</span><span class="se">\t</span><span class="s">%d</span><span class="se">\n\n</span><span class="s">"</span><span class="p">,</span> <span class="n">padLen</span><span class="p">)</span>
<span class="n">pad</span> <span class="o">=</span> <span class="n">bytes</span><span class="o">.</span><span class="n">Repeat</span><span class="p">([]</span><span class="kt">byte</span><span class="p">{</span><span class="n">padVal</span><span class="p">},</span> <span class="n">padLen</span><span class="p">)</span>
<span class="n">opts</span> <span class="o">=</span> <span class="nb">append</span><span class="p">(</span><span class="n">opts</span><span class="p">,</span> <span class="n">pad</span><span class="o">...</span><span class="p">)</span>
<span class="c">// Alternatively, this can be implemented with a loop, though it's likely less efficient:</span>
<span class="c">/*
for len(opts) % alignLen != 0 {}
opts = append(opts, padVal)
}
*/</span>
<span class="c">// Prints:</span>
<span class="c">/*
Padded:
0x9404000000000000
*/</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s">"Padded:</span><span class="se">\n</span><span class="s">%#x</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">opts</span><span class="p">)</span>
<span class="p">}</span></code></pre>
</div>
</div>
</div>
</details>
<div class="paragraph">
<p>For more extensive details, see <a href="https://datatracker.ietf.org/doc/html/rfc791#section-3.1" target="_blank" rel="noopener">RFC 791 § 3.1</a>
and the <a href="https://www.iana.org/assignments/ip-parameters/ip-parameters.xhtml" target="_blank" rel="noopener">IANA Registered IP Paramaeters</a>
(the <strong>Value</strong> column is what should be used as the <strong>Type</strong>, see <a href="#xtra_bitpacked_ip4opt_t">IPv4 Option Type</a>.</p>
</div>
<div class="paragraph">
<p>Each option (with two exceptions) is a TLV (<a href="#xtra_pad_v4opts_t">type</a> / <a href="#xtra_pad_v4opts_l">length</a> / <a href="#xtra_pad_v4opts_v">value</a>) structured field.</p>
</div>
<div class="paragraph">
<p>The two exceptions for the above are <code>EOOL</code> (<code>0x00</code>) and <code>NOP</code> (<code>0x01</code>), both of which occupy only a single byte&#8201;&#8212;&#8201;they are <strong>unvalued</strong>, so they have no length or value field.</p>
</div>
<div class="sect4">
<h5 id="xtra_pad_v4opts_t"><a class="link" href="#xtra_pad_v4opts_t">8.2.1.1. Type</a></h5>
<div class="paragraph">
<p>See <a href="#xtra_bitpacked_ip4opt_t">IPv4 Option Type</a> for details.</p>
</div>
</div>
<div class="sect4">
<h5 id="xtra_pad_v4opts_l"><a class="link" href="#xtra_pad_v4opts_l">8.2.1.2. Length</a></h5>
<div class="paragraph">
<p>The <strong>Length</strong> is the length of this <em>entire</em> option as an 8-bit unsigned integer.</p>
</div>
<div class="paragraph">
<p>In other words it includes the length of the <a href="#xtra_pad_v4opts_t">Type</a> field, the <a href="#xtra_pad_v4opts_l">Length</a> field (this field),
<strong>and</strong> the <a href="#xtra_pad_v4opts_v">Value</a> field together.</p>
</div>
<div class="paragraph">
<p>This is not present for <code>EOOL</code> (<code>0x00</code>) and <code>NOP</code> (<code>0x01</code>) (or any other "non-valued" options).</p>
</div>
</div>
<div class="sect4">
<h5 id="xtra_pad_v4opts_v"><a class="link" href="#xtra_pad_v4opts_v">8.2.1.3. Value</a></h5>
<div class="paragraph">
<p>Value is the option&#8217;s value. Its <a href="#xtra_pad_v4opts_l">Length</a> is variable depending on the <a href="#xtra_pad_v4opts_t">Type</a>.</p>
</div>
<div class="paragraph">
<p>This is not present for <code>EOOL</code> (<code>0x00</code>) and <code>NOP</code> (<code>0x01</code>) (or any other "non-valued" options).</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2025-08-30 16:39:39 -0400
Last updated 2025-08-31 00:25:52 -0400
</div>
</div>
</body>

View File

@ -1,8 +1,8 @@
package main
import (
`fmt`
`log`
"fmt"
"log"
"strconv"
)

View File

@ -1,8 +1,8 @@
package main
import (
`fmt`
`log`
"fmt"
"log"
"strconv"
)

73
examples/v4optspad.go Normal file
View File

@ -0,0 +1,73 @@
package main
import (
"bytes"
"fmt"
)
const (
padVal uint8 = 0x00 // Padded with NUL/null-bytes
alignLen int = 4 // 4-Byte alignment
)
var (
// See the examples/v4opts.go for how these bytes are constructed.
opts []byte = []byte{
// This is an example.
// Option 1
0x94, // Type 148, RTRALT (Router Alert) (RFC 2113)
0x04, // Length (4 bytes)
0x00, 0x00, // "Router shall examine packet"
// EOOL
0x00,
// Padding will go here.
}
)
func main() {
var optLen int
var padLen int
var pad []byte
optLen = len(opts)
fmt.Println("Before padding:")
// Prints:
/*
0x9404000000
(Length: 5)
*/
fmt.Printf("%#02x\n(Length: %d)\n\n", opts, optLen)
/*
The remainder of the current length divided by
alignLen (4) (modulo) is subtracted from the alignLen
to determine how much must be added to reach the next
"boundary". It's then modulo'd *again* to rule out
currently being on an alignment bounary.
*/
padLen = (alignLen - (optLen % alignLen)) % alignLen
// Prints:
/*
Pad length needed: 3
*/
fmt.Printf("Pad length needed:\t%d\n\n", padLen)
pad = bytes.Repeat([]byte{padVal}, padLen)
opts = append(opts, pad...)
// Alternatively, this can be implemented with a loop, though it's likely less efficient:
/*
for len(opts) % alignLen != 0 {}
opts = append(opts, padVal)
}
*/
// Prints:
/*
Padded:
0x9404000000000000
*/
fmt.Printf("Padded:\n%#x\n", opts)
}

111
examples/v4optstype.go Normal file
View File

@ -0,0 +1,111 @@
package main
import (
"fmt"
"log"
"strconv"
)
const (
ClassControl uint8 = iota
ClassReserved
ClassDebug
ClassReserved2
)
var (
classStr map[uint8]string = map[uint8]string{
ClassControl: "Control",
ClassReserved: "Reserved (1)",
ClassDebug: "Debugging/Measurement",
ClassReserved2: "Reserved (2)",
}
)
const (
/// This is the same type from examples/v4optspad.go.
ip4OptTypBits string = "10010100" // [1001 0100], or 148 (0x94)
)
var (
v4TypCpyOffset uint8 = 0x80 // 128
v4TypCpyMask uint8 = 0x01 // Mask to 1 bit
v4TypCpyPos uint8 = 8 - v4TypCpyMask // 7 or 0x07 (8 (bits) - mask = Shifted to 7th bit)
v4TypClsOffset uint8 = 0x60 // 96
v4TypClsMask uint8 = 0x05 // mask to 5 bits
v4TypClsPos uint8 = 8 - v4TypClsMask
v4TypNumOffset uint8 = 0x1f // 31
)
func ToV4OptTyp(copied, class, num uint8) (typ uint8) {
typ = ((copied & v4TypCpyMask) << v4TypCpyPos) | ((class & v4TypClsMask) << v4TypClsPos) | (num & v4TypNumOffset)
return
}
func FromV4OptTyp(typ uint8) (copied, class, num uint8) {
copied = (typ & v4TypCpyOffset) >> v4TypCpyPos
class = (typ & v4TypClsOffset) >> v4TypClsPos
num = (typ & v4TypNumOffset)
return
}
func main() {
var err error
var u64 uint64
var typ uint8
var cpd uint8
var cls uint8
var num uint8
// Given a type of ip4OptTypBits (see const at top)...
if u64, err = strconv.ParseUint(ip4OptTypBits, 2, 8); err != nil {
log.Panicln(err)
}
typ = uint8(u64)
// Prints:
/*
Type is: 148 (0x0094)
*/
fmt.Printf("Type is:\t%d (%#04x)\n", typ, typ)
cpd, cls, num = FromV4OptTyp(typ)
// Prints:
/*
Copied: 1 (0x0001)
Class: 0 (0x0000)
Number: 20 (0x0014)
*/
fmt.Printf(
"Copied:\t\t%d %#04x)\n"+
"Class:\t\t%d (%#04x)\n"+
"Number:\t\t%d (%#04x)\n",
cpd, cpd,
cls, cls,
num, num,
)
typ = ToV4OptTyp(cpd, cls, num)
// Prints:
/*
Confirmed Type: 148 (0x94)
*/
fmt.Printf("Confirmed Type:\t%d (%#02x)\n\n", typ, typ)
fmt.Println("Class Name:")
// Prints:
/*
Control
*/
for c, cNm := range classStr {
if c == cls {
fmt.Println(cNm)
}
}
}

View File

@ -1,8 +1,8 @@
package main
import (
`fmt`
`log`
"fmt"
"log"
"strconv"
)

View File

@ -1,8 +1,8 @@
package main
import (
`fmt`
`log`
"fmt"
"log"
"strconv"
)