diff options
| author | Viacheslav Hletenko <v.gletenko@vyos.io> | 2025-11-03 15:35:41 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-11-03 15:35:41 +0200 |
| commit | 78af000fb3be20c45514bea83b12f0eb630dd7f6 (patch) | |
| tree | 2dc1ce21765f84ef09e0b0829a68fba4eaaf7338 /src/helpers | |
| parent | 11d38ac032f1abb537614b9dfa8121d389ac6eb0 (diff) | |
| parent | 7e5fd62fcf585fa2b72161a7b1d0ba37b565c9eb (diff) | |
| download | vyos-1x-78af000fb3be20c45514bea83b12f0eb630dd7f6.tar.gz vyos-1x-78af000fb3be20c45514bea83b12f0eb630dd7f6.zip | |
Merge pull request #4818 from l0crian1/wlb-defaults
wlb: T7966: Restore default route when interface disconnects/reconnects
Diffstat (limited to 'src/helpers')
| -rwxr-xr-x | src/helpers/vyos-load-balancer.py | 66 |
1 files changed, 62 insertions, 4 deletions
diff --git a/src/helpers/vyos-load-balancer.py b/src/helpers/vyos-load-balancer.py index 5df83b31e..bd62d1479 100755 --- a/src/helpers/vyos-load-balancer.py +++ b/src/helpers/vyos-load-balancer.py @@ -24,6 +24,7 @@ import time from vyos.config import Config from vyos.template import render from vyos.utils.commit import commit_in_progress +from vyos.utils.dict import dict_search_args from vyos.utils.network import get_interface_address from vyos.utils.process import rc_cmd from vyos.utils.process import run @@ -102,15 +103,36 @@ def get_ipv4_address(ifname): return addr_json['addr_info'][0]['local'] return None +def get_dynamic_nexthop(ifname: str) -> str | None | bool: + ''' + Resolve the dynamic next-hop for a WAN interface. + + Determines the current default gateway learned dynamically on the interface: + - PPPoE interfaces (`pppoe*`): uses `parse_ppp_nexthop`. + - Other interfaces (e.g. DHCP): uses `parse_dhcp_nexthop`. + + Return values: + - str: IPv4 next-hop address + - None: when DHCP lease has no router value + - False: when PPPoE nexthop state file is missing + + Args: + ifname: Interface name (e.g. 'pppoe0', 'eth0'). + + Returns: + See above for possible values. + ''' + if ifname.startswith('pppoe'): + return parse_ppp_nexthop(ifname) + else: + return parse_dhcp_nexthop(ifname) + def dynamic_nexthop_update(lb, ifname): # Update on DHCP/PPP address/nexthop changes # Return True if nftables needs to be updated - IP change if 'dhcp_nexthop' in lb['health_state'][ifname]: - if ifname[:5] == 'pppoe': - dhcp_nexthop_addr = parse_ppp_nexthop(ifname) - else: - dhcp_nexthop_addr = parse_dhcp_nexthop(ifname) + dhcp_nexthop_addr = get_dynamic_nexthop(ifname) table_num = lb['health_state'][ifname]['table_number'] @@ -125,6 +147,40 @@ def dynamic_nexthop_update(lb, ifname): return False +def restore_default_route(lb: dict, ifname: str) -> None: + """ + Restores a missing default route for a WAN interface in its policy routing table. + + When a link flap or DHCP/PPP renegotiation removes the per-interface default route, + this function checks the interface’s assigned table for an existing default entry. + If none is found, it determines the proper next-hop (from DHCP, PPP, or static config) + and reinstalls the route using: + ip route replace table <table_num> default dev <ifname> via <nexthop> + + @param lb Load-balancer state/config dictionary. + @param ifname Interface name whose default route should be verified and restored. + @returns None — exits quietly if the table number or next-hop cannot be found. + """ + table_num = dict_search_args(lb, 'health_state', ifname, 'table_number') + if not table_num: + return + + rc, out = rc_cmd(f'ip -j route show default table {table_num}') + if rc == 0: + rt_table = json.loads(out) + if len(rt_table) > 0: + return + else: + if 'dhcp_nexthop' in lb['health_state'][ifname]: + nexthop_addr = get_dynamic_nexthop(ifname) + else: + nexthop_addr = dict_search_args(lb, 'interface_health', ifname, 'nexthop') + + if nexthop_addr: + run(f'ip route replace table {table_num} default dev {ifname} via {nexthop_addr}') + else: + return + def nftables_update(lb): # Atomically reload nftables table from template if not os.path.exists(nftables_wlb_conf): @@ -295,6 +351,8 @@ if __name__ == '__main__': if dynamic_nexthop_update(lb, ifname): ip_change = True + restore_default_route(lb, ifname) + if any(state['state_changed'] for ifname, state in lb['health_state'].items()): if not nftables_update(lb): break |
