Skip to content

Instantly share code, notes, and snippets.

@noslin005
Last active November 13, 2025 01:58
Show Gist options
  • Select an option

  • Save noslin005/f2655838cbc8a61c68a01700cd2bc35b to your computer and use it in GitHub Desktop.

Select an option

Save noslin005/f2655838cbc8a61c68a01700cd2bc35b to your computer and use it in GitHub Desktop.
Encapsulate VLAN using VXLAN tunnels on Linux

VXLAN+VLAN+BRIDGE+LINUX

  • Both Host A and Host B are running Linux with identical VXLAN and VLAN aware bridge configurations.
  • They communicate through a VXLAN overlay (ID 100) carried over multicast (239.1.1.1) on UDP port 4789.
  • VLANs are extended across the VXLAN tunnel.
  • Subinterfaces br0.10 and br0.20 provide access to specific VLAN networks (10 and 20).
  • Devices on the same VLAN across hosts can directly communicate as if they are on the same Layer 2 segment.

Use iproute2 tools (testing)

ip link del br0

ip link add br0 type bridge vlan_filtering 1
ip link add vxlan0 type vxlan id 100 group 239.1.1.1 dstport 4789 dev eth0
ip link set vxlan0 master br0
ip link set br0 up
ip link set vxlan0 up

for i in {1..20}; do 
    bridge vlan add dev vxlan0 vid $i
    bridge vlan add dev br0 self vid $i
done

ip link add link br0 name br0.10 type vlan id 10
ip link add link br0 name br0.20 type vlan id 20
ip link set dev br0.10 up
ip link set dev br0.20 up

# Host A
ip addr add 172.16.10.1/24 dev br0.10
ip addr add 172.16.20.1/24 dev br0.20


# Host B
ip addr add 172.16.10.2/24 dev br0.10
ip addr add 172.16.20.2/24 dev br0.20

Using nmcli for persistnce accross reboot

# Bridge
nmcli con del br0
nmcli con add type bridge ifname br0 con-name br0 bridge.vlan-filtering yes bridge.stp no
nmcli con modify br0 ipv4.method disabled ipv6.method disabled bridge.vlans 10,20
nmcli con up br0

# VXLAN
nmcli con del vxlan0
nmcli con add type vxlan ifname vxlan0 con-name vxlan0 vxlan.parent eth0 vxlan.ttl 16 vxlan.remote 239.1.1.1 vxlan.id 100 vxlan.destination-port 4789
nmcli con modify vxlan0 master br0
nmcli con modify vxlan0 bridge.vlans 10,20
nmcli con up vxlan0

# VLAN 10 (Host A)
nmcli con del br0.10
nmcli con add type vlan con-name br0.10 dev br0 id 10 ifname br0.10
nmcli con modify br0.10 ipv4.addresses 172.16.10.1/24 ipv4.method manual ipv6.method disabled
nmcli con up br0.10

# VLAN 20 (Host A)
nmcli con del br0.20
nmcli con add type vlan con-name br0.20 dev br0 id 20 ifname br0.20
nmcli con modify br0.20 ipv4.addresses 172.16.20.1/24 ipv4.method manual ipv6.method disabled
nmcli con up br0.20

Testing

# L2 ping from host A to Hosts B
arping -I br0.10 172.16.10.2

Topology

                            VXLAN ID 100 (Multicast Group: 239.1.1.1)
                      ┌────────────────────────────────────────────────┐
                      │                                                │
        Host A        │                 Ethernet over VXLAN            │      Host B
     ┌──────────┐     │         (VXLAN encapsulation via eth0)         │    ┌─────────┐
     │ br0      │     │                                                │    │ br0     │
     │  │       │     │                                                │    │  │      │
     │  └────┐  │     │                                                │    │  └────┐ │
     │   vxlan0 │◄────┘                                                └────►vxlan0│  │
     └────┬─────┘                                                           └────┬────┘
          │                                                                      │
   ┌──────┴──────┐                                                         ┌─────┴───────┐
   │ VLAN Bridge │                                                         │ VLAN Bridge │
   │  VLAN IDs   │                                                         │  VLAN IDs   │
   │  1..20      │                                                         │  1..20      │
   └────┬────────┘                                                         └──────┬──────┘
        │                                                                         │
 ┌──────┴──────┐                                                           ┌──────┴──────┐
 │ Subinterface│                                                           │ Subinterface│
 │ br0.10      │ 172.16.10.1/24                                            │ br0.10      │ 172.16.10.2/24
 │ br0.20      │ 172.16.20.1/24                                            │ br0.20      │ 172.16.20.2/24
 └─────────────┘                                                           └─────────────┘
@noslin005
Copy link
Author

#!/bin/bash

# =============================================================================
# VXLAN + Bridge + VLAN Setup Script (Unicast, No Multicast)
# Hosts: vm1 (192.168.24.11), vm2 (192.168.24.12), vm3 (192.168.24.13)
# VNI: 100, Port: 4789, Underlay: eth0
# VLANs: 10, 20 → br0.10, br0.20
# Overlay IPs: 172.16.{10,20}.X/24
# =============================================================================

set -euo pipefail

# --- Configuration ---
BRIDGE="br0"
VXLAN="vxlan0"
UNDERLAY="eth0"
VNI=100
DSTPORT=4789

# VLANs to create
declare -A VLANS=(
    [10]="172.16.10"
    [20]="172.16.20"
)

# Host-specific underlay IPs and host index
declare -A HOST_IP=(
    [vm1]="192.168.24.11"
    [vm2]="192.168.24.12"
    [vm3]="192.168.24.13"
)

HOST_INDEX="${HOST_IP[$HOSTNAME]:-}"
[[ -z "$HOST_INDEX" ]] && { echo "Error: Unknown hostname '$HOSTNAME'"; exit 1; }

# All peer IPs (excluding self)
PEERS=()
for h in "${!HOST_IP[@]}"; do
    [[ "$h" != "$HOSTNAME" ]] && PEERS+=("${HOST_IP[$h]}")
done

# --- Helper Functions ---
log() { echo "[+] $*"; }
warn() { echo "[!] $*" >&2; }

cleanup_if_exists() {
    local dev="$1"
    if ip link show "$dev" &>/dev/null; then
        log "Deleting existing $dev..."
        ip link del "$dev" 2>/dev/null || true
    fi
}

# --- Main Setup ---
main() {
    log "Starting VXLAN bridge setup on $HOSTNAME ($HOST_INDEX)..."

    # 1. Clean up old interfaces
    cleanup_if_exists "$BRIDGE"
    cleanup_if_exists "$VXLAN"

    # 2. Create bridge and VXLAN
    log "Creating bridge $BRIDGE with VLAN filtering..."
    ip link add name "$BRIDGE" type bridge vlan_filtering 1
    ip link set dev "$BRIDGE" up

    log "Creating VXLAN interface $VXLAN (VNI=$VNI)..."
    ip link add name "$VXLAN" type vxlan id "$VNI" dstport "$DSTPORT" dev "$UNDERLAY"
    ip link set dev "$VXLAN" master "$BRIDGE"
    ip link set dev "$VXLAN" up

    # 3. Configure VLAN subinterfaces
    for vid in "${!VLANS[@]}"; do
        local vlan_if="${BRIDGE}.${vid}"
        local subnet="${VLANS[$vid]}"

        log "Setting up VLAN $vid$vlan_if"

        # Create VLAN interface
        ip link add link "$BRIDGE" name "$vlan_if" type vlan id "$vid"
        ip link set dev "$vlan_if" up

        # Allow VLAN on bridge and VXLAN
        bridge vlan add dev "$BRIDGE" vid "$vid" self
        bridge vlan add dev "$VXLAN" vid "$vid"

        # Flush old addresses
        ip addr flush dev "$vlan_if"

        # Assign IP based on host
        local ip_suffix=$(echo "$HOST_INDEX" | awk -F. '{print $4}')
        ip addr add "${subnet}.${ip_suffix}/24" dev "$vlan_if"
    done

    # 4. Add FDB entries for BUM flooding (unicast replication)
    log "Adding FDB entries to peers: ${PEERS[*]}"
    for peer in "${PEERS[@]}"; do
        bridge fdb append to 00:00:00:00:00:00 dst "$peer" dev "$VXLAN" || true
    done

    log "Setup complete!"
    log "   Bridge: $BRIDGE"
    log "   VXLAN:  $VXLAN (VNI=$VNI, peers: ${#PEERS[@]})"
    log "   VLANs:  ${!VLANS[*]}"
}

# --- Optional Debug Mode ---
if [[ "${1:-}" == "--debug" ]]; then
    set -x
fi

# --- Run ---
main "$@"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment