On Fri, Jun 29, 2018 at 11:15 PM Alexander Couzens <lyn...@fe80.eu> wrote: > > 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 interfaces and their devices by using > the interface events. > > Fixes: FS#1571 > Signed-off-by: Alexander Couzens <lyn...@fe80.eu> Acked-by: Hans Dedecker <dedec...@gmail.com> > --- > iprule.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++++------- > iprule.h | 9 +++ > 2 files changed, 164 insertions(+), 21 deletions(-) > > diff --git a/iprule.c b/iprule.c > index 7cf7422f4168..105f469b2b35 100644 > --- a/iprule.c > +++ b/iprule.c > @@ -2,6 +2,7 @@ > * netifd - network interface daemon > * Copyright (C) 2012 Felix Fietkau <n...@openwrt.org> > * Copyright (C) 2013 Jo-Philipp Wich <j...@openwrt.org> > + * Copyright (C) 2018 Alexander Couzens <lyn...@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 > @@ -12,6 +13,7 @@ > * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > * GNU General Public License for more details. > */ > +#include <assert.h> > #include <string.h> > #include <stdlib.h> > #include <stdio.h> > @@ -66,6 +68,16 @@ const struct uci_blob_param_list rule_attr_list = { > .params = rule_attr, > }; > > +/* interface based rules are dynamic. */ > +static bool rule_ready(struct iprule *rule) { > + if (rule->flags & IPRULE_OUT && rule->out_dev == NULL) > + return false; > + > + if (rule->flags & IPRULE_IN && rule->in_dev == NULL) > + return false; > + > + return true; > +} > > static bool > iprule_parse_mark(const char *mark, struct iprule *rule) > @@ -97,13 +109,102 @@ iprule_parse_mark(const char *mark, struct iprule *rule) > return true; > } > > +/* called on interface changes of the incoming interface */ > +static void rule_in_cb( > + struct interface_user *dep, > + struct interface *iface, > + enum interface_event ev) > +{ > + struct iprule *rule = container_of(dep, struct iprule, in_iface_user); > + > + switch (ev) { > + case IFEV_UP: > + if (!iface->l3_dev.dev) > + break; > + memcpy(rule->in_dev, iface->l3_dev.dev->ifname, > sizeof(rule->in_dev)); > + if (rule_ready(rule)) > + system_add_iprule(rule); > + break; > + case IFEV_DOWN: > + case IFEV_UP_FAILED: > + case IFEV_FREE: > + if (rule_ready(rule)) > + system_del_iprule(rule); > + rule->in_dev[0] = 0; > + break; > + default: > + break; > + } > +} > + > +/* called on interface changes of the outgoing interface */ > +static void rule_out_cb( > + struct interface_user *dep, > + struct interface *iface, > + enum interface_event ev) > +{ > + struct iprule *rule = container_of(dep, struct iprule, > out_iface_user); > + > + switch (ev) { > + case IFEV_UP: > + if (!iface->l3_dev.dev) > + break; > + memcpy(rule->out_dev, iface->l3_dev.dev->ifname, > sizeof(rule->out_dev)); > + if (rule_ready(rule)) > + system_add_iprule(rule); > + break; > + case IFEV_DOWN: > + case IFEV_UP_FAILED: > + case IFEV_FREE: > + if (rule_ready(rule)) > + system_del_iprule(rule); > + rule->out_dev[0] = 0; > + break; > + default: > + break; > + } > +} > + > +/* called on all interface events */ > +static void generic_interface_cb( > + struct interface_user *dep, > + struct interface *iface, > + enum interface_event ev) > +{ > + struct iprule *rule; > + > + if (ev != IFEV_CREATE) > + return; > + > + /* add new interfaces to rules */ > + vlist_for_each_element(&iprules, rule, node) { > + if (rule_ready(rule)) > + continue; > + > + if (!strcmp(rule->out_iface, iface->name)) { > + assert(!rule->out_dev); > + memcpy(rule->out_dev, iface->l3_dev.dev->ifname, > sizeof(rule->out_dev)); > + interface_add_user(&rule->out_iface_user, iface); > + } > + > + if (!strcmp(rule->in_iface, iface->name)) { > + assert(!rule->in_dev); > + memcpy(rule->in_dev, iface->l3_dev.dev->ifname, > sizeof(rule->in_dev)); > + interface_add_user(&rule->in_iface_user, iface); > + } > + } > +} > + > +struct interface_user generic_listener = { > + .cb = generic_interface_cb > +}; > + > 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 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)); > @@ -119,26 +220,16 @@ iprule_add(struct blob_attr *attr, bool v6) > rule->invert = blobmsg_get_bool(cur); > > if ((cur = tb[RULE_INTERFACE_IN]) != NULL) { > - iif = vlist_find(&interfaces, blobmsg_data(cur), iface, node); > - > - if (!iif || !iif->l3_dev.dev) { > - DPRINTF("Failed to resolve device of network: %s\n", > (char *) blobmsg_data(cur)); > - goto error; > - } > - > - memcpy(rule->in_dev, iif->l3_dev.dev->ifname, > sizeof(rule->in_dev)); > + iface_name = calloc(1, strlen(blobmsg_data(cur)) + 1); > + rule->in_iface = strcpy(iface_name, blobmsg_data(cur)); > + rule->in_iface_user.cb = &rule_in_cb; > rule->flags |= IPRULE_IN; > } > > if ((cur = tb[RULE_INTERFACE_OUT]) != NULL) { > - oif = vlist_find(&interfaces, blobmsg_data(cur), iface, node); > - > - if (!oif || !oif->l3_dev.dev) { > - DPRINTF("Failed to resolve device of network: %s\n", > (char *) blobmsg_data(cur)); > - goto error; > - } > - > - memcpy(rule->out_dev, oif->l3_dev.dev->ifname, > sizeof(rule->out_dev)); > + iface_name = calloc(1, strlen(blobmsg_data(cur)) + 1); > + rule->out_iface = strcpy(iface_name, blobmsg_data(cur)); > + rule->out_iface_user.cb = &rule_out_cb; > rule->flags |= IPRULE_OUT; > } > > @@ -238,6 +329,31 @@ rule_cmp(const void *k1, const void *k2, void *ptr) > return memcmp(k1, k2, sizeof(struct iprule)-offsetof(struct iprule, > flags)); > } > > +static void deregister_interfaces(struct iprule *rule) > +{ > + if (rule->flags & IPRULE_IN && rule->in_iface_user.iface) > + interface_remove_user(&rule->in_iface_user); > + > + if (rule->flags & IPRULE_OUT && rule->out_iface_user.iface) > + interface_remove_user(&rule->out_iface_user); > +} > + > +static void register_interfaces(struct iprule *rule) > +{ > + struct interface *iface, *tmp; > + > + if (rule->flags & IPRULE_IN) { > + tmp = vlist_find(&interfaces, rule->in_iface, iface, node); > + if (tmp) > + interface_add_user(&rule->in_iface_user, tmp); > + } > + if (rule->flags & IPRULE_OUT) { > + tmp = vlist_find(&interfaces, rule->out_iface, iface, node); > + if (tmp) > + interface_add_user(&rule->out_iface_user, tmp); > + } > +} > + > static void > iprule_update_rule(struct vlist_tree *tree, > struct vlist_node *node_new, struct vlist_node > *node_old) > @@ -248,16 +364,34 @@ iprule_update_rule(struct vlist_tree *tree, > rule_new = container_of(node_new, struct iprule, node); > > if (node_old) { > - system_del_iprule(rule_old); > + if (rule_ready(rule_old)) > + system_del_iprule(rule_old); > + > + if (rule_old->flags & (IPRULE_IN | IPRULE_OUT)) > + deregister_interfaces(rule_old); > + > + if (rule_old->in_iface) > + free(rule_old->in_iface); > + > + if (rule_old->out_iface) > + free(rule_old->out_iface); > + > free(rule_old); > } > > - if (node_new) > - system_add_iprule(rule_new); > + if (node_new) { > + /* interface based rules calls system_add_iprule over the > event cb */ > + if (rule_new->flags & (IPRULE_IN | IPRULE_OUT)) { > + register_interfaces(rule_new); > + } else { > + system_add_iprule(rule_new); > + } > + } > } > > static void __init > iprule_init_list(void) > { > vlist_init(&iprules, rule_cmp, iprule_update_rule); > + interface_add_user(&generic_listener, NULL); > } > diff --git a/iprule.h b/iprule.h > index b723bdb05d7d..f05c3c93b4a1 100644 > --- a/iprule.h > +++ b/iprule.h > @@ -74,6 +74,15 @@ struct iprule { > > bool invert; > > + /* uci interface name */ > + char *in_iface; > + char *out_iface; > + > + /* to receive interface events */ > + struct interface_user in_iface_user; > + struct interface_user out_iface_user; > + > + /* device name */ > char in_dev[IFNAMSIZ + 1]; > char out_dev[IFNAMSIZ + 1]; > > -- > 2.18.0 >
_______________________________________________ openwrt-devel mailing list openwrt-devel@lists.openwrt.org https://lists.openwrt.org/mailman/listinfo/openwrt-devel