Skip to content

Instantly share code, notes, and snippets.

@ankurpandeyvns
Created February 25, 2026 09:02
Show Gist options
  • Select an option

  • Save ankurpandeyvns/f43365027210dd98651c88774fb182ba to your computer and use it in GitHub Desktop.

Select an option

Save ankurpandeyvns/f43365027210dd98651c88774fb182ba to your computer and use it in GitHub Desktop.
Digisol DG-GR6011 GPON ONT: Bridge Mode for Airtel IPoE (Static IP)

Digisol DG-GR6011 GPON ONT: Bridge Mode for Airtel IPoE (Static IP)

Bypass double NAT on Airtel Fiber with near-gigabit speeds (~800 Mbps) using a manual bridge on the Digisol DG-GR6011 ONT. No firmware modification required — all changes are runtime + persistent storage scripts.

Background

Airtel provides their own locked-down ONT (typically Nokia or ZTE) which runs TR-069 for remote management, giving the ISP full control over your device. You can't disable NAT, can't use bridge mode properly, and can't stop the ISP from pushing config changes to your equipment.

The Digisol DG-GR6011 is a third-party GPON ONT popular among tinkerers in India as a replacement for the ISP-provided device. By cloning your GPON serial number onto the DG-GR6011, you get:

  • Full root shell access (telnet + Linux shell)
  • No TR-069 — the ISP can't remotely manage your device
  • Full control over routing, bridging, firewall, and VLAN configuration

However, even on the DG-GR6011, getting proper bridge mode working on Airtel IPoE is non-trivial. The OLT (Airtel's upstream equipment) provisions the ONT in router mode, and the built-in bridge options don't work well:

Mode Result
Route mode (default) Full gigabit, but double NAT
Built-in bridge + VLAN tagging Works, but capped at ~100 Mbps (software VLAN path)
Built-in bridge without VLAN OLT drops packets — no connectivity

This guide achieves: manual bridge via ebtables MAC rewriting = ~800 Mbps download, no double NAT.

Prerequisites

  • Digisol DG-GR6011 GPON ONT (Realtek RTK9607C chipset) — already set up and registered on Airtel's OLT with your cloned GPON serial number
  • Airtel Fiber with IPoE / static IP provisioning (CVID 100)
  • Working internet in router mode on the DG-GR6011 (confirm this works before attempting bridge mode)
  • A downstream router (UniFi, pfSense, OpenWrt, etc.) connected to the ONT's LAN port
  • Telnet access to the ONT (default IP: 192.168.3.1)
  • SSH/console access to your downstream router

Note: You need the ONT's telnet credentials. Default credentials vary by firmware version — check forums or the device label. Once logged in, run enterlinuxshell to get a root shell.

How It Works

The Core Trick: ebtables MAC Rewriting

The OLT (Airtel's upstream equipment) filters by MAC address — it only accepts frames from the ONT's MAC and only sends frames to the ONT's MAC. A simple Linux bridge doesn't work because:

  1. Upstream: OLT drops frames with your router's source MAC
  2. Downstream: The Linux bridge "consumes" frames destined to the ONT's MAC instead of forwarding them (the is_local check)

The fix uses ebtables (Layer 2 firewall):

  • SNAT (upstream): Rewrites your router's source MAC → ONT MAC before sending to GPON
  • DNAT (downstream): Rewrites ONT destination MAC → your router's MAC before the bridge is_local check, so the bridge forwards instead of consuming
Your Router ──eth──▶ ONT br0 ──SNAT(src→ONT_MAC)──▶ nas0_0 ──▶ GPON ──▶ OLT
                       ▲                                          │
                       └──DNAT(dst→ROUTER_MAC)◀── nas0_0 ◀───────┘

Quick Setup (Non-Persistent)

This gets bridge mode working immediately but will not survive a reboot.

Step 1: Gather Information

Telnet into the ONT and get a root shell:

telnet 192.168.3.1
# Login with your credentials, then:
enterlinuxshell

Note down these values — you'll need them throughout:

# ONT's WAN MAC address (on nas0_0)
ifconfig nas0_0 | grep HWaddr
# Example output: HWaddr AA:BB:CC:DD:EE:01

# Your downstream router's WAN MAC address (visible in ONT's ARP table)
arp -n | grep 192.168.3
# Example output: 192.168.3.2 ... XX:YY:ZZ:AA:BB:02

# Current public IP
ifconfig nas0_0 | grep 'inet addr'
# Example: inet addr:X.X.X.X

# Default gateway IP
route -n | grep nas0_0 | grep UG | awk '{print $2}'
# Example: X.X.X.1

# Gateway MAC
arp -n | grep <GATEWAY_IP> | awk '{print $3}'
# Example: GG:HH:II:JJ:KK:03

Step 2: Apply Bridge on ONT

# Safety timer — auto-reboots in 8 minutes if something goes wrong
sleep 480 && reboot &

# Flush hardware flow cache and iptables NAT
echo 1 > /proc/fc/ctrl/flow_flush
iptables -t nat -F

# Remove IP from WAN interface and add it to the bridge
ifconfig nas0_0 0.0.0.0
brctl addif br0 nas0_0

# ebtables DNAT: rewrite incoming dst MAC (ONT → your router)
# This runs BEFORE the bridge is_local check
ebtables -t nat -A PREROUTING -i nas0_0 -d <ONT_MAC> \
  -j dnat --to-destination <ROUTER_WAN_MAC>

# ebtables SNAT: rewrite outgoing src MAC (your router → ONT)
# --snat-arp also rewrites ARP sender hardware address in the payload
ebtables -t nat -A POSTROUTING -o nas0_0 \
  -j snat --to-source <ONT_MAC> --snat-arp

# CRITICAL: Flush the portmapping chain
# The ONT daemon auto-populates DROP rules when nas0_0 joins the bridge
ebtables -F portmapping

Replace <ONT_MAC> and <ROUTER_WAN_MAC> with the values from Step 1.

Step 3: Configure Downstream Router

On your downstream router, configure the WAN interface with the public IP:

# Add the public IP to your WAN interface
ip addr add <PUBLIC_IP>/24 dev <WAN_INTERFACE>

# Static ARP entry for ISP gateway (prevents ARP issues through the bridge)
ip neigh replace <GATEWAY_IP> lladdr <GATEWAY_MAC> dev <WAN_INTERFACE> nud permanent

# Set default route via ISP gateway
ip route replace default via <GATEWAY_IP> dev <WAN_INTERFACE> src <PUBLIC_IP>

Step 4: Verify

# From your downstream router:
ping -c 3 1.1.1.1
curl -s ifconfig.me    # Should show your public IP

Expected performance:

  • Download: ~740-820 Mbps
  • Upload: ~250-470 Mbps
  • Ping: ~5 ms to 1.1.1.1
  • MTU 1500 works (no fragmentation)

Persistent Setup (Survives Reboot)

The ONT's root filesystem is read-only (squashfs), but /var/config/ is writable persistent storage (jffs2). We use a boot hook to auto-apply bridge mode.

ONT Boot Sequence

rcS → rc3 → runsdk.sh → /var/config/run_customized_sdk.sh  ← OUR HOOK
                       → runomci.sh (starts OMCI daemon)
     → rc18 → /bin/systemd (reconfigures interfaces ~30s later)
     → rc35 → board_init

Important timing: Our hook runs before OMCI and systemd finish. The bridge script must wait for full boot completion before applying changes, otherwise systemd will undo them.

File Transfer to ONT

The ONT has no wget, curl, scp, or base64. Use nc (netcat):

# On ONT — start listening:
nc -l -p 9999 > /var/config/<filename> &

# On your router/PC — send the file:
cat <filename> | nc -w 3 192.168.3.1 9999

File 1: /var/config/run_customized_sdk.sh

This is the boot hook — it runs early in boot and launches the bridge script.

#!/bin/sh

# Bridge mode optimizations
sleep 10
echo "hw_vlan_filter 1" > /proc/rtk_smuxdev/configure 2>/dev/null
echo 1 > /proc/fc/ctrl/bc_hwflow_accelerate 2>/dev/null

# Disable netfilter on bridges (performance)
for br in /sys/class/net/br*/bridge/nf_call_iptables; do echo 0 > $br 2>/dev/null; done
for br in /sys/class/net/br*/bridge/nf_call_ip6tables; do echo 0 > $br 2>/dev/null; done
for br in /sys/class/net/br*/bridge/nf_call_arptables; do echo 0 > $br 2>/dev/null; done

logger "Bridge mode optimizations applied"

# Launch bridge mode daemon if enabled
if [ -f /var/config/bridge_mode_enabled ] && [ -f /var/config/bridge_mode.sh ]; then
    (/var/config/bridge_mode.sh) &
fi

File 2: /var/config/bridge_mode.sh

This is the main bridge daemon. It waits for the ONT to fully boot, then applies bridge configuration and monitors it.

You must edit the two placeholder values near the top: ONT_MAC and ROUTER_MAC.

#!/bin/sh

LOGFILE="/var/config/bridge_mode.log"
INFOFILE="/var/config/bridge_info"
MAX_WAIT=300
BOOT_DELAY=30
MONITOR_INTERVAL=30
MAX_REBR_RETRIES=3

# ============================================================
# EDIT THESE — replace with your actual MAC addresses
# ============================================================
ONT_MAC="AA:BB:CC:DD:EE:01"        # ONT nas0_0 MAC (ifconfig nas0_0 | grep HWaddr)
ROUTER_MAC="XX:YY:ZZ:AA:BB:02"    # Downstream router WAN MAC
# ============================================================

log() { echo "$(date '+%H:%M:%S') $1" >> "$LOGFILE"; }

exec >> "$LOGFILE" 2>&1
echo ""
log "=== Bridge mode starting ==="

# --- Phase 1: Wait for public IP on nas0_0 ---
waited=0
while [ $waited -lt $MAX_WAIT ]; do
    ip=$(ifconfig nas0_0 2>/dev/null | grep 'inet addr' | sed 's/.*inet addr:\([^ ]*\).*/\1/')
    if [ -n "$ip" ]; then
        # Skip private ranges
        case "$ip" in
            10.*|172.1[6-9].*|172.2[0-9].*|172.3[01].*|192.168.*) ;;
            *) break ;;
        esac
    fi
    sleep 5
    waited=$((waited + 5))
done

if [ -z "$ip" ] || [ $waited -ge $MAX_WAIT ]; then
    log "FAILED: No public IP after ${MAX_WAIT}s"
    exit 1
fi
log "nas0_0 has public IP: $ip"

# --- Phase 2: Wait for full boot completion ---
log "Waiting for full boot completion..."
boot_waited=0
while [ $boot_waited -lt 120 ]; do
    if pidof boa > /dev/null 2>&1; then
        log "boa detected, boot complete"
        break
    fi
    sleep 5
    boot_waited=$((boot_waited + 5))
done
log "Safety delay (${BOOT_DELAY}s)..."
sleep $BOOT_DELAY
log "Safety delay done, proceeding with bridge setup"

# Re-check IP (may have changed during boot)
ip=$(ifconfig nas0_0 2>/dev/null | grep 'inet addr' | sed 's/.*inet addr:\([^ ]*\).*/\1/')
log "Confirmed IP: $ip"

# --- Phase 3: Capture gateway info ---
gw_ip=$(route -n | grep nas0_0 | grep UG | head -1 | awk '{print $2}')
if [ -z "$gw_ip" ]; then
    log "FAILED: No default gateway found"
    exit 1
fi

# Resolve gateway MAC via ARP
ping -c 1 -W 2 "$gw_ip" > /dev/null 2>&1
sleep 1
gw_mac=$(arp -n | grep "$gw_ip" | awk '{print $3}')
if [ -z "$gw_mac" ] || [ "$gw_mac" = "<incomplete>" ]; then
    ping -c 3 -W 2 "$gw_ip" > /dev/null 2>&1
    sleep 2
    gw_mac=$(arp -n | grep "$gw_ip" | awk '{print $3}')
fi

if [ -z "$gw_mac" ] || [ "$gw_mac" = "<incomplete>" ]; then
    log "FAILED: Cannot resolve gateway MAC for $gw_ip"
    exit 1
fi
log "Gateway: $gw_ip ($gw_mac)"

# --- Phase 4: Apply bridge ---
setup_bridge() {
    log "Applying bridge configuration..."

    # Flush flow cache and iptables NAT
    echo 1 > /proc/fc/ctrl/flow_flush
    iptables -t nat -F
    log "Flushed flow cache and iptables NAT"

    # Bridge nas0_0 into br0
    ifconfig nas0_0 0.0.0.0
    brctl addif br0 nas0_0
    log "nas0_0 bridged into br0"

    # ebtables MAC rewriting
    ebtables -t nat -F PREROUTING
    ebtables -t nat -F POSTROUTING

    # Allow management access to ONT (redirect frames for ONT LAN IP)
    ebtables -t nat -A PREROUTING -i nas0_0 -p ip --ip-dst 192.168.3.1 -j ACCEPT

    # Don't rewrite DHCP packets (let OMCI handle lease renewal)
    ebtables -t nat -A PREROUTING -i nas0_0 -p ip --ip-proto udp --ip-dport 68 -j ACCEPT

    # DNAT: downstream dst MAC rewrite (ONT → router)
    ebtables -t nat -A PREROUTING -i nas0_0 -d "$ONT_MAC" \
        -j dnat --to-destination "$ROUTER_MAC"

    # SNAT: upstream src MAC rewrite (router → ONT)
    ebtables -t nat -A POSTROUTING -o nas0_0 \
        -j snat --to-source "$ONT_MAC" --snat-arp

    log "ebtables MAC rewriting configured"

    # Flush portmapping DROP rules
    ebtables -F portmapping
    log "Portmapping flushed"
}

setup_bridge

# Save bridge info
cat > "$INFOFILE" << EOF
PUBLIC_IP=$ip
GATEWAY_IP=$gw_ip
GATEWAY_MAC=$gw_mac
ONT_MAC=$ONT_MAC
ROUTER_MAC=$ROUTER_MAC
EOF

# --- Phase 5: Start DHCP renewal ---
if [ -f /var/config/udhcpc_bridge.sh ]; then
    udhcpc -i nas0_0 -s /var/config/udhcpc_bridge.sh -p /var/run/udhcpc_bridge.pid \
        -t 5 -T 3 -A 10 -b &
    log "DHCP renewal started (PID $!)"
fi

log "=== Bridge mode ACTIVE (IP=$ip GW=$gw_ip GW_MAC=$gw_mac) ==="

# --- Phase 6: Monitor loop ---
rebr_count=0
while true; do
    sleep $MONITOR_INTERVAL

    # Re-flush portmapping if DROP rules reappear
    if ebtables -L portmapping 2>/dev/null | grep -q DROP; then
        ebtables -F portmapping
        log "Re-flushed portmapping DROP rules"
    fi

    # Re-bridge nas0_0 if removed
    if ! brctl show br0 2>/dev/null | grep -q nas0_0; then
        rebr_count=$((rebr_count + 1))
        if [ $rebr_count -le $MAX_REBR_RETRIES ]; then
            log "nas0_0 removed from br0, re-bridging (attempt $rebr_count/$MAX_REBR_RETRIES)"
            setup_bridge
        else
            log "Max re-bridge retries reached, giving up"
            break
        fi
    else
        rebr_count=0
    fi
done

File 3: /var/config/udhcpc_bridge.sh

Custom DHCP script that logs lease events without setting an IP on the interface (the bridge doesn't need one).

#!/bin/sh

LOGFILE="/var/config/bridge_mode.log"
INFOFILE="/var/config/bridge_info"

log() { echo "$(date '+%H:%M:%S') DHCP: $1" >> "$LOGFILE"; }

case "$1" in
    bound|renew)
        log "$1 ip=$ip mask=$subnet router=$router lease=$lease"
        if [ -n "$ip" ]; then
            sed -i "s/^PUBLIC_IP=.*/PUBLIC_IP=$ip/" "$INFOFILE" 2>/dev/null
        fi
        ;;
    deconfig)
        log "deconfig"
        ;;
    leasefail|nak)
        log "$1"
        ;;
esac

Deploying the Files

# Transfer all three files to ONT using nc (see "File Transfer" section above)
# Then on the ONT:

chmod +x /var/config/run_customized_sdk.sh
chmod +x /var/config/bridge_mode.sh
chmod +x /var/config/udhcpc_bridge.sh

# Enable bridge mode
touch /var/config/bridge_mode_enabled

# Reboot to activate
reboot

Downstream Router Configuration

After the ONT reboots and bridge mode activates (~90 seconds), configure your router's WAN:

# Read the bridge info file (from ONT, or just use values from Step 1)
cat /var/config/bridge_info

# On your downstream router:
ip addr add <PUBLIC_IP>/24 dev <WAN_INTERFACE>
ip neigh replace <GATEWAY_IP> lladdr <GATEWAY_MAC> dev <WAN_INTERFACE> nud permanent
ip route replace default via <GATEWAY_IP> dev <WAN_INTERFACE> src <PUBLIC_IP>

Tip: For UniFi routers, you can configure the static IP in the UniFi controller UI under WAN settings, or automate it via a boot script.

Enable / Disable

# Enable bridge mode (takes effect on next reboot):
touch /var/config/bridge_mode_enabled

# Disable bridge mode (next reboot returns to normal router mode):
rm /var/config/bridge_mode_enabled

# Force immediate reboot to router mode:
rm /var/config/bridge_mode_enabled && reboot

Troubleshooting

Check bridge mode status

cat /var/config/bridge_mode.log      # Full log
cat /var/config/bridge_info          # Current bridge parameters
brctl show br0                       # Verify nas0_0 is in bridge
ebtables -t nat -L                   # Verify MAC rewriting rules
ebtables -L portmapping              # Should be empty (no DROP rules)

Common issues

Symptom Cause Fix
No internet after bridge setup Portmapping DROP rules re-appeared ebtables -F portmapping
Bridge works then stops after ~30s systemd reconfigured interfaces Increase BOOT_DELAY in bridge_mode.sh
ARP for gateway fails Gateway MAC not resolved before bridging Check that ping <gateway> works before bridge setup
~100 Mbps instead of ~800 Mbps Using built-in bridge mode, not this script Ensure you're using the ebtables method, not the ONT's UI bridge toggle
ONT unreachable after changes Bridge misconfigured Wait for safety reboot timer, or power cycle the ONT
omcicli commands return garbage OMCI daemon stuck omcicli mib reset (forces OLT re-provisioning)

Recovery

If anything goes wrong and you can't access the ONT:

  1. Wait for the safety reboot timer (if you set one with sleep N && reboot &)
  2. Power cycle the ONT (unplug, wait 10s, plug back in)
  3. Bridge mode changes are runtime-only (except the persistent scripts). A reboot with bridge_mode_enabled removed will restore normal router mode.

Useful diagnostic commands

# OMCI
omcicli get devmode                    # Current device mode
omcicli dump conn                      # Data path connections
omcicli dump srvflow                   # Service flow table (GEM ports, VLANs)

# Flow cache
cat /proc/fc/hw_dump/flow              # Hardware flow entries
cat /proc/fc/hw_dump/l2                # L2 lookup table
echo 1 > /proc/fc/ctrl/flow_flush     # Flush all flow entries

# Interfaces
cat /proc/rtk_smuxdev/interface        # SMUX interface config
ifconfig nas0_0                        # WAN interface status
brctl show                             # Bridge membership

How It Works (Technical Deep Dive)

ONT Architecture

  • Chipset: Realtek RTK9607C, MIPS dual-core ~700 MHz
  • Kernel: Linux 4.4.140
  • GPON GEM ports: Internet (CVID 100) + Management (CVID 660)
  • Key interfaces: nas0 (raw GPON), nas0_0 (WAN, CVID 100), eth0.2.0 (LAN)

Why normal bridging fails

  1. OLT source MAC filtering: Airtel's OLT only accepts upstream frames with the ONT's registered MAC. Any other source MAC is silently dropped.
  2. Bridge is_local consumption: Linux bridges treat the bridge port's own MAC as "local" and consume frames destined to it instead of forwarding. Since downstream traffic arrives with the ONT's MAC as destination, the bridge eats it.
  3. Portmapping rules: The ONT daemon auto-populates ebtables DROP rules in a portmapping chain when nas0_0 joins a bridge, blocking IP traffic.

Why the ebtables solution works

  • PREROUTING DNAT runs before the bridge's is_local check — the bridge sees the rewritten (router) MAC and forwards normally
  • POSTROUTING SNAT with --snat-arp rewrites both the Ethernet source MAC and the ARP sender hardware address, satisfying the OLT's MAC filter
  • Portmapping flush + monitor loop keeps the DROP rules cleared

Performance

The RTK9607C handles software bridging at ~800 Mbps. The built-in bridge mode's 100 Mbps cap is caused by firmware-level VLAN processing overhead, not CPU limitations. Our method bypasses that entirely.

Known Limitations

  1. Downstream router config is also volatile — you'll need to persist the WAN IP/route on your router too (via its own config system or a boot script)
  2. DHCP IP changes — if Airtel reassigns your public IP, bridge_info updates but your downstream router config becomes stale. For truly static IPs this is not an issue.
  3. Specific to Airtel IPoE with CVID 100 — other ISPs may use different VLANs, PPPoE, or different OLT behavior. The ebtables MAC rewriting concept should still apply, but the details will differ.
  4. GPON serial cloning required — this guide assumes you've already cloned your original ONT's GPON serial onto the DG-GR6011 and have working internet in router mode.

Credits

Figured out through extensive experimentation and reverse-engineering of the DG-GR6011's OMCI stack, ASIC classification rules, and SMUX subsystem. If this helped you, star the gist!

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