PBR 1.1.1-7 cannot use the dynamic modem interface created by the GL.iNet M2 5G Development Board on Flint 3 firmware 4.8.4

PBR 1.1.1-7 cannot use the dynamic modem interface created by the GL.iNet M2 5G Development Board on Flint 3 firmware 4.8.4

Hi GL.iNet Team,

I found an integration issue between the GL.iNet modem subsystem and the Policy-Based Routing (PBR) package when using the GL.iNet M2 5G Development Board with a GL-BE9300 Flint 3.

Environment

Device: GL.iNet GL-BE9300 / Flint 3
Firmware: 4.8.4
OpenWrt: 23.05-SNAPSHOT
PBR package: 1.1.1-7
Firewall: fw4 / nftables
Cellular hardware: GL.iNet M2 5G Development Board connected through USB
Modem protocol: qcm
Parent modem interface: modem_1_1
Dynamic IPv4 interface: modem_1_1_4
Linux network device: wwan0

Problem

The GL.iNet modem subsystem creates the IPv4 cellular interface dynamically as:

modem_1_1_4

The interface is fully operational and visible through ubus:

ubus call network.interface.modem_1_1_4 status

The output shows:

"up": true
"dynamic": true
"l3_device": "wwan0"
IPv4 address: 10.196.1.131/29
Default gateway: 10.196.1.132
DNS servers: 8.8.8.8 and 8.8.4.4

However, the dynamic interface is not present in the UCI network configuration.

The following command returns no output:

uci show network | grep modem_1_1_4

Initial PBR configuration

I enabled PBR and explicitly added the dynamic interface as a supported interface:

config pbr 'config'
        option enabled '1'
        list supported_interface 'modem_1_1_4'

Despite this, PBR initially only created routing tables for:

wan
wwan
secondwan

The service output showed that modem_1_1_4 was being monitored:

pbr 1.1.1-7 monitoring interfaces: wan wwan secondwan modem_1_1_4

However, no pbr_modem_1_1_4 routing table was created.

Root cause 1: dynamic interfaces are not processed during startup

PBR creates its routing tables using:

config_foreach interface_process 'interface' 'create'

This only processes interface sections defined in /etc/config/network.

Because modem_1_1_4 is dynamically created through ubus/netifd and is not a UCI interface section, interface_process() is never called for it.

The dynamic interface is included in ifacesSupported and monitored by PBR, but it is not included in routing-table creation.

Root cause 2: policy validation rejects the dynamic interface

A policy using:

option interface 'modem_1_1_4'

was initially rejected with:

ERROR: Policy 'ZTB-FRA' has no assigned interface!

The validator only accepts interfaces defined as UCI network interfaces.

Exact tested patch

The following are the only two PBR code changes required on my router.

File:

/etc/init.d/pbr

Change 1: Process supported dynamic interfaces during startup

Original code:

interface_process 'all' 'prepare'
config_foreach interface_process 'interface' 'create'
interface_process_tor 'tor' 'destroy'

Working code:

interface_process 'all' 'prepare'
config_foreach interface_process 'interface' 'create'

for n in $ifacesSupported; do
        uci -q get "network.${n}" >/dev/null 2>&1 || interface_process "$n" "create"
done

interface_process_tor 'tor' 'destroy'

This preserves processing for all normal UCI interfaces.

It then processes interfaces from ifacesSupported that do not exist as UCI interface sections. This allows the dynamically created modem_1_1_4 interface to receive a routing table and fwmark.

Change 2: Allow a dynamic interface name in policy validation

Original validator:

'interface:or("ignore", "tor", uci("network", "@interface")):wan'

Working validator:

'interface:or("ignore", "tor", uci("network", "@interface"), string):wan'

Unified diff representation

--- a/etc/init.d/pbr
+++ b/etc/init.d/pbr
@@
                        output 1 'Processing interfaces '
                        json_add_array 'gateways'
                        interface_process 'all' 'prepare'
                        config_foreach interface_process 'interface' 'create'
+                       for n in $ifacesSupported; do
+                               uci -q get "network.${n}" >/dev/null 2>&1 || interface_process "$n" "create"
+                       done
                        interface_process_tor 'tor' 'destroy'
                        is_tor_running && interface_process_tor 'tor' 'create'
                        json_close_array

@@
-               'interface:or("ignore", "tor", uci("network", "@interface")):wan' \
+               'interface:or("ignore", "tor", uci("network", "@interface"), string):wan' \

Test policy

After applying the two changes, I configured the following policy:

config policy
        option name 'ZTB-FRA'
        option src_addr '172.16.1.5'
        option interface 'modem_1_1_4'

Successful result

PBR now processes the interface correctly:

Setting up routing for 'modem_1_1_4/wwan0/10.196.1.132' [✓]

The following routing table is created:

ip route show table pbr_modem_1_1_4

default via 10.196.1.132 dev wwan0
10.1.199.0/24 dev br-lan proto kernel scope link src 10.1.199.1
100.64.0.0/10 dev tailscale0 scope link
172.16.1.0/24 dev br-lan proto kernel scope link src 172.16.1.1

PBR also creates the correct fwmark rule:

30003: from all fwmark 0x40000/0xff0000 lookup pbr_modem_1_1_4

The policy loads successfully:

Routing 'ZTB-FRA' via modem_1_1_4 [✓]

The generated nftables policy is:

ip saddr @pbr_modem_1_1_4_4_src_ip_cfg066ff5 goto pbr_mark_0x040000 comment "ZTB-FRA"

Traffic from 172.16.1.5 is successfully routed through wwan0 and the M2 5G Development Board.

An nftables counter was temporarily added for verification and confirmed that packets from 172.16.1.5 matched the policy. The counter itself is not required for the permanent solution.

Important clarification

No modification to pbr_get_gateway() was required.

Once modem_1_1_4 was actually processed by interface_process(), the existing PBR code correctly detected the gateway as 10.196.1.132 and created the proper default route.

Suggested permanent fix

PBR currently uses ifacesSupported for monitoring dynamic interfaces, but routing-table creation only loops through UCI-defined network interfaces.

A permanent fix could process non-UCI interfaces from ifacesSupported during startup, similar to the tested patch above.

The policy validator should also allow an interface explicitly configured through supported_interface, even when that interface exists only dynamically through ubus/netifd.

Alternatively, GL.iNet could expose the M2 cellular connection through a persistent UCI network interface that third-party OpenWrt packages can reference.

Impact

This may affect other GL.iNet products and modem configurations that create dynamic IPv4 child interfaces such as modem_x_x_4.

The issue is therefore likely not limited to the GL-BE9300 or my specific M2 5G Development Board.

I have successfully tested the above patch on a GL-BE9300 running firmware 4.8.4 with the GL.iNet M2 5G Development Board.

I would be happy to test an official firmware or package fix and provide any additional diagnostics required.

Thank you.

Hi,

Thank you for the detailed bug report and for sharing the proposed fix.

However, the PBR code is not maintained by us, so you may need to submit this suggestion to the upstream community.

Once the fix is merged by the upstream community, we can update it later.