/* *------------------------------------------------------------------ * af_packet.c - linux kernel packet interface * * Copyright (c) 2016 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_af_packet_input_error \ _(PARTIAL_PKT, "partial packet") typedef enum { #define _(f,s) AF_PACKET_INPUT_ERROR_##f, foreach_af_packet_input_error #undef _ AF_PACKET_INPUT_N_ERROR, } af_packet_input_error_t; static char *af_packet_input_error_strings[] = { #define _(n,s) s, foreach_af_packet_input_error #undef _ }; typedef struct { u32 next_index; u32 hw_if_index; int block; struct tpacket2_hdr tph; } af_packet_input_trace_t; static u8 * format_af_packet_input_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 *); af_packet_input_trace_t *t = va_arg (*args, af_packet_input_trace_t *); u32 indent = format_get_indent (s); s = format (s, "af_packet: hw_if_index %d next-index %d", t->hw_if_index, t->next_index); s = format (s, "\n%Utpacket2_hdr:\n%Ustatus 0x%x len %u snaplen %u mac %u net %u" "\n%Usec 0x%x nsec 0x%x vlan %U" #ifdef TP_STATUS_VLAN_TPID_VALID " vlan_tpid %u" #endif , format_white_space, indent + 2, format_white_space, indent + 4, t->tph.tp_status, t->tph.tp_len, t->tph.tp_snaplen, t->tph.tp_mac, t->tph.tp_net, format_white_space, indent + 4, t->tph.tp_sec, t->tph.tp_nsec, format_ethernet_vlan_tci, t->tph.tp_vlan_tci #ifdef TP_STATUS_VLAN_TPID_VALID , t->tph.tp_vlan_tpid #endif ); return s; } always_inline void buffer_add_to_chain (vlib_main_t * vm, u32 bi, u32 first_bi, u32 prev_bi) { vlib_buffer_t *b = vlib_get_buffer (vm, bi); vlib_buffer_t *first_b = vlib_get_buffer (vm, first_bi); vlib_buffer_t *prev_b = vlib_get_buffer (vm, prev_bi); /* update first buffer */ first_b->total_length_not_including_first_buffer += b->current_length; /* update previous buffer */ prev_b->next_buffer = bi; prev_b->flags |= VLIB_BUFFER_NEXT_PRESENT; /* update current buffer */ b->next_buffer = 0; } static_always_inline void mark_tcp_udp_cksum_calc (vlib_buffer_t * b) { ethernet_header_t *eth = vlib_buffer_get_current (b); if (clib_net_to_host_u16 (eth->type) == ETHERNET_TYPE_IP4) { ip4_header_t *ip4 = (vlib_buffer_get_current (b) + sizeof (ethernet_header_t)); b->flags |= VNET_BUFFER_F_IS_IP4; if (ip4->protocol == IP_PROTOCOL_TCP) { b->flags |= VNET_BUFFER_F_OFFLOAD_TCP_CKSUM; ((tcp_header_t *) (vlib_buffer_get_current (b) + sizeof (ethernet_header_t) + ip4_header_bytes (ip4)))->checksum = 0; } else if (ip4->protocol == IP_PROTOCOL_UDP) { b->flags |= VNET_BUFFER_F_OFFLOAD_UDP_CKSUM; ((udp_header_t *) (vlib_buffer_get_current (b) + sizeof (ethernet_header_t) + ip4_header_bytes (ip4)))->checksum = 0; } vnet_buffer (b)->l3_hdr_offset = sizeof (ethernet_header_t); vnet_buffer (b)->l4_hdr_offset = sizeof (ethernet_header_t) + ip4_header_bytes (ip4); } else if (clib_net_to_host_u16 (eth->type) == ETHERNET_TYPE_IP6) { ip6_header_t *ip6 = (vlib_buffer_get_current (b) + sizeof (ethernet_header_t)); b->flags |= VNET_BUFFER_F_IS_IP6; u16 ip6_hdr_len = sizeof (ip6_header_t); if (ip6_ext_hdr (ip6->protocol)) { ip6_ext_header_t *p = (void *) (ip6 + 1); ip6_hdr_len += ip6_ext_header_len (p); while (ip6_ext_hdr (p->next_hdr)) { ip6_hdr_len += ip6_ext_header_len (p); p = ip6_ext_next_header (p); } } if (ip6->protocol == IP_PROTOCOL_TCP) { b->flags |= VNET_BUFFER_F_OFFLOAD_TCP_CKSUM; ((tcp_header_t *) (vlib_buffer_get_current (b) + sizeof (ethernet_header_t) + ip6_hdr_len))->checksum = 0; } else if (ip6->protocol == IP_PROTOCOL_UDP) { b->flags |= VNET_BUFFER_F_OFFLOAD_UDP_CKSUM; ((udp_header_t *) (vlib_buffer_get_current (b) + sizeof (ethernet_header_t) + ip6_hdr_len))->checksum = 0; } vnet_buffer (b)->l3_hdr_offset = sizeof (ethernet_header_t); vnet_buffer (b)->l4_hdr_offset = sizeof (ethernet_header_t) + ip6_hdr_len; } } always_inline uword af_packet_device_input_fn (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame, af_packet_if_t * apif) { af_packet_main_t *apm = &af_packet_main; struct tpacket2_hdr *tph; u32 next_index = VNET_DEVICE_INPUT_NEXT_ETHERNET_INPUT; u32 block = 0; u32 rx_frame; u32 n_free_bufs; u32 n_rx_packets = 0; u32 n_rx_bytes = 0; u32 *to_next = 0; u32 block_size = apif->rx_req->tp_block_size; u32 frame_size = apif->rx_req->tp_frame_size; u32 frame_num = apif->rx_req->tp_frame_nr; u8 *block_start = apif->rx_ring + block * block_size; uword n_trace = vlib_get_trace_count (vm, node); u32 thread_index = vm->thread_index; u32 n_buffer_bytes = vlib_buffer_get_default_data_size (vm); u32 min_bufs = apif->rx_req->tp_frame_size / n_buffer_bytes; n_free_bufs = vec_len (apm->rx_buffers[thread_index]); if (PREDICT_FALSE (n_free_bufs < VLIB_FRAME_SIZE)) { vec_validate (apm->rx_buffers[thread_index], VLIB_FRAME_SIZE + n_free_bufs - 1); n_free_bufs += vlib_buffer_alloc (vm, &apm->rx_buffers[thread_index][n_free_bufs], VLIB_FRAME_SIZE); _vec_len (apm->rx_buffers[thread_index]) = n_free_bufs; } rx_frame = apif->next_rx_frame; tph = (struct tpacket2_hdr *) (block_start + rx_frame * fram