Updated on 2026/08/05

Raspberry Pi Router on a Stick

A router on a stick is a router with only ethernet port, which is connected to a vlan switch. The single ethernet port is split into two ethernet ports via vlans (e.g., VLAN100 and VLAN500.


                       Internet /
                       ISP Router
                     192.168.66.0/24
                          |               -----------------------
                          |              | Router-on-a-Stick     |
                          |              | VLAN100 192.168.66.54 |
                          |              | VLAN300 192.168.77.1  |
                          |               -----------------------     
                          |                          | 
                          |                          | Trunk or Stick
                          |                          |         
                          |        -------------------------------------------------------------  
                          |        |     1     |     2     |     3     |     4     |     5     |     
                          |        -------------------------------------------------------------   
                          |              |                       |         Netgear GS105v2
                          |    in        |                       |
                          ---------------                        |
                                                                out 
                                                           192.168.77.0/24   
            
Port # 1 2 3 4 5
Configure VLAN 1 U U U U U
in VLAN 100 U T
trunk VLAN 200 T
out VLAN 300 T U
PVID 100 200 300 1 1
Switching Table for a Router on Stick.

Frames eminating from the trunk go to ether Port 1 or Port 3, but not both.

Network-Manager Setting

I don't believe I install vlan or the monprobe, and it still worked?

VLAN100

sudo nmcli con add con-name VLAN100 type vlan dev eth0 id 100 ipv4.address 192.168.66.45/24 ipv4.method manual ipv4.gateway 192.168.66.1 ipv4.dns 192.168.66.1 ipv6.method disabled

The output of

sudo cat /etc/NetworkManager/system-connections/VLAN100.nmconnection

is:


[connection]
id=VLAN100
uuid=e63734b8-6ae9-4a93-b768-bc4cad6cc71d
type=vlan
timestamp=1785877335

[ethernet]

[vlan]
flags=1
id=100
parent=eth0

[ipv4]
address1=192.168.66.45/24
dns=192.168.66.1;
gateway=192.168.66.1
method=manual

[ipv6]
addr-gen-mode=default
method=disabled

[proxy]
            

VLAN300

sudo nmcli con add con-name VLAN300 type vlan dev eth0 id 300 ipv4.address 192.168.77.1/24 ipv6.method disabled ipv4.method shared ipv4.shared-dhcp-range '192.168.77.100,192.168.77.199'

The output of

sudo cat /etc/NetworkManager/system-connections/VLAN300.nmconnection

is:


[connection]
id=VLAN300
uuid=9d08cf28-7d01-4a95-b1b9-f06912c6dc27
type=vlan
timestamp=1785877337

[ethernet]

[vlan]
flags=1
id=300
parent=eth0

[ipv4]
address1=192.168.77.1/24
method=shared
shared-dhcp-range=192.168.77.100,192.168.77.199

[ipv6]
addr-gen-mode=default
method=disabled

[proxy]
            

The output of

ip a

is:


1: lo:  mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host noprefixroute 
       valid_lft forever preferred_lft forever
2: eth0:  mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether e4:5f:01:9c:c4:eb brd ff:ff:ff:ff:ff:ff
3: wlan0:  mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether e4:5f:01:9c:c4:ed brd ff:ff:ff:ff:ff:ff
4: eth0.300@eth0:  mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether e4:5f:01:9c:c4:eb brd ff:ff:ff:ff:ff:ff
    inet 192.168.77.1/24 brd 192.168.77.255 scope global noprefixroute eth0.300
       valid_lft forever preferred_lft forever
5: eth0.100@eth0:  mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether e4:5f:01:9c:c4:eb brd ff:ff:ff:ff:ff:ff
    inet 192.168.66.45/24 brd 192.168.66.255 scope global noprefixroute eth0.100
       valid_lft forever preferred_lft forever            
            

Network Manager Generated Nftables

Because the VLAN300 ipv4.method was shared, the Network Manager will not only implement the DHCP protocol and supply ipv4 address from the specified range, but it will also generate nftables for fowarding and masquerade.

To see the nftables:

sudo systemctl enable nftables.service
sudo reboot

Then execute

sudo nft list ruleset

The output is:


# Generated by Debian 13 (trixie) / Raspberry Pi OS.            
table inet filter {
	chain input {
		type filter hook input priority filter; policy accept;
	}

	chain forward {
		type filter hook forward priority filter; policy accept;
	}

	chain output {
		type filter hook output priority filter; policy accept;
	}
}

# Generated by the NetworkManager because ipv4.method was shared.
table ip nm-shared-eth0.300 {
	chain nat_postrouting {
		type nat hook postrouting priority srcnat; policy accept;
		ip saddr 192.168.77.0/24 ip daddr != 192.168.77.0/24 masquerade
	}

	chain filter_forward {
		type filter hook forward priority filter; policy accept;
		ip daddr 192.168.77.0/24 oifname "eth0.300" ct state { established, related } accept
		ip saddr 192.168.77.0/24 iifname "eth0.300" accept
		iifname "eth0.300" oifname "eth0.300" accept
		iifname "eth0.300" reject
		oifname "eth0.300" reject
	}
}            
            

The output of sudo nft list rulest, only list rules and supresses all comments. The two comments were added to show which tables were produced by what.

The DHCP protocol allows for static addresses so you do not have to actually implementing the method shared, but you need the method to be shared to get the Network Manager to generate the nftables.

On the Client Side

Implementations

This was implemented on both a Raspberry Pi 5B and a RPi Zero 2W. For the Zero 2W, I had to add a USB-to-Ethernet adapter. The USB port on to Zero 2W is USB 2.0 and limited to 480 Mb/s maximum. It was fast enough for web surfing, but its not as fast as 1GB/s Ehternet.

Maybe the Zero 3w wil have USB 3.0, which has a trasfer rate of 5GB/s.

References

  1. YouTube - How to build a Router with a Raspberry Pi and managed Switch VLANs with OpenWrt
  2. Raspberry Pi as a Router Using a Single Network Interface
  3. How to Really Disable IPv6 on Raspberry PI

Explanding the Router-on-a-Stick

A Router-on-a-Stick can be expanded to create multiple subnets and seperate traffic.


                       Internet /
                       ISP Router
                     192.168.66.0/24
                          |               -----------------------
                          |              | Router-on-a-stick     |
                          |              | VLAN100 192.168.66.54 |
                          |              | VLAN300 192.168.77.1  |
                          |              | VLAN400 192.168.88.1  |
                          |              | VLAN500 192.168.99.1  |
                          |               -----------------------     
                          |                          | 
                          |                          | Trunk or Stick
                          |                          |         
                          |        -------------------------------------------------------------  
                          |        |     1     |     2     |     3     |     4     |     5     |     
                          |        -------------------------------------------------------------   
                          |              |                       |           |            |
                          |    in        |                       |           |            |
                          ---------------                        |          out 2         |
                                                                 |      192.168.88.0/24   |
                                                                 |                        |
                                                               out 1                     out 3
                                                           192.168.77.0/24          192.166.99.0/24   
            
Port # 1 2 3 4 5
Configure VLAN 1 U U U U U
in VLAN 100 U T
trunk VLAN 200 T
out 1 VLAN 300 T U
out 2 VLAN 400 T U
out 1 VLAN 50 T U U
PVID 100 200 300 1 1
Switching Table for Expanded Router on Stick.

Network Manager

There are no changes to VLAN100 and/or VLAN300.

VLAN400

sudo nmcli con add con-name VLAN400 dev eth0 id 300 ipv4.address 192.168.88.45/24 ipv6.method disabled ipv4.method shared ipv4.shared-dhcp-range '192.168.88.100,192.168.88.199'

VLAN500

sudo nmcli con add con-name VLAN500 dev eth0 id 300 ipv4.address 192.168.99.45/24 ipv6.method disabled ipv4.method shared ipv4.shared-dhcp-range '192.168.99.100,192.168.99.199'

Allowing Access from an Upstream Host

The nftable created by the Network Manager only allows traffic that originates behind the router. That is, it reject any unsolicited traffic comming into the router, and this is what a router facing the Internet should do. However, I have a laptop that is in front of the router, and it needs to communicate with host behind the router.

To allow this, I copied the nftable ruleset generated by the Network Manager, disabled the Network Manager from generating nftables, and implemented the copied ruleset manualy, and finally modified the nftable. The are a lots os steps, but they are straight forward:

  1. Enable nftable:

    sudo systemctl enable nftables
    sudo reboot

  2. Copy the rules created by the Network Manager into a file in the /etc/ directory so they can later be modified.

    sudo nft list ruleset > /etc/my_router.nft

  3. At the end of /etc/ntfables.conf add the following (including the quotations):

    include "/etc/my_router.nft"

  4. Stop the Network Manager from generating nftables for forwarding and masquerade.
     
    Creat a configuration file in "/etc/NetworkManager/conf.d" such as "no_nft.conf".

    sudo vi no_nft.conf

    Place the following two lines in the file:

    [main]
    firewall-backend=none

  5. Enable IPv4 forwarding:
     
    Create a new configuration file in /etc/sysctl.d, such as, "allow_forwarding.conf".

    sudo vi allow_forwarding.conf

    Add the following line to the file:

    net.ipv4.ip_forward=1

  6. After rebooting, you are back to where you started at, but you can now modify the old Network Manager rule file in /etc.

    sudo reboot

  7. My modified table is below:
    • 
      table ip nm-shared-end0.300 {
      	chain nat_postrouting {
      		type nat hook postrouting priority srcnat; policy accept;
      		ip saddr 192.168.66.0/24 ip daddr != 192.168.66.0/24 masquerade
      
                      # added by me on 2026/08/01
                      ip saddr != 192.168.66.0/24 ip daddr 192.168.66.0/24 masquerade
      	}
      
      	chain filter_forward {
      		type filter hook forward priority filter; policy accept;
      		ip daddr 192.168.66.0/24 oifname "end0.300" ct state { established, related } accept
      
                      # added by me on 2026/08/01
                      ip daddr 192.168.66.0/24 oifname "end0.300" ip saddr 192.168.37.67 accept
      
      		ip saddr 192.168.66.0/24 iifname "end0.300" accept
      		iifname "end0.300" oifname "end0.300" accept
      		iifname "end0.300" reject
      		oifname "end0.300" reject
      	}
      }
                                   
  8. Finally, you need to add a route on the laptop to access the hosts in the subnet:

    sudo nmcli con mod eth0 +ipv4.routes "192.168.77.0/24 192.168.66.54"