diff --git a/api/gr_api.h b/api/gr_api.h index a238d168c..eaa8c80e1 100644 --- a/api/gr_api.h +++ b/api/gr_api.h @@ -11,7 +11,7 @@ #include // Must be bumped when making non-backward compatible changes in API headers -#define GR_API_VERSION 2 +#define GR_API_VERSION 4 // API request header. struct gr_api_request { diff --git a/modules/infra/api/gr_infra.h b/modules/infra/api/gr_infra.h index f0895d65a..89a2a55b9 100644 --- a/modules/infra/api/gr_infra.h +++ b/modules/infra/api/gr_infra.h @@ -443,14 +443,30 @@ struct gr_graph_dump_resp { GR_REQ(GR_GRAPH_DUMP, struct gr_graph_dump_req, struct gr_graph_dump_resp); +typedef enum : uint16_t { + GR_GRAPH_SET_RX_BURST = GR_BIT16(0), + GR_GRAPH_SET_VECTOR = GR_BIT16(1), + GR_GRAPH_SET_ICMP_ERROR = GR_BIT16(2), + GR_GRAPH_SET_ARP = GR_BIT16(3), + GR_GRAPH_SET_ICMP = GR_BIT16(4), +} gr_graph_conf_set_attr_t; + struct gr_graph_conf { uint16_t rx_burst_max; // default 64, max 256 uint16_t vector_max; // default 64, max 256 + uint16_t icmp_error_rate; // ICMP errors/sec per node per worker, 0 = no limit, default 1000 + uint16_t arp_rate; // ARP packets/sec per worker, 0 = no limit, default 1000 + uint16_t icmp_rate; // ICMP/ICMPv6 input packets/sec per worker, 0 = no limit, default 1000 }; GR_REQ(GR_GRAPH_CONF_GET, struct gr_empty, struct gr_graph_conf); -GR_REQ(GR_GRAPH_CONF_SET, struct gr_graph_conf, struct gr_empty); +struct gr_graph_conf_set_req { + struct gr_graph_conf; + gr_graph_conf_set_attr_t set_attrs; +}; + +GR_REQ(GR_GRAPH_CONF_SET, struct gr_graph_conf_set_req, struct gr_empty); // packet tracing ////////////////////////////////////////////////////////////// diff --git a/modules/infra/cli/graph.c b/modules/infra/cli/graph.c index 3d063a85b..c9ccfd817 100644 --- a/modules/infra/cli/graph.c +++ b/modules/infra/cli/graph.c @@ -13,11 +13,31 @@ #include static cmd_status_t graph_conf_set(struct gr_api_client *c, const struct ec_pnode *p) { - struct gr_graph_conf req = {0}; + struct gr_graph_conf_set_req req = {.set_attrs = 0}; - if (arg_u16(p, "VECTOR", &req.vector_max) < 0 && errno != ENOENT) + if (arg_u16(p, "VECTOR", &req.vector_max) == 0) + req.set_attrs |= GR_GRAPH_SET_VECTOR; + else if (errno != ENOENT) return CMD_ERROR; - if (arg_u16(p, "BURST", &req.rx_burst_max) < 0 && errno != ENOENT) + + if (arg_u16(p, "BURST", &req.rx_burst_max) == 0) + req.set_attrs |= GR_GRAPH_SET_RX_BURST; + else if (errno != ENOENT) + return CMD_ERROR; + + if (arg_u16(p, "ICMP_ERROR_RATE", &req.icmp_error_rate) == 0) + req.set_attrs |= GR_GRAPH_SET_ICMP_ERROR; + else if (errno != ENOENT) + return CMD_ERROR; + + if (arg_u16(p, "ARP_RATE", &req.arp_rate) == 0) + req.set_attrs |= GR_GRAPH_SET_ARP; + else if (errno != ENOENT) + return CMD_ERROR; + + if (arg_u16(p, "ICMP_RATE", &req.icmp_rate) == 0) + req.set_attrs |= GR_GRAPH_SET_ICMP; + else if (errno != ENOENT) return CMD_ERROR; if (gr_api_client_send_recv(c, GR_GRAPH_CONF_SET, sizeof(req), &req, NULL) < 0) @@ -38,6 +58,9 @@ static cmd_status_t graph_conf_show(struct gr_api_client *c, const struct ec_pno struct gr_object *o = gr_object_new(NULL); gr_object_field(o, "vector_max", GR_DISP_INT, "%u", sizes->vector_max); gr_object_field(o, "rx_burst_max", GR_DISP_INT, "%u", sizes->rx_burst_max); + gr_object_field(o, "icmp_error_rate", GR_DISP_INT, "%u", sizes->icmp_error_rate); + gr_object_field(o, "arp_rate", GR_DISP_INT, "%u", sizes->arp_rate); + gr_object_field(o, "icmp_rate", GR_DISP_INT, "%u", sizes->icmp_rate); gr_object_free(o); free(resp_ptr); @@ -76,14 +99,27 @@ static int ctx_init(struct ec_node *root) { ret = CLI_COMMAND( CONF_CTX(root), - "set (vector-max VECTOR),(rx-burst-max BURST)", + "set (vector-max VECTOR),(rx-burst-max BURST),(icmp-error-rate ICMP_ERROR_RATE)," + "(arp-rate ARP_RATE),(icmp-rate ICMP_RATE)", graph_conf_set, - "Configure maximum burst sizes of the packet processing graph.", + "Configure packet processing graph parameters.", with_help( "Maximum size of graph vectors.", ec_node_uint("VECTOR", 1, UINT16_MAX, 10) ), with_help( "Maximum size of RX queue burst.", ec_node_uint("BURST", 1, UINT16_MAX, 10) + ), + with_help( + "ICMP errors/sec per node per worker (0 = no limit).", + ec_node_uint("ICMP_ERROR_RATE", 0, UINT16_MAX, 10) + ), + with_help( + "ARP packets/sec per worker (0 = no limit).", + ec_node_uint("ARP_RATE", 0, UINT16_MAX, 10) + ), + with_help( + "ICMP/ICMPv6 input packets/sec per worker (0 = no limit).", + ec_node_uint("ICMP_RATE", 0, UINT16_MAX, 10) ) ); if (ret < 0) diff --git a/modules/infra/control/graph.c b/modules/infra/control/graph.c index 9ae25655d..fd3b9cff4 100644 --- a/modules/infra/control/graph.c +++ b/modules/infra/control/graph.c @@ -85,9 +85,12 @@ static struct iface_info_port *find_port(vec struct iface_info_port **ports, uin return port; } -static struct gr_graph_conf graph_conf = { +struct gr_graph_conf graph_conf = { .rx_burst_max = 64, .vector_max = 64, + .icmp_error_rate = 1000, + .arp_rate = 1000, + .icmp_rate = 1000, }; static int @@ -610,19 +613,32 @@ static struct api_out graph_conf_get(const void * /*request*/, struct api_ctx *) } static struct api_out graph_conf_set(const void *request, struct api_ctx *) { - const struct gr_graph_conf *req = request; - struct gr_graph_conf prev = graph_conf; + const struct gr_graph_conf_set_req *req = request; vec struct iface_info_port **ports = NULL; + struct gr_graph_conf prev = graph_conf; struct iface *iface = NULL; int ret; - if (req->rx_burst_max > RTE_GRAPH_BURST_SIZE || req->vector_max > RTE_GRAPH_BURST_SIZE) - return api_out(EOVERFLOW, 0, NULL); - - if (req->rx_burst_max > 0) + if (req->set_attrs & GR_GRAPH_SET_RX_BURST) { + if (req->rx_burst_max == 0) + return api_out(EDOM, 0, NULL); + if (req->rx_burst_max > RTE_GRAPH_BURST_SIZE) + return api_out(EOVERFLOW, 0, NULL); graph_conf.rx_burst_max = req->rx_burst_max; - if (req->vector_max > 0) + } + if (req->set_attrs & GR_GRAPH_SET_VECTOR) { + if (req->vector_max == 0) + return api_out(EDOM, 0, NULL); + if (req->vector_max > RTE_GRAPH_BURST_SIZE) + return api_out(EOVERFLOW, 0, NULL); graph_conf.vector_max = req->vector_max; + } + if (req->set_attrs & GR_GRAPH_SET_ICMP_ERROR) + graph_conf.icmp_error_rate = req->icmp_error_rate; + if (req->set_attrs & GR_GRAPH_SET_ARP) + graph_conf.arp_rate = req->arp_rate; + if (req->set_attrs & GR_GRAPH_SET_ICMP) + graph_conf.icmp_rate = req->icmp_rate; if (graph_conf.rx_burst_max == prev.rx_burst_max && graph_conf.vector_max == prev.vector_max) diff --git a/modules/infra/control/graph.h b/modules/infra/control/graph.h index c7a79f529..f6c1a5be8 100644 --- a/modules/infra/control/graph.h +++ b/modules/infra/control/graph.h @@ -37,6 +37,8 @@ rte_node_enqueue_x1(struct rte_graph *, struct rte_node *, rte_edge_t next, void rte_edge_t gr_node_attach_parent(const char *parent, const char *node); +extern struct gr_graph_conf graph_conf; + uint16_t drop_packets(struct rte_graph *, struct rte_node *, void **, uint16_t); int drop_format(char *buf, size_t buf_len, const void *data, size_t data_len); diff --git a/modules/infra/datapath/datapath.h b/modules/infra/datapath/datapath.h index 2c1e58e5f..2738a6f75 100644 --- a/modules/infra/datapath/datapath.h +++ b/modules/infra/datapath/datapath.h @@ -3,4 +3,37 @@ #pragma once +#include + +#include + void *gr_datapath_loop(void *priv); + +struct rate_limit_ctx { + uint16_t tokens; + uint64_t last_refill; +} __attribute__((packed)); + +static inline bool +rate_limited(struct rate_limit_ctx *ctx, const uint16_t max_rate, const uint16_t nb_pkts) { + uint64_t now = rte_rdtsc(); + uint16_t add; + + assert(ctx != NULL); + + if (max_rate == 0) + return false; + + add = (now - ctx->last_refill) * max_rate / rte_get_tsc_hz(); + if (add > 0) { + ctx->tokens = RTE_MIN((uint32_t)ctx->tokens + add, (uint32_t)max_rate); + ctx->last_refill = now; + } + + if (ctx->tokens == 0) + return true; + + ctx->tokens -= RTE_MIN(ctx->tokens, nb_pkts); + + return false; +} diff --git a/modules/ip/datapath/arp_input.c b/modules/ip/datapath/arp_input.c index 2efbb31df..a701cbb43 100644 --- a/modules/ip/datapath/arp_input.c +++ b/modules/ip/datapath/arp_input.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2024 Robin Jarry +#include "datapath.h" #include "eth.h" #include "graph.h" #include "mbuf.h" @@ -9,20 +10,29 @@ #include #include +GR_NODE_CTX_TYPE(arp_input_ctx, { struct rate_limit_ctx limit; }); + enum { OP_REQUEST = 0, OP_REPLY, OP_UNSUPP, PROTO_UNSUPP, + RATE_LIMITED, EDGE_COUNT, }; static uint16_t arp_input_process(struct rte_graph *graph, struct rte_node *node, void **objs, uint16_t nb_objs) { + struct arp_input_ctx *ctx = arp_input_ctx(node); struct rte_arp_hdr *arp, *t; struct rte_mbuf *mbuf; rte_edge_t edge; + if (rate_limited(&ctx->limit, graph_conf.arp_rate, nb_objs)) { + rte_node_next_stream_move(graph, node, RATE_LIMITED); + return nb_objs; + } + for (uint16_t i = 0; i < nb_objs; i++) { mbuf = objs[i]; @@ -58,6 +68,13 @@ arp_input_process(struct rte_graph *graph, struct rte_node *node, void **objs, u return nb_objs; } +static int arp_input_init(const struct rte_graph *, struct rte_node *node) { + struct arp_input_ctx *ctx = arp_input_ctx(node); + ctx->limit.tokens = graph_conf.arp_rate; + ctx->limit.last_refill = rte_rdtsc(); + return 0; +} + static void arp_input_register(void) { gr_eth_input_add_type(RTE_BE16(RTE_ETHER_TYPE_ARP), "arp_input"); } @@ -66,6 +83,7 @@ static struct rte_node_register node = { .name = "arp_input", .process = arp_input_process, + .init = arp_input_init, .nb_edges = EDGE_COUNT, .next_nodes = { @@ -73,6 +91,7 @@ static struct rte_node_register node = { [OP_REPLY] = "arp_input_reply", [OP_UNSUPP] = "arp_input_op_unsupp", [PROTO_UNSUPP] = "arp_input_proto_unsupp", + [RATE_LIMITED] = "error_rate_limited", }, }; diff --git a/modules/ip/datapath/icmp_input.c b/modules/ip/datapath/icmp_input.c index 167cc9d79..1aa4d3117 100644 --- a/modules/ip/datapath/icmp_input.c +++ b/modules/ip/datapath/icmp_input.c @@ -2,6 +2,7 @@ // Copyright (c) 2024 Robin Jarry #include "control_output.h" +#include "datapath.h" #include "graph.h" #include "ip4_datapath.h" #include "log.h" @@ -12,11 +13,14 @@ #include +GR_NODE_CTX_TYPE(icmp_input_ctx, { struct rate_limit_ctx limit; }); + enum { OUTPUT = 0, CONTROL, INVALID, UNSUPPORTED, + RATE_LIMITED, EDGE_COUNT, }; @@ -26,6 +30,7 @@ static control_queue_cb_t icmp_cb[UINT8_MAX]; static uint16_t icmp_input_process(struct rte_graph *graph, struct rte_node *node, void **objs, uint16_t nb_objs) { + struct icmp_input_ctx *ctx = icmp_input_ctx(node); struct ip_local_mbuf_data *ip_data; struct rte_icmp_hdr *icmp; struct rte_mbuf *mbuf; @@ -33,6 +38,11 @@ icmp_input_process(struct rte_graph *graph, struct rte_node *node, void **objs, uint16_t cksum; ip4_addr_t ip; + if (rate_limited(&ctx->limit, graph_conf.icmp_rate, nb_objs)) { + rte_node_next_stream_move(graph, node, RATE_LIMITED); + return nb_objs; + } + for (uint16_t i = 0; i < nb_objs; i++) { mbuf = objs[i]; icmp = rte_pktmbuf_mtod(mbuf, struct rte_icmp_hdr *); @@ -80,6 +90,13 @@ void icmp_input_register_callback(uint8_t icmp_type, control_queue_cb_t cb) { icmp_cb[icmp_type] = cb; } +static int icmp_input_init(const struct rte_graph *, struct rte_node *node) { + struct icmp_input_ctx *ctx = icmp_input_ctx(node); + ctx->limit.tokens = graph_conf.icmp_rate; + ctx->limit.last_refill = rte_rdtsc(); + return 0; +} + static void icmp_input_register(void) { ip_input_local_add_proto(IPPROTO_ICMP, "icmp_input"); } @@ -88,6 +105,7 @@ static struct rte_node_register icmp_input_node = { .name = "icmp_input", .process = icmp_input_process, + .init = icmp_input_init, .nb_edges = EDGE_COUNT, .next_nodes = { @@ -95,6 +113,7 @@ static struct rte_node_register icmp_input_node = { [CONTROL] = "control_output", [INVALID] = "icmp_input_invalid", [UNSUPPORTED] = "icmp_input_unsupported", + [RATE_LIMITED] = "error_rate_limited", }, }; diff --git a/modules/ip/datapath/ip_error.c b/modules/ip/datapath/ip_error.c index c22652ce5..cc4b4d18e 100644 --- a/modules/ip/datapath/ip_error.c +++ b/modules/ip/datapath/ip_error.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2024 Christophe Fontaine +#include "datapath.h" #include "graph.h" #include "ip4.h" #include "ip4_datapath.h" @@ -13,18 +14,20 @@ GR_NODE_CTX_TYPE(ip_error_ctx, { uint8_t icmp_type; uint8_t icmp_code; + struct rate_limit_ctx limit; }); enum edges { ICMP_OUTPUT = 0, NO_HEADROOM, NO_IP, + RATE_LIMITED, EDGE_COUNT, }; static uint16_t ip_error_process(struct rte_graph *graph, struct rte_node *node, void **objs, uint16_t nb_objs) { - const struct ip_error_ctx *ctx = ip_error_ctx(node); + struct ip_error_ctx *ctx = ip_error_ctx(node); struct ip_local_mbuf_data *ip_data; const struct nexthop_info_l3 *l3; const struct nexthop *nh, *local; @@ -36,6 +39,11 @@ ip_error_process(struct rte_graph *graph, struct rte_node *node, void **objs, ui rte_edge_t edge; unsigned len; + if (rate_limited(&ctx->limit, graph_conf.icmp_error_rate, nb_objs)) { + rte_node_next_stream_move(graph, node, RATE_LIMITED); + return nb_objs; + } + for (uint16_t i = 0; i < nb_objs; i++) { mbuf = objs[i]; @@ -100,6 +108,8 @@ static int ttl_exceeded_init(const struct rte_graph *, struct rte_node *node) { struct ip_error_ctx *ctx = ip_error_ctx(node); ctx->icmp_type = RTE_ICMP_TYPE_TTL_EXCEEDED; ctx->icmp_code = RTE_ICMP_CODE_TTL_EXCEEDED; + ctx->limit.tokens = graph_conf.icmp_error_rate; + ctx->limit.last_refill = rte_rdtsc(); return 0; } @@ -107,6 +117,8 @@ static int no_route_init(const struct rte_graph *, struct rte_node *node) { struct ip_error_ctx *ctx = ip_error_ctx(node); ctx->icmp_type = RTE_ICMP_TYPE_DEST_UNREACHABLE; ctx->icmp_code = RTE_ICMP_CODE_UNREACH_NET; + ctx->limit.tokens = graph_conf.icmp_error_rate; + ctx->limit.last_refill = rte_rdtsc(); return 0; } @@ -114,6 +126,8 @@ static int frag_needed_init(const struct rte_graph *, struct rte_node *node) { struct ip_error_ctx *ctx = ip_error_ctx(node); ctx->icmp_type = RTE_ICMP_TYPE_DEST_UNREACHABLE; ctx->icmp_code = RTE_ICMP_CODE_UNREACH_FRAG; + ctx->limit.tokens = graph_conf.icmp_error_rate; + ctx->limit.last_refill = rte_rdtsc(); return 0; } @@ -125,6 +139,7 @@ static struct rte_node_register ip_forward_ttl_exceeded_node = { [ICMP_OUTPUT] = "icmp_output", [NO_HEADROOM] = "error_no_headroom", [NO_IP] = "error_no_local_ip", + [RATE_LIMITED] = "error_rate_limited", }, .init = ttl_exceeded_init, }; @@ -137,6 +152,7 @@ static struct rte_node_register no_route_node = { [ICMP_OUTPUT] = "icmp_output", [NO_HEADROOM] = "error_no_headroom", [NO_IP] = "error_no_local_ip", + [RATE_LIMITED] = "error_rate_limited", }, .init = no_route_init, }; @@ -149,6 +165,7 @@ static struct rte_node_register frag_needed_node = { [ICMP_OUTPUT] = "icmp_output", [NO_HEADROOM] = "error_no_headroom", [NO_IP] = "error_no_local_ip", + [RATE_LIMITED] = "error_rate_limited", }, .init = frag_needed_init, }; @@ -173,3 +190,4 @@ GR_NODE_REGISTER(info_no_route); GR_NODE_REGISTER(info_frag_needed); GR_DROP_REGISTER(error_no_local_ip); +GR_DROP_REGISTER(error_rate_limited); diff --git a/modules/ip6/datapath/icmp6_input.c b/modules/ip6/datapath/icmp6_input.c index 6ce747d05..b6614b2f1 100644 --- a/modules/ip6/datapath/icmp6_input.c +++ b/modules/ip6/datapath/icmp6_input.c @@ -2,6 +2,7 @@ // Copyright (c) 2024 Robin Jarry #include "control_output.h" +#include "datapath.h" #include "graph.h" #include "icmp6.h" #include "ip6.h" @@ -10,7 +11,11 @@ #include "mbuf.h" #include "trace.h" -#include +#include +#include +#include + +GR_NODE_CTX_TYPE(icmp6_input_ctx, { struct rate_limit_ctx limit; }); enum { ICMP6_OUTPUT = 0, @@ -22,6 +27,7 @@ enum { INVALID, UNSUPPORTED, NO_LOCAL_ADDR, + RATE_LIMITED, EDGE_COUNT, }; @@ -29,12 +35,18 @@ static control_queue_cb_t icmp6_cb[UINT8_MAX]; static uint16_t icmp6_input_process(struct rte_graph *graph, struct rte_node *node, void **objs, uint16_t nb_objs) { + struct icmp6_input_ctx *ctx = icmp6_input_ctx(node); struct ip6_local_mbuf_data *d; struct icmp6 *icmp6; struct rte_ipv6_addr tmp_ip; struct rte_mbuf *mbuf; rte_edge_t next; + if (rate_limited(&ctx->limit, graph_conf.icmp_rate, nb_objs)) { + rte_node_next_stream_move(graph, node, RATE_LIMITED); + return nb_objs; + } + for (uint16_t i = 0; i < nb_objs; i++) { mbuf = objs[i]; icmp6 = rte_pktmbuf_mtod(mbuf, struct icmp6 *); @@ -104,6 +116,13 @@ void icmp6_input_register_callback(uint8_t icmp6_type, control_queue_cb_t cb) { icmp6_cb[icmp6_type] = cb; } +static int icmp6_input_init(const struct rte_graph *, struct rte_node *node) { + struct icmp6_input_ctx *ctx = icmp6_input_ctx(node); + ctx->limit.tokens = graph_conf.icmp_rate; + ctx->limit.last_refill = rte_rdtsc(); + return 0; +} + static void icmp6_input_register(void) { ip6_input_local_add_proto(IPPROTO_ICMPV6, "icmp6_input"); } @@ -112,6 +131,7 @@ static struct rte_node_register icmp6_input_node = { .name = "icmp6_input", .process = icmp6_input_process, + .init = icmp6_input_init, .nb_edges = EDGE_COUNT, .next_nodes = { @@ -124,6 +144,7 @@ static struct rte_node_register icmp6_input_node = { [INVALID] = "icmp6_input_invalid", [UNSUPPORTED] = "icmp6_input_unsupported", [NO_LOCAL_ADDR] = "icmp6_input_no_local_addr", + [RATE_LIMITED] = "error_rate_limited", }, }; diff --git a/modules/ip6/datapath/ip6_error.c b/modules/ip6/datapath/ip6_error.c index 16fde4772..093bfa967 100644 --- a/modules/ip6/datapath/ip6_error.c +++ b/modules/ip6/datapath/ip6_error.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2024 Christophe Fontaine +#include "datapath.h" #include "graph.h" #include "icmp6.h" #include "ip6.h" @@ -10,22 +11,25 @@ #include "mbuf.h" #include +#include GR_NODE_CTX_TYPE(ip6_error_ctx, { icmp6_type_t icmp_type; uint8_t icmp_code; + struct rate_limit_ctx limit; }); enum edges { ICMP_OUTPUT = 0, NO_HEADROOM, NO_IP, + RATE_LIMITED, EDGE_COUNT, }; static uint16_t ip6_error_process(struct rte_graph *graph, struct rte_node *node, void **objs, uint16_t nb_objs) { - const struct ip6_error_ctx *ctx = ip6_error_ctx(node); + struct ip6_error_ctx *ctx = ip6_error_ctx(node); struct icmp6_err_dest_unreach *du; struct icmp6_err_ttl_exceeded *te; const struct nexthop_info_l3 *l3; @@ -37,6 +41,11 @@ ip6_error_process(struct rte_graph *graph, struct rte_node *node, void **objs, u struct icmp6 *icmp6; rte_edge_t edge; + if (rate_limited(&ctx->limit, graph_conf.icmp_error_rate, nb_objs)) { + rte_node_next_stream_move(graph, node, RATE_LIMITED); + return nb_objs; + } + for (uint16_t i = 0; i < nb_objs; i++) { mbuf = objs[i]; @@ -110,6 +119,8 @@ static int ttl_exceeded_init(const struct rte_graph *, struct rte_node *node) { struct ip6_error_ctx *ctx = ip6_error_ctx(node); ctx->icmp_type = ICMP6_ERR_TTL_EXCEEDED; ctx->icmp_code = 0; + ctx->limit.tokens = graph_conf.icmp_error_rate; + ctx->limit.last_refill = rte_rdtsc(); return 0; } @@ -117,6 +128,8 @@ static int no_route_init(const struct rte_graph *, struct rte_node *node) { struct ip6_error_ctx *ctx = ip6_error_ctx(node); ctx->icmp_type = ICMP6_ERR_DEST_UNREACH; ctx->icmp_code = 0; + ctx->limit.tokens = graph_conf.icmp_error_rate; + ctx->limit.last_refill = rte_rdtsc(); return 0; } @@ -128,6 +141,7 @@ static struct rte_node_register dest_unreach_node = { [ICMP_OUTPUT] = "icmp6_output", [NO_HEADROOM] = "error_no_headroom", [NO_IP] = "error_no_local_ip", + [RATE_LIMITED] = "error_rate_limited", }, .init = no_route_init, }; @@ -140,6 +154,7 @@ static struct rte_node_register ttl_exceeded_node = { [ICMP_OUTPUT] = "icmp6_output", [NO_HEADROOM] = "error_no_headroom", [NO_IP] = "error_no_local_ip", + [RATE_LIMITED] = "error_rate_limited", }, .init = ttl_exceeded_init, };