diff --git a/interface-event.c b/interface-event.c index 892d744..25a2c2f 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 ae525c0..dd540ea 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 cc7efbd..04479c9 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 7402330..d3d6af1 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 5c83d97..194a32c 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 19d24fb..59e6649 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 06c924b..57e6c10 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);