Muliti VLAN tagging and DHCP for single port

I have a rather interesting setup to begin with and now I want to add VLANs on top of it. I have two routers, both running 18.06.0. I will refer to them as R1 and R2. R1 is connected to my ISP and functions as the DHCP server for the network. R2 is connected to R1 and has its WAN interface bridged to the LAN interfaces and firewall disabled. It is essentially a L3 switch. Then I have a PC running ESXi connected to R2. In ESXi, I am able to configure virtual port groups and tagging for my VMs. My question is, with my current setup, can I pass more than one VLAN, e.g. VLAN 0 and VLAN 100, to R1 and have it give DHCP leases in different subnets?

I have already looked into VLANs on OpenWrt, but I haven't found anything about multiple VLANs on a single interface or passing VLAN traffic upstream. Also, I know I should have another NIC for the ESXi host, but I'm running a home setup, and have limited ports on R2.

Thoughts are appreciated!

Yes, multiple ways. (Edit: VLAN 0 is not a valid VLAN)

My preferred approach is that on a trunk, nothing is every untagged and the PVID is 4095 or a suitable "dead end" VLAN, if the switch doesn't support 4095 for a PVID.

On whatever physical switch port you want to trunk, you assign the VLANs as "tagged" on both routers and the PVID to your chosen dead-end VLAN. At this point, you should have traffic flowing between the two routers for as many VLANs as you've configured that way.

On you "main" router, you can then configure DHCP as you wish for each VLAN, as well as firewall rules to allow/restrict/prohibit inter-VLAN traffic to meet your needs. Personally, I'd also configure your "downstream" router to prohibit traffic between VLANs, either or both with firewall rules or by disabling forwarding completely at the kernel/stack level.

Thank you jeff for the thorough reply. I have some questions to follow up.

I am able to tag (trunk) the VLANs on R1, but not on R2 as it doesn't seem to have an internal switch that I can configure. Do you know if there is some way to configure the interfaces for trunking?

I also agree with your suggestion to prohibit VLAN cross-talk on R2, but I don't know how to go about doing this. I have the firewall disabled as per the instructions for bridging the LAN and WAN interfaces, so I would like to see if I can disable forwarding completely like you described. I suppose I should get the VLANs working first though.

I appreciate your help thus far. It's amazing how little documentation there is for VLANs on OpenWrt.

You can define VLAN-specific sub-interfaces on the main interface (eth0.NNNN or the like) in the usual Linux methods, and possibly through LuCI as well.

Since I think you're running in a VM, you can also define the VLAN-specific interfaces on the host, then pass those derived interfaces, or ones bridged to them, to the client. How that is done is specific to the host and VM management software running on the host.

1 Like

What make / model is R2?

When there is a port with no internal switch, and you have direct control of it (i.e OpenWrt on the bare metal, not a VM), declare VLANs with the syntax like 'eth0.3' and packets will go out tagged 3 on the cable. You can likewise attach eth0.4 to another network bridge and those packets will be tagged with 4.

Thank you all for the replies. I did some more reading and testing, and I've had a little success, but I still don't have VLANs working on the ESXi host.

@lleachii, I did some reading on the links provided, but like I said, there's not a whole lot in there about trunking on a device that doesn't have an internal switch.

@jeff, @mk24, I am not running OpenWrt in a VM. R2 is an Actiontec MI424WR Rev. D, which was provided by Frontier (back when they were called Verizon). It does not have an internal switch, so it seems I do have to configure VLANs using interface.vlan notation. I've configured everything in LUCI thus far, and here is what I have currently in my network configs:

R1 /etc/config/network
config interface 'loopback'
	option ifname 'lo'
	option proto 'static'
	option ipaddr ''
	option netmask ''

config globals 'globals'
	option ula_prefix 'fd0e:7c8c:24af::/48'

config interface 'lan'
	option type 'bridge'
	option ifname 'eth0.1'
	option proto 'static'
	option netmask ''
	option ip6assign '60'
	option ipaddr ''

config interface 'wan'
	option ifname 'eth1.2'
	option proto 'dhcp'
	option hostname 'wireless-broadband-router'
	option peerdns '0'
	option dns ''

config interface 'wan6'
	option ifname 'eth1.2'
	option proto 'dhcpv6'
	option reqaddress 'try'
	option reqprefix 'auto'
	option peerdns '0'
	option dns '2606:4700:4700::1111 2606:4700:4700::1001'
	option auto '0'

config switch
	option name 'switch0'
	option reset '1'
	option enable_vlan '1'

config switch_vlan
	option device 'switch0'
	option vlan '1'
	option vid '1'
	option ports '0 1 2 3 5t'

config switch_vlan
	option device 'switch0'
	option vlan '2'
	option ports '4 6t'
	option vid '2'

config switch_vlan
	option device 'switch0'
	option vlan '3'
	option vid '10'
	option ports '0t 5t'

config switch_vlan
	option device 'switch0'
	option vlan '4'
	option vid '100'
	option ports '0t 5t'

config interface 'vlan10'
	option proto 'static'
	option ifname 'eth0.10'
	option ipaddr ''
	option netmask ''

config interface 'vlan100'
	option proto 'static'
	option ifname 'eth0.100'
	option ipaddr ''
	option netmask ''
	option gateway ''
R2 /etc/config/network
config interface 'loopback'
	option ifname 'lo'
	option proto 'static'
	option ipaddr ''
	option netmask ''

config globals 'globals'
	option ula_prefix 'fd1a:2ab9:bf0e::/48'

config interface 'lan'
	option type 'bridge'
	option proto 'static'
	option netmask ''
	option ip6assign '60'
	option ipaddr ''
	option gateway ''
	option dns ''
	option ifname 'eth0 eth1'

config interface 'vlan10'
	option ifname 'eth0.10 eth1.10'
	option proto 'dhcp'
	option type 'bridge'
	option auto '0'

config interface 'vlan100'
	option type 'bridge'
	option proto 'dhcp'
	option ifname 'eth0.100 eth1.100'
	option defaultroute '0'
	option auto '0'

The main part to look at here are the settings for interfaces vlan10 and vlan100 on R2. I have bridged the VLANs from eth0 (lan ports) to eth1 (wan port). Keep in mind R2 is functioning as a dumb AP and has LAN/WAN interfaces bridged already and firewall disabled. Interface eth1 on R2 is connected to R1. Using this configuration, I am able to pull a valid IP address for R2 from the DHCP server on R1 (within the IP range I configured for those VLANs), so VLANs 10 and 100 are working between R1 and R2. However, I am not able to connect from any virtual interface configured for VLAN 10 or VLAN 100 on an ESXi guest. I tried static assigning an IP address with no success. I'm not really sure how ESXi assigns VLANs to the outbound packets, but I can capture some traffic with wireshark if anyone thinks it will help diagnose the issue.

EDIT: The article here suggests that ESXi follows the 802.1Q standard.

I am also considering putting down twenty bucks for a cheap "smart" switch to see if I would be able to trunk the VLANs with that. I'm not ready to invest in a robust managed switch when all I really want are VLANs.

As always, any thoughts are appreciated.

The wiki page says that the 4 LAN ports of the MI424WR go through a Micrel KSZ8995 switch chip. This chip supports VLANs (with the typical small consumer router switch limitation of 16 VLANs and the need to set vid to use VLAN tags above 15).

Does the command swconfig list show any switches?

The SG-105E says "Unmanaged" on the front panel. It may or may not be a 5 port version of the SG-108E. The SG-108E is a decent cheap "web managed" switch.

1 Like

swconfig was not installed on the router by default, but even after installing 'swconfig' and 'kmod-swconfig' packages, swconfig list returns no output. I may need to compile my own firmware if I want swconfig support...

Yes, the SG-105E is a smaller version of the SG-108E. They're not really "managed" switches, but they do support VLAN tagging and other simple features. I will pick one up from the local Fry's on my way home tomorrow.

Try also installing the ks8995 kmod. You should not need to custom compile.

1 Like

I installed 'kmod-spi-ks8995' and rebooted. Still no results from swconfig list
If there's a kz8995, it's not in the package lists for 18.06.0

Sorry 'kz' was a typo.

In the kernel boot log, is there any mention of a switch being found?

They are pretty useful little devices for the cost, but I'm not sure they support trunking, that is, passing tagged packets with tags that aren't explicitly configured.

Zyxel devices in the GS1900 series DO support this.

@mk24, from what I can tell, the kernel log contains no mention of a switch.

[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 4.9.111 ( (gcc version 7.3.0 (OpenWrt GCC 7.3.0 r7101-a63e38b) ) #0 Sat Jul 14 11:10:08 2018
[    0.000000] CPU: XScale-IXP42x Family [690541c2] revision 2 (ARMv5TE), cr=000039ff
[    0.000000] CPU: VIVT data cache, VIVT instruction cache
[    0.000000] Machine: Actiontec MI424WR
[    0.000000] Memory policy: Data cache writeback
[    0.000000] On node 0 totalpages: 8192
[    0.000000] free_area_init_node: node 0, pgdat c039f048, node_mem_map c1fb9000
[    0.000000]   DMA zone: 64 pages used for memmap
[    0.000000]   DMA zone: 0 pages reserved
[    0.000000]   DMA zone: 8192 pages, LIFO batch:0
[    0.000000] pcpu-alloc: s0 r0 d32768 u32768 alloc=1*32768
[    0.000000] pcpu-alloc: [0] 0 
[    0.000000] Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 8128
[    0.000000] Kernel command line: root=/dev/mtdblock2 rootfstype=squashfs,jffs2 noinitrd console=ttyS0,115200
[    0.000000] PID hash table entries: 128 (order: -3, 512 bytes)
[    0.000000] Dentry cache hash table entries: 4096 (order: 2, 16384 bytes)
[    0.000000] Inode-cache hash table entries: 2048 (order: 1, 8192 bytes)
[    0.000000] Memory: 28504K/32768K available (2961K kernel code, 158K rwdata, 400K rodata, 152K init, 209K bss, 4264K reserved, 0K cma-reserved)
[    0.000000] Virtual kernel memory layout:
[    0.000000]     vector  : 0xffff0000 - 0xffff1000   (   4 kB)
[    0.000000]     fixmap  : 0xffc00000 - 0xfff00000   (3072 kB)
[    0.000000]     vmalloc : 0xc2800000 - 0xff800000   ( 976 MB)
[    0.000000]     lowmem  : 0xc0000000 - 0xc2000000   (  32 MB)
[    0.000000]     modules : 0xbf000000 - 0xc0000000   (  16 MB)
[    0.000000]       .text : 0xc0008000 - 0xc02ec9d8   (2963 kB)
[    0.000000]       .init : 0xc0352000 - 0xc0378000   ( 152 kB)
[    0.000000]       .data : 0xc0378000 - 0xc039f8a0   ( 159 kB)
[    0.000000]        .bss : 0xc039f8a0 - 0xc03d3e50   ( 210 kB)
[    0.000000] SLUB: HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[    0.000000] NR_IRQS:64
[    0.000024] sched_clock: 32 bits at 66MHz, resolution 15ns, wraps every 32212576760ns
[    0.000080] clocksource: OSTS: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 28669193401 ns
[    0.000187] Calibrating delay loop... 531.66 BogoMIPS (lpj=2658304)
[    0.050192] pid_max: default: 32768 minimum: 301
[    0.050432] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes)
[    0.050463] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes)
[    0.051747] CPU: Testing write buffer coherency: ok
[    0.052590] Setting up static identity map for 0x81e0 - 0x8230
[    0.055431] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
[    0.055480] futex hash table entries: 256 (order: -1, 3072 bytes)
[    0.056804] NET: Registered protocol family 16
[    0.058426] DMA: preallocated 256 KiB pool for atomic coherent allocations
[    0.060063] IXP4xx: Using 16MiB expansion bus window size
[    0.061460] PCI: IXP4xx is host
[    0.061496] PCI: IXP4xx Using direct access for memory space
[    0.061766] PCI host bridge to bus 0000:00
[    0.061807] pci_bus 0000:00: root bus resource [io  0x0000-0xffff]
[    0.061834] pci_bus 0000:00: root bus resource [mem 0x48000000-0x4bffffff]
[    0.061858] pci_bus 0000:00: No busn resource found for root bus, will use [bus 00-ff]
[    0.061981] pci 0000:00:0d.0: [17e6:0021] type 00 class 0x020000
[    0.062036] pci 0000:00:0d.0: reg 0x10: [mem 0x00000000-0x0003ffff]
[    0.062061] pci 0000:00:0d.0: reg 0x14: [mem 0x00000000-0x00ffffff pref]
[    0.062596] pci 0000:00:0e.0: [168c:001a] type 00 class 0x020000
[    0.062646] pci 0000:00:0e.0: reg 0x10: [mem 0x00000000-0x0000ffff]
[    0.063129] pci 0000:00:0f.0: [17e6:0021] type 00 class 0x020000
[    0.063175] pci 0000:00:0f.0: reg 0x10: [mem 0x00000000-0x0003ffff]
[    0.063199] pci 0000:00:0f.0: reg 0x14: [mem 0x00000000-0x00ffffff pref]
[    0.063677] PCI: bus0: Fast back to back transfers enabled
[    0.063724] pci_bus 0000:00: busn_res: [bus 00-ff] end is updated to 00
[    0.063805] pci 0000:00:0d.0: BAR 1: assigned [mem 0x48000000-0x48ffffff pref]
[    0.063842] pci 0000:00:0f.0: BAR 1: assigned [mem 0x49000000-0x49ffffff pref]
[    0.063872] pci 0000:00:0d.0: BAR 0: assigned [mem 0x4a000000-0x4a03ffff]
[    0.063902] pci 0000:00:0f.0: BAR 0: assigned [mem 0x4a040000-0x4a07ffff]
[    0.063932] pci 0000:00:0e.0: BAR 0: assigned [mem 0x4a080000-0x4a08ffff]
[    0.091314] clocksource: Switched to clocksource OSTS
[    0.093254] NET: Registered protocol family 2
[    0.094840] TCP established hash table entries: 1024 (order: 0, 4096 bytes)
[    0.094902] TCP bind hash table entries: 1024 (order: 0, 4096 bytes)
[    0.094942] TCP: Hash tables configured (established 1024 bind 1024)
[    0.095192] UDP hash table entries: 256 (order: 0, 4096 bytes)
[    0.095230] UDP-Lite hash table entries: 256 (order: 0, 4096 bytes)
[    0.095635] NET: Registered protocol family 1
[    0.095774] PCI: CLS 32 bytes, default 32
[    0.101488] IXP4xx Queue Manager initialized.
[    0.103353] Crashlog allocated RAM at address 0x1f00000
[    0.104906] workingset: timestamp_bits=30 max_order=13 bucket_order=0
[    0.126765] squashfs: version 4.0 (2009/01/31) Phillip Lougher
[    0.126799] jffs2: version 2.2 (NAND) (SUMMARY) (LZMA) (RTIME) (CMODE_PRIORITY) (c) 2001-2006 Red Hat, Inc.
[    0.146108] io scheduler noop registered
[    0.146140] io scheduler deadline registered (default)
[    0.218343] Serial: 8250/16550 driver, 16 ports, IRQ sharing enabled
[    0.224758] console [ttyS0] disabled
[    0.224882] serial8250.0: ttyS0 at MMIO 0xc8000000 (irq = 15, base_baud = 921600) is a XScale
[    0.718880] console [ttyS0] enabled
[    0.723438] serial8250.0: ttyS1 at MMIO 0xc8001000 (irq = 13, base_baud = 921600) is a XScale
[    0.734180] IXP4XX-Flash.0: Found 1 x16 devices at 0x0 in 16-bit bank. Manufacturer ID 0x000089 Chip ID 0x000017
[    0.744515] Intel/Sharp Extended Query Table at 0x0031
[    0.749704] Intel/Sharp Extended Query Table at 0x0031
[    0.754923] Using buffer write method
[    0.758581] cfi_cmdset_0001: Erase suspend on write enabled
[    0.764180] erase region 0: offset=0x0,size=0x20000,blocks=64
[    0.766608] Searching for RedBoot partition table in IXP4XX-Flash.0 at offset 0x7e0000
[    0.889955] 5 RedBoot partitions found on MTD device IXP4XX-Flash.0
[    0.896345] Creating 5 MTD partitions on "IXP4XX-Flash.0":
[    0.901865] 0x000000000000-0x000000040000 : "RedBoot"
[    0.909855] 0x000000040000-0x0000001a0000 : "linux"
[    0.918182] 0x0000001a0000-0x0000007e0000 : "rootfs"
[    0.926562] mtd: device 2 (rootfs) set to be root filesystem
[    0.932427] 1 squashfs-split partitions found on MTD device rootfs
[    0.938631] 0x0000003c0000-0x0000007e0000 : "rootfs_data"
[    0.947400] 0x0000007e0000-0x0000007ff000 : "FIS directory"
[    0.956368] 0x0000007ff000-0x000000800000 : "RedBoot config"
[    0.973362] libphy: IXP4xx MII Bus: probed
[    0.979224] eth0: MII PHY 32 on NPE-B
[    0.984963] eth1: MII PHY 5 on NPE-C
[    0.989241] i2c /dev entries driver
[    0.993443] ixp4xx_wdt: timer heartbeat 60 sec
[    1.001557] NET: Registered protocol family 10
[    1.016523] NET: Registered protocol family 17
[    1.021088] bridge: filtering via arp/ip/ip6tables is no longer available by default. Update your scripts to load br_netfilter if you need this.
[    1.034154] 8021q: 802.1Q VLAN Support v1.8
[    1.038545] XScale DSP coprocessor detected.
[    1.045405] hctosys: unable to open rtc device (rtc0)
[    1.059670] VFS: Mounted root (squashfs filesystem) readonly on device 31:2.
[    1.067684] Freeing unused kernel memory: 152K
[    1.072197] This architecture does not have kernel memory protection.
[    2.050458] init: Console is alive
[    2.054435] init: - watchdog -
[    2.531349] random: fast init done
[    3.023398] kmodloader: loading kernel modules from /etc/modules-boot.d/*
[    3.031472] kmodloader: done loading kernel modules from /etc/modules-boot.d/*
[    3.049555] init: - preinit -
[    4.987008] random: jshn: uninitialized urandom read (4 bytes read)
[    5.036741] random: jshn: uninitialized urandom read (4 bytes read)
[    5.088671] NPE-B: firmware's license can be found in /usr/share/doc/LICENSE.IPL
[    5.096198] NPE-B: firmware functionality 0x2, revision 0x2:1
[    5.103533] eth0: link up, speed 0 Mb/s, full duplex
[   21.616914] jffs2: notice: (779) jffs2_build_xattr_subsystem: complete building xattr subsystem, 0 of xdatum (0 unchecked, 0 orphan) and 0 of xref (0 dead, 0 orphan) found.
[   21.639047] mount_root: switching to jffs2 overlay
[   21.683798] urandom-seed: Seeding with /etc/urandom.seed
[   21.894902] eth0: link up, speed 0 Mb/s, full duplex
[   21.923356] procd: - early -
[   21.926491] procd: - watchdog -
[   23.405481] procd: - watchdog -
[   23.409183] procd: - ubus -
[   23.507914] random: ubusd: uninitialized urandom read (4 bytes read)
[   23.520143] random: ubusd: uninitialized urandom read (4 bytes read)
[   23.527555] random: ubusd: uninitialized urandom read (4 bytes read)
[   23.538246] procd: - init -
[   24.389871] kmodloader: loading kernel modules from /etc/modules.d/*
[   24.484165] ip6_tables: (C) 2000-2006 Netfilter Core Team
[   24.545235] u32 classifier
[   24.547972]     input device check on
[   24.551732]     Actions configured
[   24.575267] Mirror/redirect action on
[   24.597741] nf_conntrack version 0.5.0 (1024 buckets, 4096 max)
[   24.722294] Loading modules backported from Linux version wt-2017-11-01-0-gfe248fc2c180
[   24.730326] Backport generated by backports.git v4.14-rc2-1-31-g86cf0e5d
[   24.786609] ip_tables: (C) 2000-2006 Netfilter Core Team
[   25.121180] xt_time: kernel timezone is -0000
[   25.222463] PPP generic driver version 2.4.2
[   25.233528] NET: Registered protocol family 24
[   25.268567] ath5k 0000:00:0e.0: enabling device (0340 -> 0342)
[   25.274975] ath5k 0000:00:0e.0: registered as 'phy0'
[   25.654974] random: crng init done
[   25.658406] random: 5 urandom warning(s) missed due to ratelimiting
[   25.905523] ath: EEPROM regdomain: 0x0
[   25.905539] ath: EEPROM indicates default country code should be used
[   25.905545] ath: doing EEPROM country->regdmn map search
[   25.905563] ath: country maps to regdmn code: 0x3a
[   25.905571] ath: Country alpha2 being used: US
[   25.905577] ath: Regpair used: 0x3a
[   25.906675] ieee80211 phy0: Selected rate control algorithm 'minstrel_ht'
[   25.909985] ath5k: phy0: Atheros AR2413 chip found (MAC: 0x78, PHY: 0x45)
[   25.948491] kmodloader: done loading kernel modules from /etc/modules.d/*
[   33.768461] eth0: link up, speed 0 Mb/s, full duplex
[   33.793827] br-lan: port 1(eth0) entered blocking state
[   33.799078] br-lan: port 1(eth0) entered disabled state
[   33.805258] device eth0 entered promiscuous mode
[   33.825216] br-lan: port 1(eth0) entered blocking state
[   33.830472] br-lan: port 1(eth0) entered forwarding state
[   33.836301] IPv6: ADDRCONF(NETDEV_UP): br-lan: link is not ready
[   33.983625] NPE-C: firmware's license can be found in /usr/share/doc/LICENSE.IPL
[   33.991048] NPE-C: firmware functionality 0x5, revision 0x2:1
[   34.036209] br-lan: port 2(eth1) entered blocking state
[   34.041576] br-lan: port 2(eth1) entered disabled state
[   34.047579] device eth1 entered promiscuous mode
[   34.657992] br-vlan100: port 1(eth0.100) entered blocking state
[   34.664049] br-vlan100: port 1(eth0.100) entered disabled state
[   34.670739] device eth0.100 entered promiscuous mode
[   34.726107] br-vlan100: port 1(eth0.100) entered blocking state
[   34.732159] br-vlan100: port 1(eth0.100) entered forwarding state
[   34.801503] IPv6: ADDRCONF(NETDEV_CHANGE): br-lan: link becomes ready
[   35.041702] eth1: link up, speed 100 Mb/s, full duplex
[   35.046915] br-lan: port 2(eth1) entered blocking state
[   35.052240] br-lan: port 2(eth1) entered forwarding state
[   35.170646] br-vlan100: port 2(eth1.100) entered blocking state
[   35.176698] br-vlan100: port 2(eth1.100) entered disabled state
[   35.201602] device eth1.100 entered promiscuous mode
[   35.206861] br-vlan100: port 2(eth1.100) entered blocking state
[   35.212925] br-vlan100: port 2(eth1.100) entered forwarding state
[   40.217239] IPv6: ADDRCONF(NETDEV_UP): wlan0: link is not ready
[   40.240837] br-lan: port 3(wlan0) entered blocking state
[   40.246292] br-lan: port 3(wlan0) entered disabled state
[   40.252476] device wlan0 entered promiscuous mode
[   41.096206] IPv6: ADDRCONF(NETDEV_CHANGE): wlan0: link becomes ready
[   41.102971] br-lan: port 3(wlan0) entered blocking state
[   41.108300] br-lan: port 3(wlan0) entered forwarding state

@dlakelan, I read in a review for this switch that they do in fact support setting ports to a "tagged" state. I'm guessing this is what you're referring to.

I have one of the 8 port switches, you can in fact set up several VLANs with tagging, yes, they're very useful. But if you suddenly start sending tags that aren't one of the configured tags I'm not sure what it will do, and I would guess probably drop them on the floor. So if you want a configuration like this:

NET 1 ------- Port1 : your switch : Port 5------- NET 2

and NET 1 could maybe send 5 or 10 different tags at any given point, and NET 2 can interpret those same tags, but "your switch" only knows about a few explicitly configured tags that doesn't include everything NET 1 and NET 2 could send, then NET 1 and NET 2 won't be able to fully communicate. You'll have to either configure all 5 or 10 tags on "your switch" and make port 1 and port 5 tagged members of all those VLANS, or you'll have to get a switch that supports "trunking" that is "passing VLANS it doesn't know about to certain limited ports" which the Zyxel devices can do.

If you are ok with just configuring your switch to handle some small set of tags you're good to go with the little TP-Link job.

I picked up the switch today and connected it between R2 and R1. Without configuring anything on the switch, the VLANs on the ESXi host came up and the devices were able to grab IP addresses from R1. R2 untagged connected to R1 just fine as well. Rather interesting considering that the default setting on the switch is a port-based VLAN with all the ports on VLAN 1. In theory, this means that it will trunk (tag all VLANs) going to R1, and I can use them as long as I have it configured on R1. I'll play around with the VLAN and 802.1Q settings this weekend, but for now my setup is confirmed working the way I want it to.

If anyone is interested with troubleshooting the topology that I originally had in this thread, PM me and I can provide my router for some testing.

Thank you all for the input and the help provided in finding a solution.