summary |
shortlog |
log |
commit | commitdiff |
review |
tree
raw |
patch |
inline | side by side (from parent 1:
bc91e86)
Type: improvement
For error conditions, such as TTL expired, dest unreach, etc, Rate limit the sending of ICMP error messages.
The rate limiting is done based on src,dst IP address of the received packet.
the rate limit has been chosen, somewhat arbitrarily, to be 1e-3. This is the same limit as the ARP throttling.
Signed-off-by: Neale Ranns <neale@graphiant.com>
Change-Id: I4a0b791cde8c941a9bf37de6aa5da56779d3cef4
12 files changed:
#include <vnet/ip/ip.h>
#include <vnet/pg/pg.h>
#include <vnet/ip/ip_sas.h>
#include <vnet/ip/ip.h>
#include <vnet/pg/pg.h>
#include <vnet/ip/ip_sas.h>
+#include <vnet/util/throttle.h>
static char *icmp_error_strings[] = {
#define _(f,s) s,
static char *icmp_error_strings[] = {
#define _(f,s) s,
+/** ICMP throttling */
+static throttle_t icmp_throttle;
+
static u8 *
format_ip4_icmp_type_and_code (u8 * s, va_list * args)
{
static u8 *
format_ip4_icmp_type_and_code (u8 * s, va_list * args)
{
u32 *from, *to_next;
uword n_left_from, n_left_to_next;
ip4_icmp_error_next_t next_index;
u32 *from, *to_next;
uword n_left_from, n_left_to_next;
ip4_icmp_error_next_t next_index;
+ u32 thread_index = vm->thread_index;
from = vlib_frame_vector_args (frame);
n_left_from = frame->n_vectors;
next_index = node->cached_next_index;
from = vlib_frame_vector_args (frame);
n_left_from = frame->n_vectors;
next_index = node->cached_next_index;
+ u64 seed = throttle_seed (&icmp_throttle, thread_index, vlib_time_now (vm));
+
if (node->flags & VLIB_NODE_FLAG_TRACE)
vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
/* stride */ 1,
if (node->flags & VLIB_NODE_FLAG_TRACE)
vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
/* stride */ 1,
ip_csum_t sum;
org_p0 = vlib_get_buffer (vm, org_pi0);
ip_csum_t sum;
org_p0 = vlib_get_buffer (vm, org_pi0);
+ ip0 = vlib_buffer_get_current (org_p0);
+
+ /* Rate limit based on the src,dst addresses in the original packet
+ */
+ u64 r0 =
+ (u64) ip0->dst_address.as_u32 << 32 | ip0->src_address.as_u32;
+
+ if (throttle_check (&icmp_throttle, thread_index, r0, seed))
+ {
+ vlib_error_count (vm, node->node_index, ICMP4_ERROR_DROP, 1);
+ from += 1;
+ n_left_from -= 1;
+ continue;
+ }
+
p0 = vlib_buffer_copy_no_chain (vm, org_p0, &pi0);
if (!p0 || pi0 == ~0) /* Out of buffers */
continue;
p0 = vlib_buffer_copy_no_chain (vm, org_p0, &pi0);
if (!p0 || pi0 == ~0) /* Out of buffers */
continue;
n_left_from -= 1;
n_left_to_next -= 1;
n_left_from -= 1;
n_left_to_next -= 1;
- ip0 = vlib_buffer_get_current (p0);
sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
+ vlib_buffer_copy_trace_flag (vm, p0, pi0);
+
/* Add IP header and ICMPv4 header including a 4 byte data field */
vlib_buffer_advance (p0,
-sizeof (ip4_header_t) -
/* Add IP header and ICMPv4 header including a 4 byte data field */
vlib_buffer_advance (p0,
-sizeof (ip4_header_t) -
ICMP_INPUT_NEXT_ERROR,
sizeof (cm->ip4_input_next_index_by_type));
ICMP_INPUT_NEXT_ERROR,
sizeof (cm->ip4_input_next_index_by_type));
+ vlib_thread_main_t *tm = &vlib_thread_main;
+ u32 n_vlib_mains = tm->n_vlib_mains;
+
+ throttle_init (&icmp_throttle, n_vlib_mains, 1e-3);
+
#include <vnet/ip/ip.h>
#include <vnet/pg/pg.h>
#include <vnet/ip/ip_sas.h>
#include <vnet/ip/ip.h>
#include <vnet/pg/pg.h>
#include <vnet/ip/ip_sas.h>
+#include <vnet/util/throttle.h>
+
+/** ICMP throttling */
+static throttle_t icmp_throttle;
static u8 *
format_ip6_icmp_type_and_code (u8 * s, va_list * args)
static u8 *
format_ip6_icmp_type_and_code (u8 * s, va_list * args)
u32 *from, *to_next;
uword n_left_from, n_left_to_next;
ip6_icmp_error_next_t next_index;
u32 *from, *to_next;
uword n_left_from, n_left_to_next;
ip6_icmp_error_next_t next_index;
+ u32 thread_index = vm->thread_index;
from = vlib_frame_vector_args (frame);
n_left_from = frame->n_vectors;
next_index = node->cached_next_index;
from = vlib_frame_vector_args (frame);
n_left_from = frame->n_vectors;
next_index = node->cached_next_index;
+ u64 seed = throttle_seed (&icmp_throttle, thread_index, vlib_time_now (vm));
+
if (node->flags & VLIB_NODE_FLAG_TRACE)
vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
/* stride */ 1,
if (node->flags & VLIB_NODE_FLAG_TRACE)
vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
/* stride */ 1,
int bogus_length;
org_p0 = vlib_get_buffer (vm, org_pi0);
int bogus_length;
org_p0 = vlib_get_buffer (vm, org_pi0);
+ ip0 = vlib_buffer_get_current (org_p0);
+
+ /* Rate limit based on the src,dst addresses in the original packet
+ */
+ u64 r0 = (ip6_address_hash_to_u64 (&ip0->dst_address) ^
+ ip6_address_hash_to_u64 (&ip0->src_address));
+
+ if (throttle_check (&icmp_throttle, thread_index, r0, seed))
+ {
+ vlib_error_count (vm, node->node_index, ICMP4_ERROR_DROP, 1);
+ from += 1;
+ n_left_from -= 1;
+ continue;
+ }
+
p0 = vlib_buffer_copy_no_chain (vm, org_p0, &pi0);
if (!p0 || pi0 == ~0) /* Out of buffers */
continue;
p0 = vlib_buffer_copy_no_chain (vm, org_p0, &pi0);
if (!p0 || pi0 == ~0) /* Out of buffers */
continue;
n_left_from -= 1;
n_left_to_next -= 1;
n_left_from -= 1;
n_left_to_next -= 1;
- ip0 = vlib_buffer_get_current (p0);
sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
+ vlib_buffer_copy_trace_flag (vm, p0, pi0);
+
/* Add IP header and ICMPv6 header including a 4 byte data field */
vlib_buffer_advance (p0,
-(sizeof (ip6_header_t) +
/* Add IP header and ICMPv6 header including a 4 byte data field */
vlib_buffer_advance (p0,
-(sizeof (ip6_header_t) +
cm->min_valid_length_by_type[ICMP6_redirect] =
sizeof (icmp6_redirect_header_t);
cm->min_valid_length_by_type[ICMP6_redirect] =
sizeof (icmp6_redirect_header_t);
+ vlib_thread_main_t *tm = &vlib_thread_main;
+ u32 n_vlib_mains = tm->n_vlib_mains;
+
+ throttle_init (&icmp_throttle, n_vlib_mains, 1e-3);
+
self.logger.debug(self.vapi.cli("show trace"))
return rxs
self.logger.debug(self.vapi.cli("show trace"))
return rxs
+ def send_and_expect_some(self, intf, pkts, output,
+ worker=None,
+ trace=True):
+ self.pg_send(intf, pkts, worker=worker, trace=trace)
+ rx = output._get_capture(1)
+ if trace:
+ self.logger.debug(self.vapi.cli("show trace"))
+ self.assertTrue(len(rx) > 0)
+ self.assertTrue(len(rx) < len(pkts))
+ return rx
+
def send_and_expect_only(self, intf, pkts, output, timeout=None,
stats_diff=None):
if stats_diff:
def send_and_expect_only(self, intf, pkts, output, timeout=None,
stats_diff=None):
if stats_diff:
TCP(sport=1234, dport=1234) /
Raw(b'\xa5' * 65200))
TCP(sport=1234, dport=1234) /
Raw(b'\xa5' * 65200))
- rxs = self.send_and_expect(self.pg2, 5*[p63], self.pg2, 5)
+ rxs = self.send_and_expect_some(self.pg2, 5*[p63], self.pg2, 5)
for rx in rxs:
self.assertEqual(rx[Ether].src, self.pg2.local_mac)
self.assertEqual(rx[Ether].dst, self.pg2.remote_mac)
for rx in rxs:
self.assertEqual(rx[Ether].src, self.pg2.local_mac)
self.assertEqual(rx[Ether].dst, self.pg2.remote_mac)
UDP(sport=1234, dport=1234) /
Raw(b'\xa5' * 100))
UDP(sport=1234, dport=1234) /
Raw(b'\xa5' * 100))
- rx = self.send_and_expect(self.pg0, p_ttl * NUM_PKTS, self.pg0)
+ rxs = self.send_and_expect_some(self.pg0, p_ttl * NUM_PKTS, self.pg0)
- rx = rx[0]
- icmp = rx[ICMP]
-
- self.assertEqual(icmptypes[icmp.type], "time-exceeded")
- self.assertEqual(icmpcodes[icmp.type][icmp.code],
- "ttl-zero-during-transit")
- self.assertEqual(icmp.src, self.pg0.remote_ip4)
- self.assertEqual(icmp.dst, self.pg1.remote_ip4)
+ for rx in rxs:
+ icmp = rx[ICMP]
+ self.assertEqual(icmptypes[icmp.type], "time-exceeded")
+ self.assertEqual(icmpcodes[icmp.type][icmp.code],
+ "ttl-zero-during-transit")
+ self.assertEqual(icmp.src, self.pg0.remote_ip4)
+ self.assertEqual(icmp.dst, self.pg1.remote_ip4)
self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [1500, 0, 0, 0])
self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [1500, 0, 0, 0])
- rx = self.send_and_expect(self.pg0, p_mtu * NUM_PKTS, self.pg0)
- rx = rx[0]
- icmp = rx[ICMP]
+ rxs = self.send_and_expect_some(self.pg0, p_mtu * NUM_PKTS, self.pg0)
- self.assertEqual(icmptypes[icmp.type], "dest-unreach")
- self.assertEqual(icmpcodes[icmp.type][icmp.code],
- "fragmentation-needed")
- self.assertEqual(icmp.src, self.pg0.remote_ip4)
- self.assertEqual(icmp.dst, self.pg1.remote_ip4)
+ for rx in rxs:
+ icmp = rx[ICMP]
+ self.assertEqual(icmptypes[icmp.type], "dest-unreach")
+ self.assertEqual(icmpcodes[icmp.type][icmp.code],
+ "fragmentation-needed")
+ self.assertEqual(icmp.src, self.pg0.remote_ip4)
+ self.assertEqual(icmp.dst, self.pg1.remote_ip4)
self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [2500, 0, 0, 0])
rx = self.send_and_expect(self.pg0, p_mtu * NUM_PKTS, self.pg1)
self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [2500, 0, 0, 0])
rx = self.send_and_expect(self.pg0, p_mtu * NUM_PKTS, self.pg1)
inet6.UDP(sport=1234, dport=1234) /
Raw(b'\xa5' * 100))
inet6.UDP(sport=1234, dport=1234) /
Raw(b'\xa5' * 100))
- rx = self.send_and_expect(self.pg0, p_version * NUM_PKTS, self.pg0)
- rx = rx[0]
- icmp = rx[ICMPv6TimeExceeded]
+ rxs = self.send_and_expect_some(self.pg0,
+ p_version * NUM_PKTS,
+ self.pg0)
- # 0: "hop limit exceeded in transit",
- self.assertEqual((icmp.type, icmp.code), (3, 0))
+ for rx in rxs:
+ icmp = rx[ICMPv6TimeExceeded]
+ # 0: "hop limit exceeded in transit",
+ self.assertEqual((icmp.type, icmp.code), (3, 0))
icmpv6_data = '\x0a' * 18
all_0s = "::"
icmpv6_data = '\x0a' * 18
all_0s = "::"
def verify_capture_ip6_icmp(self, src_if, capture, sent):
try:
def verify_capture_ip6_icmp(self, src_if, capture, sent):
try:
- self.assertEqual(len(capture), len(sent))
+ # rate limited ICMP
+ self.assertTrue(len(capture) <= len(sent))
for i in range(len(capture)):
tx = sent[i]
for i in range(len(capture)):
tx = sent[i]
[VppMplsLabel(333, ttl=64)],
dst_ip=self.pg1.remote_ip6,
hlim=1)
[VppMplsLabel(333, ttl=64)],
dst_ip=self.pg1.remote_ip6,
hlim=1)
- rx = self.send_and_expect(self.pg0, tx, self.pg0)
+ rx = self.send_and_expect_some(self.pg0, tx, self.pg0)
self.verify_capture_ip6_icmp(self.pg0, rx, tx)
#
self.verify_capture_ip6_icmp(self.pg0, rx, tx)
#
tx = self.create_stream_labelled_ip6(self.pg0, [VppMplsLabel(334)],
dst_ip=self.pg1.remote_ip6,
hlim=0)
tx = self.create_stream_labelled_ip6(self.pg0, [VppMplsLabel(334)],
dst_ip=self.pg1.remote_ip6,
hlim=0)
- rx = self.send_and_expect(self.pg0, tx, self.pg0)
+ rx = self.send_and_expect_some(self.pg0, tx, self.pg0)
self.verify_capture_ip6_icmp(self.pg0, rx, tx)
#
self.verify_capture_ip6_icmp(self.pg0, rx, tx)
#
[VppMplsLabel(34)],
dst_ip="ff01::1",
hlim=1)
[VppMplsLabel(34)],
dst_ip="ff01::1",
hlim=1)
- rx = self.send_and_expect(self.pg0, tx, self.pg0)
+ rx = self.send_and_expect_some(self.pg0, tx, self.pg0)
self.verify_capture_ip6_icmp(self.pg0, rx, tx)
#
self.verify_capture_ip6_icmp(self.pg0, rx, tx)
#
n = icmp4_reply.__class__(icmp4_reply)
s = bytes(icmp4_reply)
icmp4_reply = s[0:576]
n = icmp4_reply.__class__(icmp4_reply)
s = bytes(icmp4_reply)
icmp4_reply = s[0:576]
- rx = self.send_and_expect(self.pg0, p4*11, self.pg0)
+ rx = self.send_and_expect_some(self.pg0, p4*11, self.pg0)
for p in rx:
# p.show2()
# n.show2()
for p in rx:
# p.show2()
# n.show2()
s = bytes(icmp6_reply)
icmp6_reply_str = s[0:1280]
s = bytes(icmp6_reply)
icmp6_reply_str = s[0:1280]
- rx = self.send_and_expect(self.pg0, p6*9, self.pg0)
+ rx = self.send_and_expect_some(self.pg0, p6*9, self.pg0)
for p in rx:
self.validate_bytes(bytes(p[1]), icmp6_reply_str)
for p in rx:
self.validate_bytes(bytes(p[1]), icmp6_reply_str)
# in2out
pkts = self.create_stream_in(self.pg0, self.pg1, ttl=1)
# in2out
pkts = self.create_stream_in(self.pg0, self.pg1, ttl=1)
- self.pg0.add_stream(pkts)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- capture = self.pg0.get_capture(len(pkts))
+ capture = self.send_and_expect_some(self.pg0, pkts, self.pg0)
for p in capture:
self.assertIn(ICMP, p)
self.assertEqual(p[ICMP].type, 11) # 11 == time-exceeded
for p in capture:
self.assertIn(ICMP, p)
self.assertEqual(p[ICMP].type, 11) # 11 == time-exceeded
# Client side - generate traffic
pkts = self.create_stream_in(self.pg0, self.pg1, ttl=1)
# Client side - generate traffic
pkts = self.create_stream_in(self.pg0, self.pg1, ttl=1)
- self.pg0.add_stream(pkts)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
+ capture = self.send_and_expect_some(self.pg0, pkts, self.pg0)
# Client side - verify ICMP type 11 packets
# Client side - verify ICMP type 11 packets
- capture = self.pg0.get_capture(len(pkts))
self.verify_capture_in_with_icmp_errors(capture, self.pg0)
def test_dynamic_icmp_errors_out2in_ttl_1(self):
self.verify_capture_in_with_icmp_errors(capture, self.pg0)
def test_dynamic_icmp_errors_out2in_ttl_1(self):
capture = self.pg1.get_capture(len(pkts))
self.verify_capture_out(capture)
pkts = self.create_stream_out(self.pg1, ttl=1)
capture = self.pg1.get_capture(len(pkts))
self.verify_capture_out(capture)
pkts = self.create_stream_out(self.pg1, ttl=1)
- self.pg1.add_stream(pkts)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
+ capture = self.send_and_expect_some(self.pg1, pkts, self.pg1)
# Server side - verify ICMP type 11 packets
# Server side - verify ICMP type 11 packets
- capture = self.pg1.get_capture(len(pkts))
self.verify_capture_out_with_icmp_errors(capture,
src_ip=self.pg1.local_ip4)
self.verify_capture_out_with_icmp_errors(capture,
src_ip=self.pg1.local_ip4)
#
# expect ICMP - port unreachable for all packets
#
#
# expect ICMP - port unreachable for all packets
#
- rx = self.send_and_expect(self.pg0, pkts, self.pg0)
+ rx = self.send_and_expect_some(self.pg0, pkts, self.pg0)
for p in rx:
self.assertEqual(int(p[IP].proto), 1) # ICMP
for p in rx:
self.assertEqual(int(p[IP].proto), 1) # ICMP
punts = self.vapi.punt_socket_dump(type=pt_l4)
self.assertEqual(len(punts), 0)
punts = self.vapi.punt_socket_dump(type=pt_l4)
self.assertEqual(len(punts), 0)
- rx = self.send_and_expect(self.pg0, pkts, self.pg0)
+ rx = self.send_and_expect_some(self.pg0, pkts, self.pg0)
for p in rx:
self.assertEqual(int(p[IP].proto), 1) # ICMP
self.assertEqual(int(p[ICMP].code), 3) # unreachable
for p in rx:
self.assertEqual(int(p[IP].proto), 1) # ICMP
self.assertEqual(int(p[ICMP].code), 3) # unreachable
packets = self.dst_if.get_capture(
len(self.pkt_infos) - len(dropped_packet_indexes))
self.verify_capture(packets, dropped_packet_indexes)
packets = self.dst_if.get_capture(
len(self.pkt_infos) - len(dropped_packet_indexes))
self.verify_capture(packets, dropped_packet_indexes)
- pkts = self.src_if.get_capture(
- expected_count=len(dropped_packet_indexes))
+ pkts = self.src_if._get_capture(1)
for icmp in pkts:
self.assertIn(ICMPv6TimeExceeded, icmp)
self.assertIn(IPv6ExtHdrFragment, icmp)
for icmp in pkts:
self.assertIn(ICMPv6TimeExceeded, icmp)
self.assertIn(IPv6ExtHdrFragment, icmp)
packets = self.dst_if.get_capture(
len(self.pkt_infos) - len(dropped_packet_indexes))
self.verify_capture(packets, dropped_packet_indexes)
packets = self.dst_if.get_capture(
len(self.pkt_infos) - len(dropped_packet_indexes))
self.verify_capture(packets, dropped_packet_indexes)
- pkts = self.src_if.get_capture(
- expected_count=len(dropped_packet_indexes))
+ pkts = self.src_if._get_capture(1)
for icmp in pkts:
self.assertIn(ICMPv6TimeExceeded, icmp)
self.assertIn(IPv6ExtHdrFragment, icmp)
for icmp in pkts:
self.assertIn(ICMPv6TimeExceeded, icmp)
self.assertIn(IPv6ExtHdrFragment, icmp)