3-Node Mesh

┌───────┐
│       ├───┐
│ node1 │   │
│       ├─┐ │
└───────┘ │ │
┌───────┐ │ │
│       ├─┘ │
│ node2 │   │
│       ├─┐ │
└───────┘ │ │
┌───────┐ │ │
│       ├─┘ │
│ node3 │   │
│       ├───┘
└───────┘

Use asciiflow to edit.

Cabeling

┌─────┬────┐
│     │eth9├───┐
│ n-a ├────┤   │
│     │eth8├─┐ │
└─────┴────┘ │ │
┌─────┬────┐ │ │
│     │eth9├─┘ │
│ n-b ├────┤   │
│     │eth8├─┐ │
└─────┴────┘ │ │
┌─────┬────┐ │ │
│     │eth9├─┘ │
│ n-c ├────┤   │
│     │eth8├───┘
└─────┴────┘

Using IP commands

Kernel configuration

Enable fowarding:

echo 1 >/proc/sys/net/ipv6/conf/all/forwarding

Enable ignoring routes there the NIC has the status link down:

echo 1 > /proc/sys/net/ipv6/conf/all/ignore_routes_with_linkdown

IP addresses

┌─────┬────┐
│     │eth9├─fd5b:107:24a8:ac::a/64───┐
│ n-a ├────┤                          │
│     │eth8├─fd5b:107:24a8:ab::a/64─┐ │
└─────┴────┘                        │ │
┌─────┬────┐                        │ │
│     │eth9├─fd5b:107:24a8:ab::b/64─┘ │
│ n-b ├────┤                          │
│     │eth8├─fd5b:107:24a8:bc::b/64─┐ │
└─────┴────┘                        │ │
┌─────┬────┐                        │ │
│     │eth9├─fd5b:107:24a8:bc::c/64─┘ │
│ n-c ├────┤                          │
│     │eth8├─fd5b:107:24a8:ac::c/64───┘
└─────┴────┘

Node n-a

/etc/network/interfaces:

iface ens36 inet6 static
    address fd5b:107:24a8:ab::a/64

iface ens37 inet6 static
    address fd5b:107:24a8:ac::a/64

iface ens38 inet6 static
    address fd5b:107:24a8:a::1/64
ip route add fd5b:107:24a8:b::/64 via fd5b:107:24a8:ab::b metric 100
ip route add fd5b:107:24a8:c::/64 via fd5b:107:24a8:ab::b metric 200

ip route add fd5b:107:24a8:c::/64 via fd5b:107:24a8:ac::c metric 100
ip route add fd5b:107:24a8:b::/64 via fd5b:107:24a8:ac::c metric 200

Node n-b

/etc/network/interfaces:

iface ens36 inet6 static
    address fd5b:107:24a8:ab::b/64

iface ens37 inet6 static
    address fd5b:107:24a8:bc::b/64

iface ens38 inet6 static
    address fd5b:107:24a8:b::1/64
ip route add fd5b:107:24a8:a::/64 via fd5b:107:24a8:ab::a metric 100
ip route add fd5b:107:24a8:a::/64 via fd5b:107:24a8:bc::c metric 200

ip route add fd5b:107:24a8:c::/64 via fd5b:107:24a8:bc::c metric 100
ip route add fd5b:107:24a8:c::/64 via fd5b:107:24a8:ab::a metric 200

Node n-c

/etc/network/interfaces:

iface ens36 inet6 static
    address fd5b:107:24a8:bc::c/64

iface ens36 inet6 static
    address fd5b:107:24a8:ac::c/64

iface ens38 inet6 static
    address fd5b:107:24a8:c::1/64
ip route add fd5b:107:24a8:b::/64 via fd5b:107:24a8:bc::b metric 100
ip route add fd5b:107:24a8:b::/64 via fd5b:107:24a8:ac::a metric 200

ip route add fd5b:107:24a8:a::/64 via fd5b:107:24a8:ac::a metric 100
ip route add fd5b:107:24a8:a::/64 via fd5b:107:24a8:bc::b metric 200

Using Netplan

Render the node config via Jinja2

Use Jinja2 live parser to experiment with:

name: n-a
mesh_network_prefix: fd5b:107:24a8
{%-
set next_node = {
  'a': 'cb',
  'b': 'ac',
  'c': 'ba'
}
-%}

{%- set current_node = name[2] -%}

{%- set mesh_link1_node = next_node[current_node][0] -%}
{%- set mesh_link1_network = (current_node + mesh_link1_node) | list | sort | join -%}
{%- set mesh_link1_gateway = mesh_link1_network + '::' + mesh_link1_node -%}

{%- set mesh_link2_node = next_node[current_node][1] -%}
{%- set mesh_link2_network = (current_node+mesh_link2_node) | list | sort | join -%}
{%- set mesh_link2_gateway = mesh_link2_network + '::' + mesh_link2_node -%}

          eth9:
            addresses:
              - {{ mesh_network_prefix }}:{{ mesh_link1_network }}::{{ current_node }}/64
            routes:
              - to: {{ mesh_network_prefix }}:{{ mesh_link1_node }}::/64
                via: {{ mesh_network_prefix }}:{{ mesh_link1_gateway }}
                metric: 100
              - to: {{ mesh_network_prefix }}:{{mesh_link2_node}}::/64
                via: {{ mesh_network_prefix }}:{{ mesh_link1_gateway }}
                metric: 200
          eth8:
            addresses:
              - {{ mesh_network_prefix }}:{{ mesh_link2_network }}::{{ current_node }}/64"
            routes:
              - to: {{ mesh_network_prefix }}:{{ mesh_link2_node }}::/64
                via: {{ mesh_network_prefix }}:{{ mesh_link2_gateway }}
                metric: 100
              - to: {{ mesh_network_prefix }}:{{ mesh_link1_node }}::/64
                via: {{ mesh_network_prefix }}:{{ mesh_link2_gateway }}
                metric: 200

Node n-a

Configure the dummy address and the mesh transit networks:

root@n-a:~# cat /etc/netplan/60-mesh.yaml
network:
  version: 2
  bridges:
    dummy0:
      addresses:
      - fd5b:107:24a8:a::1/64
  ethernets:
    eth9:
      addresses:
        - fd5b:107:24a8:ac::a/64
      routes:
        - to: fd5b:107:24a8:c::/64
          via: fd5b:107:24a8:ac::c
          metric: 100
        - to: fd5b:107:24a8:b::/64
          via: fd5b:107:24a8:ac::c
          metric: 200
      mtu: 10222
    eth8:
      addresses:
        - fd5b:107:24a8:ab::a/64
      routes:
        - to: fd5b:107:24a8:b::/64
          via: fd5b:107:24a8:ab::b
          metric: 100
        - to: fd5b:107:24a8:c::/64
          via: fd5b:107:24a8:ab::b
          metric: 200
      mtu: 10222

Node n-b

Configure the dummy address and the mesh transit networks:

root@n-b:~# cat /etc/netplan/80-mesh.yaml
network:
  version: 2
  bridges:
    dummy0:
      addresses:
      - fd5b:107:24a8:b::1/64
  ethernets:
    eth9:
      addresses:
        - fd5b:107:24a8:ab::b/64
      routes:
        - to: fd5b:107:24a8:a::/64
          via: fd5b:107:24a8:ab::a
          metric: 100
        - to: fd5b:107:24a8:c::/64
          via: fd5b:107:24a8:ab::a
          metric: 200
      mtu: 10222
    eth8:
      addresses:
        - fd5b:107:24a8:bc::b/64
      routes:
        - to: fd5b:107:24a8:c::/64
          via: fd5b:107:24a8:bc::c
          metric: 100
        - to: fd5b:107:24a8:a::/64
          via: fd5b:107:24a8:bc::c
          metric: 200
      mtu: 10222

Node n-c

Configure the dummy address and the mesh transit networks:

root@n-c:~# cat /etc/netplan/80-mesh.yaml
network:
  version: 2
  bridges:
    dummy0:
      addresses:
      - fd5b:107:24a8:c::1/64
  ethernets:
    eth9:
      addresses:
        - fd5b:107:24a8:bc::c/64
      routes:
        - to: fd5b:107:24a8:b::/64
          via: fd5b:107:24a8:bc::b
          metric: 100
        - to: fd5b:107:24a8:a::/64
          via: fd5b:107:24a8:bc::b
          metric: 200
      mtu: 10222
    eth8:
      addresses:
        - fd5b:107:24a8:ac::c/64
      routes:
        - to: fd5b:107:24a8:a::/64
          via: fd5b:107:24a8:ac::a
          metric: 100
        - to: fd5b:107:24a8:b::/64
          via: fd5b:107:24a8:ac::a
          metric: 200
      mtu: 10222

Test connections

Test the connection from n-a to n-b and n-c:

ping -I fd5b:107:24a8:a::1 fd5b:107:24a8:b::1
ping -I fd5b:107:24a8:a::1 fd5b:107:24a8:c::1

tcpdump:

tcpdump -i ens37 icmp6 and src fd5b:107:24a8:a::1

Netcat server:

root@n-a:/etc/netplan# netcat -6 -l -p 4444 -v

Netcat client:

root@n-c:~# netcat fd5b:107:24a8:a::1 4444 -s fd5b:107:24a8:c::1

Routing switchover on link failure:

root@n-a:~# ping -I fd5b:107:24a8:a::1  fd5b:107:24a8:c::1
PING fd5b:107:24a8:c::1(fd5b:107:24a8:c::1) from fd5b:107:24a8:a::1 : 56 data bytes
From fd5b:107:24a8:a::1 icmp_seq=1 Destination unreachable: Address unreachable
From fd5b:107:24a8:a::1 icmp_seq=2 Destination unreachable: Address unreachable
From fd5b:107:24a8:a::1 icmp_seq=3 Destination unreachable: Address unreachable
64 bytes from fd5b:107:24a8:c::1: icmp_seq=4 ttl=63 time=1.01 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=5 ttl=63 time=0.401 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=6 ttl=63 time=0.356 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=7 ttl=63 time=0.360 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=8 ttl=63 time=0.356 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=9 ttl=63 time=0.356 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=10 ttl=63 time=0.352 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=11 ttl=63 time=0.514 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=12 ttl=63 time=0.396 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=13 ttl=63 time=0.284 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=14 ttl=63 time=0.429 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=15 ttl=63 time=0.393 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=16 ttl=63 time=0.349 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=17 ttl=63 time=0.387 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=18 ttl=63 time=0.401 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=19 ttl=63 time=0.351 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=20 ttl=63 time=0.380 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=21 ttl=63 time=0.418 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=22 ttl=63 time=0.435 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=23 ttl=63 time=0.391 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=24 ttl=63 time=0.340 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=25 ttl=63 time=0.351 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=26 ttl=63 time=0.443 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=27 ttl=63 time=0.377 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=28 ttl=63 time=0.332 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=29 ttl=63 time=0.396 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=30 ttl=63 time=0.381 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=31 ttl=63 time=0.370 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=32 ttl=63 time=0.502 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=33 ttl=63 time=0.360 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=34 ttl=63 time=0.346 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=35 ttl=63 time=0.332 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=36 ttl=63 time=0.451 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=37 ttl=63 time=0.366 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=38 ttl=63 time=0.415 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=39 ttl=63 time=0.361 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=40 ttl=63 time=0.329 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=41 ttl=63 time=0.383 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=42 ttl=63 time=0.494 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=43 ttl=63 time=0.412 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=44 ttl=63 time=0.368 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=45 ttl=63 time=0.340 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=46 ttl=63 time=0.420 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=47 ttl=63 time=0.450 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=48 ttl=63 time=0.354 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=49 ttl=63 time=0.343 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=50 ttl=63 time=0.385 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=51 ttl=63 time=0.361 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=52 ttl=63 time=0.455 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=53 ttl=63 time=0.428 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=54 ttl=63 time=0.366 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=55 ttl=63 time=0.374 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=56 ttl=63 time=0.313 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=57 ttl=63 time=0.429 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=58 ttl=63 time=0.399 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=59 ttl=63 time=0.357 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=60 ttl=63 time=0.334 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=61 ttl=63 time=0.378 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=62 ttl=63 time=0.347 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=63 ttl=63 time=0.429 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=64 ttl=63 time=0.445 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=65 ttl=64 time=2049 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=66 ttl=64 time=1025 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=67 ttl=64 time=0.574 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=68 ttl=64 time=0.231 ms
64 bytes from fd5b:107:24a8:c::1: icmp_seq=69 ttl=64 time=0.196 ms