<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>rationali.st</title>
    <subtitle>Andrew Cooks - Linux networking, systems programming, and performance analysis</subtitle>
    <link rel="self" type="application/atom+xml" href="https://www.rationali.st/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://www.rationali.st"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-01-01T00:00:00+00:00</updated>
    <id>https://www.rationali.st/atom.xml</id>
    <entry xml:lang="en">
        <title>When Packets Can&#x27;t Wait: Comparing Protocols for Delay-Sensitive Data</title>
        <published>2026-01-01T00:00:00+00:00</published>
        <updated>2026-01-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/delay-sensitive-protocols/"/>
        <id>https://www.rationali.st/delay-sensitive-protocols/</id>
        
        <summary type="html">&lt;p&gt;In &lt;a href=&quot;&#x2F;diagnosing-video-stuttering-over-tcp&#x2F;&quot;&gt;Diagnosing Video Stuttering Over TCP&lt;&#x2F;a&gt;, we built a diagnostic framework—identifying zero-window events (receiver overwhelmed) versus retransmits (network problems). In &lt;a href=&quot;&#x2F;tcp-jitter-cliff&#x2F;&quot;&gt;The TCP Jitter Cliff&lt;&#x2F;a&gt;, we discovered that throughput collapses unpredictably when jitter exceeds ~20% of RTT, and the chaos zone makes diagnosis treacherous.&lt;&#x2F;p&gt;
&lt;p&gt;The conclusion from both posts is clear: &lt;strong&gt;TCP is inappropriate for delay-sensitive streaming.&lt;&#x2F;strong&gt; Its guaranteed-delivery model creates unbounded latency during loss recovery. When a packet is lost, TCP will wait—potentially forever—rather than skip ahead. For a live video frame or audio sample, arriving late is the same as not arriving at all.&lt;&#x2F;p&gt;
&lt;p&gt;But “don’t use TCP” isn’t a complete answer. What &lt;em&gt;should&lt;&#x2F;em&gt; you use? The protocol landscape for delay-sensitive data is vast—spanning media streaming, industrial automation, robotics, financial messaging, and IoT. Each protocol answers the fundamental question differently.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Protocol Reference: Transport for Data with Deadlines</title>
        <published>2026-01-01T00:00:00+00:00</published>
        <updated>2026-01-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/protocol-reference/"/>
        <id>https://www.rationali.st/protocol-reference/</id>
        
        <summary type="html">&lt;p&gt;This is a quick-reference list of protocols for applications where &lt;strong&gt;data has a deadline, and late delivery is failure&lt;&#x2F;strong&gt;—whether that deadline is 10μs (servo loop), 20ms (audio buffer), or 300ms (video call).&lt;&#x2F;p&gt;
&lt;p&gt;For taxonomy, analysis, and context, see &lt;a href=&quot;&#x2F;delay-sensitive-protocols&#x2F;&quot;&gt;When Packets Can’t Wait&lt;&#x2F;a&gt;. This page is just the inventory—a living reference that grows as protocols become relevant to &lt;a href=&quot;&#x2F;categories&#x2F;jittertrap&#x2F;&quot;&gt;JitterTrap&lt;&#x2F;a&gt; development.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>The Jitter Cliff: When TCP Goes Chaotic</title>
        <published>2025-12-28T00:00:00+00:00</published>
        <updated>2025-12-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/tcp-jitter-cliff/"/>
        <id>https://www.rationali.st/tcp-jitter-cliff/</id>
        
        <summary type="html">&lt;p&gt;In &lt;a href=&quot;&#x2F;diagnosing-video-stuttering-over-tcp&#x2F;&quot;&gt;Part 1&lt;&#x2F;a&gt;, we used “video over TCP” as a stress test for TCP’s behavior—examining how zero-window events, retransmits, and the masking effect reveal what’s happening inside a struggling connection.&lt;&#x2F;p&gt;
&lt;p&gt;But during those experiments, I discovered TCP throughput degraded rapidly as jitter worsened. While I knew that packet loss would destroy TCP throughput, I hadn’t quite expected the jitter-induced cliff.&lt;&#x2F;p&gt;
&lt;p&gt;At a certain jitter threshold, throughput collapses so severely that measurements become unreliable. Single tests can vary by over 100%. This “chaos zone” makes diagnosis treacherous: the same network conditions can produce wildly different results depending on when you measure.&lt;&#x2F;p&gt;
&lt;p&gt;This post explores TCP’s behavior under jitter and loss, comparing CUBIC and BBR. It’s common knowledge that TCP is inappropriate for delay-sensitive streaming data, and this post will try to demonstrate how and why.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Diagnosing Video Stuttering Over TCP: A JitterTrap Investigation</title>
        <published>2025-12-25T00:00:00+00:00</published>
        <updated>2025-12-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/diagnosing-video-stuttering-over-tcp/"/>
        <id>https://www.rationali.st/diagnosing-video-stuttering-over-tcp/</id>
        
        <summary type="html">&lt;p&gt;Your security camera feed stutters. Your video call over the corporate VPN freezes. The question isn’t whether something is wrong—that’s obvious. The question is &lt;em&gt;what&lt;&#x2F;em&gt; is wrong, because the fix depends entirely on the diagnosis.&lt;&#x2F;p&gt;
&lt;p&gt;Is the problem the sender, the network, or the receiver? These require fundamentally different interventions. Telling someone to “upgrade their internet connection” when the real issue is their overloaded NVR is worse than useless—it wastes time and money while the actual problem persists.&lt;&#x2F;p&gt;
&lt;p&gt;Sender-side problems—where the source isn’t transmitting at the expected rate—are straightforward to detect: compare actual throughput to expected throughput. The harder question is distinguishing network problems from receiver problems when data &lt;em&gt;is&lt;&#x2F;em&gt; being sent. TCP’s built-in feedback mechanisms give us the answer.&lt;&#x2F;p&gt;
&lt;p&gt;UDP is the natural transport for real-time video—it tolerates loss gracefully and avoids head-of-line blocking. But video often ends up traveling over TCP whether we like it or not. VPN tunnels may encapsulate everything in TCP. Security cameras fall back to RTSP interleaved mode (RTP-over-TCP) when UDP is blocked. Some equipment simply doesn’t offer a choice.&lt;&#x2F;p&gt;
&lt;p&gt;The research question driving this investigation: &lt;strong&gt;Can we identify reliable TCP metrics that distinguish network problems from receiver problems?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Through controlled experiments, I found the answer is yes—with important caveats. This post builds a complete diagnostic framework covering all three problem types, with the experiments focused on the harder network-vs-receiver distinction. Part 2 will explore what happens when TCP goes chaotic.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>RFC 8362: Extended LSA Support for FRR ospf6d</title>
        <published>2025-12-24T00:00:00+00:00</published>
        <updated>2025-12-24T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/ospf6-elsa-implementation/"/>
        <id>https://www.rationali.st/ospf6-elsa-implementation/</id>
        
        <content type="html" xml:base="https://www.rationali.st/ospf6-elsa-implementation/">&lt;p&gt;This is a story about six months of work that didn’t ship, and what I’m still figuring out about why.&lt;&#x2F;p&gt;
&lt;p&gt;The surface story is technical: I implemented RFC 8362 Extended LSA support for FRR’s ospf6d. The code works. The tests pass. It wasn’t accepted upstream.&lt;&#x2F;p&gt;
&lt;p&gt;The deeper story is about what happens when you bring architectural instincts from one context into another, and they don’t translate. When your experience says one thing and the environment says another. When you’re left wondering whether your expertise is an asset or a liability in unfamiliar territory.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;Why_OSPFv3_Needed_Extended_LSAs&quot;&gt;Why OSPFv3 Needed Extended LSAs&lt;&#x2F;h1&gt;
&lt;p&gt;OSPFv3’s original extensibility mechanism was defining new LSA types—and this worked. TLV-based LSA types were added for Traffic Engineering (&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;datatracker.ietf.org&#x2F;doc&#x2F;html&#x2F;rfc5329&quot;&gt;RFC 5329&lt;&#x2F;a&gt;) and other features. But for the core LSA types (Router, Network, Inter-Area-Prefix, etc.), the format remained fixed C structs mapped directly to wire format.&lt;&#x2F;p&gt;
&lt;p&gt;This has limitations:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;64KB size limit&lt;&#x2F;strong&gt; — Large topologies with many prefixes can exceed this&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;No sub-structure in core LSAs&lt;&#x2F;strong&gt; — Metadata like Segment Routing SIDs can’t be cleanly attached to prefixes or adjacencies&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;datatracker.ietf.org&#x2F;doc&#x2F;html&#x2F;rfc8362&quot;&gt;RFC 8362&lt;&#x2F;a&gt; addresses this with Extended LSA versions of the core types, using TLV encoding:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt; 0                   1                   2                   3
&lt;&#x2F;span&gt;&lt;span&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;&#x2F;span&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;&#x2F;span&gt;&lt;span&gt;|              Type             |             Length            |
&lt;&#x2F;span&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;&#x2F;span&gt;&lt;span&gt;|                             Value...                          |
&lt;&#x2F;span&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;E-LSA is foundational for:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Segment Routing&lt;&#x2F;strong&gt; (&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;datatracker.ietf.org&#x2F;doc&#x2F;html&#x2F;rfc8666&quot;&gt;RFC 8666&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;datatracker.ietf.org&#x2F;doc&#x2F;html&#x2F;rfc9513&quot;&gt;RFC 9513&lt;&#x2F;a&gt;) — SID&#x2F;Label sub-TLVs attach to prefixes and adjacencies&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Flex-Algorithm&lt;&#x2F;strong&gt; (&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;datatracker.ietf.org&#x2F;doc&#x2F;html&#x2F;rfc9350&quot;&gt;RFC 9350&lt;&#x2F;a&gt;) — Algorithm-specific metrics and constraints via sub-TLVs&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Multi-Topology Routing&lt;&#x2F;strong&gt; (&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;datatracker.ietf.org&#x2F;doc&#x2F;html&#x2F;draft-ietf-ospf-mt-ospfv3-03&quot;&gt;draft-ietf-ospf-mt-ospfv3&lt;&#x2F;a&gt;) — Multiple independent topologies over shared infrastructure&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you’re deploying OSPFv3 with Segment Routing, you need E-LSA support.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;Two_Wire_Formats,_One_Codebase&quot;&gt;Two Wire Formats, One Codebase&lt;&#x2F;h1&gt;
&lt;p&gt;Here’s where it gets interesting—architecturally.&lt;&#x2F;p&gt;
&lt;p&gt;FRR’s ospf6d operates directly on wire-format structs. Algorithms cast the LSA body to the appropriate C struct and access fields directly. This is efficient: no parsing overhead, no intermediate representation, no allocation churn. For a single wire format, it’s a reasonable design.&lt;&#x2F;p&gt;
&lt;p&gt;But RFC 8362 introduces a second wire format for the same semantic data. Now you have two representations of “inter-area prefix”: the legacy fixed struct and the TLV-encoded E-LSA version. Every algorithm that touches LSA contents—SPF calculation, ABR summarization, ASBR external routes, flooding, show commands—needs to handle both.&lt;&#x2F;p&gt;
&lt;p&gt;The obvious approaches:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Duplicate everything.&lt;&#x2F;strong&gt; Write parallel code paths for legacy and E-LSA. Every function that processes Inter-Area-Prefix-LSA gets a sibling that processes E-Inter-Area-Prefix-LSA. This keeps the code style consistent but roughly doubles the surface area. Every bug fix, every enhancement, applied twice. Semantic drift over time as the paths evolve independently.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Abstract over the difference.&lt;&#x2F;strong&gt; Introduce an iterator or handler pattern that walks LSA contents uniformly, with format-specific parsing isolated behind it. Algorithms see one interface regardless of wire format. Less duplication, but new patterns the codebase doesn’t use elsewhere.&lt;&#x2F;p&gt;
&lt;p&gt;My instincts, shaped by years of systems work in other contexts, said: abstract. Don’t duplicate business logic. Isolate the format-specific stuff.&lt;&#x2F;p&gt;
&lt;p&gt;But it wasn’t just about this feature. The duplication approach would work, but it would leave the codebase harder to maintain than I found it. The next person adding TLV-based functionality faces the same problem, plus more code to navigate. The abstraction, if accepted, makes future work easier. Leave the place better than you found it—that’s the instinct.&lt;&#x2F;p&gt;
&lt;p&gt;There’s also consistency to consider. ospf6d already has TLV-based LSAs—Grace-LSA for graceful restart uses the same TLV header structure. The iterator pattern aligns E-LSA handling with existing TLV-based functionality rather than introducing something foreign.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;Iterator_Pattern_Across_9_LSA_Types&quot;&gt;Iterator Pattern Across 9 LSA Types&lt;&#x2F;h1&gt;
&lt;p&gt;We implemented all 9 E-LSA types from RFC 8362, using an iterator pattern. Commit &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;acooks&#x2F;frr&#x2F;commit&#x2F;b919740&quot;&gt;b919740&lt;&#x2F;a&gt; introduces the mechanism—the &lt;code&gt;foreach_lsdesc&lt;&#x2F;code&gt; iterator and handler registration. Commit &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;acooks&#x2F;frr&#x2F;commit&#x2F;589795c&quot;&gt;589795c&lt;&#x2F;a&gt; shows it applied to existing code, replacing direct struct access with handler callbacks.&lt;&#x2F;p&gt;
&lt;p&gt;The same iterator walks legacy LSA descriptors or E-LSA TLVs depending on LSA type. Format-specific parsing stays in handlers; algorithms see a uniform interface.&lt;&#x2F;p&gt;
&lt;p&gt;The scope of the work:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Metric&lt;&#x2F;th&gt;&lt;th&gt;Value&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Commits&lt;&#x2F;td&gt;&lt;td&gt;97&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Development time&lt;&#x2F;td&gt;&lt;td&gt;6.5 months&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Lines added&lt;&#x2F;td&gt;&lt;td&gt;27,293&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Lines removed&lt;&#x2F;td&gt;&lt;td&gt;1,786&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Net change&lt;&#x2F;td&gt;&lt;td&gt;+25,507 lines&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Files modified&lt;&#x2F;td&gt;&lt;td&gt;277&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Test code&lt;&#x2F;td&gt;&lt;td&gt;3,448 lines&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Test topologies&lt;&#x2F;td&gt;&lt;td&gt;7&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;The tests cover multi-area flooding, mixed legacy&#x2F;E-LSA interoperability, E-LSA-only networks, broadcast DR&#x2F;BDR election, and graceful restart. The code works. The approach handles the complexity.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;“Not_How_Things_Are_Done_Here”&quot;&gt;“Not How Things Are Done Here”&lt;&#x2F;h1&gt;
&lt;p&gt;The implementation went through three PR iterations:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;FRRouting&#x2F;frr&#x2F;pull&#x2F;16199&quot;&gt;PR #16199&lt;&#x2F;a&gt; — Initial approach&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;FRRouting&#x2F;frr&#x2F;pull&#x2F;16532&quot;&gt;PR #16532&lt;&#x2F;a&gt; — Revised approach&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;FRRouting&#x2F;frr&#x2F;pull&#x2F;17343&quot;&gt;PR #17343&lt;&#x2F;a&gt; — Final iteration&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The feedback: callback-based iteration is wrong for this codebase. Callbacks add indirection. They make code harder to follow, harder to debug. This isn’t how things are done here. The alternative: separate code paths for legacy and E-LSA.&lt;&#x2F;p&gt;
&lt;p&gt;I understood the concern. I’ve worked in codebases where callbacks created impenetrable spaghetti. The objection wasn’t unreasonable.&lt;&#x2F;p&gt;
&lt;p&gt;But I also saw the duplication path leading somewhere bad—a lot of nearly-identical code to maintain in sync forever. I asked for more specific guidance. What would an acceptable middle ground look like? The answer was conceptual rather than concrete: not callbacks, but no specific alternative I could implement.&lt;&#x2F;p&gt;
&lt;p&gt;I tried variations—inlining some callbacks, reducing indirection, restructuring the handler registration. None landed. Eventually the discussion ended without resolution.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;Was_I_Wrong,_or_Just_Unwelcome?&quot;&gt;Was I Wrong, or Just Unwelcome?&lt;&#x2F;h1&gt;
&lt;p&gt;Here’s what I’m still sitting with.&lt;&#x2F;p&gt;
&lt;p&gt;The code works. The tests pass. The iterator pattern handles both formats correctly across all 9 LSA types, SPF, ABR, ASBR, flooding, and graceful restart. We had confidence in the technical approach because we had working software proving it.&lt;&#x2F;p&gt;
&lt;p&gt;What I don’t know is whether my instincts were right for this context.&lt;&#x2F;p&gt;
&lt;p&gt;Maybe the duplication approach would have been fine—maybe the maintenance burden I imagined wouldn’t materialize, or would be acceptable to people who know this codebase better than I do. Maybe there’s a third approach I didn’t see because I was anchored on the patterns I knew. Maybe my years of “abstraction over duplication” instincts are a bias, not wisdom, in environments where those patterns aren’t established.&lt;&#x2F;p&gt;
&lt;p&gt;Or maybe I was right, and I just couldn’t communicate it effectively. Or maybe there’s no clean answer and the feature is just genuinely hard to add to this architecture. I don’t know.&lt;&#x2F;p&gt;
&lt;p&gt;What I do know is that experience from one context doesn’t transfer automatically to another. The instincts that made me effective elsewhere made me confident in an approach that wasn’t accepted here. Whether that’s because I was wrong, or because I failed to build consensus, or because the environment has different values—I genuinely can’t tell.&lt;&#x2F;p&gt;
&lt;p&gt;I’ve written a &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;acooks&#x2F;frr&#x2F;blob&#x2F;ELSA-devel-v8&#x2F;doc&#x2F;developer&#x2F;ospf6-elsa-tlv-analysis.md&quot;&gt;detailed analysis&lt;&#x2F;a&gt; comparing approaches, trying to work through the trade-offs. I still don’t have a clear answer.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;A_Working_Branch,_An_Open_Question&quot;&gt;A Working Branch, An Open Question&lt;&#x2F;h1&gt;
&lt;p&gt;The &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;acooks&#x2F;frr&#x2F;tree&#x2F;ELSA-devel-v8&quot;&gt;branch&lt;&#x2F;a&gt; works. It passes tests. It’s a complete reference implementation of RFC 8362 for ospf6d, if anyone wants it.&lt;&#x2F;p&gt;
&lt;p&gt;If you need E-LSA support in production today, &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;holo-routing&#x2F;holo&quot;&gt;Holo Routing&lt;&#x2F;a&gt; is a Rust-based routing suite with RFC 8362 support and active development. For FRR, the feature remains unimplemented.&lt;&#x2F;p&gt;
&lt;p&gt;This is one of those experiences that shaped me. I spent months pushing against a door that wasn’t going to open—not because the work was wrong, but because it wasn’t wanted in that form, in that place, at that time. The conclusion I came to: put your efforts where they’re welcomed, or go your own way.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;Acknowledgments&quot;&gt;Acknowledgments&lt;&#x2F;h1&gt;
&lt;p&gt;Thanks to Jake Lodge, who found and fixed countless bugs in this code. This implementation wouldn’t exist without his work and support.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Linux Network Configuration: A Decade Later</title>
        <published>2025-12-22T00:00:00+00:00</published>
        <updated>2025-12-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/linux-network-configuration-a-decade-later/"/>
        <id>https://www.rationali.st/linux-network-configuration-a-decade-later/</id>
        
        <summary type="html">&lt;p&gt;In 2014 I wrote about the &lt;a href=&quot;https:&#x2F;&#x2F;www.rationali.st&#x2F;linux-network-configuration&#x2F;&quot;&gt;state of Linux network configuration&lt;&#x2F;a&gt;, lamenting the proliferation of netlink libraries and how most projects hadn’t progressed past shell scripting and iproute2. I concluded that “there is a need for a good netlink library for one of the popular scripting languages.”&lt;&#x2F;p&gt;
&lt;p&gt;A decade later, that library exists. More importantly, the ecosystem has matured enough that every major language has a credible netlink option - and production systems are using them.&lt;&#x2F;p&gt;
&lt;p&gt;To compare them, I’ll use the same example throughout: create a bridge, a network namespace, and a veth pair connecting them.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>MCR: A High-Performance Userspace Multicast Relay</title>
        <published>2025-11-18T00:00:00+00:00</published>
        <updated>2025-11-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/mcr-a-high-performance-userspace-multicast-relay/"/>
        <id>https://www.rationali.st/mcr-a-high-performance-userspace-multicast-relay/</id>
        
        <content type="html" xml:base="https://www.rationali.st/mcr-a-high-performance-userspace-multicast-relay/">&lt;p&gt;In my &lt;a href=&quot;https:&#x2F;&#x2F;www.rationali.st&#x2F;the-curious-case-of-the-disappearing-multicast-packet&#x2F;&quot;&gt;previous post&lt;&#x2F;a&gt;, we hit a wall with the kernel’s multicast forwarding logic. The core issue was the &lt;strong&gt;RPF (Reverse Path Forwarding) check failure&lt;&#x2F;strong&gt; for sources on unroutable networks. While digging deeper into this, I found it’s a well-documented challenge in multicast literature, often called the &lt;strong&gt;“last-hop&#x2F;first-hop problem”&lt;&#x2F;strong&gt;. It describes the difficulty of bridging the gap between an isolated multicast source and the main network.&lt;&#x2F;p&gt;
&lt;p&gt;The investigation concluded that a userspace relay was the right approach. This post introduces &lt;strong&gt;MCR (Multicast Relay)&lt;&#x2F;strong&gt; (&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;acooks&#x2F;mcr&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;acooks&#x2F;mcr&lt;&#x2F;a&gt;), a specialized userspace relay built in Rust, as a modern, high-performance implementation of that architectural pattern. The project was built by myself, with the help of AI models Gemini and Claude. Its goal is to provide a robust solution to the first-hop problem where throughput and dynamic control are critical.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;Why_Userspace?_A_Necessary_Trade-off&quot;&gt;Why Userspace? A Necessary Trade-off&lt;&#x2F;h3&gt;
&lt;p&gt;Before diving into the architecture, it’s important to address a fundamental question: why move packet forwarding into userspace at all? For raw throughput, kernel-native forwarding is almost always faster. The decision to build a userspace relay was not driven by a desire for more performance, but by necessity, after concluding that kernel-level solutions were insufficient or impractical for this specific problem.&lt;&#x2F;p&gt;
&lt;p&gt;The core challenge is the kernel’s Reverse Path Forwarding (RPF) check. While it’s true that this check can be disabled on a per-interface basis (by setting the &lt;code&gt;rp_filter&lt;&#x2F;code&gt; sysctl to 0), this is often an inadequate solution for two key reasons:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;It’s a Blunt Instrument:&lt;&#x2F;strong&gt; Disabling &lt;code&gt;rp_filter&lt;&#x2F;code&gt; turns off a key anti-spoofing security mechanism for &lt;em&gt;all&lt;&#x2F;em&gt; traffic on that interface, not just the multicast stream in question. This is a security trade-off many administrators are unwilling to make.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;It Doesn’t Solve the Whole Problem:&lt;&#x2F;strong&gt; More importantly, simply disabling the RPF check on the first router doesn’t “clean” the packet. The packet is still forwarded with its original, unroutable source address, which will cause RPF failures on correctly configured downstream routers. It pushes the problem one hop further away without solving it.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;With the &lt;code&gt;rp_filter&lt;&#x2F;code&gt; approach being insufficient, we are left with other kernel-level mechanisms, which also have limitations:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No Inbound Multicast SNAT:&lt;&#x2F;strong&gt; Our previous investigation proved that it’s architecturally unsupported to use Netfilter’s &lt;code&gt;conntrack&lt;&#x2F;code&gt; to perform SNAT on inbound multicast, which would be the most direct way to “clean” the source address.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Static Forwarding Limits:&lt;&#x2F;strong&gt; Using direct control of the Multicast Forwarding Cache (MFC) works for routable sources, but the kernel has a hard-coded limit of 32 Virtual Interfaces (&lt;code&gt;MAXVIFS&lt;&#x2F;code&gt;), making it unsuitable for high-density scenarios.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Given these constraints, a targeted userspace application becomes the most practical way to solve the problem robustly at the network edge. It allows us to “launder” the packet once, making it fully compliant for the rest of its journey through the network. This trade-off—sacrificing the raw speed of the kernel for the correctness and flexibility of a userspace application—means that performance cannot be taken for granted and must be a central goal of the design.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;How_it_Works:_MCR’s_Architecture&quot;&gt;How it Works: MCR’s Architecture&lt;&#x2F;h3&gt;
&lt;p&gt;MCR’s architecture is designed to solve the RPF problem by bypassing the kernel’s IP stack and giving the operator direct, dynamic control over forwarding. It consists of two main components:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The Supervisor &amp;amp; Workers:&lt;&#x2F;strong&gt; The main &lt;code&gt;multicast_relay&lt;&#x2F;code&gt; process acts as a supervisor. It spawns high-performance “worker” processes, pinning each to a specific CPU core. These workers form the data plane, doing the actual packet processing.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The Control Plane:&lt;&#x2F;strong&gt; MCR is managed dynamically at runtime. A separate &lt;code&gt;control_client&lt;&#x2F;code&gt; tool communicates with the supervisor over a UNIX socket, allowing you to add, remove, and list forwarding rules without ever stopping the service.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This design separates the data plane, which is optimized for performance, from the control plane, which is optimized for flexible management.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;Data_Plane_Design_for_Performance&quot;&gt;Data Plane Design for Performance&lt;&#x2F;h4&gt;
&lt;p&gt;At its core, MCR operates on a simple but powerful principle: if the kernel won’t forward the packet, MCR will intercept it before the kernel has a chance to drop it. This is achieved through several key architectural decisions:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Unified &lt;code&gt;io_uring&lt;&#x2F;code&gt; Event Loop:&lt;&#x2F;strong&gt; At the heart of each worker is a single, unified event loop built on Linux’s most advanced I&#x2F;O interface, &lt;code&gt;io_uring&lt;&#x2F;code&gt;. This allows a single thread to manage both ingress and egress asynchronously with minimal syscall overhead, a design which avoids the need for complex and often slower cross-thread communication.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;AF_PACKET&lt;&#x2F;code&gt; for Raw Sockets:&lt;&#x2F;strong&gt; By operating at Layer 2, MCR sidesteps the kernel’s entire IP stack on the ingress path. This is the key to bypassing the RPF check and gives MCR full control over packet handling.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Efficient In-Memory Fan-Out:&lt;&#x2F;strong&gt; When a single input stream must be replicated to multiple outputs, MCR uses a reference-counted pointer (&lt;code&gt;Arc&amp;lt;[u8]&amp;gt;&lt;&#x2F;code&gt;) to share a single packet’s memory buffer across multiple send operations. This avoids expensive memory copies in userspace, a critical optimization for high-density replication scenarios.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Core-Local Buffer Pools:&lt;&#x2F;strong&gt; Each worker pre-allocates and manages its own memory buffers, a strategy aimed at avoiding the performance penalty of dynamic memory allocation in the fast path.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;While even higher performance might be achievable with technologies like eBPF&#x2F;XDP, the &lt;code&gt;AF_PACKET&lt;&#x2F;code&gt; and &lt;code&gt;io_uring&lt;&#x2F;code&gt; approach was chosen deliberately. It offers excellent performance while maintaining compatibility with a wider range of widely deployed enterprise Linux kernels, avoiding the tighter coupling to specific kernel versions and complex toolchain dependencies that an eBPF-based solution would entail.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;Example_Workflow&quot;&gt;Example Workflow&lt;&#x2F;h4&gt;
&lt;p&gt;A typical workflow looks like this: an operator starts the &lt;code&gt;multicast_relay&lt;&#x2F;code&gt; service and then uses the &lt;code&gt;control_client&lt;&#x2F;code&gt; to provision forwarding rules. For example, to relay a stream arriving on &lt;code&gt;eth0&lt;&#x2F;code&gt; to a new destination via &lt;code&gt;eth1&lt;&#x2F;code&gt;, the command is simple and declarative:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#ffffff;color:#323232;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;.&#x2F;control_client add-rule \
&lt;&#x2F;span&gt;&lt;span&gt;    --input-interface eth0 \
&lt;&#x2F;span&gt;&lt;span&gt;    --input-group 239.0.0.1 \
&lt;&#x2F;span&gt;&lt;span&gt;    --input-port 5001 \
&lt;&#x2F;span&gt;&lt;span&gt;    --output-interface eth1 \
&lt;&#x2F;span&gt;&lt;span&gt;    --output-group 239.0.0.2 \
&lt;&#x2F;span&gt;&lt;span&gt;    --output-port 6001
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This command instructs a worker to listen for multicast traffic for &lt;code&gt;239.0.0.1:5001&lt;&#x2F;code&gt; on &lt;code&gt;eth0&lt;&#x2F;code&gt; and re-transmit any received packets as a new stream to &lt;code&gt;239.0.0.2:6001&lt;&#x2F;code&gt; via &lt;code&gt;eth1&lt;&#x2F;code&gt;. The source address of the new stream is the relay server’s own, making it fully routable.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;What_About_socat?&quot;&gt;What About &lt;code&gt;socat&lt;&#x2F;code&gt;?&lt;&#x2F;h3&gt;
&lt;p&gt;For many network tasks, the versatile &lt;code&gt;socat&lt;&#x2F;code&gt; utility is an excellent choice. It can be used to solve the RPF problem by creating a userspace relay, and it’s important to understand the architectural and operational differences between that approach and MCR’s.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;Architectural_Difference:_Layer_2_vs._Layer_4&quot;&gt;Architectural Difference: Layer 2 vs. Layer 4&lt;&#x2F;h4&gt;
&lt;p&gt;The most significant difference is how each tool interacts with the network stack.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;socat&lt;&#x2F;code&gt; operates at Layer 4.&lt;&#x2F;strong&gt; It uses standard UDP sockets to receive and send packets. When it receives a packet, that packet has already been fully processed by the kernel’s network stack (checksums verified, IP headers parsed, etc.). &lt;code&gt;socat&lt;&#x2F;code&gt; then takes the payload and sends it out via another UDP socket, causing the kernel to build a new packet. This is a simple and robust model.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;MCR operates at Layer 2.&lt;&#x2F;strong&gt; As detailed in the architecture above, it uses &lt;code&gt;AF_PACKET&lt;&#x2F;code&gt; raw sockets to capture entire Ethernet frames directly from the network driver. This allows it to bypass the kernel’s IP stack and, crucially, the RPF check.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Both tools solve the RPF problem, but in different ways. &lt;code&gt;socat&lt;&#x2F;code&gt; cleverly uses “local delivery”—the kernel sees a local application is the destination and doesn’t apply the &lt;em&gt;forwarding&lt;&#x2F;em&gt; RPF check. MCR sidesteps the check entirely by intercepting the frame at a lower level.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;Operational_and_Feature_Differences&quot;&gt;Operational and Feature Differences&lt;&#x2F;h4&gt;
&lt;p&gt;The architectural differences lead to different operational models:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Process Model:&lt;&#x2F;strong&gt; &lt;code&gt;socat&lt;&#x2F;code&gt; is a point-to-point tool. To relay a single multicast stream, you run a single &lt;code&gt;socat&lt;&#x2F;code&gt; process. To relay ten streams, you must run ten separate &lt;code&gt;socat&lt;&#x2F;code&gt; processes, which can be cumbersome to manage at scale. MCR uses a supervisor model where a single daemon can manage hundreds of forwarding rules concurrently across its worker processes.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Head-End Replication:&lt;&#x2F;strong&gt; A key feature of MCR is its ability to perform “fan-out” or “head-end replication,” where a single input stream is replicated to multiple different outputs. &lt;code&gt;socat&lt;&#x2F;code&gt;’s “one-to-one” model cannot do this; it can only forward a stream from one point to another.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;Comparative_Example:_1-to-1_Relay&quot;&gt;Comparative Example: 1-to-1 Relay&lt;&#x2F;h4&gt;
&lt;p&gt;Let’s say we want to relay a stream from &lt;code&gt;239.1.1.1:5001&lt;&#x2F;code&gt; on &lt;code&gt;eth0&lt;&#x2F;code&gt; to &lt;code&gt;239.10.10.10:6001&lt;&#x2F;code&gt; on &lt;code&gt;eth1&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;With &lt;strong&gt;MCR&lt;&#x2F;strong&gt;, you would ensure the supervisor is running and then add a rule dynamically:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#ffffff;color:#323232;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;font-style:italic;color:#969896;&quot;&gt;# Add a rule to a running MCR instance
&lt;&#x2F;span&gt;&lt;span&gt;.&#x2F;control_client add-rule \
&lt;&#x2F;span&gt;&lt;span&gt;    --input-interface eth0 \
&lt;&#x2F;span&gt;&lt;span&gt;    --input-group 239.1.1.1 \
&lt;&#x2F;span&gt;&lt;span&gt;    --input-port 5001 \
&lt;&#x2F;span&gt;&lt;span&gt;    --output-interface eth1 \
&lt;&#x2F;span&gt;&lt;span&gt;    --output-group 239.10.10.10 \
&lt;&#x2F;span&gt;&lt;span&gt;    --output-port 6001
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With &lt;strong&gt;&lt;code&gt;socat&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;, you would launch a dedicated process for this specific task:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#ffffff;color:#323232;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;socat -u \
&lt;&#x2F;span&gt;&lt;span&gt;  UDP4-RECV:5001,ip-add-membership=239.1.1.1:eth0 \
&lt;&#x2F;span&gt;&lt;span&gt;  UDP4-SEND:239.10.10.10:6001,bind=10.1.1.2
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;em&gt;(Note: &lt;code&gt;socat&lt;&#x2F;code&gt; requires binding to the specific IP address of the egress interface, here assumed to be &lt;code&gt;10.1.1.2&lt;&#x2F;code&gt; on &lt;code&gt;eth1&lt;&#x2F;code&gt;.)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In summary, &lt;code&gt;socat&lt;&#x2F;code&gt; is an excellent tool for simple, static, one-to-one relay tasks. MCR is designed for more complex scenarios requiring high-density, dynamic rule management, and features like head-end replication.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;Measured_Performance&quot;&gt;Measured Performance&lt;&#x2F;h3&gt;
&lt;p&gt;To complement the architectural and operational comparison, I conducted a series of benchmarks. The full performance validation reports are available in the project’s documentation, but here’s a summary of the key findings:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Baseline Reliability (Layer 3 Routing):&lt;&#x2F;strong&gt; In a standard Layer 3 routing test (e.g., forwarding between &lt;code&gt;veth&lt;&#x2F;code&gt; pairs), both MCR and &lt;code&gt;socat&lt;&#x2F;code&gt; demonstrated &lt;strong&gt;100% packet delivery with 0% loss&lt;&#x2F;strong&gt; at moderate loads (e.g., 50,000 packets per second). For simple L3 scenarios, both tools are equally reliable.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Scalability Under High Load:&lt;&#x2F;strong&gt; Under a sustained load of &lt;strong&gt;400,000 packets per second&lt;&#x2F;strong&gt; in the same Layer 3 topology, MCR maintained excellent performance with a negligible &lt;strong&gt;0.2% packet loss&lt;&#x2F;strong&gt;, whereas &lt;code&gt;socat&lt;&#x2F;code&gt; dropped over &lt;strong&gt;15.3%&lt;&#x2F;strong&gt; of packets. This highlights MCR’s architectural advantage with &lt;code&gt;io_uring&lt;&#x2F;code&gt;’s batched I&#x2F;O, significantly reducing syscall overhead at high rates.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Architectural Suitability for L2 Bridging:&lt;&#x2F;strong&gt; A key use case for MCR is relaying traffic between logically separate Layer 2 domains, such as two different Linux bridges. In this scenario, &lt;strong&gt;MCR forwarded traffic with 0% loss, whereas &lt;code&gt;socat&lt;&#x2F;code&gt;, as a Layer 4 tool, is not designed to operate at this level and could not forward the traffic&lt;&#x2F;strong&gt;. This doesn’t represent a flaw in &lt;code&gt;socat&lt;&#x2F;code&gt;, but rather illustrates a foundational difference in capability: MCR is explicitly designed for L2 operations that are outside the scope of standard UDP socket-based tools.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Extreme Fan-out:&lt;&#x2F;strong&gt; MCR was successfully validated performing a &lt;strong&gt;1-to-50 head-end replication&lt;&#x2F;strong&gt;, where a single input stream was replicated to 50 unique outputs. This demonstrates MCR’s ability to bypass the kernel’s hard-coded &lt;code&gt;MAXVIFS&lt;&#x2F;code&gt; limit of 32 output interfaces for native multicast forwarding.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These results, gathered in a virtualized test environment, confirm MCR’s capabilities for scenarios demanding high throughput and architectural versatility. A comprehensive analysis of performance on physical hardware is planned for a future article.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;Why_Rust?&quot;&gt;Why Rust?&lt;&#x2F;h3&gt;
&lt;p&gt;Rust was chosen for MCR because its language features align well with the project’s goals. Its focus on memory safety and its ownership model help prevent common classes of bugs, like buffer overflows and use-after-free errors, which are particularly critical in networking applications that deal directly with raw memory buffers and low-level system calls. This allows the development focus to remain on the core logic, with the compiler providing strong guarantees against many potential vulnerabilities. Of course, operating at Layer 2 requires powerful capabilities (&lt;code&gt;CAP_NET_RAW&lt;&#x2F;code&gt;), which brings significant security responsibility; future work will explore strategies for dropping these privileges as soon as they are no longer needed.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;Conclusion:_The_Right_Tool_for_the_Job&quot;&gt;Conclusion: The Right Tool for the Job&lt;&#x2F;h3&gt;
&lt;p&gt;The journey from the “disappearing multicast packet” to MCR highlights a core engineering principle: when you hit a fundamental limitation in one layer of the system, an effective solution is often to move up a layer and build a more specialized tool.&lt;&#x2F;p&gt;
&lt;p&gt;MCR is that tool. It attempts to solve the specific, real-world problem of unroutable multicast sources by providing a manageable and high-performance userspace relay. It’s an exploration of how combining a deep understanding of the kernel’s boundaries with modern APIs and a language like Rust can create powerful and reliable solutions to complex networking challenges.&lt;&#x2F;p&gt;
&lt;p&gt;This approach of a lightweight, userspace-driven protocol that leverages the existing unicast network is not a new idea. It follows the design philosophy of protocols like SLIM (Self-configuring Lightweight Internet Multicast), which argued that the key to multicast adoption was to “nullify the management complexity” by avoiding multicast-specific infrastructure. MCR can be seen as a modern realization of that vision, implementing a similar architectural pattern with the high-performance, asynchronous tools available to us today.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;References&quot;&gt;References&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Hjálmtýsson, G., Brynjúlfsson, B., &amp;amp; Helgason, Ó. R. (2003). &lt;em&gt;Overcoming Last-Hop&#x2F;First-Hop Problems in IP Multicast&lt;&#x2F;em&gt;. Lecture Notes in Computer Science.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Hjálmtýsson, G., Brynjúlfsson, B., &amp;amp; Helgason, Ó. R. (2004). &lt;em&gt;Self-configuring lightweight Internet multicast protocol specification&lt;&#x2F;em&gt;. IEEE International Conference on Systems, Man and Cybernetics.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The Curious Case of the Disappearing Multicast Packet</title>
        <published>2025-11-02T00:00:00+00:00</published>
        <updated>2025-11-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/the-curious-case-of-the-disappearing-multicast-packet/"/>
        <id>https://www.rationali.st/the-curious-case-of-the-disappearing-multicast-packet/</id>
        
        <summary type="html">&lt;p&gt;Many consumer and industrial devices—from home security cameras to the HDMI-over-IP extenders I investigated in a &lt;a href=&quot;https:&#x2F;&#x2F;www.rationali.st&#x2F;jittertrap-baby-steps-in-dsp&#x2F;&quot;&gt;previous post&lt;&#x2F;a&gt;—are designed as simple appliances. They often have hard-coded, unroutable IP addresses (e.g., &lt;code&gt;192.168.1.100&lt;&#x2F;code&gt;) and expect to live on a simple, isolated network. This becomes a major problem when you need to get their multicast video or data streams from that isolated segment to users on a main LAN. My goal was to solve this with a standard Linux server, creating a simple, high-performance multicast router without a dedicated, expensive hardware box.&lt;&#x2F;p&gt;
&lt;p&gt;Can a standard Linux server act as a simple, kernel-native multicast router for devices with hard-coded, unroutable IP addresses? This article chronicles an investigation into combining &lt;code&gt;nftables&lt;&#x2F;code&gt; SNAT with direct control of the Multicast Forwarding Cache (MFC).&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>The DSP Lens on Real-World Events, Part 1: A New Dimension for Your Data</title>
        <published>2025-08-09T00:00:00+00:00</published>
        <updated>2025-08-09T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/the-dsp-lens-on-real-world-events-part-1-a-new-dimension-for-your-data/"/>
        <id>https://www.rationali.st/the-dsp-lens-on-real-world-events-part-1-a-new-dimension-for-your-data/</id>
        
        <summary type="html">&lt;h3 id=&quot;The_Universal_Challenge:_From_Events_to_Insight&quot;&gt;The Universal Challenge: From Events to Insight&lt;&#x2F;h3&gt;
&lt;p&gt;Every field that deals with streams of events over time shares a common challenge. A factory manager tracking items on a conveyor belt, a data scientist analyzing user clicks, and a network engineer monitoring packets are all trying to turn a series of discrete, often chaotic, events into meaningful insight. The tools may differ, but the fundamental problem is the same: how do we perceive the true nature of a system through the lens of the events it generates?&lt;&#x2F;p&gt;
&lt;p&gt;My own journey into this problem began with a network analysis tool, &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.jittertrap.net&quot;&gt;JitterTrap&lt;&#x2F;a&gt;. I was seeing things that shouldn’t exist: a prominent, slow-moving wave in my network throughput graph that my knowledge of the underlying system said couldn’t be real. This “ghost” in the machine forced me to look for answers in an unexpected place: the field of Digital Signal Processing (DSP).&lt;&#x2F;p&gt;
&lt;p&gt;This three-part series shares the lessons from that journey. Part 1 introduces the core DSP mental model as a powerful, universal framework. Part 2 lays out a theoretical framework for analysis, and Part 3 applies that framework to perform a rigorous, quantitative analysis of a real-world measurement problem.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>The DSP Lens on Real-World Events, Part 2: A Framework for Analysis</title>
        <published>2025-08-09T00:00:00+00:00</published>
        <updated>2025-08-09T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/the-dsp-lens-on-real-world-events-part-2-a-framework-for-analysis/"/>
        <id>https://www.rationali.st/the-dsp-lens-on-real-world-events-part-2-a-framework-for-analysis/</id>
        
        <summary type="html">&lt;h3 id=&quot;Introduction:_The_Rosetta_Stone&quot;&gt;Introduction: The Rosetta Stone&lt;&#x2F;h3&gt;
&lt;p&gt;In Part 1, we introduced the “ghost in the machine”—an aliased signal created by the very act of measuring a stream of discrete events. We established that any time-bucketed analysis is a form of filtering. But to truly understand our measurements and build a trustworthy instrument, we need a more formal framework.&lt;&#x2F;p&gt;
&lt;p&gt;This article is that framework—a “Rosetta Stone” to connect the different concepts at play. Before we can analyze our system quantitatively in Part 3, we must first understand the precise relationship between our sample rate, the frequencies we want to observe, and the characteristics of the filter we are unknowingly using.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>The DSP Lens on Real-World Events, Part 3: Building an Honest Instrument</title>
        <published>2025-08-09T00:00:00+00:00</published>
        <updated>2025-08-09T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/the-dsp-lens-on-real-world-events-part-3-building-an-honest-instrument/"/>
        <id>https://www.rationali.st/the-dsp-lens-on-real-world-events-part-3-building-an-honest-instrument/</id>
        
        <summary type="html">&lt;h3 id=&quot;Introduction:_Applying_the_Framework&quot;&gt;Introduction: Applying the Framework&lt;&#x2F;h3&gt;
&lt;p&gt;In Part 1, we introduced the DSP lens and the problem of aliasing. In Part 2, we built a detailed theoretical framework, our “Rosetta Stone,” connecting the concepts of sample rates, filters, and critical frequencies.&lt;&#x2F;p&gt;
&lt;p&gt;Now, in this final part, we apply that framework to a real-world scenario. We will use the specific parameters of the Jittertrap tool to perform a rigorous quantitative analysis of the system’s imperfections. This will allow us to determine, with numerical confidence, exactly what we can and cannot trust from our measurements, and ultimately, how to build a more honest instrument.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>A fresh coat of paint for Jittertrap</title>
        <published>2025-08-03T00:00:00+00:00</published>
        <updated>2025-08-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/a-fresh-coat-of-paint-for-jittertrap/"/>
        <id>https://www.rationali.st/a-fresh-coat-of-paint-for-jittertrap/</id>
        
        <content type="html" xml:base="https://www.rationali.st/a-fresh-coat-of-paint-for-jittertrap/">&lt;p&gt;My long-running project, Jittertrap, had been neglected for a long time… Mostly, because of a lack of interest.&lt;&#x2F;p&gt;
&lt;p&gt;However, I recently had need for it at my day job, and I dusted it off, wrote down a list of things I need to fix for it to be useful to me in that one particular use case, and decided to fix the things that embarrass me and implement the things that would improve my life.&lt;&#x2F;p&gt;
&lt;p&gt;The backend code is mostly fine. There are some tricky things about it, and I’ve been thinking about a rewrite in Rust and eBPF, but it mostly works.&lt;&#x2F;p&gt;
&lt;p&gt;The frontend, however, has always been a different story. It’s a web UI, and to be blunt, my interest in the nuances of JavaScript, CSS, and the ever-shifting sands of web frameworks is minimal. It’s necessary, but it’s not the work I enjoy.&lt;&#x2F;p&gt;
&lt;p&gt;For years, this meant the UI languished. It was functional, but it was built on an old version of Bootstrap and was riddled with the kind of minor, irritating bugs that I had little motivation to track down. The project stalled, not because the core idea was finished, but because it wasn’t fun, and it wasn’t appreciated. The uninteresting work had become a barrier to the interesting work. I decided that this is my opportunity to see what all the AI coding fuss is about.&lt;&#x2F;p&gt;
&lt;p&gt;I started using Gemini as a coding assistant, specifically to tackle the frontend. My goal was to offload the tedious parts - the parts I had no desire to master myself.&lt;&#x2F;p&gt;
&lt;p&gt;The results have been surprisingly good. Over the past month, I’ve managed to clear a huge backlog of UI debt. Here’s a quick rundown of the progress:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Modernized Frameworks:&lt;&#x2F;strong&gt; The entire UI has been migrated from Bootstrap 3 to Bootstrap 4. This was a significant undertaking that I’d been putting off for ages, and one milestone on the journey of removing the jQuery dependency. Because apparently all browsers are good now and jQuery is a liability.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Richer Charting Features:&lt;&#x2F;strong&gt; The charts have become more powerful for analysis. The “Top Talkers” chart now aggregates smaller, less relevant flows into a single “Other” category, making the visualization much cleaner. I also added a switch to toggle the Y-axis between logarithmic and linear scales, allowing for a more nuanced view of the data.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Improved IPv6 Support:&lt;&#x2F;strong&gt; The backend now properly handles IPv6 traffic. I fixed several bugs in the packet decoding logic for IPv6 addresses and extension headers, which is critical for monitoring modern networks.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Security Hardening:&lt;&#x2F;strong&gt; I took the time to harden the application by fixing several security vulnerabilities, including a potential XSS issue in the WebSocket handler and a memory leak in the backend C code.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Code Modernization:&lt;&#x2F;strong&gt; The JavaScript codebase has been brought into the modern era. All the old &lt;code&gt;var&lt;&#x2F;code&gt; declarations are gone, replaced by &lt;code&gt;let&lt;&#x2F;code&gt; and &lt;code&gt;const&lt;&#x2F;code&gt;, and I’ve been able to systematically replace old jQuery patterns and callbacks with native JS and arrow functions.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Performance Overhaul:&lt;&#x2F;strong&gt; The d3.js charts, which are central to Jittertrap, have been substantially optimized. We moved the “Top Talkers” chart to a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;&#x2F;code&gt; element, which dramatically improved rendering performance. The old &lt;code&gt;setInterval&lt;&#x2F;code&gt; loop was replaced with &lt;code&gt;requestAnimationFrame&lt;&#x2F;code&gt; for smoother, more efficient updates.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Responsive Layout:&lt;&#x2F;strong&gt; The UI is now properly responsive. The main container is fluid, and on wide screens, the throughput and top talkers charts now sit side-by-side, which is a much better use of the space.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Bug Fixes:&lt;&#x2F;strong&gt; And of course, a whole host of nagging bugs have been squashed, from layout issues with the chart legends to incorrect resizing behavior.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The interesting part of this process is how it has changed my relationship with my own project. I can now focus on the What and not get bogged down in the How of web development. I describe the desired outcome—“make the legend responsive,” “fix the grid resizing”—and the AI generates the code. It’s a powerful new tool, not unlike a compiler or a static analyzer, that handles a specific domain of work for me.&lt;&#x2F;p&gt;
&lt;p&gt;It’s a more efficient and, frankly, more enjoyable way to work on a passion project. The AI isn’t driving the project, but it’s clearing the road so I can.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Rapido - rapidly creating test VMs for driver development</title>
        <published>2023-11-25T00:00:00+00:00</published>
        <updated>2025-08-04T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/rapido-rapidly-creating-test-vms-for-driver-development/"/>
        <id>https://www.rationali.st/rapido-rapidly-creating-test-vms-for-driver-development/</id>
        
        <summary type="html">&lt;p&gt;Thanks to &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rapido-linux&#x2F;rapido&quot;&gt;rapido&lt;&#x2F;a&gt;, it’s become much simpler to test Linux device drivers for real PCIe devices in VMs.&lt;&#x2F;p&gt;
&lt;p&gt;The advantages of this approach are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;the host is protected from memory corruption errors caused by buggy kernel drivers&lt;&#x2F;li&gt;
&lt;li&gt;the PCI peripheral can be physically installed in a multi-use machine, reducing hardware &amp;amp; lab requirements&lt;&#x2F;li&gt;
&lt;li&gt;debugging info is easily available&lt;&#x2F;li&gt;
&lt;li&gt;the development cycle is short and simple - rapid even :)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>PMTU weirdness</title>
        <published>2023-03-11T00:00:00+00:00</published>
        <updated>2025-08-04T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/pmtu-weirdness/"/>
        <id>https://www.rationali.st/pmtu-weirdness/</id>
        
        <summary type="html">&lt;p&gt;On a good day, my day job involves building networking tools in python. Too many python networking tools look like shell scripts, spawning subprocesses for basic tools like ‘ping’ or ‘ip’ - often resulting in a fragile mess due to poor, or inconsistent error handling.&lt;&#x2F;p&gt;
&lt;p&gt;I was quite excited to find &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ValentinBELYN&#x2F;icmplib&quot;&gt;icmplib&lt;&#x2F;a&gt;. It provides a much simpler, less fragile way to do things like reachability tests, RTT measurements, path discovery and path MTU discovery in python code. Hopefully it finds its way into Fedora soon!&lt;&#x2F;p&gt;
&lt;p&gt;Armed with icmplib, I went on a journey of discovery to develop my understanding of Path MTU. I specifically wanted to understand the differences between IPv4 and IPv6, and the effect of &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;man7.org&#x2F;linux&#x2F;man-pages&#x2F;man4&#x2F;veth.4.html&quot;&gt;VETH&lt;&#x2F;a&gt;, VLAN and Bridge virtual devices.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Status update</title>
        <published>2017-12-03T00:00:00+00:00</published>
        <updated>2023-03-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/status-update/"/>
        <id>https://www.rationali.st/status-update/</id>
        
        <content type="html" xml:base="https://www.rationali.st/status-update/">&lt;p&gt;I’ll be speaking at &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;linux.org.au&#x2F;linux-conf-au&#x2F;&quot;&gt;linux.conf.au&lt;&#x2F;a&gt; 2018 in Sydney.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;acooks&#x2F;jittertrap&quot;&gt;Jittertrap&lt;&#x2F;a&gt; testing infrastructure is slowly improving. The idea is to include known test data so that the front-end rendering can be verified and refactored. I expect a sprint over the holidays and some new bugs for lca. :)&lt;&#x2F;p&gt;
&lt;p&gt;Clean up of the &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;acooks&#x2F;tn40xx-driver&quot;&gt;tn40xx driver&lt;&#x2F;a&gt; is continuing - hopefully towards mainline inclusion. The driver project gets more visitors and clones than Jittertrap, which I find interesting, unexpected and somewhat sobering.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Listing Network interface, MAC address and PCI bus&#x2F;device&#x2F;function</title>
        <published>2016-03-28T00:00:00+00:00</published>
        <updated>2016-03-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/listing-network-interface-mac-address-and-pci-bus-device-function/"/>
        <id>https://www.rationali.st/listing-network-interface-mac-address-and-pci-bus-device-function/</id>
        
        <content type="html" xml:base="https://www.rationali.st/listing-network-interface-mac-address-and-pci-bus-device-function/">&lt;p&gt;New hardware for &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.bufferupr.com&quot;&gt;BufferUpr&lt;&#x2F;a&gt; has arrived! I’m
working on adding Wifi capability into the product and with new hardware
comes new challenges.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Challenge:&lt;&#x2F;strong&gt;
Find a simple way to list all the network interfaces in the system, including
the PCIe slot and MAC address for each.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;find &#x2F;sys&#x2F;devices&#x2F;pci0000* \
&lt;&#x2F;span&gt;&lt;span&gt;-wholename &amp;quot;*net&#x2F;*&#x2F;address&amp;quot; \
&lt;&#x2F;span&gt;&lt;span&gt;-printf &amp;quot;%h&#x2F;%f &amp;quot; \
&lt;&#x2F;span&gt;&lt;span&gt;-exec cat &amp;#39;{}&amp;#39; \;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This depends on a proper &lt;code&gt;find&lt;&#x2F;code&gt;, of course, so Busybox won’t do.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Example output:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;&#x2F;sys&#x2F;devices&#x2F;pci0000:00&#x2F;0000:00:03.0&#x2F;0000:03:00.0&#x2F;net&#x2F;p3p0&#x2F;address 90:e2:ba:7f:97:00
&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;sys&#x2F;devices&#x2F;pci0000:00&#x2F;0000:00:03.0&#x2F;0000:03:00.1&#x2F;net&#x2F;p3p1&#x2F;address 90:e2:ba:7f:97:01
&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;sys&#x2F;devices&#x2F;pci0000:00&#x2F;0000:00:03.0&#x2F;0000:03:00.2&#x2F;net&#x2F;p3p2&#x2F;address 90:e2:ba:7f:97:02
&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;sys&#x2F;devices&#x2F;pci0000:00&#x2F;0000:00:03.0&#x2F;0000:03:00.3&#x2F;net&#x2F;p3p3&#x2F;address 90:e2:ba:7f:97:03
&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;sys&#x2F;devices&#x2F;pci0000:00&#x2F;0000:00:19.0&#x2F;net&#x2F;em1&#x2F;address 88:88:88:88:87:88
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Notice from the path to the address files that some of the interfaces on this system
are named according to their PCI BDF (bus&#x2F;device&#x2F;function) identifiers. For
example, the &lt;code&gt;p3p2&lt;&#x2F;code&gt; interface is bus 3, device 0, function 2.&lt;&#x2F;p&gt;
&lt;p&gt;From these BDF numbers, we can guess that there is a 4-port card in PCIe slot
3. However, it may not be easy to tell which physical connector on the motherboard maps
to slot 3. It might even involve screwdrivers and dust… &lt;em&gt;shudder&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;There are some more examples and a better explanation of device naming rules
&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;major.io&#x2F;2015&#x2F;08&#x2F;21&#x2F;understanding-systemds-predictable-network-device-names&#x2F;&quot;&gt;over
there&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If the disappearance of &lt;code&gt;eth0&lt;&#x2F;code&gt; is news to you and you’re frustrated that nobody asked you whether you wanted
interface naming fixed, you might want to read about &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;wiki&#x2F;Software&#x2F;systemd&#x2F;PredictableNetworkInterfaceNames&#x2F;&quot;&gt;why this change was
made&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;wiki.xenproject.org&#x2F;wiki&#x2F;Bus:Device.Function_(BDF)_Notation&quot;&gt;The Xen
wiki&lt;&#x2F;a&gt; has
a good page on BDF numbers and &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;PCI_configuration_space&quot;&gt;Wikipedia has more
detail&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Installing SciPy on Fedora 23</title>
        <published>2016-01-11T00:00:00+00:00</published>
        <updated>2016-01-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/installing-scipy-on-fedora-23/"/>
        <id>https://www.rationali.st/installing-scipy-on-fedora-23/</id>
        
        <content type="html" xml:base="https://www.rationali.st/installing-scipy-on-fedora-23/">&lt;p&gt;&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;getfedora.org&#x2F;&quot;&gt;Fedora 23&lt;&#x2F;a&gt; (the current release at the time of writing) ships an outdated version of &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.scipy.org&#x2F;&quot;&gt;SciPy&lt;&#x2F;a&gt; that doesn’t include the spectrogram function. Installing the latest Scipy was kind-of a pain, so I thought I’d record some instructions for future-me and share it with you.&lt;&#x2F;p&gt;
&lt;p&gt;These instructions install dependencies, set up a python virtualenv container and then install the latest &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;matplotlib.org&#x2F;&quot;&gt;matplotlib&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.scipy.org&#x2F;&quot;&gt;SciPy&lt;&#x2F;a&gt; and &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.numpy.org&#x2F;&quot;&gt;Numpy&lt;&#x2F;a&gt; on &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;getfedora.org&#x2F;&quot;&gt;Fedora 23&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Dependencies:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;python-virtualenv&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;libpng-devel freetype-devel pygobject2-devel&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;redhat-rpm-config&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;gcc-gfortran gcc-c++&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;atlas-devel lapack-devel&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Install these from Fedora repositories:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;sudo dnf install python-virtualenv libpng-devel freetype-devel pygobject2-devel redhat-rpm-config gcc-gfortran gcc-c++ atlas-devel lapack-devel
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And then the python packages:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;virtualenv env
&lt;&#x2F;span&gt;&lt;span&gt;source env&#x2F;bin&#x2F;activate
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;pip install -U pip &amp;amp;&amp;amp;
&lt;&#x2F;span&gt;&lt;span&gt;pip install numpy &amp;amp;&amp;amp;
&lt;&#x2F;span&gt;&lt;span&gt;pip install matplotlib &amp;amp;&amp;amp;
&lt;&#x2F;span&gt;&lt;span&gt;pip install scipy
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Installing numpy, matplotlib and scipy simultaneously fails. Thanks, pip.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>JitterTrap: Baby steps in DSP</title>
        <published>2015-12-01T00:00:00+00:00</published>
        <updated>2025-08-04T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/jittertrap-baby-steps-in-dsp/"/>
        <id>https://www.rationali.st/jittertrap-baby-steps-in-dsp/</id>
        
        <summary type="html">&lt;p&gt;This is the story of my first expensive lesson in Digital Signal Processing. It is about &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.jittertrap.net&quot;&gt;JitterTrap&lt;&#x2F;a&gt;, the free software that powers &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.jittertrap.net&#x2F;&quot;&gt;BufferUpr&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The premise of BufferUpr is to combine commodity hardware with open source software to create a product that can measure data stream delays of 1-100 milliseconds. This is the range of delay that most multimedia developers and consumers are concerned with.&lt;&#x2F;p&gt;
&lt;p&gt;To measure the delay, we count the packets and bytes as they fly past and look for changes in throughput. My naive logic was that if we look at consecutive intervals of 1ms, we’ll be able to measure the throughput over that interval, plot it and show any variation in throughput.&lt;&#x2F;p&gt;
&lt;p&gt;This seemed like a very simple task, except that I had no previous experience in Digital Signal Processing and didn’t see why that would be relevant.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Lightweight containers in Fedora using systemd</title>
        <published>2015-04-02T00:00:00+00:00</published>
        <updated>2015-04-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/lightweight-containers-in-fedora-using-systemd/"/>
        <id>https://www.rationali.st/lightweight-containers-in-fedora-using-systemd/</id>
        
        <content type="html" xml:base="https://www.rationali.st/lightweight-containers-in-fedora-using-systemd/">&lt;p&gt;It was the year of 2015 and people were still developing new applications in PHP… but for those who could no longer accept the idea of installing a system-wide LAMP stack, there was a new-old fassionable thing: Containers!&lt;&#x2F;p&gt;
&lt;p&gt;This is a quick howto for creating a throw-away container for messing around with PHP apps on Fedora. Most of it is taken from the examples at the bottom of ‘man 1 systemd-nspawn’&lt;&#x2F;p&gt;
&lt;p&gt;Create the directory for the container filesystem:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;[user@host]$ mkdir web-dev-container
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Install a minimal Fedora image (including apache and php) into the new container root:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;[user@host]$ sudo yum -y --releasever=21 --nogpg --installroot=$(pwd)&#x2F;web-dev-container --disablerepo=&amp;#39;*&amp;#39; --enablerepo=fedora install systemd passwd yum fedora-release less vim iproute httpd php
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Update the SELinux context:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;[user@host]$ sudo semanage fcontext -a -t svirt_sandbox_file_t &amp;quot;web-dev-container(&#x2F;.*)?&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;[user@host]$ sudo restorecon -R web-dev-container
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Spawn the new container:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;[user@host]$ sudo systemd-nspawn -D web-dev-container
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In the new container, change the root password:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;[root@container]# passwd
&lt;&#x2F;span&gt;&lt;span&gt;[root@container]# logout
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Boot the new container:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;[user@host]$ sudo systemd-nspawn -bD web-dev-container
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Enable and start the web server and check that it’s running:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;[root@container]# systemctl enable httpd.service
&lt;&#x2F;span&gt;&lt;span&gt;[root@container]# systemctl start httpd.service
&lt;&#x2F;span&gt;&lt;span&gt;[root@container]# ss -lt
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Check http:&#x2F;&#x2F;localhost in a web browser.&lt;&#x2F;p&gt;
&lt;p&gt;Add an index.html file:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;[root@container]# echo &amp;quot;Hello World&amp;quot; &amp;gt;&amp;gt; &#x2F;var&#x2F;www&#x2F;html&#x2F;index.html
&lt;&#x2F;span&gt;&lt;span&gt;[root@container]# echo &amp;quot;&amp;lt;?php echo \&amp;quot;Hello PHP World\&amp;quot; ?&amp;gt;&amp;quot; &amp;gt; &#x2F;var&#x2F;www&#x2F;html&#x2F;index.php
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Refresh http:&#x2F;&#x2F;localhost&lt;&#x2F;p&gt;
&lt;p&gt;Check http:&#x2F;&#x2F;localhost&#x2F;index.php&lt;&#x2F;p&gt;
&lt;p&gt;You can now logout of the container (it will keep running).&lt;&#x2F;p&gt;
&lt;p&gt;Change the ownership and hack away!&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;[user@host]$ sudo chown -R user.user web-dev-container&#x2F;var&#x2F;www&#x2F;html
&lt;&#x2F;span&gt;&lt;span&gt;[user@host]$ vim web-dev-container&#x2F;var&#x2F;www&#x2F;html&#x2F;index.php
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Login to the container, if needed:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;[user@host]$ sudo machinectl login web-dev-container
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Exit the container by pressing ^] three times in one second.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Using policy routing to loopback over external interfaces</title>
        <published>2015-04-01T00:00:00+00:00</published>
        <updated>2015-04-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/using-policy-routing-to-loopback-over-external-interfaces/"/>
        <id>https://www.rationali.st/using-policy-routing-to-loopback-over-external-interfaces/</id>
        
        <content type="html" xml:base="https://www.rationali.st/using-policy-routing-to-loopback-over-external-interfaces/">&lt;p&gt;Sometimes there is good reason to talk to yourself. You might be doing a sound check, for example.&lt;&#x2F;p&gt;
&lt;p&gt;Likewise, it can be useful to route IP packets between two interfaces on the same machine using an external path. One reason to do this is to test other network devices like routers or switches.&lt;&#x2F;p&gt;
&lt;p&gt;For many years this has been much trickier to do than it might seem, until &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;torvalds&#x2F;linux&#x2F;commit&#x2F;8153a10c08f1312af563bb92532002e46d3f504a&quot;&gt;Patrick McHardy’s introduction&lt;&#x2F;a&gt; of the &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.kernel.org&#x2F;doc&#x2F;Documentation&#x2F;networking&#x2F;ip-sysctl.txt&quot;&gt;‘accept_local’ setting&lt;&#x2F;a&gt; in December 2009.&lt;&#x2F;p&gt;
&lt;p&gt;A little more than a year after that, Kirill Smelkov &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.spinics.net&#x2F;lists&#x2F;netdev&#x2F;msg152621.html&quot;&gt;replied with a functional example script&lt;&#x2F;a&gt; that shows how to achieve full loopback bi-directional IP traffic over physical interfaces. See also this &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;pkorpine&#x2F;dda468d25416b7553cc589423c910f2e&quot;&gt;GitHub gist&lt;&#x2F;a&gt; with a similar script.&lt;&#x2F;p&gt;
&lt;p&gt;Even so, there are numerous red herring posts on StackOverflow and elsewhere that don’t really help:
&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;13095909&#x2F;using-netcat-for-external-loop-back-test-between-two-ports&quot;&gt;1&lt;&#x2F;a&gt;
&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;serverfault.com&#x2F;questions&#x2F;127636&#x2F;force-local-ip-traffic-to-an-external-interface&quot;&gt;2&lt;&#x2F;a&gt;
&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;unix.stackexchange.com&#x2F;questions&#x2F;122050&#x2F;send-traffic-to-self-over-physical-network-on-ubuntu&quot;&gt;3&lt;&#x2F;a&gt;
&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;22725112&#x2F;send-traffic-to-self-over-physical-network-on-ubuntu&quot;&gt;4&lt;&#x2F;a&gt;
&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;2734144&#x2F;linux-disable-using-loopback-and-send-data-via-wire-between-2-eth-cards-of-one&quot;&gt;5&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;So, how does it work? Policy Routing, that’s how!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Basic_preparation:&quot;&gt;Basic preparation:&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;em&gt;If you have not already done so, step 0 is to tell NetworkManager to leave the interfaces alone!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Select the interface names and addresses:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;IFACE_A=p3p1
&lt;&#x2F;span&gt;&lt;span&gt;IFACE_B=p3p4
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;IFACE_A_IP=&amp;quot;10.10.3.1&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;IFACE_B_IP=&amp;quot;10.10.3.4&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Assign addresses:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;ip addr replace $IFACE_A_IP dev $IFACE_A
&lt;&#x2F;span&gt;&lt;span&gt;ip addr replace $IFACE_B_IP dev $IFACE_B
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Ensure the links are up:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;ip link change $IFACE_A up
&lt;&#x2F;span&gt;&lt;span&gt;ip link change $IFACE_B up
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;Policy_Routing&quot;&gt;Policy Routing&lt;&#x2F;h2&gt;
&lt;p&gt;Enable receiving of local source addresses on external ifaces (see Documentation&#x2F;networking&#x2F;ip-sysctl.txt in the kernel source tree).&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;sysctl -w net.ipv4.conf.$IFACE_A.accept_local=1
&lt;&#x2F;span&gt;&lt;span&gt;sysctl -w net.ipv4.conf.$IFACE_B.accept_local=1
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Decrease priority of the ‘local’ routing table (change preference from 0 to 1000):&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;ip rule del pref    0 lookup local
&lt;&#x2F;span&gt;&lt;span&gt;ip rule add pref 1000 lookup local
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Prevent routing loop by using preference rules to route incoming packets using ‘local’ table.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;ip rule add pref 100 iif $IFACE_A lookup local
&lt;&#x2F;span&gt;&lt;span&gt;ip rule add pref 101 iif $IFACE_B lookup local
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Use a routing table for each interface, that is, destination policy routing:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;ip route add default dev $IFACE_A table 200
&lt;&#x2F;span&gt;&lt;span&gt;ip route add default dev $IFACE_B table 201
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;em&gt;Note: There’s no need to create the tables first, adding a route entry is sufficient.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Add preference rules to route each destination using a coresponding table:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;ip rule add pref 200 to ${IFACE_B_IP} lookup 200 # IFACE_A to IFACE_B
&lt;&#x2F;span&gt;&lt;span&gt;ip rule add pref 201 to ${IFACE_A_IP} lookup 201
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;em&gt;Note: Do not be confused by the numbers. ‘pref 200’ refers to the preference&#x2F;priority of the rule, whereas ‘lookup 200’ refers to table number 200.
The kernel only cares about the number, but ‘ip’ will use the optional number-to-name mapping defined in &#x2F;etc&#x2F;iproute2&#x2F;rt_tables.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Finally, flush the route cache to ensure the tables are reread:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;ip route flush cache
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Check the routing &lt;em&gt;rules&lt;&#x2F;em&gt;:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;ip rule show
&lt;&#x2F;span&gt;&lt;span&gt;100:	from all iif p3p1 lookup local 
&lt;&#x2F;span&gt;&lt;span&gt;101:	from all iif p3p4 lookup local 
&lt;&#x2F;span&gt;&lt;span&gt;200:	from all to 10.10.3.4 lookup 200 
&lt;&#x2F;span&gt;&lt;span&gt;201:	from all to 10.10.3.1 lookup 201 
&lt;&#x2F;span&gt;&lt;span&gt;1000:	from all lookup local 
&lt;&#x2F;span&gt;&lt;span&gt;32766:	from all lookup main 
&lt;&#x2F;span&gt;&lt;span&gt;32767:	from all lookup default
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Test that the traffic is routed via the external interface using ‘tcpdump’ and ‘ping’:&lt;&#x2F;p&gt;
&lt;p&gt;tcpdump:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;# tcpdump -nlq -i p3p1 -c 6
&lt;&#x2F;span&gt;&lt;span&gt;tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
&lt;&#x2F;span&gt;&lt;span&gt;listening on p3p1, link-type EN10MB (Ethernet), capture size 262144 bytes
&lt;&#x2F;span&gt;&lt;span&gt;15:51:00.965010 IP 10.10.3.1 &amp;gt; 10.10.3.4: ICMP echo request, id 27352, seq 1, length 64
&lt;&#x2F;span&gt;&lt;span&gt;15:51:00.965217 IP 10.10.3.4 &amp;gt; 10.10.3.1: ICMP echo reply, id 27352, seq 1, length 64
&lt;&#x2F;span&gt;&lt;span&gt;15:51:01.964694 IP 10.10.3.1 &amp;gt; 10.10.3.4: ICMP echo request, id 27352, seq 2, length 64
&lt;&#x2F;span&gt;&lt;span&gt;15:51:01.964825 IP 10.10.3.4 &amp;gt; 10.10.3.1: ICMP echo reply, id 27352, seq 2, length 64
&lt;&#x2F;span&gt;&lt;span&gt;15:51:02.964785 IP 10.10.3.1 &amp;gt; 10.10.3.4: ICMP echo request, id 27352, seq 3, length 64
&lt;&#x2F;span&gt;&lt;span&gt;15:51:02.964987 IP 10.10.3.4 &amp;gt; 10.10.3.1: ICMP echo reply, id 27352, seq 3, length 64
&lt;&#x2F;span&gt;&lt;span&gt;6 packets captured
&lt;&#x2F;span&gt;&lt;span&gt;6 packets received by filter
&lt;&#x2F;span&gt;&lt;span&gt;0 packets dropped by kernel
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;ping:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;$ ping -c 3 10.10.3.4
&lt;&#x2F;span&gt;&lt;span&gt;PING 10.10.3.4 (10.10.3.4) 56(84) bytes of data.
&lt;&#x2F;span&gt;&lt;span&gt;64 bytes from 10.10.3.4: icmp_seq=1 ttl=64 time=0.247 ms
&lt;&#x2F;span&gt;&lt;span&gt;64 bytes from 10.10.3.4: icmp_seq=2 ttl=64 time=0.183 ms
&lt;&#x2F;span&gt;&lt;span&gt;64 bytes from 10.10.3.4: icmp_seq=3 ttl=64 time=0.243 ms
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;--- 10.10.3.4 ping statistics ---
&lt;&#x2F;span&gt;&lt;span&gt;3 packets transmitted, 3 received, 0% packet loss, time 1999ms
&lt;&#x2F;span&gt;&lt;span&gt;rtt min&#x2F;avg&#x2F;max&#x2F;mdev = 0.183&#x2F;0.224&#x2F;0.247&#x2F;0.031 ms
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Thats it! There’s no need to mess around with ARP or reverse path filtering!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>DSCP vs Linux socket priorities</title>
        <published>2014-09-27T00:00:00+00:00</published>
        <updated>2014-09-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/dscp-vs-linux-socket-priorities/"/>
        <id>https://www.rationali.st/dscp-vs-linux-socket-priorities/</id>
        
        <summary type="html">&lt;p&gt;I received some encouraging &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;plus.google.com&#x2F;106809740510707559932&#x2F;posts&#x2F;DcXxHWwdG3S&quot;&gt;comments on G+&lt;&#x2F;a&gt;
from Jesper Dangaard Brouer about my
&lt;a href=&quot;https:&#x2F;&#x2F;www.rationali.st&#x2F;looking-into-dscp-and-ieee-802-1p-vlan-priorities&#x2F;&quot;&gt;previous post&lt;&#x2F;a&gt;
on DSCP, Linux and VLAN priorities. Those comments and the work linked to
(&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;patchwork.ozlabs.org&#x2F;patch&#x2F;389392&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;)
points to a few long-standing (but minor) issues with the way DSCP priorities
are handled in Linux.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Some DSCP values, like Expedited Forwarding, are not currently (3.17 and earlier) handled correctly.&lt;&#x2F;li&gt;
&lt;li&gt;Linux Priorities, defined in &lt;em&gt;include&#x2F;uapi&#x2F;linux&#x2F;pkt_sched.h&lt;&#x2F;em&gt;, are not
documented particularly well, but forms part of the stable interface with
userspace. Working with traffic classification (tc), queuing disciplines
(qdisc) or VLANs requires at least a basic understanding of Linux socket
priorities.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Looking into DSCP and IEEE 802.1p (VLAN priorities).</title>
        <published>2014-09-17T00:00:00+00:00</published>
        <updated>2014-09-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/looking-into-dscp-and-ieee-802-1p-vlan-priorities/"/>
        <id>https://www.rationali.st/looking-into-dscp-and-ieee-802-1p-vlan-priorities/</id>
        
        <summary type="html">&lt;p&gt;I recently discovered a flaw in the VLAN implementation I did at work.
It seemed that the normal TCP traffic had the correct VLAN priorities
applied, but audio streaming UDP traffic did not.&lt;&#x2F;p&gt;
&lt;p&gt;This was due to DSCP being applied to the streaming audio and the fact
that the VLAN device’s egress-qos-map was incorrect.&lt;&#x2F;p&gt;
&lt;p&gt;I had assumed, incorrectly, that VLAN priorities are applied to all
traffic as long as we’re not using fancy queuing disciplines (qdiscs).
After all, 802.1Q is strictly a layer 2 thing.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Linux Network Configuration</title>
        <published>2014-08-12T00:00:00+00:00</published>
        <updated>2014-08-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/linux-network-configuration/"/>
        <id>https://www.rationali.st/linux-network-configuration/</id>
        
        <content type="html" xml:base="https://www.rationali.st/linux-network-configuration/">&lt;p&gt;This concerns the proliferation of netlink libraries and a lack
of direction and documentation.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;Background:&quot;&gt;Background:&lt;&#x2F;h1&gt;
&lt;p&gt;I’ve configured a router with &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;wiki.linuxfoundation.org&#x2F;networking&#x2F;netem&quot;&gt;netem&lt;&#x2F;a&gt; &lt;em&gt;(see &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.opensourceforu.com&#x2F;2012&#x2F;06&#x2F;bandwidth-throttling-netem-network-emulation&#x2F;&quot;&gt;Bandwidth Throttling with NetEM Network Emulation&lt;&#x2F;a&gt; and the &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;man7.org&#x2F;linux&#x2F;man-pages&#x2F;man8&#x2F;tc-netem.8.html&quot;&gt;tc-netem man page&lt;&#x2F;a&gt;)&lt;&#x2F;em&gt; to test &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.tieline.com.au&quot;&gt;Tieline&lt;&#x2F;a&gt; devices under various delay and loss network conditions.&lt;&#x2F;p&gt;
&lt;p&gt;It’s not really feasible for the tester to use ‘tc’ on the command line for the various tests, so my simple requirement is to build a little web interface. It’s really easy to hack something together quickly (parsing the output of iproute2), but I’ve been developing a bit of an allergy for this kind of hacking things together.&lt;&#x2F;p&gt;
&lt;p&gt;I’d like to use a scripting language (Python or Perl, not C) for the webby stuff, so I started looking at what modules these languages provide for netlink, which seems like a better idea than parsing output of “tc” and “ip”. I couldn’t find anything that plays well with netem, but the python bindings for libnl seems to be closest to what I need.&lt;&#x2F;p&gt;
&lt;p&gt;This led me to look at the larger landscape for userspace netlink libraries…&lt;&#x2F;p&gt;
&lt;h1 id=&quot;Existing_Netlink_Libraries:&quot;&gt;Existing Netlink Libraries:&lt;&#x2F;h1&gt;
&lt;p&gt;There are a few different userspace netlink “libraries”, with somewhat different goals. The libnl &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.infradead.org&#x2F;~tgr&#x2F;libnl&#x2F;doc&#x2F;route.html&quot;&gt;documentation&lt;&#x2F;a&gt; makes the distinction between the different subsystems clear in this diagram:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;www.infradead.org&#x2F;~tgr&#x2F;libnl&#x2F;layer_diagram.png&quot; alt=&quot;libnl netlink subsystem diagram&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.infradead.org&#x2F;~tgr&#x2F;libnl&#x2F;&quot;&gt;libnl&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;seems to be aimed at general use,&lt;&#x2F;li&gt;
&lt;li&gt;includes Python bindings,&lt;&#x2F;li&gt;
&lt;li&gt;used by NetworkManager, libvirt, libpcap, etc.&lt;&#x2F;li&gt;
&lt;li&gt;decent documentation.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;However, libnl is not the only netlink library in use.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.netfilter.org&#x2F;projects&#x2F;libmnl&#x2F;&quot;&gt;libmnl&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;available for general use.&lt;&#x2F;li&gt;
&lt;li&gt;used by nftables&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.linuxfoundation.org&#x2F;collaborate&#x2F;workgroups&#x2F;networking&#x2F;iproute2&quot;&gt;libnetlink&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;internal to iproute2, not for general use.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;systemd&#x2F;systemd&#x2F;tree&#x2F;main&#x2F;src&#x2F;libsystemd&#x2F;sd-netlink&quot;&gt;sd-netlink&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;internal to systemd-networkd, not for general use.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h1 id=&quot;Network_Configuration_Managers&quot;&gt;Network Configuration Managers&lt;&#x2F;h1&gt;
&lt;p&gt;Lets look at this from another angle: what do the typical network managers &#x2F; configuration applications use?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;wiki.gnome.org&#x2F;Projects&#x2F;NetworkManager&quot;&gt;NetworkManager&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;mature&lt;&#x2F;li&gt;
&lt;li&gt;maintained&lt;&#x2F;li&gt;
&lt;li&gt;uses libnl for direct netlink comms&lt;&#x2F;li&gt;
&lt;li&gt;C&lt;&#x2F;li&gt;
&lt;li&gt;provides DBus interface&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;ConnMan&quot;&gt;ConnMan&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;maturity &#x2F; maintenance concerns&lt;&#x2F;li&gt;
&lt;li&gt;C&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;launchpad.net&#x2F;wicd&quot;&gt;Wicd&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;unmaintained&lt;&#x2F;li&gt;
&lt;li&gt;Python&lt;&#x2F;li&gt;
&lt;li&gt;calls external tools like ifconfig&lt;&#x2F;li&gt;
&lt;li&gt;provides DBus interface&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gitlab.archlinux.org&#x2F;archlinux&#x2F;netctl&quot;&gt;netctl&lt;&#x2F;a&gt; (&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;archlinux.org&#x2F;packages&#x2F;extra&#x2F;any&#x2F;netctl&#x2F;&quot;&gt;Arch package&lt;&#x2F;a&gt;):&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;maintained&lt;&#x2F;li&gt;
&lt;li&gt;Bash&lt;&#x2F;li&gt;
&lt;li&gt;calls external tools like iproute2, etc.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;wiki.openwrt.org&#x2F;doc&#x2F;techref&#x2F;netifd&quot;&gt;netifd (OpenWRT)&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;active &#x2F; maintained&lt;&#x2F;li&gt;
&lt;li&gt;C&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;systemd&#x2F;systemd&#x2F;tree&#x2F;main&#x2F;src&#x2F;libsystemd&#x2F;sd-netlink&quot;&gt;systemd-networkd&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;active &#x2F; maintained&lt;&#x2F;li&gt;
&lt;li&gt;C&lt;&#x2F;li&gt;
&lt;li&gt;uses internal sd-rtnl&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;vyos&#x2F;vyconfd&quot;&gt;vyconfd&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;maintenance concerns after vyatta sold to brocade.&lt;&#x2F;li&gt;
&lt;li&gt;Python&lt;&#x2F;li&gt;
&lt;li&gt;external iproute2, etc. tools?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;lnst-project.org&#x2F;&quot;&gt;LNST&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Python&lt;&#x2F;li&gt;
&lt;li&gt;external tools or NetworkManager via DBus&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;fedorahosted.org&#x2F;netcf&#x2F;&quot;&gt;netcf&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;tries to bridge distro-specific network config files with its own and provides a C library.&lt;&#x2F;li&gt;
&lt;li&gt;does not seem to modify the state directly.&lt;&#x2F;li&gt;
&lt;li&gt;seems to be used by virsh&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;ncfg &lt;em&gt;(slides no longer available)&lt;&#x2F;em&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;does it, or will it matter?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;em&gt;I also fell down the rabbit warren of looking what the various SDN and router OS projects are doing - and I’m still recovering from the experience - so I’ll write about that when I have a better understanding of it.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;Back_to_solving_the_problem:&quot;&gt;Back to solving the problem:&lt;&#x2F;h1&gt;
&lt;p&gt;Remember that the initial problem was how to configure Netem.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Option 1: the quick and easy hack. This is what I ended up doing at work, for now, because it’s that kind of place.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;exec tc&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;em&gt;Option 2:&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Use a network manager with a dbus interface. Both NetworkManager and Wicd can do this.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;em&gt;Alternative 2:&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Use libnl’s python bindings.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h1 id=&quot;Conclusion&quot;&gt;Conclusion&lt;&#x2F;h1&gt;
&lt;p&gt;Basically, many projects for network configuration have not progressed past shell scripting and iproute2 - some kind of  lowest common denominator - and make a large effort to tie individual tools together into something with a semi-coherent interface. I believe there is a need for a good netlink library for one of the popular scripting languages, so that we can move past the old crufty shell scripts and parsing output of stand-alone tools.&lt;&#x2F;p&gt;
&lt;p&gt;On the other hand, many of these basic tools have implemented their own abstractions for using the netlink kernel interface. A Grand Unified Netlink Userspace certainly seems like it would be an improvement over the status quo, but each netlink library probably exists for good reason and a merger seems unlikely.&lt;&#x2F;p&gt;
&lt;p&gt;libnl seems like the natural choice for mere mortals. I’ll post some examples as I learn more about it.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;See_also&quot;&gt;See also&lt;&#x2F;h1&gt;
&lt;p&gt;&lt;del&gt;Video: Can Linux network configuration suck less?&lt;&#x2F;del&gt; &lt;em&gt;(link dead)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;data.pavlix.net&#x2F;fosdem&#x2F;2013&#x2F;pavel-simerda-networking.pdf&quot;&gt;Slides&lt;&#x2F;a&gt;
&lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;wiki.openwrt.org&#x2F;doc&#x2F;techref&#x2F;netifd&quot;&gt;OpenWRT comparison of network managers&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Plain text email attachments.</title>
        <published>2014-07-24T00:00:00+00:00</published>
        <updated>2014-07-24T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/plain-text-email-attachments/"/>
        <id>https://www.rationali.st/plain-text-email-attachments/</id>
        
        <content type="html" xml:base="https://www.rationali.st/plain-text-email-attachments/">&lt;p&gt;After finally completing task 01 of the &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;eudyptula-challenge.org&#x2F;&quot;&gt;Eudyptula Challenge&lt;&#x2F;a&gt;, I’d like to share a few things I’ve learned, without divulging any crucial details about the task or solution.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;tl;dr&lt;&#x2F;strong&gt;
Pay attention.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;Patience…&quot;&gt;Patience…&lt;&#x2F;h1&gt;
&lt;p&gt;Maybe it’s just the timezone, but the turn-around time for a response to a submission meant that I could only send one message per day. That fits with my experience on the netdev and iommu mailing lists.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;Paying_attention_to_detail.&quot;&gt;Paying attention to detail.&lt;&#x2F;h1&gt;
&lt;p&gt;Having a longer turn-around time really discourages small, quick changes and “is it good enough now?” messages. This is a Good Thing. It makes you think about what you’re doing. It reinforces the concept that it’s good to take as long as you need to do things properly and double check your work, because submitting rubbish will waste a lot of your own time, not just reviewer time.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;Pay_even_more_attention_to_detail,_or,_email_clients_suck.&quot;&gt;Pay even more attention to detail, or, email clients suck.&lt;&#x2F;h1&gt;
&lt;p&gt;It took me four attempts to complete the first task. It’s embarrassing.&lt;&#x2F;p&gt;
&lt;p&gt;The task involves sending plain text attachments, as opposed to base64 encoded attachments. To achieve this, I first went through some effort to get this domain up and running and get back some of the control over email that gmail doesn’t provide. Then, I thought I’d use Alpine as a safe bet for an email client, but it turns out Alpine &lt;em&gt;always&lt;&#x2F;em&gt; uses base64 encoding for attachments. Sigh…&lt;&#x2F;p&gt;
&lt;p&gt;So, for the first attempt I submitted a patch instead of a simple attachment. I thought I could cheat, just a little, and avoid a problem with base64 encoded attachments. Unfortunately the script wasn’t expecting it and by submitting an unexpected result I wasted my time as well as my anonymous teacher’s time.&lt;&#x2F;p&gt;
&lt;p&gt;So, I switched to Thunderbird.&lt;&#x2F;p&gt;
&lt;p&gt;The second attempt was rejected, because I didn’t read the requirement properly. I won’t go into more details, but doh!&lt;&#x2F;p&gt;
&lt;p&gt;The third attempt… this is where the email hiccup came in. For the second attempt I carefully tested that Thunderbird was creating plain text attachments, as follows:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#ffffff;color:#323232;&quot;&gt;&lt;code&gt;&lt;span&gt;--------------040002060807090301030305
&lt;&#x2F;span&gt;&lt;span&gt;Content-Type: text&#x2F;plain; charset=UTF-8;
&lt;&#x2F;span&gt;&lt;span&gt; name=&amp;quot;Makefile&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;Content-Transfer-Encoding: 7bit
&lt;&#x2F;span&gt;&lt;span&gt;Content-Disposition: attachment;
&lt;&#x2F;span&gt;&lt;span&gt; filename=&amp;quot;Makefile&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It turns out that the character encoding of the email influences what Thunderbird does with the attachment. Of course I didn’t know that and I didn’t think to check it. After all, the previous message had the correct attachment encoding. So, for the record, don’t use UTF-8 encoding for the message body, or your attachment will be base64 encoded.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;Conclusion&quot;&gt;Conclusion&lt;&#x2F;h1&gt;
&lt;p&gt;The Eudyptula Challenge requires careful attention to detail, more than the standard expected of me in my day job. It encourages me to pursue a higher standard of work.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Self-hosting email again.</title>
        <published>2014-07-18T00:00:00+00:00</published>
        <updated>2014-07-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/self-hosting-email-again/"/>
        <id>https://www.rationali.st/self-hosting-email-again/</id>
        
        <content type="html" xml:base="https://www.rationali.st/self-hosting-email-again/">&lt;p&gt;It’s been a few years since I set up or maintained a public mail server. It took much longer than expected to get everything working again, but here we are!&lt;&#x2F;p&gt;
&lt;p&gt;acooks @ rationali.st&lt;&#x2F;p&gt;
&lt;p&gt;Let’s see how long it takes before I need to tweak spam filters again.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Hello World</title>
        <published>2014-07-14T00:00:00+00:00</published>
        <updated>2014-07-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/hello-world/"/>
        <id>https://www.rationali.st/hello-world/</id>
        
        <content type="html" xml:base="https://www.rationali.st/hello-world/">&lt;p&gt;What’s this? This little bit of interweb detritus was created for a few reasons:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Transparency. Employers want to know what you’ve done before.&lt;&#x2F;li&gt;
&lt;li&gt;To learn about “the cloud”. I’ve done &lt;em&gt;plenty&lt;&#x2F;em&gt; of sysadmin work in the past, but that was all on my own bare metal. This helps to keep my sysadmin skills current, since my day job doesn’t involve that at the moment and I might need those skills again.&lt;&#x2F;li&gt;
&lt;li&gt;The first task of the &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;eudyptula-challenge.org&#x2F;&quot;&gt;Eudyptula Challenge&lt;&#x2F;a&gt; suggests not using Gmail. This  blog is another side-effect of getting my own infrastructure up again.&lt;&#x2F;li&gt;
&lt;li&gt;To document some projects I’ve been working on.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>About me</title>
        <published>2014-07-14T00:00:00+00:00</published>
        <updated>2023-03-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Andrew Cooks
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.rationali.st/pages/about/"/>
        <id>https://www.rationali.st/pages/about/</id>
        
        <content type="html" xml:base="https://www.rationali.st/pages/about/">&lt;p&gt;Hi!&lt;&#x2F;p&gt;
&lt;p&gt;I’m a Software Engineer and have been a Linux and networking enthusiast since the late nineties.&lt;&#x2F;p&gt;
&lt;p&gt;Born and raised in Pretoria, South Africa. I moved to Perth, Western Australia in December 2011, before settling in Brisbane in 2016.&lt;&#x2F;p&gt;
&lt;p&gt;In 2015 I founded a startup producing test and measurement tools for real-time IP communications applications. As a business, it failed to gain any customers, but the &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.jittertrap.net&quot;&gt;Jittertrap&lt;&#x2F;a&gt; software is freely available.&lt;&#x2F;p&gt;
&lt;p&gt;In 2017 I worked on the &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.github.com&#x2F;acooks&#x2F;tn40xx-driver&#x2F;&quot;&gt;tn40xx Ethernet driver&lt;&#x2F;a&gt; (as a hobby) with the hope of upstreaming it. That ended after the chip vendor went out of business and my employement and family changed.&lt;&#x2F;p&gt;
&lt;p&gt;Since 2018 I’ve been building software for &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.boeing.com.au&#x2F;products-services&#x2F;defence-space-security&#x2F;currawong&quot;&gt;software defined networks and communication systems&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I love working in the low level details, though recently I’ve been spending more time on collaborative work like sketching designs on white boards or Visio and finding less time for independent technical contributions.&lt;&#x2F;p&gt;
&lt;p&gt;A couple of trivial patches of mine made it into &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;git.kernel.org&#x2F;cgit&#x2F;linux&#x2F;kernel&#x2F;git&#x2F;torvalds&#x2F;linux.git&#x2F;log&#x2F;?qt=author&amp;amp;q=Andrew+Cooks&quot;&gt;the kernel&lt;&#x2F;a&gt; and I’m always talking about how I want to be a kernel developer when I grow up.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.rationali.st&#x2F;pages&#x2F;about&#x2F;redhat_linux_5_small.png&quot; alt=&quot;Red Hat 5 manual from 1998&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;That’s a &lt;a rel=&quot;noopener nofollow noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Red_Hat_Linux#Version_history&quot;&gt;Red Hat Linux 5&lt;&#x2F;a&gt; (Not RHEL) user’s guide circa 1998.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.rationali.st&#x2F;pages&#x2F;about&#x2F;tcpip-unleashed_300px.jpg&quot; alt=&quot;TCP&#x2F;IP Unleashed from 1996&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;TCP&#x2F;IP Unleashed from 1996. Seems I started young and haven’t moved on.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
