Catalyst 3850: trunks with a native VLAN that does not exist

In short. Every trunk gets switchport trunk native vlan 998, and VLAN 998 is never created. An untagged frame on a trunk has no VLAN to join and is dropped. The provider's handoff is an access port in VLAN 999, and 999 rides the firewall port-channels as one more tagged VLAN. show interfaces trunk confirms the native, and a capture on the firewall's WAN confirms the provider's frames arrive tagged 999.

I want untagged frames on a trunk to go nowhere, and the WAN to reach both firewalls

Two things people search for in the same week. First, how to stop a forgotten tag from putting a server in VLAN 1. Second, how to get one provider cable to two firewalls without a second cable or an extra switch. The answer to both is the same trunk discipline. The addressing scheme that the VLAN numbers come from is in the decision post at the end.

What you need

  1. A WS-C3850-12X48U stack on IOS-XE 16.12.
  2. A VLAN plan where 998 and 999 are reserved: 998 is the poison native and is never created; 999 is WAN and is created.
  3. Two firewalls, each with a cross-stack LACP port-channel to the stack. In the examples they are fw1 and fw2 on Port-channel11 and Port-channel12.
  4. The provider handoff cabled to one stack member, here GigabitEthernet1/0/48.
flowchart LR
  ISP["Provider handoff<br/>untagged on their side"] -- "Gi1/0/48 access VLAN 999" --> ST["Catalyst stack<br/>native 998 on every trunk"]
  ST -- "Po11 trunk, all VLANs incl. 999" --> FW1["fw1<br/>WAN = VLAN 999 on the LAG"]
  ST -- "Po12 trunk, all VLANs incl. 999" --> FW2["fw2<br/>WAN = VLAN 999 on the LAG"]

Steps

  1. Create the VLANs you actually use, and only those. VLAN 999 is in the list. VLAN 998 is not, and will never be.
sw-a.site.example(config)# vlan 999
sw-a.site.example(config-vlan)# name WAN
sw-a.site.example(config-vlan)# exit
  1. On every trunk, set the native VLAN to 998 and list the allowed VLANs explicitly. The firewall port-channels carry everything because the firewalls are the gateway for everything, sync included.
sw-a.site.example(config)# interface Port-channel11
sw-a.site.example(config-if)# description fw1 lagg0
sw-a.site.example(config-if)# switchport mode trunk
sw-a.site.example(config-if)# switchport trunk native vlan 998
sw-a.site.example(config-if)# switchport trunk allowed vlan 1,2,5,10,11,100-199,999
sw-a.site.example(config-if)# switchport nonegotiate
sw-a.site.example(config-if)# spanning-tree portfast trunk

Repeat for Port-channel12, and set the same two switchport trunk lines on the physical members of each. A Proxmox node's trunk gets the same native and a shorter allowed list without 2 and 999.

  1. Configure the provider port as an access port in VLAN 999. The provider sends untagged frames; the switch tags them 999 on the way in and strips the tag on the way out. Turn off everything chatty towards a device you do not own.
sw-a.site.example(config)# interface GigabitEthernet1/0/48
sw-a.site.example(config-if)# description transit handoff
sw-a.site.example(config-if)# switchport mode access
sw-a.site.example(config-if)# switchport access vlan 999
sw-a.site.example(config-if)# switchport nonegotiate
sw-a.site.example(config-if)# no cdp enable
sw-a.site.example(config-if)# no lldp transmit
sw-a.site.example(config-if)# spanning-tree portfast
sw-a.site.example(config-if)# end
sw-a.site.example# write memory

Where a site has two independent transit uplinks, the second goes to the other member as its own access port in its own WAN VLAN. They are not bundled: a LAG implies one provider on both ends, and independent providers need independent ports so a failure shows as link-down rather than hiding in a half-working bundle.

  1. On each firewall, the WAN interface is VLAN 999 on the LACP aggregate. In OPNsense that is a VLAN interface with parent lagg0 and tag 999, assigned as WAN, with the public addressing from the provider on it and CARP for the shared address if you run a pair. Nothing on the firewall knows there is a switch between it and the provider.

  2. Decide whether you want the native VLAN tagged as well. vlan dot1q tag native in global configuration makes the switch tag frames in the native VLAN on every trunk and drop untagged frames on ingress. With a native VLAN that does not exist it changes nothing in practice, and I leave it off, but it is the belt to the braces if someone creates 998 by accident one day.

Verify it worked

Confirm the trunk state. Native should read 998 on every trunk and 998 must not appear in the forwarding list, because it has no members:

sw-a.site.example# show interfaces trunk

Port        Mode             Encapsulation  Status        Native vlan
Po11        on               802.1q         trunking      998
Po12        on               802.1q         trunking      998

Port        Vlans allowed on trunk
Po11        1-2,5,10-11,100-199,999
Po12        1-2,5,10-11,100-199,999

Port        Vlans in spanning tree forwarding state and not pruned
Po11        1-2,5,10-11,100-199,999
Po12        1-2,5,10-11,100-199,999

show vlan brief should list 999 with Gi1/0/48 as a member and should not list 998 at all. show mac address-table vlan 999 should show the provider's router MAC on Gi1/0/48 and both firewalls' MACs on their port-channels.

On the firewall, capture on the parent aggregate with the tag visible, and you should see the provider's gateway ARP replies and your own traffic carrying tag 999:

tcpdump -eni lagg0 vlan 999 and arp

Then from inside, trace to an external address and confirm it leaves via the firewall's VLAN 999 interface.

Gotchas

FAQ

Why not just use VLAN 1 as native and keep it empty? VLAN 1 cannot be deleted and it carries control protocols by default. Something always ends up in it. A number that does not exist cannot accumulate anything.

Does putting the WAN through the switch add a failure point? It replaces a dependency on one firewall's physical port with a dependency on the stack, which both firewalls depend on anyway. A firewall can now be swapped without touching the provider's cable, which has mattered more often than a stack failure has.

What about the sync VLAN between the firewalls? VLAN 2 rides the same port-channels. At the first site the firewalls used a crossover cable; it moved onto the stack when the second site needed the same pattern with no spare ports.

Related