ipsec: add missing ipv6 ah code & ipv6 tests
Change-Id: I89e90193ded1beb6cb0950c15737f9467efac1c3
Signed-off-by: Klement Sekera <ksekera@cisco.com>
diff --git a/src/vnet/ipsec/ah.h b/src/vnet/ipsec/ah.h
index 37fc29a..f74ad9b 100644
--- a/src/vnet/ipsec/ah.h
+++ b/src/vnet/ipsec/ah.h
@@ -49,6 +49,15 @@
}) ip6_and_ah_header_t;
/* *INDENT-ON* */
+always_inline u8
+ah_calc_icv_padding_len (u8 icv_size, int is_ipv6)
+{
+ ASSERT (0 == is_ipv6 || 1 == is_ipv6);
+ const u8 req_multiple = 4 + 4 * is_ipv6; // 4 for ipv4, 8 for ipv6
+ const u8 total_size = sizeof (ah_header_t) + icv_size;
+ return (req_multiple - total_size % req_multiple) % req_multiple;
+}
+
#endif /* __AH_H__ */
/*
diff --git a/src/vnet/ipsec/ah_decrypt.c b/src/vnet/ipsec/ah_decrypt.c
index c487d82..abe2e6f 100644
--- a/src/vnet/ipsec/ah_decrypt.c
+++ b/src/vnet/ipsec/ah_decrypt.c
@@ -112,6 +112,10 @@
u8 ip_hdr_size = 0;
u8 tos = 0;
u8 ttl = 0;
+ u32 ip_version_traffic_class_and_flow_label = 0;
+ u8 hop_limit = 0;
+ u8 nexthdr = 0;
+ u8 icv_padding_len = 0;
i_bi0 = from[0];
@@ -125,12 +129,29 @@
to_next[0] = i_bi0;
to_next += 1;
ih4 = vlib_buffer_get_current (i_b0);
- ip_hdr_size = ip4_header_bytes (ih4);
- ah0 = (ah_header_t *) ((u8 *) ih4 + ip_hdr_size);
-
+ ih6 = vlib_buffer_get_current (i_b0);
sa_index0 = vnet_buffer (i_b0)->ipsec.sad_index;
sa0 = pool_elt_at_index (im->sad, sa_index0);
+ if ((ih4->ip_version_and_header_length & 0xF0) == 0x40)
+ {
+ ip_hdr_size = ip4_header_bytes (ih4);
+ ah0 = (ah_header_t *) ((u8 *) ih4 + ip_hdr_size);
+ }
+ else if ((ih4->ip_version_and_header_length & 0xF0) == 0x60)
+ {
+ ip6_ext_header_t *prev = NULL;
+ ip6_ext_header_find_t (ih6, prev, ah0, IP_PROTOCOL_IPSEC_AH);
+ ip_hdr_size = sizeof (ip6_header_t);
+ ASSERT ((u8 *) ah0 - (u8 *) ih6 == ip_hdr_size);
+ }
+ else
+ {
+ vlib_node_increment_counter (vm, ah_decrypt_node.index,
+ AH_DECRYPT_ERROR_NOT_IP, 1);
+ goto trace;
+ }
+
seq = clib_host_to_net_u32 (ah0->seq_no);
/* anti-replay check */
//TODO UT remaining
@@ -164,9 +185,7 @@
u8 digest[64];
memset (sig, 0, sizeof (sig));
memset (digest, 0, sizeof (digest));
- u8 *icv =
- vlib_buffer_get_current (i_b0) + ip_hdr_size +
- sizeof (ah_header_t);
+ u8 *icv = ah0->auth_data;
memcpy (digest, icv, icv_size);
memset (icv, 0, icv_size);
@@ -178,7 +197,20 @@
ih4->ttl = 0;
ih4->checksum = 0;
ih4->flags_and_fragment_offset = 0;
- } //TODO else part for IPv6
+ icv_padding_len =
+ ah_calc_icv_padding_len (icv_size, 0 /* is_ipv6 */ );
+ }
+ else
+ {
+ ip_version_traffic_class_and_flow_label =
+ ih6->ip_version_traffic_class_and_flow_label;
+ hop_limit = ih6->hop_limit;
+ ih6->ip_version_traffic_class_and_flow_label = 0x60;
+ ih6->hop_limit = 0;
+ nexthdr = ah0->nexthdr;
+ icv_padding_len =
+ ah_calc_icv_padding_len (icv_size, 1 /* is_ipv6 */ );
+ }
hmac_calc (sa0->integ_alg, sa0->integ_key, sa0->integ_key_len,
(u8 *) ih4, i_b0->current_length, sig, sa0->use_esn,
sa0->seq_hi);
@@ -204,9 +236,9 @@
}
-
vlib_buffer_advance (i_b0,
- ip_hdr_size + sizeof (ah_header_t) + icv_size);
+ ip_hdr_size + sizeof (ah_header_t) + icv_size +
+ icv_padding_len);
i_b0->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
/* transport mode */
@@ -251,15 +283,15 @@
{
if (PREDICT_FALSE (transport_ip6))
{
- ih6 =
- (ip6_header_t *) (i_b0->data +
- sizeof (ethernet_header_t));
vlib_buffer_advance (i_b0, -sizeof (ip6_header_t));
oh6 = vlib_buffer_get_current (i_b0);
memmove (oh6, ih6, sizeof (ip6_header_t));
next0 = AH_DECRYPT_NEXT_IP6_INPUT;
- oh6->protocol = ah0->nexthdr;
+ oh6->protocol = nexthdr;
+ oh6->hop_limit = hop_limit;
+ oh6->ip_version_traffic_class_and_flow_label =
+ ip_version_traffic_class_and_flow_label;
oh6->payload_length =
clib_host_to_net_u16 (vlib_buffer_length_in_chain
(vm, i_b0) - sizeof (ip6_header_t));
diff --git a/src/vnet/ipsec/ah_encrypt.c b/src/vnet/ipsec/ah_encrypt.c
index 898c0f2..911dd33 100644
--- a/src/vnet/ipsec/ah_encrypt.c
+++ b/src/vnet/ipsec/ah_encrypt.c
@@ -110,6 +110,8 @@
u8 transport_mode = 0;
u8 tos = 0;
u8 ttl = 0;
+ u8 hop_limit = 0;
+ u32 ip_version_traffic_class_and_flow_label = 0;
i_bi0 = from[0];
from += 1;
@@ -159,6 +161,8 @@
icv_size =
em->ipsec_proto_main_integ_algs[sa0->integ_alg].trunc_size;
+ const u8 padding_len = ah_calc_icv_padding_len (icv_size, is_ipv6);
+ adv -= padding_len;
/*transport mode save the eth header before it is overwritten */
if (PREDICT_FALSE (!sa0->is_tunnel))
{
@@ -172,18 +176,18 @@
vlib_buffer_advance (i_b0, adv - icv_size);
- /* is ipv6 */
if (PREDICT_FALSE (is_ipv6))
- {
+ { /* is ipv6 */
ih6_0 = (ip6_and_ah_header_t *) ih0;
ip_hdr_size = sizeof (ip6_header_t);
oh6_0 = vlib_buffer_get_current (i_b0);
+ hop_limit = ih6_0->ip6.hop_limit;
+ ip_version_traffic_class_and_flow_label =
+ ih6_0->ip6.ip_version_traffic_class_and_flow_label;
if (PREDICT_TRUE (sa0->is_tunnel))
{
next_hdr_type = IP_PROTOCOL_IPV6;
- oh6_0->ip6.ip_version_traffic_class_and_flow_label =
- ih6_0->ip6.ip_version_traffic_class_and_flow_label;
}
else
{
@@ -192,12 +196,17 @@
}
oh6_0->ip6.protocol = IP_PROTOCOL_IPSEC_AH;
- oh6_0->ip6.hop_limit = 254;
+ oh6_0->ip6.hop_limit = 0;
+ oh6_0->ip6.ip_version_traffic_class_and_flow_label = 0x60;
+ oh6_0->ah.reserved = 0;
+ oh6_0->ah.nexthdr = next_hdr_type;
oh6_0->ah.spi = clib_net_to_host_u32 (sa0->spi);
oh6_0->ah.seq_no = clib_net_to_host_u32 (sa0->seq);
oh6_0->ip6.payload_length =
clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, i_b0) -
sizeof (ip6_header_t));
+ oh6_0->ah.hdrlen =
+ (sizeof (ah_header_t) + icv_size + padding_len) / 4 - 2;
}
else
{
@@ -227,11 +236,11 @@
oh0->ah.seq_no = clib_net_to_host_u32 (sa0->seq);
oh0->ip4.checksum = 0;
oh0->ah.nexthdr = next_hdr_type;
- oh0->ah.hdrlen = 4;
+ oh0->ah.hdrlen =
+ (sizeof (ah_header_t) + icv_size + padding_len) / 4 - 2;
}
-
if (PREDICT_TRUE
(!is_ipv6 && sa0->is_tunnel && !sa0->is_tunnel_ip6))
{
@@ -264,7 +273,8 @@
u8 sig[64];
memset (sig, 0, sizeof (sig));
u8 *digest =
- vlib_buffer_get_current (i_b0) + ip_hdr_size + icv_size;
+ vlib_buffer_get_current (i_b0) + ip_hdr_size +
+ sizeof (ah_header_t);
memset (digest, 0, icv_size);
unsigned size = hmac_calc (sa0->integ_alg, sa0->integ_key,
@@ -276,6 +286,9 @@
memcpy (digest, sig, size);
if (PREDICT_FALSE (is_ipv6))
{
+ oh6_0->ip6.hop_limit = hop_limit;
+ oh6_0->ip6.ip_version_traffic_class_and_flow_label =
+ ip_version_traffic_class_and_flow_label;
}
else
{
diff --git a/src/vnet/ipsec/ipsec_input.c b/src/vnet/ipsec/ipsec_input.c
index b7bb07b..ebfb909 100644
--- a/src/vnet/ipsec/ipsec_input.c
+++ b/src/vnet/ipsec/ipsec_input.c
@@ -348,13 +348,13 @@
};
/* *INDENT-ON* */
-VLIB_NODE_FUNCTION_MULTIARCH (ipsec_input_ip4_node, ipsec_input_ip4_node_fn)
- static vlib_node_registration_t ipsec_input_ip6_node;
+VLIB_NODE_FUNCTION_MULTIARCH (ipsec_input_ip4_node, ipsec_input_ip4_node_fn);
+static vlib_node_registration_t ipsec_input_ip6_node;
- static uword
- ipsec_input_ip6_node_fn (vlib_main_t * vm,
- vlib_node_runtime_t * node,
- vlib_frame_t * from_frame)
+static uword
+ipsec_input_ip6_node_fn (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * from_frame)
{
u32 n_left_from, *from, next_index, *to_next;
ipsec_main_t *im = &ipsec_main;
@@ -379,6 +379,7 @@
ip4_ipsec_config_t *c0;
ipsec_spd_t *spd0;
ipsec_policy_t *p0 = 0;
+ ah_header_t *ah0;
u32 header_size = sizeof (ip0[0]);
bi0 = to_next[0] = from[0];
@@ -396,6 +397,7 @@
ip0 = vlib_buffer_get_current (b0);
esp0 = (esp_header_t *) ((u8 *) ip0 + header_size);
+ ah0 = (ah_header_t *) ((u8 *) ip0 + header_size);
if (PREDICT_TRUE (ip0->protocol == IP_PROTOCOL_IPSEC_ESP))
{
@@ -426,6 +428,26 @@
goto trace0;
}
}
+ else if (ip0->protocol == IP_PROTOCOL_IPSEC_AH)
+ {
+ p0 = ipsec_input_ip6_protect_policy_match (spd0,
+ &ip0->src_address,
+ &ip0->dst_address,
+ clib_net_to_host_u32
+ (ah0->spi));
+
+ if (PREDICT_TRUE (p0 != 0))
+ {
+ p0->counter.packets++;
+ p0->counter.bytes +=
+ clib_net_to_host_u16 (ip0->payload_length);
+ p0->counter.bytes += header_size;
+ vnet_buffer (b0)->ipsec.sad_index = p0->sa_index;
+ vnet_buffer (b0)->ipsec.flags = 0;
+ next0 = im->ah_decrypt_next_index;
+ goto trace0;
+ }
+ }
trace0:
if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))