From ed1ff48f395e6586311d475ff0cd40956fee6a58 Mon Sep 17 00:00:00 2001 From: Dharmik Parmar Date: Thu, 16 Jul 2026 08:18:42 +0530 Subject: [PATCH] interface: add strong host mark routing Add an optional mark and mask to an interface and install matching rules for its IPv4 and IPv6 routing tables. Include the mapping in interface status so firewall4 can route replies through the interface they arrived on. Signed-off-by: Dharmik Parmar --- interface-event.c | 2 ++ interface-ip.c | 29 ++++++++++++++++++++++++++++ interface-ip.h | 1 + interface.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++ interface.h | 3 +++ iprule.h | 1 + ubus.c | 9 +++++++++ 7 files changed, 93 insertions(+) diff --git a/interface-event.c b/interface-event.c index 892d744b..25a2c2f1 100644 --- a/interface-event.c +++ b/interface-event.c @@ -74,6 +74,8 @@ run_cmd(const char *ifname, const char *device, enum interface_event event, setenv("IFUPDATE_PREFIXES", "1", 1); if (updated & IUF_DATA) setenv("IFUPDATE_DATA", "1", 1); + if (updated & IUF_POLICY) + setenv("IFUPDATE_POLICY", "1", 1); } argv[0] = hotplug_cmd_path; diff --git a/interface-ip.c b/interface-ip.c index ae525c0b..dd540ead 100644 --- a/interface-ip.c +++ b/interface-ip.c @@ -203,6 +203,33 @@ static int set_ip_lo_policy(bool add, bool v6, struct interface *iface) return (add) ? system_add_iprule(&rule) : system_del_iprule(&rule); } +static int +set_ip_stronghost_policy_family(bool add, bool v6, struct interface *iface) +{ + struct iprule rule = { + .flags = IPRULE_FWMARK | IPRULE_FWMASK | IPRULE_LOOKUP | + IPRULE_PRIORITY, + .priority = IPRULE_PRIORITY_STRONGHOST, + .fwmark = iface->stronghost_mark, + .fwmask = iface->stronghost_mask, + .lookup = v6 ? iface->ip6table : iface->ip4table, + }; + + if (!rule.lookup || !rule.fwmask) + return 0; + + rule.flags |= v6 ? IPRULE_INET6 : IPRULE_INET4; + + return add ? system_add_iprule(&rule) : system_del_iprule(&rule); +} + +void +interface_ip_set_stronghost_policy(struct interface *iface, bool enabled) +{ + set_ip_stronghost_policy_family(enabled, true, iface); + set_ip_stronghost_policy_family(enabled, false, iface); +} + static bool __find_ip_addr_target(struct interface_ip_settings *ip, union if_addr *a, bool v6) { @@ -1807,6 +1834,8 @@ void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled) if (ip->iface->policy_rules_set != enabled && ip->iface->l3_dev.dev) { + interface_ip_set_stronghost_policy(ip->iface, enabled); + if (ip->iface->l3_dev.dev->settings.ipv6) { set_ip_lo_policy(enabled, true, ip->iface); set_ip_source_policy(enabled, true, IPRULE_PRIORITY_REJECT + ip->iface->l3_dev.dev->ifindex, diff --git a/interface-ip.h b/interface-ip.h index cc7efbdb..04479c9a 100644 --- a/interface-ip.h +++ b/interface-ip.h @@ -185,6 +185,7 @@ void interface_ip_update_start(struct interface_ip_settings *ip); void interface_ip_update_complete(struct interface_ip_settings *ip); void interface_ip_flush(struct interface_ip_settings *ip); void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled); +void interface_ip_set_stronghost_policy(struct interface *iface, bool enabled); void interface_ip_update_metric(struct interface_ip_settings *ip, int metric); struct interface *interface_ip_add_target_route(union if_addr *addr, bool v6, struct interface *iface, diff --git a/interface.c b/interface.c index 74023302..d3d6af16 100644 --- a/interface.c +++ b/interface.c @@ -68,6 +68,7 @@ enum { IFACE_ATTR_IP6HINT, IFACE_ATTR_IP4TABLE, IFACE_ATTR_IP6TABLE, + IFACE_ATTR_STRONGHOST_MARK, IFACE_ATTR_IP6CLASS, IFACE_ATTR_DELEGATE, IFACE_ATTR_IP6IFACEID, @@ -100,6 +101,7 @@ static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = { [IFACE_ATTR_IP6HINT] = { .name = "ip6hint", .type = BLOBMSG_TYPE_STRING }, [IFACE_ATTR_IP4TABLE] = { .name = "ip4table", .type = BLOBMSG_TYPE_STRING }, [IFACE_ATTR_IP6TABLE] = { .name = "ip6table", .type = BLOBMSG_TYPE_STRING }, + [IFACE_ATTR_STRONGHOST_MARK] = { .name = "stronghost_mark", .type = BLOBMSG_TYPE_STRING }, [IFACE_ATTR_IP6CLASS] = { .name = "ip6class", .type = BLOBMSG_TYPE_ARRAY }, [IFACE_ATTR_DELEGATE] = { .name = "delegate", .type = BLOBMSG_TYPE_BOOL }, [IFACE_ATTR_IP6IFACEID] = { .name = "ip6ifaceid", .type = BLOBMSG_TYPE_STRING }, @@ -114,6 +116,27 @@ const struct uci_blob_param_list interface_attr_list = { .params = iface_attrs, }; +static bool +interface_parse_stronghost_mark(const char *str, unsigned int *mark, + unsigned int *mask) +{ + char *end; + unsigned long value, value_mask; + + value = strtoul(str, &end, 0); + if (end == str || *end != '/' || value > UINT32_MAX) + return false; + + str = end + 1; + value_mask = strtoul(str, &end, 0); + if (end == str || *end || !value_mask || value_mask > UINT32_MAX) + return false; + + *mark = value; + *mask = value_mask; + return *mark && !(*mark & ~*mask); +} + static void interface_set_main_dev(struct interface *iface, struct device *dev); static void @@ -1047,6 +1070,14 @@ interface_alloc(const char *name, struct blob_attr *config, bool dynamic) D(INTERFACE, "Failed to resolve routing table: %s", (char *) blobmsg_data(cur)); } + if ((cur = tb[IFACE_ATTR_STRONGHOST_MARK]) && + !interface_parse_stronghost_mark(blobmsg_data(cur), + &iface->stronghost_mark, + &iface->stronghost_mask)) + netifd_log_message(L_WARNING, + "Invalid stronghost_mark value '%s' for interface '%s'\n", + (char *) blobmsg_data(cur), iface->name); + iface->proto_ip.no_delegation = !blobmsg_get_bool_default(tb[IFACE_ATTR_DELEGATE], true); iface->config_autostart = iface->autostart; @@ -1508,6 +1539,7 @@ interface_change_config(struct interface *if_old, struct interface *if_new) { struct blob_attr *old_config = if_old->config; bool reload = false, reload_ip = false, update_prefix_delegation = false; + bool stronghost_policy_changed; /* * The interface is being redefined: cancel a pending deferred removal, @@ -1546,6 +1578,15 @@ interface_change_config(struct interface *if_old, struct interface *if_new) __var |= __changed; \ }) + stronghost_policy_changed = + if_old->ip4table != if_new->ip4table || + if_old->ip6table != if_new->ip6table || + if_old->stronghost_mark != if_new->stronghost_mark || + if_old->stronghost_mask != if_new->stronghost_mask; + + if (stronghost_policy_changed && if_old->policy_rules_set) + interface_ip_set_stronghost_policy(if_old, false); + if_old->config = if_new->config; if_old->tags = if_new->tags; if (if_old->config_autostart != if_new->config_autostart) { @@ -1595,6 +1636,8 @@ interface_change_config(struct interface *if_old, struct interface *if_new) UPDATE(proto_ip.no_defaultroute, reload_ip); UPDATE(ip4table, reload_ip); UPDATE(ip6table, reload_ip); + UPDATE(stronghost_mark, reload_ip); + UPDATE(stronghost_mask, reload_ip); interface_merge_assignment_data(if_old, if_new); #undef UPDATE @@ -1624,6 +1667,11 @@ interface_change_config(struct interface *if_old, struct interface *if_new) interface_ip_set_enabled(&if_old->config_ip, config_ip_enabled); } + if (stronghost_policy_changed && if_old->state == IFS_UP) { + if_old->updated |= IUF_POLICY; + interface_event(if_old, IFEV_UPDATE); + } + if (update_prefix_delegation) interface_update_prefix_delegation(&if_old->proto_ip); diff --git a/interface.h b/interface.h index 5c83d971..194a32c6 100644 --- a/interface.h +++ b/interface.h @@ -56,6 +56,7 @@ enum interface_update_flags { IUF_ROUTE = (1 << 1), IUF_PREFIX = (1 << 2), IUF_DATA = (1 << 3), + IUF_POLICY = (1 << 4), }; struct interface_error { @@ -164,6 +165,8 @@ struct interface { int dns_metric; unsigned int ip4table; unsigned int ip6table; + unsigned int stronghost_mark; + unsigned int stronghost_mask; /* IPv6 assignment parameters */ enum interface_id_selection_type assignment_iface_id_selection; diff --git a/iprule.h b/iprule.h index 19d24fbb..59e66499 100644 --- a/iprule.h +++ b/iprule.h @@ -18,6 +18,7 @@ #include "interface-ip.h" #define IPRULE_PRIORITY_ADDR 10000 +#define IPRULE_PRIORITY_STRONGHOST 9000 #define IPRULE_PRIORITY_ADDR_MASK 20000 #define IPRULE_PRIORITY_NW 90000 #define IPRULE_PRIORITY_REJECT 4200000000 diff --git a/ubus.c b/ubus.c index 06c924b7..57e6c109 100644 --- a/ubus.c +++ b/ubus.c @@ -867,6 +867,8 @@ netifd_dump_status(struct interface *iface) blobmsg_add_string(&b, NULL, "prefixes"); if (iface->updated & IUF_DATA) blobmsg_add_string(&b, NULL, "data"); + if (iface->updated & IUF_POLICY) + blobmsg_add_string(&b, NULL, "policy"); blobmsg_close_array(&b, a); } @@ -875,6 +877,13 @@ netifd_dump_status(struct interface *iface) blobmsg_add_u32(&b, "ip4table", iface->ip4table); if (iface->ip6table) blobmsg_add_u32(&b, "ip6table", iface->ip6table); + if (iface->stronghost_mask) { + void *mark = blobmsg_open_table(&b, "stronghost_mark"); + + blobmsg_add_u32(&b, "value", iface->stronghost_mark); + blobmsg_add_u32(&b, "mask", iface->stronghost_mask); + blobmsg_close_table(&b, mark); + } blobmsg_add_u32(&b, "metric", iface->metric); blobmsg_add_u32(&b, "dns_metric", iface->dns_metric); blobmsg_add_u8(&b, "delegation", !iface->proto_ip.no_delegation);