<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>Posts on network-notes</title><link>https://network-notes.com/posts/</link><description>Recent content in Posts on network-notes</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><managingEditor>brett@network-notes.com (Brett Lykins)</managingEditor><webMaster>brett@network-notes.com (Brett Lykins)</webMaster><copyright>© 2015-2026 Brett Lykins</copyright><lastBuildDate>Thu, 30 Apr 2026 09:00:00 +0000</lastBuildDate><atom:link href="https://network-notes.com/posts/feed.xml" rel="self" type="application/rss+xml"/><item><title>CLI Over HTTPS Part 2: Proving It</title><link>https://network-notes.com/posts/2026/cli-over-https-2/</link><pubDate>Thu, 30 Apr 2026 09:00:00 +0000</pubDate><author>brett@network-notes.com (Brett Lykins)</author><dc:creator>Brett Lykins</dc:creator><guid>https://network-notes.com/posts/2026/cli-over-https-2/</guid><description>&lt;p&gt;In &lt;a href="https://network-notes.com/posts/2026/cli-over-https-1/"&gt;Part 1&lt;/a&gt;, I argued that SSH is a slow transport for network automation at scale and that HTTPS is fundamentally faster. Round-trip analysis and back-of-napkin math are useful, but they&amp;rsquo;re not proof. This post is the proof.&lt;/p&gt;
&lt;p&gt;I built &lt;a href="https://github.com/lykinsbd/clibench"&gt;clibench&lt;/a&gt;, a dual-protocol network device emulator and benchmark client that measures the difference at realistic latencies sourced from &lt;a href="https://www.verizon.com/business/terms/latency/"&gt;Verizon&amp;rsquo;s published backbone measurements&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="design-constraints"&gt;Design Constraints&lt;/h2&gt;
&lt;p&gt;For the comparison to mean anything, the test has to be fair. That means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Same device, same commands, same output.&lt;/strong&gt; Both transports hit the same &lt;code&gt;device.Device&lt;/code&gt; struct. The only variable is how the command arrives and how the response leaves.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Same latency for both.&lt;/strong&gt; Delay is injected at the TCP connection level, not the application level. Both SSH and HTTPS experience identical network conditions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Realistic latency values.&lt;/strong&gt; No made-up numbers. Every profile is sourced from published measurements.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multiple modes per transport.&lt;/strong&gt; SSH gets tested with fresh connections and with connection reuse (ControlMaster-style). HTTPS gets tested with fresh connections, keep-alive, multi-command batching, and config push. Each mode represents a real-world usage pattern.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="architecture"&gt;Architecture&lt;/h2&gt;
&lt;p&gt;clibench is written in Go. The project has nine packages:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;internal/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; bench/ Benchmark runner: SSH, HTTPS, proxy, and PTY modes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; device/ Shared command engine: prefix matching, transcript loading
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sshserver/ crypto/ssh listener, CiSSHGo patterns
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; httpserver/ net/http + TLS, ASA-style /admin/exec/ and /admin/config
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; proxy/ HTTPS→SSH edge proxy (fresh + pooled modes)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; netem/ tc netem latency injection (Linux, requires sudo)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; latency/ Userspace delay injection (fallback, no root)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; stats/ Benchmark statistics: percentile, summarize, parallel runner
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; tlsutil/ Shared self-signed TLS config generator
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="https://network-notes.com/img/2026/cli-over-https-bench-architecture.5fda7576f27eb2b1430a4a744666530cfc1e3a520c633554f8ab7a4da0730954.svg" alt="Benchmark architecture: client on the left, latency injection in the middle, SSH and HTTPS servers both feeding into a shared device.Device on the right" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;The benchmark client embeds its own server. No separate process needed. Latency is injected at the kernel level using Linux &lt;code&gt;tc netem&lt;/code&gt;, applied per-port on the loopback interface so both SSH and HTTPS experience identical network conditions.&lt;/p&gt;
&lt;h3 id="the-shared-command-engine"&gt;The Shared Command Engine&lt;/h3&gt;
&lt;p&gt;Both servers use the same &lt;code&gt;device.Device&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Device&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Hostname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Username&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Password&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// &amp;#34;show version&amp;#34; -&amp;gt; transcript text&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;transcriptDir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Device&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TrimSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// exact match first&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// prefix match (&amp;#34;sh ver&amp;#34; -&amp;gt; &amp;#34;show version&amp;#34;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// returns &amp;#34;% Ambiguous command&amp;#34; or &amp;#34;% Unknown command&amp;#34; on miss&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Command transcripts are plain text files loaded from a directory. The filename convention maps to the command: &lt;code&gt;show_version.txt&lt;/code&gt; becomes &lt;code&gt;show version&lt;/code&gt;. Templates support &lt;code&gt;{{.Hostname}}&lt;/code&gt; substitution. This follows the same pattern as &lt;a href="https://network-notes.com/posts/2026/cisshgo-ssh-device-emulator/"&gt;CiSSHGo&lt;/a&gt;, which I wrote about recently.&lt;/p&gt;
&lt;h3 id="the-ssh-server"&gt;The SSH Server&lt;/h3&gt;
&lt;p&gt;The SSH side uses Go&amp;rsquo;s &lt;code&gt;crypto/ssh&lt;/code&gt; package with an ed25519 host key generated at startup. It supports both exec mode (&lt;code&gt;ssh host &amp;quot;show version&amp;quot;&lt;/code&gt;) and interactive shell sessions with prompt rendering and command matching. The benchmark client tests both, since real-world tools are split: libraries like Go&amp;rsquo;s &lt;code&gt;x/crypto/ssh&lt;/code&gt; use exec mode, while Netmiko, Ansible, and Scrapli use PTY/shell.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Exec mode: split newline-delimited payloads for batch support&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;exec&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;execCmd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;:])&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;execCmd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Builder&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;execCmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;\n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cmd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TrimSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cmd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;String&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;exit-status&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="the-https-server"&gt;The HTTPS Server&lt;/h3&gt;
&lt;p&gt;The HTTPS side generates a self-signed P-256 ECDSA certificate at startup (negotiating TLS 1.3 with &lt;code&gt;TLS_AES_128_GCM_SHA256&lt;/code&gt;) and exposes the same endpoints as the &lt;a href="https://www.cisco.com/c/en/us/td/docs/security/asa/misc/http-interface/asa-http-interface.html"&gt;Cisco ASA HTTP interface&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GET /admin/exec/show+version&lt;/code&gt;. Single command, URL-encoded&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /admin/exec/cmd1/cmd2/cmd3&lt;/code&gt;. Multiple commands, slash-separated&lt;/li&gt;
&lt;li&gt;&lt;code&gt;POST /admin/config&lt;/code&gt;. Bulk commands, newline-delimited body&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Authentication is HTTP Basic over TLS, matching the ASA&amp;rsquo;s behavior.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;handleExec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TrimPrefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/admin/exec/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Builder&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cmd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReplaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;+&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cmd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TrimSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cmd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;text/plain&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;String&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="the-benchmark-client"&gt;The Benchmark Client&lt;/h3&gt;
&lt;p&gt;The client calls both transports with the same commands and measures wall-clock time.
The key difference is visible in the code: SSH requires connection setup, auth, channel open, and per-command exec requests.
HTTPS is a single HTTP call:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// SSH: connect + auth + exec per command&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ssh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;tcp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sshConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cmd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CombinedOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// HTTPS: one request, all commands&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;addr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/admin/exec/&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;httpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="latency-injection"&gt;Latency Injection&lt;/h3&gt;
&lt;p&gt;Latency is injected using Linux &lt;code&gt;tc netem&lt;/code&gt; on the loopback interface, configured entirely via the &lt;a href="https://github.com/vishvananda/netlink"&gt;&lt;code&gt;vishvananda/netlink&lt;/code&gt;&lt;/a&gt; library, the same netlink library used by Docker and Kubernetes.
The tool sets up a &lt;code&gt;prio&lt;/code&gt; qdisc with per-port &lt;code&gt;u32&lt;/code&gt; filters so that traffic to the SSH and HTTPS server ports gets the configured one-way delay, while other loopback traffic is unaffected.
This requires root or &lt;code&gt;CAP_NET_ADMIN&lt;/code&gt;, the same requirement as most raw-socket networking tools.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Netlink qdisc setup code (click to expand)&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Qdisc setup via netlink - no shell-out to tc&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;prio&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;netlink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewPrio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;netlink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;QdiscAttrs&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;LinkIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;loopbackIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;netlink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MakeHandle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;netlink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HANDLE_ROOT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;prio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Bands&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;prio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PriorityMap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;uint8&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// unmatched traffic → band 0 (no delay)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;netlink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;QdiscAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prio&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;netem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;netlink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewNetem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;netlink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;QdiscAttrs&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;netlink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MakeHandle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;netlink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NetemQdiscAttrs&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Latency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;uint32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Microseconds&lt;/span&gt;&lt;span class="p"&gt;())},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;netlink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;QdiscAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;netem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Per-port u32 filter: match dport, classify to delayed band&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;netlink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FilterAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;netlink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;U32&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;FilterAttrs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;netlink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FilterAttrs&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;netlink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MakeHandle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x0800&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ClassId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;netlink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MakeHandle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Sel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;netlink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TcU32Sel&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Flags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;netlink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TC_U32_TERMINAL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Keys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;netlink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TcU32Key&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="nx"&gt;Val&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;uint32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Mask&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0xffff&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Off&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/details&gt;
&lt;p&gt;Because &lt;code&gt;netem&lt;/code&gt; operates at the kernel&amp;rsquo;s network stack, it captures real TCP behavior: Nagle&amp;rsquo;s algorithm, delayed ACKs, TCP window scaling, and proper per-packet delay.
Every packet in both directions, client-to-server and server-to-client, experiences the configured delay.
This is more accurate than userspace delay injection, which can&amp;rsquo;t distinguish between logically separate protocol exchanges that happen to be coalesced into a single write.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;-userspace&lt;/code&gt; flag is available as a fallback for environments where root isn&amp;rsquo;t available, but the published numbers all use &lt;code&gt;tc netem&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="latency-profiles"&gt;Latency Profiles&lt;/h2&gt;
&lt;p&gt;Each profile corresponds to a real network path, sourced from &lt;a href="https://www.verizon.com/business/terms/latency/"&gt;Verizon Enterprise&amp;rsquo;s monthly IP latency statistics&lt;/a&gt; (March 2026).
The simulated RTT values are rounded for readability; the Verizon measured column shows the exact source data:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Profile&lt;/th&gt;
&lt;th&gt;Simulated RTT&lt;/th&gt;
&lt;th&gt;Real-world path&lt;/th&gt;
&lt;th&gt;Verizon measured RTT&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;local&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0ms&lt;/td&gt;
&lt;td&gt;Co-located&lt;/td&gt;
&lt;td&gt;Baseline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;campus&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2ms&lt;/td&gt;
&lt;td&gt;Same data center&lt;/td&gt;
&lt;td&gt;AWS/Prisma: 1-2ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;regional&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;30ms&lt;/td&gt;
&lt;td&gt;US backbone&lt;/td&gt;
&lt;td&gt;29.9ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;continental&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;70ms&lt;/td&gt;
&lt;td&gt;NYC ↔ London&lt;/td&gt;
&lt;td&gt;70.2ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;intercontinental&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;150ms&lt;/td&gt;
&lt;td&gt;US ↔ Hong Kong&lt;/td&gt;
&lt;td&gt;145.5ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;transpacific&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;175ms&lt;/td&gt;
&lt;td&gt;NA ↔ Taiwan&lt;/td&gt;
&lt;td&gt;175.2ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="benchmark-modes"&gt;Benchmark Modes&lt;/h2&gt;
&lt;p&gt;The client tests these scenarios across both transports (plus a multi-command GET mode when running more than one command per iteration):&lt;/p&gt;
&lt;h3 id="ssh-exec-modes"&gt;SSH Exec Modes&lt;/h3&gt;
&lt;p&gt;SSH exec mode opens a channel, sends a command, and reads the output. This is what Go&amp;rsquo;s &lt;code&gt;x/crypto/ssh&lt;/code&gt;, Paramiko&amp;rsquo;s &lt;code&gt;exec_command()&lt;/code&gt;, and OpenSSH&amp;rsquo;s &lt;code&gt;ssh host &amp;quot;cmd&amp;quot;&lt;/code&gt; use. Each command gets its own channel.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;What it measures&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ssh/fresh-conn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full SSH lifecycle per iteration: TCP + handshake + auth + channel + exec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ssh/reuse-conn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One SSH connection shared across all iterations (ControlMaster-style)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ssh/batch-exec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Multi-line command string over a single exec session&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="ssh-ptyshell-modes"&gt;SSH PTY/Shell Modes&lt;/h3&gt;
&lt;p&gt;SSH PTY mode opens an interactive shell with a pseudo-terminal, sends commands as keystrokes, and detects the prompt after each command.
This is what &lt;strong&gt;Netmiko&lt;/strong&gt;, &lt;strong&gt;Ansible &lt;code&gt;network_cli&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;Scrapli&lt;/strong&gt;, and most real-world network automation tools use.
Many network devices don&amp;rsquo;t support exec mode properly, and automation tools need prompt detection, pagination control, and mode transitions.
(Part 1 called this the &lt;a href="https://network-notes.com/posts/2026/cli-over-https-1/#the-screen-scraping-tax"&gt;&amp;ldquo;screen-scraping tax&amp;rdquo;&lt;/a&gt;, the cost of parsing an unstructured byte stream.)&lt;/p&gt;
&lt;p&gt;The PTY benchmark includes session preparation (sending &lt;code&gt;terminal length 0&lt;/code&gt; and &lt;code&gt;terminal width 511&lt;/code&gt; before the first command) and per-command echo verification (reading until the echoed command appears, then reading until the prompt), matching the protocol-level behavior common to all major tools.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;What it measures&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ssh/pty-fresh&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full SSH lifecycle + PTY + shell + session prep + commands with echo verification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ssh/pty-reuse&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shared connection, new PTY/shell per iteration with session prep&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="https-modes"&gt;HTTPS Modes&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;What it measures&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;https/fresh-conn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;New TCP + TLS handshake per iteration (&lt;code&gt;DisableKeepAlives: true&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;https/keep-alive&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Single TCP + TLS connection reused across all iterations (default HTTP behavior)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;https/batch-post&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All commands in one POST body (&lt;code&gt;/admin/config&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;https/multi-cmd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All commands in one GET request (ASA &lt;code&gt;/admin/exec/cmd1/cmd2&lt;/code&gt; syntax)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Each mode runs N iterations, each executing 5 &lt;code&gt;show version&lt;/code&gt; commands. The client reports min, max, average, p50, p95, and standard deviation.&lt;/p&gt;
&lt;h2 id="running-it-yourself"&gt;Running It Yourself&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone https://github.com/lykinsbd/clibench.git
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; clibench
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;go build -o bin/bench ./cmd/bench/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Baseline - no added latency (no root needed)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./bin/bench -latency &lt;span class="nb"&gt;local&lt;/span&gt; -iterations &lt;span class="m"&gt;20&lt;/span&gt; -commands &lt;span class="m"&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# US backbone - 30ms RTT (requires root for tc netem)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo ./bin/bench -latency regional -iterations &lt;span class="m"&gt;20&lt;/span&gt; -commands &lt;span class="m"&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# US to Hong Kong - 150ms RTT&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo ./bin/bench -latency intercontinental -iterations &lt;span class="m"&gt;20&lt;/span&gt; -commands &lt;span class="m"&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Fallback: userspace delay injection (no root, less accurate)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./bin/bench -latency regional -iterations &lt;span class="m"&gt;20&lt;/span&gt; -commands &lt;span class="m"&gt;5&lt;/span&gt; -userspace
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;clibench embeds its own server. No separate process needed. Non-local profiles require root (or &lt;code&gt;CAP_NET_ADMIN&lt;/code&gt;) for &lt;code&gt;tc netem&lt;/code&gt; on the loopback interface. Output is JSON.&lt;/p&gt;
&lt;h2 id="results"&gt;Results&lt;/h2&gt;
&lt;p&gt;5 commands per iteration, all times in milliseconds (average of 20 iterations). Latency injected via &lt;code&gt;tc netem&lt;/code&gt; on the loopback interface.&lt;/p&gt;
&lt;p&gt;At zero latency, SSH exec mode is the fastest option. There&amp;rsquo;s no round-trip penalty, and SSH&amp;rsquo;s binary framing has less per-message overhead than HTTP headers + TLS:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://network-notes.com/img/2026/cli-over-https-bench-local.016c463cda2ffbff3ef237d3f2986ee2b27c37a09c455f2c394e3c0b416ba37c.svg" alt="Bar chart at 0ms RTT showing SSH exec-fresh at 3.9ms, PTY-fresh at 4.3ms, and HTTPS fresh at 12.0ms" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;The moment real network latency enters the picture, the result flips. At 30ms RTT, a US backbone path per Verizon&amp;rsquo;s March 2026 measurements, HTTPS batch is 16.8x faster than SSH PTY fresh and 15.9x faster than SSH exec fresh-conn:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://network-notes.com/img/2026/cli-over-https-bench-regional.eb891e20708c51e9da713ce909b2088de07d106485a700f823d35833d01957f0.svg" alt="Bar chart at 30ms RTT showing SSH exec-fresh at 494ms, PTY-fresh at 522ms, and HTTPS batch at 31ms" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;At intercontinental distances (US ↔ Hong Kong, 150ms RTT), SSH PTY fresh takes 2.6 seconds for 5 commands. SSH exec fresh takes 2.4 seconds. HTTPS batch does it in 151ms:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://network-notes.com/img/2026/cli-over-https-bench-intercontinental.5a06f3614b9f61e6c31b509722e421529c638a65a69e5f8545afcafdfff06c33.svg" alt="Bar chart at 150ms RTT showing SSH PTY-fresh at 2,565ms, exec-fresh at 2,412ms, and HTTPS batch at 151ms" loading="lazy" /&gt;&lt;/p&gt;
&lt;h3 id="exec-mode-vs-ptyshell-mode"&gt;Exec Mode vs PTY/Shell Mode&lt;/h3&gt;
&lt;p&gt;The PTY overhead comes from session preparation (&lt;code&gt;terminal length 0&lt;/code&gt;, &lt;code&gt;terminal width 511&lt;/code&gt;) and per-command echo verification. At higher latencies, this adds up:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Profile&lt;/th&gt;
&lt;th&gt;RTT&lt;/th&gt;
&lt;th&gt;SSH exec fresh&lt;/th&gt;
&lt;th&gt;SSH PTY fresh&lt;/th&gt;
&lt;th&gt;PTY overhead&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;local&lt;/td&gt;
&lt;td&gt;0ms&lt;/td&gt;
&lt;td&gt;3.9ms&lt;/td&gt;
&lt;td&gt;4.3ms&lt;/td&gt;
&lt;td&gt;+0.4ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;campus&lt;/td&gt;
&lt;td&gt;2ms&lt;/td&gt;
&lt;td&gt;40ms&lt;/td&gt;
&lt;td&gt;42ms&lt;/td&gt;
&lt;td&gt;+2ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;regional&lt;/td&gt;
&lt;td&gt;30ms&lt;/td&gt;
&lt;td&gt;494ms&lt;/td&gt;
&lt;td&gt;522ms&lt;/td&gt;
&lt;td&gt;+28ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;continental&lt;/td&gt;
&lt;td&gt;70ms&lt;/td&gt;
&lt;td&gt;1,144ms&lt;/td&gt;
&lt;td&gt;1,213ms&lt;/td&gt;
&lt;td&gt;+69ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;intercontinental&lt;/td&gt;
&lt;td&gt;150ms&lt;/td&gt;
&lt;td&gt;2,412ms&lt;/td&gt;
&lt;td&gt;2,565ms&lt;/td&gt;
&lt;td&gt;+153ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The PTY overhead scales linearly with RTT because the session prep commands add roughly one extra round trip of overhead before the first real command runs. At 150ms RTT, that&amp;rsquo;s ~150ms of pure protocol overhead. And this is the best case. Real devices add processing time, ANSI escape codes, and prompt detection regex that the emulator doesn&amp;rsquo;t capture.&lt;/p&gt;
&lt;h3 id="speedup-vs-ssh-pty-fresh-what-most-tools-actually-use"&gt;Speedup vs SSH PTY fresh (what most tools actually use)&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Profile&lt;/th&gt;
&lt;th&gt;RTT&lt;/th&gt;
&lt;th&gt;SSH exec fresh&lt;/th&gt;
&lt;th&gt;SSH reuse&lt;/th&gt;
&lt;th&gt;HTTPS keep-alive&lt;/th&gt;
&lt;th&gt;HTTPS batch&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;local&lt;/td&gt;
&lt;td&gt;0ms&lt;/td&gt;
&lt;td&gt;1.1x&lt;/td&gt;
&lt;td&gt;3.9x&lt;/td&gt;
&lt;td&gt;7.2x&lt;/td&gt;
&lt;td&gt;19.1x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;campus&lt;/td&gt;
&lt;td&gt;2ms&lt;/td&gt;
&lt;td&gt;1.1x&lt;/td&gt;
&lt;td&gt;1.8x&lt;/td&gt;
&lt;td&gt;3.3x&lt;/td&gt;
&lt;td&gt;16.3x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;regional&lt;/td&gt;
&lt;td&gt;30ms&lt;/td&gt;
&lt;td&gt;1.1x&lt;/td&gt;
&lt;td&gt;1.7x&lt;/td&gt;
&lt;td&gt;3.3x&lt;/td&gt;
&lt;td&gt;16.8x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;continental&lt;/td&gt;
&lt;td&gt;70ms&lt;/td&gt;
&lt;td&gt;1.1x&lt;/td&gt;
&lt;td&gt;1.7x&lt;/td&gt;
&lt;td&gt;3.4x&lt;/td&gt;
&lt;td&gt;16.3x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;intercontinental&lt;/td&gt;
&lt;td&gt;150ms&lt;/td&gt;
&lt;td&gt;1.1x&lt;/td&gt;
&lt;td&gt;1.7x&lt;/td&gt;
&lt;td&gt;3.4x&lt;/td&gt;
&lt;td&gt;17.0x&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="what-the-numbers-say"&gt;What the Numbers Say&lt;/h2&gt;
&lt;p&gt;All results are from 20 iterations per profile. Variance was low, at regional (30ms), SSH exec fresh-conn p50 was 492ms with p95 at 508ms.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;At zero latency, SSH exec wins.&lt;/strong&gt; When there&amp;rsquo;s no network delay, TLS handshake overhead dominates. SSH exec fresh-conn takes 3.9ms; HTTPS fresh-conn takes 12.0ms. But PTY mode is already slower at 4.3ms due to session prep overhead. The reuse modes tell a different story: SSH exec reuse (1.1ms) and HTTPS keep-alive (0.6ms) are both sub-millisecond. Once the handshake is amortized, both protocols are fast.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Most automation tools don&amp;rsquo;t use exec mode.&lt;/strong&gt; As covered &lt;a href="https://network-notes.com/posts/2026/cli-over-https-2/#ssh-ptyshell-modes"&gt;above&lt;/a&gt;, they use PTY/shell mode for prompt detection, pagination control, and mode transitions. The PTY numbers are what your automation actually experiences.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SSH reuse helps, but not enough.&lt;/strong&gt; Sharing one SSH connection (the ControlMaster pattern) eliminates the handshake cost, but each command still requires its own round trips. The improvement is consistent at ~1.7x. Real, but modest.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;HTTPS keep-alive is ~3.4x faster at any real latency.&lt;/strong&gt; Every HTTP client library does connection pooling by default. You don&amp;rsquo;t have to configure anything special. Just reuse the &lt;code&gt;http.Client&lt;/code&gt;. At 30ms RTT, that&amp;rsquo;s 158ms vs 522ms (PTY fresh).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;HTTPS batch is ~17x faster.&lt;/strong&gt; Batching all commands into a single HTTP request eliminates per-command round trips entirely. The entire exchange costs one round trip regardless of command count. At 150ms RTT, that&amp;rsquo;s 151ms vs 2,565ms (PTY fresh). Unlike keep-alive (which still pays one round trip per command), batch mode pays a fixed cost regardless of command count.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The advantage grows with command count, for per-command modes.&lt;/strong&gt; At 30ms RTT with 50 commands, SSH exec fresh-conn takes 3,253ms. SSH PTY fresh takes 1,912ms (PTY avoids per-command channel overhead but pays per-command echo verification). HTTPS keep-alive takes 1,548ms. But HTTPS batch takes just 33ms. A ~99x improvement over exec fresh and ~58x over PTY fresh. SSH batch-exec shows the same flat scaling (~250ms regardless of command count), confirming this is a property of batching, not the transport.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://network-notes.com/img/2026/cli-over-https-command-scaling.7c056e6f62149c833ff6d10a41480272480f71fb76eaa5acb3ddc1d8a7df97dc.svg" alt="Time vs command count at 30ms RTT: batch modes stay flat while per-command modes scale linearly" loading="lazy" /&gt;&lt;/p&gt;
&lt;h2 id="what-this-means-at-scale"&gt;What This Means at Scale&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;re managing 100 devices serially (worst case, no concurrency), using PTY mode (what Netmiko/Ansible actually do):&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Profile&lt;/th&gt;
&lt;th&gt;RTT&lt;/th&gt;
&lt;th&gt;SSH PTY fresh (total)&lt;/th&gt;
&lt;th&gt;HTTPS batch (total)&lt;/th&gt;
&lt;th&gt;Time saved&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;regional&lt;/td&gt;
&lt;td&gt;30ms&lt;/td&gt;
&lt;td&gt;52s&lt;/td&gt;
&lt;td&gt;3.1s&lt;/td&gt;
&lt;td&gt;49s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;continental&lt;/td&gt;
&lt;td&gt;70ms&lt;/td&gt;
&lt;td&gt;121s (2.0 min)&lt;/td&gt;
&lt;td&gt;7.4s&lt;/td&gt;
&lt;td&gt;114s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;intercontinental&lt;/td&gt;
&lt;td&gt;150ms&lt;/td&gt;
&lt;td&gt;257s (4.3 min)&lt;/td&gt;
&lt;td&gt;15s&lt;/td&gt;
&lt;td&gt;242s (4.0 min)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Concurrency shrinks the wall time, but the per-device cost stays the same. At 150ms RTT with 10 concurrent workers against 1,000 devices, SSH PTY takes ~4.3 minutes of wall time. HTTPS batch takes ~15 seconds.&lt;/p&gt;
&lt;h2 id="limitations"&gt;Limitations&lt;/h2&gt;
&lt;p&gt;This benchmark measures transport overhead, not device processing time. Real network devices add their own latency to command execution: parsing the command, generating output, writing to the terminal. That cost is the same regardless of transport, so it doesn&amp;rsquo;t affect the relative comparison.&lt;/p&gt;
&lt;p&gt;The HTTPS server uses a self-signed certificate with no session resumption. TLS 1.3 0-RTT resumption would make the HTTPS numbers even better on repeated connections, but I didn&amp;rsquo;t implement it because most device management scenarios don&amp;rsquo;t maintain long-lived TLS sessions.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;-userspace&lt;/code&gt; flag is available as a fallback for environments where root isn&amp;rsquo;t available, but it under-counts SSH round trips due to write coalescing in Go&amp;rsquo;s &lt;code&gt;crypto/ssh&lt;/code&gt;. The published numbers all use &lt;code&gt;tc netem&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="whats-next"&gt;What&amp;rsquo;s Next&lt;/h2&gt;
&lt;p&gt;In Part 3, I&amp;rsquo;ll look at what happens when you can&amp;rsquo;t change the device: the proxy pattern. Move SSH to the edge, talk HTTPS over the WAN, and capture most of the improvement without touching a single device config.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://github.com/lykinsbd/clibench"&gt;benchmark code&lt;/a&gt; already supports proxy mode. Try it yourself and see what your numbers look like.&lt;/p&gt;</description></item><item><title>CLI Over HTTPS Part 1: The Protocol Tax</title><link>https://network-notes.com/posts/2026/cli-over-https-1/</link><pubDate>Tue, 28 Apr 2026 09:00:00 +0000</pubDate><author>brett@network-notes.com (Brett Lykins)</author><dc:creator>Brett Lykins</dc:creator><guid>https://network-notes.com/posts/2026/cli-over-https-1/</guid><description>&lt;p&gt;During my six years at Rackspace, we spent a lot of time thinking about how to interact with network devices faster.
We had tens of thousands of them; firewalls, load balancers, switches, routers and more, all spread across multiple data centers on four continents.&lt;/p&gt;
&lt;p&gt;In the early days, managing devices across these data centers meant running shell scripts, Expect, or Perl from local machines and centralized bastions over the WAN.
It was operationally painful.
SSH connections to devices on other continents were slow enough that teams scheduled automation runs around maintenance windows not just because of the change itself, but because of how long it took to deliver the changes.&lt;/p&gt;
&lt;p&gt;An Erlang-based platform solved this by co-locating the SSH connections with the devices.
They ran inside each data center, talking SSH to devices over local links where the protocol overhead was negligible.
Phil Toland &lt;a href="https://www.infoq.com/presentations/Erlang-Ruby-Rackspace/"&gt;presented the Erlang architecture at Erlang Factory 2012&lt;/a&gt;, detailing Erlang and Ruby managing backups and automation for 20,000+ network devices across 8 data centers.
My team later supplemented it with a Go microservices architecture to provide API-driven access to device CLIs.
Both systems were effective not just because of language capabilities; crucially they were fast because SSH stayed local.&lt;/p&gt;
&lt;p&gt;But, even with co-located endpoints, the Cisco ASA fleet was a special case which tested our capabilities due to the extreme size of some of the Access Lists.
That&amp;rsquo;s when someone discovered that the ASA has an HTTP interface we could use.
Not the ineffective ASA Java-based REST API, but an actual CLI-over-HTTPS endpoint used by the ASDM client.
There was a URL on the device where you could send the same commands you&amp;rsquo;d type into an SSH session, but over an HTTPS request.
We tried it as an experiment, and it was remarkably faster.
What started as a curiosity became a production lifesaver for our ASA fleet.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been thinking about that experience ever since, and I finally decided to quantify it properly.
In this series of posts, I will quantify the performance difference between SSH and HTTPS as CLI transports and explain why the gap exists.&lt;/p&gt;
&lt;h2 id="the-ssh-tax"&gt;The SSH Tax&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;For a deeper look at SSH&amp;rsquo;s protocol layers, channel types, and how NETCONF and RESTCONF fit in, see &lt;a href="https://network-notes.com/posts/2026/ssh-under-the-hood/"&gt;What Actually Happens When You SSH Into a Router&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When your automation tool opens an SSH connection to a network device, here&amp;rsquo;s what actually happens on the wire before a single byte of command output comes back.
(Throughout this series, &amp;ldquo;RT&amp;rdquo; means a round trip: one message out, one message back. &amp;ldquo;RTT&amp;rdquo; is the round-trip time in milliseconds for a given network path.)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. TCP three-way handshake.&lt;/strong&gt; SYN, SYN-ACK, ACK. One round trip. (The ACK can piggyback on the first data segment, but the connection isn&amp;rsquo;t usable until the handshake completes.)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Protocol version exchange.&lt;/strong&gt; Client and server each send an identification string (&lt;code&gt;SSH-2.0-OpenSSH_9.6&lt;/code&gt;). Another round trip.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Key exchange.&lt;/strong&gt; The client and server negotiate algorithms (encryption, MAC, compression) via KEXINIT, then perform a Diffie-Hellman key exchange. This takes 1-3 round trips depending on whether the KEXINIT messages cross in flight and which DH group is negotiated.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Service request.&lt;/strong&gt; The client requests the &lt;code&gt;ssh-userauth&lt;/code&gt; service. One round trip.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. User authentication.&lt;/strong&gt; Password or public key auth. 1-3 round trips depending on how many methods the server probes (GSSAPI, publickey, then password, each one a round trip).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6. Channel open.&lt;/strong&gt; SSH multiplexes channels over a single connection. Opening a session channel is another round trip.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s &lt;strong&gt;6-10 round trips&lt;/strong&gt; just to get an authenticated channel. If you&amp;rsquo;re running a single exec-style command, add one more round trip for the request/response and you&amp;rsquo;re done.&lt;/p&gt;
&lt;p&gt;But automation tools don&amp;rsquo;t use exec mode. Netmiko, Ansible, and Scrapli open a PTY/shell channel, which adds:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7. PTY request.&lt;/strong&gt; Ask the server for a pseudo-terminal. One round trip.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8. Shell request.&lt;/strong&gt; Start an interactive shell on that PTY. One round trip.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9. Session prep.&lt;/strong&gt; Send &lt;code&gt;terminal length 0&lt;/code&gt;, &lt;code&gt;terminal width 511&lt;/code&gt;, and wait for each prompt. Two to three more round trips.&lt;/p&gt;
&lt;p&gt;Add it up: &lt;strong&gt;10-15 round trips&lt;/strong&gt; before you see the output of &lt;code&gt;show version&lt;/code&gt;, with most real-world automation sessions landing at 10-12. The &lt;a href="https://github.com/francoismichel/ssh3"&gt;SSH3 project&lt;/a&gt; cites similar overhead in their motivation for building SSH over HTTP/3.&lt;/p&gt;
&lt;p&gt;SSH multiplexing (ControlMaster) amortizes the connection setup cost across sessions, but the first connection still pays the full overhead. It helps for repeated connections to the same device, but doesn&amp;rsquo;t solve the problem at scale across thousands of hosts.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://network-notes.com/img/2026/ssh-connection-lifecycle.c495d377728639bd46d3e72a152d0060cd70f07f15913d5753113b9d35d43035.svg" alt="SSH connection lifecycle showing 10-12 round trips: TCP handshake, version exchange, key exchange, authentication, channel open, PTY request, shell request, session prep, and first command" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;At zero latency (localhost), nobody cares. At real-world distances, it compounds fast.&lt;/p&gt;
&lt;h2 id="what-this-costs-at-real-distances"&gt;What This Costs at Real Distances&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.verizon.com/business/terms/latency/"&gt;Verizon Enterprise publishes monthly backbone latency measurements&lt;/a&gt; from their global network. Here&amp;rsquo;s what SSH connection setup costs at those measured round-trip times, assuming 10-15 round trips for a typical PTY/shell automation session:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;Measured RTT&lt;/th&gt;
&lt;th&gt;SSH setup (10-15 RT)&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;US backbone (intra-region)&lt;/td&gt;
&lt;td&gt;30ms&lt;/td&gt;
&lt;td&gt;300-450ms&lt;/td&gt;
&lt;td&gt;Verizon Mar 2026: 29.9ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transatlantic (NYC ↔ London)&lt;/td&gt;
&lt;td&gt;70ms&lt;/td&gt;
&lt;td&gt;700-1,050ms&lt;/td&gt;
&lt;td&gt;Verizon Mar 2026: 70.2ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;US ↔ Hong Kong&lt;/td&gt;
&lt;td&gt;146ms&lt;/td&gt;
&lt;td&gt;1,460-2,190ms&lt;/td&gt;
&lt;td&gt;Verizon Mar 2026: 145.5ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;US ↔ New Zealand&lt;/td&gt;
&lt;td&gt;174ms&lt;/td&gt;
&lt;td&gt;1,740-2,610ms&lt;/td&gt;
&lt;td&gt;Verizon Mar 2026: 174.2ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;That&amp;rsquo;s just connection setup.
You haven&amp;rsquo;t sent a command yet.
And if your automation opens a fresh SSH connection per device, or per task, which is &lt;a href="https://www.howtouselinux.com/post/the-hidden-ssh-setting-that-makes-ansible-playbooks-faster"&gt;Ansible&amp;rsquo;s default behavior without ControlMaster&lt;/a&gt;, you pay this cost repeatedly.&lt;/p&gt;
&lt;p&gt;A thousand devices at 30ms RTT without concurrency: 300-450 seconds of pure SSH handshake overhead. At 150ms RTT: 1,500-2,250 seconds. Concurrency reduces wall time, but every device still pays the full per-connection cost.&lt;/p&gt;
&lt;h2 id="the-screen-scraping-tax"&gt;The Screen-Scraping Tax&lt;/h2&gt;
&lt;p&gt;SSH&amp;rsquo;s round-trip overhead is only half the story. The other half is what happens after you connect.&lt;/p&gt;
&lt;p&gt;SSH gives you a byte stream. A pseudo-terminal. Your automation tool has to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Detect the prompt&lt;/strong&gt; to know when command output is complete. Netmiko does this with regex matching on every chunk of bytes received, waiting for a pattern like &lt;code&gt;hostname#&lt;/code&gt;. If the output is large or arrives in small TCP segments, this means multiple read cycles.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Handle pagination.&lt;/strong&gt; &lt;code&gt;--More--&lt;/code&gt; prompts. Most tools send &lt;code&gt;terminal length 0&lt;/code&gt; first, which is another command round trip.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Navigate mode transitions.&lt;/strong&gt; &lt;code&gt;enable&lt;/code&gt;, &lt;code&gt;configure terminal&lt;/code&gt;, &lt;code&gt;interface GigabitEthernet0/0&lt;/code&gt;. Each one is a command, a prompt change, and a round trip.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wait for command completion.&lt;/strong&gt; Netmiko&amp;rsquo;s default &lt;code&gt;read_timeout&lt;/code&gt; adds deliberate delays to avoid reading partial output.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of this is Netmiko&amp;rsquo;s fault. It&amp;rsquo;s doing the best it can with what SSH gives it: an unstructured byte stream with no framing, no content-length, no end-of-message delimiter. The tool has to infer when the device is done talking. Compare that to HTTPS (&lt;code&gt;Content-Length&lt;/code&gt;), NETCONF (&lt;code&gt;]]&amp;gt;]]&amp;gt;&lt;/code&gt; delimiter), or even SSH exec mode (exit codes). PTY is the only mode where the client has to guess when the response is complete.&lt;/p&gt;
&lt;h2 id="the-https-alternative"&gt;The HTTPS Alternative&lt;/h2&gt;
&lt;p&gt;Now consider what happens when you send the same command over HTTPS:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. TCP three-way handshake.&lt;/strong&gt; Same as SSH. One round trip.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. TLS 1.3 handshake.&lt;/strong&gt; 1 round trip. (TLS 1.2 was 2 round trips; &lt;a href="https://datatracker.ietf.org/doc/html/rfc8446"&gt;TLS 1.3 cut it in half&lt;/a&gt;. With session resumption, it&amp;rsquo;s 0 round trips.)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. HTTP request/response.&lt;/strong&gt; Send the command, get the output. 1 round trip.&lt;/p&gt;
&lt;p&gt;Total: &lt;strong&gt;about 3 round trips&lt;/strong&gt; for a fresh connection. With connection reuse (HTTP keep-alive, which every HTTP client library does by default): &lt;strong&gt;1 round trip per command&lt;/strong&gt; after the first.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://network-notes.com/img/2026/https-connection-lifecycle.6eccac0763c450bcb998f810f0e09b2a21b3807bb16af02efe6e84a79d3f3784.svg" alt="HTTPS connection lifecycle showing ~3 round trips: TCP handshake, TLS 1.3 handshake, HTTP request and response" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;Side by side, the difference is stark.
Here&amp;rsquo;s the same operation (5 commands at 30ms RTT) over both protocols:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://network-notes.com/img/2026/ssh-vs-https-timeline.58a901d90c485d14c887a2dcde7437eb25d29c9a37727c56f65aba89df56a0e0.svg" alt="Side-by-side timeline: SSH PTY/shell takes 15 round trips and 522ms, HTTPS batch takes 3 round trips and 31ms" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;And here&amp;rsquo;s the part that changes the math entirely: &lt;strong&gt;you can batch commands&lt;/strong&gt;. The Cisco ASA HTTP interface accepts multiple commands in a single request, either slash-separated in the URL or newline-delimited in a POST body. Ten commands, one request, one response. That&amp;rsquo;s 1 round trip for 10 commands, vs SSH where each command requires its own channel-open and exec round trips.&lt;/p&gt;
&lt;p&gt;No prompt detection. No pagination handling. No mode transitions. The response body is the command output, with a proper HTTP &lt;code&gt;Content-Length&lt;/code&gt; header. Your client knows exactly when the response is complete.&lt;/p&gt;
&lt;h2 id="prior-art-the-cisco-asa-http-interface"&gt;Prior Art: The Cisco ASA HTTP Interface&lt;/h2&gt;
&lt;p&gt;This isn&amp;rsquo;t a theoretical proposal. Cisco has shipped an HTTP-based CLI interface on the ASA for years. Their &lt;a href="https://www.cisco.com/c/en/us/td/docs/security/asa/misc/http-interface/asa-http-interface.html"&gt;own documentation&lt;/a&gt; opens with this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;One way to interface with most network appliances including ASAs is via CLI. An automated tool could Telnet or SSH into a device, authenticate and execute commands, one at a time. This method has a number of drawbacks, however. The tool must maintain the state of the Telnet and SSH connection, and if that connection is broken, the login process has to be repeated. Using CLI, it is only possible to send one command at a time, so administering many firewalls would be time consuming, &lt;strong&gt;especially when the firewalls are some latency away from the management station&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Cisco identified the exact problem and shipped a solution. The interface is straightforward:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Single command&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk -u admin:admin https://asa.example.com/admin/exec/show+version
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Multiple commands in one request&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk -u admin:admin https://asa.example.com/admin/exec/show+version/show+ip+interface+brief
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Bulk config push&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk -u admin:admin -X POST --data-binary @config.txt &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; https://asa.example.com/admin/config
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Basic auth over TLS. URL-encoded commands in the path. Newline-delimited commands in a POST body. No SDK, no client library, no special protocol. Just HTTPS.&lt;/p&gt;
&lt;p&gt;Aaron Hackney, another principal engineer at Rackspace at the time who was dealing with the same fleet of ASAs, went deep on this interface in a &lt;a href="https://community.cisco.com/t5/security-blogs/script-an-asdm-session-part-i/bc-p/3663026"&gt;BRKSEC-2031 session at Cisco Live Orlando 2018&lt;/a&gt;.
The work involved reverse-engineering the ASDM client&amp;rsquo;s HTTP calls and building Python tooling to script against the same interface.
The two-part Cisco community blog series that followed is still one of the best references for anyone looking to automate ASAs over HTTPS instead of SSH.&lt;/p&gt;
&lt;h2 id="great-but-my-switches-dont-have-an-https-cli"&gt;&amp;ldquo;Great, but My Switches Don&amp;rsquo;t Have an HTTPS CLI&amp;rdquo;&lt;/h2&gt;
&lt;p&gt;The network automation community has spent a decade building tooling around SSH.
Netmiko, Paramiko, Ansible&amp;rsquo;s &lt;code&gt;network_cli&lt;/code&gt;, Nornir, NAPALM. All SSH-based for CLI interaction.
That investment is real and valuable. NETCONF and gNMI exist as alternatives, but they require device support for structured data models.
A different paradigm entirely. Many organizations have thousands of CLI commands, templates, and playbooks that work.
They don&amp;rsquo;t need a new data model.
They need a faster pipe.&lt;/p&gt;
&lt;p&gt;And the ASA&amp;rsquo;s HTTP interface is the exception, not the rule.
Most network platforms (IOS, NX-OS, EOS, Junos) don&amp;rsquo;t expose their CLI over HTTPS.
Realistically, that&amp;rsquo;s not going to change across the industry without a major shift in how vendors think about management plane interfaces.
I&amp;rsquo;m not holding my breath.&lt;/p&gt;
&lt;p&gt;But the protocol overhead problem doesn&amp;rsquo;t go away just because the devices don&amp;rsquo;t support the better transport natively.
So what do you do?&lt;/p&gt;
&lt;p&gt;You move the expensive SSH transactions to the edge.
Put a lightweight proxy, an API gateway, a microservice, whatever you want to call it, close to the devices it manages.
That proxy talks SSH to the devices over a low-latency local network where the round-trip overhead is negligible.
Your automation platform talks HTTPS to the proxy over the WAN, where the round-trip savings actually matter.&lt;/p&gt;
&lt;p&gt;The pattern looks like this: your centralized automation (Ansible, Nornir, custom tooling) sends an HTTPS request to a proxy co-located with the devices.
The proxy opens an SSH session to the device on a 1-2ms local link, executes the commands, and returns the output in the HTTP response.
Your automation never touches SSH directly.
It gets the speed of HTTPS over the WAN and the compatibility of SSH on the last hop.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://network-notes.com/img/2026/cli-over-https-proxy-concept.ce050a5b98dc507728b570b47f6e8daadd209ff85ebba3533b93cf6be936e078.svg" alt="Edge proxy architecture: automation talks HTTPS over the WAN to a co-located proxy, which talks SSH to devices over a 1-2ms local link" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;This isn&amp;rsquo;t hypothetical.
It&amp;rsquo;s the architecture behind tools like my &lt;a href="https://github.com/lykinsbd/naas"&gt;NAAS (Netmiko as a Service)&lt;/a&gt; application, which wraps Netmiko&amp;rsquo;s SSH sessions behind a REST API.
Deploy a NAAS instance in each region or data center, and your automation talks HTTP to the nearest one.
The SSH overhead stays local.
The WAN traffic is pure HTTPS.&lt;/p&gt;
&lt;p&gt;In &lt;a href="https://network-notes.com/posts/2026/cli-over-https-2/"&gt;Part 2&lt;/a&gt;, I&amp;rsquo;ll detail a dual-protocol device emulator I built in Go that serves the same commands over both SSH and HTTPS, and a benchmark client that measures the difference at realistic latencies.
The &lt;a href="https://github.com/lykinsbd/clibench"&gt;code is already public&lt;/a&gt; if you want to run ahead.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;My take:&lt;/strong&gt; SSH is a fine protocol for interactive terminal sessions.
It&amp;rsquo;s a poor protocol for automation at scale.
The round-trip overhead is baked into the protocol design, and no amount of connection pooling or multiplexing fully eliminates it.
HTTPS with TLS 1.3 is a strictly better transport for the &amp;ldquo;send command, get output&amp;rdquo; pattern that defines CLI automation.
The industry should be building toward it.&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>What Actually Happens When You SSH Into a Router</title><link>https://network-notes.com/posts/2026/ssh-under-the-hood/</link><pubDate>Thu, 23 Apr 2026 11:00:00 +0000</pubDate><author>brett@network-notes.com (Brett Lykins)</author><dc:creator>Brett Lykins</dc:creator><guid>https://network-notes.com/posts/2026/ssh-under-the-hood/</guid><description>&lt;p&gt;You type &lt;code&gt;ssh router1&lt;/code&gt;, hit enter, and a prompt appears. Simple.&lt;/p&gt;
&lt;p&gt;Between those two events, your client and the device exchange dozens of packets across three protocol layers, negotiate cryptographic algorithms, perform a key exchange, authenticate you, open a channel, and (if you&amp;rsquo;re using a tool like Netmiko) request a pseudo-terminal, detect the prompt, disable pagination, and set the terminal width. All before a single &lt;code&gt;show&lt;/code&gt; command runs.&lt;/p&gt;
&lt;p&gt;Most network engineers use SSH every day without thinking about any of this. That&amp;rsquo;s fine for interactive use. But if you&amp;rsquo;re building automation that opens hundreds or thousands of SSH sessions, every one of those steps costs at least one network round trip (sometimes more), and round trip times are usually the single biggest factor in how fast your automation runs.&lt;/p&gt;
&lt;h2 id="ssh-is-three-protocols-not-one"&gt;SSH Is Three Protocols, Not One&lt;/h2&gt;
&lt;p&gt;SSH isn&amp;rsquo;t a single protocol. &lt;a href="https://datatracker.ietf.org/doc/html/rfc4251"&gt;RFC 4251&lt;/a&gt; describes it as &amp;ldquo;three major components,&amp;rdquo; each defined in its own RFC:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;RFC&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Transport&lt;/td&gt;
&lt;td&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc4253"&gt;RFC 4253&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Key exchange, encryption, integrity, server authentication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authentication&lt;/td&gt;
&lt;td&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc4252"&gt;RFC 4252&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;User authentication (password, publickey, keyboard-interactive)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connection&lt;/td&gt;
&lt;td&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc4254"&gt;RFC 4254&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Channels, multiplexing, port forwarding, sessions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A fourth RFC, &lt;a href="https://datatracker.ietf.org/doc/html/rfc4251"&gt;RFC 4251&lt;/a&gt; (Architecture), defines the overall design, terminology, and trust model. It&amp;rsquo;s the framework that ties the three wire protocols together.&lt;/p&gt;
&lt;p&gt;Each layer runs on top of the previous one. The transport layer gives you an encrypted pipe. The authentication layer proves who you are. The connection layer multiplexes that pipe into channels, and channels are where the actual work happens.&lt;/p&gt;
&lt;p&gt;The application (your shell session, your &lt;code&gt;show version&lt;/code&gt; command, your NETCONF RPC) rides on top of a channel.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://network-notes.com/img/2026/ssh-protocol-stack.f8bf5ca2596bf6b78e6909738a61cbd7854c044aa2a6abf3f9313c0c8fc038b4.svg" alt="SSH protocol stack showing three layers from TCP up through Application" loading="lazy" /&gt;&lt;/p&gt;
&lt;h2 id="tcp"&gt;TCP&lt;/h2&gt;
&lt;p&gt;SSH runs over TCP; so before any SSH packets flow, you need a TCP connection:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Client → Server:&lt;/strong&gt; SYN&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server → Client:&lt;/strong&gt; SYN-ACK&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Client → Server:&lt;/strong&gt; ACK&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Cost: 1 round trip.&lt;/strong&gt; The ACK can piggyback on the first SSH data, but the connection isn&amp;rsquo;t usable until the handshake completes.&lt;/p&gt;
&lt;p&gt;This is the same cost every TCP-based protocol pays. Nothing SSH-specific yet.&lt;/p&gt;
&lt;h2 id="ssh-transport"&gt;SSH Transport&lt;/h2&gt;
&lt;h3 id="protocol-version-exchange"&gt;Protocol Version Exchange&lt;/h3&gt;
&lt;p&gt;Immediately after TCP connects, both sides send an identification string:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SSH-2.0-OpenSSH_9.6
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SSH-2.0-Cisco-1.25
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Both sides send simultaneously, so this can overlap with the TCP ACK. In practice, the client usually sends first and waits for the server&amp;rsquo;s response.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cost: 1 round trip&lt;/strong&gt; for the version exchange. Implementations vary on whether KEXINIT is pipelined with the version string or sent after receiving the server&amp;rsquo;s version.&lt;/p&gt;
&lt;h3 id="algorithm-negotiation-kexinit"&gt;Algorithm Negotiation (KEXINIT)&lt;/h3&gt;
&lt;p&gt;Both sides send &lt;code&gt;SSH_MSG_KEXINIT&lt;/code&gt;, a packet listing every algorithm they support, in preference order:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Key exchange methods (e.g., &lt;code&gt;curve25519-sha256&lt;/code&gt;, &lt;code&gt;diffie-hellman-group14-sha256&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Host key algorithms (e.g., &lt;code&gt;ssh-ed25519&lt;/code&gt;, &lt;code&gt;rsa-sha2-512&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Encryption algorithms (e.g., &lt;code&gt;aes256-gcm@openssh.com&lt;/code&gt;, &lt;code&gt;chacha20-poly1305@openssh.com&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;MAC algorithms (e.g., &lt;code&gt;hmac-sha2-256-etm@openssh.com&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Compression algorithms (usually &lt;code&gt;none&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;RFC 4253 says &amp;ldquo;key exchange will begin immediately after sending [the version] identifier,&amp;rdquo; meaning each side can fire off its KEXINIT right after its version string without waiting. If both sides do this, the KEXINIT packets arrive during the same round trip as the version exchange, and the cost is zero additional round trips. If the client waits for the server&amp;rsquo;s version string before sending KEXINIT (which some implementations do), it costs a separate round trip.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cost: 0-1 round trips&lt;/strong&gt; depending on whether the implementation pipelines KEXINIT with the version string.&lt;/p&gt;
&lt;h3 id="key-exchange"&gt;Key Exchange&lt;/h3&gt;
&lt;p&gt;The exact packet sequence depends on the algorithm:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Modern (curve25519-sha256, ecdh-sha2-nistp256):&lt;/strong&gt; 1 round trip.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Client → Server:&lt;/strong&gt; &lt;code&gt;SSH_MSG_KEX_ECDH_INIT&lt;/code&gt; (client&amp;rsquo;s ephemeral public key)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server → Client:&lt;/strong&gt; &lt;code&gt;SSH_MSG_KEX_ECDH_REPLY&lt;/code&gt; (server&amp;rsquo;s ephemeral public key + host key + signature)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Both sides now have a shared secret. The server&amp;rsquo;s signature proves it holds the private key matching the host key the client expects.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Legacy (diffie-hellman-group-exchange-sha256):&lt;/strong&gt; 2 round trips.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Client → Server:&lt;/strong&gt; &lt;code&gt;SSH_MSG_KEX_DH_GEX_REQUEST&lt;/code&gt; (preferred group size)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server → Client:&lt;/strong&gt; &lt;code&gt;SSH_MSG_KEX_DH_GEX_GROUP&lt;/code&gt; (prime and generator)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Client → Server:&lt;/strong&gt; &lt;code&gt;SSH_MSG_KEX_DH_GEX_INIT&lt;/code&gt; (client&amp;rsquo;s public value)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server → Client:&lt;/strong&gt; &lt;code&gt;SSH_MSG_KEX_DH_GEX_REPLY&lt;/code&gt; (server&amp;rsquo;s public value + signature)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The extra round trip exists because the client asks the server to choose a DH group, rather than using a fixed one. Older network devices that don&amp;rsquo;t support curve25519 or ECDH may force this path.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cost: 1-2 round trips&lt;/strong&gt; depending on the algorithm.&lt;/p&gt;
&lt;p&gt;After key exchange completes, both sides send &lt;code&gt;SSH_MSG_NEWKEYS&lt;/code&gt; simultaneously to activate the new keys (no additional round trip). The client then requests the &lt;code&gt;ssh-userauth&lt;/code&gt; service via &lt;code&gt;SSH_MSG_SERVICE_REQUEST&lt;/code&gt; / &lt;code&gt;SSH_MSG_SERVICE_ACCEPT&lt;/code&gt;, costing 1 round trip. Some implementations (like OpenSSH) pipeline this with NEWKEYS, but many wait.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Transport layer total: 3-5 round trips&lt;/strong&gt; (TCP + version exchange + KEXINIT + kex + service request).&lt;/p&gt;
&lt;h2 id="ssh-authentication"&gt;SSH Authentication&lt;/h2&gt;
&lt;p&gt;The client now needs to authenticate. This is where things get variable, because the server controls which methods it accepts and in what order.&lt;/p&gt;
&lt;h3 id="the-auth-dance"&gt;The Auth Dance&lt;/h3&gt;
&lt;p&gt;The client sends an authentication request. If it fails or the server wants to advertise available methods, the server responds with &lt;code&gt;SSH_MSG_USERAUTH_FAILURE&lt;/code&gt; plus a list of methods that can continue.&lt;/p&gt;
&lt;p&gt;A typical sequence for password auth:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Client → Server:&lt;/strong&gt; &lt;code&gt;SSH_MSG_USERAUTH_REQUEST&lt;/code&gt; (method: &amp;ldquo;none&amp;rdquo;) to discover available methods&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server → Client:&lt;/strong&gt; &lt;code&gt;SSH_MSG_USERAUTH_FAILURE&lt;/code&gt; (methods: &amp;ldquo;publickey,password&amp;rdquo;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Client → Server:&lt;/strong&gt; &lt;code&gt;SSH_MSG_USERAUTH_REQUEST&lt;/code&gt; (method: &amp;ldquo;password&amp;rdquo;, password: &amp;ldquo;&amp;hellip;&amp;rdquo;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server → Client:&lt;/strong&gt; &lt;code&gt;SSH_MSG_USERAUTH_SUCCESS&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Cost: 2 round trips&lt;/strong&gt; for password auth with a method probe.&lt;/p&gt;
&lt;p&gt;For public key auth, it&amp;rsquo;s often 2-3 round trips:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Probe (1 RT)&lt;/li&gt;
&lt;li&gt;Public key query: &amp;ldquo;will you accept this key?&amp;rdquo; (1 RT)&lt;/li&gt;
&lt;li&gt;Actual signature-based auth (1 RT)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Some servers skip the probe if the client guesses correctly on the first try. Some clients try multiple key types before finding one the server accepts. Each failed attempt is another round trip.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Authentication total: 1-3 round trips&lt;/strong&gt; depending on method and implementation.&lt;/p&gt;
&lt;h2 id="ssh-connection"&gt;SSH Connection&lt;/h2&gt;
&lt;p&gt;With an authenticated, encrypted connection established, the client can now open channels. For automation, this layer matters most, because there are three fundamentally different ways to interact with a device.&lt;/p&gt;
&lt;h3 id="channel-basics"&gt;Channel Basics&lt;/h3&gt;
&lt;p&gt;Every channel follows the same lifecycle:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Client → Server:&lt;/strong&gt; &lt;code&gt;SSH_MSG_CHANNEL_OPEN&lt;/code&gt; (channel type, initial window size)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server → Client:&lt;/strong&gt; &lt;code&gt;SSH_MSG_CHANNEL_OPEN_CONFIRMATION&lt;/code&gt; (window size, max packet size)&lt;/li&gt;
&lt;li&gt;&amp;hellip; data flows &amp;hellip;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Either side:&lt;/strong&gt; &lt;code&gt;SSH_MSG_CHANNEL_CLOSE&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Cost: 1 round trip&lt;/strong&gt; to open a channel.&lt;/p&gt;
&lt;p&gt;SSH channels have their own flow control, independent of TCP, with each side advertising a receive window size.&lt;/p&gt;
&lt;h3 id="channel-type-1-shell-pty"&gt;Channel Type 1: Shell (PTY)&lt;/h3&gt;
&lt;p&gt;This is what happens when you type &lt;code&gt;ssh router1&lt;/code&gt; and get an interactive prompt. It&amp;rsquo;s also what &lt;strong&gt;Netmiko&lt;/strong&gt;, &lt;strong&gt;Ansible &lt;code&gt;network_cli&lt;/code&gt;&lt;/strong&gt;, and &lt;strong&gt;Scrapli&lt;/strong&gt; use for automation.&lt;/p&gt;
&lt;p&gt;After opening a session channel:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Client → Server:&lt;/strong&gt; &lt;code&gt;SSH_MSG_CHANNEL_REQUEST&lt;/code&gt; (type: &amp;ldquo;pty-req&amp;rdquo;, terminal type, dimensions)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server → Client:&lt;/strong&gt; &lt;code&gt;SSH_MSG_CHANNEL_SUCCESS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Client → Server:&lt;/strong&gt; &lt;code&gt;SSH_MSG_CHANNEL_REQUEST&lt;/code&gt; (type: &amp;ldquo;shell&amp;rdquo;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server → Client:&lt;/strong&gt; &lt;code&gt;SSH_MSG_CHANNEL_SUCCESS&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Cost: 2 round trips&lt;/strong&gt; (PTY request + shell request).&lt;/p&gt;
&lt;p&gt;Now you have a byte stream. The server sends a prompt (&lt;code&gt;router1#&lt;/code&gt;). You send characters. The server echoes them back. You send a newline. The server processes the command and sends the output, followed by another prompt.&lt;/p&gt;
&lt;p&gt;The catch: &lt;strong&gt;there is no framing&lt;/strong&gt;. The server doesn&amp;rsquo;t tell you &amp;ldquo;the command output is 847 bytes long&amp;rdquo; or &amp;ldquo;I&amp;rsquo;m done sending.&amp;rdquo; It just sends bytes. Your automation tool has to figure out when the output is complete by pattern-matching the prompt, which is why Netmiko spends so much effort on prompt detection, and why it sends &lt;code&gt;terminal length 0&lt;/code&gt; (to disable pagination) and &lt;code&gt;terminal width 511&lt;/code&gt; (to prevent line wrapping) before running any real commands.&lt;/p&gt;
&lt;p&gt;Each of those setup commands is another write-read-prompt cycle on the channel.&lt;/p&gt;
&lt;h3 id="channel-type-2-exec"&gt;Channel Type 2: Exec&lt;/h3&gt;
&lt;p&gt;This is what happens when you run &lt;code&gt;ssh router1 &amp;quot;show version&amp;quot;&lt;/code&gt; from the command line. It&amp;rsquo;s also what Go&amp;rsquo;s &lt;code&gt;x/crypto/ssh&lt;/code&gt;, Paramiko&amp;rsquo;s &lt;code&gt;exec_command()&lt;/code&gt;, and some other programmatic SSH libraries in various languages use.&lt;/p&gt;
&lt;p&gt;After opening a session channel:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Client → Server:&lt;/strong&gt; &lt;code&gt;SSH_MSG_CHANNEL_REQUEST&lt;/code&gt; (type: &amp;ldquo;exec&amp;rdquo;, command: &amp;ldquo;show version&amp;rdquo;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server → Client:&lt;/strong&gt; &lt;code&gt;SSH_MSG_CHANNEL_SUCCESS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server → Client:&lt;/strong&gt; &lt;code&gt;SSH_MSG_CHANNEL_DATA&lt;/code&gt; (command output)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server → Client:&lt;/strong&gt; &lt;code&gt;SSH_MSG_CHANNEL_REQUEST&lt;/code&gt; (type: &amp;ldquo;exit-status&amp;rdquo;, code: 0)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server → Client:&lt;/strong&gt; &lt;code&gt;SSH_MSG_CHANNEL_CLOSE&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Cost: 1 round trip&lt;/strong&gt; for the exec request, then the output streams back.&lt;/p&gt;
&lt;p&gt;Exec mode is cleaner than shell mode: one command, one channel, structured exit. No prompt detection, no pagination, no terminal width issues. But there are some catches.&lt;/p&gt;
&lt;p&gt;First, &lt;strong&gt;each command needs its own channel&lt;/strong&gt;. If you want to run 5 commands, you open 5 channels, meaning 5 channel-open round trips plus 5 exec-request round trips.&lt;/p&gt;
&lt;p&gt;Second, &lt;strong&gt;many network devices don&amp;rsquo;t support exec mode properly (or at all)&lt;/strong&gt;, which is why most automation tools default to shell/PTY mode.&lt;/p&gt;
&lt;h3 id="channel-type-3-subsystem-netconf"&gt;Channel Type 3: Subsystem (NETCONF)&lt;/h3&gt;
&lt;p&gt;NETCONF (&lt;a href="https://datatracker.ietf.org/doc/html/rfc6241"&gt;RFC 6241&lt;/a&gt;) runs over SSH as a subsystem, a named service that the server knows how to handle.&lt;/p&gt;
&lt;p&gt;After opening a session channel:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Client → Server:&lt;/strong&gt; &lt;code&gt;SSH_MSG_CHANNEL_REQUEST&lt;/code&gt; (type: &amp;ldquo;subsystem&amp;rdquo;, name: &amp;ldquo;netconf&amp;rdquo;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server → Client:&lt;/strong&gt; &lt;code&gt;SSH_MSG_CHANNEL_SUCCESS&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Cost: 1 round trip.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Then NETCONF has its own handshake on top of the SSH channel:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Both sides simultaneously:&lt;/strong&gt; &lt;code&gt;&amp;lt;hello&amp;gt;&lt;/code&gt; messages listing capabilities&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Client → Server:&lt;/strong&gt; &lt;code&gt;&amp;lt;rpc&amp;gt;&lt;/code&gt; request&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server → Client:&lt;/strong&gt; &lt;code&gt;&amp;lt;rpc-reply&amp;gt;&lt;/code&gt; response&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;hello&amp;gt;&lt;/code&gt; exchange is 1 round trip. Each RPC is 1 round trip. Messages are delimited by &lt;code&gt;]]&amp;gt;]]&amp;gt;&lt;/code&gt; (the legacy base:1.0 framing) or chunked framing with &lt;code&gt;\n#length\n&lt;/code&gt; headers (base:1.1), both defined in &lt;a href="https://datatracker.ietf.org/doc/html/rfc6242"&gt;RFC 6242&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;NETCONF gives you structured XML data and transactional semantics (&lt;code&gt;&amp;lt;edit-config&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;commit&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;validate&amp;gt;&lt;/code&gt;), which beats screen-scraping CLI output by a wide margin. But it pays the full SSH transport cost to get there, plus its own capability exchange overhead.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://network-notes.com/img/2026/ssh-channel-types.2276d42651b3da07c1f45b93bd3e04ba7a95b11891f46a8864e3a996917c435c.svg" alt="SSH channel types compared: Shell/PTY, Exec, and Subsystem/NETCONF" loading="lazy" /&gt;&lt;/p&gt;
&lt;h2 id="the-round-trip-tally"&gt;The Round-Trip Tally&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s what it all adds up to, from TCP SYN to first byte of command output:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://network-notes.com/img/2026/ssh-connection-lifecycle.c495d377728639bd46d3e72a152d0060cd70f07f15913d5753113b9d35d43035.svg" alt="SSH connection lifecycle showing 10-12 round trips from TCP handshake through PTY session prep and first command" loading="lazy" /&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Round trips&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TCP handshake&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Same for any TCP protocol&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSH version exchange + KEXINIT&lt;/td&gt;
&lt;td&gt;1-2&lt;/td&gt;
&lt;td&gt;Pipelined: 1 RT; sequential: 2 RT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Key exchange (modern)&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;curve25519/ECDH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Key exchange (legacy)&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;DH group exchange&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NEWKEYS + service request&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Often pipelined&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authentication&lt;/td&gt;
&lt;td&gt;1-3&lt;/td&gt;
&lt;td&gt;Password: 2, pubkey: 2-3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Channel open&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PTY + shell request&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Shell/PTY mode only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exec request&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Exec mode only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subsystem request&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;NETCONF only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NETCONF hello exchange&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;NETCONF only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;SSH CLI (exec mode):&lt;/strong&gt; 6-10 round trips before the first command output.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SSH CLI (PTY/shell mode):&lt;/strong&gt; 7-11 round trips before the first command output, plus 2-3 more for session prep (&lt;code&gt;terminal length 0&lt;/code&gt;, &lt;code&gt;terminal width 511&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NETCONF over SSH:&lt;/strong&gt; 7-11 round trips before the first RPC response, plus 1 for the NETCONF hello exchange.&lt;/p&gt;
&lt;p&gt;For comparison:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;HTTPS (TLS 1.3):&lt;/strong&gt; 3 round trips total: TCP (1) + TLS 1.3 handshake (1) + HTTP request (1). With connection reuse: 1 round trip per request.&lt;/p&gt;
&lt;h2 id="netconf-vs-restconf-same-data-different-pipe"&gt;NETCONF vs RESTCONF: Same Data, Different Pipe&lt;/h2&gt;
&lt;p&gt;So far this post has been about what happens inside an SSH connection. But SSH isn&amp;rsquo;t the only way to talk to a network device programmatically. NETCONF and RESTCONF both operate on YANG data models, and they can configure the same things on the same devices. The difference is the transport.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;NETCONF&lt;/th&gt;
&lt;th&gt;RESTCONF&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Transport&lt;/td&gt;
&lt;td&gt;SSH (subsystem channel)&lt;/td&gt;
&lt;td&gt;HTTPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data format&lt;/td&gt;
&lt;td&gt;XML&lt;/td&gt;
&lt;td&gt;JSON or XML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connection setup&lt;/td&gt;
&lt;td&gt;7-11 RT (SSH)&lt;/td&gt;
&lt;td&gt;3 RT (TLS 1.3)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per-operation cost&lt;/td&gt;
&lt;td&gt;1 RT (RPC request/reply)&lt;/td&gt;
&lt;td&gt;1 RT (HTTP request/response)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connection reuse&lt;/td&gt;
&lt;td&gt;SSH connection persists&lt;/td&gt;
&lt;td&gt;HTTP keep-alive (default)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Framing&lt;/td&gt;
&lt;td&gt;&lt;code&gt;]]&amp;gt;]]&amp;gt;&lt;/code&gt; or chunked&lt;/td&gt;
&lt;td&gt;HTTP Content-Length&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Capabilities&lt;/td&gt;
&lt;td&gt;Explicit hello exchange&lt;/td&gt;
&lt;td&gt;YANG library (&lt;a href="https://datatracker.ietf.org/doc/html/rfc7895"&gt;RFC 7895&lt;/a&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transactions&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;commit&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;validate&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;discard-changes&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No candidate datastore; edits apply immediately to running config&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;NETCONF&amp;rsquo;s advantage is its transactional model: candidate configs, commit/rollback, validation. RESTCONF trades some of that for a simpler transport with lower connection overhead and native compatibility with standard HTTP client libraries and load balancers.&lt;/p&gt;
&lt;p&gt;For automation at scale, the transport cost matters. If you&amp;rsquo;re making hundreds of NETCONF calls across a WAN, you&amp;rsquo;re paying the SSH handshake tax on every new connection. RESTCONF over HTTPS with connection reuse eliminates that entirely.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I&amp;rsquo;m planning to benchmark both SSH CLI vs HTTPS CLI and NETCONF vs RESTCONF with real measurements: same device, same operations, same latency profiles. Theory is useful, but packet captures don&amp;rsquo;t lie.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="why-this-matters-for-automation"&gt;Why This Matters for Automation&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;re managing 1,000 devices across a WAN with 75ms latency, the math gets ugly.
Each SSH PTY/shell connection pays 675-1,050ms in handshake overhead before it does anything useful.
Parallelism helps with wall clock time, but it doesn&amp;rsquo;t reduce the per-device tax, and every one of those connections is burning round trips, file descriptors, and CPU on your automation host.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll be measuring this gap with real numbers in an upcoming series on CLI over HTTPS.
Early testing shows that at 30ms RTT, HTTPS batch mode is roughly 5x faster than SSH for the same CLI commands, because it pays 3 round trips instead of 10-15.&lt;/p&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;My take:&lt;/strong&gt; SSH is a good protocol for what it was built for: giving one person a secure shell on one machine. Every layer in the stack exists for a reason. But those layers add up to a lot of round trips, and that cost compounds when you&amp;rsquo;re hitting a thousand devices over a WAN. The protocol wasn&amp;rsquo;t designed for &amp;ldquo;blast 5 commands at 1,000 boxes as fast as possible.&amp;rdquo; Once you see the round-trip tax on the wire, the performance gap between SSH and HTTPS stops being surprising.&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>CiSSHGo: A Lightweight SSH Device Emulator for Network Automation Testing</title><link>https://network-notes.com/posts/2026/cisshgo-ssh-device-emulator/</link><pubDate>Mon, 20 Apr 2026 00:00:00 +0000</pubDate><author>brett@network-notes.com (Brett Lykins)</author><dc:creator>Brett Lykins</dc:creator><guid>https://network-notes.com/posts/2026/cisshgo-ssh-device-emulator/</guid><description>&lt;p&gt;You need to test your network automation code. You&amp;rsquo;ve got an Ansible playbook that configures interfaces across a thousand switches, or a Nornir script that collects &lt;code&gt;show version&lt;/code&gt; from every device in your inventory, and you need to know it works before it hits production.&lt;/p&gt;
&lt;p&gt;The standard answer is &amp;ldquo;spin up a lab.&amp;rdquo; CML, EVE-NG, GNS3, ContainerLab — all solid tools that run actual NOS images. They give you real device behavior, real protocol interactions, real forwarding planes. They&amp;rsquo;re also heavy. A single Cisco CSR1000v image wants 4GB of RAM. Multiply that by the number of devices in your test topology and you&amp;rsquo;re looking at serious compute just to validate that your automation sends the right commands and parses the output correctly.&lt;/p&gt;
&lt;p&gt;For a lot of automation testing, you don&amp;rsquo;t need a real forwarding plane. You need something that accepts an SSH connection, presents a prompt, and responds to &lt;code&gt;show version&lt;/code&gt; with the right output. That&amp;rsquo;s what &lt;a href="https://github.com/tbotnz/cisshgo"&gt;CiSSHGo&lt;/a&gt; does.&lt;/p&gt;
&lt;h2 id="what-cisshgo-is-and-isnt"&gt;What CiSSHGo Is (and Isn&amp;rsquo;t)&lt;/h2&gt;
&lt;p&gt;CiSSHGo is a single Go binary that spawns SSH listeners, each emulating a network device by playing back pre-defined command transcripts. You define what commands a device supports and what output it returns, and CiSSHGo handles the SSH session, prompt rendering, command matching, and context transitions (&lt;code&gt;&amp;gt;&lt;/code&gt; → &lt;code&gt;#&lt;/code&gt; → &lt;code&gt;(config)#&lt;/code&gt; → &lt;code&gt;(config-if)#&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;It is not a network simulator. There&amp;rsquo;s no forwarding plane, no routing protocol adjacencies, no MAC address table. It doesn&amp;rsquo;t run IOS or EOS or Junos. It plays back text files. That constraint is also its strength: a single CiSSHGo process can spawn thousands of SSH listeners using Go&amp;rsquo;s goroutine-per-listener model, each consuming a few kilobytes of memory. Try that with a Python-based mock server or a fleet of virtual routers.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re thinking &amp;ldquo;why not ContainerLab?&amp;rdquo; — different tool for a different job. ContainerLab runs real NOS images in containers, which gives you protocol adjacencies and a real forwarding plane at the cost of per-device resource overhead. CiSSHGo gives you SSH session fidelity at massive scale with near-zero overhead. &lt;a href="https://fakenos.github.io/fakenos/"&gt;FakeNOS&lt;/a&gt; is the closest analog — same concept, Python-based. CiSSHGo&amp;rsquo;s Go runtime gives it an edge on concurrency and memory when you&amp;rsquo;re scaling to thousands of listeners.&lt;/p&gt;
&lt;p&gt;CiSSHGo isn&amp;rsquo;t new. Tony Nealon (&lt;a href="https://github.com/tbotnz"&gt;tbotnz&lt;/a&gt;, also the creator of &lt;a href="https://github.com/tbotnz/netpalm"&gt;netpalm&lt;/a&gt;) first published it in August 2020, and it&amp;rsquo;s been in active use since. I&amp;rsquo;ve been a contributor and have used it as the SSH mock backend in my own projects. What is new is the scope of recent work: the project went from a single-platform proof of concept to a &lt;a href="https://github.com/tbotnz/cisshgo/releases/tag/v1.0.0"&gt;v1.0.0 stable release&lt;/a&gt; in March 2026 with multi-vendor support, an inventory system, scenario mode, and proper release engineering. That&amp;rsquo;s enough of a capability jump to warrant a re-introduction for anyone who hasn&amp;rsquo;t looked at it recently. It&amp;rsquo;s MIT licensed, ships as pre-built binaries for Linux/macOS/Windows (amd64 and arm64), and publishes multi-arch Docker images to &lt;a href="https://github.com/tbotnz/cisshgo/pkgs/container/cisshgo"&gt;GitHub Container Registry&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="capabilities"&gt;Capabilities&lt;/h2&gt;
&lt;h3 id="seven-platforms-out-of-the-box"&gt;Seven Platforms Out of the Box&lt;/h3&gt;
&lt;p&gt;CiSSHGo ships with transcript libraries for seven platforms: Cisco CSR1000v, IOS, IOS-XR, ASA, NX-OS, Arista EOS, and Juniper Junos. Each platform comes with &lt;code&gt;show version&lt;/code&gt;, &lt;code&gt;show ip interface brief&lt;/code&gt; (or equivalent), and &lt;code&gt;show running-config&lt;/code&gt; transcripts sourced from &lt;a href="https://github.com/networktocode/ntc-templates"&gt;NTC Templates&lt;/a&gt; test fixtures.&lt;/p&gt;
&lt;p&gt;The transcript map is a YAML file that defines everything about a platform — hostname, credentials, supported commands, context hierarchy, and prompt format:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;platforms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;csr1000v&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;vendor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;cisco&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;cisshgo1000v&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;admin&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;admin&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command_transcripts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;show version&amp;#34;: &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;cisco/csr1000v/show_version.txt&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;show ip interface brief&amp;#34;: &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;cisco/csr1000v/show_ip_interface_brief.txt&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;show running-config&amp;#34;: &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;cisco/csr1000v/show_running-config.txt&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;terminal length 0&amp;#34;: &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;generic_empty_return.txt&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;context_hierarchy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;(config-if)#&amp;#34;: &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;(config)#&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;(config)#&amp;#34;: &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;#&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;#&amp;#34;: &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;&amp;gt;&amp;#34;: &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;exit&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;context_search&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;interface&amp;#34;: &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;(config-if)#&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;configure terminal&amp;#34;: &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;(config)#&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;enable&amp;#34;: &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;#&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;base&amp;#34;: &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Adding a new platform or new commands is just adding text files and YAML entries. No Go code required.&lt;/p&gt;
&lt;h3 id="inventory-mode"&gt;Inventory Mode&lt;/h3&gt;
&lt;p&gt;For testing against multiple device types, CiSSHGo supports an inventory file that spawns different platforms on different ports from a single process:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;csr1000v&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ios&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;nxos&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;eos&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;junos&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;One binary, 50 SSH listeners, five different platform personalities. Each gets its own hostname, credentials, prompt style, and command set.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://network-notes.com/img/2026/cisshgo-inventory-listeners.1ca167f1f10e433dfd12846261999906b3c2f991205b6df042be603841d6b020.svg" alt="Inventory mode: mixed platforms and scenarios from a single binary" loading="lazy" /&gt;&lt;/p&gt;
&lt;h3 id="scenario-mode"&gt;Scenario Mode&lt;/h3&gt;
&lt;p&gt;Scenario mode is the feature that moved CiSSHGo from &amp;ldquo;useful for smoke tests&amp;rdquo; to &amp;ldquo;useful for real integration testing.&amp;rdquo; A scenario defines an ordered sequence of commands with different outputs at each step. The classic use case: &lt;code&gt;show running-config&lt;/code&gt; returns one output before a configuration change and different output after.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;scenarios&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;csr1000v-add-interface&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;csr1000v&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;sequence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;enable&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;transcript&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;generic_empty_return.txt&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;show running-config&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;transcript&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;scenarios/csr1000v-add-interface/running_config_before.txt&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;configure terminal&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;transcript&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;generic_empty_return.txt&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;interface GigabitEthernet0/0/2&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;transcript&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;generic_empty_return.txt&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ip address 172.16.0.1 255.255.255.0&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;transcript&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;generic_empty_return.txt&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;no shutdown&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;transcript&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;generic_empty_return.txt&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;end&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;transcript&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;generic_empty_return.txt&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;show running-config&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;transcript&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;scenarios/csr1000v-add-interface/running_config_after.txt&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Each SSH session gets its own sequence pointer. Commands that don&amp;rsquo;t match the current step fall through to the platform&amp;rsquo;s normal command set. Scenario mode also handles interface abbreviation matching — &lt;code&gt;int g0/0/2&lt;/code&gt; matches &lt;code&gt;interface GigabitEthernet0/0/2&lt;/code&gt; without a hardcoded abbreviation table. The sequence step itself provides the ground truth.&lt;/p&gt;
&lt;h3 id="flexible-prompts"&gt;Flexible Prompts&lt;/h3&gt;
&lt;p&gt;Not every NOS uses the Cisco &lt;code&gt;hostname#&lt;/code&gt; prompt format. CiSSHGo supports a &lt;code&gt;prompt_format&lt;/code&gt; field for platforms like Junos that use &lt;code&gt;user@hostname&amp;gt;&lt;/code&gt; style prompts, and &lt;code&gt;context_prefix_lines&lt;/code&gt; for multi-line prompts like Junos&amp;rsquo;s &lt;code&gt;[edit]&lt;/code&gt; prefix:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;junos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;vendor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;juniper&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;cisshgo-junos&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;admin&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;prompt_format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{username}@{hostname}{context}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;context_prefix_lines&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;#&amp;#34;: &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;[edit]&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This produces:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;admin@cisshgo-junos&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;admin@cisshgo-junos&amp;gt; configure
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[edit]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;admin@cisshgo-junos#
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="session-behavior"&gt;Session Behavior&lt;/h3&gt;
&lt;p&gt;Once a listener is running, CiSSHGo handles the session the way you&amp;rsquo;d expect from a real device. Commands are matched by prefix — &lt;code&gt;sh ver&lt;/code&gt; resolves to &lt;code&gt;show version&lt;/code&gt;, &lt;code&gt;sh ip int br&lt;/code&gt; to &lt;code&gt;show ip interface brief&lt;/code&gt; — and ambiguous abbreviations get the familiar &lt;code&gt;% Ambiguous command&lt;/code&gt; error. Your automation code can use the same shortened commands it uses against real hardware.&lt;/p&gt;
&lt;p&gt;Both interactive shell sessions and exec mode (&lt;code&gt;ssh host &amp;quot;show version&amp;quot;&lt;/code&gt;) work. Automation tools like Ansible&amp;rsquo;s &lt;code&gt;network_cli&lt;/code&gt; use interactive shell mode, while libraries like Go&amp;rsquo;s &lt;code&gt;x/crypto/ssh&lt;/code&gt; and Paramiko&amp;rsquo;s &lt;code&gt;exec_command()&lt;/code&gt; use exec mode — so supporting both matters for realistic testing.&lt;/p&gt;
&lt;p&gt;Transcript files support Go&amp;rsquo;s &lt;a href="https://pkg.go.dev/text/template"&gt;text/template&lt;/a&gt; syntax. You can reference device fields like &lt;code&gt;{{.Hostname}}&lt;/code&gt; in your transcript output, so the same transcript file produces different output for different devices in an inventory.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://network-notes.com/img/2026/cisshgo-terminal-session.124b1cfccfee4c8047b15372602f2c5fdbbdab97093bbaeb1f66652be5557571.svg" alt="Interactive session with abbreviated command matching and Go template variables" loading="lazy" /&gt;&lt;/p&gt;
&lt;h2 id="running-it"&gt;Running It&lt;/h2&gt;
&lt;p&gt;The quickest path is Docker:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d -p 10000-10049:10000-10049 ghcr.io/tbotnz/cisshgo:latest
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;That gives you 50 listeners on ports 10000–10049, all emulating a CSR1000v by default. Pre-built binaries are on the &lt;a href="https://github.com/tbotnz/cisshgo/releases"&gt;releases page&lt;/a&gt; if you&amp;rsquo;d rather run it directly.&lt;/p&gt;
&lt;p&gt;Every CLI flag has a corresponding environment variable (&lt;code&gt;CISSHGO_LISTENERS&lt;/code&gt;, &lt;code&gt;CISSHGO_STARTING_PORT&lt;/code&gt;, &lt;code&gt;CISSHGO_TRANSCRIPT_MAP&lt;/code&gt;, &lt;code&gt;CISSHGO_PLATFORM&lt;/code&gt;, &lt;code&gt;CISSHGO_INVENTORY&lt;/code&gt;), so it drops into a docker-compose or Kubernetes manifest without wrapper scripts.&lt;/p&gt;
&lt;h2 id="where-it-fits"&gt;Where It Fits&lt;/h2&gt;
&lt;h3 id="scale-testing"&gt;Scale Testing&lt;/h3&gt;
&lt;p&gt;This is where CiSSHGo&amp;rsquo;s Go runtime earns its keep. If you&amp;rsquo;re building or evaluating a network automation framework that needs to handle thousands of devices, you need thousands of SSH endpoints to test against. You don&amp;rsquo;t need those endpoints to actually route packets — you need them to accept connections, authenticate, and return plausible output.&lt;/p&gt;
&lt;p&gt;A single CiSSHGo process with &lt;code&gt;--listeners 10000&lt;/code&gt; spawns ten thousand SSH listeners, each running as a goroutine. Memory overhead is measured in megabytes, not gigabytes. Compare that to spinning up ten thousand virtual routers (not happening), ten thousand containers running a Python SSH server (possible but expensive), or trying to test at scale against a lab of 20 real devices and hoping the math extrapolates (it won&amp;rsquo;t — connection pooling, queue depth, and timeout behavior all change at scale).&lt;/p&gt;
&lt;p&gt;Pair CiSSHGo with an inventory file and you get a mixed-vendor topology: a few thousand IOS devices, a few thousand NX-OS, some EOS, some Junos. Your automation framework sees what looks like a heterogeneous production network. The responses are canned, the SSH handshakes and authentication are real, and that&amp;rsquo;s exactly the layer you&amp;rsquo;re trying to test.&lt;/p&gt;
&lt;h3 id="cicd-pipeline-testing"&gt;CI/CD Pipeline Testing&lt;/h3&gt;
&lt;p&gt;The simplest use case. Drop CiSSHGo into your CI pipeline as a service container, point your automation tests at it, and validate that your code sends the right commands and parses the output correctly. No lab infrastructure to maintain, no NOS licenses to manage, no flaky device VMs timing out mid-test.&lt;/p&gt;
&lt;p&gt;A docker-compose snippet for a GitHub Actions workflow:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cisshgo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ghcr.io/tbotnz/cisshgo:v1.0.0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;--listeners&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;--starting-port&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;10022&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s2"&gt;&amp;#34;10022:10022&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s2"&gt;&amp;#34;10023:10023&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Your test suite connects to &lt;code&gt;localhost:10022&lt;/code&gt;, runs commands, and asserts on the output. The CiSSHGo container starts in under a second.&lt;/p&gt;
&lt;h3 id="integration-test-stacks"&gt;Integration Test Stacks&lt;/h3&gt;
&lt;p&gt;I use CiSSHGo as the mock SSH backend in the integration test suite for &lt;a href="https://github.com/lykinsbd/naas"&gt;NAAS&lt;/a&gt; (Netmiko As A Service). The docker-compose stack spins up CiSSHGo alongside the API server, workers, and Redis, and the tests exercise the full request path: HTTP request → job queue → Netmiko SSH connection to CiSSHGo → response parsing → result delivery. The CiSSHGo container handles &lt;code&gt;send_command&lt;/code&gt;, &lt;code&gt;send_config&lt;/code&gt;, structured output parsing, platform autodetect, and authentication failure scenarios. I&amp;rsquo;ll cover that setup in detail in a future post.&lt;/p&gt;
&lt;h3 id="parser-development"&gt;Parser Development&lt;/h3&gt;
&lt;p&gt;If you&amp;rsquo;re writing or testing &lt;a href="https://github.com/networktocode/ntc-templates"&gt;TextFSM&lt;/a&gt; or &lt;a href="https://github.com/dmulyalin/ttp"&gt;TTP&lt;/a&gt; templates — something I&amp;rsquo;ve &lt;a href="https://network-notes.com/posts/2020/parsing-netdevice-output-1/"&gt;written about before&lt;/a&gt; — you need consistent, known-good command output to parse against. CiSSHGo&amp;rsquo;s transcripts are sourced from NTC Templates test fixtures, so you get realistic output that matches what the parsing community already validates against. Point your parser at CiSSHGo, iterate on your template, and know that the input is stable between runs.&lt;/p&gt;
&lt;p&gt;It also works for demos and training — a multi-vendor topology from a single container, no lab required. Students can SSH in and practice writing playbooks against something that responds like a real switch.&lt;/p&gt;
&lt;h2 id="when-not-to-use-it"&gt;When Not to Use It&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No protocol simulation.&lt;/strong&gt; If your tests depend on BGP adjacencies forming, OSPF routes being installed, or ARP tables populating, CiSSHGo can&amp;rsquo;t help. You need CML, ContainerLab, or real hardware.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No NETCONF, gNMI, or SNMP.&lt;/strong&gt; CiSSHGo is SSH-only. If your automation uses model-driven interfaces, look elsewhere.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No dynamic state beyond scenarios.&lt;/strong&gt; Outside of scenario mode, every session gets the same output for the same command. There&amp;rsquo;s no simulated interface going up or down, no counter incrementing. Scenario mode adds ordered state changes, but it&amp;rsquo;s scripted, not reactive.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transcript maintenance.&lt;/strong&gt; Your transcripts need to match what your automation expects. If a real device&amp;rsquo;s &lt;code&gt;show version&lt;/code&gt; output changes between NOS versions and your parser depends on the new format, you need to update the transcript. This is manageable but not zero-effort.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;CiSSHGo tests your automation&amp;rsquo;s SSH interaction layer — connection handling, command dispatch, output parsing, error handling. It doesn&amp;rsquo;t test whether your automation produces correct network state. A playbook that configures BGP neighbors needs a real lab (or at minimum ContainerLab) to validate that adjacencies form and routes propagate. And no amount of pre-production testing — mock or otherwise — replaces a sensible rollout strategy: canary deployments, staged rollouts, automated validation gates. CiSSHGo fits early in that pipeline, catching the class of bugs that don&amp;rsquo;t need a forwarding plane to find. The rest of the pipeline still matters. I&amp;rsquo;ll dig into automation testing strategies and deployment patterns in a future post.&lt;/p&gt;
&lt;h2 id="getting-started"&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://github.com/tbotnz/cisshgo"&gt;GitHub repo&lt;/a&gt; has pre-built binaries on the &lt;a href="https://github.com/tbotnz/cisshgo/releases"&gt;releases page&lt;/a&gt;, Docker images on GHCR, and &lt;a href="https://tbotnz.github.io/cisshgo/"&gt;documentation&lt;/a&gt; covering configuration, transcript authoring, and the inventory system. The project is MIT licensed and accepts contributions — the &lt;a href="https://github.com/tbotnz/cisshgo/blob/master/CONTRIBUTING.md"&gt;CONTRIBUTING.md&lt;/a&gt; covers the workflow.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re testing network automation against real devices or heavyweight simulators and finding it slow, flaky, or expensive to maintain — stop. Use CiSSHGo for the SSH interaction layer, save the real lab for the tests that actually need a forwarding plane.&lt;/p&gt;</description></item><item><title>SONiC at the Edge: Cheaper Switches Don't Mean a Cheaper Network</title><link>https://network-notes.com/posts/2026/sonic-business-case/</link><pubDate>Wed, 08 Apr 2026 10:00:00 -0500</pubDate><author>brett@network-notes.com (Brett Lykins)</author><dc:creator>Brett Lykins</dc:creator><guid>https://network-notes.com/posts/2026/sonic-business-case/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclosure&lt;/strong&gt;: I currently work on a team deploying SONiC at scale. I&amp;rsquo;m not a neutral party, but I&amp;rsquo;ll be honest about both sides of the ledger.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In &lt;a href="https://network-notes.com/posts/2026/sonic-access-layer/"&gt;Part 1&lt;/a&gt;, I covered the technical shift: 1G access-layer hardware now runs SONiC, the feature set is filling in, and the support ecosystem is real. If you haven&amp;rsquo;t read that yet, start there.&lt;/p&gt;
&lt;p&gt;This post is about the question that comes after &amp;ldquo;can we?&amp;rdquo; — the &amp;ldquo;should we?&amp;rdquo; That answer has less to do with switch specs and more to do with how your organization spends money, builds teams, and tolerates risk.&lt;/p&gt;
&lt;h2 id="the-capex-only-trap"&gt;The Capex-Only Trap&lt;/h2&gt;
&lt;p&gt;Everyone leads with hardware savings when pitching disaggregated networking. And the savings are real. White box switches commonly cost &lt;a href="https://network-notes.com/posts/2026/sonic-access-layer/"&gt;one-half to one-third&lt;/a&gt; what a comparable Cisco Catalyst or Arista switch runs, based on list-price comparisons across current 48-port 1G PoE models. At fleet scale, that delta gets attention from finance teams fast.&lt;/p&gt;
&lt;p&gt;But hardware is the easiest line item to compare and often the smallest. One &lt;a href="https://www.onitio.com/articles/TCO-calculator-guide"&gt;TCO analysis of a 15,000-device deployment&lt;/a&gt; found that purchase price represented only 25–30% of five-year costs, with the remaining 70–75% coming from deployment, management, support, and retirement. Network infrastructure tends to follow a similar pattern, though the exact ratio varies by environment. &lt;a href="https://networkautomation.forum/blog/sonic-reality-check-enterprise-deployments-beyond-the-hype"&gt;One analysis on the Network Automation Forum&lt;/a&gt; captured this well: an engineer cited potential savings of $1.3 million on a single edge upgrade, while others in the same discussion pointed out that compensating costs in staffing and operational complexity can eat into those numbers significantly.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what actually costs money over five years:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Staffing.&lt;/strong&gt; Someone has to operate this network. SONiC is Linux. Your team needs to be comfortable with Debian, systemd, Docker containers, and debugging at the OS level, not just CLI-driven NOS administration. If your current team can&amp;rsquo;t do that, you&amp;rsquo;re either retraining them or hiring people who can. Both cost money.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Training.&lt;/strong&gt; Retraining a team that&amp;rsquo;s spent a decade on IOS or EOS is a real investment. It&amp;rsquo;s not a week-long bootcamp. It&amp;rsquo;s months of building muscle memory on a fundamentally different operational model.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tooling and management plane.&lt;/strong&gt; Your existing monitoring, config management, and automation probably assumes a traditional NOS. Some of it ports over. Some of it gets rebuilt. That rebuild has a cost in engineering hours and in the operational risk of running new tooling in production. The bigger gap: SONiC doesn&amp;rsquo;t ship with a CloudVision or DNA Center equivalent. There&amp;rsquo;s no turnkey management plane for ZTP, firmware lifecycle, config compliance, or centralized visibility. Organizations with the scale to justify SONiC have often already built internal tooling for these functions and aren&amp;rsquo;t relying on vendor-provided platforms, which lowers this cost significantly. But if your operations today depend on a vendor management suite, budget for the engineering effort to replace it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wireless and adjacent systems.&lt;/strong&gt; SONiC is a wired switching NOS. You&amp;rsquo;ll need a separate wireless management stack, which adds another operational surface and cost line. Factor in the integration work between your wired and wireless management planes. It won&amp;rsquo;t be as seamless as a single-vendor converged solution.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Support contracts.&lt;/strong&gt; Commercial SONiC distributions aren&amp;rsquo;t free. Broadcom Enterprise SONiC, Dell Enterprise SONiC, Aviz Certified Community SONiC. They all come with support contracts. Cheaper than a Cisco SmartNet? Usually. Free? No.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Incident response.&lt;/strong&gt; Your mean time to resolution will be longer on a new platform. That&amp;rsquo;s not a knock on SONiC. It&amp;rsquo;s true of any platform your team hasn&amp;rsquo;t operated for years. It&amp;rsquo;s especially true at the access layer, where the SONiC ecosystem is younger: Marvell Prestera SAI drivers, 1G hardware platforms, and features like PVST+ and 802.1X are all more recent and less battle-tested than their data center counterparts. Budget for it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The BOM comparison is where the conversation starts. It shouldn&amp;rsquo;t be where it ends.&lt;/p&gt;
&lt;h2 id="what-youre-actually-trading"&gt;What You&amp;rsquo;re Actually Trading&lt;/h2&gt;
&lt;p&gt;The pitch for disaggregated networking is &amp;ldquo;escape vendor lock-in.&amp;rdquo; The reality is messier: you&amp;rsquo;re trading one type of dependency for another.&lt;/p&gt;
&lt;p&gt;With a traditional vendor, the deal is straightforward. You write a check for hardware, software, and support bundled together. You get a single number to call when things break. You&amp;rsquo;re locked to their roadmap, their pricing, and their release cadence, but you&amp;rsquo;re also insulated from a lot of operational complexity. That insulation has value, and it&amp;rsquo;s easy to underestimate until it&amp;rsquo;s gone.&lt;/p&gt;
&lt;p&gt;With SONiC and disaggregated hardware, you get procurement flexibility. You can source switches from Celestica this quarter and Edgecore next quarter. You&amp;rsquo;re not captive to one vendor&amp;rsquo;s pricing. But you take on the integration work. You own the testing matrix — every NOS version against every hardware platform against every feature combination you deploy, a validation effort that traditional vendors absorb for you. You&amp;rsquo;re responsible for validating that your NOS version works on your hardware with your feature set. That responsibility lands on your engineering team, and it doesn&amp;rsquo;t go away after the initial deployment.&lt;/p&gt;
&lt;p&gt;The same principle applies to network disaggregation: &lt;a href="https://www.infoq.com/articles/avoiding-lockin-switching-costs"&gt;focus on switching costs, not lock-in&lt;/a&gt;. Every platform has lock-in of some kind. The question is what it costs you to change. With Cisco, the switching cost is a hardware refresh and a config migration. With SONiC, the switching cost is lower on hardware but potentially higher on the operational and tooling side if you&amp;rsquo;ve built deep integrations.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s an upside to the trade that&amp;rsquo;s easy to overlook: with an open-source NOS, you can contribute upstream, influence the roadmap, and fix bugs yourself (or fund someone to). You&amp;rsquo;re not waiting on a vendor&amp;rsquo;s release train for a feature you need next quarter. That agency has real value for organizations with the engineering capacity to exercise it.&lt;/p&gt;
&lt;p&gt;The honest framing: you&amp;rsquo;re not eliminating dependency. You&amp;rsquo;re moving it from a vendor relationship to an internal engineering capability. Whether that&amp;rsquo;s a good trade depends entirely on your organization.&lt;/p&gt;
&lt;h2 id="the-scale-threshold"&gt;The Scale Threshold&lt;/h2&gt;
&lt;p&gt;The business case for SONiC at the access layer is scale-dependent. This isn&amp;rsquo;t a controversial statement — it&amp;rsquo;s just math.&lt;/p&gt;
&lt;p&gt;At small scale — say, under 100 switches — the engineering investment dominates. You&amp;rsquo;re hiring or retraining people, building tooling, standing up a lab, and absorbing the risk of a new platform. The hardware savings on 100 switches don&amp;rsquo;t cover that. Buy Meraki or Aruba, spend your engineering time on something else.&lt;/p&gt;
&lt;p&gt;At medium scale — a few hundred switches across a handful of sites — it gets interesting but not obvious. The hardware savings start to add up, but you&amp;rsquo;re still carrying the full weight of the operational investment. This is the zone where the decision depends heavily on your team&amp;rsquo;s existing capabilities. If you already have strong automation practices and Linux-comfortable engineers, the marginal cost of adding SONiC is lower. If you&amp;rsquo;re starting from scratch on both, it&amp;rsquo;s a harder sell.&lt;/p&gt;
&lt;p&gt;At large scale — thousands of switches across dozens or hundreds of sites — the math starts to favor SONiC, but only if the organizational readiness factors line up. The per-unit hardware savings compound across the fleet and across refresh cycles. The operational consistency argument kicks in: one NOS from spine to access port means one automation framework, one monitoring pipeline, one set of runbooks. And the negotiating power is real. When you&amp;rsquo;re buying thousands of switches, being able to source from multiple hardware vendors changes the dynamic. Scale is necessary for the business case to work, but it&amp;rsquo;s not sufficient on its own.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://network-notes.com/img/2026/sonic-scale-threshold.305df717225596bddccc93490df823d1cc74c476cb2092940a35344d671504a4.svg" alt="Scale Threshold: Hardware savings vs. engineering investment by fleet size" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;One thing the scale discussion often misses: &amp;ldquo;do nothing&amp;rdquo; isn&amp;rsquo;t free either. Vendor price increases compound — a 3–4% annual hardware uplift on a 2,000-switch fleet adds up over two refresh cycles. Lock-in deepens as you build more automation and tooling around a proprietary platform, raising your switching costs every year you stay. And the talent pool for legacy NOS administration is gradually shrinking as the industry shifts toward Linux-native infrastructure. The status quo has a cost trajectory too. Make sure your business case accounts for it.&lt;/p&gt;
&lt;p&gt;The variables that determine where your break-even sits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fleet size.&lt;/strong&gt; More switches = more capex savings to offset the opex investment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Number of sites.&lt;/strong&gt; More sites amplify both the savings (more hardware) and the complexity (more operational surface area).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Team maturity.&lt;/strong&gt; A team that already runs infrastructure as code has a shorter ramp than one that manages everything by hand.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Contract timing.&lt;/strong&gt; If you&amp;rsquo;re mid-cycle on a vendor contract with favorable terms, the urgency drops. If you&amp;rsquo;re facing a refresh with a steep price increase, the urgency spikes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Greenfield vs. brownfield.&lt;/strong&gt; New sites are easier. Migrating existing sites means running two platforms in parallel during the transition, which has its own cost.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="organizational-readiness"&gt;Organizational Readiness&lt;/h2&gt;
&lt;p&gt;Before you evaluate hardware or run a PoC, ask these questions about your organization:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Does your team manage infrastructure as code today?&lt;/strong&gt; If your network engineers are already writing Ansible playbooks, using Git, and deploying through CI/CD pipelines, the transition to SONiC is a platform change, not a paradigm change. If your workflow is &amp;ldquo;SSH into the switch and type commands,&amp;rdquo; you have a culture shift ahead of you that&amp;rsquo;s bigger than the technology shift.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can your network engineers troubleshoot at the Linux OS level?&lt;/strong&gt; SONiC problems don&amp;rsquo;t always look like network problems. Sometimes it&amp;rsquo;s a container that didn&amp;rsquo;t start. Sometimes it&amp;rsquo;s a systemd service in a failed state. Sometimes it&amp;rsquo;s a kernel driver issue. Your team needs to be as comfortable with &lt;code&gt;journalctl&lt;/code&gt; and &lt;code&gt;docker logs&lt;/code&gt; as they are with &lt;code&gt;show ip bgp&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can you recruit Linux-native network engineers?&lt;/strong&gt; This talent pool is growing, but it&amp;rsquo;s still smaller than the pool of traditional network engineers. An &lt;a href="https://www.networkworld.com/article/2075207/network-automation-challenges-are-dampening-success-rates.html"&gt;EMA survey on network automation&lt;/a&gt; found that 27% of respondents pointed to staffing issues and skills gaps as a top challenge, with one engineer noting: &amp;ldquo;The most challenging thing for me is the lack of network engineers who can contribute to automation. The community is small, and it&amp;rsquo;s hard to find people who can help you solve a problem.&amp;rdquo; If you&amp;rsquo;re in a market where hiring is already hard, factor in the recruiting challenge. If you&amp;rsquo;re in a tech hub where Linux skills are common, this is less of a concern.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Is your organization comfortable with open-source support models?&lt;/strong&gt; Commercial SONiC distributions come with support contracts, but the support experience is different from calling Cisco TAC. Community SONiC means GitHub issues and mailing lists. Know which model your organization can tolerate.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Do you have the patience for a multi-year build?&lt;/strong&gt; This isn&amp;rsquo;t a forklift upgrade you execute in a quarter. It&amp;rsquo;s a capability you build over years: lab, pilot, limited production, broad rollout. A realistic timeline, in my experience: 6–12 months to stand up a lab and run a pilot, another 6–12 months for limited production at a handful of sites, and 1–2 years to reach broad rollout. Most organizations won&amp;rsquo;t see net positive ROI until year two or three, when the hardware savings across refresh cycles start outpacing the cumulative engineering investment. If your leadership expects payback in the first year, set that expectation early. Or find a different project.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://network-notes.com/img/2026/sonic-rollout-timeline.465ffbc44c21032102efee2753af21517e8596910b42819e8911d6e1d59a5782.svg" alt="Realistic rollout timeline from lab to ROI" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;If you answered &amp;ldquo;no&amp;rdquo; to most of these, SONiC isn&amp;rsquo;t necessarily the wrong choice. But your business case needs to include the cost of getting to &amp;ldquo;yes&amp;rdquo; on each one.&lt;/p&gt;
&lt;h2 id="building-the-business-case"&gt;Building the Business Case&lt;/h2&gt;
&lt;p&gt;A credible business case has two parts: the spreadsheet and the narrative. You need both.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The spreadsheet&lt;/strong&gt; covers what you can quantify:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Hardware cost delta: per-unit savings × fleet size × number of refresh cycles in your planning horizon&lt;/li&gt;
&lt;li&gt;Support contract delta: commercial SONiC distribution costs vs. current vendor support&lt;/li&gt;
&lt;li&gt;Staffing costs: new hires, training programs, potential salary premium for Linux networking skills&lt;/li&gt;
&lt;li&gt;Tooling costs: what you build or buy to replace vendor-provided management platforms&lt;/li&gt;
&lt;li&gt;Transition costs: lab buildout, pilot program, migration execution, parallel operation during cutover&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To make this concrete: imagine a 1,000-switch fleet where white box hardware saves $2,000 per unit (a conservative estimate — Part 1 puts the delta at one-half to one-third of list price). Over two refresh cycles (roughly ten years), the capex delta is $4M. If your engineering investment is $1.5M in year one (hiring, lab, tooling) and $500K/year ongoing, you break even midway through year two. That math looks good on a slide. But if you also need to rebuild your management plane, add $500K–$1M to year one. If your team needs 12 months of ramp time before they&amp;rsquo;re productive on SONiC, push the break-even out another year. The point isn&amp;rsquo;t the specific numbers — it&amp;rsquo;s that the spreadsheet has to include all the lines, not just the hardware line.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The narrative&lt;/strong&gt; covers what you can&amp;rsquo;t easily put in a cell:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Procurement leverage — your incumbent vendors won&amp;rsquo;t stand still. They&amp;rsquo;re actively raising prices; &lt;a href="https://www.apmdigest.com/beyond-box-rethinking-network-infrastructure-era-supply-chain-volatility"&gt;Cisco implemented an average ~3.4% hardware uplift&lt;/a&gt; in late 2025, with similar increases for technical services. Walking into a renewal with a credible SONiC PoC in your back pocket will sharpen their pencil, and that leverage is valuable even if you never deploy a single white box switch. But the reverse is also true: if your business case depends on a specific capex delta, aggressive incumbent discounting can erode it. Build your case on the operational and strategic benefits, not just the BOM savings, because the BOM savings are the part your current vendor can most easily match&lt;/li&gt;
&lt;li&gt;Operational consistency — one NOS across your infrastructure reduces the cognitive load on your team and simplifies automation&lt;/li&gt;
&lt;li&gt;Talent pipeline — you&amp;rsquo;re hiring from the Linux and DevOps talent pool, not just the &amp;ldquo;CCIE or equivalent&amp;rdquo; pool&lt;/li&gt;
&lt;li&gt;Future optionality — you&amp;rsquo;re not waiting on one vendor&amp;rsquo;s roadmap for features you need. If the community or a different distro ships it first, you can adopt it&lt;/li&gt;
&lt;li&gt;Ecosystem fragmentation risk — multiple commercial SONiC distributions (Broadcom, Dell, Aviz, Asterfusion) have different feature sets, release cadences, and proprietary extensions. Evaluate portability between distros, not just between SONiC and traditional vendors. If you build deep integrations against one distro&amp;rsquo;s proprietary MCLAG implementation, you&amp;rsquo;ve traded one form of vendor lock-in for another&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Don&amp;rsquo;t oversell the savings. A business case that honestly acknowledges the tradeoffs (&amp;ldquo;we&amp;rsquo;ll save X on hardware but invest Y in engineering capability&amp;rdquo;) is more credible than one that only shows the capex delta. Decision-makers who&amp;rsquo;ve been around long enough have seen the &amp;ldquo;this will save us millions&amp;rdquo; slide deck before. They&amp;rsquo;re looking for the slide that says &amp;ldquo;here&amp;rsquo;s what could go wrong and here&amp;rsquo;s how we&amp;rsquo;ve accounted for it.&amp;rdquo;&lt;/p&gt;
&lt;h2 id="whats-next"&gt;What&amp;rsquo;s Next&lt;/h2&gt;
&lt;p&gt;This post covers whether the business case works. It doesn&amp;rsquo;t cover how you execute. If there&amp;rsquo;s interest, Part 3 will get into the operational details:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Commercial SONiC support vs. Cisco TAC in practice.&lt;/strong&gt; Response times, escalation paths, bug fix turnaround. What &amp;ldquo;different&amp;rdquo; actually looks like when you&amp;rsquo;re troubleshooting a production outage at 2 AM.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Brownfield migration strategies.&lt;/strong&gt; How do you actually migrate a live campus network from IOS to SONiC? Site-by-site? Building-by-building? How long do you run dual-stack, and what does parallel operation cost?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The testing and validation burden.&lt;/strong&gt; What the NOS version × hardware platform × feature set matrix looks like in practice, and how to build a validation pipeline that doesn&amp;rsquo;t consume your entire engineering team.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="my-take"&gt;My Take&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;My take&lt;/strong&gt;: The technology is ready — &lt;a href="https://network-notes.com/posts/2026/sonic-access-layer/"&gt;Part 1&lt;/a&gt; covered that. The business case is real at scale. But &amp;ldquo;at scale&amp;rdquo; is doing a lot of work in that sentence.&lt;/p&gt;
&lt;p&gt;The organizations that will succeed with SONiC at the edge are the ones that treat it as an organizational capability investment, not a cost-cutting exercise. If the only line in your business case is &amp;ldquo;cheaper switches,&amp;rdquo; you&amp;rsquo;re setting yourself up for a painful surprise when the opex bill comes due.&lt;/p&gt;
&lt;p&gt;The right framing: SONiC at the edge is a bet on your team&amp;rsquo;s ability to operate open infrastructure. The hardware savings fund that bet. The payoff is operational flexibility and procurement leverage that compounds over time — but only if you invest in the team and tooling to realize it.&lt;/p&gt;
&lt;p&gt;If your organization has the scale, the engineering maturity, and the patience for a multi-year build, the math works. If you&amp;rsquo;re looking for a quick win on next quarter&amp;rsquo;s budget, buy Meraki.&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>SONiC Hits the Access Layer: Why 1G Commodity Switches Change the Math</title><link>https://network-notes.com/posts/2026/sonic-access-layer/</link><pubDate>Mon, 06 Apr 2026 10:00:00 -0500</pubDate><author>brett@network-notes.com (Brett Lykins)</author><dc:creator>Brett Lykins</dc:creator><guid>https://network-notes.com/posts/2026/sonic-access-layer/</guid><description>&lt;p&gt;For years, the pitch for SONiC went something like this: &amp;ldquo;It&amp;rsquo;s what the hyperscalers run.&amp;rdquo; And that was true — and also the problem. If you weren&amp;rsquo;t running a spine-leaf fabric at 100G+, SONiC had nothing for you. The hardware didn&amp;rsquo;t exist at the access layer. No 48-port 1G copper switches. No PoE. Nothing below 25G.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s no longer the case.&lt;/p&gt;
&lt;p&gt;I currently work on a team deploying SONiC 1G switches into a global network, with a goal to be &amp;ldquo;vendor of choice&amp;rdquo; for our internal customers — so I&amp;rsquo;m not a neutral party here. What I&amp;rsquo;m seeing firsthand is a market that has shifted faster than most enterprise network teams realize. If you&amp;rsquo;re managing tens of thousands of network nodes — office buildings, warehouses, retail locations, distribution centers — SONiC should be on your evaluation list.&lt;/p&gt;
&lt;h2 id="sonic-in-60-seconds"&gt;SONiC in 60 Seconds&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;re not familiar: SONiC (Software for Open Networking in the Cloud) is an open-source network operating system built on Debian Linux. Microsoft developed it for Azure&amp;rsquo;s data center network and &lt;a href="https://azure.microsoft.com/en-us/blog/sonic-the-networking-switch-software-that-powers-the-microsoft-global-cloud/"&gt;open-sourced it in 2016&lt;/a&gt;. It now lives under the &lt;a href="https://sonicfoundation.dev/"&gt;Linux Foundation&lt;/a&gt; as the SONiC Foundation.&lt;/p&gt;
&lt;p&gt;The key architectural idea is the Switch Abstraction Interface (SAI). SAI sits between SONiC and the switching ASIC, providing a vendor-neutral API. This means the same NOS runs on silicon from Broadcom, Marvell, NVIDIA/Mellanox, and Intel — you pick the hardware, SONiC talks to it through SAI. The NOS itself is containerized: each major function (BGP, LLDP, SNMP, teamd, etc.) runs in its own Docker container, which means you can upgrade individual components without bouncing the whole switch.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://network-notes.com/img/2026/sonic-sai-architecture.7e796fb6ead04998bfe48669b9cca13eee1b0d2ce4322c10be416e2969e5c2e1.svg" alt="SONiC SAI Architecture" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;For most of its life, SONiC ran exclusively on data center hardware — Broadcom Trident and Tomahawk ASICs in 25G/100G/400G switches. The enterprise access layer was not in scope.&lt;/p&gt;
&lt;h2 id="what-changed"&gt;What Changed&lt;/h2&gt;
&lt;p&gt;Three things happened in 2024–2025 that made SONiC viable at the access layer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Marvell&amp;rsquo;s Prestera ASICs got SAI support.&lt;/strong&gt; This is the big one. Marvell&amp;rsquo;s Prestera line is the silicon inside most commodity 1G and 2.5G access switches. Once SAI drivers existed for Prestera, SONiC could run on access-layer hardware. Marvell published a blog in October 2024 specifically about &lt;a href="https://www.marvell.com/blogs/cloud-managed-enterprise-cme-switches-powered-by-sonic.html"&gt;Cloud-Managed Enterprise switches powered by SONiC&lt;/a&gt;, signaling that this wasn&amp;rsquo;t a side project.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hardware vendors shipped product.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.globenewswire.com/news-release/2024/05/15/2882388/0/en/Celestica-Introduces-Four-New-Enterprise-Access-Networking-Switches.html"&gt;Celestica announced four new enterprise access switches&lt;/a&gt; in May 2024 — the ES1000, ES1010, ES1050, and EG1050 — with 1GbE and 2.5GbE options, up to 48 ports, PoE support, and SONiC compatibility.&lt;/li&gt;
&lt;li&gt;Asterfusion started shipping 48-port 1G PoE+ switches (CX204Y, CX206Y series) on Marvell Prestera with Enterprise SONiC preloaded.&lt;/li&gt;
&lt;li&gt;Edgecore expanded its EPS and AS4600 series for enterprise access deployments.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These aren&amp;rsquo;t prototypes or reference designs — they&amp;rsquo;re shipping products with purchase orders behind them.&lt;/p&gt;
&lt;p&gt;The result: you can now buy a 48-port 1G PoE+ L3 switch with 25G uplinks, running SONiC, from multiple vendors, at a fraction of what a comparable Cisco Catalyst or Arista switch costs. That sentence was not possible two years ago.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The access-layer feature set filled in.&lt;/strong&gt; MC-LAG, DHCP snooping, and IGMP snooping are available in the commercial SONiC distributions. The &lt;a href="https://sonicfoundation.dev/sonic-202505-powering-ai-fabrics-and-enterprise-networks-with-precision-and-insight/"&gt;SONiC 202505 release&lt;/a&gt; added PVST+ and 802.1X/MAB authentication — two features that were blockers for many access-layer deployments. The gap between SONiC and a traditional enterprise NOS feature set is narrowing fast.&lt;/p&gt;
&lt;h2 id="commercial-support-catches-up"&gt;Commercial Support Catches Up&lt;/h2&gt;
&lt;p&gt;Hardware alone doesn&amp;rsquo;t make a platform viable. The support ecosystem had to catch up, and it has.&lt;/p&gt;
&lt;p&gt;The SONiC Foundation now counts &lt;a href="https://www.linuxfoundation.org/press/sonic-strengthens-global-collaboration-with-seven-new-members-and-expands-presence-at-open-source-summit-europe-2025"&gt;36 member organizations&lt;/a&gt; including Arista, who joined as a Premier Member in 2025. As of early 2024, the project had over 4,250 active contributors across 520+ organizations. This isn&amp;rsquo;t a niche project anymore.&lt;/p&gt;
&lt;p&gt;On the commercial support side, enterprises now have real options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Broadcom Enterprise SONiC Distribution&lt;/strong&gt; — the most mature commercial offering. Hardened, extended feature set beyond community SONiC, multi-ASIC support, and commercial support contracts. Think of it as the Red Hat to community SONiC&amp;rsquo;s Fedora.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dell Enterprise SONiC&lt;/strong&gt; — built on Broadcom&amp;rsquo;s distribution, validated on Dell PowerSwitch hardware, with Dell&amp;rsquo;s 24/7 support organization behind it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Aviz Certified Community SONiC&lt;/strong&gt; — &lt;a href="https://networkworld.com/article/4103440/aviz-networks-launches-enterprise-grade-community-sonic-distribution.html"&gt;launched in late 2025&lt;/a&gt;, this is a pre-tested, multi-ASIC distribution based on community SONiC with added bug fixes, telemetry, and 24/7 commercial support.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Asterfusion AsterNOS&lt;/strong&gt; — a commercial SONiC distribution specifically targeting access-layer and enterprise deployments on Marvell Prestera hardware.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You don&amp;rsquo;t have to go it alone with raw community SONiC anymore (though you can, if you have the engineering team for it).&lt;/p&gt;
&lt;h2 id="the-open-nos-landscape"&gt;The Open NOS Landscape&lt;/h2&gt;
&lt;p&gt;SONiC isn&amp;rsquo;t the only open or disaggregated NOS option. Here&amp;rsquo;s how the alternatives stack up for enterprise access-layer deployments:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;SONiC&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;DENT OS&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Pica8 PICOS&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;OcNOS&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;License&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;td&gt;Open source (LF)&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Origin&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Microsoft (2016)&lt;/td&gt;
&lt;td&gt;Amazon/LF (2019)&lt;/td&gt;
&lt;td&gt;Pica8 (2012)&lt;/td&gt;
&lt;td&gt;IP Infusion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hardware Abstraction&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SAI&lt;/td&gt;
&lt;td&gt;Linux SwitchDev&lt;/td&gt;
&lt;td&gt;Proprietary&lt;/td&gt;
&lt;td&gt;Proprietary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Supported ASICs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Broadcom, Marvell, NVIDIA, Intel&lt;/td&gt;
&lt;td&gt;Marvell Prestera&lt;/td&gt;
&lt;td&gt;Broadcom&lt;/td&gt;
&lt;td&gt;Multi-vendor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;1G Access Switches&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes (2024+)&lt;/td&gt;
&lt;td&gt;Yes (designed for edge)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PoE Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Varies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Commercial Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multiple vendors&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Pica8&lt;/td&gt;
&lt;td&gt;IP Infusion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Management Platform&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Varies by distro&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;AmpCon&lt;/td&gt;
&lt;td&gt;OcNOS Manager&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Enterprise Momentum&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4,250+ contributors; multiple commercial distros&lt;/td&gt;
&lt;td&gt;Small community&lt;/td&gt;
&lt;td&gt;Single vendor&lt;/td&gt;
&lt;td&gt;Established in SP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A few notes on the alternatives:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DENT OS&lt;/strong&gt; is the most interesting comparison. Also a Linux Foundation project, DENT was designed from the start for the distributed enterprise edge — retail, campus, remote sites. It uses Linux SwitchDev instead of SAI, which means you configure switches using standard Linux tools (&lt;code&gt;ip&lt;/code&gt;, &lt;code&gt;bridge&lt;/code&gt;, &lt;code&gt;tc&lt;/code&gt;). If you have a team of Linux sysadmins who happen to manage network switches, DENT&amp;rsquo;s approach is appealing. The tradeoff is a much smaller ecosystem and limited commercial support options. DENT is worth watching, but SONiC has the momentum.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pica8 PICOS&lt;/strong&gt; is a commercial disaggregated NOS targeting enterprise campus networks. It runs on Broadcom-based white box hardware, offers a &lt;a href="https://www.pica8.com/pica8-software-platform/"&gt;Junos-like CLI&lt;/a&gt; with transactional commit-confirm, and includes &lt;a href="https://www.pica8.com/ampcon-network-controller/"&gt;AmpCon&lt;/a&gt; for centralized ZTP and lifecycle management. The tradeoff: it&amp;rsquo;s not open source, and you&amp;rsquo;re trading one vendor dependency for another (albeit a cheaper one).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;OcNOS&lt;/strong&gt; from IP Infusion is a mature commercial disaggregated NOS with strong service provider adoption — worth evaluating if you want hardware disaggregation without the open-source model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A note on Cumulus Linux&lt;/strong&gt;: Cumulus was the original disaggregated NOS pioneer and deserves credit for proving the model. But NVIDIA&amp;rsquo;s acquisition fundamentally changed its trajectory. It now runs exclusively on NVIDIA Spectrum ASICs, dropped Broadcom support after the 4.x release line, and as of mid-2025, &lt;a href="https://blog.ipspace.net/2025/06/cumulus-linux-gone"&gt;is no longer available as a standalone image&lt;/a&gt;. I&amp;rsquo;m not including it in the comparison table because it&amp;rsquo;s not a viable option for new enterprise access-layer deployments. If you&amp;rsquo;re currently running Cumulus on Broadcom hardware, &lt;a href="https://www.pica8.com/cumulus-linux-migration/"&gt;Pica8 is actively positioning PICOS as a migration path&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="when-to-evaluate-sonic-for-your-access-layer"&gt;When to Evaluate SONiC for Your Access Layer&lt;/h2&gt;
&lt;p&gt;SONiC makes the most sense when several of these conditions are true:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You have scale.&lt;/strong&gt; If you&amp;rsquo;re managing hundreds or thousands of access switches across many sites, the per-unit cost savings on hardware compound fast. One engineer on the Network Automation Forum &lt;a href="https://networkautomation.forum/blog/sonic-reality-check-enterprise-deployments-beyond-the-hype"&gt;estimated potential savings of $1.3 million&lt;/a&gt; on a single edge upgrade — though the same discussion noted that compensating costs in staffing and operational complexity can eat into those numbers. White box hardware commonly runs at one-half to one-third the cost of branded equivalents from Cisco or Arista.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You have (or can build) the engineering team.&lt;/strong&gt; SONiC is Linux. Your network engineers need to be comfortable with Debian, systemd, Docker containers, and debugging at the OS level. This is a different skill set than CLI-driven IOS or EOS administration. If your team already treats infrastructure as code and manages config via automation, the transition is smoother than you&amp;rsquo;d expect. If your team&amp;rsquo;s workflow is &amp;ldquo;SSH in and type commands,&amp;rdquo; you have a culture change ahead of you, not just a technology change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You want operational consistency across your stack.&lt;/strong&gt; If you&amp;rsquo;re already running SONiC in your data center (or plan to), extending it to the access layer means one NOS, one automation framework, one monitoring pipeline, one set of operational procedures from spine to access port. That operational simplification has real value at scale.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You&amp;rsquo;re tired of vendor lock-in.&lt;/strong&gt; With SONiC, you can swap hardware vendors without changing your NOS, your automation, or your operational tooling. If Celestica has a better price on 48-port switches this quarter but Edgecore wins next quarter, you can mix and match.&lt;/p&gt;
&lt;h2 id="when-to-stay-away"&gt;When to Stay Away&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;You need plug-and-play simplicity.&lt;/strong&gt; If your network team is small, your sites are few, and you need something that works out of the box with a GUI and phone support, buy Meraki or Aruba. SONiC will cost you more in engineering time than you&amp;rsquo;ll save on hardware.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You need mature wireless integration.&lt;/strong&gt; SONiC is a wired switching NOS. It doesn&amp;rsquo;t manage access points, and there&amp;rsquo;s no equivalent to Cisco&amp;rsquo;s wireless controller integration or Arista&amp;rsquo;s CloudVision for converged wired/wireless management. You&amp;rsquo;ll need a separate wireless solution and the operational overhead that comes with it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Your organization can&amp;rsquo;t tolerate risk on network infrastructure.&lt;/strong&gt; Community SONiC has real rough edges — unstable build pipelines, inconsistent platform testing across releases, and gaps in management tooling compared to mature commercial platforms. The commercial distributions smooth this out significantly, but even Enterprise SONiC is younger and less battle-tested at the access layer than IOS-XE or EOS. If your business requires five-nines uptime guarantees with vendor accountability, the commercial SONiC distributions are getting there, but you should evaluate carefully.&lt;/p&gt;
&lt;h2 id="the-trajectory"&gt;The Trajectory&lt;/h2&gt;
&lt;p&gt;The direction is clear. ONUG reported in early 2025 that &lt;a href="https://onug.net/blog/state-of-enterprise-sonic-adoption-the-open-networking-shift-accelerates-in-the-ai-era/"&gt;SONiC is increasingly deployed in enterprise verticals like telco and financial services&lt;/a&gt;, and Aviz Networks CEO Vishal Shukla noted that &lt;a href="https://www.morningstar.com/news/business-wire/20251209613448/aviz-networks-unveils-turnkey-enterprise-grade-sonic-as-open-network-adoption-accelerates"&gt;&amp;ldquo;since 2023, SONiC enterprise deployment has shifted from early adopters to large, mainstream enterprises.&amp;rdquo;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The 1G access-layer hardware availability is the inflection point. SONiC&amp;rsquo;s value proposition was always strong at the data center layer — the economics made sense when you&amp;rsquo;re buying 100G switches at scale. But most enterprise network ports are 1G copper at the access layer. That&amp;rsquo;s where the volume is. That&amp;rsquo;s where the spend is. And now that&amp;rsquo;s where SONiC runs.&lt;/p&gt;
&lt;p&gt;The organizations that will benefit most are the ones with enough scale to justify the engineering investment and enough sites to make the hardware savings meaningful.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;My take&lt;/strong&gt;: The &amp;ldquo;SONiC is only for hyperscalers&amp;rdquo; era is over. The hardware gap at the access layer — the thing that kept SONiC out of most enterprise conversations — closed in 2024. If you&amp;rsquo;re running tens of thousands of switch ports across a global footprint, the math works, the hardware exists, and the support ecosystem is real. The learning curve is steep and the cultural shift is significant, but for organizations with the scale and engineering maturity to make the investment, the economics speak for themselves.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In &lt;a href="https://network-notes.com/posts/2026/sonic-business-case/"&gt;Part 2&lt;/a&gt;, I dig into the business case — what the real costs look like beyond the BOM, when the math works, and how to build a case that doesn&amp;rsquo;t fall apart when the opex bill comes due.&lt;/p&gt;</description></item><item><title>Testing ISAKMP Part 4: Using Scapy</title><link>https://network-notes.com/posts/2026/netcat-isakmp-4/</link><pubDate>Sun, 22 Feb 2026 10:59:00 -0500</pubDate><author>brett@network-notes.com (Brett Lykins)</author><dc:creator>Brett Lykins</dc:creator><guid>https://network-notes.com/posts/2026/netcat-isakmp-4/</guid><description>&lt;p&gt;In &lt;a href="https://network-notes.com/posts/2026/netcat-isakmp-2/"&gt;Part 2&lt;/a&gt;, I showed how to test ISAKMP with a pre-built hex string and netcat. In &lt;a href="https://network-notes.com/posts/2026/netcat-isakmp-3/"&gt;Part 3&lt;/a&gt;, we dove deep into the byte-by-byte construction of ISAKMP packets. Now let&amp;rsquo;s use Scapy to automate this with Python.&lt;/p&gt;
&lt;h2 id="why-scapy"&gt;Why Scapy?&lt;/h2&gt;
&lt;p&gt;Netcat with hex strings works for one-off tests, but Scapy lets you build packets programmatically, parse responses automatically, and script tests across multiple targets. It understands ISAKMP structure and handles length fields and checksums for you.&lt;/p&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Python 3.8+ installed&lt;/li&gt;
&lt;li&gt;Basic Python knowledge&lt;/li&gt;
&lt;li&gt;Understanding of ISAKMP concepts (see &lt;a href="https://network-notes.com/posts/2016/netcat-isakmp/"&gt;Part 1&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Target ISAKMP/IKE peer to test&lt;/li&gt;
&lt;li&gt;Root/sudo access (required for raw socket operations)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="installation"&gt;Installation&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Install Poetry (if not already installed)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sSL https://install.python-poetry.org &lt;span class="p"&gt;|&lt;/span&gt; python3 -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Install Scapy via Poetry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/your/project
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;poetry init
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;poetry add scapy
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Or install globally&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pip install scapy
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Verify installation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3 -c &lt;span class="s2"&gt;&amp;#34;from scapy.all import *; print(conf.version)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Scapy requires raw socket access, which needs root/sudo privileges. When using Poetry with sudo, you must install dependencies as root: &lt;code&gt;sudo poetry install&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="basic-isakmp-packet-with-scapy"&gt;Basic ISAKMP Packet with Scapy&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://network-notes.com/img/2026/isakmp-scapy-flow.85fbc3877bcea42ff3fab784f110a15c91d9337bed8a1727d6097b18b6f54fdf.svg" alt="Scapy ISAKMP testing flow: Python script builds packet via Scapy, sends to VPN peer, parses response" loading="lazy" /&gt;&lt;/p&gt;
&lt;h3 id="the-simple-approach"&gt;The Simple Approach&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="ch"&gt;#!/usr/bin/env python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;scapy.all&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Target configuration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;target_ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;192.168.1.1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;target_port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Build basic ISAKMP packet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Create IP and UDP layers&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;IP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;target_ip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;udp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UDP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sport&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dport&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;target_port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Create ISAKMP header (Identity Protection, Main Mode)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;isakmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ISAKMP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;init_cookie&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;RandString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;# Random 8-byte initiator cookie&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;resp_cookie&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\x00&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# Responder cookie (zeros for initial packet)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;exch_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# Identity Protection (Main Mode)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Create SA payload with a simple transform&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;sa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ISAKMP_payload_SA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;prop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ISAKMP_payload_Proposal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;proto&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# ISAKMP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;trans&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ISAKMP_payload_Transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;transform_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;# KEY_IKE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Assemble the complete packet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;packet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;udp&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;isakmp&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;sa&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Send and receive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sr1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;packet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;No response received&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="building-a-complete-phase-1-packet"&gt;Building a Complete Phase 1 Packet&lt;/h2&gt;
&lt;h3 id="transform-set-configuration"&gt;Transform Set Configuration&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Define transform set parameters&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Using modern cryptographic standards&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Encryption algorithms (RFC 3602, RFC 2409)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ENCR_AES_CBC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ENCR_3DES_CBC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Hash algorithms (RFC 2409)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;HASH_SHA256&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;HASH_SHA1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Diffie-Hellman groups (RFC 2409, RFC 3526)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;DH_GROUP_14&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="c1"&gt;# 2048-bit MODP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;DH_GROUP_5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="c1"&gt;# 1536-bit MODP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;DH_GROUP_2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="c1"&gt;# 1024-bit MODP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Authentication methods (RFC 2409)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;AUTH_PSK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;# Pre-Shared Key&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;AUTH_RSA_SIG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="c1"&gt;# RSA Signatures&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Life duration type (RFC 2409)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;LIFE_TYPE_SECONDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;LIFE_TYPE_KILOBYTES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Define a modern transform set&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;transform_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;encryption&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ENCR_AES_CBC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;key_length&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# AES-256&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;hash&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HASH_SHA256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;dh_group&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DH_GROUP_14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;auth_method&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AUTH_PSK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;lifetime&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt; &lt;span class="c1"&gt;# 24 hours in seconds&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="constructing-the-packet"&gt;Constructing the Packet&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;span class="lnt"&gt;45
&lt;/span&gt;&lt;span class="lnt"&gt;46
&lt;/span&gt;&lt;span class="lnt"&gt;47
&lt;/span&gt;&lt;span class="lnt"&gt;48
&lt;/span&gt;&lt;span class="lnt"&gt;49
&lt;/span&gt;&lt;span class="lnt"&gt;50
&lt;/span&gt;&lt;span class="lnt"&gt;51
&lt;/span&gt;&lt;span class="lnt"&gt;52
&lt;/span&gt;&lt;span class="lnt"&gt;53
&lt;/span&gt;&lt;span class="lnt"&gt;54
&lt;/span&gt;&lt;span class="lnt"&gt;55
&lt;/span&gt;&lt;span class="lnt"&gt;56
&lt;/span&gt;&lt;span class="lnt"&gt;57
&lt;/span&gt;&lt;span class="lnt"&gt;58
&lt;/span&gt;&lt;span class="lnt"&gt;59
&lt;/span&gt;&lt;span class="lnt"&gt;60
&lt;/span&gt;&lt;span class="lnt"&gt;61
&lt;/span&gt;&lt;span class="lnt"&gt;62
&lt;/span&gt;&lt;span class="lnt"&gt;63
&lt;/span&gt;&lt;span class="lnt"&gt;64
&lt;/span&gt;&lt;span class="lnt"&gt;65
&lt;/span&gt;&lt;span class="lnt"&gt;66
&lt;/span&gt;&lt;span class="lnt"&gt;67
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;scapy.all&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;build_isakmp_packet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transform_set&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Build a complete ISAKMP Phase 1 Main Mode packet with specified transform set.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Args:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; target_ip: Target IP address
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; transform_set: Dictionary with encryption, hash, dh_group, auth_method, lifetime
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Returns:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Complete Scapy packet ready to send
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# IP and UDP layers&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;IP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;target_ip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;udp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UDP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sport&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dport&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# ISAKMP header&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;isakmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ISAKMP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;init_cookie&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;RandString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;resp_cookie&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\x00&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;exch_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# Identity Protection (Main Mode)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Build transform attributes as list of (type, value) tuples&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Type numbers: 1=Encryption, 2=Hash, 3=Auth, 4=Group, 11=LifeType, 12=LifeDuration, 14=KeyLength&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;transforms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transform_set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;encryption&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="c1"&gt;# Encryption algorithm&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transform_set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hash&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="c1"&gt;# Hash algorithm&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transform_set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;dh_group&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="c1"&gt;# DH Group&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transform_set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;auth_method&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="c1"&gt;# Authentication method&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;# Life Type: seconds&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transform_set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;lifetime&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="c1"&gt;# Life Duration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Add key length if specified&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;transform_set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;key_length&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;transforms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transform_set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;key_length&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Build Transform payload&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ISAKMP_payload_Transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;transform_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# KEY_IKE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;transforms&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;transforms&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Build Proposal payload&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;proposal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ISAKMP_payload_Proposal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;proposal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;proto&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# ISAKMP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;trans&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Build SA payload&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;sa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ISAKMP_payload_SA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;doi&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# IPsec DOI (lowercase field name)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;situation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# Identity Only&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;prop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;proposal&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Assemble complete packet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;packet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;udp&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;isakmp&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;sa&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;packet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Example usage&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;192.168.1.1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;packet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;build_isakmp_packet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transform_set&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Scapy&amp;rsquo;s ISAKMP implementation uses a list of &lt;code&gt;(type, value)&lt;/code&gt; tuples for transform attributes, not individual attribute objects. This is simpler and matches how the protocol actually works.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="sending-and-analyzing-responses"&gt;Sending and Analyzing Responses&lt;/h2&gt;
&lt;h3 id="send-the-packet"&gt;Send the Packet&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_isakmp_packet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;packet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Send ISAKMP packet and capture response.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Args:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; packet: Scapy packet to send
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; timeout: Response timeout in seconds
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Returns:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Response packet or None
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;[*] Sending ISAKMP packet to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;packet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;IP&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:500&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;[*] Initiator Cookie: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;packet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ISAKMP&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init_cookie&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Send packet and wait for response&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sr1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;packet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;[+] Response received from &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;IP&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;[-] No response received (timeout)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Send the packet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;send_isakmp_packet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;packet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;haslayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ISAKMP&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Extract basic info&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;[*] Response Details:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34; Responder Cookie: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ISAKMP&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resp_cookie&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34; Exchange Type: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ISAKMP&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exch_type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34; Next Payload: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ISAKMP&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next_payload&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Check if SA payload is present&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;haslayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ISAKMP_payload_SA&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;[+] SA payload received - transform set accepted!&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;[-] No SA payload - transform set rejected&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Note: Some devices send NOTIFY payloads on rejection&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Check for ISAKMP_payload_Notification for detailed error info&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="understanding-the-response"&gt;Understanding the Response&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;span class="lnt"&gt;45
&lt;/span&gt;&lt;span class="lnt"&gt;46
&lt;/span&gt;&lt;span class="lnt"&gt;47
&lt;/span&gt;&lt;span class="lnt"&gt;48
&lt;/span&gt;&lt;span class="lnt"&gt;49
&lt;/span&gt;&lt;span class="lnt"&gt;50
&lt;/span&gt;&lt;span class="lnt"&gt;51
&lt;/span&gt;&lt;span class="lnt"&gt;52
&lt;/span&gt;&lt;span class="lnt"&gt;53
&lt;/span&gt;&lt;span class="lnt"&gt;54
&lt;/span&gt;&lt;span class="lnt"&gt;55
&lt;/span&gt;&lt;span class="lnt"&gt;56
&lt;/span&gt;&lt;span class="lnt"&gt;57
&lt;/span&gt;&lt;span class="lnt"&gt;58
&lt;/span&gt;&lt;span class="lnt"&gt;59
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_isakmp_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Parse ISAKMP response and extract transform set details.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Args:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; response: Scapy packet containing ISAKMP response
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Returns:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Dictionary with parsed response details
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;haslayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ISAKMP&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;responder_cookie&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ISAKMP&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resp_cookie&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;exchange_type&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ISAKMP&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exch_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;accepted&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;transform_set&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Check for SA payload (indicates acceptance)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;haslayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ISAKMP_payload_SA&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;accepted&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Extract transform attributes if present&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;haslayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ISAKMP_payload_Transform&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ISAKMP_payload_Transform&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Parse attributes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;attributes&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;attr&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;attr_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attribute_type&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;attr_val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attribute_value&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Map attribute types to names&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;attr_type&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mh"&gt;0x8001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x0001&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;transform_set&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;encryption&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attr_val&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;attr_type&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mh"&gt;0x800e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x000e&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;transform_set&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;key_length&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attr_val&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;attr_type&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mh"&gt;0x8002&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x0002&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;transform_set&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hash&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attr_val&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;attr_type&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mh"&gt;0x8004&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x0004&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;transform_set&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;dh_group&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attr_val&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;attr_type&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mh"&gt;0x8003&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x0003&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;transform_set&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;auth_method&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attr_val&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Example usage&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parse_isakmp_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;parsed&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;accepted&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;[+] Transform set ACCEPTED by peer&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34; Encryption: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;transform_set&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;encryption&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;N/A&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34; Hash: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;transform_set&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hash&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;N/A&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34; DH Group: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;transform_set&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;dh_group&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;N/A&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;[-] Transform set REJECTED or no response&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="advanced-usage"&gt;Advanced Usage&lt;/h2&gt;
&lt;h3 id="testing-multiple-transform-sets"&gt;Testing Multiple Transform Sets&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;span class="lnt"&gt;45
&lt;/span&gt;&lt;span class="lnt"&gt;46
&lt;/span&gt;&lt;span class="lnt"&gt;47
&lt;/span&gt;&lt;span class="lnt"&gt;48
&lt;/span&gt;&lt;span class="lnt"&gt;49
&lt;/span&gt;&lt;span class="lnt"&gt;50
&lt;/span&gt;&lt;span class="lnt"&gt;51
&lt;/span&gt;&lt;span class="lnt"&gt;52
&lt;/span&gt;&lt;span class="lnt"&gt;53
&lt;/span&gt;&lt;span class="lnt"&gt;54
&lt;/span&gt;&lt;span class="lnt"&gt;55
&lt;/span&gt;&lt;span class="lnt"&gt;56
&lt;/span&gt;&lt;span class="lnt"&gt;57
&lt;/span&gt;&lt;span class="lnt"&gt;58
&lt;/span&gt;&lt;span class="lnt"&gt;59
&lt;/span&gt;&lt;span class="lnt"&gt;60
&lt;/span&gt;&lt;span class="lnt"&gt;61
&lt;/span&gt;&lt;span class="lnt"&gt;62
&lt;/span&gt;&lt;span class="lnt"&gt;63
&lt;/span&gt;&lt;span class="lnt"&gt;64
&lt;/span&gt;&lt;span class="lnt"&gt;65
&lt;/span&gt;&lt;span class="lnt"&gt;66
&lt;/span&gt;&lt;span class="lnt"&gt;67
&lt;/span&gt;&lt;span class="lnt"&gt;68
&lt;/span&gt;&lt;span class="lnt"&gt;69
&lt;/span&gt;&lt;span class="lnt"&gt;70
&lt;/span&gt;&lt;span class="lnt"&gt;71
&lt;/span&gt;&lt;span class="lnt"&gt;72
&lt;/span&gt;&lt;span class="lnt"&gt;73
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_transform_sets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transform_sets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Test multiple transform sets against a target.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Args:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; target_ip: Target IP address
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; transform_sets: List of transform set dictionaries
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; timeout: Response timeout in seconds
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Returns:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; List of results with acceptance status
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transform_sets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;[*] Testing transform set &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transform_sets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34; Encryption: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;encryption&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, Hash: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hash&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, DH: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;dh_group&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Build and send packet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;packet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;build_isakmp_packet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sr1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;packet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Parse response&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parse_isakmp_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;transform_set&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;accepted&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;accepted&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;parsed&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;response&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;parsed&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;accepted&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34; [+] ACCEPTED&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34; [-] REJECTED or no response&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Small delay between tests&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Define multiple transform sets to test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;test_sets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Modern strong crypto&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;encryption&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# AES-CBC&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;key_length&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;hash&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# SHA-256&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;dh_group&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# 2048-bit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;auth_method&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;lifetime&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Moderate crypto&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;encryption&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# AES-CBC&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;key_length&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;hash&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# SHA-256&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;dh_group&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# 2048-bit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;auth_method&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;lifetime&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Run tests&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;test_transform_sets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;192.168.1.1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test_sets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Summary&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;=&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;SUMMARY&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;=&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;accepted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;accepted&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Accepted: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accepted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; transform sets&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="aggressive-mode-vs-main-mode"&gt;Aggressive Mode vs Main Mode&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;span class="lnt"&gt;45
&lt;/span&gt;&lt;span class="lnt"&gt;46
&lt;/span&gt;&lt;span class="lnt"&gt;47
&lt;/span&gt;&lt;span class="lnt"&gt;48
&lt;/span&gt;&lt;span class="lnt"&gt;49
&lt;/span&gt;&lt;span class="lnt"&gt;50
&lt;/span&gt;&lt;span class="lnt"&gt;51
&lt;/span&gt;&lt;span class="lnt"&gt;52
&lt;/span&gt;&lt;span class="lnt"&gt;53
&lt;/span&gt;&lt;span class="lnt"&gt;54
&lt;/span&gt;&lt;span class="lnt"&gt;55
&lt;/span&gt;&lt;span class="lnt"&gt;56
&lt;/span&gt;&lt;span class="lnt"&gt;57
&lt;/span&gt;&lt;span class="lnt"&gt;58
&lt;/span&gt;&lt;span class="lnt"&gt;59
&lt;/span&gt;&lt;span class="lnt"&gt;60
&lt;/span&gt;&lt;span class="lnt"&gt;61
&lt;/span&gt;&lt;span class="lnt"&gt;62
&lt;/span&gt;&lt;span class="lnt"&gt;63
&lt;/span&gt;&lt;span class="lnt"&gt;64
&lt;/span&gt;&lt;span class="lnt"&gt;65
&lt;/span&gt;&lt;span class="lnt"&gt;66
&lt;/span&gt;&lt;span class="lnt"&gt;67
&lt;/span&gt;&lt;span class="lnt"&gt;68
&lt;/span&gt;&lt;span class="lnt"&gt;69
&lt;/span&gt;&lt;span class="lnt"&gt;70
&lt;/span&gt;&lt;span class="lnt"&gt;71
&lt;/span&gt;&lt;span class="lnt"&gt;72
&lt;/span&gt;&lt;span class="lnt"&gt;73
&lt;/span&gt;&lt;span class="lnt"&gt;74
&lt;/span&gt;&lt;span class="lnt"&gt;75
&lt;/span&gt;&lt;span class="lnt"&gt;76
&lt;/span&gt;&lt;span class="lnt"&gt;77
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;build_aggressive_mode_packet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transform_set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;identity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;test@example.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Build ISAKMP Aggressive Mode packet.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Aggressive Mode sends more information in the first packet (including identity)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; but completes the exchange faster (3 packets vs 6 in Main Mode).
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Args:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; target_ip: Target IP address
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; transform_set: Transform set dictionary
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; identity: Identity string for ID payload
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Returns:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Complete Scapy packet
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# IP and UDP layers&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;IP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;target_ip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;udp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UDP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sport&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dport&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# ISAKMP header for Aggressive Mode&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;isakmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ISAKMP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;init_cookie&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;RandString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;resp_cookie&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\x00&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;next_payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# SA payload&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;exch_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# Aggressive Mode (vs 2 for Main Mode)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Build SA payload (same as Main Mode)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# ... (use build_isakmp_packet logic for SA/Proposal/Transform)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Add Key Exchange payload (sent in first packet in Aggressive Mode)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ke&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ISAKMP_payload_KE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;next_payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# ID payload follows&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ke&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;RandString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# DH public value (size depends on DH group)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Add Identification payload (sent in first packet in Aggressive Mode)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;id_payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ISAKMP_payload_ID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;next_payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;IDtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# ID_USER_FQDN&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;IdentData&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;identity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Assemble: ISAKMP / SA / KE / ID&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Note: In Main Mode, KE and ID come in later packets&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;packet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;udp&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;isakmp&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;sa&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;ke&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;id_payload&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;packet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Compare the two modes:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Main Mode (6 packets total, more secure)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Packet 1: HDR, SA&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Packet 2: HDR, SA&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Packet 3: HDR, KE, Nonce&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Packet 4: HDR, KE, Nonce&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Packet 5: HDR*, ID, HASH&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Packet 6: HDR*, ID, HASH&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;main_mode_packet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;build_isakmp_packet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;192.168.1.1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transform_set&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Main Mode packet size: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main_mode_packet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; bytes&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Main Mode exchange type: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;main_mode_packet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ISAKMP&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exch_type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Aggressive Mode (3 packets total, faster but exposes identity)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Packet 1: HDR, SA, KE, Nonce, ID&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Packet 2: HDR, SA, KE, Nonce, ID, HASH&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Packet 3: HDR*, HASH&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;aggressive_packet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;build_aggressive_mode_packet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;192.168.1.1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transform_set&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Aggressive Mode packet size: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aggressive_packet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; bytes&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Aggressive Mode exchange type: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;aggressive_packet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ISAKMP&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exch_type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Security consideration:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Main Mode encrypts identity, Aggressive Mode sends it in CLEARTEXT&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# This is a critical security vulnerability - attackers can harvest identities&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Use Main Mode unless you have a specific requirement for Aggressive Mode&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="nat-t-detection"&gt;NAT-T Detection&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# TODO: Add NAT-T vendor ID&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# TODO: Test for NAT-T support&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="creating-a-reusable-script"&gt;Creating a Reusable Script&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve created a complete script that combines all these techniques. The full code is in the &lt;a href="https://github.com/lykinsbd/nn_examples/tree/master/isakmp_testing"&gt;nn_examples repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The repository includes additional transform sets (including legacy crypto) for real-world compatibility testing.&lt;/p&gt;
&lt;h3 id="quick-start"&gt;Quick Start&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Clone the repository&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone https://github.com/lykinsbd/nn_examples.git
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; nn_examples/isakmp_testing
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Install dependencies with Poetry (both user and root)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;poetry install
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo poetry install
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Test a single target&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo poetry run python isakmp_tester.py 192.168.1.1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Test multiple transform sets&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo poetry run python isakmp_tester.py 192.168.1.1 --test-multiple
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Use Aggressive Mode&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo poetry run python isakmp_tester.py 192.168.1.1 --aggressive
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;sudo poetry install&lt;/code&gt;?&lt;/strong&gt; Scapy requires raw socket access (root privileges). Since &lt;code&gt;sudo&lt;/code&gt; runs in a separate environment, dependencies must be installed both as your user and as root.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="example-output"&gt;Example Output&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[*] Testing 192.168.1.1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Mode: Main Mode
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Encryption: 7
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Hash: 4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; DH Group: 14
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[+] ACCEPTED - Transform set accepted by peer
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Responder Cookie: a1b2c3d4e5f67890
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="self-contained-testing"&gt;Self-Contained Testing&lt;/h3&gt;
&lt;p&gt;The repository also includes &lt;code&gt;isakmp_listener.py&lt;/code&gt; - a test responder for testing without a real VPN device:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Terminal 1: Start the test listener&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo poetry run python isakmp_listener.py
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Terminal 2: Test against localhost&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo poetry run python isakmp_tester.py 127.0.0.1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo poetry run python isakmp_tester.py 127.0.0.1 --test-multiple
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The listener accepts all proposed transform sets and logs packet details. Good for testing the tester script, learning packet structure, and debugging without VPN hardware.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; When testing against localhost (127.0.0.1), you may receive ISAKMP responses even without the listener running. This is the kernel&amp;rsquo;s UDP socket handling, not actual ISAKMP protocol responses. For realistic testing, use a real ISAKMP/VPN device or test between different machines.&lt;/p&gt;
&lt;h2 id="comparison-scapy-vs-netcat-vs-ike-scan"&gt;Comparison: Scapy vs Netcat vs ike-scan&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Scapy&lt;/th&gt;
&lt;th&gt;Netcat&lt;/th&gt;
&lt;th&gt;ike-scan&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Dynamic construction&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Response parsing&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scripting&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⚠️&lt;/td&gt;
&lt;td&gt;⚠️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Learning tool&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production ready&lt;/td&gt;
&lt;td&gt;⚠️&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Installation&lt;/td&gt;
&lt;td&gt;pip&lt;/td&gt;
&lt;td&gt;Built-in&lt;/td&gt;
&lt;td&gt;Package manager&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="security-considerations"&gt;Security Considerations&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Authorization Required:&lt;/strong&gt; Only test systems you own or have explicit permission to test. ISAKMP probes are logged by VPN concentrators, firewalls, and IDS/IPS systems. Repeated probes trigger security alerts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Phase 1 Only:&lt;/strong&gt; This covers IKE Phase 1 (ISAKMP) only. Establishing a full VPN tunnel requires Phase 2 (IPsec Quick Mode) and proper authentication credentials.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cryptographic Parameters:&lt;/strong&gt; Use SHA-256 (not SHA-1), AES-256, and DH Group 14+ (2048-bit minimum).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting&lt;/h2&gt;
&lt;h3 id="permission-denied"&gt;Permission Denied&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Scapy requires root for raw sockets&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo poetry run python script.py
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Make sure dependencies are installed for root too&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo poetry install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="no-response-received"&gt;No Response Received&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Check firewall rules (UDP/500)&lt;/li&gt;
&lt;li&gt;Verify target IP is correct&lt;/li&gt;
&lt;li&gt;Confirm ISAKMP service is running&lt;/li&gt;
&lt;li&gt;Check for NAT between you and target&lt;/li&gt;
&lt;li&gt;VPN concentrators rate-limit ISAKMP attempts - wait 30-60 seconds between tests&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="import-errors"&gt;Import Errors&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Install missing dependencies&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pip install scapy cryptography
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="next-steps"&gt;Next Steps&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Explore IKEv2 with Scapy&lt;/li&gt;
&lt;li&gt;Build Phase 2 (Quick Mode) packets&lt;/li&gt;
&lt;li&gt;Implement full IKE exchange&lt;/li&gt;
&lt;li&gt;Add support for certificates (RSA signatures)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Scapy bridges the gap between manual packet construction and specialized tools like ike-scan. While netcat teaches you the raw protocol (&lt;a href="https://network-notes.com/posts/2026/netcat-isakmp-2/"&gt;Part 2&lt;/a&gt;) and manual construction reveals the internals (&lt;a href="https://network-notes.com/posts/2026/netcat-isakmp-3/"&gt;Part 3&lt;/a&gt;), Scapy gives you the power to automate and scale your testing.&lt;/p&gt;
&lt;p&gt;For production VPN scanning, use dedicated tools like &lt;code&gt;ike-scan&lt;/code&gt; or &lt;code&gt;nmap --script ike-version&lt;/code&gt;. For learning and custom testing, Scapy is unmatched.&lt;/p&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://scapy.readthedocs.io/"&gt;Scapy Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tools.ietf.org/html/rfc2408"&gt;RFC 2408 - ISAKMP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tools.ietf.org/html/rfc2409"&gt;RFC 2409 - IKE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://scapy.readthedocs.io/en/latest/api/scapy.layers.isakmp.html"&gt;Scapy ISAKMP Layer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Testing ISAKMP Part 3: Building Packets from Scratch</title><link>https://network-notes.com/posts/2026/netcat-isakmp-3/</link><pubDate>Sat, 14 Feb 2026 17:16:00 -0500</pubDate><author>brett@network-notes.com (Brett Lykins)</author><dc:creator>Brett Lykins</dc:creator><guid>https://network-notes.com/posts/2026/netcat-isakmp-3/</guid><description>&lt;p&gt;In &lt;a href="https://network-notes.com/posts/2026/netcat-isakmp-2/"&gt;Part 2&lt;/a&gt;, I provided a ready-to-use ISAKMP packet for testing VPN connectivity. But where did that hex string come from? This post breaks down ISAKMP packet construction byte-by-byte.&lt;/p&gt;
&lt;p&gt;Understanding packet structure at the byte level helps you debug protocol issues, customize packets for specific testing scenarios, and understand what tools like &lt;code&gt;ike-scan&lt;/code&gt; are doing under the hood.&lt;/p&gt;
&lt;h2 id="the-approach"&gt;The Approach&lt;/h2&gt;
&lt;p&gt;After spending time reading &lt;a href="https://tools.ietf.org/html/rfc2408"&gt;RFC2408&lt;/a&gt; for the basic header in Part 1, I decided to base this complete packet on an actual captured ISAKMP conversation. If you don&amp;rsquo;t have an ISAKMP capture handy, you can find protocol details and examples at the &lt;a href="https://wiki.wireshark.org/Protocols/isakmp"&gt;Wireshark ISAKMP Protocol page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I still recommend reading RFC2408 to understand ISAKMP message structure, otherwise this sea of hexadecimal will be confusing.&lt;/p&gt;
&lt;h2 id="hand-crafted-datagram"&gt;Hand-Crafted Datagram&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://network-notes.com/img/2026/isakmp-packet-structure.3de67292a8201c42c549ee1c0c92a5040e7729cf2573d62d65821fc728e71fe7.svg" alt="ISAKMP Phase 1 packet structure showing nested header, SA, Proposal, and Transform payloads" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;The primary ingredient needed to make a complete ISAKMP datagram is an attempt to set up the SA (Security Association) including nested inner payloads and an offer of a Transform set.&lt;/p&gt;
&lt;h3 id="header"&gt;Header&lt;/h3&gt;
&lt;p&gt;As discussed previously, the header format for ISAKMP (IKE v1) is defined in &lt;a href="https://tools.ietf.org/html/rfc2408#section-3.1"&gt;RFC2408 Section 3.1&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Initiator and Responder Cookies&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;8 bytes per Cookie&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Initiator Cookie&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;AKA the SPI or Security Parameter Index&lt;/li&gt;
&lt;li&gt;We&amp;rsquo;ll make this a value of 1 since it has to be something and we don&amp;rsquo;t care what it is.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Production note:&lt;/strong&gt; RFC 2408 recommends pseudo-random generation to prevent DoS attacks&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x00\x00\x00\x00\x00\x00\x00\x01&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Responder Cookie&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;All zeros because this is an initial communication, and as the Initiator, we don&amp;rsquo;t know what the responder will send yet.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x00\x00\x00\x00\x00\x00\x00\x00&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Next Payload&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;1 byte&lt;/li&gt;
&lt;li&gt;The Next Payload will be of a Security Association type, so per RFC2408 Section 3.1 this is a value of 1.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x01&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Major Version and Minor Version&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;4 bits each&lt;/li&gt;
&lt;li&gt;Mandatory values of 1 and 0, because we&amp;rsquo;re using ISAKMP version 1.0&lt;/li&gt;
&lt;li&gt;That would be 0001 and 0000 in binary.&lt;/li&gt;
&lt;li&gt;Together, that would look like 00010000, which equals 10 in Hex.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x10&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Exchange Type&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;1 byte&lt;/li&gt;
&lt;li&gt;The Exchange Types are laid out in &lt;a href="https://www.rfc-editor.org/rfc/rfc2408.html#section-4.4"&gt;RFC2408 Section 4.4&lt;/a&gt; to &lt;a href="https://www.rfc-editor.org/rfc/rfc2408.html#section-4.8"&gt;Section 4.8&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;From our sample packet capture, I see we want an Identity Protection type (02).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x02&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flags&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;1 byte&lt;/li&gt;
&lt;li&gt;We still need no flags, so leaving this at 0.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x00&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Message ID&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;4 bytes&lt;/li&gt;
&lt;li&gt;Must be zeros per RFC2408 for an initial Phase 1 datagram like we&amp;rsquo;re simulating.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x00\x00\x00\x00&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Length&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;4 bytes&lt;/li&gt;
&lt;li&gt;I have doubled back from the bottom to fill in this value, as you are unable to calculate it until you have built all the inner payloads.&lt;/li&gt;
&lt;li&gt;This totals up to 88 bytes or 58 in hex.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x00\x00\x00\x58&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="generic-payload-header"&gt;Generic Payload Header&lt;/h3&gt;
&lt;p&gt;There is a short header on each ISAKMP payload as shown below, it is defined in &lt;a href="https://www.rfc-editor.org/rfc/rfc2408.html#section-3.2"&gt;RFC2408 Section 3.2&lt;/a&gt;.
We&amp;rsquo;ll need one on our outer SA Payload, and one will be present on any interior/nested payloads as well (such as additional Proposals or Vendor Specific Attributes).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Next Payload&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;1 byte&lt;/li&gt;
&lt;li&gt;This is the last outer payload, so 0.&lt;/li&gt;
&lt;li&gt;We&amp;rsquo;re not simulating Vendor Specific Attributes or anything like that, just trying to make a minimum viable datagram.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x00&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RESERVED&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;1 byte&lt;/li&gt;
&lt;li&gt;Unused, set to Zero.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x00&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Payload Length&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;2 bytes&lt;/li&gt;
&lt;li&gt;I am cheating somewhat, and know this value as I have gone forward to finish the payload, and then calculated backward to get this value.&lt;/li&gt;
&lt;li&gt;It includes the Generic Payload header elements (including the length field we&amp;rsquo;re calculating now) and the length of the inner SA Payload from below.&lt;/li&gt;
&lt;li&gt;That totals 60 bytes or 3c in hex.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x00\x3c&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="security-association-payload"&gt;Security Association Payload&lt;/h3&gt;
&lt;p&gt;SA Payload is defined in &lt;a href="https://tools.ietf.org/html/rfc2408#section-3.4"&gt;RFC2408 Section 3.4&lt;/a&gt; and laid out as shown below.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Domain of Interpretation (DOI)&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;4 bytes&lt;/li&gt;
&lt;li&gt;See &lt;a href="https://www.rfc-editor.org/rfc/rfc2408.html#section-2.1"&gt;RFC2408 Section 2.1&lt;/a&gt; for more information on what the DOI is.
For our purposes, this is a value of 1, for an IPSEC DOI (&lt;a href="https://www.rfc-editor.org/rfc/rfc2407.html"&gt;RFC2407&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x00\x00\x00\x01&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Situation&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Variable length (4 bytes in this case)&lt;/li&gt;
&lt;li&gt;based on the DOI For IPSEC DOI, per &lt;a href="https://www.rfc-editor.org/rfc/rfc2407.html#section-4.2"&gt;RFC2407 section 4.2&lt;/a&gt;, this will be 4 bytes.&lt;/li&gt;
&lt;li&gt;Our sample capture indicates to say that this is Identity Only (value of 00000001).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x00\x00\x00\x01&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="proposal-payload"&gt;Proposal Payload&lt;/h3&gt;
&lt;p&gt;Inside the SA Payload, there is the payload containing the ISAKMP Proposal on how to go about setting up the Security Association.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Next Payload&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;1 byte&lt;/li&gt;
&lt;li&gt;This will be zero as there is no other Proposal being sent.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x00&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RESERVED&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;1 byte&lt;/li&gt;
&lt;li&gt;Unused, set to zero.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x00&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Payload length&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;2 bytes&lt;/li&gt;
&lt;li&gt;Again, I only know this value as I am working backward, and have calculated it out knowing the length of this payload.&lt;/li&gt;
&lt;li&gt;It is 48 bytes long (including the two bytes above, these two bytes, and everything in the payload below), which is 30 in hex.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x00\x30&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Proposal Number&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;1 byte&lt;/li&gt;
&lt;li&gt;If there is more than one Proposal in the payload, which Proposal is this?&lt;/li&gt;
&lt;li&gt;In our case, this is the first and only Proposal, so set this to 00000001.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x01&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Protocol ID&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;1 byte&lt;/li&gt;
&lt;li&gt;This took some tracking down, but as defined in &lt;a href="https://tools.ietf.org/html/rfc2408#section-3.6"&gt;RFC2408 Section 3.6&lt;/a&gt;, the specific values for these are defined by the DOI in use (IPSec DOI in our case).&lt;/li&gt;
&lt;li&gt;The IDs are defined in &lt;a href="https://www.rfc-editor.org/rfc/rfc2407.html"&gt;RFC2407 Section 4.4.1&lt;/a&gt;, and we&amp;rsquo;re using ISAKMP (value of 1).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x01&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SPI Size&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;1 byte&lt;/li&gt;
&lt;li&gt;Set to zero for ISAKMP (as the Initiator and Responder cookies will be used for the SPIs).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x00&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Number of Transforms&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;1 byte&lt;/li&gt;
&lt;li&gt;We&amp;rsquo;re just sending one Transform, so set it to a value of 1.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x01&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="transform-payload"&gt;Transform Payload&lt;/h3&gt;
&lt;p&gt;And finally, inside of THAT payload are one or more Transforms.
(I told you this would get confusing…)
For our purposes, I&amp;rsquo;m just going to offer a single, common transform set consisting of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Encryption: AES-128&lt;/li&gt;
&lt;li&gt;Hash: SHA&lt;/li&gt;
&lt;li&gt;Diffie-Hellman: Group 5&lt;/li&gt;
&lt;li&gt;Authentication: Pre-Shared Key&lt;/li&gt;
&lt;li&gt;Lifetime 86400 seconds&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Security Notice:&lt;/strong&gt; This packet uses legacy cryptographic parameters for maximum compatibility with older devices. For production VPNs, use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Hash: SHA-256 or SHA-384&lt;/li&gt;
&lt;li&gt;Encryption: AES-256-CBC or AES-256-GCM&lt;/li&gt;
&lt;li&gt;DH Group: Group 14 (2048-bit) or higher&lt;/li&gt;
&lt;li&gt;Consider IKEv2 instead of IKEv1 for modern deployments&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;It is worth noting that the Transform Attributes are expressed as TLV (Type-Length-Value) or TV (Type-Value).
If you&amp;rsquo;re not familiar with what a TLV is, &lt;a href="https://en.wikipedia.org/wiki/Type-length-value"&gt;I recommend becoming familiar&lt;/a&gt; with it as it crops up again and again in network protocols.
TLVs allow a protocol to be extended in ways beyond the original scope, for example, TLVs are what make a routing protocol like ISIS able to be extended to support Shortest Path Bridging.&lt;/p&gt;
&lt;p&gt;The TLV/TV information for our purposes is laid out across all three original IPSEC/ISAKMP/IKE RFCs (&lt;a href="https://tools.ietf.org/html/rfc2407"&gt;RFC2407&lt;/a&gt;, &lt;a href="https://tools.ietf.org/html/rfc2408"&gt;RFC2408&lt;/a&gt;, and &lt;a href="https://tools.ietf.org/html/rfc2409"&gt;RFC2409&lt;/a&gt; respectively.)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For getting the bulk of actual Types, Values, and whether they are fixed or variable length, you need &lt;a href="https://tools.ietf.org/html/rfc2409#appendix-A"&gt;RFC2409 Appendix A&lt;/a&gt; which lists the IKE Attribute Assigned Numbers.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tools.ietf.org/html/rfc2408#section-3.3"&gt;RFC2408 Section 3.3&lt;/a&gt; however, specifies the underlying Data Attribute format.&lt;/li&gt;
&lt;li&gt;But to figure out what Domain of Interpretation (DOI) to use we have to consult &lt;a href="https://tools.ietf.org/html/rfc2407#section-4.4.2"&gt;RFC2407 Section 4.4.2&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is also important to call out that &lt;a href="https://tools.ietf.org/html/rfc2408#section-3.3"&gt;RFC2408 Section 3.3&lt;/a&gt; indicates that the most significant (leftmost) bit of the Attribute field, will determine if these bits represent a TLV (0) or TV (1).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If it is a TLV (0), then there is a length field sent.&lt;/li&gt;
&lt;li&gt;If it is a TV (1), then it is assumed to be two bytes, and no length field is sent.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, this means that a TV (1) for an Encryption Algorithm (1) of AES-CBC (7) in Binary would be:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;1000 0000 0000 0001 0000 0000 0000 0111&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Translated to Hexadecimal, that would come out as:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;\x80\x01\x00\x07&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;We will similarly construct each value below (and we&amp;rsquo;ll use this value as well).&lt;/p&gt;
&lt;h4 id="transform-set"&gt;Transform Set&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Next Payload&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;1 byte&lt;/li&gt;
&lt;li&gt;This will be zero as no other Transforms are being sent.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x00&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RESERVED&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;1 byte&lt;/li&gt;
&lt;li&gt;Unused, set to zero.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x00&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Payload length&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;2 bytes&lt;/li&gt;
&lt;li&gt;This is a value of 40 bytes that I only know because I finished building the payload and came back to calculate this value.&lt;/li&gt;
&lt;li&gt;40 in hex is 28.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x00\x28&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transform Number&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;1 byte&lt;/li&gt;
&lt;li&gt;This is the only transform we&amp;rsquo;re sending, and it&amp;rsquo;s the first, so 1.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x01&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transform ID&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;1 byte&lt;/li&gt;
&lt;li&gt;This value is specified by the DOI, so &lt;a href="https://tools.ietf.org/html/rfc2407#section-4.4.2"&gt;RFC2407 Section 4.4.2&lt;/a&gt; says to use 1 for IKE.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x01&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RESERVED2&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;2 bytes&lt;/li&gt;
&lt;li&gt;Two more bytes of mandatory zeros.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x00\x00&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="transform-ike-attributes"&gt;Transform IKE Attributes&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Encryption Algorithm&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;4 bytes&lt;/li&gt;
&lt;li&gt;As we worked out above, this should be four bytes for a TV (starting with 80).&lt;/li&gt;
&lt;li&gt;Next, comes the encryption algorithm attribute class of 1.&lt;/li&gt;
&lt;li&gt;The attribute class value portion of this TV was a little tricky because AES came out well after &lt;a href="https://tools.ietf.org/html/rfc2409"&gt;RFC2409&lt;/a&gt; was published.&lt;/li&gt;
&lt;li&gt;That information is laid out in &lt;a href="https://www.rfc-editor.org/rfc/rfc3602#section-5.1"&gt;RFC3602 (The AES-CBC Cipher Algorithm and Its Use with IPsec) Section 5.1&lt;/a&gt;, and indicates that the AES ID is &amp;ldquo;7&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x80\x01\x00\x07&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Key Length&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;4 bytes&lt;/li&gt;
&lt;li&gt;We&amp;rsquo;re using a Key Length (class attribute of 14, or 0e in hex) of 128 bits (80 in hex), and this is a TV as well, so we get:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x80\x0e\x00\x80&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hash Algorithm&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;4 bytes&lt;/li&gt;
&lt;li&gt;We&amp;rsquo;re using a hash algorithm (class attribute of 2) of SHA (which is also a value of 2), and again we&amp;rsquo;re getting these values from &lt;a href="https://tools.ietf.org/html/rfc2409#appendix-A"&gt;RFC2409 Appendix A&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Also, this field is a TV, which means it again starts with \x80 and is 4 bytes, so we have:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x80\x02\x00\x02&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diffie-Hellman Group&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;4 bytes&lt;/li&gt;
&lt;li&gt;For the Group Description (class attribute of 4) we&amp;rsquo;re going to use DH Group 5, which scouring &lt;a href="https://tools.ietf.org/html/rfc2409#appendix-A"&gt;RFC2409 Appendix A&lt;/a&gt; again tells us is a value of 5.&lt;/li&gt;
&lt;li&gt;This is again a TV (starts with \x80) and is 4 bytes:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x80\x04\x00\x05&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authentication Method&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;4 bytes&lt;/li&gt;
&lt;li&gt;For the Authentication Method (class attribute of 3) we are using a pre-shared key (value of 1):&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x80\x03\x00\x01&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lifetime&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;4 + variable bytes (8 in this case)&lt;/li&gt;
&lt;li&gt;We&amp;rsquo;re going to set a lifetime of one day (or 86400 seconds).&lt;/li&gt;
&lt;li&gt;This breaks into two fields.
&lt;ul&gt;
&lt;li&gt;First a TV of Life Type (what are we measuring, seconds or kilobytes) with:
&lt;ul&gt;
&lt;li&gt;A class attribute of 11 (0b in hex) and a value of 1 for seconds.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Next is a TLV (which starts with 00 instead of 80) of Life Duration (how many of that unit) with:
&lt;ul&gt;
&lt;li&gt;A class attribute of 12 (0c in hex), a two-byte length field with a value of 4 bytes, and a value of 86400 decimal = 0x15180 hex (written as &lt;code&gt;\x00\x01\x51\x80&lt;/code&gt; in big-endian).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x80\x0b\x00\x01\x00\x0c\x00\x04\x00\x01\x51\x80&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-complete-hex-string"&gt;The Complete Hex String&lt;/h2&gt;
&lt;p&gt;Now to add all of this into one giant string of hexadecimal:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x10\x02\x00\x00\x00\x00\x00\x00\x00\x00\x58\x00\x00\x00\x3c\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x30\x01\x01\x00\x01\x00\x00\x00\x28\x01\x01\x00\x00\x80\x01\x00\x07\x80\x0e\x00\x80\x80\x02\x00\x02\x80\x04\x00\x05\x80\x03\x00\x01\x80\x0b\x00\x01\x00\x0c\x00\x04\x00\x01\x51\x80
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This 88-byte packet represents a complete ISAKMP Phase 1 Identity Protection exchange initiation with the transform set we defined above.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Building ISAKMP packets byte-by-byte is tedious but educational. It gives you deep insight into how VPN protocols work at the wire level. For practical testing, use the ready-made packet from &lt;a href="https://network-notes.com/posts/2026/netcat-isakmp-2/"&gt;Part 2&lt;/a&gt; or tools like &lt;code&gt;ike-scan&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Takeaways:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ISAKMP packets have nested payload structures (SA → Proposal → Transform)&lt;/li&gt;
&lt;li&gt;Each payload has its own header with length fields&lt;/li&gt;
&lt;li&gt;Transform attributes use TLV/TV encoding&lt;/li&gt;
&lt;li&gt;Length fields must be calculated backward from inner payloads&lt;/li&gt;
&lt;li&gt;Understanding this structure helps debug VPN issues&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For those interested in automating this process, consider exploring Python with the Scapy library for dynamic packet construction.&lt;/p&gt;</description></item><item><title>Testing ISAKMP Part 2: Pre-Built Packets</title><link>https://network-notes.com/posts/2026/netcat-isakmp-2/</link><pubDate>Sat, 07 Feb 2026 10:00:00 -0500</pubDate><author>brett@network-notes.com (Brett Lykins)</author><dc:creator>Brett Lykins</dc:creator><guid>https://network-notes.com/posts/2026/netcat-isakmp-2/</guid><description>&lt;p&gt;In &lt;a href="https://network-notes.com/posts/2016/netcat-isakmp/"&gt;my previous post&lt;/a&gt; about using &lt;code&gt;netcat&lt;/code&gt; to test ISAKMP, I validated that UDP/500 was open &lt;strong&gt;to&lt;/strong&gt; the destination, and that semi-valid ISAKMP headers were being received by the peer (a Cisco ASAv in this case). However, this validation required me to be knowledgeable in troubleshooting the Cisco ASA platform so that I could determine if my pseudo-ISAKMP packets arrived (and also required that I control or have access to the ASA). What if the peer is outside of your control or is a platform you are not at all familiar with? In that case, what I want to do now is send a complete ISAKMP initial communication and receive a reply from the peer. This test serves the dual purpose of removing the need for debugs or logging on the peer and validates the return path (and any stateful/L7 inspection) as well.&lt;/p&gt;
&lt;p&gt;Rather than hand-crafting every byte again, I based this packet on an actual captured ISAKMP conversation. If you don&amp;rsquo;t have a capture handy, you can find ISAKMP protocol details and examples at the &lt;a href="https://wiki.wireshark.org/Protocols/isakmp"&gt;Wireshark ISAKMP Protocol page&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Want to understand how this packet was built?&lt;/strong&gt; Check out &lt;a href="https://network-notes.com/posts/2026/netcat-isakmp-3/"&gt;Part 3&lt;/a&gt; for a detailed byte-by-byte breakdown of ISAKMP packet construction.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="the-test-packet"&gt;The Test Packet&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve constructed a complete 88-byte ISAKMP Phase 1 packet with the following transform set:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Encryption:&lt;/strong&gt; AES-128-CBC&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hash:&lt;/strong&gt; SHA-1&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DH Group:&lt;/strong&gt; Group 5 (1536-bit MODP)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authentication:&lt;/strong&gt; Pre-Shared Key&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lifetime:&lt;/strong&gt; 86400 seconds (24 hours)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Security Notice:&lt;/strong&gt; This packet uses legacy cryptographic parameters for maximum compatibility with older devices. For production VPNs, use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Hash: SHA-256 or SHA-384&lt;/li&gt;
&lt;li&gt;Encryption: AES-256-CBC or AES-256-GCM&lt;/li&gt;
&lt;li&gt;DH Group: Group 14 (2048-bit) or higher&lt;/li&gt;
&lt;li&gt;Consider IKEv2 instead of IKEv1 for modern deployments&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference&lt;/h2&gt;
&lt;p&gt;For those who want to skip the detailed explanation and just test connectivity, here&amp;rsquo;s everything you need:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Complete ISAKMP Test Packet:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;ISAKMP_PACKET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x10\x02\x00\x00\x00\x00\x00\x00\x00\x00\x58\x00\x00\x00\x3c\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x30\x01\x01\x00\x01\x00\x00\x00\x28\x01\x01\x00\x00\x80\x01\x00\x07\x80\x0e\x00\x80\x80\x02\x00\x02\x80\x04\x00\x05\x80\x03\x00\x01\x80\x0b\x00\x01\x00\x0c\x00\x04\x00\x01\x51\x80&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Basic Test:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; -ne &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$ISAKMP_PACKET&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; nc -u TARGET_IP &lt;span class="m"&gt;500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;With Timeout and Response Capture:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; -ne &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$ISAKMP_PACKET&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; timeout &lt;span class="m"&gt;5&lt;/span&gt; nc -u TARGET_IP &lt;span class="m"&gt;500&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; hexdump -C
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;What This Packet Offers:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Encryption: AES-128-CBC&lt;/li&gt;
&lt;li&gt;Hash: SHA-1&lt;/li&gt;
&lt;li&gt;DH Group: Group 5 (1536-bit MODP)&lt;/li&gt;
&lt;li&gt;Authentication: Pre-Shared Key&lt;/li&gt;
&lt;li&gt;Lifetime: 86400 seconds (24 hours)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="testing-the-complete-datagram"&gt;Testing the Complete Datagram&lt;/h2&gt;
&lt;p&gt;Now that we have our complete ISAKMP packet, let&amp;rsquo;s test it with netcat. The beauty of this approach is that we&amp;rsquo;ll get a proper ISAKMP response back if the peer is functioning correctly, eliminating the need for debug access.&lt;/p&gt;
&lt;h3 id="basic-test"&gt;Basic Test&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; -ne &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$ISAKMP_PACKET&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; nc -u 192.168.1.1 &lt;span class="m"&gt;500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="enhanced-test-with-timeout"&gt;Enhanced Test with Timeout&lt;/h3&gt;
&lt;p&gt;For better control and to avoid hanging connections, use netcat with a timeout:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; -ne &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$ISAKMP_PACKET&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; timeout &lt;span class="m"&gt;5&lt;/span&gt; nc -u 192.168.1.1 &lt;span class="m"&gt;500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="capturing-the-response"&gt;Capturing the Response&lt;/h3&gt;
&lt;p&gt;To see the hex response for analysis:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; -ne &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$ISAKMP_PACKET&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; timeout &lt;span class="m"&gt;5&lt;/span&gt; nc -u 192.168.1.1 &lt;span class="m"&gt;500&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; hexdump -C
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="expected-results"&gt;Expected Results&lt;/h2&gt;
&lt;h3 id="analyzing-the-response"&gt;Analyzing the Response&lt;/h3&gt;
&lt;p&gt;When you receive a response, here&amp;rsquo;s how to decode it:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bytes 0-7 (Initiator Cookie):&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;00 00 00 00 00 00 00 01
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Should match YOUR initiator cookie - confirms this is a reply to your packet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bytes 8-15 (Responder Cookie):&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;a1 b2 c3 d4 e5 f6 07 08
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Non-zero random value - the peer&amp;rsquo;s SPI. Save this for multi-packet exchanges.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Byte 16 (Next Payload):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;01&lt;/code&gt; = SA payload (peer accepted and is proposing parameters)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0d&lt;/code&gt; = Notification payload (usually an error)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Byte 18 (Exchange Type):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;02&lt;/code&gt; = Identity Protection (Main Mode) - normal&lt;/li&gt;
&lt;li&gt;&lt;code&gt;05&lt;/code&gt; = Informational - usually error notification&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Bytes 24-27 (Length):&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;00 00 00 84
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Total packet length (132 bytes). Longer responses typically mean successful negotiation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Common Notification Payloads:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;0e 00&lt;/code&gt; (14) = NO-PROPOSAL-CHOSEN - transform set mismatch&lt;/li&gt;
&lt;li&gt;&lt;code&gt;18 00&lt;/code&gt; (24) = AUTHENTICATION-FAILED - PSK/cert issue&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0d 00&lt;/code&gt; (13) = INVALID-PAYLOAD-TYPE - malformed packet&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="successful-response"&gt;Successful Response&lt;/h3&gt;
&lt;p&gt;If the ISAKMP peer is functioning correctly, you should receive a response that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Has the same initiator cookie&lt;/strong&gt; (&lt;code&gt;00 00 00 00 00 00 00 01&lt;/code&gt;) in the first 8 bytes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Contains a responder cookie&lt;/strong&gt; (non-zero values in bytes 9-16)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shows exchange type 02&lt;/strong&gt; (Identity Protection) in byte 18&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Has a reasonable length&lt;/strong&gt; (typically 100+ bytes for a full SA response)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example successful response header:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;00000000 00 00 00 00 00 00 00 01 a1 b2 c3 d4 e5 f6 07 08 |................|
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;00000010 01 10 02 00 00 00 00 00 00 00 00 84 |............|
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="error-responses"&gt;Error Responses&lt;/h3&gt;
&lt;p&gt;Different error conditions will produce different responses:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No Matching Transform Sets:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You&amp;rsquo;ll get a response with a Notification payload&lt;/li&gt;
&lt;li&gt;The notification will indicate &amp;ldquo;NO-PROPOSAL-CHOSEN&amp;rdquo; (error 14)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Authentication Issues:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;May receive &amp;ldquo;AUTHENTICATION-FAILED&amp;rdquo; notification&lt;/li&gt;
&lt;li&gt;Or connection may be silently dropped&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Firewall/NAT Issues:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No response at all (timeout)&lt;/li&gt;
&lt;li&gt;Or ICMP unreachable messages&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="troubleshooting-tips"&gt;Troubleshooting Tips&lt;/h2&gt;
&lt;h3 id="no-response"&gt;No Response&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Check basic connectivity&lt;/strong&gt; first with a basic UDP test&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verify the target IP&lt;/strong&gt; is actually running ISAKMP/IKE&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Check for NAT/firewall&lt;/strong&gt; blocking UDP/500&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Try different source ports&lt;/strong&gt; - some firewalls are picky&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wait between attempts&lt;/strong&gt; - rate limiting may block rapid tests (30-60 seconds)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="malformed-packet-errors"&gt;Malformed Packet Errors&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Double-check the hex string&lt;/strong&gt; - any typos will break the packet&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verify packet length&lt;/strong&gt; matches the length field in the header&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test with Wireshark&lt;/strong&gt; to validate packet structure&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="transform-set-mismatches"&gt;Transform Set Mismatches&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Try different encryption algorithms&lt;/strong&gt; (3DES, AES-256)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adjust DH groups&lt;/strong&gt; (Group 2, Group 14)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Change hash algorithms&lt;/strong&gt; (MD5, SHA-256)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="nat-considerations"&gt;NAT Considerations&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;NAT Detection:&lt;/strong&gt;
If testing through NAT, be aware:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Source port randomization may break return traffic&lt;/li&gt;
&lt;li&gt;Some firewalls require NAT-T (UDP 4500) for NATed ISAKMP&lt;/li&gt;
&lt;li&gt;ESP (protocol 50) will be blocked by NAT without NAT-T&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Testing through NAT:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Bind to specific source port&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; -ne &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$ISAKMP_PACKET&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; nc -u -p &lt;span class="m"&gt;500&lt;/span&gt; TARGET_IP &lt;span class="m"&gt;500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;NAT-T Detection:&lt;/strong&gt;
Modern VPN gateways may respond on UDP 4500 instead of 500 if NAT is detected.&lt;/p&gt;
&lt;h3 id="aggressive-mode-vs-main-mode"&gt;Aggressive Mode vs Main Mode&lt;/h3&gt;
&lt;p&gt;This packet uses Main Mode (Identity Protection). If you get no response, the peer might only support Aggressive Mode:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Change Exchange Type from &lt;code&gt;\x02&lt;/code&gt; to &lt;code&gt;\x04&lt;/code&gt; (byte 18)&lt;/li&gt;
&lt;li&gt;Note: Aggressive Mode is deprecated due to security weaknesses&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="ikev2-confusion"&gt;IKEv2 Confusion&lt;/h3&gt;
&lt;p&gt;If you get INVALID_MAJOR_VERSION notification:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Target only supports IKEv2 (RFC 7296)&lt;/li&gt;
&lt;li&gt;This technique only works for IKEv1&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;ike-scan&lt;/code&gt; tool for IKEv2 testing&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="validating-your-packet"&gt;Validating Your Packet&lt;/h3&gt;
&lt;p&gt;Before testing against production systems, validate your packet structure:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Capture your test packet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; -ne &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$ISAKMP_PACKET&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; nc -u 127.0.0.1 &lt;span class="m"&gt;500&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo tcpdump -i lo -w test.pcap udp port &lt;span class="m"&gt;500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Open in Wireshark and verify:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ISAKMP protocol is correctly decoded (not &amp;ldquo;Malformed Packet&amp;rdquo;)&lt;/li&gt;
&lt;li&gt;All payload lengths match actual payload sizes&lt;/li&gt;
&lt;li&gt;Transform attributes are properly formatted&lt;/li&gt;
&lt;li&gt;No &amp;ldquo;Expert Info&amp;rdquo; warnings about malformed fields&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="alternative-tools"&gt;Alternative Tools&lt;/h2&gt;
&lt;p&gt;While this manual method teaches protocol internals, consider these tools for production use:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ike-scan:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ike-scan -M TARGET_IP
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Supports IKEv1 and IKEv2&lt;/li&gt;
&lt;li&gt;Tests multiple transform sets automatically&lt;/li&gt;
&lt;li&gt;Better for production troubleshooting&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;nmap:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nmap -sU -p &lt;span class="m"&gt;500&lt;/span&gt; --script ike-version TARGET_IP
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Detects IKE version support&lt;/li&gt;
&lt;li&gt;Identifies vendor implementations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;When to use manual method:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Learning protocol internals&lt;/li&gt;
&lt;li&gt;Highly restricted environments (no tools available)&lt;/li&gt;
&lt;li&gt;Testing specific edge cases&lt;/li&gt;
&lt;li&gt;Forensic analysis of VPN behavior&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;By using a pre-crafted complete ISAKMP datagram, we&amp;rsquo;ve eliminated the dependency on having debug access to the peer device. This approach validates outbound connectivity (UDP/500 reachability), peer responsiveness, and return path functionality - all without needing access to the remote device.&lt;/p&gt;
&lt;p&gt;For a detailed breakdown of how this packet was constructed, see &lt;a href="https://network-notes.com/posts/2026/netcat-isakmp-3/"&gt;Part 3&lt;/a&gt;. For automated testing with Python and Scapy, check out &lt;a href="https://network-notes.com/posts/2026/netcat-isakmp-4/"&gt;Part 4&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Next Steps:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Want to understand how this packet was built? Read &lt;a href="https://network-notes.com/posts/2026/netcat-isakmp-3/"&gt;Part 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Explore automating this with Python and the Scapy library&lt;/li&gt;
&lt;li&gt;Check out &lt;code&gt;ike-scan&lt;/code&gt; for more advanced VPN testing scenarios&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Parsing Network Device Output Part 3: CiscoConfParse</title><link>https://network-notes.com/posts/2026/parsing-netdevice-output-3/</link><pubDate>Mon, 19 Jan 2026 10:55:00 -0500</pubDate><author>brett@network-notes.com (Brett Lykins)</author><dc:creator>Brett Lykins</dc:creator><guid>https://network-notes.com/posts/2026/parsing-netdevice-output-3/</guid><description>&lt;p&gt;In the first post in this series, I talked briefly about &lt;a href="https://network-notes.com/posts/2020/parsing-netdevice-output-1/"&gt;utilizing Regular Expressions (Regex)&lt;/a&gt; to parse network device output. Next, I delved into &lt;a href="https://network-notes.com/posts/2020/parsing-netdevice-output-2/"&gt;the difference between status and configuration&lt;/a&gt; for network device output, and why you might use one tool vs another depending on which type of output you are working with.&lt;/p&gt;
&lt;p&gt;As a brief refresher, network device output (from a traditional CLI session) can generally come in two forms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Status
&lt;ul&gt;
&lt;li&gt;An output that presents some attribute or information about the current state of the device or its performance.&lt;/li&gt;
&lt;li&gt;Example commands for gathering status include:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;show version&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;show interfaces&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Example commands for modifying status include:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;clear counters&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;reload&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Configuration
&lt;ul&gt;
&lt;li&gt;An output that presents the desired/requested operating condition of the device.&lt;/li&gt;
&lt;li&gt;Example commands for gathering configuration include:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;show running-config&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;show startup-config&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this post, we&amp;rsquo;ll look at one of the open-source parsing libraries that exist for network device configuration: &lt;a href="http://www.pennington.net/py/ciscoconfparse/"&gt;CiscoConfParse&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="why-not-just-use-regex"&gt;Why Not Just Use Regex?&lt;/h2&gt;
&lt;p&gt;CiscoConfParse, as well as &lt;a href="https://github.com/netdevops/hier_config"&gt;Hierarchical Configuration (heir_config)&lt;/a&gt; which we&amp;rsquo;ll cover in the next post, provide much more power than using Regex alone to match and extract a pattern from a line of status or configuration. These parsing libraries provide a framework to understand and manipulate &amp;ldquo;Cisco style&amp;rdquo; configuration (with indented blocks representing related items), or even &amp;ldquo;Juniper style&amp;rdquo; curly brace (&lt;code&gt;{}&lt;/code&gt;) delineated configuration.&lt;/p&gt;
&lt;p&gt;If you think about the previous Regex example, how would we utilize Regex to parse the following configuration output, specifically looking to answer this question:&lt;/p&gt;
&lt;p&gt;What interfaces have &lt;code&gt;security-level 0&lt;/code&gt; configured on them?&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-cisco" data-lang="cisco"&gt;!
interface GigabitEthernet0/0
description OUTSIDE-VLAN300-10.10.10.0/24
nameif OUTSIDE
security-level 0
ip address 10.10.10.50 255.255.255.0
!
interface GigabitEthernet0/1
no nameif
no security-level
no ip address
!
interface GigabitEthernet0/1.400
description INSIDE-VLAN400-10.10.40.0/24
vlan 400
nameif INSIDE
security-level 100
ip address 10.10.40.1 255.255.255.0
!
interface Management0/0
management-only
nameif MGMT
security-level 0
ip address 10.10.60.2 255.255.255.0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For example, you can certainly search for the string &amp;ldquo;&lt;code&gt;security-level 0&lt;/code&gt;&amp;rdquo;, however how do you then tie it back to the interface above it? Remember that when we&amp;rsquo;re evaluating a string with Regex, we can work on a multi-line string.
We would have to set up capturing groups for the &lt;code&gt;interface&lt;/code&gt; and &lt;code&gt;nameif&lt;/code&gt;, and then only match if there was also our target string.
And while this is doable, it quickly becomes cumbersome to do for every item you wish to match.
Also consider, what if you want to output the entire interface configuration for any interface that matches &amp;ldquo;&lt;code&gt;security-level 0&lt;/code&gt;&amp;rdquo;?
There is a better and simpler way to handle this.&lt;/p&gt;
&lt;h2 id="enter-ciscoconfparse"&gt;Enter CiscoConfParse&lt;/h2&gt;
&lt;p&gt;CiscoConfParse solves this problem, and more, by breaking the configuration into parent/child chunks and allowing you to search and find based on the contents of either.
For an in-depth walk-through and examples I highly recommend the &lt;a href="http://www.pennington.net/py/ciscoconfparse/installation.html#using-ciscoconfparse"&gt;CiscoConfParse documentation&lt;/a&gt;; below we&amp;rsquo;ll demonstrate a quick use case.&lt;/p&gt;
&lt;p&gt;For our test environment, we&amp;rsquo;re utilizing the same firewall pair and management station &lt;a href="https://network-notes.com/posts/2020/parsing-netdevice-output-1/"&gt;from the previous post&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://network-notes.com/img/2020/nn_example_parse_net_device_config.5054cb1ec14a913104423c2c3700f858a8d77108a3b3c4ae90c59011102795b2.svg" alt="Example network: management station with Python connecting to two Cisco ASAv firewalls" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;In this series of posts, we&amp;rsquo;ll just use the network shown here. There is a Management Station with Python 3.8 installed, and two Cisco ASAv firewalls (ASAv1 and ASAv2) in a tiered setup.&lt;/p&gt;
&lt;p&gt;Code samples and &lt;code&gt;requirements.txt&lt;/code&gt; for this post can be found &lt;a href="https://github.com/lykinsbd/nn_examples/tree/master/parsing_net_devices"&gt;on my Github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Below is an example script, based on the previous examples in this series, where we log into these ASAs, gather the output of &lt;code&gt;show run interface&lt;/code&gt;, and print out the interface(s) with &lt;code&gt;security-level 0&lt;/code&gt; configured.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;span class="lnt"&gt;45
&lt;/span&gt;&lt;span class="lnt"&gt;46
&lt;/span&gt;&lt;span class="lnt"&gt;47
&lt;/span&gt;&lt;span class="lnt"&gt;48
&lt;/span&gt;&lt;span class="lnt"&gt;49
&lt;/span&gt;&lt;span class="lnt"&gt;50
&lt;/span&gt;&lt;span class="lnt"&gt;51
&lt;/span&gt;&lt;span class="lnt"&gt;52
&lt;/span&gt;&lt;span class="lnt"&gt;53
&lt;/span&gt;&lt;span class="lnt"&gt;54
&lt;/span&gt;&lt;span class="lnt"&gt;55
&lt;/span&gt;&lt;span class="lnt"&gt;56
&lt;/span&gt;&lt;span class="lnt"&gt;57
&lt;/span&gt;&lt;span class="lnt"&gt;58
&lt;/span&gt;&lt;span class="lnt"&gt;59
&lt;/span&gt;&lt;span class="lnt"&gt;60
&lt;/span&gt;&lt;span class="lnt"&gt;61
&lt;/span&gt;&lt;span class="lnt"&gt;62
&lt;/span&gt;&lt;span class="lnt"&gt;63
&lt;/span&gt;&lt;span class="lnt"&gt;64
&lt;/span&gt;&lt;span class="lnt"&gt;65
&lt;/span&gt;&lt;span class="lnt"&gt;66
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="ch"&gt;#!/usr/bin/env python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;Example code for Network-Notes.com entry on Parsing Network Device Output
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;getpass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;ciscoconfparse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CiscoConfParse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;netmiko&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ConnectHandler&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;Test parsing network device configuration
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Gather the needed credentials&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;net_device_username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Username: &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;net_device_password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getpass&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getpass&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Set enable/secret = password for now&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;net_device_secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;net_device_password&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Setup a dict with our ASAvs in it, in real world this could be read&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# from a CSV or the CLI or any other source&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;firewalls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;ASAv1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ip&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;10.10.10.50&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;platform&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;cisco_asa&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;ASAv2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ip&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;10.10.60.2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;platform&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;cisco_asa&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Setup an empty dict for our results:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Instantiate netmiko connection objects and gather the output of&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# `show run interface` on these two firewalls&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;fw_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fw_data&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;firewalls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Connecting to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fw_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;fw_connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConnectHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fw_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ip&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;device_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fw_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;platform&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;net_device_username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;net_device_password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;net_device_secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;fw_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fw_connection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;command_string&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;show run interface&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Parse our results:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Parsing Results...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;outside_interfaces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;fw_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;interface_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CiscoConfParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;splitlines&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;outside_interfaces&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;fw_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;interface_config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_objects_w_child&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;parentspec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;^interface&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;childspec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;security-level 0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Print our results&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;fw_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;interface_list&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;outside_interfaces&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;==== ==== [ &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fw_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&amp;#34;&lt;/span&gt;&lt;span class="s2"&gt;Outside&lt;/span&gt;&lt;span class="se"&gt;\&amp;#34;&lt;/span&gt;&lt;span class="s2"&gt; interfaces ] ==== ====&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;interface&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;interface_list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34; ==== [ &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;interface&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ] ==== &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;interface&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ioscfg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;It will produce the following output when executed:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-cisco" data-lang="cisco"&gt;Username: cisco
Password:
Connecting to ASAv1â€¦
Connecting to ASAv2â€¦
Parsing Resultsâ€¦
==== ==== [ ASAv1 &amp;#34;Outside&amp;#34; interfaces ] ==== ====
==== [ interface GigabitEthernet0/0 ] ====
interface GigabitEthernet0/0
description OUTSIDE-VLAN300-10.10.10.0/24
nameif OUTSIDE
security-level 0
ip address 10.10.10.50 255.255.255.0
ospf authentication-key
ospf message-digest-key 1 md5
ospf authentication message-digest
==== ==== [ ASAv2 &amp;#34;Outside&amp;#34; interfaces ] ==== ====
==== [ interface Management0/0 ] ====
interface Management0/0
management-only
nameif MGMT
security-level 0
ip address 10.10.60.2 255.255.255.0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As you can see, we found the interfaces we needed, and have the entire configuration. Now let&amp;rsquo;s break apart what happened a little bit more.&lt;/p&gt;
&lt;h2 id="parentchild-relationships"&gt;Parent/Child Relationships&lt;/h2&gt;
&lt;p&gt;CiscoConfParse understands the hierarchical nature of Cisco-style configuration. In our example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Parent&lt;/strong&gt;: &lt;code&gt;interface GigabitEthernet0/0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Children&lt;/strong&gt;: All indented lines under that interface (description, nameif, security-level, etc.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The key method in our script is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;interface_config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_objects_w_child&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;parentspec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;^interface&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;childspec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;security-level 0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This searches for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;parentspec&lt;/strong&gt;: Lines matching regex &lt;code&gt;^interface&lt;/code&gt; (lines starting with &amp;ldquo;interface&amp;rdquo;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;childspec&lt;/strong&gt;: That have a child line containing &amp;ldquo;security-level 0&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result is a list of &lt;code&gt;IOSCfgLine&lt;/code&gt; objects representing matching parent interfaces.&lt;/p&gt;
&lt;h2 id="key-ciscoconfparse-methods"&gt;Key CiscoConfParse Methods&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;find_objects()&lt;/code&gt; - Find lines matching a regex pattern&lt;/li&gt;
&lt;li&gt;&lt;code&gt;find_objects_w_child()&lt;/code&gt; - Find parents with specific children&lt;/li&gt;
&lt;li&gt;&lt;code&gt;find_objects_wo_child()&lt;/code&gt; - Find parents without specific children&lt;/li&gt;
&lt;li&gt;&lt;code&gt;find_children()&lt;/code&gt; - Find all children of a parent&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each object provides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.text&lt;/code&gt; - The configuration line text&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.ioscfg&lt;/code&gt; - Complete configuration block (parent + children)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.children&lt;/code&gt; - List of child objects&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="why-this-matters"&gt;Why This Matters&lt;/h2&gt;
&lt;p&gt;Compare this to the regex approach from Part 1. To find interfaces with &lt;code&gt;security-level 0&lt;/code&gt; using regex, you&amp;rsquo;d need complex multi-line patterns and capturing groups. CiscoConfParse makes it intuitive by understanding configuration structure.&lt;/p&gt;
&lt;p&gt;This works well for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Security audits (find interfaces with specific settings)&lt;/li&gt;
&lt;li&gt;Configuration validation (ensure required settings exist)&lt;/li&gt;
&lt;li&gt;Bulk changes (identify what needs updating)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="whats-next"&gt;What&amp;rsquo;s Next&lt;/h2&gt;
&lt;p&gt;In the next post, we&amp;rsquo;ll explore Hierarchical Configuration (hier_config), which builds on these concepts to provide configuration comparison and remediation capabilities.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;CiscoConfParse transforms configuration parsing from complex regex gymnastics into readable, maintainable code. By understanding parent/child relationships in network configurations, it makes complex parsing tasks manageable.&lt;/p&gt;</description></item><item><title>Migrating from WordPress to Hugo on Cloudflare Pages</title><link>https://network-notes.com/posts/2023/migrating-to-hugo/</link><pubDate>Tue, 11 Jul 2023 13:42:23 -0400</pubDate><author>brett@network-notes.com (Brett Lykins)</author><dc:creator>Brett Lykins</dc:creator><guid>https://network-notes.com/posts/2023/migrating-to-hugo/</guid><description>&lt;p&gt;After years of running WordPress on a self-hosted DigitalOcean droplet, I decided it was time for a change. The site was slow, required constant maintenance, and felt like overkill for a personal blog. Here&amp;rsquo;s how I migrated to Hugo and Cloudflare Pages.&lt;/p&gt;
&lt;h2 id="the-old-setup"&gt;The Old Setup&lt;/h2&gt;
&lt;p&gt;My previous infrastructure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hosting&lt;/strong&gt;: DigitalOcean droplet ($12/month)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CMS&lt;/strong&gt;: Self-hosted WordPress with MySQL&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CDN&lt;/strong&gt;: Cloudflare (free tier) in front&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maintenance&lt;/strong&gt;: Regular updates, security patches, backups&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt;: Decent, but database queries added latency&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The droplet ran Ubuntu with Apache, PHP, MySQL, and all the typical WordPress stack. It worked, but it felt heavy for what was essentially a collection of markdown-style posts.&lt;/p&gt;
&lt;h2 id="why-hugo"&gt;Why Hugo?&lt;/h2&gt;
&lt;p&gt;I wanted something:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fast&lt;/strong&gt;: Static sites are blazing fast&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Minimal&lt;/strong&gt;: No database, no PHP, just HTML/CSS/JS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Portable&lt;/strong&gt;: Content in markdown, straightforward to version control&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Free&lt;/strong&gt;: Cloudflare Pages offers free hosting&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secure&lt;/strong&gt;: No CMS to hack, no database to compromise&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hugo checked all these boxes. It&amp;rsquo;s a static site generator written in Go that&amp;rsquo;s incredibly fast and has a great ecosystem of themes.&lt;/p&gt;
&lt;h2 id="the-migration-process"&gt;The Migration Process&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://network-notes.com/img/2023/hugo-migration-architecture.67db947821b32de1e55f9e752fa4508f962fbdcbb8f65efdbe39d8da5023a4bb.svg" alt="Before and after architecture: WordPress on DigitalOcean VPS vs Hugo on Cloudflare Pages with CI/CD pipeline" loading="lazy" /&gt;&lt;/p&gt;
&lt;h3 id="1-export-wordpress-content"&gt;1. Export WordPress Content&lt;/h3&gt;
&lt;p&gt;I used the built-in WordPress export tool to generate an XML file with all posts, then converted them to markdown format. Several tools exist for this conversion, including wordpress-to-hugo-exporter and various online converters. Manual cleanup was necessary afterward to fix formatting inconsistencies.&lt;/p&gt;
&lt;h3 id="2-choose-a-theme"&gt;2. Choose a Theme&lt;/h3&gt;
&lt;p&gt;I selected the &lt;a href="https://themes.gohugo.io/themes/hugo-theme-terminal/"&gt;Terminal theme&lt;/a&gt; for its clean, minimal design. The retro terminal aesthetic fits well with technical content and provides excellent readability.&lt;/p&gt;
&lt;h3 id="3-set-up-hugo-locally"&gt;3. Set Up Hugo Locally&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Install Hugo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;brew install hugo
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Create new site&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hugo new site network-notes.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; network-notes.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Add theme&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git submodule add https://github.com/panr/hugo-theme-terminal.git themes/terminal
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Configure&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cp themes/terminal/exampleSite/config.toml .
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Edit config.toml with my settings&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="4-migrate-content"&gt;4. Migrate Content&lt;/h3&gt;
&lt;p&gt;I moved all the converted markdown files into &lt;code&gt;content/posts/&lt;/code&gt; organized by year:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;content/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└── posts/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├── 2015/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├── 2016/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └── 2020/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Each post needed some cleanup - fixing image paths, updating internal links, and adjusting frontmatter.&lt;/p&gt;
&lt;h3 id="5-set-up-cloudflare-pages"&gt;5. Set Up Cloudflare Pages&lt;/h3&gt;
&lt;p&gt;Cloudflare Pages is straightforward:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Push Hugo site to GitHub&lt;/li&gt;
&lt;li&gt;Connect GitHub repo to Cloudflare Pages&lt;/li&gt;
&lt;li&gt;Configure build settings:
&lt;ul&gt;
&lt;li&gt;Build command: &lt;code&gt;hugo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Build output directory: &lt;code&gt;public&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Environment variable: &lt;code&gt;HUGO_VERSION=0.120.0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That&amp;rsquo;s it. Every push to &lt;code&gt;main&lt;/code&gt; triggers a new build and deployment.&lt;/p&gt;
&lt;h3 id="6-configure-dns"&gt;6. Configure DNS&lt;/h3&gt;
&lt;p&gt;Updated my Cloudflare DNS to point to Pages:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;network-notes.com → CNAME → network-notes-com.pages.dev (proxied)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;I also set up a preview branch:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;blog2.network-notes.com → CNAME → preview.network-notes-com.pages.dev (proxied)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This lets me review posts before publishing to production.&lt;/p&gt;
&lt;h3 id="7-decommission-digitalocean"&gt;7. Decommission DigitalOcean&lt;/h3&gt;
&lt;p&gt;Once everything was working, I:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Backed up the WordPress database (just in case)&lt;/li&gt;
&lt;li&gt;Took a final snapshot of the droplet&lt;/li&gt;
&lt;li&gt;Destroyed the droplet&lt;/li&gt;
&lt;li&gt;Saved $12/month&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-results"&gt;The Results&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt;: Page load times dropped from ~800ms to ~200ms. Static HTML is fast.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Maintenance&lt;/strong&gt;: Zero. No more WordPress updates, no more security patches, no more database backups.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cost&lt;/strong&gt;: $0/month for hosting (Cloudflare Pages free tier is generous)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Workflow&lt;/strong&gt;: Write in markdown, commit to git, automatic deployment. Much better than the WordPress editor.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Reliability&lt;/strong&gt;: Cloudflare&amp;rsquo;s global CDN means the site is fast everywhere and has excellent uptime.&lt;/p&gt;
&lt;h2 id="infrastructure-as-code"&gt;Infrastructure as Code&lt;/h2&gt;
&lt;p&gt;I manage all my DNS and Cloudflare configuration with Terraform. The network-notes.com configuration includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DNS records&lt;/li&gt;
&lt;li&gt;Page rules for caching&lt;/li&gt;
&lt;li&gt;SSL/TLS settings&lt;/li&gt;
&lt;li&gt;Preview branch setup&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Everything is version controlled and reproducible, making it straightforward to recreate or modify the infrastructure.&lt;/p&gt;
&lt;h2 id="lessons-learned"&gt;Lessons Learned&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Markdown is powerful&lt;/strong&gt;: Once you get used to writing in markdown, it&amp;rsquo;s hard to go back to WYSIWYG editors.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Static sites aren&amp;rsquo;t limiting&lt;/strong&gt;: For a blog, you don&amp;rsquo;t need dynamic content. Comments can be handled by external services if needed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Git-based workflows are great&lt;/strong&gt;: Having your entire site in version control is liberating. Rollbacks are trivial, history is preserved, and collaboration is straightforward.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cloudflare Pages is excellent&lt;/strong&gt;: The free tier is generous, builds are fast, and the preview deployments are incredibly useful.&lt;/p&gt;
&lt;h2 id="would-i-recommend-it"&gt;Would I Recommend It?&lt;/h2&gt;
&lt;p&gt;Absolutely, if you&amp;rsquo;re running a personal blog or documentation site. The migration took a weekend, but the ongoing benefits are worth it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No more server maintenance&lt;/li&gt;
&lt;li&gt;Faster site&lt;/li&gt;
&lt;li&gt;Better workflow&lt;/li&gt;
&lt;li&gt;Lower costs&lt;/li&gt;
&lt;li&gt;More secure&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you need dynamic features (user accounts, complex forms, e-commerce), WordPress or another CMS might still make sense. But for content-focused sites, static site generators like Hugo are hard to beat.&lt;/p&gt;
&lt;h2 id="resources"&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gohugo.io/documentation/"&gt;Hugo Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.cloudflare.com/pages/"&gt;Cloudflare Pages Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://themes.gohugo.io/themes/hugo-theme-terminal/"&gt;Terminal Theme&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The blog is now faster, cheaper, and easier to maintain. Sometimes simpler really is better.&lt;/p&gt;</description></item><item><title>Parsing Network Device Output Part 2: Status or Configuration?</title><link>https://network-notes.com/posts/2020/parsing-netdevice-output-2/</link><pubDate>Sat, 21 Mar 2020 20:01:00 -0500</pubDate><author>brett@network-notes.com (Brett Lykins)</author><dc:creator>Brett Lykins</dc:creator><guid>https://network-notes.com/posts/2020/parsing-netdevice-output-2/</guid><description>&lt;h2 id="a-brief-interlude"&gt;A Brief Interlude&lt;/h2&gt;
&lt;p&gt;In my first post in this series, I dove into &lt;a href="https://network-notes.com/posts/2020/parsing-netdevice-output-1/"&gt;utilizing Regular Expressions (Regex) to parse network device output&lt;/a&gt;. Before I continue with some of the other parsing options, I thought it would be worthwhile to post a short blog laying out some definitions that I’ll be relying on. Specifically, I&amp;rsquo;ll be using them for delineation among the different parsing options and their use cases.&lt;/p&gt;
&lt;p&gt;At the heart of this problem, is that when interacting with traditional network devices there aren’t many programmatic options, such as a REST API, to return structured data. Sure, you could use SNMP for &lt;em&gt;some things&lt;/em&gt;, but not for everything. In addition, working with SNMP can be its type of nightmare, believe me, I’ve fought that fight many times in the past. This is changing as the industry matures, Cisco even has an entire certification track around utilizing their APIs now, however even today most Network Engineers&amp;rsquo; first foray into automation uses SSH to query a device for information. This brings us to output.&lt;/p&gt;
&lt;h2 id="what-is-output"&gt;What is Output?&lt;/h2&gt;
&lt;p&gt;For our purposes, &amp;ldquo;Output&amp;rdquo; will be defined as any response that a network device returns when given a command via its Command Line Interface (CLI). This could be accomplished by many means; depending on the network device in question the CLI could be reached via Telnet, SSH, or even HTTPS. No matter the access method, the network device is still returning the same data, our output.&lt;/p&gt;
&lt;p&gt;Execute &lt;code&gt;show version&lt;/code&gt; on a Cisco-like device, and it will return some form of the status of the version as that device understands it. It will be structured, loosely, for human consumption with perhaps headings/titles or indentation/punctuation for legibility. It will &lt;strong&gt;not&lt;/strong&gt; generally be structured in a way that would easily allow a computer program to understand and interact with it.&lt;/p&gt;
&lt;p&gt;Just as you can issue &lt;code&gt;show version&lt;/code&gt; and get output, you can similarly on most network devices execute a command akin to &lt;code&gt;show running-config&lt;/code&gt; and get output. This would return some form of data about the configuration that was currently in use on that network device. Again, it will have some form of structure, usually either delineated by indentation or curly braces (&lt;code&gt;{}&lt;/code&gt;), but mostly it will be in a form that is not easily understood or manipulated by a computer program that you are writing.&lt;/p&gt;
&lt;p&gt;I make the specific call out that it may not easily be understood by a program that &lt;em&gt;you are writing&lt;/em&gt; because of course the network device itself can take that configuration and turn it into actionable information. However, that does us no good, as most vendors aren’t open sourcing their internal operating systems&amp;rsquo; parsing logic.&lt;/p&gt;
&lt;h2 id="status-vs-configuration"&gt;Status vs. Configuration&lt;/h2&gt;
&lt;p&gt;In the two examples above, we received output from a network device after we issued a command via the CLI. One gave us some sort of status about the device, and the other gave us information about its configuration. Broadly speaking, all CLI commands on a network device fall into these two categories. You are either gathering or changing the status of the device with commands such as &lt;code&gt;show version&lt;/code&gt;, &lt;code&gt;show flash&lt;/code&gt;, or &lt;code&gt;clear counters&lt;/code&gt;. Or you are gathering or changing the configuration of the device with commands such as &lt;code&gt;show running-config&lt;/code&gt;, or &lt;code&gt;config t;interface e1/0;no shutdown&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It is vital to keep in mind which type of information you’re looking for or what changes you are trying to make, and whether it involves the device’s status or configuration. This will heavily influence how you parse/interact with the output that the device has given you. Some parsing methods or libraries are made specifically for turning configuration into something easy to manipulate/change in Python (CiscoConfParse, Heir Config), and some are more generic and can parse both status and configuration (Regex, TextFSM, Genie).&lt;/p&gt;
&lt;p&gt;In the next post, I’ll dive into &lt;strong&gt;CiscoConfParse&lt;/strong&gt; first, and we’ll see how it can make the act of understanding and manipulating traditional unstructured network device configuration much simpler.&lt;/p&gt;</description></item><item><title>Parsing Network Device Output Part 1: Regex</title><link>https://network-notes.com/posts/2020/parsing-netdevice-output-1/</link><pubDate>Wed, 04 Mar 2020 20:17:00 -0500</pubDate><author>brett@network-notes.com (Brett Lykins)</author><dc:creator>Brett Lykins</dc:creator><guid>https://network-notes.com/posts/2020/parsing-netdevice-output-1/</guid><description>&lt;h2 id="in-the-beginning"&gt;In the Beginning&lt;/h2&gt;
&lt;p&gt;Often when making their first steps into Network Automation, people may have an idea of what they want to do, but not exactly how to get there. For example, an engineer may want to simplify a single time-intensive task “Gather the SW version, serial numbers, and uptime from all of my Cisco ASAs.” However, upon getting the information, they’re unsure what to do with it or how to parse it and use it for something else. I’ve seen this question come up a few different times, and thought a series of blog posts was in order. Each of the posts (including this one) outline some sample parsing options. However, it is worth noting that Regex is not better or worse than using a parsing library, for example, they may just have different use cases.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First, in this post, I will outline the basics of gathering our information and parsing it directly with our own Regular Expressions (Regex).&lt;/li&gt;
&lt;li&gt;Next, I will demonstrate parsing this information with the popular &lt;a href="https://github.com/mpenning/ciscoconfparse"&gt;CiscoConfParse&lt;/a&gt; and &lt;a href="https://github.com/netdevops/hier_config"&gt;HeirConfig&lt;/a&gt; libraries.&lt;/li&gt;
&lt;li&gt;Third, I will demonstrate simple parsing with Google’s &lt;a href="https://github.com/google/textfsm"&gt;TextFSM&lt;/a&gt; and the awesome library of templates provided by &lt;a href="https://www.networktocode.com/"&gt;Network To Code&lt;/a&gt;, &lt;a href="https://github.com/networktocode/ntc-templates"&gt;ntc-templates&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Finally, I will demonstrate simple parsing with Cisco’s &lt;a href="https://developer.cisco.com/docs/pyats/"&gt;Genie/PyATS&lt;/a&gt; libraries.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that I am going to skip over a lot of the minutia of &lt;a href="https://pynet.twb-tech.com/blog/automation/netmiko.html"&gt;what Netmiko is&lt;/a&gt;, and &lt;a href="https://pynet.twb-tech.com/blog/automation/netmiko.html"&gt;how to use it&lt;/a&gt; to gather network device information, as there are &lt;a href="https://codingpackets.com/blog/netmiko-getting-started"&gt;many&lt;/a&gt; &lt;a href="https://www.charleslabri.com/python-lesson-5-netmiko/"&gt;wonderful&lt;/a&gt; &lt;a href="https://medium.com/@jrecchia0/intro-to-network-automation-with-python-and-netmiko-a80f08a0c2c2"&gt;resources&lt;/a&gt; out there already on this. We’re going to focus primarily on what to do with the output once you have it.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://network-notes.com/img/2020/nn_example_parse_net_device_config.5054cb1ec14a913104423c2c3700f858a8d77108a3b3c4ae90c59011102795b2.svg" alt="Example network: management station with Python connecting to two Cisco ASAv firewalls" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;In this series of posts, we’ll just use the simple network shown here. There is a Management Station with Python 3.8 installed, and two Cisco ASAv firewalls (ASAv1 and ASAv2) in a tiered setup.&lt;/p&gt;
&lt;p&gt;Code samples and “requirements.txt” for this post can be found &lt;a href="https://github.com/lykinsbd/nn_examples/tree/master/parsing_net_devices"&gt;on my Github&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="environment-preparation"&gt;Environment Preparation&lt;/h2&gt;
&lt;p&gt;For this entire series of posts, I will be working the examples in a python virtual environment setup with the following dependencies installed. Note that we’re installing iPython, which is a much better alternative to the built in Python interactive shell. I highly recommend &lt;a href="https://ipython.readthedocs.io/en/stable/"&gt;checking it out&lt;/a&gt; by executing &lt;code&gt;ipython&lt;/code&gt; in this virtual environment. iPython allows us to do some more interactive introspection on objects and method/object doc_strings in real time, which we’ll use in later posts.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;iPython&lt;/li&gt;
&lt;li&gt;Netmiko&lt;/li&gt;
&lt;li&gt;CiscoConfParse&lt;/li&gt;
&lt;li&gt;HeirConfig&lt;/li&gt;
&lt;li&gt;NTC Templates&lt;/li&gt;
&lt;li&gt;Genie/PyATS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;First, I create a virtual environment with whatever name you wish, and then activate that environment:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3.8 -m venv nn_examples
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; nn_examples&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; bin/activate
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Next, I install the dependencies as discussed above:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pip install ciscoconfparse genie hier-config ipython netmiko ntc_templates pyats
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="gathering-our-output"&gt;Gathering our Output&lt;/h2&gt;
&lt;p&gt;We need to instantiate a connection to our ASAs, and then execute the command “show version | inc So|Serial| up” against them to gather the needed output. Simple enough with Netmiko as shown below (&lt;a href="https://github.com/lykinsbd/nn_examples/blob/master/parsing_net_devices/raw_gather.py"&gt;or on Github&lt;/a&gt;):&lt;/p&gt;
&lt;details class="collapsable-code" &gt;
&lt;summary title="Click to interact"&gt;&lt;span class="collapsable-code__title"&gt;raw_gather.py&lt;/span&gt;&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;span class="lnt"&gt;45
&lt;/span&gt;&lt;span class="lnt"&gt;46
&lt;/span&gt;&lt;span class="lnt"&gt;47
&lt;/span&gt;&lt;span class="lnt"&gt;48
&lt;/span&gt;&lt;span class="lnt"&gt;49
&lt;/span&gt;&lt;span class="lnt"&gt;50
&lt;/span&gt;&lt;span class="lnt"&gt;51
&lt;/span&gt;&lt;span class="lnt"&gt;52
&lt;/span&gt;&lt;span class="lnt"&gt;53
&lt;/span&gt;&lt;span class="lnt"&gt;54
&lt;/span&gt;&lt;span class="lnt"&gt;55
&lt;/span&gt;&lt;span class="lnt"&gt;56
&lt;/span&gt;&lt;span class="lnt"&gt;57
&lt;/span&gt;&lt;span class="lnt"&gt;58
&lt;/span&gt;&lt;span class="lnt"&gt;59
&lt;/span&gt;&lt;span class="lnt"&gt;60
&lt;/span&gt;&lt;span class="lnt"&gt;61
&lt;/span&gt;&lt;span class="lnt"&gt;62
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# !/usr/bin/env python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;Example code for Network-Notes.com entry on Parsing Network Device Output
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;getpass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;re&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;netmiko&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ConnectHandler&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;Test parsing network device configuration
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Gather the needed credentials&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;net_device_username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Username: &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;net_device_password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getpass&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getpass&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Set enable/secret = password for now&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;net_device_secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;net_device_password&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Setup a dict with our ASAvs in it, in real world this could be read&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# from a CSV or the CLI or any other source&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;firewalls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;ASAv1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ip&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;10.10.10.50&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;platform&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;cisco_asa&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;ASAv2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ip&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;10.10.60.2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;platform&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;cisco_asa&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Setup an empty dict for our results&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Instantiate netmiko connection objects and gather the output of&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# `show version | inc So|Serial| up` on these two firewalls&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;fw_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fw_data&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;firewalls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Connecting to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fw_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;fw_connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConnectHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fw_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ip&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;device_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fw_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;platform&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;net_device_username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;net_device_password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;net_device_secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;fw_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fw_connection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;command_string&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;show version | inc So|Serial| up&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Print our results&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;fw_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fw_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; information:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/details&gt;
&lt;p&gt;Successfully executed with “python3.8 raw_gather.py”, this will output:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Username: cisco
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Password:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Connecting to ASAv1â€¦
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Connecting to ASAv2â€¦
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ASAv1 information:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Cisco Adaptive Security Appliance Software Version 9.12(1)2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ASAv1 up 23 mins 50 secs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Serial Number: 123456789AB
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ASAv2 information:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Cisco Adaptive Security Appliance Software Version 9.13(1)2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ASAv2 up 24 mins 5 secs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Serial Number: 123456789AC
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Now that we have an example of gathering the information, let’s parse it to python variables via Regex.&lt;/p&gt;
&lt;h2 id="parsing-with-regex"&gt;Parsing with Regex&lt;/h2&gt;
&lt;p&gt;Much like above, I will not exhaustively cover what Regex (Regular Expressions) are, as there are boundless resources on the internet (&lt;a href="https://www.amazon.com/Mastering-Regular-Expressions-Jeffrey-Friedl/dp/0596528124"&gt;and good old O’Reilly books&lt;/a&gt;) that do this. Suffice to say for our purposes, that it is a way to match patterns in a string of text and gather output based on that, and that I strongly recommend finding a good test bed like &lt;a href="https://regex101.com/"&gt;Regex101&lt;/a&gt; to help you along the way. You can get extremely complicated with Regex, and it can also bite you in the behind, &lt;a href="https://blog.cloudflare.com/details-of-the-cloudflare-outage-on-july-2-2019/"&gt;just ask Cloudflare&lt;/a&gt;… Here is the three Regular Expressions I am using to match relevant output from the ASA with links to Regex101 examples demonstrating them.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://regex101.com/r/UhumV2/4"&gt;Software Version&lt;/a&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Software Version (\d.\d{1,2}(?:(?\d{1,2}?)?\d{1,2}?)?)\W*$&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://regex101.com/r/aIwznp/2/"&gt;Uptime&lt;/a&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;up (.*)$&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://regex101.com/r/8hVK8j/2"&gt;Serial Number&lt;/a&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Serial Number: (\S*)$&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now here is what the program looks like utilizing the python re Regex module. The only difference is the addition of a section for parsing the output we’ve received, so that is all I’ll list below. The complete file is on Github as &lt;a href="https://github.com/lykinsbd/nn_examples/blob/master/parsing_net_devices/parse_with_regex.py"&gt;parse_with_regex.py&lt;/a&gt;:&lt;/p&gt;
&lt;details class="collapsable-code" &gt;
&lt;summary title="Click to interact"&gt;&lt;span class="collapsable-code__title"&gt;raw_gather.py&lt;/span&gt;&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Parse our results&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Parsing Results...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;parsed_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;fw_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;parsed_results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;fw_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;parsed_results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;fw_name&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;version&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Software Version (\d.\d{1,2}(?:\(?\d{1,2}?\)?\d{1,2}?)?)\W*$&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MULTILINE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;parsed_results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;fw_name&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;uptime&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;up (.*)$&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MULTILINE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;parsed_results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;fw_name&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;serial&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Serial Number: (\S*)$&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MULTILINE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Print our results&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;fw_name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fw_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; information:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;Version: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;parsed_results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;fw_name&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;version&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;Uptime: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;parsed_results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;fw_name&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;uptime&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;Serial Number: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;parsed_results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;fw_name&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;serial&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/details&gt;
&lt;p&gt;This will give us the results of:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Username: cisco
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Password:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Connecting to ASAv1â€¦
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Connecting to ASAv2â€¦
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Parsing Resultsâ€¦
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ASAv1 information:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Version: 9.12(1)2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Uptime: 1 hour 25 mins
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Serial Number: 123456789AB
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ASAv2 information:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Version: 9.13(1)2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Uptime: 1 hour 25 mins
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Serial Number: 123456789AC
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;As you can see, this is a much more structured and useful output. We could also similarly output this to a CSV or other data source, now that we have parsed the data into something of more value than a raw string of all the data.&lt;/p&gt;
&lt;h2 id="next-up-parsing-libraries"&gt;Next Up, Parsing Libraries&lt;/h2&gt;
&lt;p&gt;In the next post I publish, I will utilize this same environment, but make use of some common configuration parsing libraries (CiscoConfParse and HeirConfig) to show how they could be useful for certain use cases.&lt;/p&gt;</description></item><item><title>Cisco Certification Program Refresh</title><link>https://network-notes.com/posts/2019/cisco-cert-refresh/</link><pubDate>Mon, 10 Jun 2019 12:30:00 -0500</pubDate><author>brett@network-notes.com (Brett Lykins)</author><dc:creator>Brett Lykins</dc:creator><guid>https://network-notes.com/posts/2019/cisco-cert-refresh/</guid><description>&lt;p&gt;One of the primary reasons I originally started this blog, was to use it for personal accountability with my Cisco certification progress. As I studied and understood a particular topic, I could write a blog article on that topic to cement it in my mind and share that knowledge with others.&lt;/p&gt;
&lt;p&gt;The complete lack of blog posts reflects how well that plan went… I did complete my CCNP Security and was in pursuit of my CCIE Security when my career trajectory shifted. No longer was I solely a Network or Security Architect, an increasing portion of my role was focused on Network Automation and solving problems at a massive scale. I stopped chasing traditional networking certifications and spent many thousands of hours learning Python and Golang to best solve the problems I was faced with at my job.&lt;/p&gt;
&lt;p&gt;While that work has been fulfilling and challenging, it doesn’t present itself well to the traditional Networking world mindset of “certification chasing”. Since I stopped actively pursuing Cisco certifications I have had more than one conversation where people have asked things like: “So where’s your CCIE at?” or “Are you going into management now?” As I looked at the Cisco certifications available, there wasn’t one that presented value to me and the work I was doing every day, beyond the basic knowledge acquired in getting my CCNA and CCNP Voice, Security, and Route/Switch in the past. I couldn’t justify the expense (in both money and time away from my family) to chase a CCIE if it didn’t apply to my career.&lt;/p&gt;
&lt;p&gt;Well as of today, Cisco is announcing something that changes all of that, and I’m super excited. They are doing a complete, top to bottom, refresh and realignment of the certifications that are offered, and the process to get/keep them.&lt;/p&gt;
&lt;h2 id="new-and-improved-cisco-certifications"&gt;New and Improved Cisco Certifications&lt;/h2&gt;
&lt;p&gt;All the discussed changes below go into effect on February 24th, 2020. So you’ve got time to finish your existing studies or gear up for a new certification/track.&lt;/p&gt;
&lt;p&gt;First and foremost, and the most personally exciting to me, is that there is a completely new certification track:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://network-notes.com/img/2019/new_cert_tracks-1.png"
alt="New Cisco Certification Tracks"&gt;&lt;figcaption&gt;
&lt;p&gt;The New Certification Tracks from Cisco!&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Now Network Automation/Netdevops is to be recognized via DevNet certifications in their track! Including up to a forthcoming DevNet Expert certification targeted at the same level of expertise expected out of CCIE-level folks.&lt;/p&gt;
&lt;p&gt;According to &lt;a href="https://developer.cisco.com/certification/"&gt;DevNet&lt;/a&gt; the DevNet Professional level exam/certification is targeted at:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;… developers who have at least three to five years of experience designing and implementing applications built on Cisco platforms. Two exams cover designing and developing resilient, robust and secure applications using Cisco APIs and platforms, and managing and deploying applications on Cisco infrastructure.&lt;/p&gt;
&lt;p&gt;Cisco DevNet&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is exactly where I find myself sitting today, and I’m super excited to see that Cisco and the folks at DevNet are listening to the community and responding to this need! I will be one of the first people in line next February when these exams go live. And I can 100% see the value in pursuing the DevNet Expert examination in my career path.&lt;/p&gt;
&lt;h2 id="additional-changes"&gt;Additional Changes&lt;/h2&gt;
&lt;p&gt;There are some significant changes to the certification paths, even inside the existing network-focused paths.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No more prerequisites for any Cisco certification, if you’re at the Professional or Expert level in your field, go sit that exam.&lt;/li&gt;
&lt;li&gt;All certifications are good for 3 years from the last pass date (including Expert level).&lt;/li&gt;
&lt;li&gt;There are now additional options, including Continuing Education, for renewing your certifications at all levels.&lt;/li&gt;
&lt;li&gt;Associate level (CCNA or DevNet Associate) is one exam.&lt;/li&gt;
&lt;li&gt;Professional level (CCNP or DevNet Professional) is two exams:
&lt;ul&gt;
&lt;li&gt;A Core skills exam in your track.&lt;/li&gt;
&lt;li&gt;A Concentration exam in a more specific area of focus to your specialization.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Taking additional specialization exams will grant you Cisco Specialist badges/awards.&lt;/li&gt;
&lt;li&gt;The tracks available to the Networking path narrow down to Enterprise, Security, Service Provider, Collaboration, and Data Center.
&lt;ul&gt;
&lt;li&gt;For example, Route/Switch and Wireless are being collapsed into the Enterprise track.&lt;/li&gt;
&lt;li&gt;Each of these topics now exists as a concentration or specialization exam inside the Enterprise track at the Professional level.&lt;/li&gt;
&lt;li&gt;If you’re mid-CCNP and want to know what the path forward for you looks like:
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.cisco.com/c/en/us/training-events/training-certifications/certifications/professional/ccnp-security-migration-tool.html"&gt;CCNP Security Migration Tool&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cisco.com/c/en/us/training-events/training-certifications/certifications/professional/ccnp-routing-switching-migration-tool.html"&gt;CCNP RS Migration Tool&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cisco.com/c/en/us/training-events/training-certifications/certifications/professional/ccnp-wireless-migration-tool.html"&gt;CCNP Wireless Migration Tool&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As noted, these changes will go into place on February 24th, 2020. However, exam topics and other information are now live via the links below. And expect MANY blog posts and other information from Cisco in the coming days, weeks, and months.&lt;/p&gt;
&lt;p&gt;Happy Certing!&lt;/p&gt;
&lt;h2 id="links"&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.cisco.com/c/dam/en_us/training-events/training-certification-faqs.pdf"&gt;Frequently Asked Questions (PDF)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cisco.com/c/en/us/training-events/training-certifications/take-learning-to-next-level.html"&gt;General information on the new certification changes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cisco.com/c/en/us/training-events/training-certifications/certifications/devnet.html"&gt;Information on the DevNet certifications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.cisco.com/certification/"&gt;More information from DevNet on these certs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cisco.com/c/en/us/training-events/training-certifications/next-level-certifications.html"&gt;Information on the certification tracks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>#CiscoChampion 2019</title><link>https://network-notes.com/posts/2019/cisco-champion-2019/</link><pubDate>Fri, 04 Jan 2019 08:18:00 -0500</pubDate><author>brett@network-notes.com (Brett Lykins)</author><dc:creator>Brett Lykins</dc:creator><guid>https://network-notes.com/posts/2019/cisco-champion-2019/</guid><description>&lt;p&gt;Happy 2019 everyone! Since I was lucky enough to be selected as a &lt;a href="https://community.cisco.com/t5/cisco-champions-public-documents/cisco-champion-program-faq-updated-october-2018/ta-p/3732770"&gt;Cisco Champion for 2019&lt;/a&gt;, I thought it was time to revive my zombie blog!&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve got quite a few posts in the hopper and will focus on not being such a perfectionist to posts that I don&amp;rsquo;t just hit that &amp;ldquo;Publish&amp;rdquo; button. Posts will be coming about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Network Security&lt;/li&gt;
&lt;li&gt;Network Programmability&lt;/li&gt;
&lt;li&gt;Python&lt;/li&gt;
&lt;li&gt;Systems Architecture&lt;/li&gt;
&lt;li&gt;And more!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;rsquo;s to an exciting and educational 2019!&lt;/p&gt;</description></item><item><title>Testing ISAKMP Part 1: Basic Connectivity</title><link>https://network-notes.com/posts/2016/netcat-isakmp/</link><pubDate>Fri, 20 May 2016 22:44:00 -0500</pubDate><author>brett@network-notes.com (Brett Lykins)</author><dc:creator>Brett Lykins</dc:creator><guid>https://network-notes.com/posts/2016/netcat-isakmp/</guid><description>&lt;h2 id="the-scenario"&gt;The Scenario&lt;/h2&gt;
&lt;p&gt;In the course of my day-to-day job, I interact with VPNs on many devices (primarily IPSec VPNs on the Cisco ASA). Oftentimes the simplest way to test an IPSec VPN is to fire up &lt;a href="http://linux.die.net/man/8/vpnc"&gt;&lt;code&gt;vpnc&lt;/code&gt;&lt;/a&gt; in a VM, change the config file as needed, and validate the connection.&lt;/p&gt;
&lt;p&gt;Sometimes though, you need to be more granular with your testing. Perhaps the person controlling the other endpoint/peer is not in charge of the intermediate network, and you need to validate that ISAKMP traffic is allowed through to the peer. Perhaps you want to validate an ACL that should be blocking ISAKMP. Or perhaps you&amp;rsquo;re just curious to gain a deeper understanding of what is happening at the protocol level. Either way, there&amp;rsquo;s no easy way to validate that ISAKMP is permitted end to end.&lt;/p&gt;
&lt;p&gt;Because ISAKMP utilizes UDP port 500 for transport, you can&amp;rsquo;t simply Telnet to the port and validate that it is permitted. We&amp;rsquo;re talking about UDP, not TCP, which means there is no Layer 4 validation of an open socket (the Three-Way-Handshake of TCP). This means that you can&amp;rsquo;t do something like &lt;code&gt;nc -uvv $host 500&lt;/code&gt; because while &lt;a href="http://linux.die.net/man/1/nc"&gt;&lt;code&gt;netcat&lt;/code&gt;&lt;/a&gt; can certainly open and utilize UDP sockets, netcat alone can&amp;rsquo;t tell you if a UDP port is open and listening. To test this below, I attempt to use netcat to check whether my local box is listening on UDP port 500 (which I know it is not):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ nc -uvv 127.0.0.1 &lt;span class="m"&gt;500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; found &lt;span class="m"&gt;0&lt;/span&gt; associations
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; found &lt;span class="m"&gt;1&lt;/span&gt; connections:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 1: &lt;span class="nv"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;82&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; outif &lt;span class="o"&gt;(&lt;/span&gt;null&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; src 127.0.0.1 port &lt;span class="m"&gt;59894&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; dst 127.0.0.1 port &lt;span class="m"&gt;500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; rank info not available
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Connection to 127.0.0.1 port &lt;span class="m"&gt;500&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;udp/isakmp&lt;span class="o"&gt;]&lt;/span&gt; succeeded!
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Succeeded indeed… Although we need to do something more to validate whether UDP/500 is open and listening for ISAKMP datagrams, as mentioned above we can still actually utilize netcat We can use it to open the UDP socket and then pipe in semi-valid ISAKMP data for netcat to pass to our destination. This way, we can at least get some sort of response or validation via either an ISAKMP reply message or debugs/counters on the far side peer.&lt;/p&gt;
&lt;h2 id="rfc2048--internet-security-association-and-key-management-protocol"&gt;RFC2048 – Internet Security Association and Key Management Protocol&lt;/h2&gt;
&lt;p&gt;ISAKMP is defined in &lt;a href="https://datatracker.ietf.org/doc/html/rfc2408"&gt;RFC2048&lt;/a&gt;, which describes in great detail the underlying structure of an ISAKMP UDP datagram. In section 3.1, it lays out the header format which is all we need for testing:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 1 2 3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ! Initiator !
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ! Cookie !
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ! Responder !
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ! Cookie !
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ! Next Payload ! MjVer ! MnVer ! Exchange Type ! Flags !
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ! Message ID !
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ! Length !
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Essentially, if you haven’t seen this style of packet diagram, each number across the top represents a bit, and each row represents 32 bits or 4 octets/bytes whichever you prefer. In this case, the ISAKMP header is 7 rows tall, which means that the ISAKMP header is 224 bits (7*32), or more commonly, 28 bytes long.&lt;/p&gt;
&lt;p&gt;Now we know how long the data has to be, but what are we going to put into it? The header breaks down into the following chunks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Initiator and Responder Cookies&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;8 bytes per Cookie&lt;/li&gt;
&lt;li&gt;These are tokens that enable each endpoint to identify Security Associations (&lt;a href="https://datatracker.ietf.org/doc/html/rfc2408#section-2.4"&gt;RFC2048, Sec 2.4&lt;/a&gt;), and to act as a method to assist in securing the communication (&lt;a href="https://datatracker.ietf.org/doc/html/rfc2408#section-2.5.3"&gt;RFC2048, Sec 2.5.3&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Also known as the Security Parameter Index (SPI).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Next Payload&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;1 byte&lt;/li&gt;
&lt;li&gt;Indicates what type of message is following this header.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Major Version and Minor Version&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;4 bits each&lt;/li&gt;
&lt;li&gt;Set to 1 and 0 respectively in RFC2048.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Exchange Type&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;1 byte&lt;/li&gt;
&lt;li&gt;Tells the other system what type of messages and payloads to expect.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flags&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;1 byte&lt;/li&gt;
&lt;li&gt;Set specific ISAKMP options; only the first three bits are specified in RFC2048 rest are to be zeros.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Message ID&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;4 bytes&lt;/li&gt;
&lt;li&gt;A unique ID is used in Phase 2 negotiations; as we’re simulating a Phase 1 datagram these are set to all zeros.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Length&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;4 bytes&lt;/li&gt;
&lt;li&gt;The combined length of the header and payloads is represented in bytes/octets.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Taking this information, we are now able to determine what fields we need to fill out and with what values to create our testing datagram. Once we are complete, we can simply feed the appropriate arrangement of binary into netcat, and it should be able to simulate the beginning of a valid ISAKMP exchange with the peer.&lt;/p&gt;
&lt;p&gt;The only hitch with this plan is the need for binary. For mere humans, visually tracking long strings of binary is not easy; at a glance can you tell if this is 7 or 8 digits &lt;code&gt;00000001&lt;/code&gt;? In addition, writing out each byte would get tedious and increase the likelihood of human error.&lt;/p&gt;
&lt;h2 id="hexadecimal-to-the-rescue"&gt;Hexadecimal to the Rescue&lt;/h2&gt;
&lt;p&gt;Luckily we can shortcut this process by representing the binary values with &lt;a href="https://en.wikipedia.org/wiki/Hexadecimal#Using_0.E2.80.939_and_A.E2.80.93F"&gt;Hexadecimal in &lt;code&gt;\x&lt;/code&gt; notation&lt;/a&gt;. This allows us to simply represent each byte or octet as a four-character string. For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Binary: &lt;span class="m"&gt;000000000000000100000010000000011&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Hexadecimal: &lt;span class="se"&gt;\x&lt;/span&gt;00&lt;span class="se"&gt;\x&lt;/span&gt;01&lt;span class="se"&gt;\x&lt;/span&gt;02&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="m"&gt;03&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Validation using xxd:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;\x01\x02\x03\x04&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; xxd -b 0000000: &lt;span class="m"&gt;00000001&lt;/span&gt; &lt;span class="m"&gt;00000010&lt;/span&gt; &lt;span class="m"&gt;00000011&lt;/span&gt; &lt;span class="m"&gt;00000100&lt;/span&gt; ....
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Working with that format, and referencing the above breakdown of the header format gives us the following Hexadecimal:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Initiator and Responder Cookies&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;8 bytes per Cookie&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Initiator Cookie&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;We’ll make this a value of 1 since it has to be something and we don’t care what it is.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x00\x00\x00\x00\x00\x00\x00\x01&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Responder Cookie&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;All zeros because this is an initial communication.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x00\x00\x00\x00\x00\x00\x00\x00&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Next Payload&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;1 byte&lt;/li&gt;
&lt;li&gt;No Next Payload, so a value of 0.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x00&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Major Version and Minor Version&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;4 bits each&lt;/li&gt;
&lt;li&gt;Mandatory values of 1 and 0, because we’re using ISAKMP version 1.0&lt;/li&gt;
&lt;li&gt;That would be 0001 and 0000 in binary.&lt;/li&gt;
&lt;li&gt;Together, that would look like 00010000, which equals 10 in Hex.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x10&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Exchange Type&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;1 byte&lt;/li&gt;
&lt;li&gt;We’re making a fake datagram with no payload, so we’ll try none (0) which appears to be permitted by RFC2048.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x00&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flags&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;1 byte&lt;/li&gt;
&lt;li&gt;No specific flags are needed, so we’re also setting this to 0.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x00&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Message ID&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;4 bytes&lt;/li&gt;
&lt;li&gt;Must be zeros per RFC2048 for an initial Phase 1 datagram like we’re simulating.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x00\x00\x00\x00&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Length&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;4 bytes&lt;/li&gt;
&lt;li&gt;This will be equal to 28 since we have a header with no payload.&lt;/li&gt;
&lt;li&gt;28 in Hex is 1C.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\x1C&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally, our complete string of Hex for the simulated initial ISAKMP datagram would be:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x1C
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="sending-our-data"&gt;Sending Our Data&lt;/h2&gt;
&lt;p&gt;Now that we have the complete Hex string for our test datagram, how do we go about sending it? Well as described earlier, we’re going to simply pass it into netcat by printing the string and piping it into netcat. But first, we need to get a destination setup that is listening for ISAKMP traffic. In my case, I’ve spun up a virtual Cisco ASA in my lab to act as our peer, and have it listening for ISAKMP traffic on the &lt;code&gt;INSIDE&lt;/code&gt; interface (172.16.16.1).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ASAv-1A/pri/act# show run int G0/4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;!
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; interface GigabitEthernet0/4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; nameif INSIDE
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; security-level 100
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ip address 172.16.16.1 255.255.255.0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; standby 172.16.16.2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ASAv-1A/pri/act# show run crypto ikev1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; crypto ikev1 enable INSIDE
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Taking the above Hex string and using &lt;code&gt;printf&lt;/code&gt; to send it into netcat gives us the following output (all of the Hex should be on one line, however for readability I have broken it up here):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="se"&gt;\x&lt;/span&gt;00&lt;span class="se"&gt;\x&lt;/span&gt;00&lt;span class="se"&gt;\x&lt;/span&gt;00&lt;span class="se"&gt;\x&lt;/span&gt;00&lt;span class="se"&gt;\x&lt;/span&gt;00&lt;span class="se"&gt;\x&lt;/span&gt;00&lt;span class="se"&gt;\x&lt;/span&gt;00&lt;span class="se"&gt;\x&lt;/span&gt;01&lt;span class="se"&gt;\x&lt;/span&gt;00&lt;span class="se"&gt;\x&lt;/span&gt;00&lt;span class="se"&gt;\x&lt;/span&gt;00&lt;span class="se"&gt;\x&lt;/span&gt;00&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="se"&gt;\x&lt;/span&gt;00&lt;span class="se"&gt;\x&lt;/span&gt;00&lt;span class="se"&gt;\x&lt;/span&gt;00&lt;span class="se"&gt;\x&lt;/span&gt;00&lt;span class="se"&gt;\x&lt;/span&gt;10&lt;span class="se"&gt;\x&lt;/span&gt;00&lt;span class="se"&gt;\x&lt;/span&gt;00&lt;span class="se"&gt;\x&lt;/span&gt;00&lt;span class="se"&gt;\x&lt;/span&gt;00&lt;span class="se"&gt;\x&lt;/span&gt;00&lt;span class="se"&gt;\x&lt;/span&gt;00&lt;span class="se"&gt;\x&lt;/span&gt;1C &lt;span class="p"&gt;|&lt;/span&gt; nc -uvv 172.16.16.1 &lt;span class="m"&gt;500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Connection to 172.16.16.1 &lt;span class="m"&gt;500&lt;/span&gt; port &lt;span class="o"&gt;[&lt;/span&gt;udp/isakmp&lt;span class="o"&gt;]&lt;/span&gt; succeeded!
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;^C
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;As you can see, netcat says “Connection succeeded” again, and the output on our end is not so different from when we tested to a non-ISAKMP speaking endpoint. However, on the ASA IKEv1 statistics, we can see something very interesting:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ASAv-1A/pri/act# show crypto ikev1 stats | inc Drop
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;In Drop Packets: 6 Out Drop Packets: 0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;So the ASA saw the packet and marked it as invalid in some way then dropped it (which makes sense because we didn’t send any payload at all).&lt;/p&gt;
&lt;p&gt;This validates that the path for UDP/500 is open from my test box to the peer!&lt;/p&gt;
&lt;h2 id="bonus-validations"&gt;Bonus Validations&lt;/h2&gt;
&lt;p&gt;I’m a curious soul though, so I wanted to dig deeper still. For more detail we can turn on the IKEv1 debugs on the ASA and see as it is receiving and discarding each of the packets:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ASAv-1A/pri/act# debug crypto ikev1 255
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ASAv-1A/pri/act#
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;May 21 03:25:04 [IKEv1]IKE Receiver: Packet received on 172.16.16.1:500 from 172.16.16.16:39994
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;May 21 03:25:04 [IKEv1]IKE Receiver: Runt ISAKMP packet discarded on Port 500 from 172.16.16.16:39994
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;May 21 03:25:04 [IKEv1]IKE Receiver: Packet received on 172.16.16.1:500 from 172.16.16.16:39994
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;May 21 03:25:04 [IKEv1]IKE Receiver: Runt ISAKMP packet discarded on Port 500 from 172.16.16.16:39994
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;May 21 03:25:05 [IKEv1]IKE Receiver: Packet received on 172.16.16.1:500 from 172.16.16.16:39994
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;May 21 03:25:05 [IKEv1]IKE Receiver: Runt ISAKMP packet discarded on Port 500 from 172.16.16.16:39994
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;May 21 03:25:06 [IKEv1]IKE Receiver: Packet received on 172.16.16.1:500 from 172.16.16.16:39994
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;May 21 03:25:06 [IKEv1]IKE Receiver: Runt ISAKMP packet discarded on Port 500 from 172.16.16.16:39994
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;May 21 03:25:07 [IKEv1]IKE Receiver: Packet received on 172.16.16.1:500 from 172.16.16.16:39994
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;May 21 03:25:07 [IKEv1]IKE Receiver: Runt ISAKMP packet discarded on Port 500 from 172.16.16.16:39994
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;May 21 03:25:07 [IKEv1]IKE Receiver: Packet received on 172.16.16.1:500 from 172.16.16.16:39994
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;May 21 03:25:07 [IKEv1]IKE Receiver: Discarding packet, invalid IKE version
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;And for the coup, I did a packet capture on the ASA for all UDP/500 traffic and decoded it to see if the protocol decoder could interpret the frames as ISAKMP:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ASAv-1A/pri/act# cap cap1 int INSIDE match udp any any eq 500
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ASAv-1A/pri/act# show cap cap1 decode 6 packets captured
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 1: 03:34:01.443916 172.16.16.16.35457 &amp;gt; 172.16.16.1.500: udp 1 [ISAKMP header incomplete]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 2: 03:34:01.444007 172.16.16.16.35457 &amp;gt; 172.16.16.1.500: udp 1 [ISAKMP header incomplete]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 3: 03:34:02.444892 172.16.16.16.35457 &amp;gt; 172.16.16.1.500: udp 1 [ISAKMP header incomplete]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 4: 03:34:03.445609 172.16.16.16.35457 &amp;gt; 172.16.16.1.500: udp 1 [ISAKMP header incomplete]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 5: 03:34:04.447059 172.16.16.16.35457 &amp;gt; 172.16.16.1.500: udp 1 [ISAKMP header incomplete]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 6: 03:34:04.450126 172.16.16.16.35457 &amp;gt; 172.16.16.1.500: udp 75 ISAKMP Header Initiator COOKIE: 78 30 30 78 30 30 78 30 Responder COOKIE: 30 78 30 30 78 30 30 78 Next Payload: IKEV2 LEAP PAYLOAD Version: 3.0 Exchange Type: DOI Specific Use Flags: MessageID: 30783031 Length: 2016424056 [ISAKMP payload corrupted or truncated]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;And there you have it, a means to quickly and easily validate a UDP/500 path!&lt;/p&gt;
&lt;h2 id="series-update-2026"&gt;Series Update (2026)&lt;/h2&gt;
&lt;p&gt;This post has been expanded into a comprehensive series:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Part 1&lt;/strong&gt; (this post): Basic connectivity testing with minimal ISAKMP headers&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://network-notes.com/posts/2026/netcat-isakmp-2/"&gt;Part 2&lt;/a&gt;&lt;/strong&gt;: Testing with pre-built complete packets&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://network-notes.com/posts/2026/netcat-isakmp-3/"&gt;Part 3&lt;/a&gt;&lt;/strong&gt;: Building ISAKMP packets from scratch&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://network-notes.com/posts/2026/netcat-isakmp-4/"&gt;Part 4&lt;/a&gt;&lt;/strong&gt;: Automating tests with Scapy (coming soon)&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>300-208 SISAS - What Is Cisco ISE?</title><link>https://network-notes.com/posts/2015/300-208-sisas-2/</link><pubDate>Sat, 12 Dec 2015 21:20:00 -0500</pubDate><author>brett@network-notes.com (Brett Lykins)</author><dc:creator>Brett Lykins</dc:creator><guid>https://network-notes.com/posts/2015/300-208-sisas-2/</guid><description>&lt;p&gt;In my &lt;a href="https://network-notes.com/posts/2015/300-208-sisas/"&gt;earlier post about the Cisco 300-208 SISAS&lt;/a&gt; (Implementing Cisco Secure Access Solutions) exam, I gave a brief overview of the exam and listed the exam topics as &lt;a href="https://learningnetwork.cisco.com/community/certifications/ccnpsecurity/sisas/exam-topics"&gt;laid out by the Cisco Learning Community&lt;/a&gt;. However, I felt that these largely boil down to a few key concepts related to &lt;a href="http://www.cisco.com/c/en/us/products/collateral/security/identity-services-engine/data_sheet_c78-656174.html"&gt;Cisco ISE (Identity Services Engine)&lt;/a&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Understand what ISE is.&lt;/li&gt;
&lt;li&gt;Understand why you might use ISE in a wired or wireless network.&lt;/li&gt;
&lt;li&gt;Understand what ISE does at a protocol level.&lt;/li&gt;
&lt;li&gt;Understand how ISE interacts with Network Access Devices and other systems.&lt;/li&gt;
&lt;li&gt;Understand how to configure ISE and the Network Access Devices.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This post will deal with Concept 1, Understand what Cisco ISE is.&lt;/p&gt;
&lt;h2 id="a-little-history"&gt;A Little History&lt;/h2&gt;
&lt;p&gt;Before you can understand what ISE is, I feel that you need to know where it came from. Cisco&amp;rsquo;s NAC (Network Access Control) offerings have been fairly scattershot over the years. They even tried unsuccessfully to market a contrary meaning of NAC for several years, attempting to sell their services as Network Admission Control. Thankfully, they&amp;rsquo;ve given in and joined the rest of us in Network Access Control land.&lt;/p&gt;
&lt;p&gt;Cisco ACS (Access Control Server) was the direct predecessor to ISE and still exists today. It provides RADIUS and TACACS services and can integrate with central Identity Stores such as Active Directory or another LDAP-speaking software. This means that you would often find ACS in an enterprise network serving to authenticate VPN and wireless users or control access to network devices. Over time ACS evolved, as most Cisco applications have, from something you installed on top of Windows, to a full-blown Linux-based appliance (&lt;a href="http://www.cisco.com/c/en/us/td/docs/net_mgmt/cisco_secure_access_control_system/5-0/release/notes/ACS-50-releasenotes.html"&gt;as of ACS 5.0 in 2009&lt;/a&gt;). The common &amp;ldquo;appliance&amp;rdquo; model in use today, means that without a little (non-Cisco approved) tinkering you don&amp;rsquo;t have access to the Linux guts of Cisco ACS, or ISE for that matter, you&amp;rsquo;ve simply presented an application.&lt;/p&gt;
&lt;p&gt;At the same time that ACS was growing and advancing in capabilities, there was also the burst of BYOD onto the market and a growing market in servers/services to manage and administer user Identity on the network. Cisco saw an opportunity to forklift the capabilities of ACS into a new product that was targeted directly at the &amp;ldquo;new&amp;rdquo; Identity Management market, not just traditional &amp;ldquo;access control&amp;rdquo;. They took the underlying operating system from ACS, a RHEL/CentOS-based distribution that they call ADE-OS (Application Deployment Engine), and added a few new capabilities and features to the application. This creation is what we know today as Cisco ISE. Essentially, you can think of ISE as ACS version 6.0.&lt;/p&gt;
&lt;p&gt;The only thing left out of ISE (&lt;a href="http://www.cisco.com/c/en/us/td/docs/security/ise/2-0/release_notes/ise20_rn.html"&gt;until the recent release of Cisco ISE v2.0&lt;/a&gt;) was TACACS, as it was intended that you still purchase ACS to control network device administrative access. For the 300-208 SISAS exam today, you can consider TACACS to be out-of-scope for ISE, and to be the sole purview of ACS, although you still have to understand TACACS for the exam.&lt;/p&gt;
&lt;p&gt;What Is Identity Management?&lt;/p&gt;
&lt;p&gt;A full discussion of what Identity and Identity Management is, could take up many pages and posts (&lt;a href="https://en.wikipedia.org/wiki/Identity_management"&gt;see Wikipedia&lt;/a&gt;), however for the 300-208 SISAS exam it can be summed up as this from the Official Cert Guide:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;An identity is a representation of who a user or device is. Cisco ISE uses an endpoint&amp;rsquo;s MAC address to uniquely identify that endpoint. A username is one method of uniquely identifying an end user. Although SSIDs and IP addresses can be used as conditions or attributes in ISE policies, they are not identities.&lt;/p&gt;
&lt;p&gt;Woland, Aaron; Redmon, Kevin (2015-04-27). CCNP Security SISAS 300-208 Official Cert Guide (Certification Guide) (Kindle Locations 13023-13026). Cisco Press. Kindle Edition.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;An Identity Management system can then be defined as a way to keep track of Identity information for many users or devices, as well as the associated Authorization and Authentication information for those entities. This is precisely what Cisco ISE is and does. It provides all manner of services related to managing who users and devices are, and what network resources they may access. Cisco ISE does not directly stop an entity from accessing a portion of the network (there are sometimes Inline Policy Nodes, but they are not common). The Network Access Devices themselves handle the heavy lifting of granting/denying access by utilizing IEEE 802.1x which I will discuss in a subsequent post. ISE simply provides a centralized location to set policy, gather reporting, and then interact with the NAD via Radius.&lt;/p&gt;
&lt;p&gt;In the next post, I will delve into Concept 2, the Whys surrounding Cisco ISE, as well as give a few example use cases.&lt;/p&gt;</description></item><item><title>The Elusive A+ Rating on SSL Labs</title><link>https://network-notes.com/posts/2015/ssl-labs-aplus/</link><pubDate>Mon, 24 Aug 2015 22:58:00 -0500</pubDate><author>brett@network-notes.com (Brett Lykins)</author><dc:creator>Brett Lykins</dc:creator><guid>https://network-notes.com/posts/2015/ssl-labs-aplus/</guid><description>&lt;p&gt;I often have to talk people off a cliff because of their website&amp;rsquo;s (sometimes perceived) vulnerabilities on &lt;a href="https://www.ssllabs.com/ssltest"&gt;Qualys&amp;rsquo; SSL Labs&lt;/a&gt; testing site. They have received an A- score for their site after running the tests, and see a few things in yellow. Suddenly, they begin to believe that every villain on the web is now running amok with their data. The truth of the matter is that if your SSL configuration is rating at an A-, that configuration is usually just fine. Out of curiosity, I recently spent a few hours tweaking and managed to upgrade from an A- to an A+ for this domain. I wanted to share that process so that anyone else can know what it might take to get an A+.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://network-notes.com/img/2015/SSL-Labs-APlus.jpg"
alt="SSL Labs A&amp;#43; Rating"&gt;&lt;figcaption&gt;
&lt;p&gt;A+ Rating for network-notes.com on Qualys SSL Labs Testing&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="caveats-and-advice"&gt;Caveats and Advice&lt;/h2&gt;
&lt;p&gt;The things that get you to an A+, are mostly semantic and not always the most obvious and open vulnerabilities. The SSL Labs scoring process, while open and based on common sense/best practices, is simply one entity&amp;rsquo;s opinion of your SSL configuration. In addition to the somewhat arbitrary nature of any benchmarking site, such as SSL Labs, remember that a configuration that gets an A+ today is one new vulnerability away from an F. Your security posture on any system must be continuously evolving, along with the threats you&amp;rsquo;re defending against.&lt;/p&gt;
&lt;p&gt;These caveats also hold for any configurations listed here. These are not guarantees that your site will be secure, or even recommendations. They merely reflect the Apache and mod_ssl configurations that suited my needs, in my circumstances, at the time of this writing. Please use common sense when establishing an SSL policy for your website.&lt;/p&gt;
&lt;p&gt;The tweaks boiled down to either explicitly defining things in my Apache VirtualHost configuration that I assumed didn&amp;rsquo;t matter, or bending my strict configuration a little bit to meet the requirements of older browsers. For example, I was initially only going to enable TLSv1.2. If you can&amp;rsquo;t support TLSv1.2, I wasn&amp;rsquo;t really worried if the site was unavailable to you. However, as I thought about it, I realized that it was a pointless stance to take. Why isolate this site from an unfortunately large, albeit less secure, portion of the web when I don&amp;rsquo;t have to? I&amp;rsquo;m not hosting any sensitive information on these servers; I am not asking users to make credit card transactions or give me their Social Security numbers. I eventually walked my SSL configuration backward and enabled TLSv1.0, TLSv1.1, and TLSv1.2 as part of my remediation efforts to get the elusive A+ from SSL Labs.&lt;/p&gt;
&lt;h2 id="how-to-get-an-a--without-really-trying"&gt;How to Get an A- Without Really Trying&lt;/h2&gt;
&lt;p&gt;Below is an example of the Apache VirtualHost configuration I had in place before beginning this process. It was graded at an A- by SSL Labs and is pretty standard. Most of it was pulled directly from the &lt;code&gt;default-ssl.conf&lt;/code&gt; file in Apache 2.4.&lt;/p&gt;
&lt;details class="collapsable-code" &gt;
&lt;summary title="Click to interact"&gt;&lt;span class="collapsable-code__title"&gt;Starter Apache Config&lt;/span&gt;&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-apacheconf" data-lang="apacheconf"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;IfModule&lt;/span&gt; &lt;span class="s"&gt;mod_ssl.c&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;VirtualHost&lt;/span&gt; &lt;span class="s"&gt;*:443&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;ServerName&lt;/span&gt; network-notes.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;# SSL Configuration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;SSLEngine&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;SSLProtocol&lt;/span&gt; +TLSv1.1 +TLSv1.2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;SSLCipherSuite&lt;/span&gt; HIGH:!aNULL:!MD5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;SSLCertificateFile&lt;/span&gt; network-notes.com.pem
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;SSLCertificateKeyFile&lt;/span&gt; network-notes.com.key
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;SSLCertificateChainFile&lt;/span&gt; network-notes.com-cabundle
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;# Fixes for IE being, well, IE...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;BrowserMatch&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;MSIE [2-6]&amp;#34;&lt;/span&gt; nokeepalive ssl-unclean-shutdown
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;\downgrade-1&lt;/span&gt;.&lt;span class="err"&gt;0&lt;/span&gt; &lt;span class="err"&gt;force-response-1&lt;/span&gt;.&lt;span class="err"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;BrowserMatch&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;MSIE [17-9]&amp;#34;&lt;/span&gt; ssl-unclean-shutdown
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;# Browser caching for static content&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;filesMatch&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;.(js|css|png|jpeg|jpg|gif|ico|swf|flv|pdf|zip)$&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Header&lt;/span&gt; set Cache-Control &lt;span class="s2"&gt;&amp;#34;max-age=1209600, public&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/filesMatch&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;# Redirect &amp;#34;/&amp;#34; to &amp;#34;/blog/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;If&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;%{REQUEST_URI} == &amp;#39;/&amp;#39;&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Redirect&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/blog/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/If&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/VirtualHost&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/IfModule&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/details&gt;
&lt;p&gt;Now, here&amp;rsquo;s what the good folks at Qualys SSL Labs had to say about that configuration:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&amp;ldquo;The server does not support Forward Secrecy with the reference browsers. Grade reduced to A-. &lt;a href="https://en.wikipedia.org/wiki/Forward_secrecy"&gt;MORE INFO»&lt;/a&gt;&amp;rdquo;
&lt;ol&gt;
&lt;li&gt;&amp;ldquo;Forward Secrecy - With some browsers (&lt;a href="https://community.qualys.com/blogs/securitylabs/2013/06/25/ssl-labs-deploying-forward-secrecy"&gt;more info&lt;/a&gt;)&amp;rdquo;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Incorrect SNI Alerts - &amp;lt;www.network-notes.com&amp;gt;&amp;rdquo;&lt;/li&gt;
&lt;li&gt;In addition, unless a browser was one of the newest versions available, there was also a slew of &amp;ldquo;Protocol or cipher suite mismatch&amp;rdquo;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="working-towards-an-a"&gt;Working Towards an A&lt;/h2&gt;
&lt;p&gt;The easiest fix was enabling TLSv1.o as I described earlier. By simply adding &lt;code&gt;+TLSv1&lt;/code&gt; to the &lt;code&gt;SSLProtocol&lt;/code&gt; directive, I opened up a much larger range of possible Protocol/Cipher Suite combinations:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-apacheconf" data-lang="apacheconf"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;SSLProtocol&lt;/span&gt; +TLSv1 +TLSv1.1 +TLSv1.2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Unfortunately, this change resulted in no movement whatsoever of my A- rating. At the very least, issue #3 was resolved; the wide majority of browsers could negotiate a connection. The question remains, however, why would Forward Secrecy only work with some browsers? I have configured all of the DHE and ECDHE ciphers needed and was able to confirm this fact because many browser simulations can connect with an ECDHE cipher. Upon further investigation of the output from SSL Labs, I found the following, &amp;ldquo;Cipher Suites (sorted by strength as the server has no preference)&amp;rdquo;. The Forward Secrecy enabled ciphers are available to all browsers, but I wondered if SSL Labs was indicating that some might not pick them because they weren&amp;rsquo;t offered first.&lt;/p&gt;
&lt;p&gt;I always assumed cipher suite order was generally arbitrary, but to appease SSL Labs I sorted them by strength. I added &lt;code&gt;@strength&lt;/code&gt; to the end of my &lt;code&gt;SSLCipherSuite&lt;/code&gt; directive, and also added the SSLHonorCipherOrder directive. The &lt;a href="http://httpd.apache.org/docs/2.4/mod/mod_ssl.html#sslhonorcipherorder"&gt;&lt;code&gt;SSLHonorCipherOrder&lt;/code&gt;&lt;/a&gt; directive was because, if I&amp;rsquo;m going to sort my cipher suite, your browser better obey.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-apacheconf" data-lang="apacheconf"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;SSLCipherSuite&lt;/span&gt; HIGH:!aNULL:!MD5:@STRENGTH
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;SSLHonorCipherOrder&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Upon testing again I had&amp;hellip; Success! I was upgraded to an A!&lt;/p&gt;
&lt;p&gt;From SSL Labs: &amp;ldquo;Forward Secrecy - Yes (with most browsers) ROBUST (&lt;a href="https://community.qualys.com/blogs/securitylabs/2013/06/25/ssl-labs-deploying-forward-secrecy"&gt;more info&lt;/a&gt;)&amp;rdquo; There is now only one more issue in yellow, so I assumed that fixing the SNI mixup should get me to an A+.&lt;/p&gt;
&lt;h2 id="sni-ed-remarks"&gt;SNI-ed Remarks&lt;/h2&gt;
&lt;p&gt;The SNI error given was not exactly verbose: &amp;ldquo;Incorrect SNI Alerts - &amp;lt;www.network-notes.com&amp;gt;&amp;rdquo;. This was a little puzzling, as I&amp;rsquo;m not using SNI for this site; there&amp;rsquo;s only one domain hosted on the server. After Googling for a few minutes, I found myself at a &lt;a href="https://community.qualys.com/thread/13532"&gt;Qualys Community post&lt;/a&gt; from August 2014 detailing this issue. Most helpful it gave a quick and dirty test to validate the issue yourself using OpenSSL (&lt;code&gt;openssl&lt;/code&gt;), which I tweaked as shown below to look for unrecognized name SSL alerts. You can see in the first example using network-notes.com, that there was no issue. However, when using &amp;lt;www.network-notes.com&amp;gt;, the server was indeed giving back an unrecognized name error:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ &lt;span class="nv"&gt;SERVER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;network-notes.com&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; -e &lt;span class="s2"&gt;&amp;#34;HEAD / HTTP/1.0\r\nHost: &lt;/span&gt;&lt;span class="nv"&gt;$SERVER&lt;/span&gt;&lt;span class="s2"&gt;\r\n\r\n&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; openssl s_client -servername &lt;span class="nv"&gt;$SERVER&lt;/span&gt; -connect &lt;span class="nv"&gt;$SERVER&lt;/span&gt;:443 -state 2&amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep unrecognized
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ &lt;span class="nv"&gt;SERVER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;www.network-notes.com&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; -e &lt;span class="s2"&gt;&amp;#34;HEAD / HTTP/1.0\r\nHost: &lt;/span&gt;&lt;span class="nv"&gt;$SERVER&lt;/span&gt;&lt;span class="s2"&gt;\r\n\r\n&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; openssl s_client -servername &lt;span class="nv"&gt;$SERVER&lt;/span&gt; -connect &lt;span class="nv"&gt;$SERVER&lt;/span&gt;:443 -state 2&amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep unrecognized
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SSL3 alert read:warning:unrecognized name
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This is a perfect example of the minutia you have to wade through in search of an A+. My DNS for this domain is configured with &amp;lt;www.network-notes.com&amp;gt; as a CNAME for network-notes.com, so no one should ever wind up at &amp;lt;www.network-notes.com&amp;gt;. However, since it is a SAN in my SSL certificate, according to Qualys I need to specify it as a ServerAlias in my Apache VirtualHost configuration:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-apacheconf" data-lang="apacheconf"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;ServerAlias&lt;/span&gt; www.network-notes.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This change cleared up the SNI error on SSL Labs, and in my testing, however, my grade is still (only?) an A. Now it&amp;rsquo;s time to think this through and re-read the output from SSL Labs.&lt;/p&gt;
&lt;h2 id="headed-for-an-a"&gt;Headed for an A+&lt;/h2&gt;
&lt;p&gt;One of the lines indicated that I did not have the HSTS (HTTP Strict Transport Security) enabled. There&amp;rsquo;s a great &lt;a href="https://raymii.org/s/tutorials/HTTP_Strict_Transport_Security_for_Apache_NGINX_and_Lighttpd.html"&gt;summary of HSTS, including sample configurations&lt;/a&gt; at Raymii.org, but essentially it is an HTTP header that is returned to your browser on the first visit. This header tells your browser to always use HTTPS for any subsequent visits to your domain. Since I have all traffic redirecting to HTTPS already, I figured this would be a simple addition. After all, at this point, I was grasping at straws and I added the following configuration:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-apacheconf" data-lang="apacheconf"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Header&lt;/span&gt; always set Strict-Transport-Security &lt;span class="s2"&gt;&amp;#34;max-age=63072000; includeSubdomains; preload&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;And finally, lo and behold, the elusive A+!&lt;/p&gt;
&lt;p&gt;Quote from the test: &amp;ldquo;This server supports HTTP Strict Transport Security with long duration. Grade set to A+.&amp;rdquo;&lt;/p&gt;
&lt;h2 id="final-results"&gt;Final Results&lt;/h2&gt;
&lt;p&gt;Now that we have the A+, I figured I should go ahead and complete the other task that I was considering as a possibility, configuring &lt;a href="https://en.wikipedia.org/wiki/OCSP_stapling"&gt;OSCP Stapling&lt;/a&gt;. This was a simple enough configuration consisting of two additional lines, one defining where the OSCP Stapling cache would be and another turning the feature on. While this change didn&amp;rsquo;t upgrade my SSL Labs score any further (A++ anyone?), it did green up another field. After all this time working on the configuration was still fairly satisfying.&lt;/p&gt;
&lt;p&gt;Below is an example of what my SSL VirtualHosts file looked like after all of these contortions. I hope you all find it useful, and please let me know if you all have any recommendations, corrections, or your own stories of optimizing SSL.&lt;/p&gt;
&lt;details class="collapsable-code" &gt;
&lt;summary title="Click to interact"&gt;&lt;span class="collapsable-code__title"&gt;Final Apache Config&lt;/span&gt;&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-apacheconf" data-lang="apacheconf"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;IfModule&lt;/span&gt; &lt;span class="s"&gt;mod_ssl.c&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;SSLStaplingCache&lt;/span&gt; shmcb:/tmp/stapling_cache(128000)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;VirtualHost&lt;/span&gt; &lt;span class="s"&gt;*:443&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;ServerName&lt;/span&gt; network-notes.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;ServerAlias&lt;/span&gt; www.network-notes.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;# SSL Configuration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;SSLEngine&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;SSLProtocol&lt;/span&gt; +TLSv1 +TLSv1.1 +TLSv1.2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;SSLCipherSuite&lt;/span&gt; HIGH:!aNULL:!MD5:@STRENGTH
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;SSLHonorCipherOrder&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;SSLCertificateFile&lt;/span&gt; network-notes.com.pem
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;SSLCertificateKeyFile&lt;/span&gt; network-notes.com.key
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;SSLCertificateChainFile&lt;/span&gt; network-notes.com-cabundle
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;SSLUseStapling&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;# HSTS Configuration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Header&lt;/span&gt; always set Strict-Transport-Security &lt;span class="s2"&gt;&amp;#34;max-age=63072000; includeSubdomains; preload&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;# Fixes for IE being, well, IE...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;BrowserMatch&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;MSIE [2-6]&amp;#34;&lt;/span&gt; nokeepalive ssl-unclean-shutdown
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;\downgrade-1&lt;/span&gt;.&lt;span class="err"&gt;0&lt;/span&gt; &lt;span class="err"&gt;force-response-1&lt;/span&gt;.&lt;span class="err"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;BrowserMatch&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;MSIE [17-9]&amp;#34;&lt;/span&gt; ssl-unclean-shutdown
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;# Browser caching for static content&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;filesMatch&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;.(js|css|png|jpeg|jpg|gif|ico|swf|flv|pdf|zip)$&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Header&lt;/span&gt; set Cache-Control &lt;span class="s2"&gt;&amp;#34;max-age=1209600, public&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/filesMatch&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;# Redirect &amp;#34;/&amp;#34; to &amp;#34;/blog/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;If&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;%{REQUEST_URI} == &amp;#39;/&amp;#39;&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Redirect&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/blog/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/If&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/VirtualHost&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/IfModule&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/details&gt;</description></item><item><title>300-208 SISAS - How to Tackle the Beast</title><link>https://network-notes.com/posts/2015/300-208-sisas/</link><pubDate>Wed, 19 Aug 2015 23:09:00 -0500</pubDate><author>brett@network-notes.com (Brett Lykins)</author><dc:creator>Brett Lykins</dc:creator><guid>https://network-notes.com/posts/2015/300-208-sisas/</guid><description>&lt;p&gt;The best way to finish something is to begin it. So I decided I would begin my prep for the 300-208 SISAS (Implementing Cisco Secure Access Solutions) exam, by laying out my personal study plan against the exam topics, found &lt;a href="https://learningnetwork.cisco.com/community/certifications/ccnpsecurity/sisas/exam-topics"&gt;here on the Cisco Learning Community&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The exam topics are broken into five broad categories, and Cisco also gives a general indication of what percentage of the exam is on each topic:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1.0 Identity Management/Secure Access - 33%
2.0 Threat Defense - 10%
3.0 Troubleshooting, Monitoring, and Reporting Tools - 7%
4.0 Threat Defense Architectures - 17%
5.0 Identity Management Architectures - 33%
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each of those broad topics has several sub-categories; 1.0 Identity Management/Secure Access, for example, is broken down into over 60 sub-sections. This depth of expected knowledge can seem quite daunting at first, especially given the vague nature of some of the topics. However, I&amp;rsquo;ve found that the exam breaks down into 5 key areas, and are all focused primarily on &lt;a href="http://www.cisco.com/c/en/us/products/collateral/security/identity-services-engine/data_sheet_c78-656174.html"&gt;Cisco ISE (Identity Services Engine)&lt;/a&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Understand what ISE is.&lt;/li&gt;
&lt;li&gt;Understand why you might use ISE in a wired or wireless network.&lt;/li&gt;
&lt;li&gt;Understand what ISE does at a protocol level.&lt;/li&gt;
&lt;li&gt;Understand how ISE interacts with Network Access Devices and other systems.&lt;/li&gt;
&lt;li&gt;Understand how to configure ISE and the Network Access Devices.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is why my primary focus in these exam preparations is getting hands-on with Cisco ISE. I have been spending at least 10 minutes per night, getting familiar with both the GUI itself and the operations that ISE can do. 10 minutes may not seem like much, but getting any hands-on time with a complex system matters. You have to keep yourself sharp and fresh when preparing for an exam. Especially if, like in my current job, you do not interact with ISE at all on a day-to-day basis.&lt;/p&gt;
&lt;p&gt;In the next few posts, I&amp;rsquo;ll dig further into each of these 5 key areas, as well as discuss how to set up an ISE test lab for yourself and talk through some of the scenarios I&amp;rsquo;m using for my lab.&lt;/p&gt;</description></item><item><title>CCNP Security - Halfway There</title><link>https://network-notes.com/posts/2015/ccnp-sec-halfway/</link><pubDate>Sat, 15 Aug 2015 21:52:00 -0500</pubDate><author>brett@network-notes.com (Brett Lykins)</author><dc:creator>Brett Lykins</dc:creator><guid>https://network-notes.com/posts/2015/ccnp-sec-halfway/</guid><description>&lt;p&gt;I am halfway towards my CCNP Security and am finally gearing up to finish it. When I completed the 642-618 FIREWALL and 642-648 VPN exams at the beginning of 2014, I was promptly sidetracked by the little things in life. (Such as moving across the country, starting a new job, and finishing my BSIT at WGU.) Knowing that the old CCNP Security exams had cycled out in April of 2014, I used &lt;a href="http://www.cisco.com/web/learning/tools/ccnp_security/index.html"&gt;Cisco&amp;rsquo;s CCNP Security Migration Path&lt;/a&gt; tool to validate that I was left with these two exams:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;300-207 SITCS (Implementing Cisco Threat Control Solutions)&lt;/li&gt;
&lt;li&gt;300-208 SISAS (Implementing Cisco Secure Access Solutions)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I am starting first with the 300-208 SISAS exam as it covers a range of topics, such as 802.1x, Cisco ISE, and Radius, that I am very familiar with. However, from everything I&amp;rsquo;ve read, it goes into great depth on the minutia of the ISE interface. As I haven&amp;rsquo;t touched ISE in a production environment in over a year now, I&amp;rsquo;ve been spending time most evenings in my lab spinning up and down many different scenarios.&lt;/p&gt;
&lt;p&gt;My lab for this study is entirely virtual and is a dry run for building my CCIE Security lab. It currently consists of the following, largely coordinated and controlled via GNS3:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ISE 1.2 - VMWare Fusion on my MacBook&lt;/li&gt;
&lt;li&gt;ACS 5.6 - VMWare Fusion on my MacBook&lt;/li&gt;
&lt;li&gt;CentOS test boxes (x2) - VMWare Fusion on my MacBook&lt;/li&gt;
&lt;li&gt;IOU L2 Image (x2) - Rackspace Cloud Server&lt;/li&gt;
&lt;li&gt;IOU L3 Image (x6) - Rackspace Cloud Server&lt;/li&gt;
&lt;li&gt;ASA 8.4 (x2) - QEMU on a Rackspace Cloud Server&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the weeks to come I&amp;rsquo;ll be posting more about my exam preparations, including lab scenarios and links.  This is mostly for me, but if anyone else gets some use out of it too, even better.&lt;/p&gt;</description></item></channel></rss>