<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Subnetica</title>
    <description>The latest articles on DEV Community by Subnetica (@subnetica).</description>
    <link>https://dev.to/subnetica</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4083725%2Ff92be4cd-12dc-4407-8c17-deba266dd607.png</url>
      <title>DEV Community: Subnetica</title>
      <link>https://dev.to/subnetica</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/subnetica"/>
    <language>en</language>
    <item>
      <title>5 Common Subnetting Mistakes That Break Real Networks</title>
      <dc:creator>Subnetica</dc:creator>
      <pubDate>Thu, 20 Aug 2026 03:40:26 +0000</pubDate>
      <link>https://dev.to/subnetica/5-common-subnetting-mistakes-that-break-real-networks-4an7</link>
      <guid>https://dev.to/subnetica/5-common-subnetting-mistakes-that-break-real-networks-4an7</guid>
      <description>&lt;p&gt;Subnetting errors rarely announce themselves as "bad math." More often, two devices make different decisions about whether a destination is local, a route points at the wrong boundary, or a cloud/VPN design contains two networks that cannot be unambiguously routed. These five failure modes are worth recognizing in live configurations.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The two hosts use different masks
&lt;/h2&gt;

&lt;p&gt;Consider Host A at &lt;code&gt;192.168.10.10/24&lt;/code&gt; and Host B at &lt;code&gt;192.168.11.10/16&lt;/code&gt;. A calculates that B is outside &lt;code&gt;192.168.10.0/24&lt;/code&gt;, so A sends the packet to its default gateway. B calculates that A is inside &lt;code&gt;192.168.0.0/16&lt;/code&gt;, so B treats A as local and tries ARP directly.&lt;/p&gt;

&lt;p&gt;The result can be asymmetric: one direction follows a router, while the reply is sent directly or never reaches the expected gateway. Check the actual prefix on both interfaces, not just the dotted decimal mask shown in a diagram.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;ip -br addr
ip route
ping -c 3 192.168.11.10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Correct the prefix so both endpoints agree, or intentionally route between two correctly defined subnets.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Overlapping subnets are assigned to different networks
&lt;/h2&gt;

&lt;p&gt;Suppose a branch uses &lt;code&gt;10.20.0.0/16&lt;/code&gt;, while a cloud VPC or VPN peer also uses &lt;code&gt;10.20.0.0/16&lt;/code&gt;. The problem is not that either mask is mathematically invalid. The problem is that a router cannot distinguish "the branch's &lt;code&gt;10.20.5.0/24&lt;/code&gt;" from "the cloud's &lt;code&gt;10.20.5.0/24&lt;/code&gt;" if both are reachable through different paths.&lt;/p&gt;

&lt;p&gt;Symptoms include traffic taking the wrong tunnel, routes that cannot be installed, or a VPN that connects but cannot reach some subnets. Inventory both sides of a tunnel and compare the complete network/prefix pairs. A longer, more specific route may make one destination appear to work while hiding the underlying overlap.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;ip route
ip route get 10.20.5.25
traceroute -n 10.20.5.25
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The durable correction is renumbering or using an intentional translation/design boundary. Adding increasingly specific routes is usually a brittle workaround.&lt;/p&gt;

&lt;p&gt;This is also why I prefer teaching subnetting inside routing and troubleshooting scenarios rather than only through standalone CIDR exercises. On &lt;a href="https://subnetica.xyz/" rel="noopener noreferrer"&gt;Subnetica&lt;/a&gt;, subnetting shows up as part of the network you actually have to diagnose and configure.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The default gateway is assumed to be reachable
&lt;/h2&gt;

&lt;p&gt;A default gateway is not simply "the first address in the subnet." For an ordinary Ethernet host, the gateway must be reachable on a directly connected local network. A host at &lt;code&gt;192.168.50.20/24&lt;/code&gt; cannot normally use &lt;code&gt;192.168.51.1&lt;/code&gt; as its gateway because the host believes that address is remote and has no route to reach it.&lt;/p&gt;

&lt;p&gt;"It looks close" is not a routing rule. Verify the interface prefix and the connected route before debugging the gateway device.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;ip addr show dev eth0
ip route
ip route get 1.1.1.1
ip neigh show dev eth0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fix the host prefix, choose an on-link gateway, or add a deliberate design that makes the next hop reachable. On point-to-point links and some special configurations, the exact on-link behavior differs, so inspect the kernel's route rather than relying on a blanket rule.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. A network or broadcast address is used as a host address
&lt;/h2&gt;

&lt;p&gt;For &lt;code&gt;192.0.2.0/24&lt;/code&gt;, &lt;code&gt;192.0.2.0&lt;/code&gt; is the network address and &lt;code&gt;192.0.2.255&lt;/code&gt; is the directed-broadcast address in the conventional IPv4 subnet model. Assigning either as an ordinary host address creates ambiguity or is rejected by the platform. The usable host range is commonly &lt;code&gt;192.0.2.1&lt;/code&gt; through &lt;code&gt;192.0.2.254&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This rule is about the address plan, not a claim that every modern point-to-point implementation behaves identically. For a point-to-point link, follow the platform's documented interface and prefix semantics; do not mechanically apply LAN broadcast assumptions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Inspect the address and the connected route
&lt;span class="go"&gt;ip addr show
ip route

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Ask a calculator or your own binary-prefix method:
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;192.0.2.0/24 -&amp;gt; network .0, broadcast .255
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Correct the host address and verify that ARP/neighbor discovery and the connected route now match the intended subnet.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. The route covers too small a network
&lt;/h2&gt;

&lt;p&gt;Imagine the design calls for &lt;code&gt;10.20.0.0/16&lt;/code&gt;, but the router is configured with &lt;code&gt;10.20.0.0/24&lt;/code&gt;. Destinations from &lt;code&gt;10.20.0.0&lt;/code&gt; through &lt;code&gt;10.20.0.255&lt;/code&gt; match; &lt;code&gt;10.20.1.10&lt;/code&gt; does not. That creates the frustrating symptom where "some hosts in the network work" while others follow a default route or disappear into a different path.&lt;/p&gt;

&lt;p&gt;Routing protocols, administrative preference, and metrics determine which candidate route for a given prefix is installed. When forwarding a packet, the system then selects the most specific matching installed route using longest-prefix match. A &lt;code&gt;/24&lt;/code&gt; has a longer prefix length than a &lt;code&gt;/16&lt;/code&gt;, so it is more specific and covers a narrower network. A more-specific mistaken route can override a correct summary without changing the summary itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;
ip route
ip route get 10.20.0.10
ip route get 10.20.1.10

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;On an FRR router, compare the selected RIB entry too:
&lt;span class="go"&gt;show ip route 10.20.1.10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Correct the route to &lt;code&gt;10.20.0.0/16&lt;/code&gt; if that is the intended boundary, then check for competing more-specific routes and the return path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why longest-prefix match exposes bad subnetting
&lt;/h2&gt;

&lt;p&gt;Routers do not choose the route with the prettiest address or the route learned first. The routing process first decides which candidate for each prefix should be installed, using protocol rules, administrative preference, and metrics. The forwarding table then compares a packet’s destination with the installed routes and uses longest-prefix match. A &lt;code&gt;/24&lt;/code&gt; beats a &lt;code&gt;/16&lt;/code&gt; for destinations inside that &lt;code&gt;/24&lt;/code&gt; because the &lt;code&gt;/24&lt;/code&gt; is narrower and more specific.&lt;/p&gt;

&lt;p&gt;This makes an incorrect subnet mask especially deceptive. A broad summary can carry most traffic successfully while one accidental more-specific route sends a small range somewhere else. When only a handful of destinations fail, compare &lt;code&gt;ip route get&lt;/code&gt; results for a working and failing address, then inspect the exact prefix, not just the next hop. Compare addresses on either side of a suspected boundary&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;ip route get 10.20.0.250
ip route get 10.20.1.10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a Linux router, the chosen device and gateway are the evidence&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;ip -br link
ip route
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  When subnetting looks suspicious, check this first
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Source address and prefix, on the actual interface.&lt;/li&gt;
&lt;li&gt;Destination address and whether it should be local or routed.&lt;/li&gt;
&lt;li&gt;Default gateway and whether the kernel sees it as reachable.&lt;/li&gt;
&lt;li&gt;The routing table and the result of longest-prefix match.&lt;/li&gt;
&lt;li&gt;Duplicate, overlapping, or unexpectedly summarized networks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A short diagnostic challenge
&lt;/h2&gt;

&lt;p&gt;Given &lt;code&gt;192.168.10.10/24&lt;/code&gt; and &lt;code&gt;192.168.11.10/16&lt;/code&gt;, which host believes the other is local? Host B, because its &lt;code&gt;/16&lt;/code&gt; covers both addresses. Host A does not, because its &lt;code&gt;/24&lt;/code&gt; ends at &lt;code&gt;192.168.10.255&lt;/code&gt;. The important lesson is not the answer. It is that "same subnet" is a calculation made independently by each endpoint.&lt;/p&gt;

&lt;p&gt;If you want to practice these concepts in actual network scenarios, take a look at &lt;a href="https://subnetica.xyz/" rel="noopener noreferrer"&gt;Subnetica&lt;/a&gt;, a networking learning platform with lessons, quizzes, and automatically graded FRRouting/Linux labs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://man7.org/linux/man-pages/man8/ip-route.8.html" rel="noopener noreferrer"&gt;Linux ip-route(8) manual page&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.rfc-editor.org/rfc/rfc4632" rel="noopener noreferrer"&gt;IETF RFC 4632, Classless Inter-domain Routing&lt;/a&gt;&lt;/p&gt;

</description>
      <category>networking</category>
      <category>beginners</category>
      <category>tutorial</category>
      <category>learning</category>
    </item>
    <item>
      <title>FRRouting and the Linux Routing Table: How Routes Reach the Kernel</title>
      <dc:creator>Subnetica</dc:creator>
      <pubDate>Wed, 19 Aug 2026 18:02:35 +0000</pubDate>
      <link>https://dev.to/subnetica/frrouting-and-the-linux-routing-table-how-routes-reach-the-kernel-4gpp</link>
      <guid>https://dev.to/subnetica/frrouting-and-the-linux-routing-table-how-routes-reach-the-kernel-4gpp</guid>
      <description>&lt;p&gt;FRRouting does not forward packets by itself. Its protocol daemons learn and evaluate routes; Zebra coordinates the selected result with the Linux kernel, whose forwarding tables ultimately decide what happens to a packet.&lt;/p&gt;

&lt;h2&gt;
  
  
  The route's journey
&lt;/h2&gt;

&lt;p&gt;A useful mental model is to separate learning a route from forwarding a packet. OSPF, BGP, or a static configuration produces routing information inside FRR. The protocol daemon sends candidates to Zebra. Zebra applies FRR's selection rules, resolves next hops, and asks the kernel to install the winners. Linux then performs the lookup when a packet arrives.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqgtgo294nqx3bz8oeabq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqgtgo294nqx3bz8oeabq.png" alt="A protocol database is upstream of forwarding; the Linux kernel is the final packet-forwarding authority." width="732" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What each FRR daemon does
&lt;/h2&gt;

&lt;p&gt;FRR is a suite of cooperating daemons. &lt;code&gt;ospfd&lt;/code&gt; maintains OSPF adjacencies and its link-state database, then calculates OSPF paths. &lt;code&gt;bgpd&lt;/code&gt; exchanges routes with BGP peers and applies BGP policy. &lt;code&gt;staticd&lt;/code&gt; manages static routes when that daemon is enabled. These daemons know their own protocol state; they are not independent kernel forwarding planes.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;zebra&lt;/code&gt; is the routing manager and the bridge to the operating system. It receives route candidates, keeps FRR's main routing view, chooses the routes that should win across protocols, and communicates interface, address, and route state to the Linux kernel. The command syntax may look familiar to Cisco users, but FRR's architecture and implementation are not Cisco IOS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Protocol information, the RIB, and the FIB
&lt;/h2&gt;

&lt;p&gt;There are several layers of "the route exists." A protocol database may contain an OSPF learned path. FRR's RIB may select that path as the best route for a prefix. The kernel's FIB may then contain an installed route. Only the last layer is directly used by Linux for ordinary packet forwarding.&lt;/p&gt;

&lt;p&gt;That is why "OSPF learned it" does not prove that the host will forward traffic through it. A better administrative distance or preference can win. The next hop may not resolve recursively. An interface can be down, a route can be filtered by policy, or the kernel can reject an installation. A newer route can also replace the one you were inspecting.&lt;/p&gt;

&lt;h2&gt;
  
  
  RIB vs FIB: why the distinction matters
&lt;/h2&gt;

&lt;p&gt;In practical troubleshooting, think of the RIB as the selected control-plane answer and the FIB as the installed forwarding answer. On Linux, the operational view is exposed through commands such as &lt;code&gt;ip route&lt;/code&gt;; FRR's view is exposed through &lt;code&gt;show ip route&lt;/code&gt;. They should agree for routes that FRR successfully installed, but they are not guaranteed to be identical snapshots.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspect both sides
&lt;/h2&gt;

&lt;p&gt;Start with FRR's selected view, then inspect the Linux interfaces, neighbors, and routes. The comparison is more valuable than either command alone.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;FRR: selected routes and protocol-specific evidence
&lt;span class="go"&gt;show ip route
show ip ospf route
show bgp ipv4 unicast

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Linux: addresses, neighbors, and the forwarding table
&lt;span class="go"&gt;ip -br addr
ip neigh
ip route

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Ask Linux which route it would use &lt;span class="k"&gt;for &lt;/span&gt;a destination
&lt;span class="go"&gt;ip route get 10.44.8.10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;show ip route&lt;/code&gt; can show a route selected by FRR while &lt;code&gt;ip route&lt;/code&gt; tells you whether that route reached the kernel. &lt;code&gt;ip route get&lt;/code&gt; is especially useful because it performs a lookup for a concrete destination and displays the chosen interface, source address, and next hop.&lt;/p&gt;

&lt;h3&gt;
  
  
  Watch the installation boundary
&lt;/h3&gt;

&lt;p&gt;If the state changes while you are troubleshooting, watch both views instead of repeatedly taking screenshots. Linux can report route changes as they happen, while FRR can show whether a route was replaced or withdrawn. This is useful when an interface flaps or a protocol reconverges:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;ip monitor route

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;In another terminal, watch the selected FRR view
&lt;span class="go"&gt;show ip route 10.44.8.0/24
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A route disappearing from the kernel immediately after FRR selects it points toward installation, interface, or next-hop problems. A route that never reaches FRR's selected view points earlier in the chain: adjacency, policy, preference, or recursive resolution. The timing often tells you more than a single static table.&lt;/p&gt;

&lt;p&gt;This distinction between FRR's view of the network and the Linux kernel's forwarding state is one of the reasons I built &lt;a href="https://subnetica.xyz/" rel="noopener noreferrer"&gt;Subnetica&lt;/a&gt;'s labs around FRRouting and Linux rather than hiding the underlying networking stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario: OSPF learned the route, but forwarding fails
&lt;/h2&gt;

&lt;p&gt;Suppose Router R1 reports an OSPF route to &lt;code&gt;10.44.8.0/24&lt;/code&gt;, but a host behind R1 cannot reach &lt;code&gt;10.44.8.10&lt;/code&gt;. Work from the control plane toward the data plane:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;    Check &lt;code&gt;show ip ospf neighbor&lt;/code&gt; and confirm the expected adjacency is actually Full. A stale or partial adjacency may leave the protocol database incomplete.&lt;/li&gt;
&lt;li&gt;    Use &lt;code&gt;show ip ospf route&lt;/code&gt; to verify the prefix and next hop are present in OSPF's view.&lt;/li&gt;
&lt;li&gt;    Use &lt;code&gt;show ip route 10.44.8.0/24&lt;/code&gt; or &lt;code&gt;show ip route&lt;/code&gt; to see whether FRR selected an OSPF path, or whether another protocol won.&lt;/li&gt;
&lt;li&gt;    Run &lt;code&gt;ip route&lt;/code&gt; and &lt;code&gt;ip route get 10.44.8.10&lt;/code&gt;. If Linux has no matching route, the problem is between FRR selection and kernel installation, or the route was never selected.&lt;/li&gt;
&lt;li&gt;    Check &lt;code&gt;ip -br addr&lt;/code&gt; for interface state and addressing, then &lt;code&gt;ip neigh&lt;/code&gt; for a missing or failed next-hop resolution.&lt;/li&gt;
&lt;li&gt;    If both tables contain the route, test the next hop and the return path. A correct local FIB entry cannot repair a missing reverse route, filtering rule, or disabled forwarding on another hop.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This method avoids a common category error: treating a protocol's knowledge as proof of end-to-end connectivity. Route installation, neighbor resolution, interface state, and return traffic all still matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do not confuse route presence with forwarding success
&lt;/h2&gt;

&lt;p&gt;Even a matching FIB entry is only one part of packet delivery. Linux still needs an operational output interface and a usable neighbor entry for the next hop. The receiving host needs a route back, and local firewall or forwarding policy may reject the packet. For a router namespace, also check that IPv4 forwarding is enabled; a namespace with two addresses is not automatically a router.&lt;/p&gt;

&lt;p&gt;That layered model scales beyond FRR. It is the same reason a Kubernetes node can have a route in one namespace while an application namespace has a different view, or why a container route can be correct while the host-side veth or bridge is misconfigured.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where namespaces make this easier to see
&lt;/h2&gt;

&lt;p&gt;Linux network namespaces make it possible to inspect several independent routing tables on one host. Put FRR in a router namespace, connect it with veth pairs, and the same protocol-to-Zebra-to-kernel pipeline becomes visible without physical hardware. The companion guide explains that topology in detail.&lt;/p&gt;

&lt;p&gt;If you want to practice this kind of troubleshooting interactively, &lt;a href="https://subnetica.xyz/labs" rel="noopener noreferrer"&gt;Subnetica&lt;/a&gt; has browser-based networking labs where you can inspect FRR state, routing tables, and connectivity and have the resulting network automatically graded.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.frrouting.org/en/latest/zebra.html" rel="noopener noreferrer"&gt;FRRouting Zebra documentation&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.frrouting.org/en/latest/basic.html" rel="noopener noreferrer"&gt;FRRouting basic commands and daemons&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.frrouting.org/en/latest/ospfd.html" rel="noopener noreferrer"&gt;FRRouting OSPFv2 documentation&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.frrouting.org/en/latest/bgp.html" rel="noopener noreferrer"&gt;FRRouting BGP documentation&lt;/a&gt;&lt;br&gt;
&lt;a href="https://man7.org/linux/man-pages/man8/ip-route.8.html" rel="noopener noreferrer"&gt;Linux ip-route(8) manual page&lt;/a&gt;&lt;/p&gt;

</description>
      <category>networking</category>
      <category>linux</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How Network Namespaces Let You Build an Entire Network on One Linux Host</title>
      <dc:creator>Subnetica</dc:creator>
      <pubDate>Tue, 18 Aug 2026 20:03:59 +0000</pubDate>
      <link>https://dev.to/subnetica/how-network-namespaces-let-you-build-an-entire-network-on-one-linux-host-95</link>
      <guid>https://dev.to/subnetica/how-network-namespaces-let-you-build-an-entire-network-on-one-linux-host-95</guid>
      <description>&lt;p&gt;A Linux network namespace is an independent network stack. Give several namespaces interfaces, addresses, routes, and virtual Ethernet connections, and one machine can behave like multiple hosts, switches, and routers without pretending they share one routing table.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a namespace isolates
&lt;/h2&gt;

&lt;p&gt;Each namespace gets its own interfaces, IP addresses, routing table, neighbor/ARP table, firewall state, and loopback interface. The host kernel still provides the machinery, but a process placed in namespace &lt;code&gt;host-a&lt;/code&gt; sees a different network world from a process in namespace &lt;code&gt;host-b&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The namespace starts with a loopback device that is normally down. Bring it up before relying on software that expects localhost to work. The namespace does not magically have a connection to the outside world; connectivity appears only when you create and connect interfaces.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyst4t9ov8kxakju2fxm9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyst4t9ov8kxakju2fxm9.png" alt="Two host namespaces connect to a router namespace through two virtual Ethernet cables." width="732" height="163"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  veth pairs are virtual Ethernet cables
&lt;/h2&gt;

&lt;p&gt;A veth pair always has two endpoints. A packet written to one endpoint appears at the other. Move one endpoint into &lt;code&gt;host-a&lt;/code&gt; and the other into router, and you have a link between those namespaces. Repeat the operation for router and &lt;code&gt;host-b&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns add host-a
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns add router
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns add host-b

&lt;span class="nb"&gt;sudo &lt;/span&gt;ip &lt;span class="nb"&gt;link &lt;/span&gt;add veth-a &lt;span class="nb"&gt;type &lt;/span&gt;veth peer name veth-r1
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip &lt;span class="nb"&gt;link &lt;/span&gt;add veth-b &lt;span class="nb"&gt;type &lt;/span&gt;veth peer name veth-r2
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip &lt;span class="nb"&gt;link set &lt;/span&gt;veth-a netns host-a
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip &lt;span class="nb"&gt;link set &lt;/span&gt;veth-r1 netns router
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip &lt;span class="nb"&gt;link set &lt;/span&gt;veth-b netns host-b
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip &lt;span class="nb"&gt;link set &lt;/span&gt;veth-r2 netns router
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These commands require root privileges and change the host's live network configuration. Use an isolated test machine or a disposable VM, and keep a recovery path. The commands create only the links; they do not assign addresses, enable forwarding, or make the links operational.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bring the links and loopbacks up
&lt;/h2&gt;

&lt;p&gt;Configure each namespace from the outside with &lt;code&gt;ip netns exec&lt;/code&gt;. The endpoint names are local to their namespace after the move.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;host-a ip &lt;span class="nb"&gt;link set &lt;/span&gt;lo up
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;router ip &lt;span class="nb"&gt;link set &lt;/span&gt;lo up
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;host-b ip &lt;span class="nb"&gt;link set &lt;/span&gt;lo up

&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;host-a ip &lt;span class="nb"&gt;link set &lt;/span&gt;veth-a up
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;router ip &lt;span class="nb"&gt;link set &lt;/span&gt;veth-r1 up
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;router ip &lt;span class="nb"&gt;link set &lt;/span&gt;veth-r2 up
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;host-b ip &lt;span class="nb"&gt;link set &lt;/span&gt;veth-b up

&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;host-a ip addr add 192.0.2.2/24 dev veth-a
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;router ip addr add 192.0.2.1/24 dev veth-r1
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;router ip addr add 198.51.100.1/24 dev veth-r2
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;host-b ip addr add 198.51.100.2/24 dev veth-b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point each directly connected subnet should be visible only in the namespace that owns the interface. Verify the separation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;host-a ip &lt;span class="nt"&gt;-br&lt;/span&gt; addr
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;host-a ip route
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;router ip route
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;host-b ip neigh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Make the router namespace route
&lt;/h2&gt;

&lt;p&gt;Host A knows how to reach &lt;code&gt;192.0.2.0/24&lt;/code&gt;; Host B knows how to reach &lt;code&gt;198.51.100.0/24&lt;/code&gt;. Neither host knows the other network is behind the router. Add routes to the remote subnet and enable IPv4 forwarding in the router namespace.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;host-a ip route add 198.51.100.0/24 via 192.0.2.1
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;host-b ip route add 192.0.2.0/24 via 198.51.100.1
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;router sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.ipv4.ip_forward&lt;span class="o"&gt;=&lt;/span&gt;1

&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;host-a ping &lt;span class="nt"&gt;-c&lt;/span&gt; 3 198.51.100.2
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;host-a ip route get 198.51.100.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Forwarding is a router function, not a property of merely having two interfaces. If the ping fails, check link state, addresses, both return routes, neighbor entries, and the router's forwarding setting. Network namespaces make this troubleshooting concrete because each command can be run from the exact node whose view matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bridges act like virtual switches
&lt;/h2&gt;

&lt;p&gt;A Linux bridge connects ports at Layer 2. Attach several namespace veth endpoints to the bridge and they share an Ethernet broadcast domain. The bridge learns MAC addresses and forwards frames between ports; it does not replace the IP routing table. A router namespace or the host itself can have an IP address on a bridge when it needs to participate at Layer 3.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ip &lt;span class="nb"&gt;link &lt;/span&gt;add br-lan &lt;span class="nb"&gt;type &lt;/span&gt;bridge
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip &lt;span class="nb"&gt;link set &lt;/span&gt;br-lan up

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a separate cable to the bridge for &lt;code&gt;host-a&lt;/code&gt;. These names are different from the &lt;code&gt;veth-a/veth-r1&lt;/code&gt; pair above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ip &lt;span class="nb"&gt;link &lt;/span&gt;add veth-switch-a &lt;span class="nb"&gt;type &lt;/span&gt;veth peer name veth-bridge-a
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip &lt;span class="nb"&gt;link set &lt;/span&gt;veth-switch-a netns host-a
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip &lt;span class="nb"&gt;link set &lt;/span&gt;veth-bridge-a master br-lan
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;host-a ip &lt;span class="nb"&gt;link set &lt;/span&gt;veth-switch-a up
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip &lt;span class="nb"&gt;link set &lt;/span&gt;veth-bridge-a up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inspect bridge ports and learned MAC addresses&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bridge &lt;span class="nb"&gt;link
&lt;/span&gt;bridge fdb show
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In real setups, connect the bridge to a namespace endpoint or a host-facing interface deliberately. Avoid attaching production interfaces to an experimental bridge without understanding the impact.&lt;/p&gt;

&lt;h2&gt;
  
  
  From namespaces to a virtual router
&lt;/h2&gt;

&lt;p&gt;A namespace can run a routing daemon such as FRRouting and behave like a router:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbaqxskk3cc0tj5wir3mg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbaqxskk3cc0tj5wir3mg.png" alt="A virtual router follows the same control-plane/data-plane split as a physical router." width="732" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;FRR's protocol daemon learns routes, Zebra selects and coordinates them, and the Linux kernel forwards packets through R1's namespace.&lt;/p&gt;

&lt;p&gt;This is also the basic idea behind browser-based networking environments such as &lt;a href="https://subnetica.xyz/" rel="noopener noreferrer"&gt;Subnetica&lt;/a&gt;: Linux networking primitives and FRRouting can be combined to create isolated virtual networks that behave much more like real systems than a purely diagram-based simulator.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters for containers and labs
&lt;/h2&gt;

&lt;p&gt;Containers use namespaces to isolate process and network views. Kubernetes networking builds on related primitives, often combining namespaces, veth pairs, bridges or other virtual devices, routing, and policy. Network emulators use them to model hosts and routers cheaply. The concepts transfer: inspect the namespace's own interfaces and routes, then follow the packet across each virtual link.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clean up a disposable experiment
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns del host-a
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns del router
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns del host-b
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip &lt;span class="nb"&gt;link &lt;/span&gt;del br-lan  &lt;span class="c"&gt;# only if you created it in the root namespace&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deleting a namespace removes its contained interfaces and routes. Treat cleanup as part of the experiment so the host does not accumulate stale virtual devices.&lt;/p&gt;

&lt;p&gt;If you want to experiment with routing and troubleshooting without building the namespace topology yourself, check out &lt;a href="https://subnetica.xyz/" rel="noopener noreferrer"&gt;Subnetica&lt;/a&gt;, which provides browser-based FRRouting/Linux labs with automatic grading.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://man7.org/linux/man-pages/man7/network_namespaces.7.html" rel="noopener noreferrer"&gt;Linux network_namespaces(7) manual page&lt;/a&gt;&lt;br&gt;
&lt;a href="https://man7.org/linux/man-pages/man8/ip-netns.8.html" rel="noopener noreferrer"&gt;Linux ip-netns(8) manual page&lt;/a&gt;&lt;br&gt;
&lt;a href="https://man7.org/linux/man-pages/man4/veth.4.html" rel="noopener noreferrer"&gt;Linux veth(4) manual page&lt;/a&gt;&lt;br&gt;
&lt;a href="https://man7.org/linux/man-pages/man8/bridge.8.html" rel="noopener noreferrer"&gt;Linux bridge(8) manual page&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.frrouting.org/en/latest/zebra.html" rel="noopener noreferrer"&gt;FRRouting Zebra documentation&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>devops</category>
      <category>linux</category>
      <category>networking</category>
    </item>
  </channel>
</rss>
