VPP-183: IPSec transport mode 33/1933/1
authorMatus Fabian <matfabia@cisco.com>
Mon, 11 Jul 2016 14:08:55 +0000 (07:08 -0700)
committerMatus Fabian <matfabia@cisco.com>
Mon, 11 Jul 2016 14:10:01 +0000 (07:10 -0700)
Change-Id: I22399aa9d55db0d91da7ba6acbbf552c0d201458
Signed-off-by: Matus Fabian <matfabia@cisco.com>
vnet/vnet/ipsec/esp_decrypt.c
vnet/vnet/ipsec/esp_encrypt.c
vnet/vnet/ipsec/ipsec_output.c
vpp/vpp-api/api.c

index 8d94f20..2e1e050 100644 (file)
@@ -42,7 +42,8 @@ typedef enum {
  _(NO_BUFFER, "No buffer (packed dropped)")         \
  _(DECRYPTION_FAILED, "ESP decryption failed")      \
  _(INTEG_ERROR, "Integrity check failed")           \
- _(REPLAY, "SA replayed packet")
+ _(REPLAY, "SA replayed packet")                    \
+ _(NOT_IP, "Not IP packet (dropped)")
 
 
 typedef enum {
@@ -265,6 +266,11 @@ esp_decrypt_node_fn (vlib_main_t * vm,
           ipsec_sa_t * sa0;
           u32 sa_index0 = ~0;
           u32 seq;
+          ip4_header_t *ih4 = 0, *oh4 = 0;
+          ip6_header_t *ih6 = 0, *oh6 = 0;
+          u8 tunnel_mode = 1;
+          u8 transport_ip6 = 0;
+
 
           i_bi0 = from[0];
           from += 1;
@@ -345,34 +351,103 @@ esp_decrypt_node_fn (vlib_main_t * vm,
             const int BLOCK_SIZE = 16;
             const int IV_SIZE = 16;
             esp_footer_t * f0;
+            u8 ip_hdr_size = 0;
 
             int blocks = (i_b0->current_length - sizeof (esp_header_t) - IV_SIZE) / BLOCK_SIZE;
 
             o_b0->current_data = sizeof(ethernet_header_t);
 
+            /* transport mode */
+            if (PREDICT_FALSE(!sa0->is_tunnel && !sa0->is_tunnel_ip6))
+              {
+                tunnel_mode = 0;
+                ih4 = (ip4_header_t *) (i_b0->data + sizeof(ethernet_header_t));
+                if (PREDICT_TRUE((ih4->ip_version_and_header_length & 0xF0 ) != 0x40))
+                  {
+                    if (PREDICT_TRUE((ih4->ip_version_and_header_length & 0xF0 ) == 0x60))
+                      {
+                        transport_ip6 = 1;
+                        ip_hdr_size = sizeof(ip6_header_t);
+                        ih6 = (ip6_header_t *) (i_b0->data + sizeof(ethernet_header_t));
+                        oh6 = vlib_buffer_get_current (o_b0);
+                      }
+                    else
+                      {
+                        vlib_node_increment_counter (vm, esp_decrypt_node.index,
+                                                     ESP_DECRYPT_ERROR_NOT_IP,
+                                                     1);
+                        o_b0 = 0;
+                        goto trace;
+                      }
+                  }
+                else
+                  {
+                    oh4 = vlib_buffer_get_current (o_b0);
+                    ip_hdr_size = sizeof(ip4_header_t);
+                  }
+              }
+
             esp_decrypt_aes_cbc(sa0->crypto_alg,
                                 esp0->data + IV_SIZE,
-                                (u8 *) vlib_buffer_get_current (o_b0),
+                                (u8 *) vlib_buffer_get_current (o_b0) + ip_hdr_size,
                                 BLOCK_SIZE * blocks,
                                 sa0->crypto_key,
                                 esp0->data);
 
-            o_b0->current_length = (blocks * 16) - 2;
+            o_b0->current_length = (blocks * 16) - 2 + ip_hdr_size;
             o_b0->flags = VLIB_BUFFER_TOTAL_LENGTH_VALID;
             f0 = (esp_footer_t *) ((u8 *) vlib_buffer_get_current (o_b0) + o_b0->current_length);
             o_b0->current_length -= f0->pad_length;
-            if (PREDICT_TRUE(f0->next_header == IP_PROTOCOL_IP_IN_IP))
-              next0 = ESP_DECRYPT_NEXT_IP4_INPUT;
-            else if (f0->next_header == IP_PROTOCOL_IPV6)
-              next0 = ESP_DECRYPT_NEXT_IP6_INPUT;
+
+            /* tunnel mode */
+            if (PREDICT_TRUE(tunnel_mode))
+              {
+                if (PREDICT_TRUE(f0->next_header == IP_PROTOCOL_IP_IN_IP))
+                  next0 = ESP_DECRYPT_NEXT_IP4_INPUT;
+                else if (f0->next_header == IP_PROTOCOL_IPV6)
+                  next0 = ESP_DECRYPT_NEXT_IP6_INPUT;
+                else
+                  {
+                    clib_warning("next header: 0x%x", f0->next_header);
+                    vlib_node_increment_counter (vm, esp_decrypt_node.index,
+                                                 ESP_DECRYPT_ERROR_DECRYPTION_FAILED,
+                                                 1);
+                    o_b0 = 0;
+                    goto trace;
+                  }
+              }
+            /* transport mode */
             else
               {
-                clib_warning("next header: 0x%x", f0->next_header);
-                vlib_node_increment_counter (vm, esp_decrypt_node.index,
-                                             ESP_DECRYPT_ERROR_DECRYPTION_FAILED,
-                                             1);
-                o_b0 = 0;
-                goto trace;
+                if (PREDICT_FALSE(transport_ip6))
+                  {
+                    next0 = ESP_DECRYPT_NEXT_IP6_INPUT;
+                    oh6->ip_version_traffic_class_and_flow_label =
+                        ih6->ip_version_traffic_class_and_flow_label;
+                    oh6->protocol = f0->next_header;
+                    oh6->hop_limit = ih6->hop_limit;
+                    oh6->src_address.as_u64[0] = ih6->src_address.as_u64[0];
+                    oh6->src_address.as_u64[1] = ih6->src_address.as_u64[1];
+                    oh6->dst_address.as_u64[0] = ih6->dst_address.as_u64[0];
+                    oh6->dst_address.as_u64[1] = ih6->dst_address.as_u64[1];
+                    oh6->payload_length = clib_host_to_net_u16 (
+                        vlib_buffer_length_in_chain (vm, o_b0) - sizeof(ip6_header_t));
+                  }
+                else
+                  {
+                    next0 = ESP_DECRYPT_NEXT_IP4_INPUT;
+                    oh4->ip_version_and_header_length = 0x45;
+                    oh4->tos = ih4->tos;
+                    oh4->fragment_id = 0;
+                    oh4->flags_and_fragment_offset = 0;
+                    oh4->ttl = ih4->ttl;
+                    oh4->protocol = f0->next_header;
+                    oh4->src_address.as_u32 = ih4->src_address.as_u32;
+                    oh4->dst_address.as_u32 = ih4->dst_address.as_u32;
+                    oh4->length = clib_host_to_net_u16 (
+                        vlib_buffer_length_in_chain (vm, o_b0));
+                    oh4->checksum = ip4_header_checksum (oh4);
+                  }
               }
 
             to_next[0] = o_bi0;
index ff0688c..596c3f4 100644 (file)
@@ -178,6 +178,8 @@ esp_encrypt_node_fn (vlib_main_t * vm,
           u8 is_ipv6;
           u8 ip_hdr_size;
           u8 next_hdr_type;
+          u32 ip_proto = 0;
+          u8 transport_mode = 0;
 
           i_bi0 = from[0];
           from += 1;
@@ -229,8 +231,13 @@ esp_encrypt_node_fn (vlib_main_t * vm,
                   ih6_0->ip6.ip_version_traffic_class_and_flow_label;
               oh6_0->ip6.protocol = IP_PROTOCOL_IPSEC_ESP;
               oh6_0->ip6.hop_limit = 254;
+              oh6_0->ip6.src_address.as_u64[0] = ih6_0->ip6.src_address.as_u64[0];
+              oh6_0->ip6.src_address.as_u64[1] = ih6_0->ip6.src_address.as_u64[1];
+              oh6_0->ip6.dst_address.as_u64[0] = ih6_0->ip6.dst_address.as_u64[0];
+              oh6_0->ip6.dst_address.as_u64[1] = ih6_0->ip6.dst_address.as_u64[1];
               oh6_0->esp.spi = clib_net_to_host_u32(sa0->spi);
               oh6_0->esp.seq = clib_net_to_host_u32(sa0->seq);
+              ip_proto = ih6_0->ip6.protocol;
             }
           else
             {
@@ -246,8 +253,11 @@ esp_encrypt_node_fn (vlib_main_t * vm,
               oh0->ip4.flags_and_fragment_offset = 0;
               oh0->ip4.ttl = 254;
               oh0->ip4.protocol = IP_PROTOCOL_IPSEC_ESP;
+              oh0->ip4.src_address.as_u32 = ih0->ip4.src_address.as_u32;
+              oh0->ip4.dst_address.as_u32 = ih0->ip4.dst_address.as_u32;
               oh0->esp.spi = clib_net_to_host_u32(sa0->spi);
               oh0->esp.seq = clib_net_to_host_u32(sa0->seq);
+              ip_proto = ih0->ip4.protocol;
             }
 
           if (PREDICT_TRUE(sa0->is_tunnel && !sa0->is_tunnel_ip6))
@@ -272,6 +282,13 @@ esp_encrypt_node_fn (vlib_main_t * vm,
             }
           else
             {
+              transport_mode = 1;
+              ethernet_header_t *ieh0, *oeh0;
+              ieh0 = (ethernet_header_t *) i_b0->data;
+              oeh0 = (ethernet_header_t *) o_b0->data;
+              clib_memcpy (oeh0, ieh0, sizeof(ethernet_header_t));
+              vlib_buffer_advance(i_b0, ip_hdr_size);
+              next_hdr_type = ip_proto;
               next0 = ESP_ENCRYPT_NEXT_INTERFACE_OUTPUT;
               o_b0->flags |= BUFFER_OUTPUT_FEAT_DONE;
               vnet_buffer (o_b0)->sw_if_index[VLIB_TX] =
@@ -344,6 +361,9 @@ esp_encrypt_node_fn (vlib_main_t * vm,
               oh0->ip4.checksum = ip4_header_checksum (&oh0->ip4);
             }
 
+          if (transport_mode)
+              vlib_buffer_reset (o_b0);
+
 trace:
           if (PREDICT_FALSE(i_b0->flags & VLIB_BUFFER_IS_TRACED)) {
             if (o_b0) {
index 9355468..ca82a24 100644 (file)
@@ -147,8 +147,8 @@ ip6_addr_match_range (ip6_address_t * a, ip6_address_t * la, ip6_address_t * ua)
 
 always_inline ipsec_policy_t *
 ipsec_output_ip6_policy_match (ipsec_spd_t * spd,
-                               ip6_address_t * sa,
-                               ip6_address_t * da,
+                               ip6_address_t * la,
+                               ip6_address_t * ra,
                                u16 lp,
                                u16 rp,
                                u8 pr)
@@ -162,10 +162,10 @@ ipsec_output_ip6_policy_match (ipsec_spd_t * spd,
       if (PREDICT_FALSE(p->protocol && (p->protocol != pr)))
         continue;
 
-      if (!ip6_addr_match_range(sa, &p->raddr.start.ip6, &p->raddr.stop.ip6))
+      if (!ip6_addr_match_range(ra, &p->raddr.start.ip6, &p->raddr.stop.ip6))
         continue;
 
-      if (!ip6_addr_match_range(da, &p->laddr.start.ip6, &p->laddr.stop.ip6))
+      if (!ip6_addr_match_range(la, &p->laddr.start.ip6, &p->laddr.stop.ip6))
         continue;
 
       if (PREDICT_FALSE((pr != IP_PROTOCOL_TCP) && (pr != IP_PROTOCOL_UDP)))
index f7338af..1f415ce 100644 (file)
@@ -5746,7 +5746,7 @@ static void vl_api_ipsec_spd_add_del_entry_t_handler
     p.is_outbound = mp->is_outbound;
     p.is_ipv6 = mp->is_ipv6;
 
-    if (mp->is_ipv6) {
+    if (mp->is_ipv6 || mp->is_ip_any) {
         clib_memcpy(&p.raddr.start, mp->remote_address_start, 16);
         clib_memcpy(&p.raddr.stop, mp->remote_address_stop, 16);
         clib_memcpy(&p.laddr.start, mp->local_address_start, 16);