From cd233079064f4b0fa8eacaf60d9fbd454ad47e30 Mon Sep 17 00:00:00 2001 From: Chirag Shah Date: Wed, 21 Jun 2017 16:37:51 -0700 Subject: [PATCH] zebra: Enable JSON for EVPN show cmds Ticket: CM-16959 Reviewed By: CCR-6401 Testing Done: Execute 'show evpn .... json' form of outputs with local/remote vteps Signed-off-by: Chirag Shah --- zebra/zebra_vty.c | 92 ++++-- zebra/zebra_vxlan.c | 609 +++++++++++++++++++++++++++++------- zebra/zebra_vxlan.h | 29 +- zebra/zebra_vxlan_private.h | 12 +- 4 files changed, 580 insertions(+), 162 deletions(-) diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 13c4429d6..61fe171cf 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -2300,89 +2300,100 @@ DEFUN (show_vrf, DEFUN (show_evpn_vni, show_evpn_vni_cmd, - "show evpn vni", + "show evpn vni [json]", SHOW_STR "EVPN\n" - "VxLAN information\n") + "VxLAN information\n" + "JavaScript Object Notation\n") { struct zebra_vrf *zvrf; + u_char uj = use_json(argc, argv); zvrf = vrf_info_lookup(VRF_DEFAULT); - zebra_vxlan_print_vnis(vty, zvrf); + zebra_vxlan_print_vnis(vty, zvrf, uj); return CMD_SUCCESS; } DEFUN (show_evpn_vni_vni, show_evpn_vni_vni_cmd, - "show evpn vni " CMD_VNI_RANGE, + "show evpn vni " CMD_VNI_RANGE "[json]", SHOW_STR "EVPN\n" "VxLAN Network Identifier\n" - "VNI number\n") + "VNI number\n" + "JavaScript Object Notation\n") { struct zebra_vrf *zvrf; vni_t vni; + u_char uj = use_json(argc, argv); vni = strtoul(argv[3]->arg, NULL, 10); zvrf = vrf_info_lookup(VRF_DEFAULT); - zebra_vxlan_print_vni(vty, zvrf, vni); + zebra_vxlan_print_vni(vty, zvrf, vni, uj); return CMD_SUCCESS; } DEFUN (show_evpn_mac_vni, show_evpn_mac_vni_cmd, - "show evpn mac vni " CMD_VNI_RANGE, + "show evpn mac vni " CMD_VNI_RANGE "[json]", SHOW_STR "EVPN\n" "MAC addresses\n" "VxLAN Network Identifier\n" - "VNI number\n") + "VNI number\n" + "JavaScript Object Notation\n") { struct zebra_vrf *zvrf; vni_t vni; + u_char uj = use_json(argc, argv); vni = strtoul(argv[4]->arg, NULL, 10); zvrf = vrf_info_lookup(VRF_DEFAULT); - zebra_vxlan_print_macs_vni(vty, zvrf, vni); + zebra_vxlan_print_macs_vni(vty, zvrf, vni, uj); return CMD_SUCCESS; } DEFUN (show_evpn_mac_vni_all, show_evpn_mac_vni_all_cmd, - "show evpn mac vni all", + "show evpn mac vni all [json]", SHOW_STR "EVPN\n" "MAC addresses\n" "VxLAN Network Identifier\n" - "All VNIs\n") + "All VNIs\n" + "JavaScript Object Notation\n") { struct zebra_vrf *zvrf; + u_char uj = use_json(argc, argv); zvrf = vrf_info_lookup(VRF_DEFAULT); - zebra_vxlan_print_macs_all_vni(vty, zvrf); + zebra_vxlan_print_macs_all_vni(vty, zvrf, uj); return CMD_SUCCESS; } DEFUN (show_evpn_mac_vni_all_vtep, show_evpn_mac_vni_all_vtep_cmd, - "show evpn mac vni all vtep A.B.C.D", + "show evpn mac vni all vtep A.B.C.D [json]", SHOW_STR "EVPN\n" "MAC addresses\n" "VxLAN Network Identifier\n" "All VNIs\n" "Remote VTEP\n" - "Remote VTEP IP address\n") + "Remote VTEP IP address\n" + "JavaScript Object Notation\n") { struct zebra_vrf *zvrf; struct in_addr vtep_ip; + u_char uj = use_json(argc, argv); if (!inet_aton(argv[6]->arg, &vtep_ip)) { - vty_out(vty, "%% Malformed VTEP IP address\n"); + if (!uj) + vty_out(vty, "%% Malformed VTEP IP address\n"); return CMD_WARNING; } zvrf = vrf_info_lookup(VRF_DEFAULT); - zebra_vxlan_print_macs_all_vni_vtep(vty, zvrf, vtep_ip); + zebra_vxlan_print_macs_all_vni_vtep(vty, zvrf, vtep_ip, uj); return CMD_SUCCESS; } @@ -2415,112 +2426,125 @@ DEFUN (show_evpn_mac_vni_mac, DEFUN (show_evpn_mac_vni_vtep, show_evpn_mac_vni_vtep_cmd, - "show evpn mac vni " CMD_VNI_RANGE " vtep A.B.C.D", + "show evpn mac vni " CMD_VNI_RANGE " vtep A.B.C.D" "[json]", SHOW_STR "EVPN\n" "MAC addresses\n" "VxLAN Network Identifier\n" "VNI number\n" "Remote VTEP\n" - "Remote VTEP IP address\n") + "Remote VTEP IP address\n" + "JavaScript Object Notation\n") { struct zebra_vrf *zvrf; vni_t vni; struct in_addr vtep_ip; + u_char uj = use_json(argc, argv); vni = strtoul(argv[4]->arg, NULL, 10); if (!inet_aton(argv[6]->arg, &vtep_ip)) { - vty_out(vty, "%% Malformed VTEP IP address\n"); + if (!uj) + vty_out(vty, "%% Malformed VTEP IP address\n"); return CMD_WARNING; } zvrf = vrf_info_lookup(VRF_DEFAULT); - zebra_vxlan_print_macs_vni_vtep(vty, zvrf, vni, vtep_ip); + zebra_vxlan_print_macs_vni_vtep(vty, zvrf, vni, vtep_ip, uj); return CMD_SUCCESS; } DEFUN (show_evpn_neigh_vni, show_evpn_neigh_vni_cmd, - "show evpn arp-cache vni " CMD_VNI_RANGE, + "show evpn arp-cache vni " CMD_VNI_RANGE "[json]", SHOW_STR "EVPN\n" "ARP and ND cache\n" "VxLAN Network Identifier\n" - "VNI number\n") + "VNI number\n" + "JavaScript Object Notation\n") { struct zebra_vrf *zvrf; vni_t vni; + u_char uj = use_json(argc, argv); vni = strtoul(argv[4]->arg, NULL, 10); zvrf = vrf_info_lookup(VRF_DEFAULT); - zebra_vxlan_print_neigh_vni(vty, zvrf, vni); + zebra_vxlan_print_neigh_vni(vty, zvrf, vni, uj); return CMD_SUCCESS; } DEFUN (show_evpn_neigh_vni_all, show_evpn_neigh_vni_all_cmd, - "show evpn arp-cache vni all", + "show evpn arp-cache vni all [json]", SHOW_STR "EVPN\n" "ARP and ND cache\n" "VxLAN Network Identifier\n" - "All VNIs\n") + "All VNIs\n" + "JavaScript Object Notation\n") { struct zebra_vrf *zvrf; + u_char uj = use_json(argc, argv); zvrf = vrf_info_lookup(VRF_DEFAULT); - zebra_vxlan_print_neigh_all_vni(vty, zvrf); + zebra_vxlan_print_neigh_all_vni(vty, zvrf, uj); return CMD_SUCCESS; } DEFUN (show_evpn_neigh_vni_neigh, show_evpn_neigh_vni_neigh_cmd, - "show evpn arp-cache vni " CMD_VNI_RANGE " ip WORD", + "show evpn arp-cache vni " CMD_VNI_RANGE " ip WORD [json]", SHOW_STR "EVPN\n" "ARP and ND cache\n" "VxLAN Network Identifier\n" "VNI number\n" "Neighbor\n" - "Neighbor address (IPv4 or IPv6 address)\n") + "Neighbor address (IPv4 or IPv6 address)\n" + "JavaScript Object Notation\n") { struct zebra_vrf *zvrf; vni_t vni; struct ipaddr ip; + u_char uj = use_json(argc, argv); vni = strtoul(argv[4]->arg, NULL, 10); if (str2ipaddr(argv[6]->arg, &ip) != 0) { - vty_out(vty, "%% Malformed Neighbor address\n"); + if (!uj) + vty_out(vty, "%% Malformed Neighbor address\n"); return CMD_WARNING; } zvrf = vrf_info_lookup(VRF_DEFAULT); - zebra_vxlan_print_specific_neigh_vni(vty, zvrf, vni, &ip); + zebra_vxlan_print_specific_neigh_vni(vty, zvrf, vni, &ip, uj); return CMD_SUCCESS; } DEFUN (show_evpn_neigh_vni_vtep, show_evpn_neigh_vni_vtep_cmd, - "show evpn arp-cache vni " CMD_VNI_RANGE " vtep A.B.C.D", + "show evpn arp-cache vni " CMD_VNI_RANGE " vtep A.B.C.D [json]", SHOW_STR "EVPN\n" "ARP and ND cache\n" "VxLAN Network Identifier\n" "VNI number\n" "Remote VTEP\n" - "Remote VTEP IP address\n") + "Remote VTEP IP address\n" + "JavaScript Object Notation\n") { struct zebra_vrf *zvrf; vni_t vni; struct in_addr vtep_ip; + u_char uj = use_json(argc, argv); vni = strtoul(argv[4]->arg, NULL, 10); if (!inet_aton(argv[6]->arg, &vtep_ip)) { - vty_out(vty, "%% Malformed VTEP IP address\n"); + if (!uj) + vty_out(vty, "%% Malformed VTEP IP address\n"); return CMD_WARNING; } zvrf = vrf_info_lookup(VRF_DEFAULT); - zebra_vxlan_print_neigh_vni_vtep(vty, zvrf, vni, vtep_ip); + zebra_vxlan_print_neigh_vni_vtep(vty, zvrf, vni, vtep_ip, uj); return CMD_SUCCESS; } diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index dc8e6e9c0..cb555ff1e 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -46,6 +46,7 @@ #include "zebra/zebra_vxlan.h" #include "zebra/zebra_memory.h" #include "zebra/zebra_l2.h" +#include "lib/json.h" DEFINE_MTYPE_STATIC(ZEBRA, ZVNI, "VNI hash"); DEFINE_MTYPE_STATIC(ZEBRA, ZVNI_VTEP, "VNI remote VTEP"); @@ -56,15 +57,15 @@ DEFINE_MTYPE_STATIC(ZEBRA, NEIGH, "VNI Neighbor"); /* static function declarations */ -static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt); +static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json); static void zvni_print_neigh_hash(struct hash_backet *backet, void *ctxt); static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet, - void *ctxt); + void **args); static void zvni_print_mac(zebra_mac_t *mac, void *ctxt); static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt); static void zvni_print_mac_hash_all_vni(struct hash_backet *backet, void *ctxt); -static void zvni_print(zebra_vni_t *zvni, void *ctxt); -static void zvni_print_hash(struct hash_backet *backet, void *ctxt); +static void zvni_print(zebra_vni_t *zvni, void **ctxt); +static void zvni_print_hash(struct hash_backet *backet, void *ctxt[]); static int zvni_macip_send_msg_to_client(struct zebra_vrf *zvrf, vni_t vni, struct ethaddr *macaddr, @@ -180,18 +181,34 @@ static void zvni_find_neigh_addr_width(struct hash_backet *backet, void *ctxt) /* * Print a specific neighbor entry. */ -static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt) +static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json) { struct vty *vty; char buf1[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; - ipaddr2str(&n->ip, buf2, sizeof(buf2)), vty = (struct vty *)ctxt; - vty_out(vty, "IP: %s\n", ipaddr2str(&n->ip, buf2, sizeof(buf2))); - vty_out(vty, " MAC: %s", prefix_mac2str(&n->emac, buf1, sizeof(buf1))); - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) - vty_out(vty, " Remote VTEP: %s", inet_ntoa(n->r_vtep_ip)); - vty_out(vty, "\n"); + ipaddr2str(&n->ip, buf2, sizeof(buf2)); + prefix_mac2str(&n->emac, buf1, sizeof(buf1)); + vty = (struct vty *)ctxt; + if (json == NULL) { + vty_out(vty, "IP: %s\n", + ipaddr2str(&n->ip, buf2, sizeof(buf2))); + vty_out(vty, " MAC: %s", + prefix_mac2str(&n->emac, buf1, sizeof(buf1))); + } else { + json_object_string_add(json, "ip", buf2); + json_object_string_add(json, "mac", buf1); + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + if (json == NULL) + vty_out(vty, " Remote VTEP: %s", + inet_ntoa(n->r_vtep_ip)); + else + json_object_string_add(json, "remoteVtep", + inet_ntoa(n->r_vtep_ip)); + } + if (json == NULL) + vty_out(vty, "\n"); } /* @@ -200,65 +217,115 @@ static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt) static void zvni_print_neigh_hash(struct hash_backet *backet, void *ctxt) { struct vty *vty; + json_object *json_vni = NULL, *json_row = NULL; zebra_neigh_t *n; char buf1[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; struct neigh_walk_ctx *wctx = ctxt; vty = wctx->vty; + json_vni = wctx->json; n = (zebra_neigh_t *)backet->data; if (!n) return; + if (json_vni) + json_row = json_object_new_object(); + prefix_mac2str(&n->emac, buf1, sizeof(buf1)); ipaddr2str(&n->ip, buf2, sizeof(buf2)); if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) && !(wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP)) { - vty_out(vty, "%*s %-6s %-17s\n", -wctx->addr_width, buf2, - "local", buf1); + if (json_vni == NULL) { + vty_out(vty, "%*s %-6s %-17s\n", -wctx->addr_width, + buf2, "local", buf1); + } else { + json_object_string_add(json_row, "type", "local"); + json_object_string_add(json_row, "mac", buf1); + } wctx->count++; } else { if (wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) { if (IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip)) { - if (wctx->count == 0) + if (json_vni == NULL) { + if (wctx->count == 0) + vty_out(vty, + "%*s %-6s %-17s %-21s\n", + -wctx->addr_width, + "Neighbor", "Type", + "MAC", "Remote VTEP"); vty_out(vty, "%*s %-6s %-17s %-21s\n", - -wctx->addr_width, "Neighbor", - "Type", "MAC", "Remote VTEP"); + -wctx->addr_width, buf2, + "remote", buf1, + inet_ntoa(n->r_vtep_ip)); + } else { + json_object_string_add(json_row, "type", + "remote"); + json_object_string_add(json_row, "mac", + buf1); + json_object_string_add( + json_row, "remoteVtep", + inet_ntoa(n->r_vtep_ip)); + } + wctx->count++; + } + } else { + if (json_vni == NULL) { vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx->addr_width, buf2, "remote", buf1, inet_ntoa(n->r_vtep_ip)); - wctx->count++; + } else { + json_object_string_add(json_row, "type", + "remote"); + json_object_string_add(json_row, "mac", buf1); + json_object_string_add(json_row, "remoteVtep", + inet_ntoa(n->r_vtep_ip)); } - } else { - vty_out(vty, "%*s %-6s %-17s %-21s\n", - -wctx->addr_width, buf2, "remote", buf1, - inet_ntoa(n->r_vtep_ip)); wctx->count++; } } + + if (json_vni) + json_object_object_add(json_vni, buf2, json_row); } /* * Print neighbors for all VNI. */ static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet, - void *ctxt) + void **args) { struct vty *vty; + json_object *json = NULL, *json_vni = NULL; zebra_vni_t *zvni; u_int32_t num_neigh; struct neigh_walk_ctx wctx; + char vni_str[VNI_STR_LEN]; + + vty = (struct vty *)args[0]; + json = (json_object *)args[1]; - vty = (struct vty *)ctxt; zvni = (zebra_vni_t *)backet->data; - if (!zvni) + if (!zvni) { + if (json) + vty_out(vty, "{}\n"); return; - + } num_neigh = hashcount(zvni->neigh_table); - vty_out(vty, "\nVNI %u #ARP (IPv4 and IPv6, local and remote) %u\n\n", - zvni->vni, num_neigh); - if (!num_neigh) + if (json == NULL) + vty_out(vty, + "\nVNI %u #ARP (IPv4 and IPv6, local and remote) %u\n\n", + zvni->vni, num_neigh); + else { + json_vni = json_object_new_object(); + json_object_int_add(json_vni, "numArpNd", num_neigh); + snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni); + } + if (!num_neigh) { + if (json) + json_object_object_add(json, vni_str, json_vni); return; + } /* Since we have IPv6 addresses to deal with which can vary widely in * size, we try to be a bit more elegant in display by first computing @@ -268,11 +335,16 @@ static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet, wctx.zvni = zvni; wctx.vty = vty; wctx.addr_width = 15; + wctx.json = json_vni; hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); - vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx.addr_width, "IP", "Type", - "MAC", "Remote VTEP"); + if (json == NULL) + vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx.addr_width, "IP", + "Type", "MAC", "Remote VTEP"); hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx); + + if (json) + json_object_object_add(json, vni_str, json_vni); } /* @@ -313,16 +385,22 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt) static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt) { struct vty *vty; + json_object *json_mac_hdr = NULL, *json_mac = NULL; zebra_mac_t *mac; char buf1[20]; struct mac_walk_ctx *wctx = ctxt; vty = wctx->vty; + json_mac_hdr = wctx->json; mac = (zebra_mac_t *)backet->data; if (!mac) return; prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)); + + if (json_mac_hdr) + json_mac = json_object_new_object(); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) && !(wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP)) { struct zebra_ns *zns; @@ -336,29 +414,70 @@ static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt) if (!ifp) // unexpected return; vid = mac->fwd_info.local.vid; - vty_out(vty, "%-17s %-6s %-21s", buf1, "local", ifp->name); - if (vid) - vty_out(vty, " %-5u", vid); - vty_out(vty, "\n"); + if (json_mac_hdr == NULL) + vty_out(vty, "%-17s %-6s %-21s", buf1, "local", + ifp->name); + else { + json_object_string_add(json_mac, "type", "local"); + json_object_string_add(json_mac, "intf", ifp->name); + } + if (vid) { + if (json_mac_hdr == NULL) + vty_out(vty, " %-5u", vid); + else + json_object_int_add(json_mac, "vlan", vid); + } + if (json_mac_hdr == NULL) + vty_out(vty, "\n"); + else + json_object_object_add(json_mac_hdr, buf1, json_mac); wctx->count++; } else { if (wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) { if (IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &wctx->r_vtep_ip)) { if (wctx->count == 0) { - vty_out(vty, "\nVNI %u", - wctx->zvni->vni); - vty_out(vty, "%-17s %-6s %-21s %-5s", - "MAC", "Type", - "Intf/Remote VTEP", "VLAN"); + if (json_mac_hdr == NULL) { + vty_out(vty, "\nVNI %u\n\n", + wctx->zvni->vni); + vty_out(vty, + "%-17s %-6s %-21s %-5s\n", + "MAC", "Type", + "Intf/Remote VTEP", + "VLAN"); + } + } + if (json_mac_hdr == NULL) + vty_out(vty, "%-17s %-6s %-21s\n", buf1, + "remote", + inet_ntoa(mac->fwd_info + .r_vtep_ip)); + else { + json_object_string_add(json_mac, "type", + "remote"); + json_object_string_add( + json_mac, "remoteVtep", + inet_ntoa(mac->fwd_info + .r_vtep_ip)); + json_object_object_add(json_mac_hdr, + buf1, json_mac); } - vty_out(vty, "%-17s %-6s %-21s", buf1, "remote", - inet_ntoa(mac->fwd_info.r_vtep_ip)); wctx->count++; } } else { - vty_out(vty, "%-17s %-6s %-21s", buf1, "remote", - inet_ntoa(mac->fwd_info.r_vtep_ip)); + if (json_mac_hdr == NULL) + vty_out(vty, "%-17s %-6s %-21s\n", buf1, + "remote", + inet_ntoa(mac->fwd_info.r_vtep_ip)); + else { + json_object_string_add(json_mac, "type", + "remote"); + json_object_string_add( + json_mac, "remoteVtep", + inet_ntoa(mac->fwd_info.r_vtep_ip)); + json_object_object_add(json_mac_hdr, buf1, + json_mac); + } wctx->count++; } } @@ -370,15 +489,22 @@ static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt) static void zvni_print_mac_hash_all_vni(struct hash_backet *backet, void *ctxt) { struct vty *vty; + json_object *json = NULL, *json_vni = NULL; + json_object *json_mac = NULL; zebra_vni_t *zvni; u_int32_t num_macs; struct mac_walk_ctx *wctx = ctxt; + char vni_str[VNI_STR_LEN]; vty = (struct vty *)wctx->vty; + json = (struct json_object *)wctx->json; zvni = (zebra_vni_t *)backet->data; - if (!zvni) + if (!zvni) { + if (json) + vty_out(vty, "{}\n"); return; + } wctx->zvni = zvni; /*We are iterating over a new VNI, set the count to 0*/ @@ -387,59 +513,115 @@ static void zvni_print_mac_hash_all_vni(struct hash_backet *backet, void *ctxt) num_macs = hashcount(zvni->mac_table); if (!num_macs) return; - if (!CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP)) { - vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n", - zvni->vni, num_macs); - vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC", "Type", - "Intf/Remote VTEP", "VLAN"); + + if (json) { + json_vni = json_object_new_object(); + json_mac = json_object_new_object(); + snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni); } + if (!CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP)) { + if (json == NULL) { + vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n", + zvni->vni, num_macs); + vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC", "Type", + "Intf/Remote VTEP", "VLAN"); + } else + json_object_int_add(json_vni, "numMacs", num_macs); + } + /* assign per-vni to wctx->json object to fill macs + * under the vni. Re-assign primary json object to fill + * next vni information. + */ + wctx->json = json_mac; hash_iterate(zvni->mac_table, zvni_print_mac_hash, wctx); + wctx->json = json; + if (json) { + if (wctx->count) + json_object_object_add(json_vni, "macs", json_mac); + json_object_object_add(json, vni_str, json_vni); + } } /* * Print a specific VNI entry. */ -static void zvni_print(zebra_vni_t *zvni, void *ctxt) +static void zvni_print(zebra_vni_t *zvni, void **ctxt) { struct vty *vty; zebra_vtep_t *zvtep; u_int32_t num_macs; u_int32_t num_neigh; + json_object *json = NULL; + json_object *json_vtep_list = NULL; + json_object *json_ip_str = NULL; - vty = (struct vty *)ctxt; + vty = ctxt[0]; + json = ctxt[1]; + + if (json == NULL) + vty_out(vty, "VNI: %u\n", zvni->vni); + else + json_object_int_add(json, "vni", zvni->vni); - vty_out(vty, "VNI: %u\n", zvni->vni); if (!zvni->vxlan_if) { // unexpected - vty_out(vty, " VxLAN interface: unknown\n"); + if (json == NULL) + vty_out(vty, " VxLAN interface: unknown\n"); return; } - vty_out(vty, " VxLAN interface: %s ifIndex: %u VTEP IP: %s\n", - zvni->vxlan_if->name, zvni->vxlan_if->ifindex, - inet_ntoa(zvni->local_vtep_ip)); - + num_macs = hashcount(zvni->mac_table); + num_neigh = hashcount(zvni->neigh_table); + if (json == NULL) + vty_out(vty, " VxLAN interface: %s ifIndex: %u VTEP IP: %s\n", + zvni->vxlan_if->name, zvni->vxlan_if->ifindex, + inet_ntoa(zvni->local_vtep_ip)); + else { + json_object_string_add(json, "vxlanInterface", + zvni->vxlan_if->name); + json_object_int_add(json, "ifindex", zvni->vxlan_if->ifindex); + json_object_string_add(json, "vtepIp", + inet_ntoa(zvni->local_vtep_ip)); + json_object_int_add(json, "numMacs", num_macs); + json_object_int_add(json, "numArpNd", num_neigh); + } if (!zvni->vteps) { - vty_out(vty, " No remote VTEPs known for this VNI\n"); + if (json == NULL) + vty_out(vty, " No remote VTEPs known for this VNI\n"); } else { - vty_out(vty, " Remote VTEPs for this VNI:\n"); - for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) - vty_out(vty, " %s\n", inet_ntoa(zvtep->vtep_ip)); + if (json == NULL) + vty_out(vty, " Remote VTEPs for this VNI:\n"); + else + json_vtep_list = json_object_new_array(); + for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) { + if (json == NULL) + vty_out(vty, " %s\n", + inet_ntoa(zvtep->vtep_ip)); + else { + json_ip_str = json_object_new_string( + inet_ntoa(zvtep->vtep_ip)); + json_object_array_add(json_vtep_list, + json_ip_str); + } + } + if (json) + json_object_object_add(json, "numRemoteVteps", + json_vtep_list); + } + if (json == NULL) { + vty_out(vty, + " Number of MACs (local and remote) known for this VNI: %u\n", + num_macs); + vty_out(vty, + " Number of ARPs (IPv4 and IPv6, local and remote) " + "known for this VNI: %u\n", + num_neigh); } - num_macs = hashcount(zvni->mac_table); - vty_out(vty, - " Number of MACs (local and remote) known for this VNI: %u\n", - num_macs); - num_neigh = hashcount(zvni->neigh_table); - vty_out(vty, - " Number of ARPs (IPv4 and IPv6, local and remote) " - "known for this VNI: %u", - num_neigh); } /* * Print a VNI hash entry - called for display of all VNIs. */ -static void zvni_print_hash(struct hash_backet *backet, void *ctxt) +static void zvni_print_hash(struct hash_backet *backet, void *ctxt[]) { struct vty *vty; zebra_vni_t *zvni; @@ -447,8 +629,14 @@ static void zvni_print_hash(struct hash_backet *backet, void *ctxt) u_int32_t num_vteps = 0; u_int32_t num_macs = 0; u_int32_t num_neigh = 0; + json_object *json = NULL; + json_object *json_vni = NULL; + json_object *json_ip_str = NULL; + json_object *json_vtep_list = NULL; + + vty = ctxt[0]; + json = ctxt[1]; - vty = (struct vty *)ctxt; zvni = (zebra_vni_t *)backet->data; if (!zvni) return; @@ -461,9 +649,36 @@ static void zvni_print_hash(struct hash_backet *backet, void *ctxt) num_macs = hashcount(zvni->mac_table); num_neigh = hashcount(zvni->neigh_table); - vty_out(vty, "%-10u %-21s %-15s %-8u %-8u %-15u\n", zvni->vni, - zvni->vxlan_if ? zvni->vxlan_if->name : "unknown", - inet_ntoa(zvni->local_vtep_ip), num_macs, num_neigh, num_vteps); + if (json == NULL) + vty_out(vty, "%-10u %-21s %-15s %-8u %-8u %-15u\n", zvni->vni, + zvni->vxlan_if ? zvni->vxlan_if->name : "unknown", + inet_ntoa(zvni->local_vtep_ip), num_macs, num_neigh, + num_vteps); + else { + char vni_str[VNI_STR_LEN]; + snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni); + json_vni = json_object_new_object(); + json_object_string_add(json_vni, "vxlanIf", + zvni->vxlan_if ? zvni->vxlan_if->name + : "unknown"); + json_object_string_add(json_vni, "vtepIp", + inet_ntoa(zvni->local_vtep_ip)); + json_object_int_add(json_vni, "numMacs", num_macs); + json_object_int_add(json_vni, "numArpNd", num_neigh); + json_object_int_add(json_vni, "numRemoteVteps", num_vteps); + if (num_vteps) { + json_vtep_list = json_object_new_array(); + for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) { + json_ip_str = json_object_new_string( + inet_ntoa(zvtep->vtep_ip)); + json_object_array_add(json_vtep_list, + json_ip_str); + } + json_object_object_add(json_vni, "remoteVteps", + json_vtep_list); + } + json_object_object_add(json, vni_str, json_vni); + } } /* @@ -1943,23 +2158,30 @@ static void zvni_cleanup_all(struct hash_backet *backet, void *zvrf) * Display Neighbors for a VNI (VTY command handler). */ void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, - vni_t vni) + vni_t vni, u_char use_json) { zebra_vni_t *zvni; u_int32_t num_neigh; struct neigh_walk_ctx wctx; + json_object *json = NULL; if (!EVPN_ENABLED(zvrf)) return; zvni = zvni_lookup(zvrf, vni); if (!zvni) { - vty_out(vty, "%% VNI %u does not exist\n", vni); + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% VNI %u does not exist\n", vni); return; } num_neigh = hashcount(zvni->neigh_table); if (!num_neigh) return; + if (use_json) + json = json_object_new_object(); + /* Since we have IPv6 addresses to deal with which can vary widely in * size, we try to be a bit more elegant in display by first computing * the maximum width. @@ -1968,25 +2190,52 @@ void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, wctx.zvni = zvni; wctx.vty = vty; wctx.addr_width = 15; + wctx.json = json; hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); - vty_out(vty, - "Number of ARPs (local and remote) known for this VNI: %u\n", - num_neigh); - vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx.addr_width, "IP", "Type", - "MAC", "Remote VTEP"); + if (!use_json) { + vty_out(vty, + "Number of ARPs (local and remote) known for this VNI: %u\n", + num_neigh); + vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx.addr_width, "IP", + "Type", "MAC", "Remote VTEP"); + } else + json_object_int_add(json, "numArpNd", num_neigh); hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx); + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } } /* * Display neighbors across all VNIs (VTY command handler). */ -void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf) +void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf, + u_char use_json) { + json_object *json = NULL; + void *args[2]; + if (!EVPN_ENABLED(zvrf)) return; - hash_iterate(zvrf->vni_table, zvni_print_neigh_hash_all_vni, vty); + + if (use_json) + json = json_object_new_object(); + + args[0] = vty; + args[1] = json; + hash_iterate(zvrf->vni_table, + (void (*)(struct hash_backet *, + void *))zvni_print_neigh_hash_all_vni, + args); + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } } /* @@ -1994,26 +2243,40 @@ void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf) */ void zebra_vxlan_print_specific_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, - struct ipaddr *ip) + struct ipaddr *ip, u_char use_json) { zebra_vni_t *zvni; zebra_neigh_t *n; + json_object *json = NULL; if (!EVPN_ENABLED(zvrf)) return; zvni = zvni_lookup(zvrf, vni); if (!zvni) { - vty_out(vty, "%% VNI %u does not exist", vni); + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% VNI %u does not exist\n", vni); return; } n = zvni_neigh_lookup(zvni, ip); if (!n) { - vty_out(vty, "%% Requested neighbor does not exist in VNI %u\n", - vni); + if (!use_json) + vty_out(vty, + "%% Requested neighbor does not exist in VNI %u\n", + vni); return; } + if (use_json) + json = json_object_new_object(); + + zvni_print_neigh(n, vty, json); - zvni_print_neigh(n, vty); + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } } /* @@ -2021,17 +2284,22 @@ void zebra_vxlan_print_specific_neigh_vni(struct vty *vty, * By definition, these are remote neighbors. */ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, - vni_t vni, struct in_addr vtep_ip) + vni_t vni, struct in_addr vtep_ip, + u_char use_json) { zebra_vni_t *zvni; u_int32_t num_neigh; struct neigh_walk_ctx wctx; + json_object *json = NULL; if (!EVPN_ENABLED(zvrf)) return; zvni = zvni_lookup(zvrf, vni); if (!zvni) { - vty_out(vty, "%% VNI %u does not exist\n", vni); + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% VNI %u does not exist\n", vni); return; } num_neigh = hashcount(zvni->neigh_table); @@ -2043,56 +2311,98 @@ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, wctx.vty = vty; wctx.flags = SHOW_REMOTE_NEIGH_FROM_VTEP; wctx.r_vtep_ip = vtep_ip; - + wctx.json = json; hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } } /* * Display MACs for a VNI (VTY command handler). */ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, - vni_t vni) + vni_t vni, u_char use_json) { zebra_vni_t *zvni; u_int32_t num_macs; struct mac_walk_ctx wctx; + json_object *json = NULL; + json_object *json_mac = NULL; if (!EVPN_ENABLED(zvrf)) return; zvni = zvni_lookup(zvrf, vni); if (!zvni) { - vty_out(vty, "%% VNI %u does not exist\n", vni); + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% VNI %u does not exist\n", vni); return; } num_macs = hashcount(zvni->mac_table); if (!num_macs) return; + if (use_json) { + json = json_object_new_object(); + json_mac = json_object_new_object(); + } + memset(&wctx, 0, sizeof(struct mac_walk_ctx)); wctx.zvni = zvni; wctx.vty = vty; + wctx.json = json_mac; - vty_out(vty, - "Number of MACs (local and remote) known for this VNI: %u\n", - num_macs); - vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC", "Type", - "Intf/Remote VTEP", "VLAN"); + if (!use_json) { + vty_out(vty, + "Number of MACs (local and remote) known for this VNI: %u\n", + num_macs); + vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC", "Type", + "Intf/Remote VTEP", "VLAN"); + } else + json_object_int_add(json, "numMacs", num_macs); hash_iterate(zvni->mac_table, zvni_print_mac_hash, &wctx); + + if (use_json) { + json_object_object_add(json, "macs", json_mac); + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } } /* * Display MACs for all VNIs (VTY command handler). */ -void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf) +void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf, + u_char use_json) { struct mac_walk_ctx wctx; + json_object *json = NULL; - if (!EVPN_ENABLED(zvrf)) + if (!EVPN_ENABLED(zvrf)) { + if (use_json) + vty_out(vty, "{}\n"); return; + } + if (use_json) + json = json_object_new_object(); + memset(&wctx, 0, sizeof(struct mac_walk_ctx)); wctx.vty = vty; + wctx.json = json; hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni, &wctx); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } } /* @@ -2100,17 +2410,30 @@ void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf) */ void zebra_vxlan_print_macs_all_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, - struct in_addr vtep_ip) + struct in_addr vtep_ip, + u_char use_json) { struct mac_walk_ctx wctx; + json_object *json = NULL; if (!EVPN_ENABLED(zvrf)) return; + + if (use_json) + json = json_object_new_object(); + memset(&wctx, 0, sizeof(struct mac_walk_ctx)); wctx.vty = vty; wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP; wctx.r_vtep_ip = vtep_ip; + wctx.json = json; hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni, &wctx); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } } /* @@ -2143,64 +2466,124 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf, * Display MACs for a VNI from specific VTEP (VTY command handler). */ void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, - vni_t vni, struct in_addr vtep_ip) + vni_t vni, struct in_addr vtep_ip, + u_char use_json) { zebra_vni_t *zvni; u_int32_t num_macs; struct mac_walk_ctx wctx; + json_object *json = NULL; + json_object *json_mac = NULL; if (!EVPN_ENABLED(zvrf)) return; zvni = zvni_lookup(zvrf, vni); if (!zvni) { - vty_out(vty, "%% VNI %u does not exist\n", vni); + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% VNI %u does not exist\n", vni); return; } num_macs = hashcount(zvni->mac_table); if (!num_macs) return; + + if (use_json) { + json = json_object_new_object(); + json_mac = json_object_new_object(); + } + memset(&wctx, 0, sizeof(struct mac_walk_ctx)); wctx.zvni = zvni; wctx.vty = vty; wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP; wctx.r_vtep_ip = vtep_ip; + wctx.json = json_mac; hash_iterate(zvni->mac_table, zvni_print_mac_hash, &wctx); + + if (use_json) { + json_object_int_add(json, "numMacs", wctx.count); + if (wctx.count) + json_object_object_add(json, "macs", json_mac); + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } } /* * Display VNI information (VTY command handler). */ -void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni) +void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, + u_char use_json) { zebra_vni_t *zvni; + json_object *json = NULL; + void *args[2]; if (!EVPN_ENABLED(zvrf)) return; zvni = zvni_lookup(zvrf, vni); if (!zvni) { - vty_out(vty, "%% VNI %u does not exist\n", vni); + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - zvni_print(zvni, (void *)vty); + if (use_json) + json = json_object_new_object(); + args[0] = vty; + args[1] = json; + zvni_print(zvni, (void *)args); + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } } /* * Display VNI hash table (VTY command handler). */ -void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf) +void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf, + u_char use_json) { u_int32_t num_vnis; + json_object *json = NULL; + void *args[2]; if (!EVPN_ENABLED(zvrf)) return; num_vnis = hashcount(zvrf->vni_table); - if (!num_vnis) + if (!num_vnis) { + if (use_json) + vty_out(vty, "{}\n"); return; - vty_out(vty, "Number of VNIs: %u\n", num_vnis); - vty_out(vty, "%-10s %-21s %-15s %-8s %-8s %-15s\n", "VNI", "VxLAN IF", - "VTEP IP", "# MACs", "# ARPs", "# Remote VTEPs"); - hash_iterate(zvrf->vni_table, zvni_print_hash, vty); + } + if (use_json) { + json = json_object_new_object(); + json_object_int_add(json, "numVnis", num_vnis); + } else { + vty_out(vty, "Number of VNIs: %u\n", num_vnis); + vty_out(vty, "%-10s %-21s %-15s %-8s %-8s %-15s\n", "VNI", + "VxLAN IF", "VTEP IP", "# MACs", "# ARPs", + "# Remote VTEPs"); + } + args[0] = vty; + args[1] = json; + + hash_iterate(zvrf->vni_table, + (void (*)(struct hash_backet *, void *))zvni_print_hash, + args); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } } /* diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h index dca800603..f7c1afc95 100644 --- a/zebra/zebra_vxlan.h +++ b/zebra/zebra_vxlan.h @@ -41,32 +41,41 @@ #define ZEBRA_VXLIF_MASTER_CHANGE 0x2 #define ZEBRA_VXLIF_VLAN_CHANGE 0x4 +#define VNI_STR_LEN 32 + extern void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, - vni_t vni); + vni_t vni, u_char use_json); extern void zebra_vxlan_print_macs_all_vni(struct vty *vty, - struct zebra_vrf *zvrf); + struct zebra_vrf *zvrf, + u_char use_json); extern void zebra_vxlan_print_macs_all_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, - struct in_addr vtep_ip); + struct in_addr vtep_ip, + u_char use_json); extern void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, struct ethaddr *mac); extern void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, - struct in_addr vtep_ip); + struct in_addr vtep_ip, + u_char use_json); extern void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, - vni_t vni); + vni_t vni, u_char use_json); extern void zebra_vxlan_print_neigh_all_vni(struct vty *vty, - struct zebra_vrf *zvrf); + struct zebra_vrf *zvrf, + u_char use_json); extern void zebra_vxlan_print_specific_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, - vni_t vni, struct ipaddr *ip); + vni_t vni, struct ipaddr *ip, + u_char use_json); extern void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, - struct in_addr vtep_ip); + struct in_addr vtep_ip, + u_char use_json); extern void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, - vni_t vni); -extern void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf); + vni_t vni, u_char use_json); +extern void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf, + u_char use_json); extern int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, int add); diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h index 2e5943a75..2bf00291e 100644 --- a/zebra/zebra_vxlan_private.h +++ b/zebra/zebra_vxlan_private.h @@ -135,8 +135,9 @@ struct mac_walk_ctx { struct in_addr r_vtep_ip; /* To walk MACs from specific VTEP */ - struct vty *vty; /* Used by VTY handlers */ - u_int32_t count; /* Used by VTY handlers */ + struct vty *vty; /* Used by VTY handlers */ + u_int32_t count; /* Used by VTY handlers */ + struct json_object *json; /* Used for JSON Output */ }; /* @@ -186,9 +187,10 @@ struct neigh_walk_ctx { struct in_addr r_vtep_ip; /* To walk neighbors from specific VTEP */ - struct vty *vty; /* Used by VTY handlers */ - u_int32_t count; /* Used by VTY handlers */ - u_char addr_width; /* Used by VTY handlers */ + struct vty *vty; /* Used by VTY handlers */ + u_int32_t count; /* Used by VTY handlers */ + u_char addr_width; /* Used by VTY handlers */ + struct json_object *json; /* Used for JSON Output */ }; #endif /* _ZEBRA_VXLAN_PRIVATE_H */ -- 2.39.2