[OpenWrt-Devel] [PATCH][netifd] iprule: rework interface based rules to handle dynamic interfaces

Alexander Couzens lynxis at fe80.eu
Mon Jun 18 11:06:26 EDT 2018


Previous netifd would only apply `ip rule`s while config phase.
If the iprule is depending on an interface (iif or oif), the rule
will fail if the interface is not up.

Allow iprules to track interface and their devices by hooking into
into the interface_ip_set_enabled() call.

Fixes: FS#1571
---
 interface-ip.c |   3 ++
 iprule.c       | 104 ++++++++++++++++++++++++++++++++++++++++---------
 iprule.h       |   7 ++++
 3 files changed, 96 insertions(+), 18 deletions(-)

diff --git a/interface-ip.c b/interface-ip.c
index 1e49fe6feac7..1260f6059ecd 100644
--- a/interface-ip.c
+++ b/interface-ip.c
@@ -1451,6 +1451,9 @@ void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled)
 			NULL, 0, 0, ip->iface, "failed_policy", true);
 		ip->iface->policy_rules_set = enabled;
 	}
+
+	/* apply ip rules */
+	iprule_set_enabled(ip->iface, enabled);
 }
 
 void
diff --git a/iprule.c b/iprule.c
index 7cf7422f4168..79e6eca9dfb7 100644
--- a/iprule.c
+++ b/iprule.c
@@ -2,6 +2,7 @@
  * netifd - network interface daemon
  * Copyright (C) 2012 Felix Fietkau <nbd at openwrt.org>
  * Copyright (C) 2013 Jo-Philipp Wich <jow at openwrt.org>
+ * Copyright (C) 2018 Alexander Couzens <lynxis at fe80.eu>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2
@@ -66,6 +67,16 @@ const struct uci_blob_param_list rule_attr_list = {
 	.params = rule_attr,
 };
 
+/* interface based rules are dynamic. */
+static int rule_ready(struct iprule *rule) {
+	if (rule->flags & IPRULE_OUT && rule->out_dev == NULL)
+		return 0;
+
+	if (rule->flags & IPRULE_IN && rule->in_dev == NULL)
+		return 0;
+
+	return 1;
+}
 
 static bool
 iprule_parse_mark(const char *mark, struct iprule *rule)
@@ -97,13 +108,65 @@ iprule_parse_mark(const char *mark, struct iprule *rule)
 	return true;
 }
 
+/* remove all rules from the system which depend on iface */
+static void
+remove_iface_rules(struct interface *iface) {
+	struct iprule *rule;
+
+	vlist_for_each_element(&iprules, rule, node) {
+		if (!(rule->flags & (IPRULE_IN | IPRULE_OUT)))
+			continue;
+
+		if (!strcmp(rule->in_iface, iface->name)) {
+			if (rule_ready(rule))
+				system_del_iprule(rule);
+			rule->in_dev[0] = 0;
+		}
+
+		if (!strcmp(rule->out_iface, iface->name)) {
+			if (rule_ready(rule))
+				system_del_iprule(rule);
+			rule->out_dev[0] = 0;
+		}
+	}
+}
+
+/* update rules which depend on iface. Add them to the system if they validates afterwards. */
+static void add_iface_rule(struct interface *iface) {
+	struct iprule *rule;
+
+	vlist_for_each_element(&iprules, rule, node) {
+		if (rule_ready(rule))
+			continue;
+
+		if (!strcmp(rule->out_iface, iface->name))
+			memcpy(rule->out_dev, iface->l3_dev.dev->ifname, sizeof(rule->out_dev));
+
+		if (!strcmp(rule->in_iface, iface->name))
+			memcpy(rule->in_dev, iface->l3_dev.dev->ifname, sizeof(rule->in_dev));
+
+		if (rule_ready(rule))
+			system_add_iprule(rule);
+	}
+}
+
+/* called by interface_ip_set_enabled */
+void iprule_set_enabled(struct interface *iface, int enabled)
+{
+	if (enabled)
+		add_iface_rule(iface);
+	else
+		remove_iface_rules(iface);
+}
+
 void
 iprule_add(struct blob_attr *attr, bool v6)
 {
-	struct interface *iif = NULL, *oif = NULL;
 	struct blob_attr *tb[__RULE_MAX], *cur;
-	struct interface *iface;
+	struct interface *iface, *tmp;
 	struct iprule *rule;
+	char *iface_name;
 	int af = v6 ? AF_INET6 : AF_INET;
 
 	blobmsg_parse(rule_attr, __RULE_MAX, tb, blobmsg_data(attr), blobmsg_data_len(attr));
@@ -118,28 +181,30 @@ iprule_add(struct blob_attr *attr, bool v6)
 	if ((cur = tb[RULE_INVERT]) != NULL)
 		rule->invert = blobmsg_get_bool(cur);
 
+	/* TODO: check if we need to validate the uci interface name or not */
 	if ((cur = tb[RULE_INTERFACE_IN]) != NULL) {
-		iif = vlist_find(&interfaces, blobmsg_data(cur), iface, node);
+		iface_name = calloc(1, strlen(blobmsg_data(cur)) + 1);
+		rule->in_iface = strcpy(iface_name, blobmsg_data(cur));
+		rule->flags |= IPRULE_IN;
 
-		if (!iif || !iif->l3_dev.dev) {
-			DPRINTF("Failed to resolve device of network: %s\n", (char *) blobmsg_data(cur));
-			goto error;
+		/* this can be filled later by the set_interface_ip call */
+		tmp = vlist_find(&interfaces, blobmsg_data(cur), iface, node);
+		if (tmp && tmp->l3_dev.dev) {
+			memcpy(rule->in_dev, tmp->l3_dev.dev->ifname, sizeof(rule->in_dev));
 		}
-
-		memcpy(rule->in_dev, iif->l3_dev.dev->ifname, sizeof(rule->in_dev));
-		rule->flags |= IPRULE_IN;
 	}
 
+	/* TODO: check if we need to validate the uci interface name or not */
 	if ((cur = tb[RULE_INTERFACE_OUT]) != NULL) {
-		oif = vlist_find(&interfaces, blobmsg_data(cur), iface, node);
+		iface_name = calloc(1, strlen(blobmsg_data(cur)) + 1);
+		rule->out_iface = strcpy(iface_name, blobmsg_data(cur));
+		rule->flags |= IPRULE_OUT;
 
-		if (!oif || !oif->l3_dev.dev) {
-			DPRINTF("Failed to resolve device of network: %s\n", (char *) blobmsg_data(cur));
-			goto error;
+		/* this can be filled later by the set_interface_ip call */
+		tmp = vlist_find(&interfaces, blobmsg_data(cur), iface, node);
+		if (tmp && tmp->l3_dev.dev) {
+			memcpy(rule->out_dev, tmp->l3_dev.dev->ifname, sizeof(rule->out_dev));
 		}
-
-		memcpy(rule->out_dev, oif->l3_dev.dev->ifname, sizeof(rule->out_dev));
-		rule->flags |= IPRULE_OUT;
 	}
 
 	if ((cur = tb[RULE_SRC]) != NULL) {
@@ -248,12 +313,15 @@ iprule_update_rule(struct vlist_tree *tree,
 	rule_new = container_of(node_new, struct iprule, node);
 
 	if (node_old) {
-		system_del_iprule(rule_old);
+		/* interface based rules must not be present at all times */
+		if (rule_ready(rule_old))
+			system_del_iprule(rule_old);
 		free(rule_old);
 	}
 
-	if (node_new)
+	if (node_new && rule_ready(rule_new)) {
 		system_add_iprule(rule_new);
+	}
 }
 
 static void __init
diff --git a/iprule.h b/iprule.h
index b723bdb05d7d..c399c413efb0 100644
--- a/iprule.h
+++ b/iprule.h
@@ -74,6 +74,11 @@ struct iprule {
 
 	bool invert;
 
+	/* uci interface name */
+	const char *in_iface;
+	const char *out_iface;
+
+	/* device name */
 	char in_dev[IFNAMSIZ + 1];
 	char out_dev[IFNAMSIZ + 1];
 
@@ -102,4 +107,6 @@ void iprule_add(struct blob_attr *attr, bool v6);
 void iprule_update_start(void);
 void iprule_update_complete(void);
 
+void iprule_set_enabled(struct interface *iface, int enabled);
+
 #endif
-- 
2.17.1


_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/listinfo/openwrt-devel



More information about the openwrt-devel mailing list