[OpenWrt-Devel] [PATCH][v3] iprule: rework interface based rules to handle dynamic interfaces
Hans Dedecker
dedeckeh at gmail.com
Sat Jun 30 02:55:44 EDT 2018
On Fri, Jun 29, 2018 at 11:15 PM Alexander Couzens <lynxis at 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 <lynxis at fe80.eu>
Acked-by: Hans Dedecker <dedeckeh at 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 <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
> @@ -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 at lists.openwrt.org
https://lists.openwrt.org/mailman/listinfo/openwrt-devel
More information about the openwrt-devel
mailing list