From 4cf4fad15307fab9e21dd228fc7f80be4da801c2 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah Date: Sat, 18 Dec 2021 11:28:49 -0800 Subject: [PATCH 01/12] zebra: add support for maintaining local neigh entries Currently specific local neighbors (attached to SVIs) are maintatined in an EVPN specific database. There is a need to maintain L3 neighbors for other purposes including MAC resolution for PBR nexthops. Signed-off-by: Donald Sharp Cleanup compile and fix crash Signed-off-by: Anuradha Karuppiah --- zebra/debug.c | 25 ++++ zebra/debug.h | 5 + zebra/rt_netlink.c | 30 ++++- zebra/subdir.am | 2 + zebra/zebra_neigh.c | 279 +++++++++++++++++++++++++++++++++++++++++++ zebra/zebra_neigh.h | 64 ++++++++++ zebra/zebra_pbr.h | 17 +++ zebra/zebra_router.c | 3 + zebra/zebra_router.h | 2 + zebra/zebra_vty.c | 12 ++ 10 files changed, 433 insertions(+), 6 deletions(-) create mode 100644 zebra/zebra_neigh.c create mode 100644 zebra/zebra_neigh.h diff --git a/zebra/debug.c b/zebra/debug.c index 525180d4ee..98e25af857 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -42,6 +42,7 @@ unsigned long zebra_debug_mlag; unsigned long zebra_debug_nexthop; unsigned long zebra_debug_evpn_mh; unsigned long zebra_debug_pbr; +unsigned long zebra_debug_neigh; DEFINE_HOOK(zebra_debug_show_debugging, (struct vty *vty), (vty)); @@ -327,6 +328,22 @@ DEFUN (debug_zebra_pbr, return CMD_SUCCESS; } +DEFPY (debug_zebra_neigh, + debug_zebra_neigh_cmd, + "[no$no] debug zebra neigh", + NO_STR + DEBUG_STR + "Zebra configuration\n" + "Debug zebra neigh events\n") +{ + if (no) + UNSET_FLAG(zebra_debug_neigh, ZEBRA_DEBUG_NEIGH); + else + SET_FLAG(zebra_debug_neigh, ZEBRA_DEBUG_NEIGH); + + return CMD_SUCCESS; +} + DEFPY (debug_zebra_mlag, debug_zebra_mlag_cmd, "[no$no] debug zebra mlag", @@ -694,6 +711,11 @@ static int config_write_debug(struct vty *vty) write++; } + if (IS_ZEBRA_DEBUG_NEIGH) { + vty_out(vty, "debug zebra neigh\n"); + write++; + } + return write; } @@ -713,6 +735,7 @@ void zebra_debug_init(void) zebra_debug_nht = 0; zebra_debug_nexthop = 0; zebra_debug_pbr = 0; + zebra_debug_neigh = 0; install_node(&debug_node); @@ -734,6 +757,7 @@ void zebra_debug_init(void) install_element(ENABLE_NODE, &debug_zebra_mlag_cmd); install_element(ENABLE_NODE, &debug_zebra_nexthop_cmd); install_element(ENABLE_NODE, &debug_zebra_pbr_cmd); + install_element(ENABLE_NODE, &debug_zebra_neigh_cmd); install_element(ENABLE_NODE, &no_debug_zebra_events_cmd); install_element(ENABLE_NODE, &no_debug_zebra_nht_cmd); install_element(ENABLE_NODE, &no_debug_zebra_mpls_cmd); @@ -764,6 +788,7 @@ void zebra_debug_init(void) install_element(CONFIG_NODE, &debug_zebra_dplane_cmd); install_element(CONFIG_NODE, &debug_zebra_nexthop_cmd); install_element(CONFIG_NODE, &debug_zebra_pbr_cmd); + install_element(CONFIG_NODE, &debug_zebra_neigh_cmd); install_element(CONFIG_NODE, &no_debug_zebra_events_cmd); install_element(CONFIG_NODE, &no_debug_zebra_nht_cmd); diff --git a/zebra/debug.h b/zebra/debug.h index dc44367d01..e761e5e3e3 100644 --- a/zebra/debug.h +++ b/zebra/debug.h @@ -70,6 +70,8 @@ extern "C" { #define ZEBRA_DEBUG_PBR 0x01 +#define ZEBRA_DEBUG_NEIGH 0x01 + /* Debug related macro. */ #define IS_ZEBRA_DEBUG_EVENT (zebra_debug_event & ZEBRA_DEBUG_EVENT) @@ -121,6 +123,8 @@ extern "C" { #define IS_ZEBRA_DEBUG_PBR (zebra_debug_pbr & ZEBRA_DEBUG_PBR) +#define IS_ZEBRA_DEBUG_NEIGH (zebra_debug_neigh & ZEBRA_DEBUG_NEIGH) + extern unsigned long zebra_debug_event; extern unsigned long zebra_debug_packet; extern unsigned long zebra_debug_kernel; @@ -135,6 +139,7 @@ extern unsigned long zebra_debug_mlag; extern unsigned long zebra_debug_nexthop; extern unsigned long zebra_debug_evpn_mh; extern unsigned long zebra_debug_pbr; +extern unsigned long zebra_debug_neigh; extern void zebra_debug_init(void); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index ad9e13a0f8..21c991c7db 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -80,6 +80,7 @@ #include "zebra/zebra_errors.h" #include "zebra/zebra_evpn_mh.h" #include "zebra/zebra_trace.h" +#include "zebra/zebra_neigh.h" #ifndef AF_MPLS #define AF_MPLS 28 @@ -3882,10 +3883,10 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id) } else if (IS_ZEBRA_IF_BRIDGE(ifp)) link_if = ifp; else { + link_if = NULL; if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( " Neighbor Entry received is not on a VLAN or a BRIDGE, ignoring"); - return 0; } memset(&mac, 0, sizeof(mac)); @@ -3949,12 +3950,25 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id) */ local_inactive = false; - return zebra_vxlan_handle_kernel_neigh_update( - ifp, link_if, &ip, &mac, ndm->ndm_state, is_ext, - is_router, local_inactive, dp_static); + /* Add local neighbors to the l3 interface database */ + if (is_ext) + zebra_neigh_del(ifp, &ip); + else + zebra_neigh_add(ifp, &ip, &mac); + + if (link_if) + zebra_vxlan_handle_kernel_neigh_update( + ifp, link_if, &ip, &mac, ndm->ndm_state, + is_ext, is_router, local_inactive, + dp_static); + return 0; } - return zebra_vxlan_handle_kernel_neigh_del(ifp, link_if, &ip); + + zebra_neigh_del(ifp, &ip); + if (link_if) + zebra_vxlan_handle_kernel_neigh_del(ifp, link_if, &ip); + return 0; } if (IS_ZEBRA_DEBUG_KERNEL) @@ -3967,7 +3981,11 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id) /* Process the delete - it may result in re-adding the neighbor if it is * a valid "remote" neighbor. */ - return zebra_vxlan_handle_kernel_neigh_del(ifp, link_if, &ip); + zebra_neigh_del(ifp, &ip); + if (link_if) + zebra_vxlan_handle_kernel_neigh_del(ifp, link_if, &ip); + + return 0; } static int netlink_neigh_table(struct nlmsghdr *h, ns_id_t ns_id, int startup) diff --git a/zebra/subdir.am b/zebra/subdir.am index a09b895cee..e895ffa65f 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -125,6 +125,7 @@ zebra_zebra_SOURCES = \ zebra/zebra_vty.c \ zebra/zebra_vxlan.c \ zebra/zebra_evpn_mh.c \ + zebra/zebra_neigh.c \ zebra/zserv.c \ # end @@ -197,6 +198,7 @@ noinst_HEADERS += \ zebra/zebra_vxlan.h \ zebra/zebra_vxlan_private.h \ zebra/zebra_evpn_mh.h \ + zebra/zebra_neigh.h \ zebra/zserv.h \ # end diff --git a/zebra/zebra_neigh.c b/zebra/zebra_neigh.c new file mode 100644 index 0000000000..2fca2a0615 --- /dev/null +++ b/zebra/zebra_neigh.c @@ -0,0 +1,279 @@ +/* + * Zebra neighbor table management + * + * Copyright (C) 2021 Nvidia + * Anuradha Karuppiah + * + * 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. + */ + +#include + +#include "command.h" +#include "hash.h" +#include "if.h" +#include "jhash.h" +#include "linklist.h" +#include "log.h" +#include "memory.h" +#include "prefix.h" +#include "stream.h" +#include "table.h" + +#include "zebra/zebra_router.h" +#include "zebra/debug.h" +#include "zebra/interface.h" +#include "zebra/rib.h" +#include "zebra/rt.h" +#include "zebra/rt_netlink.h" +#include "zebra/zebra_errors.h" +#include "zebra/interface.h" +#include "zebra/zebra_neigh.h" +#include "zebra/zebra_pbr.h" + +DEFINE_MTYPE_STATIC(ZEBRA, ZNEIGH_INFO, "Zebra neigh table"); +DEFINE_MTYPE_STATIC(ZEBRA, ZNEIGH_ENT, "Zebra neigh entry"); + +static int zebra_neigh_rb_cmp(const struct zebra_neigh_ent *n1, + const struct zebra_neigh_ent *n2) +{ + if (n1->ifindex < n2->ifindex) + return -1; + + if (n1->ifindex > n2->ifindex) + return 1; + + if (n1->ip.ipa_type < n2->ip.ipa_type) + return -1; + + if (n1->ip.ipa_type > n2->ip.ipa_type) + return 1; + + if (n1->ip.ipa_type == AF_INET) { + if (n1->ip.ipaddr_v4.s_addr < n2->ip.ipaddr_v4.s_addr) + return -1; + + if (n1->ip.ipaddr_v4.s_addr > n2->ip.ipaddr_v4.s_addr) + return 1; + + return 0; + } + + return memcmp(&n1->ip.ipaddr_v6, &n2->ip.ipaddr_v6, IPV6_MAX_BYTELEN); +} +RB_GENERATE(zebra_neigh_rb_head, zebra_neigh_ent, rb_node, zebra_neigh_rb_cmp); + +static struct zebra_neigh_ent *zebra_neigh_find(ifindex_t ifindex, + struct ipaddr *ip) +{ + struct zebra_neigh_ent tmp; + + tmp.ifindex = ifindex; + memcpy(&tmp.ip, ip, sizeof(*ip)); + return RB_FIND(zebra_neigh_rb_head, &zneigh_info->neigh_rb_tree, &tmp); +} + +static struct zebra_neigh_ent * +zebra_neigh_new(ifindex_t ifindex, struct ipaddr *ip, struct ethaddr *mac) +{ + struct zebra_neigh_ent *n; + + n = XCALLOC(MTYPE_ZNEIGH_ENT, sizeof(struct zebra_neigh_ent)); + + memcpy(&n->ip, ip, sizeof(*ip)); + n->ifindex = ifindex; + if (mac) { + memcpy(&n->mac, mac, sizeof(*mac)); + n->flags |= ZEBRA_NEIGH_ENT_ACTIVE; + } + + /* Add to rb_tree */ + if (RB_INSERT(zebra_neigh_rb_head, &zneigh_info->neigh_rb_tree, n)) { + XFREE(MTYPE_ZNEIGH_ENT, n); + return NULL; + } + + /* Initialise the pbr rule list */ + n->pbr_rule_list = list_new(); + listset_app_node_mem(n->pbr_rule_list); + + if (IS_ZEBRA_DEBUG_NEIGH) + zlog_debug("zebra neigh new if %d %pIA %pEA", n->ifindex, + &n->ip, &n->mac); + + return n; +} + +static void zebra_neigh_pbr_rules_update(struct zebra_neigh_ent *n) +{ + struct zebra_pbr_rule *rule; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(n->pbr_rule_list, node, rule)) + dplane_pbr_rule_update(rule, rule); +} + +static void zebra_neigh_free(struct zebra_neigh_ent *n) +{ + if (listcount(n->pbr_rule_list)) { + /* if rules are still using the neigh mark it as inactive and + * update the dataplane + */ + if (n->flags & ZEBRA_NEIGH_ENT_ACTIVE) { + n->flags &= ~ZEBRA_NEIGH_ENT_ACTIVE; + memset(&n->mac, 0, sizeof(n->mac)); + } + zebra_neigh_pbr_rules_update(n); + return; + } + if (IS_ZEBRA_DEBUG_NEIGH) + zlog_debug("zebra neigh free if %d %pIA %pEA", n->ifindex, + &n->ip, &n->mac); + + /* cleanup resources maintained against the neigh */ + list_delete(&n->pbr_rule_list); + + RB_REMOVE(zebra_neigh_rb_head, &zneigh_info->neigh_rb_tree, n); + + XFREE(MTYPE_ZNEIGH_ENT, n); +} + +/* kernel neigh del */ +void zebra_neigh_del(struct interface *ifp, struct ipaddr *ip) +{ + struct zebra_neigh_ent *n; + + if (IS_ZEBRA_DEBUG_NEIGH) + zlog_debug("zebra neigh del if %s/%d %pIA", ifp->name, + ifp->ifindex, ip); + + n = zebra_neigh_find(ifp->ifindex, ip); + if (!n) + return; + zebra_neigh_free(n); +} + +/* kernel neigh add */ +void zebra_neigh_add(struct interface *ifp, struct ipaddr *ip, + struct ethaddr *mac) +{ + struct zebra_neigh_ent *n; + + if (IS_ZEBRA_DEBUG_NEIGH) + zlog_debug("zebra neigh add if %s/%d %pIA %pEA", ifp->name, + ifp->ifindex, ip, mac); + + n = zebra_neigh_find(ifp->ifindex, ip); + if (n) { + if (!memcmp(&n->mac, mac, sizeof(*mac))) + return; + + memcpy(&n->mac, mac, sizeof(*mac)); + n->flags |= ZEBRA_NEIGH_ENT_ACTIVE; + + /* update rules linked to the neigh */ + zebra_neigh_pbr_rules_update(n); + } else { + zebra_neigh_new(ifp->ifindex, ip, mac); + } +} + +void zebra_neigh_deref(struct zebra_pbr_rule *rule) +{ + struct zebra_neigh_ent *n = rule->action.neigh; + + if (IS_ZEBRA_DEBUG_NEIGH) + zlog_debug("zebra neigh deref if %d %pIA by pbr rule %u", + n->ifindex, &n->ip, rule->rule.seq); + + rule->action.neigh = NULL; + /* remove rule from the list and free if it is inactive */ + list_delete_node(n->pbr_rule_list, &rule->action.neigh_listnode); + if (!(n->flags & ZEBRA_NEIGH_ENT_ACTIVE)) + zebra_neigh_free(n); +} + +/* XXX - this needs to work with evpn's neigh read */ +static void zebra_neigh_read_on_first_ref(void) +{ + static bool neigh_read_done; + + if (!neigh_read_done) { + neigh_read(zebra_ns_lookup(NS_DEFAULT)); + neigh_read_done = true; + } +} + +void zebra_neigh_ref(int ifindex, struct ipaddr *ip, + struct zebra_pbr_rule *rule) +{ + struct zebra_neigh_ent *n; + + if (IS_ZEBRA_DEBUG_NEIGH) + zlog_debug("zebra neigh ref if %d %pIA by pbr rule %u", ifindex, + ip, rule->rule.seq); + + zebra_neigh_read_on_first_ref(); + n = zebra_neigh_find(ifindex, ip); + if (!n) + n = zebra_neigh_new(ifindex, ip, NULL); + + /* link the pbr entry to the neigh */ + if (rule->action.neigh == n) + return; + + if (rule->action.neigh) + zebra_neigh_deref(rule); + + rule->action.neigh = n; + listnode_init(&rule->action.neigh_listnode, rule); + listnode_add(n->pbr_rule_list, &rule->action.neigh_listnode); +} + +static void zebra_neigh_show_one(struct vty *vty, struct zebra_neigh_ent *n) +{ + char mac_buf[ETHER_ADDR_STRLEN]; + char ip_buf[INET6_ADDRSTRLEN]; + struct interface *ifp; + + ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + n->ifindex); + ipaddr2str(&n->ip, ip_buf, sizeof(ip_buf)); + prefix_mac2str(&n->mac, mac_buf, sizeof(mac_buf)); + vty_out(vty, "%-20s %-30s %-18s %u\n", ifp ? ifp->name : "-", ip_buf, + mac_buf, listcount(n->pbr_rule_list)); +} + +void zebra_neigh_show(struct vty *vty) +{ + struct zebra_neigh_ent *n; + + vty_out(vty, "%-20s %-30s %-18s %s\n", "Interface", "Neighbor", "MAC", + "#Rules"); + RB_FOREACH (n, zebra_neigh_rb_head, &zneigh_info->neigh_rb_tree) + zebra_neigh_show_one(vty, n); +} + +void zebra_neigh_init(void) +{ + zneigh_info = XCALLOC(MTYPE_ZNEIGH_INFO, sizeof(*zrouter.neigh_info)); + RB_INIT(zebra_neigh_rb_head, &zneigh_info->neigh_rb_tree); +} + +void zebra_neigh_terminate(void) +{ + if (!zrouter.neigh_info) + return; + + XFREE(MTYPE_ZNEIGH_INFO, zneigh_info); +} diff --git a/zebra/zebra_neigh.h b/zebra/zebra_neigh.h new file mode 100644 index 0000000000..953f2e300a --- /dev/null +++ b/zebra/zebra_neigh.h @@ -0,0 +1,64 @@ +/* + * Zebra neighbor table management + * + * Copyright (C) 2021 Nvidia + * Anuradha Karuppiah + * + * 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. + */ + +#ifndef _ZEBRA_NEIGH_H +#define _ZEBRA_NEIGH_H + +#include + +#include "if.h" + +#define zneigh_info zrouter.neigh_info + +struct zebra_neigh_ent { + ifindex_t ifindex; + struct ipaddr ip; + + struct ethaddr mac; + + uint32_t flags; +#define ZEBRA_NEIGH_ENT_ACTIVE (1 << 0) /* can be used for traffic */ + + /* memory used for adding the neigt entry to zneigh_info->es_rb_tree */ + RB_ENTRY(zebra_neigh_ent) rb_node; + + /* list of pbr rules associated with this neigh */ + struct list *pbr_rule_list; +}; +RB_HEAD(zebra_neigh_rb_head, zebra_neigh_ent); +RB_PROTOTYPE(zebra_neigh_rb_head, zebra_neigh_ent, rb_node, zebra_es_rb_cmp); + +struct zebra_neigh_info { + /* RB tree of neighbor entries */ + struct zebra_neigh_rb_head neigh_rb_tree; +}; + + +/****************************************************************************/ +extern void zebra_neigh_add(struct interface *ifp, struct ipaddr *ip, + struct ethaddr *mac); +extern void zebra_neigh_del(struct interface *ifp, struct ipaddr *ip); +extern void zebra_neigh_show(struct vty *vty); +extern void zebra_neigh_init(void); +extern void zebra_neigh_terminate(void); +extern void zebra_neigh_deref(struct zebra_pbr_rule *rule); +extern void zebra_neigh_ref(int ifindex, struct ipaddr *ip, + struct zebra_pbr_rule *rule); + +#endif /* _ZEBRA_NEIGH_H */ diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index 33b8162a88..64f0193876 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -36,6 +36,21 @@ extern "C" { #endif +struct zebra_pbr_action { + afi_t afi; + + /* currently only one nexthop is supported */ + union g_addr gate; + + /* dest-interface */ + ifindex_t ifindex; + + /* neigh */ + struct zebra_neigh_ent *neigh; + /* zebr_pbr_rule is linked to neigh via neigh_listnode */ + struct listnode neigh_listnode; +}; + struct zebra_pbr_rule { int sock; @@ -43,6 +58,8 @@ struct zebra_pbr_rule { char ifname[INTERFACE_NAMSIZ]; + struct zebra_pbr_action action; + vrf_id_t vrf_id; }; diff --git a/zebra/zebra_router.c b/zebra/zebra_router.c index 92d519bad1..9fccda9e08 100644 --- a/zebra/zebra_router.c +++ b/zebra/zebra_router.c @@ -29,6 +29,7 @@ #include "zebra_vxlan.h" #include "zebra_mlag.h" #include "zebra_nhg.h" +#include "zebra_neigh.h" #include "debug.h" #include "zebra_script.h" @@ -242,6 +243,7 @@ void zebra_router_terminate(void) zebra_vxlan_disable(); zebra_mlag_terminate(); + zebra_neigh_terminate(); /* Free NHE in ID table only since it has unhashable entries as well */ hash_clean(zrouter.nhgs_id, zebra_nhg_hash_free); @@ -282,6 +284,7 @@ void zebra_router_init(bool asic_offload, bool notify_on_ack) zebra_vxlan_init(); zebra_mlag_init(); + zebra_neigh_init(); zrouter.rules_hash = hash_create_size(8, zebra_pbr_rules_hash_key, zebra_pbr_rules_hash_equal, diff --git a/zebra/zebra_router.h b/zebra/zebra_router.h index c96c8e5f46..2ae2482487 100644 --- a/zebra/zebra_router.h +++ b/zebra/zebra_router.h @@ -159,6 +159,8 @@ struct zebra_router { /* Tables and other global info maintained for EVPN multihoming */ struct zebra_evpn_mh_info *mh_info; + struct zebra_neigh_info *neigh_info; + /* EVPN MH broadcast domains indexed by the VID */ struct hash *evpn_vlan_table; diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 011fa2a1e5..0725d19677 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -64,6 +64,7 @@ #include "zebra/table_manager.h" #include "zebra/zebra_script.h" #include "zebra/rtadv.h" +#include "zebra/zebra_neigh.h" extern int allow_delete; @@ -3005,6 +3006,15 @@ DEFUN (show_evpn_global, return CMD_SUCCESS; } +DEFPY(show_evpn_neigh, show_neigh_cmd, "show ip neigh", + SHOW_STR IP_STR "neighbors\n") + +{ + zebra_neigh_show(vty); + + return CMD_SUCCESS; +} + DEFPY(show_evpn_l2_nh, show_evpn_l2_nh_cmd, "show evpn l2-nh [json$json]", @@ -4529,6 +4539,8 @@ void zebra_vty_init(void) install_element(VIEW_NODE, &show_evpn_neigh_vni_all_dad_cmd); install_element(ENABLE_NODE, &clear_evpn_dup_addr_cmd); + install_element(VIEW_NODE, &show_neigh_cmd); + install_element(VIEW_NODE, &show_pbr_ipset_cmd); install_element(VIEW_NODE, &show_pbr_iptable_cmd); install_element(VIEW_NODE, &show_route_zebra_dump_cmd); From 59f47eb0107517f417820d82db1226d011113039 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah Date: Sat, 18 Dec 2021 10:34:31 -0800 Subject: [PATCH 02/12] zebra: expand pbr rule action for dataplane programming PBR rules are installed as match, action rules in most dataplanes. This requires the action to be resolved via a GW. And the GW to be subsequently resolved to {SMAC, DMAC}. Signed-off-by: Anuradha Karuppiah --- pbrd/pbr_vty.c | 8 +- zebra/zebra_pbr.c | 217 ++++++++++++++++++++++++++++++++++++++++------ zebra/zebra_pbr.h | 7 +- zebra/zebra_vty.c | 27 ++++++ 4 files changed, 231 insertions(+), 28 deletions(-) diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index b5946bd40d..d68bcfa160 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -848,9 +848,13 @@ static void vty_show_pbrms(struct vty *vty, } if (pbrms->src) - vty_out(vty, " SRC Match: %pFX\n", pbrms->src); + vty_out(vty, " SRC IP Match: %pFX\n", pbrms->src); if (pbrms->dst) - vty_out(vty, " DST Match: %pFX\n", pbrms->dst); + vty_out(vty, " DST IP Match: %pFX\n", pbrms->dst); + if (pbrms->src_prt) + vty_out(vty, " SRC Port Match: %u\n", pbrms->src_prt); + if (pbrms->dst_prt) + vty_out(vty, " DST Port Match: %u\n", pbrms->dst_prt); if (pbrms->dsfield & PBR_DSFIELD_DSCP) vty_out(vty, " DSCP Match: %u\n", (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2); diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index bb963bcc23..e6424fea4f 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -32,6 +32,7 @@ #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"); @@ -123,6 +124,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 +150,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 */ @@ -475,24 +485,181 @@ static void *pbr_rule_alloc_intern(void *arg) 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_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 -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; + + 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 @@ -508,13 +675,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_TMP, old); } else { if (IS_ZEBRA_DEBUG_PBR) zlog_debug( @@ -522,10 +696,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 +714,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 +787,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); } } @@ -915,11 +1087,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) diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index 64f0193876..c83ece6173 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -47,7 +47,7 @@ struct zebra_pbr_action { /* neigh */ struct zebra_neigh_ent *neigh; - /* zebr_pbr_rule is linked to neigh via neigh_listnode */ + /* zebra_pbr_rule is linked to neigh via neigh_listnode */ struct listnode neigh_listnode; }; @@ -260,6 +260,8 @@ extern void zebra_pbr_iptable_free(void *arg); extern uint32_t zebra_pbr_iptable_hash_key(const void *arg); extern bool zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2); +extern void zebra_pbr_config_write(struct vty *vty); +extern void zebra_pbr_expand_action_update(bool enable); extern void zebra_pbr_init(void); extern void zebra_pbr_show_ipset_list(struct vty *vty, char *ipsetname); extern void zebra_pbr_show_iptable(struct vty *vty, char *iptable); @@ -267,6 +269,9 @@ extern void zebra_pbr_iptable_update_interfacelist(struct stream *s, struct zebra_pbr_iptable *zpi); size_t zebra_pbr_tcpflags_snprintf(char *buffer, size_t len, uint16_t tcp_val); +extern void zebra_pbr_show_rule(struct vty *vty); +extern void zebra_pbr_show_rule_unit(struct zebra_pbr_rule *rule, + struct vty *vty); DECLARE_HOOK(zebra_pbr_ipset_entry_get_stat, (struct zebra_pbr_ipset_entry *ipset, uint64_t *pkts, diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 0725d19677..440e4fce62 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -3652,6 +3652,29 @@ DEFUN (show_pbr_iptable, return CMD_SUCCESS; } +/* policy routing contexts */ +DEFPY (show_pbr_rule, + show_pbr_rule_cmd, + "show pbr rule", + SHOW_STR + "Policy-Based Routing\n" + "Rule\n") +{ + zebra_pbr_show_rule(vty); + return CMD_SUCCESS; +} + +DEFPY (pbr_nexthop_resolve, + pbr_nexthop_resolve_cmd, + "[no$no] pbr nexthop-resolve", + NO_STR + "Policy Based Routing\n" + "Resolve nexthop for dataplane programming\n") +{ + zebra_pbr_expand_action_update(!no); + return CMD_SUCCESS; +} + DEFPY (clear_evpn_dup_addr, clear_evpn_dup_addr_cmd, "clear evpn dup-addr vni ]>", @@ -3922,6 +3945,8 @@ static int config_write_protocol(struct vty *vty) zebra_evpn_mh_config_write(vty); + zebra_pbr_config_write(vty); + /* Include nexthop-group config */ if (!zebra_nhg_kernel_nexthops_enabled()) vty_out(vty, "no zebra nexthop kernel enable\n"); @@ -4543,6 +4568,8 @@ void zebra_vty_init(void) install_element(VIEW_NODE, &show_pbr_ipset_cmd); install_element(VIEW_NODE, &show_pbr_iptable_cmd); + install_element(VIEW_NODE, &show_pbr_rule_cmd); + install_element(CONFIG_NODE, &pbr_nexthop_resolve_cmd); install_element(VIEW_NODE, &show_route_zebra_dump_cmd); install_element(CONFIG_NODE, &evpn_mh_mac_holdtime_cmd); From 9898473fb4a2a6f12388a5542be7485a9f837fd6 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah Date: Sat, 18 Dec 2021 10:47:27 -0800 Subject: [PATCH 03/12] zebra: pass PBR expanded actions to the dataplane These attributes are needed for dpdk dataplane programming Signed-off-by: Anuradha Karuppiah --- zebra/zebra_dplane.c | 104 ++++++++++++++++++++++++++++++++++++++++++- zebra/zebra_dplane.h | 14 ++++++ zebra/zebra_pbr.h | 3 ++ 3 files changed, 120 insertions(+), 1 deletion(-) diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 3a3bac6c74..424cea4673 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -36,6 +36,7 @@ #include "zebra/rt.h" #include "zebra/debug.h" #include "zebra/zebra_pbr.h" +#include "zebra/zebra_neigh.h" #include "printfrr.h" /* Memory types */ @@ -265,6 +266,8 @@ struct dplane_ctx_rule { struct prefix src_ip; struct prefix dst_ip; uint8_t ip_proto; + uint16_t src_port; + uint16_t dst_port; uint8_t action_pcp; uint16_t action_vlan_id; @@ -273,6 +276,10 @@ struct dplane_ctx_rule { uint32_t action_queue_id; char ifname[INTERFACE_NAMSIZ + 1]; + struct ethaddr smac; + struct ethaddr dmac; + int out_ifindex; + intptr_t dp_flow_ptr; }; struct dplane_rule_info { @@ -2144,6 +2151,34 @@ uint8_t dplane_ctx_rule_get_old_ipproto(const struct zebra_dplane_ctx *ctx) return ctx->u.rule.old.ip_proto; } +uint16_t dplane_ctx_rule_get_src_port(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.new.src_port; +} + +uint16_t dplane_ctx_rule_get_old_src_port(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.old.src_port; +} + +uint16_t dplane_ctx_rule_get_dst_port(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.new.dst_port; +} + +uint16_t dplane_ctx_rule_get_old_dst_port(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.old.dst_port; +} + uint8_t dplane_ctx_rule_get_dsfield(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -2258,6 +2293,51 @@ void dplane_ctx_get_pbr_ipset_entry(const struct zebra_dplane_ctx *ctx, memcpy(entry, &ctx->u.ipset_entry.entry, sizeof(struct zebra_pbr_ipset_entry)); } +const struct ethaddr * +dplane_ctx_rule_get_smac(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &(ctx->u.rule.new.smac); +} + +const struct ethaddr * +dplane_ctx_rule_get_dmac(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &(ctx->u.rule.new.dmac); +} + +int dplane_ctx_rule_get_out_ifindex(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.new.out_ifindex; +} + +intptr_t dplane_ctx_rule_get_old_dp_flow_ptr(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.old.dp_flow_ptr; +} + +intptr_t dplane_ctx_rule_get_dp_flow_ptr(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.new.dp_flow_ptr; +} + +void dplane_ctx_rule_set_dp_flow_ptr(struct zebra_dplane_ctx *ctx, + intptr_t dp_flow_ptr) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.rule.new.dp_flow_ptr = dp_flow_ptr; +} + /* * End of dplane context accessors */ @@ -2976,6 +3056,8 @@ done: static void dplane_ctx_rule_init_single(struct dplane_ctx_rule *dplane_rule, struct zebra_pbr_rule *rule) { + struct zebra_neigh_ent *n; + dplane_rule->priority = rule->rule.priority; dplane_rule->table = rule->rule.action.table; @@ -2983,6 +3065,8 @@ static void dplane_ctx_rule_init_single(struct dplane_ctx_rule *dplane_rule, dplane_rule->fwmark = rule->rule.filter.fwmark; dplane_rule->dsfield = rule->rule.filter.dsfield; dplane_rule->ip_proto = rule->rule.filter.ip_proto; + dplane_rule->src_port = rule->rule.filter.src_port; + dplane_rule->dst_port = rule->rule.filter.dst_port; prefix_copy(&(dplane_rule->dst_ip), &rule->rule.filter.dst_ip); prefix_copy(&(dplane_rule->src_ip), &rule->rule.filter.src_ip); @@ -2992,6 +3076,19 @@ static void dplane_ctx_rule_init_single(struct dplane_ctx_rule *dplane_rule, dplane_rule->action_queue_id = rule->rule.action.queue_id; strlcpy(dplane_rule->ifname, rule->ifname, INTERFACE_NAMSIZ); + dplane_rule->dp_flow_ptr = rule->action.dp_flow_ptr; + n = rule->action.neigh; + if (n && (n->flags & ZEBRA_NEIGH_ENT_ACTIVE)) { + struct interface *ifp = if_lookup_by_index_per_ns( + zebra_ns_lookup(NS_DEFAULT), n->ifindex); + if (ifp) { + dplane_rule->out_ifindex = n->ifindex; + memcpy(&dplane_rule->dmac, &n->mac, ETH_ALEN); + memcpy(&dplane_rule->smac, ifp->hw_addr, ETH_ALEN); + } else { + dplane_rule->out_ifindex = 0; + } + } } /** @@ -3031,8 +3128,13 @@ static int dplane_ctx_rule_init(struct zebra_dplane_ctx *ctx, ctx->u.rule.seq = new_rule->rule.seq; dplane_ctx_rule_init_single(&ctx->u.rule.new, new_rule); - if (op == DPLANE_OP_RULE_UPDATE) + if (op == DPLANE_OP_RULE_UPDATE) { dplane_ctx_rule_init_single(&ctx->u.rule.old, old_rule); + /* clear the dp_flow_ptr in the old_rule - it is about to be + * deleted + */ + old_rule->action.dp_flow_ptr = (intptr_t)NULL; + } return AOK; } diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index d147a3e21c..d940bd9568 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -547,6 +547,10 @@ uint8_t dplane_ctx_rule_get_dsfield(const struct zebra_dplane_ctx *ctx); uint8_t dplane_ctx_rule_get_old_dsfield(const struct zebra_dplane_ctx *ctx); uint8_t dplane_ctx_rule_get_ipproto(const struct zebra_dplane_ctx *ctx); uint8_t dplane_ctx_rule_get_old_ipproto(const struct zebra_dplane_ctx *ctx); +uint16_t dplane_ctx_rule_get_src_port(const struct zebra_dplane_ctx *ctx); +uint16_t dplane_ctx_rule_get_old_src_port(const struct zebra_dplane_ctx *ctx); +uint16_t dplane_ctx_rule_get_dst_port(const struct zebra_dplane_ctx *ctx); +uint16_t dplane_ctx_rule_get_old_dst_port(const struct zebra_dplane_ctx *ctx); const struct prefix * dplane_ctx_rule_get_src_ip(const struct zebra_dplane_ctx *ctx); const struct prefix * @@ -555,6 +559,16 @@ const struct prefix * dplane_ctx_rule_get_dst_ip(const struct zebra_dplane_ctx *ctx); const struct prefix * dplane_ctx_rule_get_old_dst_ip(const struct zebra_dplane_ctx *ctx); +const struct ethaddr * +dplane_ctx_rule_get_smac(const struct zebra_dplane_ctx *ctx); +const struct ethaddr * +dplane_ctx_rule_get_dmac(const struct zebra_dplane_ctx *ctx); +int dplane_ctx_rule_get_out_ifindex(const struct zebra_dplane_ctx *ctx); +intptr_t dplane_ctx_rule_get_dp_flow_ptr(const struct zebra_dplane_ctx *ctx); +intptr_t +dplane_ctx_rule_get_old_dp_flow_ptr(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_rule_set_dp_flow_ptr(struct zebra_dplane_ctx *ctx, + intptr_t dp_flow_ptr); /* Accessors for policy based routing iptable information */ struct zebra_pbr_iptable; void dplane_ctx_get_pbr_iptable(const struct zebra_dplane_ctx *ctx, diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index c83ece6173..baa8755fa5 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -45,6 +45,9 @@ struct zebra_pbr_action { /* dest-interface */ ifindex_t ifindex; + /* dataplane info */ + intptr_t dp_flow_ptr; + /* neigh */ struct zebra_neigh_ent *neigh; /* zebra_pbr_rule is linked to neigh via neigh_listnode */ From 4f8223454f1b3df5d3b367b4c7ccfa5859bc0ada Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah Date: Thu, 30 Dec 2021 08:33:36 -0800 Subject: [PATCH 04/12] zebra: document the pbr and neigh extended commands needed for some dataplanes config - 1. "pbr nexthop-resolve" display - 1. "show pbr rule" 2. "show ip neigh" Signed-off-by: Anuradha Karuppiah --- doc/user/zebra.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index eca67c0609..77c7f87aa1 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -299,6 +299,15 @@ the default route. table. An alternative form of the command is ``show ip import-check`` and this form of the command is deprecated at this point in time. +PBR dataplane programming +========================= + +Some dataplanes require the PBR nexthop to be resolved into a SMAC, DMAC and +outgoing interface + +.. clicmd:: pbr nexthop-resolve + + Resolve PBR nexthop via ip neigh tracking Administrative Distance ======================= @@ -1188,6 +1197,14 @@ zebra Terminal Mode Commands Display whether the host's IP v6 forwarding is enabled or not. +.. clicmd:: show ip neigh + + Display the ip neighbor table + +.. clicmd:: show pbr rule + + Display the pbr rule table with resolved nexthops + .. clicmd:: show zebra Display various statistics related to the installation and deletion From 92b06d6bddbcf1336131652f0fe1cfbf0aa64ecb Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah Date: Mon, 3 Jan 2022 11:45:56 -0800 Subject: [PATCH 05/12] lib: add SYS_RAWIO to the capabilities definitions Signed-off-by: Anuradha Karuppiah --- lib/privs.c | 4 ++++ lib/privs.h | 1 + 2 files changed, 5 insertions(+) diff --git a/lib/privs.c b/lib/privs.c index 24a15a0c0b..c012178e71 100644 --- a/lib/privs.c +++ b/lib/privs.c @@ -163,6 +163,10 @@ static struct { { 1, (pvalue_t[]){CAP_IPC_LOCK}, }, + [ZCAP_SYS_RAWIO] = + { + 1, (pvalue_t[]){CAP_SYS_RAWIO}, + }, #endif /* HAVE_LCAPS */ }; diff --git a/lib/privs.h b/lib/privs.h index 9f22d28cf8..bc0786178b 100644 --- a/lib/privs.h +++ b/lib/privs.h @@ -45,6 +45,7 @@ typedef enum { ZCAP_READ_SEARCH, ZCAP_FOWNER, ZCAP_IPC_LOCK, + ZCAP_SYS_RAWIO, ZCAP_MAX } zebra_capabilities_t; From 0c57fcc731b78949b26f7f12e13b7d725db362f5 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah Date: Mon, 3 Jan 2022 11:48:20 -0800 Subject: [PATCH 06/12] zebra: add ipc_lock, read_search and sys_rawio to zebra's privileges These are needed for dpdk:rte_eal_init. Signed-off-by: Anuradha Karuppiah --- zebra/main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zebra/main.c b/zebra/main.c index 87f3de2d97..2d492f8649 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -105,9 +105,9 @@ const struct option longopts[] = { #endif /* HAVE_NETLINK */ {0}}; -zebra_capabilities_t _caps_p[] = { - ZCAP_NET_ADMIN, ZCAP_SYS_ADMIN, ZCAP_NET_RAW, -}; +zebra_capabilities_t _caps_p[] = {ZCAP_NET_ADMIN, ZCAP_SYS_ADMIN, + ZCAP_NET_RAW, ZCAP_IPC_LOCK, + ZCAP_READ_SEARCH, ZCAP_SYS_RAWIO}; /* zebra privileges to run with */ struct zebra_privs_t zserv_privs = { From 36c3b29675300b5683a97f1e3796a1f6d1778d88 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah Date: Sat, 1 Jan 2022 09:18:33 -0800 Subject: [PATCH 07/12] zebra: infastructure for the new dataplane plugin Signed-off-by: Anuradha Karuppiah --- configure.ac | 2 +- vtysh/vtysh.h | 1 + zebra/debug.c | 40 +++++ zebra/debug.h | 9 ++ zebra/dpdk/zebra_dplane_dpdk.c | 215 +++++++++++++++++++++++++ zebra/dpdk/zebra_dplane_dpdk.h | 30 ++++ zebra/dpdk/zebra_dplane_dpdk_private.h | 43 +++++ zebra/dpdk/zebra_dplane_dpdk_vty.c | 45 ++++++ zebra/subdir.am | 11 ++ 9 files changed, 395 insertions(+), 1 deletion(-) create mode 100644 zebra/dpdk/zebra_dplane_dpdk.c create mode 100644 zebra/dpdk/zebra_dplane_dpdk.h create mode 100644 zebra/dpdk/zebra_dplane_dpdk_private.h create mode 100644 zebra/dpdk/zebra_dplane_dpdk_vty.c diff --git a/configure.ac b/configure.ac index ff53e1224f..b901d7a423 100644 --- a/configure.ac +++ b/configure.ac @@ -1793,7 +1793,6 @@ AS_IF([test "$enable_pathd" != "no"], [ AC_DEFINE([HAVE_PATHD], [1], [pathd]) ]) - if test "$ac_cv_lib_json_c_json_object_get" = "no" -a "$BFDD" = "bfdd"; then AC_MSG_ERROR(["you must use json-c library to use bfdd"]) fi @@ -2707,6 +2706,7 @@ AM_CONDITIONAL([FABRICD], [test "$enable_fabricd" != "no"]) AM_CONDITIONAL([VRRPD], [test "$enable_vrrpd" != "no"]) AM_CONDITIONAL([PATHD], [test "$enable_pathd" != "no"]) AM_CONDITIONAL([PATHD_PCEP], [test "$enable_pathd" != "no"]) +AM_CONDITIONAL([DP_DPDK], [test "$enable_dp_dpdk" = "yes"]) AC_CONFIG_FILES([Makefile],[ test "$enable_dev_build" = "yes" && makefile_devbuild="--dev-build" diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index 6053955be9..1f02efee20 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -66,6 +66,7 @@ extern struct thread_master *master; /* Daemons who can process nexthop-group configs */ #define VTYSH_NH_GROUP VTYSH_PBRD|VTYSH_SHARPD #define VTYSH_SR VTYSH_ZEBRA|VTYSH_PATHD +#define VTYSH_DPDK VTYSH_ZEBRA enum vtysh_write_integrated { WRITE_INTEGRATED_UNSPECIFIED, diff --git a/zebra/debug.c b/zebra/debug.c index 98e25af857..69aaed33ac 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -38,6 +38,7 @@ unsigned long zebra_debug_mpls; unsigned long zebra_debug_vxlan; unsigned long zebra_debug_pw; unsigned long zebra_debug_dplane; +unsigned long zebra_debug_dplane_dpdk; unsigned long zebra_debug_mlag; unsigned long zebra_debug_nexthop; unsigned long zebra_debug_evpn_mh; @@ -108,6 +109,11 @@ DEFUN_NOSH (show_debugging_zebra, vty_out(vty, " Zebra detailed dataplane debugging is on\n"); else if (IS_ZEBRA_DEBUG_DPLANE) vty_out(vty, " Zebra dataplane debugging is on\n"); + if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) + vty_out(vty, + " Zebra detailed dpdk dataplane debugging is on\n"); + else if (IS_ZEBRA_DEBUG_DPLANE_DPDK) + vty_out(vty, " Zebra dataplane dpdk debugging is on\n"); if (IS_ZEBRA_DEBUG_MLAG) vty_out(vty, " Zebra mlag debugging is on\n"); if (IS_ZEBRA_DEBUG_NHG_DETAIL) @@ -317,6 +323,29 @@ DEFUN (debug_zebra_dplane, return CMD_SUCCESS; } +DEFPY(debug_zebra_dplane_dpdk, debug_zebra_dplane_dpdk_cmd, + "[no$no] debug zebra dplane dpdk [detailed$detail]", + NO_STR DEBUG_STR + "Zebra configuration\n" + "Debug zebra dataplane events\n" + "Debug zebra DPDK offload events\n" + "Detailed debug information\n") +{ + if (no) { + UNSET_FLAG(zebra_debug_dplane_dpdk, ZEBRA_DEBUG_DPLANE_DPDK); + UNSET_FLAG(zebra_debug_dplane_dpdk, + ZEBRA_DEBUG_DPLANE_DPDK_DETAIL); + } else { + SET_FLAG(zebra_debug_dplane_dpdk, ZEBRA_DEBUG_DPLANE_DPDK); + + if (detail) + SET_FLAG(zebra_debug_dplane, + ZEBRA_DEBUG_DPLANE_DPDK_DETAIL); + } + + return CMD_SUCCESS; +} + DEFUN (debug_zebra_pbr, debug_zebra_pbr_cmd, "debug zebra pbr", @@ -698,6 +727,14 @@ static int config_write_debug(struct vty *vty) write++; } + if (CHECK_FLAG(zebra_debug_dplane, ZEBRA_DEBUG_DPLANE_DPDK_DETAIL)) { + vty_out(vty, "debug zebra dplane dpdk detailed\n"); + write++; + } else if (CHECK_FLAG(zebra_debug_dplane, ZEBRA_DEBUG_DPLANE_DPDK)) { + vty_out(vty, "debug zebra dplane dpdk\n"); + write++; + } + if (CHECK_FLAG(zebra_debug_nexthop, ZEBRA_DEBUG_NHG_DETAILED)) { vty_out(vty, "debug zebra nexthop detail\n"); write++; @@ -730,6 +767,7 @@ void zebra_debug_init(void) zebra_debug_vxlan = 0; zebra_debug_pw = 0; zebra_debug_dplane = 0; + zebra_debug_dplane_dpdk = 0; zebra_debug_mlag = 0; zebra_debug_evpn_mh = 0; zebra_debug_nht = 0; @@ -758,6 +796,7 @@ void zebra_debug_init(void) install_element(ENABLE_NODE, &debug_zebra_nexthop_cmd); install_element(ENABLE_NODE, &debug_zebra_pbr_cmd); install_element(ENABLE_NODE, &debug_zebra_neigh_cmd); + install_element(ENABLE_NODE, &debug_zebra_dplane_dpdk_cmd); install_element(ENABLE_NODE, &no_debug_zebra_events_cmd); install_element(ENABLE_NODE, &no_debug_zebra_nht_cmd); install_element(ENABLE_NODE, &no_debug_zebra_mpls_cmd); @@ -786,6 +825,7 @@ void zebra_debug_init(void) install_element(CONFIG_NODE, &debug_zebra_rib_cmd); install_element(CONFIG_NODE, &debug_zebra_fpm_cmd); install_element(CONFIG_NODE, &debug_zebra_dplane_cmd); + install_element(CONFIG_NODE, &debug_zebra_dplane_dpdk_cmd); install_element(CONFIG_NODE, &debug_zebra_nexthop_cmd); install_element(CONFIG_NODE, &debug_zebra_pbr_cmd); install_element(CONFIG_NODE, &debug_zebra_neigh_cmd); diff --git a/zebra/debug.h b/zebra/debug.h index e761e5e3e3..73546de632 100644 --- a/zebra/debug.h +++ b/zebra/debug.h @@ -58,6 +58,9 @@ extern "C" { #define ZEBRA_DEBUG_DPLANE 0x01 #define ZEBRA_DEBUG_DPLANE_DETAILED 0x02 +#define ZEBRA_DEBUG_DPLANE_DPDK 0x01 +#define ZEBRA_DEBUG_DPLANE_DPDK_DETAIL 0x02 + #define ZEBRA_DEBUG_MLAG 0x01 #define ZEBRA_DEBUG_NHG 0x01 @@ -105,6 +108,11 @@ extern "C" { #define IS_ZEBRA_DEBUG_DPLANE_DETAIL \ (zebra_debug_dplane & ZEBRA_DEBUG_DPLANE_DETAILED) +#define IS_ZEBRA_DEBUG_DPLANE_DPDK \ + (zebra_debug_dplane & ZEBRA_DEBUG_DPLANE_DPDK) +#define IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL \ + (zebra_debug_dplane & ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) + #define IS_ZEBRA_DEBUG_MLAG (zebra_debug_mlag & ZEBRA_DEBUG_MLAG) #define IS_ZEBRA_DEBUG_NHG (zebra_debug_nexthop & ZEBRA_DEBUG_NHG) @@ -135,6 +143,7 @@ extern unsigned long zebra_debug_mpls; extern unsigned long zebra_debug_vxlan; extern unsigned long zebra_debug_pw; extern unsigned long zebra_debug_dplane; +extern unsigned long zebra_debug_dplane_dpdk; extern unsigned long zebra_debug_mlag; extern unsigned long zebra_debug_nexthop; extern unsigned long zebra_debug_evpn_mh; diff --git a/zebra/dpdk/zebra_dplane_dpdk.c b/zebra/dpdk/zebra_dplane_dpdk.c new file mode 100644 index 0000000000..a8dfdf86d5 --- /dev/null +++ b/zebra/dpdk/zebra_dplane_dpdk.c @@ -0,0 +1,215 @@ +/* + * Zebra dataplane plugin for DPDK based hw offload + * + * Copyright (C) 2021 Nvidia + * Anuradha Karuppiah + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" /* Include this explicitly */ +#endif + +#include "lib/libfrr.h" + +#include "zebra/debug.h" +#include "zebra/interface.h" +#include "zebra/zebra_dplane.h" +#include "zebra/debug.h" +#include "zebra/zebra_pbr.h" + +#include "zebra/dpdk/zebra_dplane_dpdk_private.h" + +static const char *plugin_name = "zebra_dplane_dpdk"; + +static struct zd_dpdk_ctx dpdk_ctx_buf, *dpdk_ctx = &dpdk_ctx_buf; +#define dpdk_stat (&dpdk_ctx->stats) + + +void zd_dpdk_stat_show(struct vty *vty) +{ + uint32_t tmp_cnt; + + vty_out(vty, "%30s\n%30s\n", "Dataplane DPDK counters", + "======================="); + +#define ZD_DPDK_SHOW_COUNTER(label, counter) \ + do { \ + tmp_cnt = \ + atomic_load_explicit(&counter, memory_order_relaxed); \ + vty_out(vty, "%28s: %u\n", (label), (tmp_cnt)); \ + } while (0); + + ZD_DPDK_SHOW_COUNTER("PBR rule adds", dpdk_stat->rule_adds); + ZD_DPDK_SHOW_COUNTER("PBR rule dels", dpdk_stat->rule_dels); + ZD_DPDK_SHOW_COUNTER("Ignored updates", dpdk_stat->ignored_updates); +} + + +static void zd_dpdk_rule_add(struct zebra_dplane_ctx *ctx) +{ + /* XXX - place holder */ +} + + +static void zd_dpdk_rule_del(const char *ifname, int in_ifindex, + intptr_t dp_flow_ptr) +{ + + /* XXX - place holder */ +} + + +static void zd_dpdk_rule_update(struct zebra_dplane_ctx *ctx) +{ + enum dplane_op_e op; + int in_ifindex; + intptr_t dp_flow_ptr; + + if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) { + zlog_debug("Dplane %s", dplane_op2str(dplane_ctx_get_op(ctx))); + } + + op = dplane_ctx_get_op(ctx); + switch (op) { + case DPLANE_OP_RULE_ADD: + atomic_fetch_add_explicit(&dpdk_stat->rule_adds, 1, + memory_order_relaxed); + zd_dpdk_rule_add(ctx); + break; + + case DPLANE_OP_RULE_UPDATE: + /* delete old rule and install new one */ + atomic_fetch_add_explicit(&dpdk_stat->rule_adds, 1, + memory_order_relaxed); + in_ifindex = dplane_ctx_get_ifindex(ctx); + dp_flow_ptr = dplane_ctx_rule_get_old_dp_flow_ptr(ctx); + zd_dpdk_rule_del(dplane_ctx_rule_get_ifname(ctx), in_ifindex, + dp_flow_ptr); + zd_dpdk_rule_add(ctx); + break; + + case DPLANE_OP_RULE_DELETE: + atomic_fetch_add_explicit(&dpdk_stat->rule_dels, 1, + memory_order_relaxed); + in_ifindex = dplane_ctx_get_ifindex(ctx); + dp_flow_ptr = dplane_ctx_rule_get_dp_flow_ptr(ctx); + zd_dpdk_rule_del(dplane_ctx_rule_get_ifname(ctx), in_ifindex, + dp_flow_ptr); + break; + + default:; + } +} + + +/* DPDK provider callback. + */ +static void zd_dpdk_process_update(struct zebra_dplane_ctx *ctx) +{ + switch (dplane_ctx_get_op(ctx)) { + + case DPLANE_OP_RULE_ADD: + case DPLANE_OP_RULE_UPDATE: + case DPLANE_OP_RULE_DELETE: + zd_dpdk_rule_update(ctx); + break; + + default: + atomic_fetch_add_explicit(&dpdk_stat->ignored_updates, 1, + memory_order_relaxed); + + break; + } +} + + +static int zd_dpdk_process(struct zebra_dplane_provider *prov) +{ + struct zebra_dplane_ctx *ctx; + int counter, limit; + + if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) + zlog_debug("processing %s", dplane_provider_get_name(prov)); + + limit = dplane_provider_get_work_limit(prov); + for (counter = 0; counter < limit; counter++) { + ctx = dplane_provider_dequeue_in_ctx(prov); + if (!ctx) + break; + + zd_dpdk_process_update(ctx); + dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS); + dplane_provider_enqueue_out_ctx(prov, ctx); + } + + return 0; +} + + +static int zd_dpdk_start(struct zebra_dplane_provider *prov) +{ + if (IS_ZEBRA_DEBUG_DPLANE_DPDK) + zlog_debug("%s start", dplane_provider_get_name(prov)); + + /* XXX - place holder */ + return 0; +} + + +static int zd_dpdk_finish(struct zebra_dplane_provider *prov, bool early) +{ + if (early) { + if (IS_ZEBRA_DEBUG_DPLANE_DPDK) + zlog_debug("%s early finish", + dplane_provider_get_name(prov)); + + return 0; + } + + if (IS_ZEBRA_DEBUG_DPLANE_DPDK) + zlog_debug("%s finish", dplane_provider_get_name(prov)); + + + /* XXX - place holder */ + return 0; +} + + +static int zd_dpdk_plugin_init(struct thread_master *tm) +{ + int ret; + + ret = dplane_provider_register( + plugin_name, DPLANE_PRIO_KERNEL, DPLANE_PROV_FLAGS_DEFAULT, + zd_dpdk_start, zd_dpdk_process, zd_dpdk_finish, dpdk_ctx, NULL); + + if (IS_ZEBRA_DEBUG_DPLANE_DPDK) + zlog_debug("%s register status %d", plugin_name, ret); + + return 0; +} + + +static int zd_dpdk_module_init(void) +{ + hook_register(frr_late_init, zd_dpdk_plugin_init); + return 0; +} + +FRR_MODULE_SETUP(.name = "dplane_dpdk", .version = "0.0.1", + .description = "Data plane plugin using dpdk for hw offload", + .init = zd_dpdk_module_init, ); diff --git a/zebra/dpdk/zebra_dplane_dpdk.h b/zebra/dpdk/zebra_dplane_dpdk.h new file mode 100644 index 0000000000..469fd8c5bc --- /dev/null +++ b/zebra/dpdk/zebra_dplane_dpdk.h @@ -0,0 +1,30 @@ +/* + * Zebra dataplane plugin for DPDK based hw offload + * + * Copyright (C) 2021 Nvidia + * Anuradha Karuppiah + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _ZEBRA_DPLANE_DPDK_H +#define _ZEBRA_DPLANE_DPDK_H + +#include + +extern void zd_dpdk_stat_show(struct vty *vty); +extern void zd_dpdk_vty_init(void); + +#endif diff --git a/zebra/dpdk/zebra_dplane_dpdk_private.h b/zebra/dpdk/zebra_dplane_dpdk_private.h new file mode 100644 index 0000000000..24b943dddd --- /dev/null +++ b/zebra/dpdk/zebra_dplane_dpdk_private.h @@ -0,0 +1,43 @@ +/* + * Zebra dataplane plugin for DPDK based hw offload + * + * Copyright (C) 2021 Nvidia + * Anuradha Karuppiah + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _ZEBRA_DPLANE_DPDK_PRIVATE_H +#define _ZEBRA_DPLANE_DPDK_PRIVATE_H + +#include + +#include "zebra_dplane_dpdk.h" + + +struct zd_dpdk_stat { + _Atomic uint32_t ignored_updates; + + _Atomic uint32_t rule_adds; + _Atomic uint32_t rule_dels; +}; + +struct zd_dpdk_ctx { + /* Stats */ + struct zd_dpdk_stat stats; + int dpdk_logtype; +}; + +#endif diff --git a/zebra/dpdk/zebra_dplane_dpdk_vty.c b/zebra/dpdk/zebra_dplane_dpdk_vty.c new file mode 100644 index 0000000000..c1fbb20c4c --- /dev/null +++ b/zebra/dpdk/zebra_dplane_dpdk_vty.c @@ -0,0 +1,45 @@ +/* + * Zebra dataplane plugin for DPDK based hw offload + * + * Copyright (C) 2021 Nvidia + * Donald Sharp + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "lib/json.h" +#include "zebra/dpdk/zebra_dplane_dpdk.h" + +#ifndef VTYSH_EXTRACT_PL +#include "zebra/dpdk/zebra_dplane_dpdk_vty_clippy.c" +#endif + +#define ZD_STR "Zebra dataplane information\n" +#define ZD_DPDK_STR "DPDK offload information\n" + +DEFPY(zd_dpdk_show_counters, zd_dpdk_show_counters_cmd, + "show dplane dpdk counters", + SHOW_STR ZD_STR ZD_DPDK_STR "show counters\n") +{ + zd_dpdk_stat_show(vty); + + return CMD_SUCCESS; +} + +void zd_dpdk_vty_init(void) +{ + install_element(VIEW_NODE, &zd_dpdk_show_counters_cmd); +} diff --git a/zebra/subdir.am b/zebra/subdir.am index e895ffa65f..89836f3ba4 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -20,6 +20,7 @@ vtysh_scan += \ zebra/zebra_vty.c \ zebra/zserv.c \ zebra/zebra_vrf.c \ + zebra/dpdk/zebra_dplane_dpdk_vty.c \ # end # can be loaded as DSO - always include for vtysh @@ -139,6 +140,7 @@ clippy_scan += \ zebra/zebra_vty.c \ zebra/zebra_srv6_vty.c \ zebra/zebra_vrf.c \ + zebra/dpdk/zebra_dplane_dpdk_vty.c \ # end noinst_HEADERS += \ @@ -200,6 +202,8 @@ noinst_HEADERS += \ zebra/zebra_evpn_mh.h \ zebra/zebra_neigh.h \ zebra/zserv.h \ + zebra/dpdk/zebra_dplane_dpdk.h \ + zebra/dpdk/zebra_dplane_dpdk_private.h \ # end zebra_zebra_irdp_la_SOURCES = \ @@ -257,3 +261,10 @@ zebra_zebra_SOURCES += \ zebra/debug_nl.c \ # end endif + +if DP_DPDK +module_LTLIBRARIES += zebra/zebra_dplane_dpdk.la +endif + +zebra_zebra_dplane_dpdk_la_SOURCES = zebra/dpdk/zebra_dplane_dpdk.c zebra/dpdk/zebra_dplane_dpdk_vty.c +zebra_zebra_dplane_dpdk_la_LDFLAGS = -avoid-version -module -shared -export-dynamic -L/usr/local/lib -v From fd03f1d4b7c9d03846b300258dc3d2d3ab70432c Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah Date: Thu, 30 Dec 2021 14:41:42 -0800 Subject: [PATCH 08/12] configure, zebra: include DPDK headers and shared libs in the dp-dpdk build Signed-off-by: Donald Sharp -> Moved new capabilities needed to under HAVE_DPDK Signed-off-by: Anuradha Karuppiah --- configure.ac | 12 ++++++++++++ zebra/main.c | 10 +++++++--- zebra/subdir.am | 2 ++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index b901d7a423..330752a79f 100644 --- a/configure.ac +++ b/configure.ac @@ -1998,6 +1998,18 @@ if test "$enable_grpc" = "yes"; then AC_LANG_POP([C++]) fi +dnl --------- +dnl DPDK +dnl --------- +if test "$enable_dp_dpdk" = "yes"; then + PKG_CHECK_MODULES([DPDK], [libdpdk], [ + AC_DEFINE([HAVE_DPDK], [1], [Enable DPDK backend]) + DPDK=true + ], [ + AC_MSG_ERROR([configuration specifies --enable-dp-dpdk but DPDK libs were not found]) + ]) +fi + dnl ----- dnl LTTng dnl ----- diff --git a/zebra/main.c b/zebra/main.c index 2d492f8649..e516688a19 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -105,9 +105,13 @@ const struct option longopts[] = { #endif /* HAVE_NETLINK */ {0}}; -zebra_capabilities_t _caps_p[] = {ZCAP_NET_ADMIN, ZCAP_SYS_ADMIN, - ZCAP_NET_RAW, ZCAP_IPC_LOCK, - ZCAP_READ_SEARCH, ZCAP_SYS_RAWIO}; +zebra_capabilities_t _caps_p[] = {ZCAP_NET_ADMIN, ZCAP_SYS_ADMIN, + ZCAP_NET_RAW, +#ifdef HAVE_DPDK + ZCAP_IPC_LOCK, ZCAP_READ_SEARCH, + ZCAP_SYS_RAWIO +#endif +}; /* zebra privileges to run with */ struct zebra_privs_t zserv_privs = { diff --git a/zebra/subdir.am b/zebra/subdir.am index 89836f3ba4..a926c14adf 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -268,3 +268,5 @@ endif zebra_zebra_dplane_dpdk_la_SOURCES = zebra/dpdk/zebra_dplane_dpdk.c zebra/dpdk/zebra_dplane_dpdk_vty.c zebra_zebra_dplane_dpdk_la_LDFLAGS = -avoid-version -module -shared -export-dynamic -L/usr/local/lib -v +zebra_zebra_dplane_dpdk_la_CFLAGS = $(DPDK_CFLAGS) +zebra_zebra_dplane_dpdk_la_LIBADD = $(DPDK_LIBS) From 67f5a232408af238d88f4c85691ff56227f002be Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah Date: Thu, 30 Dec 2021 14:49:19 -0800 Subject: [PATCH 09/12] zebra: initialize hw via DPDK Signed-off-by: Anuradha Karuppiah --- zebra/dpdk/zebra_dplane_dpdk.c | 31 +++++++++++++++++++++++--- zebra/dpdk/zebra_dplane_dpdk_private.h | 2 ++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/zebra/dpdk/zebra_dplane_dpdk.c b/zebra/dpdk/zebra_dplane_dpdk.c index a8dfdf86d5..44c6bac94d 100644 --- a/zebra/dpdk/zebra_dplane_dpdk.c +++ b/zebra/dpdk/zebra_dplane_dpdk.c @@ -35,6 +35,8 @@ static const char *plugin_name = "zebra_dplane_dpdk"; +extern struct zebra_privs_t zserv_privs; + static struct zd_dpdk_ctx dpdk_ctx_buf, *dpdk_ctx = &dpdk_ctx_buf; #define dpdk_stat (&dpdk_ctx->stats) @@ -159,19 +161,37 @@ static int zd_dpdk_process(struct zebra_dplane_provider *prov) return 0; } +static int zd_dpdk_init(void) +{ + int rc; + char *argv[] = {(char *)"/usr/lib/frr/zebra", (char *)"--"}; + + zd_dpdk_vty_init(); + + frr_with_privs (&zserv_privs) { + rc = rte_eal_init(sizeof(argv) / sizeof(argv[0]), argv); + } + if (rc < 0) { + zlog_warn("EAL init failed %s", rte_strerror(rte_errno)); + return -1; + } + + return 0; +} static int zd_dpdk_start(struct zebra_dplane_provider *prov) { if (IS_ZEBRA_DEBUG_DPLANE_DPDK) zlog_debug("%s start", dplane_provider_get_name(prov)); - /* XXX - place holder */ - return 0; + return zd_dpdk_init(); } static int zd_dpdk_finish(struct zebra_dplane_provider *prov, bool early) { + int rc; + if (early) { if (IS_ZEBRA_DEBUG_DPLANE_DPDK) zlog_debug("%s early finish", @@ -184,7 +204,12 @@ static int zd_dpdk_finish(struct zebra_dplane_provider *prov, bool early) zlog_debug("%s finish", dplane_provider_get_name(prov)); - /* XXX - place holder */ + frr_with_privs (&zserv_privs) { + rc = rte_eal_cleanup(); + } + if (rc < 0) + zlog_warn("EAL cleanup failed %s", rte_strerror(rte_errno)); + return 0; } diff --git a/zebra/dpdk/zebra_dplane_dpdk_private.h b/zebra/dpdk/zebra_dplane_dpdk_private.h index 24b943dddd..8f50296084 100644 --- a/zebra/dpdk/zebra_dplane_dpdk_private.h +++ b/zebra/dpdk/zebra_dplane_dpdk_private.h @@ -24,6 +24,8 @@ #include +#include + #include "zebra_dplane_dpdk.h" From a66d6246616e7ba3aebda5d2524164e3fd8f7269 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah Date: Thu, 30 Dec 2021 14:56:30 -0800 Subject: [PATCH 10/12] zebra: setup the zebra interface to dpdk port map table 1. Create mappping table between ifIndex and dpdk-port-id 2. Start the DPDK port Signed-off-by: Anuradha Karuppiah --- zebra/dpdk/zebra_dplane_dpdk.c | 148 +++++++++++++++++++++++++ zebra/dpdk/zebra_dplane_dpdk.h | 5 + zebra/dpdk/zebra_dplane_dpdk_private.h | 9 ++ zebra/dpdk/zebra_dplane_dpdk_vty.c | 24 ++++ 4 files changed, 186 insertions(+) diff --git a/zebra/dpdk/zebra_dplane_dpdk.c b/zebra/dpdk/zebra_dplane_dpdk.c index 44c6bac94d..79b8c22b02 100644 --- a/zebra/dpdk/zebra_dplane_dpdk.c +++ b/zebra/dpdk/zebra_dplane_dpdk.c @@ -40,6 +40,9 @@ extern struct zebra_privs_t zserv_privs; static struct zd_dpdk_ctx dpdk_ctx_buf, *dpdk_ctx = &dpdk_ctx_buf; #define dpdk_stat (&dpdk_ctx->stats) +static struct zd_dpdk_port *zd_dpdk_port_find_by_index(int ifindex); + +DEFINE_MTYPE_STATIC(ZEBRA, DPDK_PORTS, "ZD DPDK port database"); void zd_dpdk_stat_show(struct vty *vty) { @@ -161,6 +164,147 @@ static int zd_dpdk_process(struct zebra_dplane_provider *prov) return 0; } +static void zd_dpdk_port_show_entry(struct zd_dpdk_port *dport, struct vty *vty, + int detail) +{ + struct rte_eth_dev_info *dev_info; + + dev_info = &dport->dev_info; + if (detail) { + vty_out(vty, "DPDK port: %u\n", dport->port_id); + vty_out(vty, " Device: %s\n", + dev_info->device ? dev_info->device->name : "-"); + vty_out(vty, " Driver: %s\n", + dev_info->driver_name ? dev_info->driver_name : "-"); + vty_out(vty, " Interface: %s (%d)\n", + ifindex2ifname(dev_info->if_index, VRF_DEFAULT), + dev_info->if_index); + vty_out(vty, " Switch: %s Domain: %u Port: %u\n", + dev_info->switch_info.name, + dev_info->switch_info.domain_id, + dev_info->switch_info.port_id); + vty_out(vty, "\n"); + } else { + vty_out(vty, "%-4u %-16s %-16s %-16d %s,%u,%u\n", + dport->port_id, + dev_info->device ? dev_info->device->name : "-", + ifindex2ifname(dev_info->if_index, VRF_DEFAULT), + dev_info->if_index, dev_info->switch_info.name, + dev_info->switch_info.domain_id, + dev_info->switch_info.port_id); + } +} + + +static struct zd_dpdk_port *zd_dpdk_port_find_by_index(int ifindex) +{ + int count; + struct zd_dpdk_port *dport; + struct rte_eth_dev_info *dev_info; + + for (count = 0; count < RTE_MAX_ETHPORTS; ++count) { + dport = &dpdk_ctx->dpdk_ports[count]; + if (!(dport->flags & ZD_DPDK_PORT_FLAG_INITED)) + continue; + dev_info = &dport->dev_info; + if (dev_info->if_index == (uint32_t)ifindex) + return dport; + } + + return NULL; +} + + +void zd_dpdk_port_show(struct vty *vty, uint16_t port_id, bool uj, int detail) +{ + int count; + struct zd_dpdk_port *dport; + + /* XXX - support for json is yet to be added */ + if (uj) + return; + + if (!detail) { + vty_out(vty, "%-4s %-16s %-16s %-16s %s\n", "Port", "Device", + "IfName", "IfIndex", "sw,domain,port"); + } + + for (count = 0; count < RTE_MAX_ETHPORTS; ++count) { + dport = &dpdk_ctx->dpdk_ports[count]; + if (dport->flags & ZD_DPDK_PORT_FLAG_INITED) + zd_dpdk_port_show_entry(dport, vty, detail); + } +} + + +static void zd_dpdk_port_init(void) +{ + struct zd_dpdk_port *dport; + uint16_t port_id; + struct rte_eth_dev_info *dev_info; + int count; + int rc; + struct rte_flow_error error; + + /* allocate a list of ports */ + dpdk_ctx->dpdk_ports = + XCALLOC(MTYPE_DPDK_PORTS, + sizeof(struct zd_dpdk_port) * RTE_MAX_ETHPORTS); + + if (IS_ZEBRA_DEBUG_DPLANE_DPDK) + zlog_debug("dpdk port init"); + count = 0; + RTE_ETH_FOREACH_DEV(port_id) + { + if (IS_ZEBRA_DEBUG_DPLANE_DPDK) + zlog_debug("dpdk port init %d", port_id); + dport = &dpdk_ctx->dpdk_ports[count]; + count++; + dport->port_id = port_id; + dport->flags |= ZD_DPDK_PORT_FLAG_PROBED; + dev_info = &dport->dev_info; + if (rte_eth_dev_info_get(port_id, dev_info) < 0) { + zlog_warn("failed to get dev info for %u, %s", port_id, + rte_strerror(rte_errno)); + continue; + } + dport->flags |= ZD_DPDK_PORT_FLAG_INITED; + if (IS_ZEBRA_DEBUG_DPLANE_DPDK) + zlog_debug( + "port %u, dev %s, ifI %d, sw_name %s, sw_domain %u, sw_port %u", + port_id, + dev_info->device ? dev_info->device->name : "-", + dev_info->if_index, dev_info->switch_info.name, + dev_info->switch_info.domain_id, + dev_info->switch_info.port_id); + if (rte_flow_isolate(port_id, 1, &error)) { + if (IS_ZEBRA_DEBUG_DPLANE_DPDK) + zlog_debug( + "Flow isolate on port %u failed %d\n", + port_id, error.type); + } else { + if (IS_ZEBRA_DEBUG_DPLANE_DPDK) + zlog_debug("Flow isolate on port %u\n", + port_id); + } + rc = rte_eth_dev_start(port_id); + if (rc) { + zlog_warn("DPDK port %d start error: %s", port_id, + rte_strerror(-rc)); + continue; + } + if (IS_ZEBRA_DEBUG_DPLANE_DPDK) + zlog_debug("DPDK port %d started in promiscuous mode ", + port_id); + } + + if (!count) { + if (IS_ZEBRA_DEBUG_DPLANE_DPDK) + zlog_debug("no probed ethernet devices"); + } +} + + static int zd_dpdk_init(void) { int rc; @@ -176,9 +320,13 @@ static int zd_dpdk_init(void) return -1; } + frr_with_privs (&zserv_privs) { + zd_dpdk_port_init(); + } return 0; } + static int zd_dpdk_start(struct zebra_dplane_provider *prov) { if (IS_ZEBRA_DEBUG_DPLANE_DPDK) diff --git a/zebra/dpdk/zebra_dplane_dpdk.h b/zebra/dpdk/zebra_dplane_dpdk.h index 469fd8c5bc..32ace04078 100644 --- a/zebra/dpdk/zebra_dplane_dpdk.h +++ b/zebra/dpdk/zebra_dplane_dpdk.h @@ -24,6 +24,11 @@ #include + +#define ZD_DPDK_INVALID_PORT 0xffff + +extern void zd_dpdk_port_show(struct vty *vty, uint16_t port_id, bool uj, + int detail); extern void zd_dpdk_stat_show(struct vty *vty); extern void zd_dpdk_vty_init(void); diff --git a/zebra/dpdk/zebra_dplane_dpdk_private.h b/zebra/dpdk/zebra_dplane_dpdk_private.h index 8f50296084..0eda45b2e3 100644 --- a/zebra/dpdk/zebra_dplane_dpdk_private.h +++ b/zebra/dpdk/zebra_dplane_dpdk_private.h @@ -29,6 +29,14 @@ #include "zebra_dplane_dpdk.h" +struct zd_dpdk_port { + uint16_t port_id; /* dpdk port_id */ + struct rte_eth_dev_info dev_info; /* PCI info + driver name */ + uint32_t flags; +#define ZD_DPDK_PORT_FLAG_PROBED (1 << 0) +#define ZD_DPDK_PORT_FLAG_INITED (1 << 1) +}; + struct zd_dpdk_stat { _Atomic uint32_t ignored_updates; @@ -39,6 +47,7 @@ struct zd_dpdk_stat { struct zd_dpdk_ctx { /* Stats */ struct zd_dpdk_stat stats; + struct zd_dpdk_port *dpdk_ports; int dpdk_logtype; }; diff --git a/zebra/dpdk/zebra_dplane_dpdk_vty.c b/zebra/dpdk/zebra_dplane_dpdk_vty.c index c1fbb20c4c..11df697485 100644 --- a/zebra/dpdk/zebra_dplane_dpdk_vty.c +++ b/zebra/dpdk/zebra_dplane_dpdk_vty.c @@ -39,7 +39,31 @@ DEFPY(zd_dpdk_show_counters, zd_dpdk_show_counters_cmd, return CMD_SUCCESS; } + +DEFPY (zd_dpdk_show_ports, + zd_dpdk_show_ports_cmd, + "show dplane dpdk port [(1-32)$port_id] [detail$detail] [json$json]", + SHOW_STR + ZD_STR + ZD_DPDK_STR + "show port info\n" + "DPDK port identifier\n" + "Detailed information\n" + JSON_STR) +{ + bool uj = !!json; + bool ud = !!detail; + + if (!port_id) + port_id = ZD_DPDK_INVALID_PORT; + zd_dpdk_port_show(vty, port_id, uj, ud); + + return CMD_SUCCESS; +} + + void zd_dpdk_vty_init(void) { install_element(VIEW_NODE, &zd_dpdk_show_counters_cmd); + install_element(VIEW_NODE, &zd_dpdk_show_ports_cmd); } From 7f0416b368b3192c1ac81834d03130b313fd67f5 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah Date: Thu, 30 Dec 2021 15:08:07 -0800 Subject: [PATCH 11/12] zebra: PBR dpdk programming 1. Offload PBR rule add/del 2. Query DPDK flow stats and display per-PBR entry Signed-off-by: Anuradha Karuppiah --- zebra/dpdk/zebra_dplane_dpdk.c | 385 +++++++++++++++++++++++-- zebra/dpdk/zebra_dplane_dpdk.h | 1 + zebra/dpdk/zebra_dplane_dpdk_private.h | 7 + zebra/dpdk/zebra_dplane_dpdk_vty.c | 16 + 4 files changed, 389 insertions(+), 20 deletions(-) diff --git a/zebra/dpdk/zebra_dplane_dpdk.c b/zebra/dpdk/zebra_dplane_dpdk.c index 79b8c22b02..11a1af86c6 100644 --- a/zebra/dpdk/zebra_dplane_dpdk.c +++ b/zebra/dpdk/zebra_dplane_dpdk.c @@ -35,8 +35,6 @@ static const char *plugin_name = "zebra_dplane_dpdk"; -extern struct zebra_privs_t zserv_privs; - static struct zd_dpdk_ctx dpdk_ctx_buf, *dpdk_ctx = &dpdk_ctx_buf; #define dpdk_stat (&dpdk_ctx->stats) @@ -56,7 +54,7 @@ void zd_dpdk_stat_show(struct vty *vty) tmp_cnt = \ atomic_load_explicit(&counter, memory_order_relaxed); \ vty_out(vty, "%28s: %u\n", (label), (tmp_cnt)); \ - } while (0); + } while (0) ZD_DPDK_SHOW_COUNTER("PBR rule adds", dpdk_stat->rule_adds); ZD_DPDK_SHOW_COUNTER("PBR rule dels", dpdk_stat->rule_dels); @@ -64,17 +62,279 @@ void zd_dpdk_stat_show(struct vty *vty) } -static void zd_dpdk_rule_add(struct zebra_dplane_ctx *ctx) +static void zd_dpdk_flow_stat_show(struct vty *vty, int in_ifindex, + intptr_t dp_flow_ptr) { - /* XXX - place holder */ + struct rte_flow_action_count count = {.shared = 0, .id = 0}; + const struct rte_flow_action actions[] = { + { + .type = RTE_FLOW_ACTION_TYPE_COUNT, + .conf = &count, + }, + { + .type = RTE_FLOW_ACTION_TYPE_END, + }, + }; + int rc; + struct zd_dpdk_port *in_dport; + struct rte_flow_query_count query; + struct rte_flow_error error; + uint64_t hits, bytes; + + in_dport = zd_dpdk_port_find_by_index(in_ifindex); + if (!in_dport) { + vty_out(vty, "PBR dpdk flow query failed; in_port %d missing\n", + in_ifindex); + return; + } + memset(&query, 0, sizeof(query)); + rc = rte_flow_query(in_dport->port_id, (struct rte_flow *)dp_flow_ptr, + actions, &query, &error); + if (rc) { + vty_out(vty, + "PBR dpdk flow query failed; in_ifindex %d rc %d\n", + in_ifindex, error.type); + return; + } + hits = (query.hits_set) ? query.hits : 0; + bytes = (query.bytes_set) ? query.bytes : 0; + vty_out(vty, " DPDK stats: packets %" PRIu64 " bytes %" PRIu64 "\n", + hits, bytes); } -static void zd_dpdk_rule_del(const char *ifname, int in_ifindex, - intptr_t dp_flow_ptr) +static int zd_dpdk_pbr_show_rules_walkcb(struct hash_bucket *bucket, void *arg) { + struct zebra_pbr_rule *rule = (struct zebra_pbr_rule *)bucket->data; + struct vty *vty = (struct vty *)arg; + struct vrf *vrf; + struct interface *ifp = NULL; + struct zebra_pbr_action *zaction = &rule->action; - /* XXX - place holder */ + zebra_pbr_show_rule_unit(rule, vty); + if (zaction->dp_flow_ptr) { + vrf = vrf_lookup_by_id(rule->vrf_id); + if (vrf) + ifp = if_lookup_by_name_vrf(rule->ifname, vrf); + + if (ifp) + zd_dpdk_flow_stat_show(vty, ifp->ifindex, + zaction->dp_flow_ptr); + } + return HASHWALK_CONTINUE; +} + + +void zd_dpdk_pbr_flows_show(struct vty *vty) +{ + hash_walk(zrouter.rules_hash, zd_dpdk_pbr_show_rules_walkcb, vty); +} + + +static void zd_dpdk_rule_add(struct zebra_dplane_ctx *ctx) +{ + static struct rte_flow_attr attrs = {.ingress = 1, .transfer = 1}; + uint32_t filter_bm = dplane_ctx_rule_get_filter_bm(ctx); + int in_ifindex = dplane_ctx_get_ifindex(ctx); + int out_ifindex = dplane_ctx_rule_get_out_ifindex(ctx); + struct rte_flow_item_eth eth, eth_mask; + struct rte_flow_item_ipv4 ip, ip_mask; + struct rte_flow_item_udp udp, udp_mask; + struct rte_flow_action_count conf_count; + struct rte_flow_action_set_mac conf_smac, conf_dmac; + struct rte_flow_action_port_id conf_port; + struct rte_flow_item items[ZD_PBR_PATTERN_MAX]; + struct rte_flow_action actions[ZD_PBR_ACTION_MAX]; + int item_cnt = 0; + int act_cnt = 0; + struct in_addr tmp_mask; + const struct ethaddr *mac; + struct rte_flow *flow; + struct rte_flow_error error; + struct zd_dpdk_port *in_dport; + struct zd_dpdk_port *out_dport; + uint32_t pri = dplane_ctx_rule_get_priority(ctx); + int seq = dplane_ctx_rule_get_seq(ctx); + int unique = dplane_ctx_rule_get_unique(ctx); + + if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) + zlog_debug( + "PBR dpdk flow create ifname %s seq %d pri %u unique %d\n", + dplane_ctx_rule_get_ifname(ctx), seq, pri, unique); + in_dport = zd_dpdk_port_find_by_index(in_ifindex); + if (!in_dport) { + if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) + zlog_debug( + "PBR dpdk flow create ifname %s seq %d pri %u unique %d failed; in_port %d missing\n", + dplane_ctx_rule_get_ifname(ctx), seq, pri, + unique, in_ifindex); + return; + } + + out_dport = zd_dpdk_port_find_by_index(out_ifindex); + if (!out_dport) { + if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) + zlog_debug( + "PBR dpdk flow create ifname %s seq %d pri %u unique %d failed; out_port %d missing\n", + dplane_ctx_rule_get_ifname(ctx), seq, pri, + unique, out_ifindex); + return; + } + + /*********************** match items **************************/ + memset(ð, 0, sizeof(eth)); + memset(ð_mask, 0, sizeof(eth_mask)); + eth.type = eth_mask.type = htons(RTE_ETHER_TYPE_IPV4); + items[item_cnt].type = RTE_FLOW_ITEM_TYPE_ETH; + items[item_cnt].spec = ð + items[item_cnt].mask = ð_mask; + items[item_cnt].last = NULL; + ++item_cnt; + + memset(&ip, 0, sizeof(ip)); + memset(&ip_mask, 0, sizeof(ip_mask)); + if (filter_bm & PBR_FILTER_SRC_IP) { + const struct prefix *src_ip; + + src_ip = dplane_ctx_rule_get_src_ip(ctx); + ip.hdr.src_addr = src_ip->u.prefix4.s_addr; + masklen2ip(src_ip->prefixlen, &tmp_mask); + ip_mask.hdr.src_addr = tmp_mask.s_addr; + } + if (filter_bm & PBR_FILTER_DST_IP) { + const struct prefix *dst_ip; + + dst_ip = dplane_ctx_rule_get_dst_ip(ctx); + ip.hdr.dst_addr = dst_ip->u.prefix4.s_addr; + masklen2ip(dst_ip->prefixlen, &tmp_mask); + ip_mask.hdr.dst_addr = tmp_mask.s_addr; + } + if (filter_bm & PBR_FILTER_IP_PROTOCOL) { + ip.hdr.next_proto_id = dplane_ctx_rule_get_ipproto(ctx); + ip_mask.hdr.next_proto_id = UINT8_MAX; + } + items[item_cnt].type = RTE_FLOW_ITEM_TYPE_IPV4; + items[item_cnt].spec = &ip; + items[item_cnt].mask = &ip_mask; + items[item_cnt].last = NULL; + ++item_cnt; + + if ((filter_bm & (PBR_FILTER_SRC_PORT | PBR_FILTER_DST_PORT))) { + memset(&udp, 0, sizeof(udp)); + memset(&udp_mask, 0, sizeof(udp_mask)); + if (filter_bm & PBR_FILTER_SRC_PORT) { + udp.hdr.src_port = + RTE_BE16(dplane_ctx_rule_get_src_port(ctx)); + udp_mask.hdr.src_port = UINT16_MAX; + } + if (filter_bm & PBR_FILTER_DST_PORT) { + udp.hdr.dst_port = + RTE_BE16(dplane_ctx_rule_get_dst_port(ctx)); + udp_mask.hdr.dst_port = UINT16_MAX; + } + items[item_cnt].type = RTE_FLOW_ITEM_TYPE_UDP; + items[item_cnt].spec = &udp; + items[item_cnt].mask = &udp_mask; + items[item_cnt].last = NULL; + ++item_cnt; + } + + items[item_cnt].type = RTE_FLOW_ITEM_TYPE_END; + + /*************************** actions *****************************/ + actions[act_cnt].type = RTE_FLOW_ACTION_TYPE_COUNT; + memset(&conf_count, 0, sizeof(conf_count)); + actions[act_cnt].conf = &conf_count; + ++act_cnt; + + actions[act_cnt].type = RTE_FLOW_ACTION_TYPE_DEC_TTL; + ++act_cnt; + + mac = dplane_ctx_rule_get_smac(ctx); + memcpy(conf_smac.mac_addr, mac, RTE_ETHER_ADDR_LEN); + actions[act_cnt].type = RTE_FLOW_ACTION_TYPE_SET_MAC_SRC; + actions[act_cnt].conf = &conf_smac; + ++act_cnt; + + mac = dplane_ctx_rule_get_dmac(ctx); + memcpy(conf_dmac.mac_addr, mac, RTE_ETHER_ADDR_LEN); + actions[act_cnt].type = RTE_FLOW_ACTION_TYPE_SET_MAC_DST; + actions[act_cnt].conf = &conf_dmac; + ++act_cnt; + + memset(&conf_port, 0, sizeof(conf_port)); + conf_port.id = out_dport->port_id; + actions[act_cnt].type = RTE_FLOW_ACTION_TYPE_PORT_ID; + actions[act_cnt].conf = &conf_port; + ++act_cnt; + + actions[act_cnt].type = RTE_FLOW_ACTION_TYPE_END; + + frr_with_privs (&zserv_privs) { + flow = rte_flow_create(in_dport->port_id, &attrs, items, + actions, &error); + } + + if (flow) { + dplane_ctx_rule_set_dp_flow_ptr(ctx, (intptr_t)flow); + if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) + zlog_debug( + "PBR dpdk flow 0x%" PRIxPTR + " created ifname %s seq %d pri %u unique %d\n", + (intptr_t)flow, dplane_ctx_rule_get_ifname(ctx), + seq, pri, unique); + } else { + zlog_warn( + "PBR dpdk flow create failed ifname %s seq %d pri %u unique %d; rc %d\n", + dplane_ctx_rule_get_ifname(ctx), seq, pri, unique, + error.type); + } +} + + +static void zd_dpdk_rule_del(struct zebra_dplane_ctx *ctx, const char *ifname, + int in_ifindex, intptr_t dp_flow_ptr) +{ + struct zd_dpdk_port *in_dport; + struct rte_flow_error error; + int rc; + + if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) + zlog_debug( + "PBR dpdk flow delete ifname %s ifindex %d dp_flow 0x%" PRIxPTR + "\n", + ifname, in_ifindex, dp_flow_ptr); + + if (!dp_flow_ptr) { + if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) + zlog_debug( + "PBR dpdk flow delete failed; ifname %s ifindex %d dp_flow 0x%" PRIxPTR + "; empty dp\n", + ifname, in_ifindex, dp_flow_ptr); + return; + } + + dplane_ctx_rule_set_dp_flow_ptr(ctx, (intptr_t)NULL); + in_dport = zd_dpdk_port_find_by_index(in_ifindex); + if (!in_dport) { + if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) + zlog_debug( + "PBR dpdk flow delete failed; ifname %s ifindex %d dp_flow 0x%" PRIxPTR + " in port missing\n", + ifname, in_ifindex, dp_flow_ptr); + return; + } + + frr_with_privs (&zserv_privs) { + rc = rte_flow_destroy(in_dport->port_id, + (struct rte_flow *)dp_flow_ptr, &error); + } + + if (rc) + zlog_warn( + "PBR dpdk flow delete failed; ifname %s ifindex %d dp_flow 0x%" PRIxPTR + "\n", + ifname, in_ifindex, dp_flow_ptr); } @@ -84,9 +344,9 @@ static void zd_dpdk_rule_update(struct zebra_dplane_ctx *ctx) int in_ifindex; intptr_t dp_flow_ptr; - if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) { + if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) zlog_debug("Dplane %s", dplane_op2str(dplane_ctx_get_op(ctx))); - } + op = dplane_ctx_get_op(ctx); switch (op) { @@ -102,8 +362,8 @@ static void zd_dpdk_rule_update(struct zebra_dplane_ctx *ctx) memory_order_relaxed); in_ifindex = dplane_ctx_get_ifindex(ctx); dp_flow_ptr = dplane_ctx_rule_get_old_dp_flow_ptr(ctx); - zd_dpdk_rule_del(dplane_ctx_rule_get_ifname(ctx), in_ifindex, - dp_flow_ptr); + zd_dpdk_rule_del(ctx, dplane_ctx_rule_get_ifname(ctx), + in_ifindex, dp_flow_ptr); zd_dpdk_rule_add(ctx); break; @@ -112,11 +372,54 @@ static void zd_dpdk_rule_update(struct zebra_dplane_ctx *ctx) memory_order_relaxed); in_ifindex = dplane_ctx_get_ifindex(ctx); dp_flow_ptr = dplane_ctx_rule_get_dp_flow_ptr(ctx); - zd_dpdk_rule_del(dplane_ctx_rule_get_ifname(ctx), in_ifindex, - dp_flow_ptr); + zd_dpdk_rule_del(ctx, dplane_ctx_rule_get_ifname(ctx), + in_ifindex, dp_flow_ptr); break; - default:; + case DPLANE_OP_NONE: + case DPLANE_OP_ROUTE_INSTALL: + case DPLANE_OP_ROUTE_UPDATE: + case DPLANE_OP_ROUTE_DELETE: + case DPLANE_OP_ROUTE_NOTIFY: + case DPLANE_OP_NH_INSTALL: + case DPLANE_OP_NH_UPDATE: + case DPLANE_OP_NH_DELETE: + case DPLANE_OP_LSP_INSTALL: + case DPLANE_OP_LSP_UPDATE: + case DPLANE_OP_LSP_DELETE: + case DPLANE_OP_LSP_NOTIFY: + case DPLANE_OP_PW_INSTALL: + case DPLANE_OP_PW_UNINSTALL: + case DPLANE_OP_SYS_ROUTE_ADD: + case DPLANE_OP_SYS_ROUTE_DELETE: + case DPLANE_OP_ADDR_INSTALL: + case DPLANE_OP_ADDR_UNINSTALL: + case DPLANE_OP_MAC_INSTALL: + case DPLANE_OP_MAC_DELETE: + case DPLANE_OP_NEIGH_INSTALL: + case DPLANE_OP_NEIGH_UPDATE: + case DPLANE_OP_NEIGH_DELETE: + case DPLANE_OP_VTEP_ADD: + case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_NEIGH_DISCOVER: + case DPLANE_OP_BR_PORT_UPDATE: + case DPLANE_OP_IPTABLE_ADD: + case DPLANE_OP_IPTABLE_DELETE: + case DPLANE_OP_IPSET_ADD: + case DPLANE_OP_IPSET_DELETE: + case DPLANE_OP_IPSET_ENTRY_ADD: + case DPLANE_OP_IPSET_ENTRY_DELETE: + case DPLANE_OP_NEIGH_IP_INSTALL: + case DPLANE_OP_NEIGH_IP_DELETE: + case DPLANE_OP_NEIGH_TABLE_UPDATE: + case DPLANE_OP_GRE_SET: + case DPLANE_OP_INTF_ADDR_ADD: + case DPLANE_OP_INTF_ADDR_DEL: + case DPLANE_OP_INTF_NETCONFIG: + case DPLANE_OP_INTF_INSTALL: + case DPLANE_OP_INTF_UPDATE: + case DPLANE_OP_INTF_DELETE: + break; } } @@ -132,8 +435,49 @@ static void zd_dpdk_process_update(struct zebra_dplane_ctx *ctx) case DPLANE_OP_RULE_DELETE: zd_dpdk_rule_update(ctx); break; - - default: + case DPLANE_OP_NONE: + case DPLANE_OP_ROUTE_INSTALL: + case DPLANE_OP_ROUTE_UPDATE: + case DPLANE_OP_ROUTE_DELETE: + case DPLANE_OP_ROUTE_NOTIFY: + case DPLANE_OP_NH_INSTALL: + case DPLANE_OP_NH_UPDATE: + case DPLANE_OP_NH_DELETE: + case DPLANE_OP_LSP_INSTALL: + case DPLANE_OP_LSP_UPDATE: + case DPLANE_OP_LSP_DELETE: + case DPLANE_OP_LSP_NOTIFY: + case DPLANE_OP_PW_INSTALL: + case DPLANE_OP_PW_UNINSTALL: + case DPLANE_OP_SYS_ROUTE_ADD: + case DPLANE_OP_SYS_ROUTE_DELETE: + case DPLANE_OP_ADDR_INSTALL: + case DPLANE_OP_ADDR_UNINSTALL: + case DPLANE_OP_MAC_INSTALL: + case DPLANE_OP_MAC_DELETE: + case DPLANE_OP_NEIGH_INSTALL: + case DPLANE_OP_NEIGH_UPDATE: + case DPLANE_OP_NEIGH_DELETE: + case DPLANE_OP_VTEP_ADD: + case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_NEIGH_DISCOVER: + case DPLANE_OP_BR_PORT_UPDATE: + case DPLANE_OP_IPTABLE_ADD: + case DPLANE_OP_IPTABLE_DELETE: + case DPLANE_OP_IPSET_ADD: + case DPLANE_OP_IPSET_DELETE: + case DPLANE_OP_IPSET_ENTRY_ADD: + case DPLANE_OP_IPSET_ENTRY_DELETE: + case DPLANE_OP_NEIGH_IP_INSTALL: + case DPLANE_OP_NEIGH_IP_DELETE: + case DPLANE_OP_NEIGH_TABLE_UPDATE: + case DPLANE_OP_GRE_SET: + case DPLANE_OP_INTF_ADDR_ADD: + case DPLANE_OP_INTF_ADDR_DEL: + case DPLANE_OP_INTF_NETCONFIG: + case DPLANE_OP_INTF_INSTALL: + case DPLANE_OP_INTF_UPDATE: + case DPLANE_OP_INTF_DELETE: atomic_fetch_add_explicit(&dpdk_stat->ignored_updates, 1, memory_order_relaxed); @@ -308,12 +652,13 @@ static void zd_dpdk_port_init(void) static int zd_dpdk_init(void) { int rc; - char *argv[] = {(char *)"/usr/lib/frr/zebra", (char *)"--"}; + static const char *argv[] = {(char *)"/usr/lib/frr/zebra", + (char *)"--"}; zd_dpdk_vty_init(); frr_with_privs (&zserv_privs) { - rc = rte_eal_init(sizeof(argv) / sizeof(argv[0]), argv); + rc = rte_eal_init(ARRAY_SIZE(argv), argv); } if (rc < 0) { zlog_warn("EAL init failed %s", rte_strerror(rte_errno)); @@ -385,4 +730,4 @@ static int zd_dpdk_module_init(void) FRR_MODULE_SETUP(.name = "dplane_dpdk", .version = "0.0.1", .description = "Data plane plugin using dpdk for hw offload", - .init = zd_dpdk_module_init, ); + .init = zd_dpdk_module_init); diff --git a/zebra/dpdk/zebra_dplane_dpdk.h b/zebra/dpdk/zebra_dplane_dpdk.h index 32ace04078..40f9263ca1 100644 --- a/zebra/dpdk/zebra_dplane_dpdk.h +++ b/zebra/dpdk/zebra_dplane_dpdk.h @@ -27,6 +27,7 @@ #define ZD_DPDK_INVALID_PORT 0xffff +extern void zd_dpdk_pbr_flows_show(struct vty *vty); extern void zd_dpdk_port_show(struct vty *vty, uint16_t port_id, bool uj, int detail); extern void zd_dpdk_stat_show(struct vty *vty); diff --git a/zebra/dpdk/zebra_dplane_dpdk_private.h b/zebra/dpdk/zebra_dplane_dpdk_private.h index 0eda45b2e3..b8483f4918 100644 --- a/zebra/dpdk/zebra_dplane_dpdk_private.h +++ b/zebra/dpdk/zebra_dplane_dpdk_private.h @@ -28,6 +28,13 @@ #include "zebra_dplane_dpdk.h" +/* match on eth, sip, dip, udp */ +#define ZD_PBR_PATTERN_MAX 6 +/* dec_ttl, set_smac, set_dmac, * phy_port, count + */ +#define ZD_PBR_ACTION_MAX 6 + +#define ZD_ETH_TYPE_IP 0x800 struct zd_dpdk_port { uint16_t port_id; /* dpdk port_id */ diff --git a/zebra/dpdk/zebra_dplane_dpdk_vty.c b/zebra/dpdk/zebra_dplane_dpdk_vty.c index 11df697485..748bce9e34 100644 --- a/zebra/dpdk/zebra_dplane_dpdk_vty.c +++ b/zebra/dpdk/zebra_dplane_dpdk_vty.c @@ -62,8 +62,24 @@ DEFPY (zd_dpdk_show_ports, } +DEFPY (zd_dpdk_show_pbr_flows, + zd_dpdk_show_pbr_flows_cmd, + "show dplane dpdk pbr flows", + SHOW_STR + ZD_STR + ZD_DPDK_STR + "show pbr info\n" + "DPDK flows\n") +{ + zd_dpdk_pbr_flows_show(vty); + + return CMD_SUCCESS; +} + + void zd_dpdk_vty_init(void) { install_element(VIEW_NODE, &zd_dpdk_show_counters_cmd); install_element(VIEW_NODE, &zd_dpdk_show_ports_cmd); + install_element(VIEW_NODE, &zd_dpdk_show_pbr_flows_cmd); } From 3c6732e43dca013ebcd1875c5e98be8c371a16a0 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah Date: Thu, 30 Dec 2021 16:15:46 -0800 Subject: [PATCH 12/12] zebra: add documentation for the DPDK dataplane plugin Signed-off-by: Anuradha Karuppiah --- doc/user/zebra.rst | 53 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index 77c7f87aa1..d96df0ce52 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -1140,6 +1140,59 @@ order to off-load work from the main zebra pthread. waiting to be processed by the dataplane pthread. +DPDK dataplane +============== + +The zebra DPDK subsystem programs the dataplane via rte_XXX APIs. +This module needs be compiled in via "--enable-dp-dpdk=yes" +and enabled at start up time via the zebra daemon option "-M dplane_dpdk". + +To program the PBR rules as rte_flows you additionally need to configure +"pbr nexthop-resolve". This is used to expland the PBR actions into the +{SMAC, DMAC, outgoing port} needed by rte_flow. + + +.. clicmd:: show dplane dpdk port [detail] + + Displays the mapping table between zebra interfaces and DPDK port-ids. + Sample output: + + :: + Port Device IfName IfIndex sw,domain,port + + 0 0000:03:00.0 p0 4 0000:03:00.0,0,65535 + 1 0000:03:00.0 pf0hpf 6 0000:03:00.0,0,4095 + 2 0000:03:00.0 pf0vf0 15 0000:03:00.0,0,4096 + 3 0000:03:00.0 pf0vf1 16 0000:03:00.0,0,4097 + 4 0000:03:00.1 p1 5 0000:03:00.1,1,65535 + 5 0000:03:00.1 pf1hpf 7 0000:03:00.1,1,20479 + +.. clicmd:: show dplane dpdk pbr flows + Displays the DPDK stats per-PBR entry. + Sample output: + + :: + Rules if pf0vf0 + Seq 1 pri 300 + SRC Match 77.0.0.8/32 + DST Match 88.0.0.8/32 + Tableid: 10000 + Action: nh: 45.0.0.250 intf: p0 + Action: mac: 00:00:5e:00:01:fa + DPDK flow: installed 0x40 + DPDK flow stats: packets 13 bytes 1586 + +.. clicmd:: show dplane dpdk counters + Displays the ZAPI message handler counters + + Sample output: + + :: + Ignored updates: 0 + PBR rule adds: 1 + PBR rule dels: 0 + + zebra Terminal Mode Commands ============================