Punt Infra

A punt/exception path that provides:
 1) clients that use the infra
 2) clients can create punt reasons
 3) clients can register to recieve packets that are punted
    for a given reason to be sent to the desired node.
 4) nodes which punt packets fill in the {reason,protocol} of the
    buffere (in the meta-data) and send to the new node "punt-dispatch"
 5) punt-dispatch sends packets to the registered nodes or drops

Change-Id: Ia4f144337f1387cbe585b4f375d0842aefffcde5
Signed-off-by: Neale Ranns <nranns@cisco.com>
This commit is contained in:
Neale Ranns
2018-09-28 15:16:14 +00:00
committed by Damjan Marion
parent 1f4e1cbf57
commit 76b5649d07
14 changed files with 1706 additions and 30 deletions
+19 -19
View File
@@ -22,6 +22,7 @@
#include <vnet/vxlan-gbp/vxlan_gbp.h>
#include <vlibmemory/api.h>
#include <vnet/fib/fib_table.h>
#include <vlib/punt.h>
/**
* A reference to a VXLAN-GBP tunnel created as a child/dependent tunnel
@@ -66,6 +67,10 @@ index_t *gbp_vxlan_tunnel_db;
*/
index_t *vxlan_tunnel_ref_db;
/**
* handle registered with the ;unt infra
*/
static vlib_punt_hdl_t punt_hdl;
static char *gbp_vxlan_tunnel_layer_strings[] = {
#define _(n,s) [GBP_VXLAN_TUN_##n] = s,
@@ -672,28 +677,23 @@ VLIB_CLI_COMMAND (gbp_vxlan_show_node, static) = {
static clib_error_t *
gbp_vxlan_init (vlib_main_t * vm)
{
u32 slot4;
vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "gbp-vxlan4");
/*
* insert ourselves into the VXLAN-GBP arc to collect the no-tunnel
* packets.
*/
slot4 = vlib_node_add_next_with_slot (vm,
vxlan4_gbp_input_node.index,
node->index,
VXLAN_GBP_INPUT_NEXT_NO_TUNNEL);
ASSERT (slot4 == VXLAN_GBP_INPUT_NEXT_NO_TUNNEL);
/* slot6 = vlib_node_add_next_with_slot (vm, */
/* vxlan6_gbp_input_node.index, */
/* gbp_vxlan6_input_node.index, */
/* VXLAN_GBP_INPUT_NEXT_NO_TUNNEL); */
/* ASSERT (slot6 == VXLAN_GBP_INPUT_NEXT_NO_TUNNEL); */
vxlan_gbp_main_t *vxm = &vxlan_gbp_main;
clib_error_t *error;
gt_logger = vlib_log_register_class ("gbp", "tun");
return (NULL);
if ((error = vlib_call_init_function (vm, punt_init)))
return error;
if ((error = vlib_call_init_function (vm, vxlan_gbp_init)))
return error;
punt_hdl = vlib_punt_client_register ("gbp-vxlan");
vlib_punt_register (punt_hdl,
vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP4],
"gbp-vxlan4");
return (error);
}
VLIB_INIT_FUNCTION (gbp_vxlan_init);
+1
View File
@@ -24,6 +24,7 @@ add_vpp_plugin(unittest
ipsec_test.c
interface_test.c
mfib_test.c
punt_test.c
session_test.c
string_test.c
tcp_test.c
File diff suppressed because it is too large Load Diff
+4
View File
@@ -63,6 +63,8 @@ add_vpp_library(vlib
node_format.c
pci/pci.c
physmem.c
punt.c
punt_node.c
threads.c
threads_cli.c
trace.c
@@ -77,6 +79,7 @@ add_vpp_library(vlib
MULTIARCH_SOURCES
drop.c
punt_node.c
INSTALL_HEADERS
buffer_funcs.h
@@ -102,6 +105,7 @@ add_vpp_library(vlib
pci/pci.h
physmem_funcs.h
physmem.h
punt.h
threads.h
trace_funcs.h
trace.h
+9 -2
View File
@@ -139,8 +139,15 @@ typedef union
* VLIB_BUFFER_NEXT_PRESENT flag is set. */
u32 next_buffer;
/** Used by feature subgraph arcs to visit enabled feature nodes */
u32 current_config_index;
/** The following fields can be in a union because once a packet enters
* the punt path, it is no longer on a feature arc */
union
{
/** Used by feature subgraph arcs to visit enabled feature nodes */
u32 current_config_index;
/* the reason the packet once punted */
u32 punt_reason;
};
/** Opaque data used by sub-graphs for their own purposes. */
u32 opaque[10];
+629
View File
File diff suppressed because it is too large Load Diff
+93
View File
@@ -0,0 +1,93 @@
/*
* Copyright (c) 2018 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.
*/
#ifndef __PUNT_H__
#define __PUNT_H__
#include <vlib/vlib.h>
/**
* The 'syatem' defined punt reasons.
* Only add to this list reasons defined and used within the vlib subsystem.
* To define new reasons in e.g. plgins, use punt_reason_alloc()
*/
typedef enum vlib_punt_reason_t_
{
PUNT_N_REASONS,
} vlib_punt_reason_t;
/**
* @brief Format a punt reason
*/
extern u8 *format_vlib_punt_reason (u8 * s, va_list * args);
/**
* Typedef for a client handle
*/
typedef int vlib_punt_hdl_t;
/**
* @brief Register a new clinet
*
* @param who - The name of the client
*
* @retrun the handle the punt infra allocated for this client that must
* be used when the client wishes to use the infra
*/
vlib_punt_hdl_t vlib_punt_client_register (const char *who);
/**
* Allocate a new punt reason
*/
extern int vlib_punt_reason_alloc (vlib_punt_hdl_t client,
const char *reason_name,
vlib_punt_reason_t * reason);
/**
* @brief Register a node to receive particular punted buffers
*
* @paran client - The registered client registering for the packets
* @param reason - The reason the packet was punted
* @param node - The node to which the punted packets will be sent
*/
extern int vlib_punt_register (vlib_punt_hdl_t client,
vlib_punt_reason_t reason, const char *node);
extern int vlib_punt_unregister (vlib_punt_hdl_t client,
vlib_punt_reason_t pr, const char *node);
/**
* FOR USE IN THE DP ONLY
*
* Arc[s] to follow for each reason
*/
extern u16 **punt_dp_db;
/**
* FOR USE IN THE DP ONLY
*
* Per-reason counters
*/
extern vlib_combined_counter_main_t punt_counters;
#endif
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/
File diff suppressed because it is too large Load Diff
+1
View File
@@ -58,6 +58,7 @@ typedef u32 vlib_log_class_t;
#include <vlib/error.h>
#include <vlib/init.h>
#include <vlib/node.h>
#include <vlib/punt.h>
#include <vlib/trace.h>
#include <vlib/log.h>
+2 -2
View File
@@ -1059,7 +1059,7 @@ VLIB_CLI_COMMAND (show_punt_socket_registration_command, static) =
/* *INDENT-ON* */
clib_error_t *
punt_init (vlib_main_t * vm)
ip_punt_init (vlib_main_t * vm)
{
punt_main_t *pm = &punt_main;
@@ -1077,7 +1077,7 @@ punt_init (vlib_main_t * vm)
return 0;
}
VLIB_INIT_FUNCTION (punt_init);
VLIB_INIT_FUNCTION (ip_punt_init);
#endif /* CLIB_MARCH_VARIANT */
static clib_error_t *
+22 -4
View File
@@ -16,7 +16,7 @@
*/
#include <vlib/vlib.h>
#include <vnet/pg/pg.h>
#include <vnet/vxlan-gbp/vxlan_gbp.h>
typedef struct
@@ -309,7 +309,13 @@ vxlan_gbp_input (vlib_main_t * vm,
else
{
error0 = VXLAN_GBP_ERROR_NO_SUCH_TUNNEL;
next0 = VXLAN_GBP_INPUT_NEXT_NO_TUNNEL;
next0 = VXLAN_GBP_INPUT_NEXT_PUNT;
if (is_ip4)
b0->punt_reason =
vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP4];
else
b0->punt_reason =
vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP6];
}
b0->error = node->errors[error0];
}
@@ -342,7 +348,13 @@ vxlan_gbp_input (vlib_main_t * vm,
else
{
error1 = VXLAN_GBP_ERROR_NO_SUCH_TUNNEL;
next1 = VXLAN_GBP_INPUT_NEXT_NO_TUNNEL;
next1 = VXLAN_GBP_INPUT_NEXT_PUNT;
if (is_ip4)
b1->punt_reason =
vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP4];
else
b1->punt_reason =
vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP6];
}
b1->error = node->errors[error1];
}
@@ -444,7 +456,13 @@ vxlan_gbp_input (vlib_main_t * vm,
else
{
error0 = VXLAN_GBP_ERROR_NO_SUCH_TUNNEL;
next0 = VXLAN_GBP_INPUT_NEXT_NO_TUNNEL;
next0 = VXLAN_GBP_INPUT_NEXT_PUNT;
if (is_ip4)
b0->punt_reason =
vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP4];
else
b0->punt_reason =
vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP6];
}
b0->error = node->errors[error0];
}
+15 -2
View File
@@ -28,7 +28,7 @@
*
* VXLAN GBP provides the features of vxlan and carry group policy id.
*/
static vlib_punt_hdl_t punt_hdl;
vxlan_gbp_main_t vxlan_gbp_main;
@@ -1144,10 +1144,14 @@ clib_error_t *
vxlan_gbp_init (vlib_main_t * vm)
{
vxlan_gbp_main_t *vxm = &vxlan_gbp_main;
clib_error_t *error;
vxm->vnet_main = vnet_get_main ();
vxm->vlib_main = vm;
if ((error = vlib_call_init_function (vm, punt_init)))
return (error);
/* initialize the ip6 hash */
clib_bihash_init_16_8 (&vxm->vxlan4_gbp_tunnel_by_key, "vxlan4-gbp",
VXLAN_GBP_HASH_NUM_BUCKETS,
@@ -1162,7 +1166,16 @@ vxlan_gbp_init (vlib_main_t * vm)
fib_node_register_type (FIB_NODE_TYPE_VXLAN_GBP_TUNNEL, &vxlan_gbp_vft);
return 0;
punt_hdl = vlib_punt_client_register ("vxlan-gbp");
vlib_punt_reason_alloc (punt_hdl,
"VXLAN-GBP-no-such-v4-tunnel",
&vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP4]);
vlib_punt_reason_alloc (punt_hdl,
"VXLAN-GBP-no-such-v6-tunnel",
&vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP6]);
return (error);
}
VLIB_INIT_FUNCTION (vxlan_gbp_init);
+6 -1
View File
@@ -132,7 +132,7 @@ typedef struct
#define foreach_vxlan_gbp_input_next \
_(DROP, "error-drop") \
_(NO_TUNNEL, "error-punt") \
_(PUNT, "punt-dispatch") \
_(L2_INPUT, "l2-input") \
_(IP4_INPUT, "ip4-input") \
_(IP6_INPUT, "ip6-input")
@@ -189,6 +189,11 @@ typedef struct
/* Record used instances */
uword *instance_used;
/**
* Punt reasons for no such tunnel
*/
vlib_punt_reason_t punt_no_such_tunnel[FIB_PROTOCOL_IP_MAX];
} vxlan_gbp_main_t;
extern vxlan_gbp_main_t vxlan_gbp_main;
+176
View File
@@ -23,6 +23,9 @@ from scapy.layers.inet6 import IPv6, ICMPv6DestUnreach
import six
from framework import VppTestCase, VppTestRunner
from vpp_ip import DpoProto
from vpp_ip_route import VppIpRoute, VppRoutePath
# Format MAC Address
def get_mac_addr(bytes_addr):
@@ -671,5 +674,178 @@ class TestIP6PuntSocket(TestPuntSocket):
punts = self.vapi.punt_socket_dump(is_ip6=1)
self.assertEqual(len(punts), 0)
class TestPunt(VppTestCase):
""" Punt Test Case """
@classmethod
def setUpClass(cls):
super(TestPunt, cls).setUpClass()
@classmethod
def tearDownClass(cls):
super(TestPunt, cls).tearDownClass()
def setUp(self):
super(TestPunt, self).setUp()
self.create_pg_interfaces(range(4))
for i in self.pg_interfaces:
i.admin_up()
i.config_ip4()
i.resolve_arp()
i.config_ip6()
i.resolve_ndp()
def tearDown(self):
for i in self.pg_interfaces:
i.unconfig_ip4()
i.unconfig_ip6()
i.ip6_disable()
i.admin_down()
super(TestPunt, self).tearDown()
def test_punt(self):
""" Excpetion Path testing """
#
# Using the test CLI we will hook in a exception path to
# send ACL deny packets out of pg0 and pg1.
# the ACL is src,dst = 1.1.1.1,1.1.1.2
#
ip_1_1_1_2 = VppIpRoute(self, "1.1.1.2", 32,
[VppRoutePath(self.pg3.remote_ip4,
self.pg3.sw_if_index)])
ip_1_1_1_2.add_vpp_config()
ip_1_2 = VppIpRoute(self, "1::2", 128,
[VppRoutePath(self.pg3.remote_ip6,
self.pg3.sw_if_index,
proto=DpoProto.DPO_PROTO_IP6)],
is_ip6=1)
ip_1_2.add_vpp_config()
p4 = (Ether(src=self.pg2.remote_mac,
dst=self.pg2.local_mac) /
IP(src="1.1.1.1", dst="1.1.1.2") /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
p6 = (Ether(src=self.pg2.remote_mac,
dst=self.pg2.local_mac) /
IPv6(src="1::1", dst="1::2") /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
self.send_and_expect(self.pg2, p4*1, self.pg3)
self.send_and_expect(self.pg2, p6*1, self.pg3)
#
# apply the punting features
#
self.vapi.cli("test punt pg2")
#
# pkts now dropped
#
self.send_and_assert_no_replies(self.pg2, p4*65)
self.send_and_assert_no_replies(self.pg2, p6*65)
#
# Check state:
# 1 - node error counters
# 2 - per-reason counters
# 2, 3 are the index of the assigned punt reason
#
stats = self.statistics.get_counter(
"/err/punt-dispatch/No registrations")
self.assertEqual(stats, 130)
stats = self.statistics.get_counter("/net/punt")
self.assertEqual(stats[0][2]['packets'], 65)
self.assertEqual(stats[0][3]['packets'], 65)
#
# use the test CLI to test a client that punts exception
# packets out of pg0
#
self.vapi.cli("test punt pg0 %s" % self.pg0.remote_ip4)
self.vapi.cli("test punt pg0 %s" % self.pg0.remote_ip6)
rx4s = self.send_and_expect(self.pg2, p4*65, self.pg0)
rx6s = self.send_and_expect(self.pg2, p6*65, self.pg0)
#
# check the packets come out IP unmodified but destined to pg0 host
#
for rx in rx4s:
self.assertEqual(rx[Ether].dst, self.pg0.remote_mac)
self.assertEqual(rx[Ether].src, self.pg0.local_mac)
self.assertEqual(p4[IP].dst, rx[IP].dst)
self.assertEqual(p4[IP].ttl, rx[IP].ttl)
for rx in rx6s:
self.assertEqual(rx[Ether].dst, self.pg0.remote_mac)
self.assertEqual(rx[Ether].src, self.pg0.local_mac)
self.assertEqual(p6[IPv6].dst, rx[IPv6].dst)
self.assertEqual(p6[IPv6].hlim, rx[IPv6].hlim)
stats = self.statistics.get_counter("/net/punt")
self.assertEqual(stats[0][2]['packets'], 2*65)
self.assertEqual(stats[0][3]['packets'], 2*65)
#
# add another registration for the same reason to send packets
# out of pg1
#
self.vapi.cli("test punt pg1 %s" % self.pg1.remote_ip4)
self.vapi.cli("test punt pg1 %s" % self.pg1.remote_ip6)
self.vapi.cli("clear trace")
self.pg2.add_stream(p4 * 65)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rxd = self.pg0.get_capture(65)
for rx in rxd:
self.assertEqual(rx[Ether].dst, self.pg0.remote_mac)
self.assertEqual(rx[Ether].src, self.pg0.local_mac)
self.assertEqual(p4[IP].dst, rx[IP].dst)
self.assertEqual(p4[IP].ttl, rx[IP].ttl)
rxd = self.pg1.get_capture(65)
for rx in rxd:
self.assertEqual(rx[Ether].dst, self.pg1.remote_mac)
self.assertEqual(rx[Ether].src, self.pg1.local_mac)
self.assertEqual(p4[IP].dst, rx[IP].dst)
self.assertEqual(p4[IP].ttl, rx[IP].ttl)
self.vapi.cli("clear trace")
self.pg2.add_stream(p6 * 65)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rxd = self.pg0.get_capture(65)
for rx in rxd:
self.assertEqual(rx[Ether].dst, self.pg0.remote_mac)
self.assertEqual(rx[Ether].src, self.pg0.local_mac)
self.assertEqual(p6[IPv6].dst, rx[IPv6].dst)
self.assertEqual(p6[IPv6].hlim, rx[IPv6].hlim)
rxd = self.pg1.get_capture(65)
for rx in rxd:
self.assertEqual(rx[Ether].dst, self.pg1.remote_mac)
self.assertEqual(rx[Ether].src, self.pg1.local_mac)
self.assertEqual(p6[IPv6].dst, rx[IPv6].dst)
self.assertEqual(p6[IPv6].hlim, rx[IPv6].hlim)
stats = self.statistics.get_counter("/net/punt")
self.assertEqual(stats[0][2]['packets'], 3*65)
self.assertEqual(stats[0][3]['packets'], 3*65)
self.logger.info(self.vapi.cli("show vlib graph punt-dispatch"))
self.logger.info(self.vapi.cli("show punt client"))
self.logger.info(self.vapi.cli("show punt reason"))
self.logger.info(self.vapi.cli("show punt stats"))
self.logger.info(self.vapi.cli("show punt db"))
self.vapi.cli("test punt clear")
if __name__ == '__main__':
unittest.main(testRunner=VppTestRunner)