From 36abbf10045ba81733410f50a3f5a9463f7137d1 Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Tue, 12 Mar 2019 02:34:07 -0700 Subject: [PATCH] GBP: L3 out fixes Change-Id: I0562d597fd45c7ddcb6db42cf17d3ffb569eb140 Signed-off-by: Neale Ranns --- src/plugins/gbp/gbp_classify_node.c | 89 ++++++++++++++++++++--- src/plugins/gbp/gbp_endpoint.c | 13 +++- src/plugins/gbp/gbp_endpoint.h | 5 ++ src/plugins/gbp/gbp_ext_itf.c | 12 +--- src/plugins/gbp/gbp_itf.c | 35 +++++++++- test/test_gbp.py | 136 +++++++++++++++++++++++++++--------- test/vpp_l2.py | 29 ++++++++ 7 files changed, 260 insertions(+), 59 deletions(-) diff --git a/src/plugins/gbp/gbp_classify_node.c b/src/plugins/gbp/gbp_classify_node.c index 1b2cb0a2bc7..58b1f7ac545 100644 --- a/src/plugins/gbp/gbp_classify_node.c +++ b/src/plugins/gbp/gbp_classify_node.c @@ -308,6 +308,28 @@ ethertype_to_dpo_proto (const ethernet_header_t * eh0) return (DPO_PROTO_NONE); } +/** + * per-packet trace data + */ +typedef struct gbp_lpm_classify_trace_t_ +{ + sclass_t sclass; + index_t lbi; +} gbp_lpm_classify_trace_t; + +/* packet trace format function */ +static u8 * +format_gbp_lpm_classify_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + gbp_lpm_classify_trace_t *t = va_arg (*args, gbp_lpm_classify_trace_t *); + + s = format (s, "sclass:%d lb:%d", t->sclass, t->lbi); + + return s; +} + /* * Determine the SRC EPG from a LPM */ @@ -335,8 +357,9 @@ gbp_lpm_classify_inline (vlib_main_t * vm, { u32 bi0, sw_if_index0, fib_index0, lbi0; gbp_lpm_classify_next_t next0; + const ethernet_header_t *eh0; const gbp_policy_dpo_t *gpd0; - const gbp_ext_itf_t *gx0; + const gbp_endpoint_t *ge0; const gbp_recirc_t *gr0; const dpo_id_t *dpo0; load_balance_t *lb0; @@ -355,6 +378,8 @@ gbp_lpm_classify_inline (vlib_main_t * vm, ip6_0 = NULL; next0 = GPB_LPM_CLASSIFY_DROP; + lbi0 = ~0; + eh0 = NULL; b0 = vlib_get_buffer (vm, bi0); sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; @@ -366,8 +391,6 @@ gbp_lpm_classify_inline (vlib_main_t * vm, ip6_0 = vlib_buffer_get_current (b0); else if (DPO_PROTO_ETHERNET == dproto) { - const ethernet_header_t *eh0; - eh0 = vlib_buffer_get_current (b0); dproto = ethertype_to_dpo_proto (eh0); @@ -393,17 +416,61 @@ gbp_lpm_classify_inline (vlib_main_t * vm, { gr0 = gbp_recirc_get (sw_if_index0); fib_index0 = gr0->gr_fib_index[dproto]; + ge0 = NULL; vnet_feature_next (&next0, b0); } else { - gx0 = gbp_ext_itf_get (sw_if_index0); - fib_index0 = gx0->gx_fib_index[dproto]; + if (NULL == eh0) + { + /* packet should be l2 */ + sclass0 = SCLASS_INVALID; + goto trace; + } + + ge0 = gbp_endpoint_find_mac (eh0->src_address, + vnet_buffer (b0)->l2.bd_index); + + if (NULL == ge0) + { + /* packet must have come from an EP's mac */ + sclass0 = SCLASS_INVALID; + goto trace; + } + + fib_index0 = ge0->ge_fwd.gef_fib_index; + + if (~0 == fib_index0) + { + sclass0 = SCLASS_INVALID; + goto trace; + } + + if (DPO_PROTO_IP4 == dproto) + { + ge0 = + gbp_endpoint_find_ip4 (&ip4_0->src_address, fib_index0); + } + else if (DPO_PROTO_IP6 == dproto) + { + ge0 = + gbp_endpoint_find_ip6 (&ip6_0->src_address, fib_index0); + } next0 = vnet_l2_feature_next (b0, gscm->l2_input_feat_next[GBP_SRC_CLASSIFY_LPM], L2INPUT_FEAT_GBP_LPM_CLASSIFY); + + /* + * if we found the EP by IP lookup, it must be from the EP + * not a network behind it + */ + if (NULL != ge0) + { + sclass0 = ge0->ge_fwd.gef_sclass; + goto trace; + } } if (DPO_PROTO_IP4 == dproto) @@ -420,6 +487,7 @@ gbp_lpm_classify_inline (vlib_main_t * vm, { /* not IP so no LPM classify possible */ sclass0 = SCLASS_INVALID; + next0 = GPB_LPM_CLASSIFY_DROP; goto trace; } lb0 = load_balance_get (lbi0); @@ -434,7 +502,7 @@ gbp_lpm_classify_inline (vlib_main_t * vm, { /* could not classify => drop */ sclass0 = SCLASS_INVALID; - next0 = GPB_LPM_CLASSIFY_DROP; + goto trace; } trace: @@ -442,9 +510,10 @@ gbp_lpm_classify_inline (vlib_main_t * vm, if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED))) { - gbp_classify_trace_t *t = + gbp_lpm_classify_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); t->sclass = sclass0; + t->lbi = lbi0; } vlib_validate_buffer_enqueue_x1 (vm, node, next_index, @@ -483,7 +552,7 @@ VLIB_NODE_FN (gbp_l2_lpm_classify_node) (vlib_main_t * vm, VLIB_REGISTER_NODE (gbp_ip4_lpm_classify_node) = { .name = "ip4-gbp-lpm-classify", .vector_size = sizeof (u32), - .format_trace = format_gbp_classify_trace, + .format_trace = format_gbp_lpm_classify_trace, .type = VLIB_NODE_TYPE_INTERNAL, .n_errors = 0, @@ -496,7 +565,7 @@ VLIB_REGISTER_NODE (gbp_ip4_lpm_classify_node) = { VLIB_REGISTER_NODE (gbp_ip6_lpm_classify_node) = { .name = "ip6-gbp-lpm-classify", .vector_size = sizeof (u32), - .format_trace = format_gbp_classify_trace, + .format_trace = format_gbp_lpm_classify_trace, .type = VLIB_NODE_TYPE_INTERNAL, .n_errors = 0, @@ -509,7 +578,7 @@ VLIB_REGISTER_NODE (gbp_ip6_lpm_classify_node) = { VLIB_REGISTER_NODE (gbp_l2_lpm_classify_node) = { .name = "l2-gbp-lpm-classify", .vector_size = sizeof (u32), - .format_trace = format_gbp_classify_trace, + .format_trace = format_gbp_lpm_classify_trace, .type = VLIB_NODE_TYPE_INTERNAL, .n_errors = 0, diff --git a/src/plugins/gbp/gbp_endpoint.c b/src/plugins/gbp/gbp_endpoint.c index 36e0050c39d..2472199e320 100644 --- a/src/plugins/gbp/gbp_endpoint.c +++ b/src/plugins/gbp/gbp_endpoint.c @@ -254,7 +254,8 @@ gbp_endpoint_alloc (const ip46_address_t * ips, fib_node_init (&ge->ge_node, gbp_endpoint_fib_type); gei = gbp_endpoint_index (ge); ge->ge_key.gek_gbd = - ge->ge_key.gek_grd = ge->ge_fwd.gef_itf = INDEX_INVALID; + ge->ge_key.gek_grd = + ge->ge_fwd.gef_itf = ge->ge_fwd.gef_fib_index = INDEX_INVALID; ge->ge_last_time = vlib_time_now (vlib_get_main ()); ge->ge_key.gek_gbd = gbp_bridge_domain_index (gbd); @@ -602,7 +603,7 @@ gbb_endpoint_fwd_reset (gbp_endpoint_t * ge) { l2fib_del_entry (ge->ge_key.gek_mac.bytes, gbd->gb_bd_index, gef->gef_itf); - gbp_itf_set_l2_input_feature (gef->gef_itf, gei, (L2INPUT_FEAT_NONE)); + gbp_itf_set_l2_input_feature (gef->gef_itf, gei, L2INPUT_FEAT_NONE); gbp_itf_set_l2_output_feature (gef->gef_itf, gei, L2OUTPUT_FEAT_NONE); gbp_itf_unlock (gef->gef_itf); @@ -681,6 +682,7 @@ gbb_endpoint_fwd_recalc (gbp_endpoint_t * ge) rewrite = NULL; grd = gbp_route_domain_get (ge->ge_key.gek_grd); fib_index = grd->grd_fib_index[pfx->fp_proto]; + gef->gef_fib_index = fib_index; bd_add_del_ip_mac (gbd->gb_bd_index, fib_proto_to_ip46 (pfx->fp_proto), &pfx->fp_addr, &ge->ge_key.gek_mac, 1); @@ -786,7 +788,12 @@ gbb_endpoint_fwd_recalc (gbp_endpoint_t * ge) } } - if (gbp_endpoint_is_local (ge) && !gbp_endpoint_is_external (ge)) + if (gbp_endpoint_is_external (ge)) + { + gbp_itf_set_l2_input_feature (gef->gef_itf, gei, + L2INPUT_FEAT_GBP_LPM_CLASSIFY); + } + else if (gbp_endpoint_is_local (ge)) { /* * non-remote endpoints (i.e. those not arriving on iVXLAN diff --git a/src/plugins/gbp/gbp_endpoint.h b/src/plugins/gbp/gbp_endpoint.h index aba1f9d498c..a0d354ab8ab 100644 --- a/src/plugins/gbp/gbp_endpoint.h +++ b/src/plugins/gbp/gbp_endpoint.h @@ -168,6 +168,11 @@ typedef struct gbp_endpoint_fwd_t_ */ sclass_t gef_sclass; + /** + * FIB index the EP is in + */ + u32 gef_fib_index; + gbp_endpoint_flags_t gef_flags; } gbp_endpoint_fwd_t; diff --git a/src/plugins/gbp/gbp_ext_itf.c b/src/plugins/gbp/gbp_ext_itf.c index 16cdaa87b77..be2d614e1fa 100644 --- a/src/plugins/gbp/gbp_ext_itf.c +++ b/src/plugins/gbp/gbp_ext_itf.c @@ -60,7 +60,6 @@ gbp_ext_itf_add (u32 sw_if_index, u32 bd_id, u32 rd_id) if (INDEX_INVALID == gxi) { - gbp_bridge_domain_t *gb; gbp_route_domain_t *gr; fib_protocol_t fproto; index_t gbi, gri; @@ -81,11 +80,11 @@ gbp_ext_itf_add (u32 sw_if_index, u32 bd_id, u32 rd_id) pool_get_zero (gbp_ext_itf_pool, gx); gxi = gx - gbp_ext_itf_pool; - gb = gbp_bridge_domain_get (gbi); gr = gbp_route_domain_get (gri); gx->gx_bd = gbi; gx->gx_rd = gri; + gx->gx_itf = sw_if_index; FOR_EACH_FIB_IP_PROTOCOL (fproto) { @@ -93,10 +92,6 @@ gbp_ext_itf_add (u32 sw_if_index, u32 bd_id, u32 rd_id) gr->grd_fib_index[fib_proto_to_dpo (fproto)]; } - gx->gx_itf = gbp_itf_add_and_lock (sw_if_index, gb->gb_bd_index); - gbp_itf_set_l2_input_feature (gx->gx_itf, (gxi | GBP_EXT_ITF_ID), - L2INPUT_FEAT_GBP_LPM_CLASSIFY); - gbp_ext_itf_db[sw_if_index] = gxi; GBP_EXT_ITF_DBG ("add: %U", format_gbp_ext_itf, gx); @@ -124,11 +119,6 @@ gbp_ext_itf_delete (u32 sw_if_index) GBP_EXT_ITF_DBG ("del: %U", format_gbp_ext_itf, gx); - gbp_itf_set_l2_input_feature (gx->gx_itf, - (gxi | GBP_EXT_ITF_ID), - L2INPUT_FEAT_NONE); - gbp_itf_unlock (gx->gx_itf); - gbp_route_domain_unlock (gx->gx_rd); gbp_bridge_domain_unlock (gx->gx_bd); diff --git a/src/plugins/gbp/gbp_itf.c b/src/plugins/gbp/gbp_itf.c index 7cfe9da0ab0..4944f278144 100644 --- a/src/plugins/gbp/gbp_itf.c +++ b/src/plugins/gbp/gbp_itf.c @@ -194,15 +194,48 @@ format_gbp_itf (u8 * s, va_list * args) gi = gbp_itf_get (gii); - s = format (s, "%U locks:%d input-feats:%U output-feats:%U", + s = format (s, "%U locks:%d bd-index:%d input-feats:%U output-feats:%U", format_vnet_sw_if_index_name, vnet_get_main (), gi->gi_sw_if_index, gi->gi_locks, + gi->gi_bd_index, format_l2_input_features, gi->gi_l2_input_fb, 0, format_l2_output_features, gi->gi_l2_output_fb, 0); return (s); } +static clib_error_t * +gbp_itf_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + u32 gii; + + vlib_cli_output (vm, "Interfaces:"); + + vec_foreach_index (gii, gbp_itfs) + { + vlib_cli_output (vm, " [%d] %U", gii, format_gbp_itf, gii); + } + + return (NULL); +} + +/*? + * Show Group Based Interfaces + * + * @cliexpar + * @cliexstart{show gbp contract} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_contract_show_node, static) = { + .path = "show gbp interface", + .short_help = "show gbp interface\n", + .function = gbp_itf_show, +}; +/* *INDENT-ON* */ + + /* * fd.io coding-style-patch-verification: ON * diff --git a/test/test_gbp.py b/test/test_gbp.py index cf9ad251d4b..4d8328474b9 100644 --- a/test/test_gbp.py +++ b/test/test_gbp.py @@ -5,7 +5,7 @@ import unittest from scapy.packet import Raw from scapy.layers.l2 import Ether, ARP, Dot1Q -from scapy.layers.inet import IP, UDP +from scapy.layers.inet import IP, UDP, ICMP from scapy.layers.inet6 import IPv6, ICMPv6ND_NS, ICMPv6NDOptSrcLLAddr, \ ICMPv6ND_NA from scapy.utils6 import in6_getnsma, in6_getnsmac @@ -19,7 +19,7 @@ from vpp_interface import VppInterface from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable, \ VppIpInterfaceAddress, VppIpInterfaceBind, find_route from vpp_l2 import VppBridgeDomain, VppBridgeDomainPort, \ - VppBridgeDomainArpEntry, VppL2FibEntry, find_bridge_domain_port + VppBridgeDomainArpEntry, VppL2FibEntry, find_bridge_domain_port, VppL2Vtr from vpp_sub_interface import VppDot1QSubint from vpp_ip import VppIpAddress, VppIpPrefix from vpp_papi import VppEnum, MACAddress @@ -67,10 +67,6 @@ class VppGbpEndpoint(VppObject): def mac(self): return str(self.vmac) - @property - def mac(self): - return self.itf.remote_mac - @property def ip4(self): return self._ip4 @@ -3197,7 +3193,12 @@ class TestGBP(VppTestCase): # vlan_100 = VppDot1QSubint(self, self.pg0, 100) vlan_100.admin_up() - ext_itf = VppGbpExtItf(self, vlan_100, bd1, rd1) + VppL2Vtr(self, vlan_100, L2_VTR_OP.L2_POP_1).add_vpp_config() + vlan_101 = VppDot1QSubint(self, self.pg0, 101) + vlan_101.admin_up() + VppL2Vtr(self, vlan_101, L2_VTR_OP.L2_POP_1).add_vpp_config() + + ext_itf = VppGbpExtItf(self, self.loop0, bd1, rd1) ext_itf.add_vpp_config() # @@ -3209,16 +3210,45 @@ class TestGBP(VppTestCase): self.pg2.local_ip4) vx_tun_l3.add_vpp_config() + # + # External Endpoints + # + eep1 = VppGbpEndpoint(self, vlan_100, + epg_220, None, + "10.0.0.1", "11.0.0.1", + "2001:10::1", "3001::1", + ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL) + eep1.add_vpp_config() + eep2 = VppGbpEndpoint(self, vlan_101, + epg_220, None, + "10.0.0.2", "11.0.0.2", + "2001:10::2", "3001::2", + ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL) + eep2.add_vpp_config() + + # + # A remote endpoint + # + rep = VppGbpEndpoint(self, vx_tun_l3, + epg_220, None, + "10.0.0.101", "11.0.0.101", + "2001:10::101", "3001::101", + ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE, + self.pg7.local_ip4, + self.pg7.remote_ip4, + mac=None) + rep.add_vpp_config() + # # packets destined to unknown addresses in the BVI's subnet # are ARP'd for # - p4 = (Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) / + p4 = (Ether(src=eep1.mac, dst=str(self.router_mac)) / Dot1Q(vlan=100) / IP(src="10.0.0.1", dst="10.0.0.88") / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) - p6 = (Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) / + p6 = (Ether(src=eep1.mac, dst=str(self.router_mac)) / Dot1Q(vlan=100) / IPv6(src="2001:10::1", dst="2001:10::88") / UDP(sport=1234, dport=1234) / @@ -3243,29 +3273,6 @@ class TestGBP(VppTestCase): self.assertTrue(inner.haslayer(ARP)) - # - # An external Endpoint - # - eep = VppGbpEndpoint(self, vlan_100, - epg_220, None, - "10.0.0.1", "11.0.0.1", - "2001:10::1", "3001::1", - ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL) - eep.add_vpp_config() - - # - # A remote endpoint - # - rep = VppGbpEndpoint(self, vx_tun_l3, - epg_220, None, - "10.0.0.101", "11.0.0.101", - "2001:10::101", "3001::101", - ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE, - self.pg7.local_ip4, - self.pg7.remote_ip4, - mac=None) - rep.add_vpp_config() - # # remote to external # @@ -3282,12 +3289,42 @@ class TestGBP(VppTestCase): rxs = self.send_and_expect(self.pg7, p * 1, self.pg0) + # + # local EP pings router + # + p = (Ether(src=eep1.mac, dst=str(self.router_mac)) / + Dot1Q(vlan=100) / + IP(src=eep1.ip4.address, dst="10.0.0.128") / + ICMP(type='echo-request')) + + rxs = self.send_and_expect(self.pg0, p * 1, self.pg0) + + for rx in rxs: + self.assertEqual(rx[Ether].src, str(self.router_mac)) + self.assertEqual(rx[Ether].dst, eep1.mac) + self.assertEqual(rx[Dot1Q].vlan, 100) + + # + # local EP pings other local EP + # + p = (Ether(src=eep1.mac, dst=eep2.mac) / + Dot1Q(vlan=100) / + IP(src=eep1.ip4.address, dst=eep2.ip4.address) / + ICMP(type='echo-request')) + + rxs = self.send_and_expect(self.pg0, p * 1, self.pg0) + + for rx in rxs: + self.assertEqual(rx[Ether].src, eep1.mac) + self.assertEqual(rx[Ether].dst, eep2.mac) + self.assertEqual(rx[Dot1Q].vlan, 101) + # # A subnet reachable through the external EP # ip_220 = VppIpRoute(self, "10.220.0.0", 24, - [VppRoutePath(eep.ip4.address, - eep.epg.bvi.sw_if_index)], + [VppRoutePath(eep1.ip4.address, + eep1.epg.bvi.sw_if_index)], table_id=t4.table_id) ip_220.add_vpp_config() @@ -3310,10 +3347,41 @@ class TestGBP(VppTestCase): rxs = self.send_and_expect(self.pg7, p * 1, self.pg0) + # + # A subnet reachable through the external EP + # + ip_221 = VppIpRoute(self, "10.221.0.0", 24, + [VppRoutePath(eep2.ip4.address, + eep2.epg.bvi.sw_if_index)], + table_id=t4.table_id) + ip_221.add_vpp_config() + + l3o_221 = VppGbpSubnet( + self, rd1, "10.221.0.0", 24, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT, + epg=epg_220) + l3o_221.add_vpp_config() + + # + # ping between hosts in remote subnets + # + p = (Ether(src=eep1.mac, dst=str(self.router_mac)) / + Dot1Q(vlan=100) / + IP(src="10.220.0.1", dst="10.221.0.1") / + ICMP(type='echo-request')) + + rxs = self.send_and_expect(self.pg0, p * 1, self.pg0) + + for rx in rxs: + self.assertEqual(rx[Ether].src, str(self.router_mac)) + self.assertEqual(rx[Ether].dst, eep2.mac) + self.assertEqual(rx[Dot1Q].vlan, 101) + # # cleanup # self.pg7.unconfig_ip4() + vlan_100.set_vtr(L2_VTR_OP.L2_DISABLED) if __name__ == '__main__': diff --git a/test/vpp_l2.py b/test/vpp_l2.py index 45b7d69dd01..dc73e4238f3 100644 --- a/test/vpp_l2.py +++ b/test/vpp_l2.py @@ -208,3 +208,32 @@ class VppL2FibEntry(VppObject): def object_id(self): return "L2-Fib-Entry-%s-%s-%s" % (self.bd, self.mac, self.itf) + + +class VppL2Vtr(VppObject): + + def __init__(self, test, itf, op): + self._test = test + self.itf = itf + self.op = op + + def add_vpp_config(self): + self.itf.set_vtr(self.op) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self.itf.set_vtr(L2_VTR_OP.L2_DISABLED) + + def query_vpp_config(self): + ds = self._test.vapi.sw_interface_dump() + d = self.itf.get_interface_config_from_dump(ds) + + if d is not None: + return (d.vtr_op == self.op) + return False + + def __str__(self): + return self.object_id() + + def object_id(self): + return "L2-vtr-%s-%d" % (str(self.itf), self.op) -- 2.16.6