From 8145842bf273823192140c57fc773bb92d9db64f Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Mon, 12 Mar 2018 06:59:36 -0700 Subject: [PATCH] Common form of fib-path reproting in dumps Change-Id: I8f6fdbbeef2ac7e9fe5d87490ae5cba6e9a0b294 Signed-off-by: Neale Ranns --- src/vnet.am | 1 + src/vnet/bier/bier_api.c | 12 +- src/vnet/fib/fib_api.c | 268 +++++++++++++++++++++++++++++++++++++++++++++ src/vnet/fib/fib_api.h | 8 +- src/vnet/fib/fib_entry.h | 1 + src/vnet/fib/fib_path.c | 10 +- src/vnet/fib/fib_types.api | 9 ++ src/vnet/ip/ip_api.c | 109 ++---------------- src/vnet/mpls/mpls_api.c | 13 +-- src/vpp-api/vom/route.cpp | 20 +++- test/test_dvr.py | 17 +++ 11 files changed, 338 insertions(+), 130 deletions(-) create mode 100644 src/vnet/fib/fib_api.c diff --git a/src/vnet.am b/src/vnet.am index 9548509715d..a9d984b3ce7 100644 --- a/src/vnet.am +++ b/src/vnet.am @@ -1076,6 +1076,7 @@ libvnet_la_SOURCES += \ vnet/fib/fib_path_ext.c \ vnet/fib/fib_urpf_list.c \ vnet/fib/fib_attached_export.c \ + vnet/fib/fib_api.c \ vnet/fib/fib_bfd.c nobase_include_HEADERS += \ diff --git a/src/vnet/bier/bier_api.c b/src/vnet/bier/bier_api.c index 77b2cabaa44..8f168c46e29 100644 --- a/src/vnet/bier/bier_api.c +++ b/src/vnet/bier/bier_api.c @@ -307,11 +307,7 @@ send_bier_route_details (const bier_table_t *bt, fp = mp->br_paths; vec_foreach (api_rpath, api_rpaths) { - fp->weight = api_rpath->rpath.frp_weight; - fp->preference = api_rpath->rpath.frp_preference; - fp->sw_if_index = htonl (api_rpath->rpath.frp_sw_if_index); - fp->n_labels = 0; - copy_fib_next_hop (api_rpath, fp); + fib_api_path_encode(api_rpath, fp); fp++; } @@ -672,11 +668,7 @@ send_bier_disp_entry_details (const bier_disp_table_t *bdt, fp = mp->bde_paths; vec_foreach (api_rpath, api_rpaths) { - fp->weight = api_rpath->rpath.frp_weight; - fp->preference = api_rpath->rpath.frp_preference; - fp->sw_if_index = htonl (api_rpath->rpath.frp_sw_if_index); - fp->n_labels = 0; - copy_fib_next_hop (api_rpath, fp); + fib_api_path_encode(api_rpath, fp); fp++; } diff --git a/src/vnet/fib/fib_api.c b/src/vnet/fib/fib_api.c new file mode 100644 index 00000000000..a2f7e79b03c --- /dev/null +++ b/src/vnet/fib/fib_api.c @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define vl_typedefs /* define message structures */ +#include +#undef vl_typedefs + +#define vl_endianfun /* define message structures */ +#include +#undef vl_endianfun + +/* instantiate all the print functions we know about */ +#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) +#define vl_printfun +#include +#undef vl_printfun + +#include + +int +fib_path_api_parse (const vl_api_fib_path_t *in, + fib_route_path_t *out) +{ + fib_route_path_flags_t path_flags; + mpls_label_t next_hop_via_label; + int rv = 0, n_labels; + u8 ii; + + path_flags = FIB_ROUTE_PATH_FLAG_NONE; + next_hop_via_label = ntohl (in->via_label); + memset(out, 0, sizeof(*out)); + out->frp_sw_if_index = ~0; + + out->frp_proto = in->afi; + // .frp_addr = (NULL == next_hop ? zero_addr : *next_hop), + out->frp_sw_if_index = ntohl(in->sw_if_index); + out->frp_fib_index = ntohl(in->table_id); + out->frp_weight = in->weight; + out->frp_preference = in->preference; + + /* + * the special INVALID label meams we are not recursing via a + * label. Exp-null value is never a valid via-label so that + * also means it's not a via-label and means clients that set + * it to 0 by default get the expected behaviour + */ + if ((MPLS_LABEL_INVALID != next_hop_via_label) && + (0 != next_hop_via_label)) + { + out->frp_proto = DPO_PROTO_MPLS; + out->frp_local_label = next_hop_via_label; + out->frp_eos = MPLS_NON_EOS; + } + + n_labels = in->n_labels; + if (n_labels == 0) + ; + else + { + vec_validate (out->frp_label_stack, n_labels - 1); + for (ii = 0; ii < n_labels; ii++) + { + out->frp_label_stack[ii].fml_value = + ntohl(in->label_stack[ii].label); + out->frp_label_stack[ii].fml_ttl = + in->label_stack[ii].ttl; + out->frp_label_stack[ii].fml_exp = + in->label_stack[ii].exp; + out->frp_label_stack[ii].fml_mode = + (in->label_stack[ii].is_uniform ? + FIB_MPLS_LSP_MODE_UNIFORM : + FIB_MPLS_LSP_MODE_PIPE); + } + } + + if (in->is_dvr) + path_flags |= FIB_ROUTE_PATH_DVR; + if (in->is_resolve_host) + path_flags |= FIB_ROUTE_PATH_RESOLVE_VIA_HOST; + if (in->is_resolve_attached) + path_flags |= FIB_ROUTE_PATH_RESOLVE_VIA_ATTACHED; + /* if (in->is_interface_rx) */ + /* path_flags |= FIB_ROUTE_PATH_INTF_RX; */ + /* if (in->is_rpf_id) */ + /* path_flags |= FIB_ROUTE_PATH_RPF_ID; */ + if (in->is_source_lookup) + path_flags |= FIB_ROUTE_PATH_SOURCE_LOOKUP; + + if (in->is_udp_encap) + { + path_flags |= FIB_ROUTE_PATH_UDP_ENCAP; + out->frp_udp_encap_id = ntohl(in->next_hop_id); + } + else + { + if (DPO_PROTO_IP4 == in->afi) + { + clib_memcpy (&out->frp_addr.ip4, + in->next_hop, + sizeof (out->frp_addr.ip4)); + } + else if (DPO_PROTO_IP6 == in->afi) + { + clib_memcpy (&out->frp_addr.ip6, + in->next_hop, + sizeof (out->frp_addr.ip6)); + } + + if (ip46_address_is_zero(&out->frp_addr)) + { + if (DPO_PROTO_BIER == in->afi) + { + index_t bdti; + + bdti = bier_disp_table_find(ntohl(in->table_id)); + + if (INDEX_INVALID != bdti) + { + out->frp_fib_index = bdti; + out->frp_proto = DPO_PROTO_BIER; + } + else + { + rv = VNET_API_ERROR_NO_SUCH_FIB; + } + } + else if (out->frp_sw_if_index == ~0 && + out->frp_fib_index != ~0) + { + path_flags |= FIB_ROUTE_PATH_DEAG; + } + } + } + + out->frp_flags = path_flags; + + return (rv); +} + +void +fib_prefix_to_api (const fib_prefix_t *pfx, + u8 address[16], + u8 *length, + u8 *is_ip6) +{ + *length = pfx->fp_len; + *is_ip6 = (FIB_PROTOCOL_IP6 == pfx->fp_proto ? 1 : 0); + + if (FIB_PROTOCOL_IP6 == pfx->fp_proto) + { + memcpy (address, &pfx->fp_addr.ip6, sizeof (pfx->fp_addr.ip6)); + } + else + { + memcpy (address, &pfx->fp_addr.ip4, sizeof (pfx->fp_addr.ip4)); + } +} + +static void +fib_api_path_copy_next_hop (const fib_route_path_encode_t * api_rpath, void *fp_arg) +{ + int is_ip4; + vl_api_fib_path_t *fp = (vl_api_fib_path_t *) fp_arg; + + if (api_rpath->rpath.frp_proto == DPO_PROTO_IP4) + fp->afi = IP46_TYPE_IP4; + else if (api_rpath->rpath.frp_proto == DPO_PROTO_IP6) + fp->afi = IP46_TYPE_IP6; + else + { + is_ip4 = ip46_address_is_ip4 (&api_rpath->rpath.frp_addr); + if (is_ip4) + fp->afi = IP46_TYPE_IP4; + else + fp->afi = IP46_TYPE_IP6; + } + if (fp->afi == IP46_TYPE_IP4) + memcpy (fp->next_hop, &api_rpath->rpath.frp_addr.ip4, + sizeof (api_rpath->rpath.frp_addr.ip4)); + else + memcpy (fp->next_hop, &api_rpath->rpath.frp_addr.ip6, + sizeof (api_rpath->rpath.frp_addr.ip6)); +} + +void +fib_api_path_encode (const fib_route_path_encode_t * api_rpath, + vl_api_fib_path_t *out) +{ + memset (out, 0, sizeof (*out)); + switch (api_rpath->dpo.dpoi_type) + { + case DPO_RECEIVE: + out->is_local = true; + break; + case DPO_DROP: + out->is_drop = true; + break; + case DPO_IP_NULL: + switch (api_rpath->dpo.dpoi_index) + { + case IP_NULL_ACTION_NONE: + out->is_drop = true; + break; + case IP_NULL_ACTION_SEND_ICMP_UNREACH: + out->is_unreach = true; + break; + case IP_NULL_ACTION_SEND_ICMP_PROHIBIT: + out->is_prohibit = true; + break; + default: + break; + } + break; + default: + break; + } + out->weight = api_rpath->rpath.frp_weight; + out->preference = api_rpath->rpath.frp_preference; + out->sw_if_index = htonl (api_rpath->rpath.frp_sw_if_index); + out->afi = api_rpath->rpath.frp_proto; + fib_api_path_copy_next_hop (api_rpath, out); + + if (~0 == api_rpath->rpath.frp_sw_if_index && + !ip46_address_is_zero(&api_rpath->rpath.frp_addr)) + { + if ((DPO_PROTO_IP6 == api_rpath->rpath.frp_proto) || + (DPO_PROTO_IP6 == api_rpath->rpath.frp_proto)) + { + fib_table_t *fib; + + fib = fib_table_get (api_rpath->rpath.frp_fib_index, + dpo_proto_to_fib(api_rpath->rpath.frp_proto)); + + out->table_id = htonl (fib->ft_table_id); + } + } + + if (api_rpath->rpath.frp_flags & FIB_ROUTE_PATH_DVR) + { + out->is_dvr = 1; + } + if (api_rpath->rpath.frp_flags & FIB_ROUTE_PATH_UDP_ENCAP) + { + out->is_udp_encap = 1; + out->next_hop_id = api_rpath->rpath.frp_udp_encap_id; + } +} diff --git a/src/vnet/fib/fib_api.h b/src/vnet/fib/fib_api.h index d10ba00913b..c8b782ab156 100644 --- a/src/vnet/fib/fib_api.h +++ b/src/vnet/fib/fib_api.h @@ -16,6 +16,7 @@ #ifndef __FIB_API_H__ #define __FIB_API_H__ +#include int add_del_route_check (fib_protocol_t table_proto, @@ -55,8 +56,9 @@ add_del_route_t_handler (u8 is_multipath, mpls_label_t next_hop_via_label, fib_mpls_label_t * next_hop_out_label_stack); -void -copy_fib_next_hop (fib_route_path_encode_t * api_rpath, - void * fp_arg); +struct _vl_api_fib_path; + +extern void fib_api_path_encode (const fib_route_path_encode_t * api_rpath, + struct _vl_api_fib_path *out); #endif /* __FIB_API_H__ */ diff --git a/src/vnet/fib/fib_entry.h b/src/vnet/fib/fib_entry.h index 3fde97add32..4e88314005e 100644 --- a/src/vnet/fib/fib_entry.h +++ b/src/vnet/fib/fib_entry.h @@ -161,6 +161,7 @@ STATIC_ASSERT (sizeof(fib_source_t) == 1, [FIB_SOURCE_MPLS] = "mpls", \ [FIB_SOURCE_URPF_EXEMPT] = "urpf-exempt", \ [FIB_SOURCE_DEFAULT_ROUTE] = "default-route", \ + [FIB_SOURCE_PLUGIN_HI] = "plugin-hi", \ } #define FOR_EACH_FIB_SOURCE(_item) \ diff --git a/src/vnet/fib/fib_path.c b/src/vnet/fib/fib_path.c index a8593905438..8cfe86a7a90 100644 --- a/src/vnet/fib/fib_path.c +++ b/src/vnet/fib/fib_path.c @@ -80,7 +80,7 @@ typedef enum fib_path_type_t_ { */ FIB_PATH_TYPE_INTF_RX, /** - * interface receive. + * Path resolves via a UDP encap object. */ FIB_PATH_TYPE_UDP_ENCAP, /** @@ -2619,6 +2619,14 @@ fib_path_encode (fib_node_index_t path_list_index, case FIB_PATH_TYPE_RECURSIVE: api_rpath->rpath.frp_addr = path->recursive.fp_nh.fp_ip; break; + case FIB_PATH_TYPE_DVR: + api_rpath->rpath.frp_sw_if_index = path->dvr.fp_interface; + api_rpath->rpath.frp_flags |= FIB_ROUTE_PATH_DVR; + break; + case FIB_PATH_TYPE_UDP_ENCAP: + api_rpath->rpath.frp_udp_encap_id = path->udp_encap.fp_udp_encap_id; + api_rpath->rpath.frp_flags |= FIB_ROUTE_PATH_UDP_ENCAP; + break; default: break; } diff --git a/src/vnet/fib/fib_types.api b/src/vnet/fib/fib_types.api index fde2c337190..89f87c0f16f 100644 --- a/src/vnet/fib/fib_types.api +++ b/src/vnet/fib/fib_types.api @@ -33,6 +33,10 @@ typeonly define fib_mpls_label @param is_unreach - Drop the packet and rate limit send ICMP unreachable @param is_prohibit - Drop the packet and rate limit send ICMP prohibited @param is_udp_encap - The path describes a UDP-o-IP encapsulation. + @param is_dvr - Does the route resolve via a DVR interface. + @param is_source_lookup - The the path is a deaggregate path (i.e. a lookup + in another table) is the lookup on the packet's + source address or destination. @param afi - the afi of the next hop, IP46_TYPE_IP4=1, IP46_TYPE_IP6=2 @param next_hop[16] - the next hop address @param next_hop_id - Used when the path resolves via an object @@ -50,10 +54,15 @@ typeonly define fib_path u8 is_udp_encap; u8 is_unreach; u8 is_prohibit; + u8 is_resolve_host; + u8 is_resolve_attached; + u8 is_dvr; + u8 is_source_lookup; u8 afi; u8 next_hop[16]; u32 next_hop_id; u32 rpf_id; + u32 via_label; u8 n_labels; vl_api_fib_mpls_label_t label_stack[16]; }; diff --git a/src/vnet/ip/ip_api.c b/src/vnet/ip/ip_api.c index 726d24ca5a8..9cd625799b6 100644 --- a/src/vnet/ip/ip_api.c +++ b/src/vnet/ip/ip_api.c @@ -177,33 +177,6 @@ vl_api_ip_neighbor_dump_t_handler (vl_api_ip_neighbor_dump_t * mp) } } - -void -copy_fib_next_hop (fib_route_path_encode_t * api_rpath, void *fp_arg) -{ - int is_ip4; - vl_api_fib_path_t *fp = (vl_api_fib_path_t *) fp_arg; - - if (api_rpath->rpath.frp_proto == DPO_PROTO_IP4) - fp->afi = IP46_TYPE_IP4; - else if (api_rpath->rpath.frp_proto == DPO_PROTO_IP6) - fp->afi = IP46_TYPE_IP6; - else - { - is_ip4 = ip46_address_is_ip4 (&api_rpath->rpath.frp_addr); - if (is_ip4) - fp->afi = IP46_TYPE_IP4; - else - fp->afi = IP46_TYPE_IP6; - } - if (fp->afi == IP46_TYPE_IP4) - memcpy (fp->next_hop, &api_rpath->rpath.frp_addr.ip4, - sizeof (api_rpath->rpath.frp_addr.ip4)); - else - memcpy (fp->next_hop, &api_rpath->rpath.frp_addr.ip6, - sizeof (api_rpath->rpath.frp_addr.ip6)); -} - static void send_ip_fib_details (vpe_api_main_t * am, vl_api_registration_t * reg, @@ -234,38 +207,7 @@ send_ip_fib_details (vpe_api_main_t * am, fp = mp->path; vec_foreach (api_rpath, api_rpaths) { - memset (fp, 0, sizeof (*fp)); - switch (api_rpath->dpo.dpoi_type) - { - case DPO_RECEIVE: - fp->is_local = true; - break; - case DPO_DROP: - fp->is_drop = true; - break; - case DPO_IP_NULL: - switch (api_rpath->dpo.dpoi_index) - { - case IP_NULL_ACTION_NONE: - fp->is_drop = true; - break; - case IP_NULL_ACTION_SEND_ICMP_UNREACH: - fp->is_unreach = true; - break; - case IP_NULL_ACTION_SEND_ICMP_PROHIBIT: - fp->is_prohibit = true; - break; - default: - break; - } - break; - default: - break; - } - fp->weight = api_rpath->rpath.frp_weight; - fp->preference = api_rpath->rpath.frp_preference; - fp->sw_if_index = htonl (api_rpath->rpath.frp_sw_if_index); - copy_fib_next_hop (api_rpath, fp); + fib_api_path_encode (api_rpath, fp); fp++; } @@ -362,38 +304,7 @@ send_ip6_fib_details (vpe_api_main_t * am, fp = mp->path; vec_foreach (api_rpath, api_rpaths) { - memset (fp, 0, sizeof (*fp)); - switch (api_rpath->dpo.dpoi_type) - { - case DPO_RECEIVE: - fp->is_local = true; - break; - case DPO_DROP: - fp->is_drop = true; - break; - case DPO_IP_NULL: - switch (api_rpath->dpo.dpoi_index) - { - case IP_NULL_DPO_ACTION_NUM + IP_NULL_ACTION_NONE: - fp->is_drop = true; - break; - case IP_NULL_DPO_ACTION_NUM + IP_NULL_ACTION_SEND_ICMP_UNREACH: - fp->is_unreach = true; - break; - case IP_NULL_DPO_ACTION_NUM + IP_NULL_ACTION_SEND_ICMP_PROHIBIT: - fp->is_prohibit = true; - break; - default: - break; - } - break; - default: - break; - } - fp->weight = api_rpath->rpath.frp_weight; - fp->preference = api_rpath->rpath.frp_preference; - fp->sw_if_index = htonl (api_rpath->rpath.frp_sw_if_index); - copy_fib_next_hop (api_rpath, fp); + fib_api_path_encode (api_rpath, fp); fp++; } @@ -464,6 +375,10 @@ vl_api_ip6_fib_dump_t_handler (vl_api_ip6_fib_dump_t * mp) /* *INDENT-OFF* */ pool_foreach (fib_table, im6->fibs, ({ + /* don't send link locals */ + if (fib_table->ft_flags & FIB_TABLE_FLAG_IP6_LL) + continue; + api_ip6_fib_table_get_all(reg, mp, fib_table); })); /* *INDENT-ON* */ @@ -505,11 +420,7 @@ send_ip_mfib_details (vl_api_registration_t * reg, fp = mp->path; vec_foreach (api_rpath, api_rpaths) { - memset (fp, 0, sizeof (*fp)); - - fp->weight = 0; - fp->sw_if_index = htonl (api_rpath->rpath.frp_sw_if_index); - copy_fib_next_hop (api_rpath, fp); + fib_api_path_encode (api_rpath, fp); fp++; } vec_free (api_rpaths); @@ -601,11 +512,7 @@ send_ip6_mfib_details (vpe_api_main_t * am, fp = mp->path; vec_foreach (api_rpath, api_rpaths) { - memset (fp, 0, sizeof (*fp)); - - fp->weight = 0; - fp->sw_if_index = htonl (api_rpath->rpath.frp_sw_if_index); - copy_fib_next_hop (api_rpath, fp); + fib_api_path_encode (api_rpath, fp); fp++; } diff --git a/src/vnet/mpls/mpls_api.c b/src/vnet/mpls/mpls_api.c index 169ee406a91..9f5100a7f83 100644 --- a/src/vnet/mpls/mpls_api.c +++ b/src/vnet/mpls/mpls_api.c @@ -427,12 +427,7 @@ send_mpls_tunnel_entry (u32 mti, void *arg) fp = mp->mt_paths; vec_foreach (api_rpath, api_rpaths) { - memset (fp, 0, sizeof (*fp)); - - fp->weight = api_rpath->rpath.frp_weight; - fp->preference = api_rpath->rpath.frp_preference; - fp->sw_if_index = htonl (api_rpath->rpath.frp_sw_if_index); - copy_fib_next_hop (api_rpath, fp); + fib_api_path_encode (api_rpath, fp); fp++; } @@ -491,11 +486,7 @@ send_mpls_fib_details (vpe_api_main_t * am, fp = mp->path; vec_foreach (api_rpath, api_rpaths) { - memset (fp, 0, sizeof (*fp)); - fp->weight = api_rpath->rpath.frp_weight; - fp->preference = api_rpath->rpath.frp_preference; - fp->sw_if_index = htonl (api_rpath->rpath.frp_sw_if_index); - copy_fib_next_hop (api_rpath, fp); + fib_api_path_encode (api_rpath, fp); fp++; } diff --git a/src/vpp-api/vom/route.cpp b/src/vpp-api/vom/route.cpp index 780881e5805..fa6e5d43b37 100644 --- a/src/vpp-api/vom/route.cpp +++ b/src/vpp-api/vom/route.cpp @@ -427,8 +427,14 @@ ip_route::event_handler::handle_populate(const client_db::key_t& key) boost::asio::ip::address address = from_bytes(0, p.next_hop); std::shared_ptr itf = interface::find(p.sw_if_index); if (itf) { - path path_v4(address, *itf, p.weight, p.preference); - ip_r.add(path_v4); + if (p.is_dvr) { + path path_v4(*itf, nh_proto_t::IPV4, route::path::flags_t::DVR, + p.weight, p.preference); + ip_r.add(path_v4); + } else { + path path_v4(address, *itf, p.weight, p.preference); + ip_r.add(path_v4); + } } else { path path_v4(rd_temp, address, p.weight, p.preference); ip_r.add(path_v4); @@ -474,8 +480,14 @@ ip_route::event_handler::handle_populate(const client_db::key_t& key) std::shared_ptr itf = interface::find(p.sw_if_index); boost::asio::ip::address address = from_bytes(1, p.next_hop); if (itf) { - path path_v6(address, *itf, p.weight, p.preference); - ip_r.add(path_v6); + if (p.is_dvr) { + path path_v6(*itf, nh_proto_t::IPV6, route::path::flags_t::DVR, + p.weight, p.preference); + ip_r.add(path_v6); + } else { + path path_v6(address, *itf, p.weight, p.preference); + ip_r.add(path_v6); + } } else { path path_v6(rd_temp, address, p.weight, p.preference); ip_r.add(path_v6); diff --git a/test/test_dvr.py b/test/test_dvr.py index e2e960584a6..d508a8d8b1a 100644 --- a/test/test_dvr.py +++ b/test/test_dvr.py @@ -214,6 +214,23 @@ class TestDVR(VppTestCase): self.vapi.sw_interface_set_l2_bridge(self.loop0.sw_if_index, 1, bvi=1, enable=0) + # + # Do a FIB dump to make sure the paths are correctly reported as DVR + # + routes = self.vapi.ip_fib_dump() + + for r in routes: + if (inet_pton(AF_INET, ip_tag_bridged) == r.address): + print r + self.assertEqual(r.path[0].sw_if_index, + sub_if_on_pg3.sw_if_index) + self.assertEqual(r.path[0].is_dvr, 1) + if (inet_pton(AF_INET, ip_non_tag_bridged) == r.address): + print r + self.assertEqual(r.path[0].sw_if_index, + self.pg1.sw_if_index) + self.assertEqual(r.path[0].is_dvr, 1) + # # the explicit route delete is require so it happens before # the sbu-interface delete. subinterface delete is required -- 2.16.6