/* * esp_decrypt.c : IPSec ESP decrypt node * * 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 #include #include #include #include #include #include #include #include #define foreach_esp_decrypt_next \ _(DROP, "error-drop") \ _(IP4_INPUT, "ip4-input-no-checksum") \ _(IP6_INPUT, "ip6-input") \ _(L2_INPUT, "l2-input") \ _(HANDOFF, "handoff") #define _(v, s) ESP_DECRYPT_NEXT_##v, typedef enum { foreach_esp_decrypt_next #undef _ ESP_DECRYPT_N_NEXT, } esp_decrypt_next_t; #define foreach_esp_decrypt_error \ _(RX_PKTS, "ESP pkts received") \ _(DECRYPTION_FAILED, "ESP decryption failed") \ _(INTEG_ERROR, "Integrity check failed") \ _(CRYPTO_ENGINE_ERROR, "crypto engine error (packet dropped)") \ _(REPLAY, "SA replayed packet") \ _(RUNT, "undersized packet") \ _(NO_BUFFERS, "no buffers (packet dropped)") \ _(OVERSIZED_HEADER, "buffer with oversized header (dropped)") \ _(NO_TAIL_SPACE, "no enough buffer tail space (dropped)") \ _(TUN_NO_PROTO, "no tunnel protocol") \ _(UNSUP_PAYLOAD, "unsupported payload") \ typedef enum { #define _(sym,str) ESP_DECRYPT_ERROR_##sym, foreach_esp_decrypt_error #undef _ ESP_DECRYPT_N_ERROR, } esp_decrypt_error_t; static char *esp_decrypt_error_strings[] = { #define _(sym,string) string, foreach_esp_decrypt_error #undef _ }; typedef struct { u32 seq; u32 sa_seq; u32 sa_seq_hi; ipsec_crypto_alg_t crypto_alg; ipsec_integ_alg_t integ_alg; } esp_decrypt_trace_t; /* packet trace format function */ static u8 * format_esp_decrypt_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 *); esp_decrypt_trace_t *t = va_arg (*args, esp_decrypt_trace_t *); s = format (s, "esp: crypto %U integrity %U pkt-seq %d sa-seq %u sa-seq-hi %u", format_ipsec_crypto_alg, t->crypto_alg, format_ipsec_integ_alg, t->integ_alg, t->seq, t->sa_seq, t->sa_seq_hi); return s; } typedef struct { vlib_buffer_t *lb; union { struct { u8 icv_sz; u8 iv_sz; ipsec_sa_flags_t flags; u32 sa_index; }; u64 sa_data; }; u32 seq; u32 free_buffer_index; i16 current_data; i16 current_length; u16 hdr_sz; u8 icv_removed; u8 __unused; } esp_decrypt_packet_data_t; STATIC_ASSERT_SIZEOF (esp_decrypt_packet_data_t, 4 * sizeof (u64)); #define ESP_ENCRYPT_PD_F_FD_TRANSPORT (1 << 2) static_always_inline void esp_process_ops (vlib_main_t * vm, vlib_node_runtime_t * node, vnet_crypto_op_t * ops, vlib_buffer_t * b[], u16 * nexts, int e) { vnet_crypto_op_t *op = ops; u32 n_fail, n_ops = vec_len (ops); if (n_ops == 0) return; n_fail = n_ops - vnet_crypto_process_ops (vm, op, n_ops); while (n_fail) { ASSERT (op - ops < n_ops); if (op->status != VNET_CRYPTO_OP_STATUS_COMPLETED) { u32 err, bi = op->user_data; if (op->status == VNET_CRYPTO_OP_STATUS_FAIL_BAD_HMAC) err = e; else err = ESP_DECRYPT_ERROR_CRYPTO_ENGINE_ERROR; b[bi]->error = node->errors[err]; nexts[bi] = ESP_DECRYPT_NEXT_DROP; n_fail--; } op++; } } static_always_inline void esp_process_chained_ops (vlib_main_t * vm, vlib_node_runtime_t * node, vnet_crypto_op_t * ops, vlib_buffer_t * b[], u16 * nexts, vnet_crypto_op_chunk_t * chunks, int e) { vnet_crypto_op_t *op = ops; u32 n_fail, n_ops = vec_len (ops); if (n_ops == 0) return; n_fail = n_ops - vnet_crypto_process_chained_ops (vm, op, chunks, n_ops); while (n_fail) { ASSERT (op - ops < n_ops); if (op->status != VNET_CRYPTO_OP_STATUS_COMPLETED) { u32 err, bi = op->user_data; if (op->status == VNET_CRYPTO_OP_STATUS_FAIL_BAD_HMAC) err = e; else err = ESP_DECRYPT_ERROR_CRYPTO_ENGINE_ERROR; b[bi]->error = node->errors[err]; nexts[bi] = ESP_DECRYPT_NEXT_DROP; n_fail--; } op++; } } always_inline void esp_remove_tail (vlib_main_t * vm, vlib_buffer_t * b, vlib_buffer_t * last, u16 tail) { vlib_buffer_t *before_last = b; if (last->current_length > tail) { last->current_length -= tail; return; } ASSERT (b->flags & VLIB_BUFFER_NEXT_PRESENT); while (b->flags & VLIB_BUFFER_NEXT_PRESENT) { before_last = b; b = vlib_get_buffer (vm, b->next_buffer); } before_last->current_length -= tail - last->current_length; vlib_buffer_free_one (vm, before_last->next_buffer); before_last->flags &= ~VLIB_BUFFER_NEXT_PRESENT; } /* ICV is splitted in last two buffers so move it to the last buffer and return pointer to it */ static_always_inline u8 * esp_move_icv (vlib_main_t * vm, vlib_buffer_t * first, esp_decrypt_packet_data_t * pd, u16 icv_sz, u16 * dif) { vlib_buffer_t *before_last, *bp; u16 last_sz = pd->lb->current_length; u16 first_sz = icv_sz
from vpp_interface import VppInterface


class VppVhostInterface(VppInterface):
    """VPP vhost interface."""

    def __init__(self, test, sock_filename, is_server=0, renumber=0,
                 disable_mrg_rxbuf=0, disable_indirect_desc=0,
                 custom_dev_instance=0, use_custom_mac=0, mac_address='',
                 tag=''):

        """ Create VPP Vhost interface """
        super(VppVhostInterface, self).__init__(test)
        self.is_server = is_server
        self.sock_filename = sock_filename
        self.renumber = renumber
        self.disable_mrg_rxbuf = disable_mrg_rxbuf
        self.disable_indirect_desc = disable_indirect_desc
        self.custom_dev_instance = custom_dev_instance
        self.use_custom_mac = use_custom_mac
        self.mac_address = mac_address
        self.tag = tag

    def add_vpp_config(self):
        r = self.test.vapi.create_vhost_user_if(self.is_server,
                                                self.sock_filename,
                                                self.renumber,
                                                self.disable_mrg_rxbuf,
                                                self.disable_indirect_desc,
                                                self.custom_dev_instance,
                                                self.use_custom_mac,
                                                self.mac_address,
                                                self.tag)
        self.set_sw_if_index(r.sw_if_index)

    def remove_vpp_config(self):
        self.test.vapi.delete_vhost_user_if(self.sw_if_index)

    def is_interface_config_in_dump(self, dump):
        for i in dump:
            if i.sw_if_index == self.sw_if_index:
                return True
        else:
            return False
pad_length; next_header = f->next_header; } else { pad_length = (bt - 1)[0]; next_header = ((u8 *) vlib_buffer_get_current (pd->lb))[0]; } } else { esp_footer_t *f = (esp_footer_t *) (pd->lb->data + pd->lb->current_data + pd->lb->current_length - sizeof (esp_footer_t) - icv_sz); pad_length = f->pad_length; next_header = f->next_header; } u16 adv = pd->iv_sz + esp_sz; u16 tail = sizeof (esp_footer_t) + pad_length + icv_sz; u16 tail_orig = sizeof (esp_footer_t) + pad_length + pd->icv_sz; b[0]->flags &= ~VLIB_BUFFER_TOTAL_LENGTH_VALID; if ((pd->flags & tun_flags) == 0 && !is_tun) /* transport mode */ { u8 udp_sz = (is_ip6 == 0 && pd->flags & IPSEC_SA_FLAG_UDP_ENCAP) ? sizeof (udp_header_t) : 0; u16 ip_hdr_sz = pd->hdr_sz - udp_sz; u8 *old_ip = b[0]->data + pd->current_data - ip_hdr_sz - udp_sz; u8 *ip = old_ip + adv + udp_sz; if (is_ip6 && ip_hdr_sz > 64) memmove (ip, old_ip, ip_hdr_sz); else clib_memcpy_le64 (ip, old_ip, ip_hdr_sz); b[0]->current_data = pd->current_data + adv - ip_hdr_sz; b[0]->current_length += ip_hdr_sz - adv; esp_remove_tail (vm, b[0], pd->lb, tail); if (is_ip6) { ip6_header_t *ip6 = (ip6_header_t *) ip; u16 len = clib_net_to_host_u16 (ip6->payload_length); len -= adv + tail_orig; ip6->payload_length = clib_host_to_net_u16 (len); ip6->protocol = next_header; next[0] = ESP_DECRYPT_NEXT_IP6_INPUT; } else { ip4_header_t *ip4 = (ip4_header_t *) ip; ip_csum_t sum = ip4->checksum; u16 len = clib_net_to_host_u16 (ip4->length); len = clib_host_to_net_u16 (len - adv - tail_orig - udp_sz); sum = ip_csum_update (sum, ip4->protocol, next_header, ip4_header_t, protocol); sum = ip_csum_update (sum, ip4->length, len, ip4_header_t, length); ip4->checksum = ip_csum_fold (sum); ip4->protocol = next_header; ip4->length = len; next[0] = ESP_DECRYPT_NEXT_IP4_INPUT; } } else { if (PREDICT_TRUE (next_header == IP_PROTOCOL_IP_IN_IP)) { next[0] = ESP_DECRYPT_NEXT_IP4_INPUT; b[0]->current_data = pd->current_data + adv; b[0]->current_length = pd->current_length - adv; esp_remove_tail (vm, b[0], pd->lb, tail); } else if (next_header == IP_PROTOCOL_IPV6) { next[0] = ESP_DECRYPT_NEXT_IP6_INPUT; b[0]->current_data = pd->current_data + adv; b[0]->current_length = pd->current_length - adv; esp_remove_tail (vm, b[0], pd->lb, tail); } else { if (is_tun && next_header == IP_PROTOCOL_GRE) { gre_header_t *gre; b[0]->current_data = pd->current_data + adv; b[0]->current_length = pd->current_length - adv - tail; gre = vlib_buffer_get_current (b[0]); vlib_buffer_advance (b[0], sizeof (*gre)); switch (clib_net_to_host_u16 (gre->protocol)) { case GRE_PROTOCOL_teb: vnet_update_l2_len (b[0]); next[0] = ESP_DECRYPT_NEXT_L2_INPUT; break; case GRE_PROTOCOL_ip4: next[0] = ESP_DECRYPT_NEXT_IP4_INPUT; break; case GRE_PROTOCOL_ip6: next[0] = ESP_DECRYPT_NEXT_IP6_INPUT; break; default: b[0]->error = node->errors[ESP_DECRYPT_ERROR_UNSUP_PAYLOAD]; next[0] = ESP_DECRYPT_NEXT_DROP; break; } } else { next[0] = ESP_DECRYPT_NEXT_DROP; b[0]->error = node->errors[ESP_DECRYPT_ERROR_UNSUP_PAYLOAD]; goto trace; } } if (is_tun) { if (ipsec_sa_is_set_IS_PROTECT (sa0)) { /* * There are two encap possibilities * 1) the tunnel and ths SA are prodiving encap, i.e. it's * MAC | SA-IP | TUN-IP | ESP | PAYLOAD * implying the SA is in tunnel mode (on a tunnel interface) * 2) only the tunnel provides encap * MAC | TUN-IP | ESP | PAYLOAD * implying the SA is in transport mode. * * For 2) we need only strip the tunnel encap and we're good. * since the tunnel and crypto ecnap (int the tun=protect * object) are the same and we verified above that these match * for 1) we need to strip the SA-IP outer headers, to * reveal the tunnel IP and then check that this matches * the configured tunnel. */ const ipsec_tun_protect_t *itp; itp = ipsec_tun_protect_get (vnet_buffer (b[0])->ipsec.protect_index); if (PREDICT_TRUE (next_header == IP_PROTOCOL_IP_IN_IP)) { const ip4_header_t *ip4; ip4 = vlib_buffer_get_current (b[0]); if (!ip46_address_is_equal_v4 (&itp->itp_tun.src, &ip4->dst_address) || !ip46_address_is_equal_v4 (&itp->itp_tun.dst, &ip4->src_address)) { next[0] = ESP_DECRYPT_NEXT_DROP; b[0]->error = node->errors[ESP_DECRYPT_ERROR_TUN_NO_PROTO]; } } else if (next_header == IP_PROTOCOL_IPV6) { const ip6_header_t *ip6; ip6 = vlib_buffer_get_current (b[0]); if (!ip46_address_is_equal_v6 (&itp->itp_tun.src, &ip6->dst_address) || !ip46_address_is_equal_v6 (&itp->itp_tun.dst, &ip6->src_address)) { next[0] = ESP_DECRYPT_NEXT_DROP; b[0]->error = node->errors[ESP_DECRYPT_ERROR_TUN_NO_PROTO]; } } } } } trace: if (PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED)) { esp_decrypt_trace_t *tr; tr = vlib_add_trace (vm, node, b[0], sizeof (*tr)); sa0 = pool_elt_at_index (im->sad, vnet_buffer (b[0])->ipsec.sad_index); tr->crypto_alg = sa0->crypto_alg; tr->integ_alg = sa0->integ_alg; tr->seq = pd->seq; tr->sa_seq = sa0->last_seq; tr->sa_seq_hi = sa0->seq_hi; } /* next */ n_left -= 1; next += 1; pd += 1; b += 1; } n_left = from_frame->n_vectors; vlib_node_increment_counter (vm, node->node_index, ESP_DECRYPT_ERROR_RX_PKTS, n_left); vlib_buffer_enqueue_to_next (vm, node, from, nexts, n_left); return n_left; } VLIB_NODE_FN (esp4_decrypt_node) (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * from_frame) { return esp_decrypt_inline (vm, node, from_frame, 0, 0); } VLIB_NODE_FN (esp4_decrypt_tun_node) (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * from_frame) { return esp_decrypt_inline (vm, node, from_frame, 0, 1); } VLIB_NODE_FN (esp6_decrypt_node) (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * from_frame) { return esp_decrypt_inline (vm, node, from_frame, 1, 0); } VLIB_NODE_FN (esp6_decrypt_tun_node) (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * from_frame) { return esp_decrypt_inline (vm, node, from_frame, 1, 1); } /* *INDENT-OFF* */ VLIB_REGISTER_NODE (esp4_decrypt_node) = { .name = "esp4-decrypt", .vector_size = sizeof (u32), .format_trace = format_esp_decrypt_trace, .type = VLIB_NODE_TYPE_INTERNAL, .n_errors = ARRAY_LEN(esp_decrypt_error_strings), .error_strings = esp_decrypt_error_strings, .n_next_nodes = ESP_DECRYPT_N_NEXT, .next_nodes = { [ESP_DECRYPT_NEXT_DROP] = "ip4-drop", [ESP_DECRYPT_NEXT_IP4_INPUT] = "ip4-input-no-checksum", [ESP_DECRYPT_NEXT_IP6_INPUT] = "ip6-input", [ESP_DECRYPT_NEXT_L2_INPUT] = "l2-input", [ESP_DECRYPT_NEXT_HANDOFF] = "esp4-decrypt-handoff", }, }; VLIB_REGISTER_NODE (esp6_decrypt_node) = { .name = "esp6-decrypt", .vector_size = sizeof (u32), .format_trace = format_esp_decrypt_trace, .type = VLIB_NODE_TYPE_INTERNAL, .n_errors = ARRAY_LEN(esp_decrypt_error_strings), .error_strings = esp_decrypt_error_strings, .n_next_nodes = ESP_DECRYPT_N_NEXT, .next_nodes = { [ESP_DECRYPT_NEXT_DROP] = "ip6-drop", [ESP_DECRYPT_NEXT_IP4_INPUT] = "ip4-input-no-checksum", [ESP_DECRYPT_NEXT_IP6_INPUT] = "ip6-input", [ESP_DECRYPT_NEXT_L2_INPUT] = "l2-input", [ESP_DECRYPT_NEXT_HANDOFF]= "esp6-decrypt-handoff", }, }; VLIB_REGISTER_NODE (esp4_decrypt_tun_node) = { .name = "esp4-decrypt-tun", .vector_size = sizeof (u32), .format_trace = format_esp_decrypt_trace, .type = VLIB_NODE_TYPE_INTERNAL, .n_errors = ARRAY_LEN(esp_decrypt_error_strings), .error_strings = esp_decrypt_error_strings, .n_next_nodes = ESP_DECRYPT_N_NEXT, .next_nodes = { [ESP_DECRYPT_NEXT_DROP] = "ip4-drop", [ESP_DECRYPT_NEXT_IP4_INPUT] = "ip4-input-no-checksum", [ESP_DECRYPT_NEXT_IP6_INPUT] = "ip6-input", [ESP_DECRYPT_NEXT_L2_INPUT] = "l2-input", [ESP_DECRYPT_NEXT_HANDOFF] = "esp4-decrypt-tun-handoff", }, }; VLIB_REGISTER_NODE (esp6_decrypt_tun_node) = { .name = "esp6-decrypt-tun", .vector_size = sizeof (u32), .format_trace = format_esp_decrypt_trace, .type = VLIB_NODE_TYPE_INTERNAL, .n_errors = ARRAY_LEN(esp_decrypt_error_strings), .error_strings = esp_decrypt_error_strings, .n_next_nodes = ESP_DECRYPT_N_NEXT, .next_nodes = { [ESP_DECRYPT_NEXT_DROP] = "ip6-drop", [ESP_DECRYPT_NEXT_IP4_INPUT] = "ip4-input-no-checksum", [ESP_DECRYPT_NEXT_IP6_INPUT] = "ip6-input", [ESP_DECRYPT_NEXT_L2_INPUT] = "l2-input", [ESP_DECRYPT_NEXT_HANDOFF]= "esp6-decrypt-tun-handoff", }, }; /* *INDENT-ON* */ /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */