summaryrefslogtreecommitdiffstats
path: root/zebra/zebra_pbr.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:53:30 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:53:30 +0000
commit2c7cac91ed6e7db0f6937923d2b57f97dbdbc337 (patch)
treec05dc0f8e6aa3accc84e3e5cffc933ed94941383 /zebra/zebra_pbr.c
parentInitial commit. (diff)
downloadfrr-upstream.tar.xz
frr-upstream.zip
Adding upstream version 8.4.4.upstream/8.4.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--zebra/zebra_pbr.c1454
1 files changed, 1454 insertions, 0 deletions
diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c
new file mode 100644
index 0000000..43e21a6
--- /dev/null
+++ b/zebra/zebra_pbr.c
@@ -0,0 +1,1454 @@
+/* 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 <jhash.h>
+#include <hash.h>
+#include <memory.h>
+#include <hook.h>
+
+#include "zebra/zebra_router.h"
+#include "zebra/zebra_pbr.h"
+#include "zebra/rt.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");
+
+/* definitions */
+static const struct message ipset_type_msg[] = {
+ {IPSET_NET_PORT_NET, "net,port,net"},
+ {IPSET_NET_PORT, "net,port"},
+ {IPSET_NET_NET, "net,net"},
+ {IPSET_NET, "net"},
+ {0}
+};
+
+const struct message icmp_typecode_str[] = {
+ { 0 << 8, "echo-reply"},
+ { 0 << 8, "pong"},
+ { 3 << 8, "network-unreachable"},
+ { (3 << 8) + 1, "host-unreachable"},
+ { (3 << 8) + 2, "protocol-unreachable"},
+ { (3 << 8) + 3, "port-unreachable"},
+ { (3 << 8) + 4, "fragmentation-needed"},
+ { (3 << 8) + 5, "source-route-failed"},
+ { (3 << 8) + 6, "network-unknown"},
+ { (3 << 8) + 7, "host-unknown"},
+ { (3 << 8) + 9, "network-prohibited"},
+ { (3 << 8) + 10, "host-prohibited"},
+ { (3 << 8) + 11, "TOS-network-unreachable"},
+ { (3 << 8) + 12, "TOS-host-unreachable"},
+ { (3 << 8) + 13, "communication-prohibited"},
+ { (3 << 8) + 14, "host-precedence-violation"},
+ { (3 << 8) + 15, "precedence-cutoff"},
+ { 4 << 8, "source-quench"},
+ { 5 << 8, "network-redirect"},
+ { (5 << 8) + 1, "host-redirect"},
+ { (5 << 8) + 2, "TOS-network-redirect"},
+ { (5 << 8) + 3, "TOS-host-redirect"},
+ { 8 << 8, "echo-request"},
+ { 8 << 8, "ping"},
+ { 9 << 8, "router-advertisement"},
+ { 10 << 8, "router-solicitation"},
+ { 11 << 8, "ttl-zero-during-transit"},
+ { (11 << 8) + 1, "ttl-zero-during-reassembly"},
+ { 12 << 8, "ip-header-bad"},
+ { (12 << 8) + 1, "required-option-missing"},
+ { 13 << 8, "timestamp-request"},
+ { 14 << 8, "timestamp-reply"},
+ { 17 << 8, "address-mask-request"},
+ { 18 << 8, "address-mask-reply"},
+ {0}
+};
+
+const struct message icmpv6_typecode_str[] = {
+ { 128 << 8, "echo-request"},
+ { 129 << 8, "echo-reply"},
+ { 1 << 8, "no-route"},
+ { (1 << 8) + 1, "communication-prohibited"},
+ { (1 << 8) + 3, "address-unreachable"},
+ { (1 << 8) + 4, "port-unreachable"},
+ { (2 << 8), "packet-too-big"},
+ { 3 << 0, "ttl-zero-during-transit"},
+ { (3 << 8) + 1, "ttl-zero-during-reassembly"},
+ { 4 << 0, "bad-header"},
+ { (4 << 0) + 1, "unknown-header-type"},
+ { (4 << 0) + 2, "unknown-option"},
+ { 133 << 8, "router-solicitation"},
+ { 134 << 8, "router-advertisement"},
+ { 135 << 8, "neighbor-solicitation"},
+ { 136 << 8, "neighbor-advertisement"},
+ { 137 << 8, "redirect"},
+ {0}
+};
+
+/* definitions */
+static const struct message tcp_value_str[] = {
+ {TCP_HEADER_FIN, "FIN"},
+ {TCP_HEADER_SYN, "SYN"},
+ {TCP_HEADER_RST, "RST"},
+ {TCP_HEADER_PSH, "PSH"},
+ {TCP_HEADER_ACK, "ACK"},
+ {TCP_HEADER_URG, "URG"},
+ {0}
+};
+
+static const struct message fragment_value_str[] = {
+ {1, "dont-fragment"},
+ {2, "is-fragment"},
+ {4, "first-fragment"},
+ {8, "last-fragment"},
+ {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,
+ uint64_t *bytes),
+ (ipset, pkts, bytes));
+
+DEFINE_HOOK(zebra_pbr_iptable_get_stat,
+ (struct zebra_pbr_iptable *iptable, uint64_t *pkts,
+ uint64_t *bytes),
+ (iptable, pkts, bytes));
+
+DEFINE_HOOK(zebra_pbr_iptable_update,
+ (int cmd, struct zebra_pbr_iptable *iptable), (cmd, iptable));
+
+DEFINE_HOOK(zebra_pbr_ipset_entry_update,
+ (int cmd, struct zebra_pbr_ipset_entry *ipset), (cmd, ipset));
+
+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 */
+void zebra_pbr_rules_free(void *arg)
+{
+ struct zebra_pbr_rule *rule;
+
+ rule = (struct zebra_pbr_rule *)arg;
+
+ (void)dplane_pbr_rule_delete(rule);
+ XFREE(MTYPE_TMP, rule);
+}
+
+uint32_t zebra_pbr_rules_hash_key(const void *arg)
+{
+ const struct zebra_pbr_rule *rule;
+ uint32_t key;
+
+ rule = arg;
+ key = jhash_3words(rule->rule.seq, rule->rule.priority,
+ rule->rule.action.table,
+ prefix_hash_key(&rule->rule.filter.src_ip));
+
+ key = jhash_3words(rule->rule.filter.fwmark, rule->vrf_id,
+ rule->rule.filter.ip_proto, key);
+
+ key = jhash(rule->ifname, strlen(rule->ifname), key);
+
+ return jhash_3words(rule->rule.filter.src_port,
+ rule->rule.filter.dst_port,
+ prefix_hash_key(&rule->rule.filter.dst_ip),
+ jhash_1word(rule->rule.unique, key));
+}
+
+bool zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct zebra_pbr_rule *r1, *r2;
+
+ r1 = (const struct zebra_pbr_rule *)arg1;
+ r2 = (const struct zebra_pbr_rule *)arg2;
+
+ if (r1->rule.seq != r2->rule.seq)
+ return false;
+
+ if (r1->rule.priority != r2->rule.priority)
+ return false;
+
+ if (r1->rule.unique != r2->rule.unique)
+ return false;
+
+ if (r1->rule.action.table != r2->rule.action.table)
+ return false;
+
+ if (r1->rule.filter.src_port != r2->rule.filter.src_port)
+ return false;
+
+ if (r1->rule.filter.dst_port != r2->rule.filter.dst_port)
+ return false;
+
+ if (r1->rule.filter.fwmark != r2->rule.filter.fwmark)
+ return false;
+
+ if (r1->rule.filter.ip_proto != r2->rule.filter.ip_proto)
+ return false;
+
+ if (!prefix_same(&r1->rule.filter.src_ip, &r2->rule.filter.src_ip))
+ return false;
+
+ if (!prefix_same(&r1->rule.filter.dst_ip, &r2->rule.filter.dst_ip))
+ return false;
+
+ if (strcmp(r1->rule.ifname, r2->rule.ifname) != 0)
+ return false;
+
+ if (r1->vrf_id != r2->vrf_id)
+ return false;
+
+ return true;
+}
+
+struct pbr_rule_unique_lookup {
+ struct zebra_pbr_rule *rule;
+ uint32_t unique;
+ char ifname[INTERFACE_NAMSIZ + 1];
+ vrf_id_t vrf_id;
+};
+
+static int pbr_rule_lookup_unique_walker(struct hash_bucket *b, void *data)
+{
+ struct pbr_rule_unique_lookup *pul = data;
+ struct zebra_pbr_rule *rule = b->data;
+
+ if (pul->unique == rule->rule.unique
+ && strncmp(pul->ifname, rule->rule.ifname, INTERFACE_NAMSIZ) == 0
+ && pul->vrf_id == rule->vrf_id) {
+ pul->rule = rule;
+ return HASHWALK_ABORT;
+ }
+
+ return HASHWALK_CONTINUE;
+}
+
+static struct zebra_pbr_rule *
+pbr_rule_lookup_unique(struct zebra_pbr_rule *zrule)
+{
+ struct pbr_rule_unique_lookup pul;
+
+ pul.unique = zrule->rule.unique;
+ strlcpy(pul.ifname, zrule->rule.ifname, INTERFACE_NAMSIZ);
+ pul.rule = NULL;
+ pul.vrf_id = zrule->vrf_id;
+ hash_walk(zrouter.rules_hash, &pbr_rule_lookup_unique_walker, &pul);
+
+ return pul.rule;
+}
+
+void zebra_pbr_ipset_free(void *arg)
+{
+ struct zebra_pbr_ipset *ipset;
+
+ ipset = (struct zebra_pbr_ipset *)arg;
+ hook_call(zebra_pbr_ipset_update, 0, ipset);
+ XFREE(MTYPE_TMP, ipset);
+}
+
+uint32_t zebra_pbr_ipset_hash_key(const void *arg)
+{
+ const struct zebra_pbr_ipset *ipset = arg;
+ uint32_t *pnt = (uint32_t *)&ipset->ipset_name;
+ uint32_t key = jhash_1word(ipset->vrf_id, 0x63ab42de);
+
+ key = jhash_1word(ipset->family, key);
+
+ return jhash2(pnt, ZEBRA_IPSET_NAME_HASH_SIZE, key);
+}
+
+bool zebra_pbr_ipset_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct zebra_pbr_ipset *r1, *r2;
+
+ r1 = (const struct zebra_pbr_ipset *)arg1;
+ r2 = (const struct zebra_pbr_ipset *)arg2;
+
+ if (r1->type != r2->type)
+ return false;
+ if (r1->unique != r2->unique)
+ return false;
+ if (r1->vrf_id != r2->vrf_id)
+ return false;
+ if (r1->family != r2->family)
+ return false;
+
+ if (strncmp(r1->ipset_name, r2->ipset_name,
+ ZEBRA_IPSET_NAME_SIZE))
+ return false;
+ return true;
+}
+
+void zebra_pbr_ipset_entry_free(void *arg)
+{
+ struct zebra_pbr_ipset_entry *ipset;
+
+ ipset = (struct zebra_pbr_ipset_entry *)arg;
+
+ hook_call(zebra_pbr_ipset_entry_update, 0, ipset);
+
+ XFREE(MTYPE_TMP, ipset);
+}
+
+uint32_t zebra_pbr_ipset_entry_hash_key(const void *arg)
+{
+ const struct zebra_pbr_ipset_entry *ipset;
+ uint32_t key;
+
+ ipset = arg;
+ key = prefix_hash_key(&ipset->src);
+ key = jhash_1word(ipset->unique, key);
+ key = jhash_1word(prefix_hash_key(&ipset->dst), key);
+ key = jhash(&ipset->dst_port_min, 2, key);
+ key = jhash(&ipset->dst_port_max, 2, key);
+ key = jhash(&ipset->src_port_min, 2, key);
+ key = jhash(&ipset->src_port_max, 2, key);
+ key = jhash(&ipset->proto, 1, key);
+
+ return key;
+}
+
+bool zebra_pbr_ipset_entry_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct zebra_pbr_ipset_entry *r1, *r2;
+
+ r1 = (const struct zebra_pbr_ipset_entry *)arg1;
+ r2 = (const struct zebra_pbr_ipset_entry *)arg2;
+
+ if (r1->unique != r2->unique)
+ return false;
+
+ if (!prefix_same(&r1->src, &r2->src))
+ return false;
+
+ if (!prefix_same(&r1->dst, &r2->dst))
+ return false;
+
+ if (r1->src_port_min != r2->src_port_min)
+ return false;
+
+ if (r1->src_port_max != r2->src_port_max)
+ return false;
+
+ if (r1->dst_port_min != r2->dst_port_min)
+ return false;
+
+ if (r1->dst_port_max != r2->dst_port_max)
+ return false;
+
+ if (r1->proto != r2->proto)
+ return false;
+ return true;
+}
+
+/* this function gives option to flush plugin memory contexts
+ * with all parameter. set it to true to flush all
+ * set it to false to flush only passed arg argument
+ */
+static void _zebra_pbr_iptable_free_all(void *arg, bool all)
+{
+ struct zebra_pbr_iptable *iptable;
+ struct listnode *node, *nnode;
+ char *name;
+
+ iptable = (struct zebra_pbr_iptable *)arg;
+
+ if (all)
+ hook_call(zebra_pbr_iptable_update, 0, iptable);
+
+ if (iptable->interface_name_list) {
+ for (ALL_LIST_ELEMENTS(iptable->interface_name_list, node,
+ nnode, name)) {
+ XFREE(MTYPE_PBR_IPTABLE_IFNAME, name);
+ list_delete_node(iptable->interface_name_list, node);
+ }
+ list_delete(&iptable->interface_name_list);
+ }
+ XFREE(MTYPE_TMP, iptable);
+}
+
+void zebra_pbr_iptable_free(void *arg)
+{
+ _zebra_pbr_iptable_free_all(arg, false);
+}
+
+uint32_t zebra_pbr_iptable_hash_key(const void *arg)
+{
+ const struct zebra_pbr_iptable *iptable = arg;
+ uint32_t *pnt = (uint32_t *)&(iptable->ipset_name);
+ uint32_t key;
+
+ key = jhash2(pnt, ZEBRA_IPSET_NAME_HASH_SIZE,
+ 0x63ab42de);
+ key = jhash_1word(iptable->fwmark, key);
+ key = jhash_1word(iptable->family, key);
+ key = jhash_1word(iptable->flow_label, key);
+ key = jhash_1word(iptable->pkt_len_min, key);
+ key = jhash_1word(iptable->pkt_len_max, key);
+ key = jhash_1word(iptable->tcp_flags, key);
+ key = jhash_1word(iptable->tcp_mask_flags, key);
+ key = jhash_1word(iptable->dscp_value, key);
+ key = jhash_1word(iptable->protocol, key);
+ key = jhash_1word(iptable->fragment, key);
+ key = jhash_1word(iptable->vrf_id, key);
+
+ return jhash_3words(iptable->filter_bm, iptable->type,
+ iptable->unique, key);
+}
+
+bool zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct zebra_pbr_iptable *r1, *r2;
+
+ r1 = (const struct zebra_pbr_iptable *)arg1;
+ r2 = (const struct zebra_pbr_iptable *)arg2;
+
+ if (r1->vrf_id != r2->vrf_id)
+ return false;
+ if (r1->type != r2->type)
+ return false;
+ if (r1->unique != r2->unique)
+ return false;
+ if (r1->filter_bm != r2->filter_bm)
+ return false;
+ if (r1->fwmark != r2->fwmark)
+ return false;
+ if (r1->action != r2->action)
+ return false;
+ if (strncmp(r1->ipset_name, r2->ipset_name,
+ ZEBRA_IPSET_NAME_SIZE))
+ return false;
+ if (r1->family != r2->family)
+ return false;
+ if (r1->flow_label != r2->flow_label)
+ return false;
+ if (r1->pkt_len_min != r2->pkt_len_min)
+ return false;
+ if (r1->pkt_len_max != r2->pkt_len_max)
+ return false;
+ if (r1->tcp_flags != r2->tcp_flags)
+ return false;
+ if (r1->tcp_mask_flags != r2->tcp_mask_flags)
+ return false;
+ if (r1->dscp_value != r2->dscp_value)
+ return false;
+ if (r1->fragment != r2->fragment)
+ return false;
+ if (r1->protocol != r2->protocol)
+ return false;
+ return true;
+}
+
+static void *pbr_rule_alloc_intern(void *arg)
+{
+ struct zebra_pbr_rule *zpr;
+ struct zebra_pbr_rule *new;
+
+ zpr = (struct zebra_pbr_rule *)arg;
+
+ new = XCALLOC(MTYPE_TMP, sizeof(*new));
+
+ memcpy(new, zpr, sizeof(*zpr));
+
+ return new;
+}
+
+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_TMP, 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 NULL;
+
+ return pbr_rule_free(lookup, free_data);
+}
+
+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;
+
+ default:
+ 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
+ * over the hash table, not via a hash operation).
+ */
+ found = pbr_rule_lookup_unique(rule);
+
+ /* If found, this is an update */
+ if (found) {
+ if (IS_ZEBRA_DEBUG_PBR)
+ zlog_debug(
+ "%s: seq: %d, prior: %d, unique: %d, ifname: %s -- update",
+ __func__, rule->rule.seq, rule->rule.priority,
+ rule->rule.unique, rule->rule.ifname);
+
+ /* 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_TMP, old);
+ } else {
+ if (IS_ZEBRA_DEBUG_PBR)
+ zlog_debug(
+ "%s: seq: %d, prior: %d, unique: %d, ifname: %s -- new",
+ __func__, rule->rule.seq, rule->rule.priority,
+ rule->rule.unique, rule->rule.ifname);
+
+ /* 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 zebra_pbr_del_rule(struct zebra_pbr_rule *rule)
+{
+ if (IS_ZEBRA_DEBUG_PBR)
+ zlog_debug("%s: seq: %d, prior: %d, unique: %d, ifname: %s",
+ __func__, rule->rule.seq, rule->rule.priority,
+ rule->rule.unique, rule->rule.ifname);
+
+ (void)dplane_pbr_rule_delete(rule);
+
+ if (pbr_rule_release(rule, true))
+ zlog_debug("%s: Rule being deleted we know nothing about",
+ __func__);
+}
+
+void zebra_pbr_process_iptable(struct zebra_dplane_ctx *ctx)
+{
+ int mode, ret = 0;
+ struct zebra_pbr_iptable ipt;
+
+ if (dplane_ctx_get_op(ctx) == DPLANE_OP_IPTABLE_ADD)
+ mode = 1;
+ else
+ mode = 0;
+
+ dplane_ctx_get_pbr_iptable(ctx, &ipt);
+
+ ret = hook_call(zebra_pbr_iptable_update, mode, &ipt);
+ if (ret)
+ dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS);
+ else
+ dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_FAILURE);
+}
+
+void zebra_pbr_process_ipset(struct zebra_dplane_ctx *ctx)
+{
+ int mode, ret = 0;
+ struct zebra_pbr_ipset ipset;
+
+ if (dplane_ctx_get_op(ctx) == DPLANE_OP_IPSET_ADD)
+ mode = 1;
+ else
+ mode = 0;
+
+ dplane_ctx_get_pbr_ipset(ctx, &ipset);
+
+ ret = hook_call(zebra_pbr_ipset_update, mode, &ipset);
+ if (ret)
+ dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS);
+ else
+ dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_FAILURE);
+}
+
+void zebra_pbr_process_ipset_entry(struct zebra_dplane_ctx *ctx)
+{
+ int mode, ret = 0;
+ struct zebra_pbr_ipset_entry ipset_entry;
+ struct zebra_pbr_ipset ipset;
+
+ if (dplane_ctx_get_op(ctx) == DPLANE_OP_IPSET_ENTRY_ADD)
+ mode = 1;
+ else
+ mode = 0;
+
+ dplane_ctx_get_pbr_ipset_entry(ctx, &ipset_entry);
+ dplane_ctx_get_pbr_ipset(ctx, &ipset);
+
+ ipset_entry.backpointer = &ipset;
+
+ ret = hook_call(zebra_pbr_ipset_entry_update, mode, &ipset_entry);
+ if (ret)
+ dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS);
+ else
+ dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_FAILURE);
+}
+
+static void zebra_pbr_cleanup_rules(struct hash_bucket *b, void *data)
+{
+ struct zebra_pbr_rule *rule = b->data;
+ int *sock = data;
+
+ if (rule->sock == *sock) {
+ (void)dplane_pbr_rule_delete(rule);
+ pbr_rule_free(rule, true);
+ }
+}
+
+static void zebra_pbr_cleanup_ipset(struct hash_bucket *b, void *data)
+{
+ struct zebra_pbr_ipset *ipset = b->data;
+ int *sock = data;
+
+ if (ipset->sock == *sock) {
+ if (hash_release(zrouter.ipset_hash, ipset))
+ zebra_pbr_ipset_free(ipset);
+ else
+ hook_call(zebra_pbr_ipset_update, 0, ipset);
+ }
+}
+
+static void zebra_pbr_cleanup_ipset_entry(struct hash_bucket *b, void *data)
+{
+ struct zebra_pbr_ipset_entry *ipset = b->data;
+ int *sock = data;
+
+ if (ipset->sock == *sock) {
+ if (hash_release(zrouter.ipset_entry_hash, ipset))
+ zebra_pbr_ipset_entry_free(ipset);
+ else
+ hook_call(zebra_pbr_ipset_entry_update, 0, ipset);
+ }
+}
+
+static void zebra_pbr_cleanup_iptable(struct hash_bucket *b, void *data)
+{
+ struct zebra_pbr_iptable *iptable = b->data;
+ int *sock = data;
+
+ if (iptable->sock == *sock) {
+ if (hash_release(zrouter.iptable_hash, iptable))
+ _zebra_pbr_iptable_free_all(iptable, true);
+ else
+ hook_call(zebra_pbr_iptable_update, 0, iptable);
+ }
+}
+
+static int zebra_pbr_client_close_cleanup(struct zserv *client)
+{
+ int sock = client->sock;
+
+ if (!sock)
+ return 0;
+ hash_iterate(zrouter.rules_hash, zebra_pbr_cleanup_rules, &sock);
+ hash_iterate(zrouter.iptable_hash, zebra_pbr_cleanup_iptable, &sock);
+ hash_iterate(zrouter.ipset_entry_hash, zebra_pbr_cleanup_ipset_entry,
+ &sock);
+ hash_iterate(zrouter.ipset_hash, zebra_pbr_cleanup_ipset, &sock);
+ return 1;
+}
+
+void zebra_pbr_init(void)
+{
+ hook_register(zserv_client_close, zebra_pbr_client_close_cleanup);
+}
+
+static void *pbr_ipset_alloc_intern(void *arg)
+{
+ struct zebra_pbr_ipset *zpi;
+ struct zebra_pbr_ipset *new;
+
+ zpi = (struct zebra_pbr_ipset *)arg;
+
+ new = XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_ipset));
+
+ memcpy(new, zpi, sizeof(*zpi));
+
+ return new;
+}
+
+void zebra_pbr_create_ipset(struct zebra_pbr_ipset *ipset)
+{
+ (void)hash_get(zrouter.ipset_hash, ipset, pbr_ipset_alloc_intern);
+ (void)dplane_pbr_ipset_add(ipset);
+}
+
+void zebra_pbr_destroy_ipset(struct zebra_pbr_ipset *ipset)
+{
+ struct zebra_pbr_ipset *lookup;
+
+ lookup = hash_lookup(zrouter.ipset_hash, ipset);
+ (void)dplane_pbr_ipset_delete(ipset);
+ if (lookup) {
+ hash_release(zrouter.ipset_hash, lookup);
+ XFREE(MTYPE_TMP, lookup);
+ } else
+ zlog_debug(
+ "%s: IPSet Entry being deleted we know nothing about",
+ __func__);
+}
+
+struct pbr_ipset_name_lookup {
+ struct zebra_pbr_ipset *ipset;
+ char ipset_name[ZEBRA_IPSET_NAME_SIZE];
+};
+
+const char *zebra_pbr_ipset_type2str(uint32_t type)
+{
+ return lookup_msg(ipset_type_msg, type,
+ "Unrecognized IPset Type");
+}
+
+static int zebra_pbr_ipset_pername_walkcb(struct hash_bucket *bucket, void *arg)
+{
+ struct pbr_ipset_name_lookup *pinl =
+ (struct pbr_ipset_name_lookup *)arg;
+ struct zebra_pbr_ipset *zpi = (struct zebra_pbr_ipset *)bucket->data;
+
+ if (!strncmp(pinl->ipset_name, zpi->ipset_name,
+ ZEBRA_IPSET_NAME_SIZE)) {
+ pinl->ipset = zpi;
+ return HASHWALK_ABORT;
+ }
+ return HASHWALK_CONTINUE;
+}
+
+struct zebra_pbr_ipset *zebra_pbr_lookup_ipset_pername(char *ipsetname)
+{
+ struct pbr_ipset_name_lookup pinl;
+ struct pbr_ipset_name_lookup *ptr = &pinl;
+
+ if (!ipsetname)
+ return NULL;
+ memset(ptr, 0, sizeof(struct pbr_ipset_name_lookup));
+ snprintf((char *)ptr->ipset_name, ZEBRA_IPSET_NAME_SIZE, "%s",
+ ipsetname);
+ hash_walk(zrouter.ipset_hash, zebra_pbr_ipset_pername_walkcb, ptr);
+ return ptr->ipset;
+}
+
+static void *pbr_ipset_entry_alloc_intern(void *arg)
+{
+ struct zebra_pbr_ipset_entry *zpi;
+ struct zebra_pbr_ipset_entry *new;
+
+ zpi = (struct zebra_pbr_ipset_entry *)arg;
+
+ new = XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_ipset_entry));
+
+ memcpy(new, zpi, sizeof(*zpi));
+
+ return new;
+}
+
+void zebra_pbr_add_ipset_entry(struct zebra_pbr_ipset_entry *ipset)
+{
+ (void)hash_get(zrouter.ipset_entry_hash, ipset,
+ pbr_ipset_entry_alloc_intern);
+ (void)dplane_pbr_ipset_entry_add(ipset);
+}
+
+void zebra_pbr_del_ipset_entry(struct zebra_pbr_ipset_entry *ipset)
+{
+ struct zebra_pbr_ipset_entry *lookup;
+
+ lookup = hash_lookup(zrouter.ipset_entry_hash, ipset);
+ (void)dplane_pbr_ipset_entry_delete(ipset);
+ if (lookup) {
+ hash_release(zrouter.ipset_entry_hash, lookup);
+ XFREE(MTYPE_TMP, lookup);
+ } else
+ zlog_debug("%s: IPSet being deleted we know nothing about",
+ __func__);
+}
+
+static void *pbr_iptable_alloc_intern(void *arg)
+{
+ struct zebra_pbr_iptable *zpi;
+ struct zebra_pbr_iptable *new;
+ struct listnode *ln;
+ char *ifname;
+
+ zpi = (struct zebra_pbr_iptable *)arg;
+
+ new = XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_iptable));
+
+ /* Deep structure copy */
+ memcpy(new, zpi, sizeof(*zpi));
+ new->interface_name_list = list_new();
+
+ if (zpi->interface_name_list) {
+ for (ALL_LIST_ELEMENTS_RO(zpi->interface_name_list, ln, ifname))
+ listnode_add(new->interface_name_list,
+ XSTRDUP(MTYPE_PBR_IPTABLE_IFNAME, ifname));
+ }
+
+ return new;
+}
+
+void zebra_pbr_add_iptable(struct zebra_pbr_iptable *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)
+{
+ struct zebra_pbr_iptable *lookup;
+
+ lookup = hash_lookup(zrouter.iptable_hash, iptable);
+ (void)dplane_pbr_iptable_delete(iptable);
+ if (lookup) {
+ struct listnode *node, *nnode;
+ char *name;
+
+ hash_release(zrouter.iptable_hash, lookup);
+ for (ALL_LIST_ELEMENTS(iptable->interface_name_list,
+ node, nnode, name)) {
+ XFREE(MTYPE_PBR_IPTABLE_IFNAME, name);
+ list_delete_node(iptable->interface_name_list,
+ node);
+ }
+ list_delete(&iptable->interface_name_list);
+ XFREE(MTYPE_TMP, lookup);
+ } else
+ zlog_debug("%s: IPTable being deleted we know nothing about",
+ __func__);
+}
+
+/*
+ * Handle success or failure of rule (un)install in the kernel.
+ */
+void zebra_pbr_dplane_result(struct zebra_dplane_ctx *ctx)
+{
+ enum zebra_dplane_result res;
+ enum dplane_op_e op;
+
+ res = dplane_ctx_get_status(ctx);
+ op = dplane_ctx_get_op(ctx);
+ if (op == DPLANE_OP_RULE_ADD || op == DPLANE_OP_RULE_UPDATE)
+ zsend_rule_notify_owner(ctx, res == ZEBRA_DPLANE_REQUEST_SUCCESS
+ ? ZAPI_RULE_INSTALLED
+ : ZAPI_RULE_FAIL_INSTALL);
+ else if (op == DPLANE_OP_RULE_DELETE)
+ zsend_rule_notify_owner(ctx, res == ZEBRA_DPLANE_REQUEST_SUCCESS
+ ? ZAPI_RULE_REMOVED
+ : ZAPI_RULE_FAIL_REMOVE);
+ else if (op == DPLANE_OP_IPTABLE_ADD)
+ zsend_iptable_notify_owner(ctx,
+ res == ZEBRA_DPLANE_REQUEST_SUCCESS
+ ? ZAPI_IPTABLE_INSTALLED
+ : ZAPI_IPTABLE_FAIL_INSTALL);
+ else if (op == DPLANE_OP_IPTABLE_DELETE)
+ zsend_iptable_notify_owner(ctx,
+ res == ZEBRA_DPLANE_REQUEST_SUCCESS
+ ? ZAPI_IPTABLE_REMOVED
+ : ZAPI_IPTABLE_FAIL_REMOVE);
+ else if (op == DPLANE_OP_IPSET_ADD)
+ zsend_ipset_notify_owner(ctx,
+ res == ZEBRA_DPLANE_REQUEST_SUCCESS
+ ? ZAPI_IPSET_INSTALLED
+ : ZAPI_IPSET_FAIL_INSTALL);
+ else if (op == DPLANE_OP_IPSET_DELETE)
+ zsend_ipset_notify_owner(ctx,
+ res == ZEBRA_DPLANE_REQUEST_SUCCESS
+ ? ZAPI_IPSET_REMOVED
+ : ZAPI_IPSET_FAIL_REMOVE);
+ else if (op == DPLANE_OP_IPSET_ENTRY_ADD)
+ zsend_ipset_entry_notify_owner(
+ ctx, res == ZEBRA_DPLANE_REQUEST_SUCCESS
+ ? ZAPI_IPSET_ENTRY_INSTALLED
+ : ZAPI_IPSET_ENTRY_FAIL_INSTALL);
+ else if (op == DPLANE_OP_IPSET_ENTRY_DELETE)
+ zsend_ipset_entry_notify_owner(
+ ctx, res == ZEBRA_DPLANE_REQUEST_SUCCESS
+ ? ZAPI_IPSET_ENTRY_REMOVED
+ : ZAPI_IPSET_ENTRY_FAIL_REMOVE);
+ else
+ flog_err(
+ EC_ZEBRA_PBR_RULE_UPDATE,
+ "Context received in pbr rule dplane result handler with incorrect OP code (%u)",
+ op);
+}
+
+/*
+ * Handle rule delete notification from kernel.
+ */
+int kernel_pbr_rule_del(struct zebra_pbr_rule *rule)
+{
+ return 0;
+}
+
+struct zebra_pbr_ipset_entry_unique_display {
+ struct zebra_pbr_ipset *zpi;
+ struct vty *vty;
+ struct zebra_ns *zns;
+};
+
+
+static const char *zebra_pbr_prefix2str(union prefixconstptr pu,
+ char *str, int size)
+{
+ const struct prefix *p = pu.p;
+ char buf[PREFIX2STR_BUFFER];
+
+ if ((p->family == AF_INET && p->prefixlen == IPV4_MAX_BITLEN)
+ || (p->family == AF_INET6 && p->prefixlen == IPV6_MAX_BITLEN)) {
+ snprintf(str, size, "%s", inet_ntop(p->family, &p->u.prefix,
+ buf, PREFIX2STR_BUFFER));
+ return str;
+ }
+ return prefix2str(pu, str, size);
+}
+
+static void zebra_pbr_display_icmp(struct vty *vty,
+ struct zebra_pbr_ipset_entry *zpie)
+{
+ char decoded_str[20];
+ uint16_t port;
+ struct zebra_pbr_ipset *zpi;
+
+ zpi = zpie->backpointer;
+
+ /* range icmp type */
+ if (zpie->src_port_max || zpie->dst_port_max) {
+ vty_out(vty, ":icmp:[type <%u:%u>;code <%u:%u>",
+ zpie->src_port_min, zpie->src_port_max,
+ zpie->dst_port_min, zpie->dst_port_max);
+ } else {
+ port = ((zpie->src_port_min << 8) & 0xff00) +
+ (zpie->dst_port_min & 0xff);
+ memset(decoded_str, 0, sizeof(decoded_str));
+ snprintf(decoded_str, sizeof(decoded_str), "%u/%u",
+ zpie->src_port_min, zpie->dst_port_min);
+ vty_out(vty, ":%s:%s",
+ zpi->family == AF_INET6 ? "ipv6-icmp" : "icmp",
+ lookup_msg(zpi->family == AF_INET6 ?
+ icmpv6_typecode_str : icmp_typecode_str,
+ port, decoded_str));
+ }
+}
+
+static void zebra_pbr_display_port(struct vty *vty, uint32_t filter_bm,
+ uint16_t port_min, uint16_t port_max,
+ uint8_t proto)
+{
+ if (!(filter_bm & PBR_FILTER_PROTO)) {
+ if (port_max)
+ vty_out(vty, ":udp/tcp:%d-%d",
+ port_min, port_max);
+ else
+ vty_out(vty, ":udp/tcp:%d",
+ port_min);
+ } else {
+ if (port_max)
+ vty_out(vty, ":proto %d:%d-%d",
+ proto, port_min, port_max);
+ else
+ vty_out(vty, ":proto %d:%d",
+ proto, port_min);
+ }
+}
+
+static int zebra_pbr_show_ipset_entry_walkcb(struct hash_bucket *bucket,
+ void *arg)
+{
+ struct zebra_pbr_ipset_entry_unique_display *unique =
+ (struct zebra_pbr_ipset_entry_unique_display *)arg;
+ struct zebra_pbr_ipset *zpi = unique->zpi;
+ struct vty *vty = unique->vty;
+ struct zebra_pbr_ipset_entry *zpie =
+ (struct zebra_pbr_ipset_entry *)bucket->data;
+ uint64_t pkts = 0, bytes = 0;
+ int ret = 0;
+
+ if (zpie->backpointer != zpi)
+ return HASHWALK_CONTINUE;
+
+ if ((zpi->type == IPSET_NET_NET) ||
+ (zpi->type == IPSET_NET_PORT_NET)) {
+ char buf[PREFIX_STRLEN];
+
+ zebra_pbr_prefix2str(&(zpie->src), buf, sizeof(buf));
+ vty_out(vty, "\tfrom %s", buf);
+ if (zpie->filter_bm & PBR_FILTER_SRC_PORT &&
+ zpie->proto != IPPROTO_ICMP)
+ zebra_pbr_display_port(vty, zpie->filter_bm,
+ zpie->src_port_min,
+ zpie->src_port_max,
+ zpie->proto);
+ vty_out(vty, " to ");
+ zebra_pbr_prefix2str(&(zpie->dst), buf, sizeof(buf));
+ vty_out(vty, "%s", buf);
+ if (zpie->filter_bm & PBR_FILTER_DST_PORT &&
+ zpie->proto != IPPROTO_ICMP)
+ zebra_pbr_display_port(vty, zpie->filter_bm,
+ zpie->dst_port_min,
+ zpie->dst_port_max,
+ zpie->proto);
+ if (zpie->proto == IPPROTO_ICMP)
+ zebra_pbr_display_icmp(vty, zpie);
+ } else if ((zpi->type == IPSET_NET) ||
+ (zpi->type == IPSET_NET_PORT)) {
+ char buf[PREFIX_STRLEN];
+
+ if (zpie->filter_bm & PBR_FILTER_SRC_IP) {
+ zebra_pbr_prefix2str(&(zpie->src), buf, sizeof(buf));
+ vty_out(vty, "\tfrom %s", buf);
+ }
+ if (zpie->filter_bm & PBR_FILTER_SRC_PORT &&
+ zpie->proto != IPPROTO_ICMP)
+ zebra_pbr_display_port(vty, zpie->filter_bm,
+ zpie->src_port_min,
+ zpie->src_port_max,
+ zpie->proto);
+ if (zpie->filter_bm & PBR_FILTER_DST_IP) {
+ zebra_pbr_prefix2str(&(zpie->dst), buf, sizeof(buf));
+ vty_out(vty, "\tto %s", buf);
+ }
+ if (zpie->filter_bm & PBR_FILTER_DST_PORT &&
+ zpie->proto != IPPROTO_ICMP)
+ zebra_pbr_display_port(vty, zpie->filter_bm,
+ zpie->dst_port_min,
+ zpie->dst_port_max,
+ zpie->proto);
+ if (zpie->proto == IPPROTO_ICMP)
+ zebra_pbr_display_icmp(vty, zpie);
+ }
+ vty_out(vty, " (%u)\n", zpie->unique);
+
+ ret = hook_call(zebra_pbr_ipset_entry_get_stat, zpie, &pkts,
+ &bytes);
+ if (ret && pkts > 0)
+ vty_out(vty, "\t pkts %" PRIu64 ", bytes %" PRIu64"\n",
+ pkts, bytes);
+ return HASHWALK_CONTINUE;
+}
+
+static int zebra_pbr_show_ipset_walkcb(struct hash_bucket *bucket, void *arg)
+{
+ struct zebra_pbr_env_display *uniqueipset =
+ (struct zebra_pbr_env_display *)arg;
+ struct zebra_pbr_ipset *zpi = (struct zebra_pbr_ipset *)bucket->data;
+ struct zebra_pbr_ipset_entry_unique_display unique;
+ struct vty *vty = uniqueipset->vty;
+ struct zebra_ns *zns = uniqueipset->zns;
+
+ vty_out(vty, "IPset %s type %s family %s\n", zpi->ipset_name,
+ zebra_pbr_ipset_type2str(zpi->type),
+ family2str(zpi->family));
+ unique.vty = vty;
+ unique.zpi = zpi;
+ unique.zns = zns;
+ hash_walk(zrouter.ipset_entry_hash, zebra_pbr_show_ipset_entry_walkcb,
+ &unique);
+ vty_out(vty, "\n");
+ return HASHWALK_CONTINUE;
+}
+
+size_t zebra_pbr_tcpflags_snprintf(char *buffer, size_t len,
+ uint16_t tcp_val)
+{
+ size_t len_written = 0;
+ static struct message nt = {0};
+ const struct message *pnt;
+ int incr = 0;
+
+ for (pnt = tcp_value_str;
+ memcmp(pnt, &nt, sizeof(struct message)); pnt++)
+ if (pnt->key & tcp_val) {
+ len_written += snprintf(buffer + len_written,
+ len - len_written,
+ "%s%s", incr ?
+ ",":"", pnt->str);
+ incr++;
+ }
+ return len_written;
+}
+
+/*
+ */
+void zebra_pbr_show_ipset_list(struct vty *vty, char *ipsetname)
+{
+ struct zebra_pbr_ipset *zpi;
+ struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
+ struct zebra_pbr_ipset_entry_unique_display unique;
+ struct zebra_pbr_env_display uniqueipset;
+
+ if (ipsetname) {
+ zpi = zebra_pbr_lookup_ipset_pername(ipsetname);
+ if (!zpi) {
+ vty_out(vty, "No IPset %s found\n", ipsetname);
+ return;
+ }
+ vty_out(vty, "IPset %s type %s family %s\n", ipsetname,
+ zebra_pbr_ipset_type2str(zpi->type),
+ family2str(zpi->family));
+ unique.vty = vty;
+ unique.zpi = zpi;
+ unique.zns = zns;
+ hash_walk(zrouter.ipset_entry_hash,
+ zebra_pbr_show_ipset_entry_walkcb, &unique);
+ return;
+ }
+ uniqueipset.zns = zns;
+ uniqueipset.vty = vty;
+ uniqueipset.name = NULL;
+ hash_walk(zrouter.ipset_hash, zebra_pbr_show_ipset_walkcb,
+ &uniqueipset);
+}
+
+struct pbr_rule_fwmark_lookup {
+ struct zebra_pbr_rule *ptr;
+ uint32_t fwmark;
+};
+
+static int zebra_pbr_rule_lookup_fwmark_walkcb(struct hash_bucket *bucket,
+ void *arg)
+{
+ struct pbr_rule_fwmark_lookup *iprule =
+ (struct pbr_rule_fwmark_lookup *)arg;
+ struct zebra_pbr_rule *zpr = (struct zebra_pbr_rule *)bucket->data;
+
+ if (iprule->fwmark == zpr->rule.filter.fwmark) {
+ iprule->ptr = zpr;
+ return HASHWALK_ABORT;
+ }
+ return HASHWALK_CONTINUE;
+}
+
+static void zebra_pbr_show_iptable_unit(struct zebra_pbr_iptable *iptable,
+ struct vty *vty,
+ struct zebra_ns *zns)
+{
+ int ret;
+ uint64_t pkts = 0, bytes = 0;
+
+ vty_out(vty, "IPtable %s family %s action %s (%u)\n",
+ iptable->ipset_name,
+ family2str(iptable->family),
+ iptable->action == ZEBRA_IPTABLES_DROP ? "drop" : "redirect",
+ iptable->unique);
+ if (iptable->type == IPSET_NET_PORT ||
+ iptable->type == IPSET_NET_PORT_NET) {
+ if (!(iptable->filter_bm & MATCH_ICMP_SET)) {
+ if (iptable->filter_bm & PBR_FILTER_DST_PORT)
+ vty_out(vty, "\t lookup dst port\n");
+ else if (iptable->filter_bm & PBR_FILTER_SRC_PORT)
+ vty_out(vty, "\t lookup src port\n");
+ }
+ }
+ if (iptable->pkt_len_min || iptable->pkt_len_max) {
+ if (!iptable->pkt_len_max)
+ vty_out(vty, "\t pkt len %u\n",
+ iptable->pkt_len_min);
+ else
+ vty_out(vty, "\t pkt len [%u;%u]\n",
+ iptable->pkt_len_min,
+ iptable->pkt_len_max);
+ }
+ if (iptable->tcp_flags || iptable->tcp_mask_flags) {
+ char tcp_flag_str[64];
+ char tcp_flag_mask_str[64];
+
+ zebra_pbr_tcpflags_snprintf(tcp_flag_str,
+ sizeof(tcp_flag_str),
+ iptable->tcp_flags);
+ zebra_pbr_tcpflags_snprintf(tcp_flag_mask_str,
+ sizeof(tcp_flag_mask_str),
+ iptable->tcp_mask_flags);
+ vty_out(vty, "\t tcpflags [%s/%s]\n",
+ tcp_flag_str, tcp_flag_mask_str);
+ }
+ if (iptable->filter_bm & (MATCH_DSCP_SET | MATCH_DSCP_INVERSE_SET)) {
+ vty_out(vty, "\t dscp %s %d\n",
+ iptable->filter_bm & MATCH_DSCP_INVERSE_SET ?
+ "not" : "", iptable->dscp_value);
+ }
+ if (iptable->filter_bm & (MATCH_FLOW_LABEL_SET |
+ MATCH_FLOW_LABEL_INVERSE_SET)) {
+ vty_out(vty, "\t flowlabel %s %d\n",
+ iptable->filter_bm & MATCH_FLOW_LABEL_INVERSE_SET ?
+ "not" : "", iptable->flow_label);
+ }
+ if (iptable->fragment) {
+ char val_str[10];
+
+ snprintf(val_str, sizeof(val_str), "%d", iptable->fragment);
+ vty_out(vty, "\t fragment%s %s\n",
+ iptable->filter_bm & MATCH_FRAGMENT_INVERSE_SET ?
+ " not" : "", lookup_msg(fragment_value_str,
+ iptable->fragment, val_str));
+ }
+ if (iptable->protocol) {
+ vty_out(vty, "\t protocol %d\n",
+ iptable->protocol);
+ }
+ ret = hook_call(zebra_pbr_iptable_get_stat, iptable, &pkts,
+ &bytes);
+ if (ret && pkts > 0)
+ vty_out(vty, "\t pkts %" PRIu64 ", bytes %" PRIu64"\n",
+ pkts, bytes);
+ if (iptable->action != ZEBRA_IPTABLES_DROP) {
+ struct pbr_rule_fwmark_lookup prfl;
+
+ prfl.fwmark = iptable->fwmark;
+ prfl.ptr = NULL;
+ hash_walk(zrouter.rules_hash,
+ &zebra_pbr_rule_lookup_fwmark_walkcb, &prfl);
+ if (prfl.ptr) {
+ struct zebra_pbr_rule *zpr = prfl.ptr;
+
+ vty_out(vty, "\t table %u, fwmark %u\n",
+ zpr->rule.action.table,
+ prfl.fwmark);
+ }
+ }
+}
+
+static int zebra_pbr_show_iptable_walkcb(struct hash_bucket *bucket, void *arg)
+{
+ struct zebra_pbr_iptable *iptable =
+ (struct zebra_pbr_iptable *)bucket->data;
+ struct zebra_pbr_env_display *env = (struct zebra_pbr_env_display *)arg;
+ struct vty *vty = env->vty;
+ struct zebra_ns *zns = env->zns;
+ char *iptable_name = env->name;
+
+ if (!iptable_name)
+ zebra_pbr_show_iptable_unit(iptable, vty, zns);
+ else if (!strncmp(iptable_name,
+ iptable->ipset_name,
+ ZEBRA_IPSET_NAME_SIZE))
+ zebra_pbr_show_iptable_unit(iptable, vty, zns);
+ return HASHWALK_CONTINUE;
+}
+
+void zebra_pbr_show_iptable(struct vty *vty, char *iptable_name)
+{
+ struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
+ struct zebra_pbr_env_display env;
+
+ env.vty = vty;
+ env.zns = zns;
+ env.name = iptable_name;
+ hash_walk(zrouter.iptable_hash, zebra_pbr_show_iptable_walkcb, &env);
+}
+
+void zebra_pbr_iptable_update_interfacelist(struct stream *s,
+ struct zebra_pbr_iptable *zpi)
+{
+ uint32_t i = 0, index;
+ struct interface *ifp;
+ char *name;
+
+ for (i = 0; i < zpi->nb_interface; i++) {
+ STREAM_GETL(s, index);
+ ifp = if_lookup_by_index(index, zpi->vrf_id);
+ if (!ifp)
+ continue;
+ name = XSTRDUP(MTYPE_PBR_IPTABLE_IFNAME, ifp->name);
+ listnode_add(zpi->interface_name_list, name);
+ }
+stream_failure:
+ return;
+}