]> git.proxmox.com Git - mirror_frr.git/blobdiff - zebra/zebra_pbr.c
Merge pull request #13479 from ryndia/fix_leak
[mirror_frr.git] / zebra / zebra_pbr.c
index e66d7aaf5c8aab9fe1c405db9d9006fa73d07698..56cac1342ef8622718848818e34a22afd0ce2a13 100644 (file)
@@ -1,22 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
 /* Zebra Policy Based Routing (PBR) main handling.
  * Copyright (C) 2018  Cumulus Networks, Inc.
- *
- * This file is part of FRR.
- *
- * FRR is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
- *
- * FRR is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with FRR; see the file COPYING.  If not, write to the Free
- * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
  */
 
 #include <zebra.h>
 #include "zebra/zapi_msg.h"
 #include "zebra/zserv.h"
 #include "zebra/debug.h"
+#include "zebra/zebra_neigh.h"
 
 /* definitions */
 DEFINE_MTYPE_STATIC(ZEBRA, PBR_IPTABLE_IFNAME, "PBR interface list");
+DEFINE_MTYPE(ZEBRA, PBR_OBJ, "PBR");
 
 /* definitions */
 static const struct message ipset_type_msg[] = {
@@ -123,6 +109,12 @@ static const struct message fragment_value_str[] = {
        {0}
 };
 
+struct zebra_pbr_env_display {
+       struct zebra_ns *zns;
+       struct vty *vty;
+       char *name;
+};
+
 /* static function declarations */
 DEFINE_HOOK(zebra_pbr_ipset_entry_get_stat,
            (struct zebra_pbr_ipset_entry *ipset, uint64_t *pkts,
@@ -143,6 +135,9 @@ DEFINE_HOOK(zebra_pbr_ipset_entry_update,
 DEFINE_HOOK(zebra_pbr_ipset_update,
            (int cmd, struct zebra_pbr_ipset *ipset), (cmd, ipset));
 
+/* resolve nexthop for dataplane (dpdk) programming */
+static bool zebra_pbr_expand_action;
+
 /* Private functions */
 
 /* Public functions */
@@ -153,7 +148,7 @@ void zebra_pbr_rules_free(void *arg)
        rule = (struct zebra_pbr_rule *)arg;
 
        (void)dplane_pbr_rule_delete(rule);
-       XFREE(MTYPE_TMP, rule);
+       XFREE(MTYPE_PBR_OBJ, rule);
 }
 
 uint32_t zebra_pbr_rules_hash_key(const void *arg)
@@ -265,7 +260,7 @@ void zebra_pbr_ipset_free(void *arg)
 
        ipset = (struct zebra_pbr_ipset *)arg;
        hook_call(zebra_pbr_ipset_update, 0, ipset);
-       XFREE(MTYPE_TMP, ipset);
+       XFREE(MTYPE_PBR_OBJ, ipset);
 }
 
 uint32_t zebra_pbr_ipset_hash_key(const void *arg)
@@ -309,7 +304,7 @@ void zebra_pbr_ipset_entry_free(void *arg)
 
        hook_call(zebra_pbr_ipset_entry_update, 0, ipset);
 
-       XFREE(MTYPE_TMP, ipset);
+       XFREE(MTYPE_PBR_OBJ, ipset);
 }
 
 uint32_t zebra_pbr_ipset_entry_hash_key(const void *arg)
@@ -386,7 +381,7 @@ static void _zebra_pbr_iptable_free_all(void *arg, bool all)
                }
                list_delete(&iptable->interface_name_list);
        }
-       XFREE(MTYPE_TMP, iptable);
+       XFREE(MTYPE_PBR_OBJ, iptable);
 }
 
 void zebra_pbr_iptable_free(void *arg)
@@ -468,31 +463,189 @@ static void *pbr_rule_alloc_intern(void *arg)
 
        zpr = (struct zebra_pbr_rule *)arg;
 
-       new = XCALLOC(MTYPE_TMP, sizeof(*new));
+       new = XCALLOC(MTYPE_PBR_OBJ, sizeof(*new));
 
        memcpy(new, zpr, sizeof(*zpr));
 
        return new;
 }
 
-static int pbr_rule_release(struct zebra_pbr_rule *rule)
+static struct zebra_pbr_rule *pbr_rule_free(struct zebra_pbr_rule *hash_data,
+                                           bool free_data)
+{
+       if (hash_data->action.neigh)
+               zebra_neigh_deref(hash_data);
+       hash_release(zrouter.rules_hash, hash_data);
+       if (free_data) {
+               XFREE(MTYPE_PBR_OBJ, hash_data);
+               return NULL;
+       }
+
+       return hash_data;
+}
+
+static struct zebra_pbr_rule *pbr_rule_release(struct zebra_pbr_rule *rule,
+                                              bool free_data)
 {
        struct zebra_pbr_rule *lookup;
 
        lookup = hash_lookup(zrouter.rules_hash, rule);
 
        if (!lookup)
-               return -ENOENT;
+               return NULL;
 
-       hash_release(zrouter.rules_hash, lookup);
-       XFREE(MTYPE_TMP, lookup);
+       return pbr_rule_free(lookup, free_data);
+}
 
-       return 0;
+void zebra_pbr_show_rule_unit(struct zebra_pbr_rule *rule, struct vty *vty)
+{
+       struct pbr_rule *prule = &rule->rule;
+       struct zebra_pbr_action *zaction = &rule->action;
+
+       vty_out(vty, "Rules if %s\n", rule->ifname);
+       vty_out(vty, "  Seq %u pri %u\n", prule->seq, prule->priority);
+       if (prule->filter.filter_bm & PBR_FILTER_SRC_IP)
+               vty_out(vty, "  SRC IP Match: %pFX\n", &prule->filter.src_ip);
+       if (prule->filter.filter_bm & PBR_FILTER_DST_IP)
+               vty_out(vty, "  DST IP Match: %pFX\n", &prule->filter.dst_ip);
+       if (prule->filter.filter_bm & PBR_FILTER_IP_PROTOCOL)
+               vty_out(vty, "  IP protocol Match: %u\n",
+                       prule->filter.ip_proto);
+       if (prule->filter.filter_bm & PBR_FILTER_SRC_PORT)
+               vty_out(vty, "  SRC Port Match: %u\n", prule->filter.src_port);
+       if (prule->filter.filter_bm & PBR_FILTER_DST_PORT)
+               vty_out(vty, "  DST Port Match: %u\n", prule->filter.dst_port);
+
+       if (prule->filter.filter_bm & PBR_FILTER_DSFIELD) {
+               vty_out(vty, "  DSCP Match: %u\n",
+                       (prule->filter.dsfield & PBR_DSFIELD_DSCP) >> 2);
+               vty_out(vty, "  ECN Match: %u\n",
+                       prule->filter.dsfield & PBR_DSFIELD_ECN);
+       }
+
+       if (prule->filter.filter_bm & PBR_FILTER_FWMARK)
+               vty_out(vty, "  MARK Match: %u\n", prule->filter.fwmark);
+
+       vty_out(vty, "  Tableid: %u\n", prule->action.table);
+       if (zaction->afi == AFI_IP)
+               vty_out(vty, "  Action: nh: %pI4 intf: %s\n",
+                       &zaction->gate.ipv4,
+                       ifindex2ifname(zaction->ifindex, rule->vrf_id));
+       if (zaction->afi == AFI_IP6)
+               vty_out(vty, "  Action: nh: %pI6 intf: %s\n",
+                       &zaction->gate.ipv6,
+                       ifindex2ifname(zaction->ifindex, rule->vrf_id));
+       if (zaction->neigh && (zaction->neigh->flags & ZEBRA_NEIGH_ENT_ACTIVE))
+               vty_out(vty, "  Action: mac: %pEA\n", &zaction->neigh->mac);
+}
+
+static int zebra_pbr_show_rules_walkcb(struct hash_bucket *bucket, void *arg)
+{
+       struct zebra_pbr_rule *rule = (struct zebra_pbr_rule *)bucket->data;
+       struct zebra_pbr_env_display *env = (struct zebra_pbr_env_display *)arg;
+       struct vty *vty = env->vty;
+
+       zebra_pbr_show_rule_unit(rule, vty);
+
+       return HASHWALK_CONTINUE;
+}
+
+void zebra_pbr_show_rule(struct vty *vty)
+{
+       struct zebra_pbr_env_display env;
+
+       env.vty = vty;
+       hash_walk(zrouter.rules_hash, zebra_pbr_show_rules_walkcb, &env);
+}
+
+void zebra_pbr_config_write(struct vty *vty)
+{
+       if (zebra_pbr_expand_action)
+               vty_out(vty, "pbr nexthop-resolve\n");
+}
+
+void zebra_pbr_expand_action_update(bool enable)
+{
+       zebra_pbr_expand_action = enable;
+}
+
+static void zebra_pbr_expand_rule(struct zebra_pbr_rule *rule)
+{
+       struct prefix p;
+       struct route_table *table;
+       struct route_node *rn;
+       rib_dest_t *dest;
+       struct route_entry *re;
+       const struct nexthop_group *nhg;
+       const struct nexthop *nexthop;
+       struct zebra_pbr_action *action = &rule->action;
+       struct ipaddr ip;
+
+       if (!zebra_pbr_expand_action)
+               return;
+
+       table = zebra_vrf_get_table_with_table_id(
+               AFI_IP, SAFI_UNICAST, VRF_DEFAULT, rule->rule.action.table);
+       if (!table)
+               return;
+
+       memset(&p, 0, sizeof(p));
+       p.family = AF_INET;
+
+       rn = route_node_lookup(table, &p);
+       if (!rn)
+               return;
+
+       dest = rib_dest_from_rnode(rn);
+       re = dest->selected_fib;
+       if (!re) {
+               route_unlock_node(rn);
+               return;
+       }
+
+       nhg = rib_get_fib_nhg(re);
+       if (!nhg) {
+               route_unlock_node(rn);
+               return;
+       }
+
+       nexthop = nhg->nexthop;
+       if (nexthop) {
+               switch (nexthop->type) {
+               case NEXTHOP_TYPE_IPV4:
+               case NEXTHOP_TYPE_IPV4_IFINDEX:
+                       action->afi = AFI_IP;
+                       action->gate.ipv4 = nexthop->gate.ipv4;
+                       action->ifindex = nexthop->ifindex;
+                       ip.ipa_type = AF_INET;
+                       ip.ipaddr_v4 = action->gate.ipv4;
+                       zebra_neigh_ref(action->ifindex, &ip, rule);
+                       break;
+
+               case NEXTHOP_TYPE_IPV6:
+               case NEXTHOP_TYPE_IPV6_IFINDEX:
+                       action->afi = AFI_IP6;
+                       action->gate.ipv6 = nexthop->gate.ipv6;
+                       action->ifindex = nexthop->ifindex;
+                       ip.ipa_type = AF_INET6;
+                       ip.ipaddr_v6 = action->gate.ipv6;
+                       zebra_neigh_ref(action->ifindex, &ip, rule);
+                       break;
+
+               case NEXTHOP_TYPE_BLACKHOLE:
+               case NEXTHOP_TYPE_IFINDEX:
+                       action->afi = AFI_UNSPEC;
+               }
+       }
+
+       route_unlock_node(rn);
 }
 
 void zebra_pbr_add_rule(struct zebra_pbr_rule *rule)
 {
        struct zebra_pbr_rule *found;
+       struct zebra_pbr_rule *old;
+       struct zebra_pbr_rule *new;
 
        /**
         * Check if we already have it (this checks via a unique ID, walking
@@ -508,13 +661,20 @@ void zebra_pbr_add_rule(struct zebra_pbr_rule *rule)
                                __func__, rule->rule.seq, rule->rule.priority,
                                rule->rule.unique, rule->rule.ifname);
 
-               (void)dplane_pbr_rule_update(found, rule);
-
-               if (pbr_rule_release(found))
-                       zlog_debug(
-                               "%s: Rule being updated we know nothing about",
-                               __func__);
-
+               /* remove the old entry from the hash but don't free the hash
+                * data yet as we need it for the dplane update
+                */
+               old = pbr_rule_release(found, false);
+
+               /* insert new entry into hash */
+               new = hash_get(zrouter.rules_hash, rule, pbr_rule_alloc_intern);
+               /* expand the action if needed */
+               zebra_pbr_expand_rule(new);
+               /* update dataplane */
+               (void)dplane_pbr_rule_update(found, new);
+               /* release the old hash data */
+               if (old)
+                       XFREE(MTYPE_PBR_OBJ, old);
        } else {
                if (IS_ZEBRA_DEBUG_PBR)
                        zlog_debug(
@@ -522,10 +682,13 @@ void zebra_pbr_add_rule(struct zebra_pbr_rule *rule)
                                __func__, rule->rule.seq, rule->rule.priority,
                                rule->rule.unique, rule->rule.ifname);
 
-               (void)dplane_pbr_rule_add(rule);
+               /* insert new entry into hash */
+               new = hash_get(zrouter.rules_hash, rule, pbr_rule_alloc_intern);
+               /* expand the action if needed */
+               zebra_pbr_expand_rule(new);
+               (void)dplane_pbr_rule_add(new);
        }
 
-       (void)hash_get(zrouter.rules_hash, rule, pbr_rule_alloc_intern);
 }
 
 void zebra_pbr_del_rule(struct zebra_pbr_rule *rule)
@@ -537,7 +700,7 @@ void zebra_pbr_del_rule(struct zebra_pbr_rule *rule)
 
        (void)dplane_pbr_rule_delete(rule);
 
-       if (pbr_rule_release(rule))
+       if (pbr_rule_release(rule, true))
                zlog_debug("%s: Rule being deleted we know nothing about",
                           __func__);
 }
@@ -610,12 +773,7 @@ static void zebra_pbr_cleanup_rules(struct hash_bucket *b, void *data)
 
        if (rule->sock == *sock) {
                (void)dplane_pbr_rule_delete(rule);
-               if (hash_release(zrouter.rules_hash, rule))
-                       XFREE(MTYPE_TMP, rule);
-               else
-                       zlog_debug(
-                               "%s: Rule seq: %u is being cleaned but we can't find it in our tables",
-                               __func__, rule->rule.seq);
+               pbr_rule_free(rule, true);
        }
 }
 
@@ -684,7 +842,7 @@ static void *pbr_ipset_alloc_intern(void *arg)
 
        zpi = (struct zebra_pbr_ipset *)arg;
 
-       new = XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_ipset));
+       new = XCALLOC(MTYPE_PBR_OBJ, sizeof(struct zebra_pbr_ipset));
 
        memcpy(new, zpi, sizeof(*zpi));
 
@@ -705,7 +863,7 @@ void zebra_pbr_destroy_ipset(struct zebra_pbr_ipset *ipset)
        (void)dplane_pbr_ipset_delete(ipset);
        if (lookup) {
                hash_release(zrouter.ipset_hash, lookup);
-               XFREE(MTYPE_TMP, lookup);
+               XFREE(MTYPE_PBR_OBJ, lookup);
        } else
                zlog_debug(
                        "%s: IPSet Entry being deleted we know nothing about",
@@ -758,7 +916,7 @@ static void *pbr_ipset_entry_alloc_intern(void *arg)
 
        zpi = (struct zebra_pbr_ipset_entry *)arg;
 
-       new = XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_ipset_entry));
+       new = XCALLOC(MTYPE_PBR_OBJ, sizeof(struct zebra_pbr_ipset_entry));
 
        memcpy(new, zpi, sizeof(*zpi));
 
@@ -780,7 +938,7 @@ void zebra_pbr_del_ipset_entry(struct zebra_pbr_ipset_entry *ipset)
        (void)dplane_pbr_ipset_entry_delete(ipset);
        if (lookup) {
                hash_release(zrouter.ipset_entry_hash, lookup);
-               XFREE(MTYPE_TMP, lookup);
+               XFREE(MTYPE_PBR_OBJ, lookup);
        } else
                zlog_debug("%s: IPSet being deleted we know nothing about",
                           __func__);
@@ -795,7 +953,7 @@ static void *pbr_iptable_alloc_intern(void *arg)
 
        zpi = (struct zebra_pbr_iptable *)arg;
 
-       new = XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_iptable));
+       new = XCALLOC(MTYPE_PBR_OBJ, sizeof(struct zebra_pbr_iptable));
 
        /* Deep structure copy */
        memcpy(new, zpi, sizeof(*zpi));
@@ -812,8 +970,11 @@ static void *pbr_iptable_alloc_intern(void *arg)
 
 void zebra_pbr_add_iptable(struct zebra_pbr_iptable *iptable)
 {
-       (void)hash_get(zrouter.iptable_hash, iptable, pbr_iptable_alloc_intern);
-       (void)dplane_pbr_iptable_add(iptable);
+       struct zebra_pbr_iptable *ipt_hash;
+
+       ipt_hash = hash_get(zrouter.iptable_hash, iptable,
+                           pbr_iptable_alloc_intern);
+       (void)dplane_pbr_iptable_add(ipt_hash);
 }
 
 void zebra_pbr_del_iptable(struct zebra_pbr_iptable *iptable)
@@ -834,7 +995,7 @@ void zebra_pbr_del_iptable(struct zebra_pbr_iptable *iptable)
                                         node);
                }
                list_delete(&iptable->interface_name_list);
-               XFREE(MTYPE_TMP, lookup);
+               XFREE(MTYPE_PBR_OBJ, lookup);
        } else
                zlog_debug("%s: IPTable being deleted we know nothing about",
                           __func__);
@@ -893,9 +1054,6 @@ void zebra_pbr_dplane_result(struct zebra_dplane_ctx *ctx)
                        EC_ZEBRA_PBR_RULE_UPDATE,
                        "Context received in pbr rule dplane result handler with incorrect OP code (%u)",
                        op);
-
-
-       dplane_ctx_fini(&ctx);
 }
 
 /*
@@ -912,11 +1070,6 @@ struct zebra_pbr_ipset_entry_unique_display {
        struct zebra_ns *zns;
 };
 
-struct zebra_pbr_env_display {
-       struct zebra_ns *zns;
-       struct vty *vty;
-       char *name;
-};
 
 static const char *zebra_pbr_prefix2str(union prefixconstptr pu,
                                        char *str, int size)