summaryrefslogtreecommitdiffstats
path: root/src/plugins/cnat/cnat_node.h
diff options
context:
space:
mode:
authorNathan Skrzypczak <nathan.skrzypczak@gmail.com>2020-09-10 17:44:41 +0200
committerDave Barach <openvpp@barachs.net>2020-09-25 19:55:39 +0000
commit613b2c3c78fbec12cc87a0095ee5488252449698 (patch)
treeb85ba0853e4ad8127a1213242d2a42d85e256b85 /src/plugins/cnat/cnat_node.h
parentece39214bcb05c535ba5de9af97b5f84f6911cba (diff)
cnat: Add support for SNat ICMP
Type: feature snat supports : * echo request/reply by allocating an identifier when translating echo requests * icmp errors in the same manner as dnat Change-Id: I684e983b0181f95c5eace5a984d40084e5625fa4 Signed-off-by: Nathan Skrzypczak <nathan.skrzypczak@gmail.com>
Diffstat (limited to 'src/plugins/cnat/cnat_node.h')
-rw-r--r--src/plugins/cnat/cnat_node.h211
1 files changed, 170 insertions, 41 deletions
diff --git a/src/plugins/cnat/cnat_node.h b/src/plugins/cnat/cnat_node.h
index a3967960078..2e3b0e0275a 100644
--- a/src/plugins/cnat/cnat_node.h
+++ b/src/plugins/cnat/cnat_node.h
@@ -43,6 +43,30 @@ icmp_type_is_error_message (u8 icmp_type)
}
static_always_inline u8
+icmp_type_is_echo (u8 icmp_type)
+{
+ switch (icmp_type)
+ {
+ case ICMP4_echo_request:
+ case ICMP4_echo_reply:
+ return 1;
+ }
+ return 0;
+}
+
+static_always_inline u8
+icmp6_type_is_echo (u8 icmp_type)
+{
+ switch (icmp_type)
+ {
+ case ICMP6_echo_request:
+ case ICMP6_echo_reply:
+ return 1;
+ }
+ return 0;
+}
+
+static_always_inline u8
icmp6_type_is_error_message (u8 icmp_type)
{
switch (icmp_type)
@@ -170,20 +194,39 @@ cnat_tcp_update_session_lifetime (tcp_header_t * tcp, u32 index)
}
static_always_inline void
-cnat_translation_icmp4 (ip4_header_t * outer_ip4, udp_header_t * outer_udp,
- ip4_address_t outer_new_addr[VLIB_N_DIR],
- u16 outer_new_port[VLIB_N_DIR], u8 snat_outer_ip)
+cnat_translation_icmp4_echo (ip4_header_t * ip4, icmp46_header_t * icmp,
+ ip4_address_t new_addr[VLIB_N_DIR],
+ u16 new_port[VLIB_N_DIR])
+{
+ ip_csum_t sum;
+ u16 old_port;
+ cnat_echo_header_t *echo = (cnat_echo_header_t *) (icmp + 1);
+
+ cnat_ip4_translate_l3 (ip4, new_addr);
+ old_port = echo->identifier;
+ echo->identifier = new_port[VLIB_RX];
+
+ sum = icmp->checksum;
+ sum = ip_csum_update (sum, old_port, new_port[VLIB_RX],
+ ip4_header_t /* cheat */ ,
+ length /* changed member */ );
+
+ icmp->checksum = ip_csum_fold (sum);
+}
+
+static_always_inline void
+cnat_translation_icmp4_error (ip4_header_t * outer_ip4,
+ icmp46_header_t * icmp,
+ ip4_address_t outer_new_addr[VLIB_N_DIR],
+ u16 outer_new_port[VLIB_N_DIR],
+ u8 snat_outer_ip)
{
- icmp46_header_t *icmp = (icmp46_header_t *) outer_udp;
ip4_address_t new_addr[VLIB_N_DIR];
ip4_address_t old_addr[VLIB_N_DIR];
u16 new_port[VLIB_N_DIR];
u16 old_port[VLIB_N_DIR];
ip_csum_t sum, old_ip_sum, inner_l4_sum, inner_l4_old_sum;
- if (!icmp_type_is_error_message (icmp->type))
- return;
-
ip4_header_t *ip4 = (ip4_header_t *) (icmp + 2);
udp_header_t *udp = (udp_header_t *) (ip4 + 1);
tcp_header_t *tcp = (tcp_header_t *) udp;
@@ -287,10 +330,18 @@ cnat_translation_ip4 (const cnat_session_t * session,
}
else if (ip4->protocol == IP_PROTOCOL_ICMP)
{
- /* SNAT only if src_addr was translated */
- u8 snat_outer_ip =
- (ip4->src_address.as_u32 == session->key.cs_ip[VLIB_RX].ip4.as_u32);
- cnat_translation_icmp4 (ip4, udp, new_addr, new_port, snat_outer_ip);
+ icmp46_header_t *icmp = (icmp46_header_t *) udp;
+ if (icmp_type_is_error_message (icmp->type))
+ {
+ /* SNAT only if src_addr was translated */
+ u8 snat_outer_ip =
+ (ip4->src_address.as_u32 ==
+ session->key.cs_ip[VLIB_RX].ip4.as_u32);
+ cnat_translation_icmp4_error (ip4, icmp, new_addr, new_port,
+ snat_outer_ip);
+ }
+ else if (icmp_type_is_echo (icmp->type))
+ cnat_translation_icmp4_echo (ip4, icmp, new_addr, new_port);
}
}
@@ -358,11 +409,52 @@ cnat_ip6_translate_l4 (ip6_header_t * ip6, udp_header_t * udp,
}
static_always_inline void
-cnat_translation_icmp6 (ip6_header_t * outer_ip6, udp_header_t * outer_udp,
- ip6_address_t outer_new_addr[VLIB_N_DIR],
- u16 outer_new_port[VLIB_N_DIR], u8 snat_outer_ip)
+cnat_translation_icmp6_echo (ip6_header_t * ip6, icmp46_header_t * icmp,
+ ip6_address_t new_addr[VLIB_N_DIR],
+ u16 new_port[VLIB_N_DIR])
+{
+ cnat_echo_header_t *echo = (cnat_echo_header_t *) (icmp + 1);
+ ip6_address_t old_addr[VLIB_N_DIR];
+ ip_csum_t sum;
+ u16 old_port;
+ old_port = echo->identifier;
+ ip6_address_copy (&old_addr[VLIB_TX], &ip6->dst_address);
+ ip6_address_copy (&old_addr[VLIB_RX], &ip6->src_address);
+
+ sum = icmp->checksum;
+
+ cnat_ip6_translate_l3 (ip6, new_addr);
+ if (has_ip6_address (&new_addr[VLIB_TX]))
+ {
+ sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[0]);
+ sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[1]);
+ sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[0]);
+ sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[1]);
+ }
+
+ if (has_ip6_address (&new_addr[VLIB_RX]))
+ {
+ sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[0]);
+ sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[1]);
+ sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[0]);
+ sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[1]);
+ }
+
+ echo->identifier = new_port[VLIB_RX];
+ sum = ip_csum_update (sum, old_port, new_port[VLIB_RX],
+ ip4_header_t /* cheat */ ,
+ length /* changed member */ );
+
+ icmp->checksum = ip_csum_fold (sum);
+}
+
+static_always_inline void
+cnat_translation_icmp6_error (ip6_header_t * outer_ip6,
+ icmp46_header_t * icmp,
+ ip6_address_t outer_new_addr[VLIB_N_DIR],
+ u16 outer_new_port[VLIB_N_DIR],
+ u8 snat_outer_ip)
{
- icmp46_header_t *icmp = (icmp46_header_t *) outer_udp;
ip6_address_t new_addr[VLIB_N_DIR];
ip6_address_t old_addr[VLIB_N_DIR];
ip6_address_t outer_old_addr[VLIB_N_DIR];
@@ -411,6 +503,7 @@ cnat_translation_icmp6 (ip6_header_t * outer_ip6, udp_header_t * outer_udp,
sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_RX].as_u64[1]);
}
+ /* Translate inner TCP / UDP */
if (ip6->protocol == IP_PROTOCOL_TCP)
{
inner_l4_old_sum = inner_l4_sum = tcp->checksum;
@@ -494,10 +587,18 @@ cnat_translation_ip6 (const cnat_session_t * session,
}
else if (ip6->protocol == IP_PROTOCOL_ICMP6)
{
- /* SNAT only if src_addr was translated */
- u8 snat_outer_ip = cmp_ip6_address (&ip6->src_address,
- &session->key.cs_ip[VLIB_RX].ip6);
- cnat_translation_icmp6 (ip6, udp, new_addr, new_port, snat_outer_ip);
+ icmp46_header_t *icmp = (icmp46_header_t *) udp;
+ if (icmp6_type_is_error_message (icmp->type))
+ {
+ /* SNAT only if src_addr was translated */
+ u8 snat_outer_ip = cmp_ip6_address (&ip6->src_address,
+ &session->key.
+ cs_ip[VLIB_RX].ip6);
+ cnat_translation_icmp6_error (ip6, icmp, new_addr, new_port,
+ snat_outer_ip);
+ }
+ else if (icmp6_type_is_echo (icmp->type))
+ cnat_translation_icmp6_echo (ip6, icmp, new_addr, new_port);
}
}
@@ -517,18 +618,32 @@ cnat_session_make_key (vlib_buffer_t * b, ip_address_family_t af,
if (PREDICT_FALSE (ip4->protocol == IP_PROTOCOL_ICMP))
{
icmp46_header_t *icmp = (icmp46_header_t *) (ip4 + 1);
- if (!icmp_type_is_error_message (icmp->type))
+ if (icmp_type_is_error_message (icmp->type))
+ {
+ ip4 = (ip4_header_t *) (icmp + 2); /* Use inner packet */
+ udp = (udp_header_t *) (ip4 + 1);
+ /* Swap dst & src for search as ICMP payload is reversed */
+ ip46_address_set_ip4 (&session->key.cs_ip[VLIB_RX],
+ &ip4->dst_address);
+ ip46_address_set_ip4 (&session->key.cs_ip[VLIB_TX],
+ &ip4->src_address);
+ session->key.cs_proto = ip4->protocol;
+ session->key.cs_port[VLIB_TX] = udp->src_port;
+ session->key.cs_port[VLIB_RX] = udp->dst_port;
+ }
+ else if (icmp_type_is_echo (icmp->type))
+ {
+ cnat_echo_header_t *echo = (cnat_echo_header_t *) (icmp + 1);
+ ip46_address_set_ip4 (&session->key.cs_ip[VLIB_TX],
+ &ip4->dst_address);
+ ip46_address_set_ip4 (&session->key.cs_ip[VLIB_RX],
+ &ip4->src_address);
+ session->key.cs_proto = ip4->protocol;
+ session->key.cs_port[VLIB_TX] = echo->identifier;
+ session->key.cs_port[VLIB_RX] = echo->identifier;
+ }
+ else
goto error;
- ip4 = (ip4_header_t *) (icmp + 2); /* Use inner packet */
- udp = (udp_header_t *) (ip4 + 1);
- /* Swap dst & src for search as ICMP payload is reversed */
- ip46_address_set_ip4 (&session->key.cs_ip[VLIB_RX],
- &ip4->dst_address);
- ip46_address_set_ip4 (&session->key.cs_ip[VLIB_TX],
- &ip4->src_address);
- session->key.cs_proto = ip4->protocol;
- session->key.cs_port[VLIB_TX] = udp->src_port;
- session->key.cs_port[VLIB_RX] = udp->dst_port;
}
else
{
@@ -550,18 +665,32 @@ cnat_session_make_key (vlib_buffer_t * b, ip_address_family_t af,
if (PREDICT_FALSE (ip6->protocol == IP_PROTOCOL_ICMP6))
{
icmp46_header_t *icmp = (icmp46_header_t *) (ip6 + 1);
- if (!icmp6_type_is_error_message (icmp->type))
+ if (icmp6_type_is_error_message (icmp->type))
+ {
+ ip6 = (ip6_header_t *) (icmp + 2); /* Use inner packet */
+ udp = (udp_header_t *) (ip6 + 1);
+ /* Swap dst & src for search as ICMP payload is reversed */
+ ip46_address_set_ip6 (&session->key.cs_ip[VLIB_RX],
+ &ip6->dst_address);
+ ip46_address_set_ip6 (&session->key.cs_ip[VLIB_TX],
+ &ip6->src_address);
+ session->key.cs_proto = ip6->protocol;
+ session->key.cs_port[VLIB_TX] = udp->src_port;
+ session->key.cs_port[VLIB_RX] = udp->dst_port;
+ }
+ else if (icmp6_type_is_echo (icmp->type))
+ {
+ cnat_echo_header_t *echo = (cnat_echo_header_t *) (icmp + 1);
+ ip46_address_set_ip6 (&session->key.cs_ip[VLIB_TX],
+ &ip6->dst_address);
+ ip46_address_set_ip6 (&session->key.cs_ip[VLIB_RX],
+ &ip6->src_address);
+ session->key.cs_proto = ip6->protocol;
+ session->key.cs_port[VLIB_TX] = echo->identifier;
+ session->key.cs_port[VLIB_RX] = echo->identifier;
+ }
+ else
goto error;
- ip6 = (ip6_header_t *) (icmp + 2); /* Use inner packet */
- udp = (udp_header_t *) (ip6 + 1);
- /* Swap dst & src for search as ICMP payload is reversed */
- ip46_address_set_ip6 (&session->key.cs_ip[VLIB_RX],
- &ip6->dst_address);
- ip46_address_set_ip6 (&session->key.cs_ip[VLIB_TX],
- &ip6->src_address);
- session->key.cs_proto = ip6->protocol;
- session->key.cs_port[VLIB_TX] = udp->src_port;
- session->key.cs_port[VLIB_RX] = udp->dst_port;
}
else
{