summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeale Ranns <nranns@cisco.com>2019-06-03 13:21:40 +0000
committerNeale Ranns <nranns@cisco.com>2019-06-06 12:12:58 +0000
commit922077505b43f9560550653f530cf93acc81044d (patch)
tree99bbd28d90de7b1b4db986a5586c04143a8f0c61
parent831f4200cab2b363f2a8ea340331343c14407b7d (diff)
IP-Punt-redirect: allow the use of a FIB path to describe how to
redirect Change-Id: I2a3ba2a3d73ea8511e3a511855b041432328f0a8 Signed-off-by: Neale Ranns <nranns@cisco.com>
-rw-r--r--src/vnet/CMakeLists.txt1
-rw-r--r--src/vnet/fib/fib_node.h4
-rw-r--r--src/vnet/ip/ip4.h3
-rw-r--r--src/vnet/ip/ip4_punt_drop.c220
-rw-r--r--src/vnet/ip/ip6.h2
-rw-r--r--src/vnet/ip/ip6_punt_drop.c111
-rw-r--r--src/vnet/ip/ip_api.c91
-rw-r--r--src/vnet/ip/ip_punt_drop.c251
-rw-r--r--src/vnet/ip/ip_punt_drop.h134
-rw-r--r--test/vpp_ip_route.py9
10 files changed, 439 insertions, 387 deletions
diff --git a/src/vnet/CMakeLists.txt b/src/vnet/CMakeLists.txt
index dcbdb73d841..1c85daf84bf 100644
--- a/src/vnet/CMakeLists.txt
+++ b/src/vnet/CMakeLists.txt
@@ -445,6 +445,7 @@ list(APPEND VNET_SOURCES
ip/ip.c
ip/ip_init.c
ip/ip_in_out_acl.c
+ ip/ip_punt_drop.c
ip/lookup.c
ip/ping.c
ip/punt_api.c
diff --git a/src/vnet/fib/fib_node.h b/src/vnet/fib/fib_node.h
index de366f27704..e5a72a16f87 100644
--- a/src/vnet/fib/fib_node.h
+++ b/src/vnet/fib/fib_node.h
@@ -48,6 +48,7 @@ typedef enum fib_node_type_t_ {
FIB_NODE_TYPE_BIER_ENTRY,
FIB_NODE_TYPE_VXLAN_GBP_TUNNEL,
FIB_NODE_TYPE_IPSEC_SA,
+ FIB_NODE_TYPE_IP_PUNT_REDIRECT,
/**
* Marker. New types before this one. leave the test last.
*/
@@ -75,7 +76,8 @@ typedef enum fib_node_type_t_ {
[FIB_NODE_TYPE_BIER_FMASK] = "bier-fmask", \
[FIB_NODE_TYPE_BIER_ENTRY] = "bier-entry", \
[FIB_NODE_TYPE_VXLAN_GBP_TUNNEL] = "vxlan-gbp-tunnel", \
- [FIB_NODE_TYPE_IPSEC_SA] = "ipsec-sa" \
+ [FIB_NODE_TYPE_IPSEC_SA] = "ipsec-sa", \
+ [FIB_NODE_TYPE_IP_PUNT_REDIRECT] = "ip-punt-redirect" \
}
/**
diff --git a/src/vnet/ip/ip4.h b/src/vnet/ip/ip4.h
index 9f25f43b98e..0ead3faa1b8 100644
--- a/src/vnet/ip/ip4.h
+++ b/src/vnet/ip/ip4.h
@@ -302,6 +302,9 @@ void ip4_punt_policer_add_del (u8 is_add, u32 policer_index);
void ip4_punt_redirect_add (u32 rx_sw_if_index,
u32 tx_sw_if_index, ip46_address_t * nh);
+void ip4_punt_redirect_add_paths (u32 rx_sw_if_index,
+ fib_route_path_t * paths);
+
void ip4_punt_redirect_del (u32 rx_sw_if_index);
/* Compute flow hash. We'll use it to select which adjacency to use for this
diff --git a/src/vnet/ip/ip4_punt_drop.c b/src/vnet/ip/ip4_punt_drop.c
index 85409857f38..4771e8d5b34 100644
--- a/src/vnet/ip/ip4_punt_drop.c
+++ b/src/vnet/ip/ip4_punt_drop.c
@@ -33,8 +33,6 @@ VNET_FEATURE_ARC_INIT (ip4_drop) =
/* *INDENT-ON* */
extern ip_punt_policer_t ip4_punt_policer_cfg;
-extern ip_punt_redirect_t ip4_punt_redirect_cfg;
-extern ip_punt_redirect_rx_t uninit_rx_redirect;
#ifndef CLIB_MARCH_VARIANT
u8 *
@@ -89,41 +87,6 @@ VNET_FEATURE_INIT (ip4_punt_policer_node) = {
};
/* *INDENT-ON* */
-#ifndef CLIB_MARCH_VARIANT
-u8 *
-format_ip_punt_redirect_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 *);
- ip_punt_redirect_trace_t *t = va_arg (*args, ip_punt_redirect_trace_t *);
- vnet_main_t *vnm = vnet_get_main ();
- vnet_sw_interface_t *si;
-
- si = vnet_get_sw_interface_safe (vnm, t->redirect.tx_sw_if_index);
-
- if (NULL != si)
- s = format (s, "via %U on %U using adj:%d",
- format_ip46_address, &t->redirect.nh, IP46_TYPE_ANY,
- format_vnet_sw_interface_name, vnm, si,
- t->redirect.adj_index);
- else
- s = format (s, "via %U on %d using adj:%d",
- format_ip46_address, &t->redirect.nh, IP46_TYPE_ANY,
- t->redirect.tx_sw_if_index, t->redirect.adj_index);
-
- return s;
-}
-
-/* *INDENT-OFF* */
-ip_punt_redirect_t ip4_punt_redirect_cfg = {
- .any_rx_sw_if_index = {
- .tx_sw_if_index = ~0,
- .adj_index = ADJ_INDEX_INVALID,
- },
-};
-/* *INDENT-ON* */
-#endif /* CLIB_MARCH_VARIANT */
-
#define foreach_ip4_punt_redirect_error \
_(DROP, "ip4 punt redirect drop")
@@ -148,7 +111,7 @@ VLIB_NODE_FN (ip4_punt_redirect_node) (vlib_main_t * vm,
{
return (ip_punt_redirect (vm, node, frame,
vnet_feat_arc_ip4_punt.feature_arc_index,
- &ip4_punt_redirect_cfg));
+ FIB_PROTOCOL_IP4));
}
/* *INDENT-OFF* */
@@ -327,72 +290,34 @@ VLIB_CLI_COMMAND (ip4_punt_policer_command, static) =
/* *INDENT-ON* */
#ifndef CLIB_MARCH_VARIANT
-/*
- * an uninitalised rx-redirect strcut used to pad the vector
- */
-ip_punt_redirect_rx_t uninit_rx_redirect = {
- .tx_sw_if_index = ~0,
- .adj_index = ADJ_INDEX_INVALID,
-};
void
-ip_punt_redirect_add (ip_punt_redirect_t * cfg,
- u32 rx_sw_if_index,
- ip_punt_redirect_rx_t * redirect,
- fib_protocol_t fproto, vnet_link_t linkt)
+ip4_punt_redirect_add (u32 rx_sw_if_index,
+ u32 tx_sw_if_index, ip46_address_t * nh)
{
- ip_punt_redirect_rx_t *new;
-
- if (~0 == rx_sw_if_index)
- {
- cfg->any_rx_sw_if_index = *redirect;
- new = &cfg->any_rx_sw_if_index;
- }
- else
- {
- vec_validate_init_empty (cfg->redirect_by_rx_sw_if_index,
- rx_sw_if_index, uninit_rx_redirect);
- cfg->redirect_by_rx_sw_if_index[rx_sw_if_index] = *redirect;
- new = &cfg->redirect_by_rx_sw_if_index[rx_sw_if_index];
- }
+ /* *INDENT-OFF* */
+ fib_route_path_t *rpaths = NULL, rpath = {
+ .frp_proto = DPO_PROTO_IP4,
+ .frp_addr = *nh,
+ .frp_sw_if_index = tx_sw_if_index,
+ .frp_weight = 1,
+ .frp_fib_index = ~0,
+ };
+ /* *INDENT-ON* */
- new->adj_index = adj_nbr_add_or_lock (fproto, linkt,
- &redirect->nh,
- redirect->tx_sw_if_index);
-}
+ vec_add1 (rpaths, rpath);
-void
-ip_punt_redirect_del (ip_punt_redirect_t * cfg, u32 rx_sw_if_index)
-{
- ip_punt_redirect_rx_t *old;
+ ip4_punt_redirect_add_paths (rx_sw_if_index, rpaths);
- if (~0 == rx_sw_if_index)
- {
- old = &cfg->any_rx_sw_if_index;
- }
- else
- {
- old = &cfg->redirect_by_rx_sw_if_index[rx_sw_if_index];
- }
-
- if ((old == NULL) || (old->adj_index == ADJ_INDEX_INVALID))
- return;
-
- adj_unlock (old->adj_index);
- *old = uninit_rx_redirect;
+ vec_free (rpaths);
}
void
-ip4_punt_redirect_add (u32 rx_sw_if_index,
- u32 tx_sw_if_index, ip46_address_t * nh)
+ip4_punt_redirect_add_paths (u32 rx_sw_if_index, fib_route_path_t * rpaths)
{
- ip_punt_redirect_rx_t rx = {
- .tx_sw_if_index = tx_sw_if_index,
- .nh = *nh,
- };
-
- ip_punt_redirect_add (&ip4_punt_redirect_cfg,
- rx_sw_if_index, &rx, FIB_PROTOCOL_IP4, VNET_LINK_IP4);
+ ip_punt_redirect_add (FIB_PROTOCOL_IP4,
+ rx_sw_if_index,
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP4, rpaths);
vnet_feature_enable_disable ("ip4-punt", "ip4-punt-redirect", 0, 1, 0, 0);
}
@@ -402,7 +327,7 @@ ip4_punt_redirect_del (u32 rx_sw_if_index)
{
vnet_feature_enable_disable ("ip4-punt", "ip4-punt-redirect", 0, 0, 0, 0);
- ip_punt_redirect_del (&ip4_punt_redirect_cfg, rx_sw_if_index);
+ ip_punt_redirect_del (FIB_PROTOCOL_IP4, rx_sw_if_index);
}
#endif /* CLIB_MARCH_VARIANT */
@@ -412,10 +337,9 @@ ip4_punt_redirect_cmd (vlib_main_t * vm,
vlib_cli_command_t * cmd)
{
unformat_input_t _line_input, *line_input = &_line_input;
- ip46_address_t nh = ip46_address_initializer;
+ fib_route_path_t *rpaths = NULL, rpath;
clib_error_t *error = 0;
- u32 rx_sw_if_index = 0;
- u32 tx_sw_if_index = 0;
+ u32 rx_sw_if_index = ~0;
vnet_main_t *vnm;
u8 is_add;
@@ -436,14 +360,9 @@ ip4_punt_redirect_cmd (vlib_main_t * vm,
else if (unformat (line_input, "rx %U",
unformat_vnet_sw_interface, vnm, &rx_sw_if_index))
;
- else if (unformat (line_input, "via %U %U",
- unformat_ip4_address,
- &nh.ip4,
- unformat_vnet_sw_interface, vnm, &tx_sw_if_index))
- ;
else if (unformat (line_input, "via %U",
- unformat_vnet_sw_interface, vnm, &tx_sw_if_index))
- clib_memset (&nh, 0, sizeof (nh));
+ unformat_fib_route_path, &rpath))
+ vec_add1 (rpaths, rpath);
else
{
error = unformat_parse_error (line_input);
@@ -451,19 +370,20 @@ ip4_punt_redirect_cmd (vlib_main_t * vm,
}
}
+ if (~0 == rx_sw_if_index)
+ {
+ error = unformat_parse_error (line_input);
+ goto done;
+ }
+
if (is_add)
{
- if (rx_sw_if_index && tx_sw_if_index)
- {
- ip4_punt_redirect_add (rx_sw_if_index, tx_sw_if_index, &nh);
- }
+ if (vec_len (rpaths))
+ ip4_punt_redirect_add_paths (rx_sw_if_index, rpaths);
}
else
{
- if (rx_sw_if_index)
- {
- ip4_punt_redirect_del (rx_sw_if_index);
- }
+ ip4_punt_redirect_del (rx_sw_if_index);
}
done:
@@ -485,84 +405,12 @@ VLIB_CLI_COMMAND (ip4_punt_redirect_command, static) =
};
/* *INDENT-ON* */
-#ifndef CLIB_MARCH_VARIANT
-u8 *
-format_ip_punt_redirect (u8 * s, va_list * args)
-{
- ip_punt_redirect_t *cfg = va_arg (*args, ip_punt_redirect_t *);
- ip_punt_redirect_rx_t *rx;
- u32 rx_sw_if_index;
- vnet_main_t *vnm = vnet_get_main ();
-
- vec_foreach_index (rx_sw_if_index, cfg->redirect_by_rx_sw_if_index)
- {
- rx = &cfg->redirect_by_rx_sw_if_index[rx_sw_if_index];
- if (~0 != rx->tx_sw_if_index)
- {
- s = format (s, " rx %U redirect via %U %U\n",
- format_vnet_sw_interface_name, vnm,
- vnet_get_sw_interface (vnm, rx_sw_if_index),
- format_ip46_address, &rx->nh, IP46_TYPE_ANY,
- format_vnet_sw_interface_name, vnm,
- vnet_get_sw_interface (vnm, rx->tx_sw_if_index));
- }
- }
- if (~0 != cfg->any_rx_sw_if_index.tx_sw_if_index)
- {
- s = format (s, " rx all redirect via %U %U\n",
- format_ip46_address, &cfg->any_rx_sw_if_index.nh,
- IP46_TYPE_ANY, format_vnet_sw_interface_name, vnm,
- vnet_get_sw_interface (vnm,
- cfg->
- any_rx_sw_if_index.tx_sw_if_index));
- }
-
- return (s);
-}
-
-ip_punt_redirect_detail_t *
-ip4_punt_redirect_entries (u32 sw_if_index)
-{
- ip_punt_redirect_rx_t *pr;
- ip_punt_redirect_detail_t *prs = 0;
- u32 rx_sw_if_index;
-
- vec_foreach_index (rx_sw_if_index,
- ip4_punt_redirect_cfg.redirect_by_rx_sw_if_index)
- {
- if (sw_if_index == ~0 || sw_if_index == rx_sw_if_index)
- {
- pr =
- &ip4_punt_redirect_cfg.redirect_by_rx_sw_if_index[rx_sw_if_index];
- if (~0 != pr->tx_sw_if_index)
- {
- ip_punt_redirect_detail_t detail = {.rx_sw_if_index =
- rx_sw_if_index,
- .punt_redirect = *pr
- };
- vec_add1 (prs, detail);
- }
- }
- }
- if (~0 != ip4_punt_redirect_cfg.any_rx_sw_if_index.tx_sw_if_index)
- {
- pr = &ip4_punt_redirect_cfg.any_rx_sw_if_index;
- ip_punt_redirect_detail_t detail = {.rx_sw_if_index = ~0,
- .punt_redirect = *pr
- };
- vec_add1 (prs, detail);
- }
-
- return prs;
-}
-#endif /* CLIB_MARCH_VARIANT */
-
static clib_error_t *
ip4_punt_redirect_show_cmd (vlib_main_t * vm,
unformat_input_t * main_input,
vlib_cli_command_t * cmd)
{
- vlib_cli_output (vm, "%U", format_ip_punt_redirect, &ip4_punt_redirect_cfg);
+ vlib_cli_output (vm, "%U", format_ip_punt_redirect, FIB_PROTOCOL_IP4);
return (NULL);
}
diff --git a/src/vnet/ip/ip6.h b/src/vnet/ip/ip6.h
index b511cccbaa7..2cff713ab05 100644
--- a/src/vnet/ip/ip6.h
+++ b/src/vnet/ip/ip6.h
@@ -468,6 +468,8 @@ u32 ip6_tcp_udp_icmp_validate_checksum (vlib_main_t * vm, vlib_buffer_t * p0);
void ip6_punt_policer_add_del (u8 is_add, u32 policer_index);
void ip6_punt_redirect_add (u32 rx_sw_if_index,
u32 tx_sw_if_index, ip46_address_t * nh);
+void ip6_punt_redirect_add_paths (u32 rx_sw_if_index,
+ fib_route_path_t * paths);
void ip6_punt_redirect_del (u32 rx_sw_if_index);
int vnet_set_ip6_classify_intfc (vlib_main_t * vm, u32 sw_if_index,
diff --git a/src/vnet/ip/ip6_punt_drop.c b/src/vnet/ip/ip6_punt_drop.c
index d1145fbf82e..330ef55483f 100644
--- a/src/vnet/ip/ip6_punt_drop.c
+++ b/src/vnet/ip/ip6_punt_drop.c
@@ -227,16 +227,6 @@ VLIB_CLI_COMMAND (ip6_punt_policer_command, static) =
.short_help = "ip6 punt policer [add|del] <index>",
};
-extern ip_punt_redirect_t ip6_punt_redirect_cfg;
-
-#ifndef CLIB_MARCH_VARIANT
-ip_punt_redirect_t ip6_punt_redirect_cfg = {
- .any_rx_sw_if_index = {
- .tx_sw_if_index = ~0,
- .adj_index = ADJ_INDEX_INVALID,
- },
-};
-#endif /* CLIB_MARCH_VARIANT */
/* *INDENT-ON* */
#define foreach_ip6_punt_redirect_error \
@@ -262,7 +252,7 @@ VLIB_NODE_FN (ip6_punt_redirect_node) (vlib_main_t * vm,
{
return (ip_punt_redirect (vm, node, frame,
vnet_feat_arc_ip6_punt.feature_arc_index,
- &ip6_punt_redirect_cfg));
+ FIB_PROTOCOL_IP6));
}
/* *INDENT-OFF* */
@@ -290,17 +280,33 @@ VNET_FEATURE_INIT (ip6_punt_redirect_node, static) = {
/* *INDENT-ON* */
#ifndef CLIB_MARCH_VARIANT
+
void
ip6_punt_redirect_add (u32 rx_sw_if_index,
u32 tx_sw_if_index, ip46_address_t * nh)
{
- ip_punt_redirect_rx_t rx = {
- .tx_sw_if_index = tx_sw_if_index,
- .nh = *nh,
+ /* *INDENT-OFF* */
+ fib_route_path_t *rpaths = NULL, rpath = {
+ .frp_proto = DPO_PROTO_IP6,
+ .frp_addr = *nh,
+ .frp_sw_if_index = tx_sw_if_index,
+ .frp_weight = 1,
+ .frp_fib_index = ~0,
};
+ /* *INDENT-ON* */
+ vec_add1 (rpaths, rpath);
- ip_punt_redirect_add (&ip6_punt_redirect_cfg,
- rx_sw_if_index, &rx, FIB_PROTOCOL_IP6, VNET_LINK_IP6);
+ ip6_punt_redirect_add_paths (rx_sw_if_index, rpaths);
+
+ vec_free (rpaths);
+}
+
+void
+ip6_punt_redirect_add_paths (u32 rx_sw_if_index, fib_route_path_t * rpaths)
+{
+ ip_punt_redirect_add (FIB_PROTOCOL_IP6,
+ rx_sw_if_index,
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP6, rpaths);
vnet_feature_enable_disable ("ip6-punt", "ip6-punt-redirect", 0, 1, 0, 0);
}
@@ -310,7 +316,7 @@ ip6_punt_redirect_del (u32 rx_sw_if_index)
{
vnet_feature_enable_disable ("ip6-punt", "ip6-punt-redirect", 0, 0, 0, 0);
- ip_punt_redirect_del (&ip6_punt_redirect_cfg, rx_sw_if_index);
+ ip_punt_redirect_del (FIB_PROTOCOL_IP6, rx_sw_if_index);
}
#endif /* CLIB_MARCH_VARIANT */
@@ -320,10 +326,9 @@ ip6_punt_redirect_cmd (vlib_main_t * vm,
vlib_cli_command_t * cmd)
{
unformat_input_t _line_input, *line_input = &_line_input;
+ fib_route_path_t *rpaths = NULL, rpath;
clib_error_t *error = 0;
- u32 rx_sw_if_index = 0;
- u32 tx_sw_if_index = 0;
- ip46_address_t nh;
+ u32 rx_sw_if_index = ~0;
vnet_main_t *vnm;
u8 is_add;
@@ -344,14 +349,9 @@ ip6_punt_redirect_cmd (vlib_main_t * vm,
else if (unformat (line_input, "rx %U",
unformat_vnet_sw_interface, vnm, &rx_sw_if_index))
;
- else if (unformat (line_input, "via %U %U",
- unformat_ip6_address,
- &nh.ip6,
- unformat_vnet_sw_interface, vnm, &tx_sw_if_index))
- ;
else if (unformat (line_input, "via %U",
- unformat_vnet_sw_interface, vnm, &tx_sw_if_index))
- clib_memset (&nh, 0, sizeof (nh));
+ unformat_fib_route_path, &rpath))
+ vec_add1 (rpaths, rpath);
else
{
error = unformat_parse_error (line_input);
@@ -359,19 +359,20 @@ ip6_punt_redirect_cmd (vlib_main_t * vm,
}
}
+ if (~0 == rx_sw_if_index)
+ {
+ error = unformat_parse_error (line_input);
+ goto done;
+ }
+
if (is_add)
{
- if (rx_sw_if_index && tx_sw_if_index)
- {
- ip6_punt_redirect_add (rx_sw_if_index, tx_sw_if_index, &nh);
- }
+ if (vec_len (rpaths))
+ ip6_punt_redirect_add_paths (rx_sw_if_index, rpaths);
}
else
{
- if (rx_sw_if_index)
- {
- ip6_punt_redirect_del (rx_sw_if_index);
- }
+ ip6_punt_redirect_del (rx_sw_if_index);
}
done:
@@ -394,45 +395,7 @@ VLIB_CLI_COMMAND (ip6_punt_redirect_command, static) =
/* *INDENT-ON* */
#ifndef CLIB_MARCH_VARIANT
-ip_punt_redirect_detail_t *
-ip6_punt_redirect_entries (u32 sw_if_index)
-{
- ip_punt_redirect_rx_t *pr;
- ip_punt_redirect_detail_t *prs = 0;
- u32 rx_sw_if_index;
-
- vec_foreach_index (rx_sw_if_index,
- ip6_punt_redirect_cfg.redirect_by_rx_sw_if_index)
- {
- if (sw_if_index == ~0 || sw_if_index == rx_sw_if_index)
- {
- pr =
- &ip6_punt_redirect_cfg.redirect_by_rx_sw_if_index[rx_sw_if_index];
- if (NULL != pr && ~0 != pr->tx_sw_if_index)
- {
- ip_punt_redirect_detail_t detail = {.rx_sw_if_index =
- rx_sw_if_index,
- .punt_redirect = *pr
- };
- vec_add1 (prs, detail);
- }
- }
- }
- if (~0 != ip6_punt_redirect_cfg.any_rx_sw_if_index.tx_sw_if_index)
- {
- pr = &ip6_punt_redirect_cfg.any_rx_sw_if_index;
- if (NULL != pr)
- {
- ip_punt_redirect_detail_t detail = {.rx_sw_if_index =
- rx_sw_if_index,
- .punt_redirect = *pr
- };
- vec_add1 (prs, detail);
- }
- }
- return prs;
-}
#endif /* CLIB_MARCH_VARIANT */
static clib_error_t *
@@ -440,7 +403,7 @@ ip6_punt_redirect_show_cmd (vlib_main_t * vm,
unformat_input_t * main_input,
vlib_cli_command_t * cmd)
{
- vlib_cli_output (vm, "%U", format_ip_punt_redirect, &ip6_punt_redirect_cfg);
+ vlib_cli_output (vm, "%U", format_ip_punt_redirect, FIB_PROTOCOL_IP6);
return (NULL);
}
diff --git a/src/vnet/ip/ip_api.c b/src/vnet/ip/ip_api.c
index 06caf111b91..bcbcf5ac8d4 100644
--- a/src/vnet/ip/ip_api.c
+++ b/src/vnet/ip/ip_api.c
@@ -44,6 +44,7 @@
#include <vnet/ip/ip_source_and_port_range_check.h>
#include <vnet/fib/ip4_fib.h>
#include <vnet/fib/ip6_fib.h>
+#include <vnet/fib/fib_path_list.h>
#include <vnet/ip/ip6_hop_by_hop.h>
#include <vnet/ip/ip4_reassembly.h>
#include <vnet/ip/ip6_reassembly.h>
@@ -3358,74 +3359,76 @@ void
REPLY_MACRO (VL_API_IP_REASSEMBLY_ENABLE_DISABLE_REPLY);
}
-void
-send_ip_punt_redirect_details (vl_api_registration_t * reg,
- u32 context, u32 sw_if_index,
- ip_punt_redirect_rx_t * pr, u8 is_ipv6)
+typedef struct ip_punt_redirect_walk_ctx_t_
{
+ vl_api_registration_t *reg;
+ u32 context;
+} ip_punt_redirect_walk_ctx_t;
+
+static walk_rc_t
+send_ip_punt_redirect_details (u32 rx_sw_if_index,
+ const ip_punt_redirect_rx_t * ipr, void *arg)
+{
+ fib_route_path_encode_t *api_rpaths = NULL;
+ ip_punt_redirect_walk_ctx_t *ctx = arg;
vl_api_ip_punt_redirect_details_t *mp;
mp = vl_msg_api_alloc (sizeof (*mp));
if (!mp)
- return;
+ return (WALK_STOP);;
clib_memset (mp, 0, sizeof (*mp));
mp->_vl_msg_id = ntohs (VL_API_IP_PUNT_REDIRECT_DETAILS);
- mp->context = context;
- mp->punt.rx_sw_if_index = htonl (sw_if_index);
- mp->punt.tx_sw_if_index = htonl (pr->tx_sw_if_index);
- if (is_ipv6)
- {
- ip_address_encode (&pr->nh, IP46_TYPE_IP6, &mp->punt.nh);
- }
- else
- {
- ip_address_encode (&pr->nh, IP46_TYPE_IP4, &mp->punt.nh);
- }
+ mp->context = ctx->context;
- vl_api_send_msg (reg, (u8 *) mp);
+ fib_path_list_walk_w_ext (ipr->pl, NULL, fib_path_encode, &api_rpaths);
+
+ mp->punt.rx_sw_if_index = htonl (rx_sw_if_index);
+ mp->punt.tx_sw_if_index = htonl (api_rpaths[0].rpath.frp_sw_if_index);
+
+ ip_address_encode (&api_rpaths[0].rpath.frp_addr,
+ fib_proto_to_ip46 (ipr->fproto), &mp->punt.nh);
+
+ vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+ vec_free (api_rpaths);
+
+ return (WALK_CONTINUE);
}
static void
vl_api_ip_punt_redirect_dump_t_handler (vl_api_ip_punt_redirect_dump_t * mp)
{
vl_api_registration_t *reg;
- u32 sw_if_index;
- int rv __attribute__ ((unused)) = 0;
+ fib_protocol_t fproto;
- sw_if_index = ntohl (mp->sw_if_index);
reg = vl_api_client_index_to_registration (mp->client_index);
if (!reg)
return;
- if (~0 != sw_if_index)
- VALIDATE_SW_IF_INDEX (mp);
+ fproto = mp->is_ipv6 ? FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4;
- ip_punt_redirect_detail_t *pr, *prs;
- if (mp->is_ipv6)
+ ip_punt_redirect_walk_ctx_t ctx = {
+ .reg = reg,
+ .context = mp->context,
+ };
+
+ if (~0 != mp->sw_if_index)
{
- prs = ip6_punt_redirect_entries (sw_if_index);
- /* *INDENT-OFF* */
- vec_foreach (pr, prs)
- {
- send_ip_punt_redirect_details (reg, mp->context, pr->rx_sw_if_index, &pr->punt_redirect, 1);
- }
- /* *INDENT-ON* */
- vec_free (prs);
+ u32 rx_sw_if_index;
+ index_t pri;
+
+ rx_sw_if_index = ntohl (mp->sw_if_index);
+ pri = ip_punt_redirect_find (fproto, rx_sw_if_index);
+
+ if (INDEX_INVALID == pri)
+ return;
+
+ send_ip_punt_redirect_details (rx_sw_if_index,
+ ip_punt_redirect_get (pri), &ctx);
}
else
- {
- prs = ip4_punt_redirect_entries (sw_if_index);
- /* *INDENT-OFF* */
- vec_foreach (pr, prs)
- {
- send_ip_punt_redirect_details (reg, mp->context, pr->rx_sw_if_index, &pr->punt_redirect, 0);
- }
- /* *INDENT-ON* */
- vec_free (prs);
- }
-
- BAD_SW_IF_INDEX_LABEL;
+ ip_punt_redirect_walk (fproto, send_ip_punt_redirect_details, &ctx);
}
#define vl_msg_name_crc_list
diff --git a/src/vnet/ip/ip_punt_drop.c b/src/vnet/ip/ip_punt_drop.c
new file mode 100644
index 00000000000..01577a472ce
--- /dev/null
+++ b/src/vnet/ip/ip_punt_drop.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2015 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 <vnet/ip/ip.h>
+#include <vnet/ip/ip_punt_drop.h>
+#include <vnet/policer/policer.h>
+#include <vnet/policer/police_inlines.h>
+#include <vnet/fib/fib_path_list.h>
+
+ip_punt_redirect_cfg_t ip_punt_redirect_cfg;
+
+u8 *
+format_ip_punt_redirect_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 *);
+ ip_punt_redirect_trace_t *t = va_arg (*args, ip_punt_redirect_trace_t *);
+
+ if (INDEX_INVALID == t->rrxi)
+ s = format (s, "drop");
+ else
+ s = format (s, "via redirect:%d", t->rrxi);
+
+ return s;
+}
+
+static void
+ip_punt_redirect_stack (ip_punt_redirect_rx_t * ipr)
+{
+ dpo_id_t dpo = DPO_INVALID;
+ vlib_node_t *pnode;
+
+ fib_path_list_contribute_forwarding (ipr->pl,
+ ipr->payload_type,
+ FIB_PATH_LIST_FWD_FLAG_COLLAPSE, &dpo);
+
+ if (FIB_PROTOCOL_IP4 == ipr->fproto)
+ pnode =
+ vlib_get_node_by_name (vlib_get_main (), (u8 *) "ip4-punt-redirect");
+ else
+ pnode =
+ vlib_get_node_by_name (vlib_get_main (), (u8 *) "ip6-punt-redirect");
+
+ dpo_stack_from_node (pnode->index, &ipr->dpo, &dpo);
+ dpo_reset (&dpo);
+}
+
+index_t
+ip_punt_redirect_find (fib_protocol_t fproto, u32 rx_sw_if_index)
+{
+ index_t *rxs;
+
+ rxs = ip_punt_redirect_cfg.redirect_by_rx_sw_if_index[fproto];
+
+ if (vec_len (rxs) <= rx_sw_if_index)
+ return (INDEX_INVALID);
+
+ return rxs[rx_sw_if_index];
+}
+
+void
+ip_punt_redirect_add (fib_protocol_t fproto,
+ u32 rx_sw_if_index,
+ fib_forward_chain_type_t ct, fib_route_path_t * rpaths)
+{
+ ip_punt_redirect_rx_t *ipr;
+ index_t ipri;
+
+ if (~0 == rx_sw_if_index)
+ rx_sw_if_index = 0;
+
+ vec_validate_init_empty (ip_punt_redirect_cfg.redirect_by_rx_sw_if_index
+ [fproto], rx_sw_if_index, INDEX_INVALID);
+
+ pool_get (ip_punt_redirect_cfg.pool, ipr);
+ ipri = ipr - ip_punt_redirect_cfg.pool;
+
+ ip_punt_redirect_cfg.redirect_by_rx_sw_if_index[fproto][rx_sw_if_index] =
+ ipri;
+
+ fib_node_init (&ipr->node, FIB_NODE_TYPE_IP_PUNT_REDIRECT);
+ ipr->fproto = fproto;
+ ipr->payload_type = ct;
+
+ ipr->pl = fib_path_list_create (FIB_PATH_LIST_FLAG_NO_URPF, rpaths);
+
+ ipr->sibling = fib_path_list_child_add (ipr->pl,
+ FIB_NODE_TYPE_IP_PUNT_REDIRECT,
+ ipri);
+
+ ip_punt_redirect_stack (ipr);
+}
+
+void
+ip_punt_redirect_del (fib_protocol_t fproto, u32 rx_sw_if_index)
+{
+ ip_punt_redirect_rx_t *ipr;
+ index_t *rxs;
+
+ if (~0 == rx_sw_if_index)
+ rx_sw_if_index = 0;
+
+ rxs = ip_punt_redirect_cfg.redirect_by_rx_sw_if_index[fproto];
+
+ if ((vec_len (rxs) <= rx_sw_if_index) ||
+ (INDEX_INVALID == rxs[rx_sw_if_index]))
+ return;
+
+ ipr = ip_punt_redirect_get (rxs[rx_sw_if_index]);
+
+ fib_path_list_child_remove (ipr->pl, ipr->sibling);
+ dpo_reset (&ipr->dpo);
+ pool_put (ip_punt_redirect_cfg.pool, ipr);
+
+ rxs[rx_sw_if_index] = INDEX_INVALID;
+}
+
+u8 *
+format_ip_punt_redirect (u8 * s, va_list * args)
+{
+ fib_protocol_t fproto = va_arg (*args, int);
+ ip_punt_redirect_rx_t *rx;
+ index_t *rxs;
+ u32 rx_sw_if_index;
+ vnet_main_t *vnm = vnet_get_main ();
+
+ rxs = ip_punt_redirect_cfg.redirect_by_rx_sw_if_index[fproto];
+
+ vec_foreach_index (rx_sw_if_index, rxs)
+ {
+ if (INDEX_INVALID == rxs[rx_sw_if_index])
+ continue;
+
+ rx = ip_punt_redirect_get (rxs[rx_sw_if_index]);
+
+ s = format (s, " rx %U via:\n",
+ format_vnet_sw_interface_name, vnm,
+ vnet_get_sw_interface (vnm, rx_sw_if_index));
+ s = format (s, " %U", format_fib_path_list, rx->pl, 2);
+ s = format (s, " forwarding\n", format_dpo_id, &rx->dpo, 0);
+ s = format (s, " %U\n", format_dpo_id, &rx->dpo, 0);
+ }
+
+ return (s);
+}
+
+void
+ip_punt_redirect_walk (fib_protocol_t fproto,
+ ip_punt_redirect_walk_cb_t cb, void *ctx)
+{
+ ip_punt_redirect_rx_t *rx;
+ u32 ii, rx_sw_if_index;
+ index_t *rxs;
+
+ rxs = ip_punt_redirect_cfg.redirect_by_rx_sw_if_index[fproto];
+
+ vec_foreach_index (ii, rxs)
+ {
+ if (INDEX_INVALID == rxs[ii])
+ continue;
+
+ rx = ip_punt_redirect_get (rxs[ii]);
+
+ rx_sw_if_index = (ii == 0 ? ~0 : ii);
+ cb (rx_sw_if_index, rx, ctx);
+ }
+}
+
+static fib_node_t *
+ip_punt_redirect_get_node (fib_node_index_t index)
+{
+ ip_punt_redirect_rx_t *ipr = ip_punt_redirect_get (index);
+ return (&(ipr->node));
+}
+
+static ip_punt_redirect_rx_t *
+ip_punt_redirect_get_from_node (fib_node_t * node)
+{
+ return ((ip_punt_redirect_rx_t *) (((char *) node) -
+ STRUCT_OFFSET_OF (ip_punt_redirect_rx_t,
+ node)));
+}
+
+static void
+ip_punt_redirect_last_lock_gone (fib_node_t * node)
+{
+ /*
+ * the lifetime of the entry is managed by the table.
+ */
+ ASSERT (0);
+}
+
+/*
+ * A back walk has reached this BIER entry
+ */
+static fib_node_back_walk_rc_t
+ip_punt_redirect_back_walk_notify (fib_node_t * node,
+ fib_node_back_walk_ctx_t * ctx)
+{
+ /*
+ * re-populate the ECMP tables with new choices
+ */
+ ip_punt_redirect_rx_t *ipr = ip_punt_redirect_get_from_node (node);
+
+ ip_punt_redirect_stack (ipr);
+
+ /*
+ * no need to propagate further up the graph, since there's nothing there
+ */
+ return (FIB_NODE_BACK_WALK_CONTINUE);
+}
+
+/*
+ * The BIER fmask's graph node virtual function table
+ */
+static const fib_node_vft_t ip_punt_redirect_vft = {
+ .fnv_get = ip_punt_redirect_get_node,
+ .fnv_last_lock = ip_punt_redirect_last_lock_gone,
+ .fnv_back_walk = ip_punt_redirect_back_walk_notify,
+};
+
+static clib_error_t *
+ip_punt_drop_init (vlib_main_t * vm)
+{
+ fib_node_register_type (FIB_NODE_TYPE_IP_PUNT_REDIRECT,
+ &ip_punt_redirect_vft);
+
+ return (NULL);
+}
+
+VLIB_INIT_FUNCTION (ip_punt_drop_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/ip/ip_punt_drop.h b/src/vnet/ip/ip_punt_drop.h
index ccf88e593cf..3e2796ee868 100644
--- a/src/vnet/ip/ip_punt_drop.h
+++ b/src/vnet/ip/ip_punt_drop.h
@@ -194,19 +194,19 @@ ip_punt_policer (vlib_main_t * vm,
typedef struct ip_punt_redirect_rx_t_
{
/**
- * The next-hop to send redirected packets to
+ * Node linkage into the FIB graph
*/
- ip46_address_t nh;
+ fib_node_t node;
- /**
- * the TX interface to send redirected packets
- */
- u32 tx_sw_if_index;
+ fib_protocol_t fproto;
+ fib_forward_chain_type_t payload_type;
+ fib_node_index_t pl;
+ u32 sibling;
/**
- * redirect forwarding adjacency
+ * redirect forwarding
*/
- adj_index_t adj_index;
+ dpo_id_t dpo;
} ip_punt_redirect_rx_t;
/**
@@ -214,16 +214,17 @@ typedef struct ip_punt_redirect_rx_t_
*/
typedef struct ip_punt_redirect_t_
{
- /**
- * any RX interface redirect
- */
- ip_punt_redirect_rx_t any_rx_sw_if_index;
+ ip_punt_redirect_rx_t *pool;
+
+ /**
+ * per-RX interface configuration.
+ * sw_if_index = 0 (from which packets are never received) is used to
+ * indicate 'from-any'
+ */
+ index_t *redirect_by_rx_sw_if_index[FIB_PROTOCOL_IP_MAX];
+} ip_punt_redirect_cfg_t;
- /**
- * per-RX interface configuration
- */
- ip_punt_redirect_rx_t *redirect_by_rx_sw_if_index;
-} ip_punt_redirect_t;
+extern ip_punt_redirect_cfg_t ip_punt_redirect_cfg;
/**
* IP punt redirect next nodes
@@ -241,75 +242,51 @@ typedef enum ip_punt_redirect_next_t_
*/
typedef struct ip4_punt_redirect_trace_t_
{
- ip_punt_redirect_rx_t redirect;
+ index_t rrxi;
u32 next;
} ip_punt_redirect_trace_t;
-typedef struct ip_punt_redirect_detail_t_
-{
- /**
- * the RX interface
- */
- u32 rx_sw_if_index;
- /**
- * IP punt redirect configuration
- */
- ip_punt_redirect_rx_t punt_redirect;
-} ip_punt_redirect_detail_t;
-
/**
* Add a punt redirect entry
*/
-extern void ip_punt_redirect_add (ip_punt_redirect_t * cfg,
+extern void ip_punt_redirect_add (fib_protocol_t fproto,
u32 rx_sw_if_index,
- ip_punt_redirect_rx_t * redirect,
- fib_protocol_t fproto, vnet_link_t linkt);
-extern void ip_punt_redirect_del (ip_punt_redirect_t * cfg,
- u32 rx_sw_if_index);
+ fib_forward_chain_type_t ct,
+ fib_route_path_t * rpaths);
+
+extern void ip_punt_redirect_del (fib_protocol_t fproto, u32 rx_sw_if_index);
+extern index_t ip_punt_redirect_find (fib_protocol_t fproto,
+ u32 rx_sw_if_index);
extern u8 *format_ip_punt_redirect (u8 * s, va_list * args);
extern u8 *format_ip_punt_redirect_trace (u8 * s, va_list * args);
-extern ip_punt_redirect_detail_t *ip4_punt_redirect_entries (u32 sw_if_index);
-extern ip_punt_redirect_detail_t *ip6_punt_redirect_entries (u32 sw_if_index);
+typedef walk_rc_t (*ip_punt_redirect_walk_cb_t) (u32 rx_sw_if_index,
+ const ip_punt_redirect_rx_t *
+ redirect, void *arg);
+extern void ip_punt_redirect_walk (fib_protocol_t fproto,
+ ip_punt_redirect_walk_cb_t cb, void *ctx);
-always_inline u32
-ip_punt_redirect_tx_via_adj (vlib_buffer_t * b0, adj_index_t ai)
+static_always_inline ip_punt_redirect_rx_t *
+ip_punt_redirect_get (index_t rrxi)
{
- ip_adjacency_t *adj = adj_get (ai);
- u32 next0;
-
- vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ai;
-
- switch (adj->lookup_next_index)
- {
- case IP_LOOKUP_NEXT_ARP:
- next0 = IP_PUNT_REDIRECT_NEXT_ARP;
- break;
- case IP_LOOKUP_NEXT_REWRITE:
- next0 = IP_PUNT_REDIRECT_NEXT_TX;
- break;
- default:
- next0 = IP_PUNT_REDIRECT_NEXT_DROP;
- break;
- }
-
- return (next0);
+ return (pool_elt_at_index (ip_punt_redirect_cfg.pool, rrxi));
}
always_inline uword
ip_punt_redirect (vlib_main_t * vm,
vlib_node_runtime_t * node,
- vlib_frame_t * frame,
- u8 arc_index, ip_punt_redirect_t * redirect)
+ vlib_frame_t * frame, u8 arc_index, fib_protocol_t fproto)
{
u32 *from, *to_next, n_left_from, n_left_to_next, next_index;
vnet_feature_main_t *fm = &feature_main;
vnet_feature_config_main_t *cm = &fm->feature_config_mains[arc_index];
+ index_t *redirects;
from = vlib_frame_vector_args (frame);
n_left_from = frame->n_vectors;
next_index = node->cached_next_index;
+ redirects = ip_punt_redirect_cfg.redirect_by_rx_sw_if_index[fproto];
while (n_left_from > 0)
{
@@ -317,13 +294,13 @@ ip_punt_redirect (vlib_main_t * vm,
while (n_left_from > 0 && n_left_to_next > 0)
{
- u32 rx_sw_if_index0;
+ u32 rx_sw_if_index0, rrxi0;
ip_punt_redirect_rx_t *rrx0;
vlib_buffer_t *b0;
u32 next0;
u32 bi0;
- rrx0 = NULL;
+ rrxi0 = INDEX_INVALID;
next0 = 0;
bi0 = to_next[0] = from[0];
@@ -339,24 +316,24 @@ ip_punt_redirect (vlib_main_t * vm,
rx_sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
- if (vec_len (redirect->redirect_by_rx_sw_if_index) >
- rx_sw_if_index0)
+ /*
+ * If config exists for this particular RX interface use it,
+ * else use the default (at RX = 0)
+ */
+ if (vec_len (redirects) > rx_sw_if_index0)
{
- rrx0 = &redirect->redirect_by_rx_sw_if_index[rx_sw_if_index0];
- if (~0 != rrx0->tx_sw_if_index)
- {
- next0 = ip_punt_redirect_tx_via_adj (b0, rrx0->adj_index);
- }
- else if (~0 != redirect->any_rx_sw_if_index.tx_sw_if_index)
- {
- rrx0 = &redirect->any_rx_sw_if_index;
- next0 = ip_punt_redirect_tx_via_adj (b0, rrx0->adj_index);
- }
+ rrxi0 = redirects[rx_sw_if_index0];
+ if (INDEX_INVALID == rrxi0)
+ rrxi0 = redirects[0];
}
- else if (~0 != redirect->any_rx_sw_if_index.tx_sw_if_index)
+ else if (vec_len (redirects) >= 1)
+ rrxi0 = redirects[0];
+
+ if (PREDICT_TRUE (INDEX_INVALID != rrxi0))
{
- rrx0 = &redirect->any_rx_sw_if_index;
- next0 = ip_punt_redirect_tx_via_adj (b0, rrx0->adj_index);
+ rrx0 = ip_punt_redirect_get (rrxi0);
+ vnet_buffer (b0)->ip.adj_index[VLIB_TX] = rrx0->dpo.dpoi_index;
+ next0 = rrx0->dpo.dpoi_next_node;
}
if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
@@ -364,8 +341,7 @@ ip_punt_redirect (vlib_main_t * vm,
ip_punt_redirect_trace_t *t =
vlib_add_trace (vm, node, b0, sizeof (*t));
t->next = next0;
- if (rrx0)
- t->redirect = *rrx0;
+ t->rrxi = rrxi0;
}
vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
diff --git a/test/vpp_ip_route.py b/test/vpp_ip_route.py
index 0bd6dd33735..db5f4b636bf 100644
--- a/test/vpp_ip_route.py
+++ b/test/vpp_ip_route.py
@@ -287,7 +287,7 @@ class VppRoutePath(object):
self.next_hop_id = next_hop_id
self.is_dvr = is_dvr
- def encode_labels(self):
+ def encode_labels(self, pad_labels=False):
lstack = []
for l in self.nh_labels:
if type(l) == VppMplsLabel:
@@ -295,9 +295,12 @@ class VppRoutePath(object):
else:
lstack.append({'label': l,
'ttl': 255})
+ if (pad_labels):
+ while (len(lstack) < 16):
+ lstack.append({})
return lstack
- def encode(self):
+ def encode(self, pad_labels=False):
return {'next_hop': self.nh_addr,
'weight': 1,
'preference': 0,
@@ -307,7 +310,7 @@ class VppRoutePath(object):
'afi': self.proto,
'is_udp_encap': self.is_udp_encap,
'n_labels': len(self.nh_labels),
- 'label_stack': self.encode_labels()}
+ 'label_stack': self.encode_labels(pad_labels)}
def __eq__(self, other):
if isinstance(other, self.__class__):
2009'>2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357