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@openwrt.org>
  * Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
+ * Copyright (C) 2018 Alexander Couzens <lynxis@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
