Skip to content

Commit b52e5fb

Browse files
Add iptables FORWARD rules for nexthop-based static routes
When static routes use nexthop (gateway) instead of referencing a private gateway's public IP, the iptables FORWARD rules were not being generated. This caused traffic to be dropped by ACLs. This fix: - Adds a shared helper CsHelper.find_device_for_gateway() to determine which interface a gateway belongs to by checking subnet membership - Updates CsStaticRoutes to use the shared helper instead of duplicating the device-finding logic - Modifies CsAddress firewall rule generation to handle both old-style (ip_address-based) and new-style (nexthop-based) static routes - Generates the required FORWARD and PREROUTING rules for nexthop routes: * -A PREROUTING -s <network> ! -d <interface_ip>/32 -i <dev> -j ACL_OUTBOUND_<dev> * -A FORWARD -d <network> -o <dev> -j ACL_INBOUND_<dev> * -A FORWARD -d <network> -o <dev> -m state --state RELATED,ESTABLISHED -j ACCEPT Fixes the second part of #12857
1 parent 8a60b80 commit b52e5fb

File tree

3 files changed

+45
-26
lines changed

3 files changed

+45
-26
lines changed

systemvm/debian/opt/cloud/bin/cs/CsAddress.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -603,10 +603,24 @@ def fw_vpcrouter(self):
603603
if item == "id":
604604
continue
605605
static_route = static_routes.get_bag()[item]
606-
if 'ip_address' in static_route and static_route['ip_address'] == self.address['public_ip'] and not static_route['revoke']:
606+
if static_route['revoke']:
607+
continue
608+
609+
# Check if this static route applies to this interface
610+
# Old style: ip_address field matches this interface's public_ip
611+
# New style (nexthop): gateway is in this interface's subnet
612+
applies_to_interface = False
613+
if 'ip_address' in static_route and static_route['ip_address'] == self.address['public_ip']:
614+
applies_to_interface = True
615+
elif 'gateway' in static_route:
616+
device = CsHelper.find_device_for_gateway(self.config, static_route['gateway'])
617+
if device == self.dev:
618+
applies_to_interface = True
619+
620+
if applies_to_interface:
607621
self.fw.append(["mangle", "",
608622
"-A PREROUTING -m state --state NEW -i %s -s %s ! -d %s/32 -j ACL_OUTBOUND_%s" %
609-
(self.dev, static_route['network'], static_route['ip_address'], self.dev)])
623+
(self.dev, static_route['network'], self.address['public_ip'], self.dev)])
610624
self.fw.append(["filter", "front", "-A FORWARD -d %s -o %s -j ACL_INBOUND_%s" %
611625
(static_route['network'], self.dev, self.dev)])
612626
self.fw.append(["filter", "front",

systemvm/debian/opt/cloud/bin/cs/CsHelper.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import os.path
2626
import re
2727
import shutil
28+
from typing import Optional
2829
from netaddr import *
2930

3031
PUBLIC_INTERFACES = {"router": "eth2", "vpcrouter": "eth1"}
@@ -270,3 +271,29 @@ def copy(src, dest):
270271
logging.error("Could not copy %s to %s" % (src, dest))
271272
else:
272273
logging.info("Copied %s to %s" % (src, dest))
274+
275+
276+
def find_device_for_gateway(config, gateway_ip: str) -> Optional[str]:
277+
"""
278+
Find which ethernet device the gateway IP belongs to by checking
279+
if the gateway is in any of the configured interface subnets.
280+
281+
Args:
282+
config: CsConfig instance containing network configuration
283+
gateway_ip: IP address of the gateway to locate
284+
285+
Returns:
286+
Device name (e.g., 'eth2') or None if not found
287+
"""
288+
try:
289+
interfaces = config.address().get_interfaces()
290+
for interface in interfaces:
291+
if not interface.is_added():
292+
continue
293+
if interface.ip_in_subnet(gateway_ip):
294+
return interface.get_device()
295+
logging.debug("No matching device found for gateway %s" % gateway_ip)
296+
return None
297+
except Exception as e:
298+
logging.error("Error finding device for gateway %s: %s" % (gateway_ip, e))
299+
return None

systemvm/debian/opt/cloud/bin/cs/CsStaticRoutes.py

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -32,29 +32,7 @@ def process(self):
3232
continue
3333
self.__update(self.dbag[item])
3434

35-
def __find_device_for_gateway(self, gateway_ip):
36-
"""
37-
Find which ethernet device the gateway IP belongs to by checking
38-
if the gateway is in any of the configured interface subnets.
39-
Returns device name (e.g., 'eth2') or None if not found.
40-
"""
41-
try:
42-
# Get all configured interfaces from the address databag
43-
interfaces = self.config.address().get_interfaces()
4435

45-
for interface in interfaces:
46-
if not interface.is_added():
47-
continue
48-
49-
# Check if gateway IP is in this interface's subnet
50-
if interface.ip_in_subnet(gateway_ip):
51-
return interface.get_device()
52-
53-
logging.debug("No matching device found for gateway %s" % gateway_ip)
54-
return None
55-
except Exception as e:
56-
logging.error("Error finding device for gateway %s: %s" % (gateway_ip, e))
57-
return None
5836

5937
def __update(self, route):
6038
network = route['network']
@@ -66,7 +44,7 @@ def __update(self, route):
6644
CsHelper.execute(command)
6745

6846
# Delete from PBR table if applicable
69-
device = self.__find_device_for_gateway(gateway)
47+
device = CsHelper.find_device_for_gateway(self.config, gateway)
7048
if device:
7149
cs_route = CsRoute()
7250
table_name = cs_route.get_tablename(device)
@@ -83,7 +61,7 @@ def __update(self, route):
8361
logging.info("Added static route %s via %s to main table" % (network, gateway))
8462

8563
# Add to PBR table if applicable
86-
device = self.__find_device_for_gateway(gateway)
64+
device = CsHelper.find_device_for_gateway(self.config, gateway)
8765
if device:
8866
cs_route = CsRoute()
8967
table_name = cs_route.get_tablename(device)

0 commit comments

Comments
 (0)