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.
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.
- 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
enterlinuxshellto get a root shell.
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:
- Upstream: OLT drops frames with your router's source MAC
- Downstream: The Linux bridge "consumes" frames destined to the ONT's MAC instead of forwarding them (the
is_localcheck)
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_localcheck, 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 ◀───────┘
This gets bridge mode working immediately but will not survive a reboot.
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
# 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.
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>
# 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)
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.
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.
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
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
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_MACandROUTER_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
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
# 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
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 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
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)
| 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) |
If anything goes wrong and you can't access the ONT:
- Wait for the safety reboot timer (if you set one with
sleep N && reboot &) - Power cycle the ONT (unplug, wait 10s, plug back in)
- Bridge mode changes are runtime-only (except the persistent scripts). A reboot with
bridge_mode_enabledremoved will restore normal router mode.
# 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
- 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)
- OLT source MAC filtering: Airtel's OLT only accepts upstream frames with the ONT's registered MAC. Any other source MAC is silently dropped.
- Bridge
is_localconsumption: 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. - Portmapping rules: The ONT daemon auto-populates ebtables DROP rules in a
portmappingchain whennas0_0joins a bridge, blocking IP traffic.
- PREROUTING DNAT runs before the bridge's
is_localcheck — the bridge sees the rewritten (router) MAC and forwards normally - POSTROUTING SNAT with
--snat-arprewrites 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
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.
- 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)
- DHCP IP changes — if Airtel reassigns your public IP,
bridge_infoupdates but your downstream router config becomes stale. For truly static IPs this is not an issue. - 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.
- 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.
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!