diff options
Diffstat (limited to 'src/dpi_node.c')
-rw-r--r-- | src/dpi_node.c | 1042 |
1 files changed, 1042 insertions, 0 deletions
diff --git a/src/dpi_node.c b/src/dpi_node.c new file mode 100644 index 0000000..2e86c65 --- /dev/null +++ b/src/dpi_node.c @@ -0,0 +1,1042 @@ +/* + * Copyright (c) 2019 Intel 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 <vlib/vlib.h> +#include <vnet/pg/pg.h> +#include <vnet/vnet.h> +#include <vnet/ip/ip.h> +#include <vppinfra/bihash_48_8.h> +#include <vppinfra/dlist.h> +#include <vppinfra/pool.h> +#include <vppinfra/vec.h> +#include <vnet/plugin/plugin.h> +#include <vpp/app/version.h> +#include <vnet/flow/flow.h> +#include <vnet/tcp/tcp_packet.h> + +#include <dpi/dpi.h> + +vlib_node_registration_t dpi4_input_node; +vlib_node_registration_t dpi6_input_node; +vlib_node_registration_t dpi4_flow_input_node; +vlib_node_registration_t dpi6_flow_input_node; + + +#define foreach_dpi_input_error \ + _(NONE, "no error") \ + _(NO_SUCH_FLOW, "flow not existed") + +typedef enum +{ +#define _(sym,str) DPI_INPUT_ERROR_##sym, + foreach_dpi_input_error +#undef _ + DPI_INPUT_N_ERROR, +} dpi_input_error_t; + +static char *dpi_input_error_strings[] = { +#define _(sym,string) string, + foreach_dpi_input_error +#undef _ +}; + +typedef struct +{ + u32 next_index; + u32 flow_id; + u32 app_id; + u32 error; +} dpi_rx_trace_t; + +/* *INDENT-OFF* */ +VNET_FEATURE_INIT (dpi4_input, static) = +{ + .arc_name = "ip4-unicast", + .node_name = "dpi4-input", + .runs_before = VNET_FEATURES ("ip4-lookup"), +}; + +VNET_FEATURE_INIT (dpi6_input, static) = +{ + .arc_name = "ip6-unicast", + .node_name = "dpi6-input", + .runs_before = VNET_FEATURES ("ip6-lookup"), +}; +/* *INDENT-on* */ + +static u8 * +format_dpi_rx_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 *); + dpi_rx_trace_t *t = va_arg (*args, dpi_rx_trace_t *); + + if (t->flow_id == ~0) + return format (s, "DPI error - flow %d does not exist", + t->flow_id); + + return format (s, "DPI from flow %d app_id %d next %d error %d", + t->flow_id, t->app_id, t->next_index, t->error); +} + + + +static inline int +parse_ip4_packet_and_lookup (ip4_header_t * ip4, u32 fib_index, + dpi4_flow_key_t * key4, + int * not_found, u64 * flow_id) +{ + dpi_main_t *dm = &dpi_main; + u8 protocol = ip4_is_fragment (ip4) ? 0xfe : ip4->protocol; + u16 src_port = 0; + u16 dst_port = 0; + dpi_flow_entry_t *flow; + + key4->key[0] = ip4->src_address.as_u32 + | (((u64) ip4->dst_address.as_u32) << 32); + + if (protocol == IP_PROTOCOL_UDP || protocol == IP_PROTOCOL_TCP) + { + /* tcp and udp ports have the same offset */ + udp_header_t * udp = ip4_next_header (ip4); + src_port = udp->src_port; + dst_port = udp->dst_port; + } + + key4->key[1] = (((u64) protocol) << 32) | ((u32) src_port << 16) | dst_port; + key4->key[2] = (u64) fib_index; + + key4->value = ~0; + *not_found = clib_bihash_search_inline_24_8 (&dm->dpi4_flow_by_key, key4); + *flow_id = key4->value; + + /* not found, then create new SW flow dynamically */ + if (*not_found) + { + int add_failed; + pool_get_aligned(dm->dpi_flows, flow, CLIB_CACHE_LINE_BYTES); + clib_memset(flow, 0, sizeof(*flow)); + *flow_id = flow - dm->dpi_flows; + + flow->next_index = DPI_INPUT_NEXT_IP4_LOOKUP; + flow->flow_index = ~0; + + pool_get_aligned(dm->dpi_infos, flow->info, CLIB_CACHE_LINE_BYTES); + clib_memset(flow->info, 0, sizeof(*flow->info)); + flow->info->app_id = ~0; + + /* Add forwarding flow entry */ + key4->value = *flow_id; + add_failed = clib_bihash_add_del_24_8 (&dm->dpi4_flow_by_key, key4, + 1 /*add */); + + if (add_failed) + { + pool_put(dm->dpi_infos, flow->info); + pool_put(dm->dpi_flows, flow); + return -1; + } + + /* Add reverse flow entry*/ + key4->key[0] = ip4->dst_address.as_u32 + | (((u64) ip4->src_address.as_u32) << 32); + key4->key[1] = (((u64) protocol) << 32) | ((u32) dst_port << 16) + | src_port; + key4->key[2] = (u64) fib_index; + key4->value = (u64) flow_id | ((u64) 1 << 63); + add_failed = clib_bihash_add_del_24_8 (&dm->dpi4_flow_by_key, key4, + 1 /*add */); + + if (add_failed) + { + pool_put(dm->dpi_infos, flow->info); + pool_put(dm->dpi_flows, flow); + return -1; + } + + /* Open a Hyperscan stream for each flow */ + hs_error_t err = hs_open_stream (dm->default_db.database, 0, + &(flow->info->stream)); + + if (err != HS_SUCCESS) + { + pool_put(dm->dpi_infos, flow->info); + pool_put(dm->dpi_flows, flow); + return -1; + } + } + + return 0; +} + +static inline int +parse_ip6_packet_and_lookup (ip6_header_t * ip6, u32 fib_index, + dpi6_flow_key_t * key6, + int * not_found, u64 * flow_id) +{ + dpi_main_t *dm = &dpi_main; + u8 protocol = ip6->protocol; + u16 src_port = 0; + u16 dst_port = 0; + dpi_flow_entry_t *flow; + + key6->key[0] = ip6->src_address.as_u64[0]; + key6->key[1] = ip6->src_address.as_u64[1]; + key6->key[2] = ip6->dst_address.as_u64[0]; + key6->key[3] = ip6->dst_address.as_u64[1]; + + if (protocol == IP_PROTOCOL_UDP || protocol == IP_PROTOCOL_TCP) + { + /* tcp and udp ports have the same offset */ + udp_header_t * udp = ip6_next_header(ip6); + src_port = udp->src_port; + dst_port = udp->dst_port; + } + + key6->key[4] = (((u64) protocol) << 32) + | ((u32) src_port << 16) + | dst_port; + key6->key[5] = (u64) fib_index; + + key6->value = ~0; + *not_found = clib_bihash_search_inline_48_8 (&dm->dpi6_flow_by_key, key6); + *flow_id = key6->value; + + /* not found, then create new SW flow dynamically */ + if (*not_found) + { + int add_failed; + pool_get_aligned(dm->dpi_flows, flow, CLIB_CACHE_LINE_BYTES); + clib_memset(flow, 0, sizeof(*flow)); + *flow_id = flow - dm->dpi_flows; + + flow->next_index = DPI_INPUT_NEXT_IP4_LOOKUP; + flow->flow_index = ~0; + + pool_get_aligned(dm->dpi_infos, flow->info, CLIB_CACHE_LINE_BYTES); + clib_memset(flow->info, 0, sizeof(*flow->info)); + flow->info->app_id = ~0; + + /* Add forwarding flow entry */ + key6->value = (u64) flow_id; + add_failed = clib_bihash_add_del_48_8 (&dm->dpi6_flow_by_key, + key6, 1 /*add */ ); + if (add_failed) + { + pool_put(dm->dpi_infos, flow->info); + pool_put(dm->dpi_flows, flow); + return -1; + } + + /* Add reverse flow entry*/ + key6->key[0] = ip6->dst_address.as_u64[0]; + key6->key[1] = ip6->dst_address.as_u64[1]; + key6->key[2] = ip6->src_address.as_u64[0]; + key6->key[3] = ip6->src_address.as_u64[1]; + key6->key[4] = (((u64) protocol) << 32) + | ((u32) dst_port << 16) + | src_port; + key6->key[5] = (u64) fib_index; + key6->value = (u64) flow_id | ((u64) 1 << 63); + add_failed = clib_bihash_add_del_48_8 (&dm->dpi6_flow_by_key, + key6, 1 /*add */ ); + + if (add_failed) + { + pool_put(dm->dpi_infos, flow->info); + pool_put(dm->dpi_flows, flow); + return -1; + } + + /* Open a Hyperscan stream for each flow */ + hs_error_t err = hs_open_stream (dm->default_db.database, 0, + &(flow->info->stream)); + + if (err != HS_SUCCESS) + { + pool_put(dm->dpi_infos, flow->info); + pool_put(dm->dpi_flows, flow); + return -1; + } + } + + return 0; +} + +static inline void +dpi_trim_overlap(u32 left_sn, segment *seg) +{ + int overlap_len; + + overlap_len = left_sn - seg->send_sn; + /* trim leading overlap bytes */ + seg->data += overlap_len; + seg->len -= overlap_len; + seg->send_sn += overlap_len; + + /* trim the right overlap bytes */ + if( seg->next + && (seg->send_sn+seg->len) > (seg->next->send_sn) ) + { + overlap_len = (seg->send_sn+seg->len) - (seg->next->send_sn); + if(seg->len > overlap_len) + { + seg->len -= overlap_len; + } + } +} + +/* + * re-order out-of-order segments, and handle overlap segments. + * */ +static inline void +dpi_handle_tcp_segments (dpi_flow_entry_t *flow, tcp_stream_t *stream, + u32 bi, u8 *pkt, u32 payload_len) +{ + dpi_main_t *dm = &dpi_main; + u32 send_sn; + u32 ack_sn; + u32 next_sn; + u32 left_sn; + segment *first_seg = 0; + segment *seg = 0; + segment *new_seg = 0; + tcp_header_t *tcp = (tcp_header_t *)pkt; + u8 *payload = pkt + tcp_doff(tcp) * 4; + + if((tcp->flags & TCP_FLAG_ACK) == TCP_FLAG_ACK) + { + ack_sn = clib_net_to_host_u32(tcp->ack_number); + if(ack_sn != stream->ack_sn) + { + stream->ack_sn = ack_sn; + } + } + + send_sn = clib_net_to_host_u32(tcp->seq_number); + next_sn = send_sn + payload_len; + + /* Handle fully overlapping segments */ + if(SN_GT(stream->send_sn, next_sn)) + { + flow->consumed = 1; + return; + } + + if(SN_LT(stream->send_sn, send_sn)) + { + /* Store out-of-order segments to segment queue */ + for(seg=stream->seg_queue; seg; seg=seg->next) + { + if (send_sn < seg->send_sn ) + break; + } + + pool_get_aligned (dm->seg_pool, new_seg, CLIB_CACHE_LINE_BYTES); + new_seg->bi = bi; + new_seg->send_sn = send_sn; + new_seg->data = payload; + new_seg->len = payload_len; + + /* Insert new segment to right position of segment queue */ + if(seg == stream->seg_queue) + { + new_seg->next = stream->seg_queue; + stream->seg_queue = new_seg; + stream->send_sn = seg->send_sn; + left_sn = stream->send_sn; + } + else + { + new_seg->next = seg->next; + seg->next = new_seg; + left_sn = seg->send_sn; + } + + /* trim overlapped packet */ + dpi_trim_overlap(left_sn, new_seg); + + flow->consumed = 1; + } + else + { + pool_get_aligned(dm->seg_pool, first_seg, CLIB_CACHE_LINE_BYTES); + first_seg->bi = bi; + first_seg->send_sn = send_sn; + first_seg->data = payload; + first_seg->len = payload_len; + first_seg->next = stream->seg_queue; + + /* trim overlapped packet */ + dpi_trim_overlap (stream->send_sn, first_seg); + + /* reassemble continuous segments and move forward to scan */ + for (seg = first_seg; seg->next; seg = seg->next) + { + if (seg->send_sn + seg->len != seg->next->send_sn) + break; + } + + /* left non-continuous segments */ + stream->seg_queue = seg->next; + stream->send_sn = seg->send_sn + seg->len; + + flow->first_seg = first_seg; + seg->next = 0; + + /* scan ordered segments */ + for (seg = first_seg; seg; seg = seg->next) + { + /* detect layer 7 application for single segment */ + dpi_detect_application (seg->data, seg->len, flow->info); + if(flow->info->detect_done) + break; + } + } +} + +static inline int +dpi_handle_tcp_stream (dpi_flow_entry_t *flow, u32 bi, + u8 *pkt, u32 payload_len, u8 is_reverse) +{ + tcp_header_t *tcp; + tcp_stream_t *stream; + + tcp = (tcp_header_t *)pkt; + if((tcp->flags & (TCP_FLAG_SYN|TCP_FLAG_ACK)) == TCP_FLAG_SYN) + { + flow->c2s.send_sn = clib_net_to_host_u32(tcp->seq_number) + 1; + flow->pkt_dir = DIR_C2S; + flow->forward_is_c2s = !is_reverse; + flow->tcp_state = TCP_STATE_SYN; + } + else + { + /* + forward_is_c2s | is_reverse + 0 1 + 0 s2c(1) c2s(0) + 1 c2s(0) s2c(1) + */ + flow->pkt_dir = (flow->forward_is_c2s == is_reverse); + } + + switch(flow->tcp_state) + { + case TCP_STATE_SYN: + { + if(flow->pkt_dir != DIR_S2C) + break; + + if((tcp->flags & (TCP_FLAG_SYN|TCP_FLAG_ACK)) + != (TCP_FLAG_SYN|TCP_FLAG_ACK)) + break; + + flow->s2c.send_sn = clib_net_to_host_u32(tcp->seq_number) + 1; + flow->s2c.ack_sn = clib_net_to_host_u32(tcp->ack_number) + 1; + flow->tcp_state = TCP_STATE_SYN_ACK; + break; + } + + case TCP_STATE_SYN_ACK: + { + if(flow->pkt_dir != DIR_C2S) + break; + + flow->c2s.ack_sn = clib_net_to_host_u32(tcp->ack_number) + 1; + flow->tcp_state = TCP_STATE_ESTABLISH; + break; + } + + case TCP_STATE_ACK: + case TCP_STATE_ESTABLISH: + case TCP_STATE_FIN1: + { + stream = (flow->pkt_dir == DIR_C2S)? &(flow->c2s) : &(flow->s2c); + if( (flow->reass_dir == REASS_BOTH) + || ((flow->pkt_dir==DIR_C2S) && (flow->reass_dir==REASS_C2S)) + || ((flow->pkt_dir==DIR_S2C) && (flow->reass_dir==REASS_S2C)) ) + { + dpi_handle_tcp_segments(flow, stream, bi, pkt, payload_len); + } + + break; + } + + case TCP_STATE_CLOSE: + { + /* Free all segments in the queue */ + break; + } + } + + return 0; +} + +void +dpi_detect_application (u8 *payload, u32 payload_len, + dpi_flow_info_t *flow) +{ + + /* detect if payload is SSL's payload for default port */ + dpi_search_tcp_ssl(payload, payload_len, flow); + + /* TBD: add detect if is SSL's payload with non default port*/ + +} + +always_inline uword +dpi_input_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, u32 is_ip4) +{ + dpi_main_t *dm = &dpi_main; + u32 *from, *to_next, n_left_from, n_left_to_next, next_index; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0, next0 = 0; + vlib_buffer_t *b0; + ip4_header_t *ip40; + ip6_header_t *ip60; + tcp_header_t *tcp0; + udp_header_t *udp0; + dpi4_flow_key_t key40; + dpi6_flow_key_t key60; + u32 fib_index0 = ~0; + u64 flow_id0 = ~0; + u32 flow_index0 = ~0; + int not_found0 = 0; + u8 is_reverse0 = 0; + dpi_flow_entry_t *flow0 = 0; + u32 ip_len0, l4_len0, payload_len0; + u8 protocol0; + u8 *l4_pkt0, *payload0; + u16 dst_port = 0; + segment *seg = 0; + segment *prev_seg = 0; + int rv; + + bi0 = to_next[0] = from[0]; + b0 = vlib_get_buffer (vm, bi0); + ip_len0 = vlib_buffer_length_in_chain (vm, b0); + + if (is_ip4) + { + ip40 = vlib_buffer_get_current (b0); + ip4_main_t *im4 = &ip4_main; + fib_index0 = vec_elt (im4->fib_index_by_sw_if_index, + vnet_buffer(b0)->sw_if_index[VLIB_RX]); + rv = + parse_ip4_packet_and_lookup(ip40, fib_index0, &key40, + ¬_found0, &flow_id0); + } + else + { + ip60 = vlib_buffer_get_current (b0); + ip6_main_t *im6 = &ip6_main; + fib_index0 = vec_elt (im6->fib_index_by_sw_if_index, + vnet_buffer(b0)->sw_if_index[VLIB_RX]); + rv = + parse_ip6_packet_and_lookup(ip60, fib_index0, &key60, + ¬_found0, &flow_id0); + } + + if (!rv) + goto enqueue0; + + is_reverse0 = (u8)((flow_id0 >> 63) & 0x1); + flow_index0 = (u32)(flow_id0 & (u32)(~0)); + flow0 = pool_elt_at_index (dm->dpi_flows, flow_index0); + + /* have detected successfully, directly return */ + if(flow0->info->detect_done) + goto enqueue0; + + /* check layer4 */ + if (is_ip4) + { + l4_pkt0 = (u8 *)(ip40 + 1); + l4_len0 = ip_len0 - sizeof(ip4_header_t); + protocol0 = ip40->protocol; + } + else + { + l4_pkt0 = (u8 *)(ip60 + 1); + l4_len0 = ip_len0 - sizeof(ip6_header_t); + protocol0 = ip60->protocol; + } + + if((protocol0 == IP_PROTOCOL_TCP) && (l4_len0 >= 20)) + { + tcp0 = (tcp_header_t *)l4_pkt0; + payload_len0 = l4_len0 - tcp_doff(tcp0) * 4; + payload0 = l4_pkt0 + tcp_doff(tcp0) * 4; + dst_port = tcp0->dst_port; + } + else if ((protocol0 == IP_PROTOCOL_UDP) && (l4_len0 >= 8)) + { + udp0 = (udp_header_t *)l4_pkt0; + payload_len0 = l4_len0 - sizeof(udp_header_t); + payload0 = l4_pkt0 + sizeof(udp_header_t); + dst_port = udp0->dst_port; + } + else + { + payload_len0 = l4_len0; + payload0 = l4_pkt0; + } + + flow0->info->l4_protocol = protocol0; + flow0->info->dst_port = dst_port; + + /* TCP stream reassembly and detect a protocol pdu */ + if((protocol0 == IP_PROTOCOL_TCP) && (flow0->reass_en)) + { + dpi_handle_tcp_stream(flow0, bi0, l4_pkt0, payload_len0, is_reverse0); + + /* This packet has been consumed, retrieve next packet */ + if(flow0->consumed) + goto trace0; + + /* send out continuous scanned segments */ + seg=flow0->first_seg; + dpi_enqueue_tcp_segments(seg,vm,node,next_index,to_next,n_left_to_next,bi0,next0); + flow0->first_seg = 0; + + /* Here detected successfully, send out remaining segments in seg_queue */ + if(flow0->info->detect_done) + { + seg=flow0->c2s.seg_queue; + dpi_enqueue_tcp_segments(seg,vm,node,next_index,to_next,n_left_to_next,bi0,next0); + flow0->c2s.seg_queue = 0; + + seg=flow0->s2c.seg_queue; + dpi_enqueue_tcp_segments(seg,vm,node,next_index,to_next,n_left_to_next,bi0,next0); + flow0->s2c.seg_queue = 0; + } + goto trace0; + } + else + { + /* detect layer 7 application for single packet */ + dpi_detect_application (payload0, payload_len0, flow0->info); + } + +enqueue0: + to_next[0] = bi0; + to_next++; + n_left_to_next--; + next0 = flow0->next_index; + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + +trace0: + if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) + { + dpi_rx_trace_t *tr + = vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->app_id = flow0->info->app_id; + tr->next_index = next0; + tr->error = b0->error; + tr->flow_id = flow_index0; + } + + from += 1; + n_left_from -= 1; + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + return frame->n_vectors; +} + +VLIB_NODE_FN (dpi4_input_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return dpi_input_inline (vm, node, frame, /* is_ip4 */ 1); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (dpi4_input_node) = +{ + .name = "dpi4-input", + .vector_size = sizeof (u32), + .n_errors = DPI_INPUT_N_ERROR, + .error_strings = dpi_input_error_strings, + .n_next_nodes = DPI_INPUT_N_NEXT, + .next_nodes = { +#define _(s,n) [DPI_INPUT_NEXT_##s] = n, + foreach_dpi_input_next +#undef _ + }, + .format_trace = format_dpi_rx_trace, +}; + +/* *INDENT-ON* */ + +/* Dummy init function to get us linked in. */ +static clib_error_t * +dpi4_input_init (vlib_main_t * vm) +{ + return 0; +} + +VLIB_INIT_FUNCTION (dpi4_input_init); + + +VLIB_NODE_FN (dpi6_input_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return dpi_input_inline (vm, node, frame, /* is_ip4 */ 0); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (dpi6_input_node) = +{ + .name = "dpi6-input", + .vector_size = sizeof (u32), + .n_errors = DPI_INPUT_N_ERROR, + .error_strings = dpi_input_error_strings, + .n_next_nodes = DPI_INPUT_N_NEXT, + .next_nodes = { +#define _(s,n) [DPI_INPUT_NEXT_##s] = n, + foreach_dpi_input_next +#undef _ + }, + .format_trace = format_dpi_rx_trace, +}; +/* *INDENT-ON* */ + +/* Dummy init function to get us linked in. */ +static clib_error_t * +dpi6_input_init (vlib_main_t * vm) +{ + return 0; +} + +VLIB_INIT_FUNCTION (dpi6_input_init); + + + +#define foreach_dpi_flow_input_next \ +_(DROP, "error-drop") \ +_(IP4_LOOKUP, "ip4-lookup") + +typedef enum +{ +#define _(s,n) DPI_FLOW_NEXT_##s, + foreach_dpi_flow_input_next +#undef _ + DPI_FLOW_N_NEXT, +} dpi_flow_input_next_t; + +#define foreach_dpi_flow_error \ + _(NONE, "no error") \ + _(IP_CHECKSUM_ERROR, "Rx ip checksum errors") \ + _(IP_HEADER_ERROR, "Rx ip header errors") \ + _(UDP_CHECKSUM_ERROR, "Rx udp checksum errors") \ + _(UDP_LENGTH_ERROR, "Rx udp length errors") + +typedef enum +{ +#define _(f,s) DPI_FLOW_ERROR_##f, + foreach_dpi_flow_error +#undef _ + DPI_FLOW_N_ERROR, +} dpi_flow_error_t; + +static char *dpi_flow_error_strings[] = { +#define _(n,s) s, + foreach_dpi_flow_error +#undef _ +}; + +static_always_inline u8 +dpi_check_ip4 (ip4_header_t * ip4, u16 payload_len) +{ + u16 ip_len = clib_net_to_host_u16 (ip4->length); + return ip_len > payload_len || ip4->ttl == 0 + || ip4->ip_version_and_header_length != 0x45; +} + +static_always_inline u8 +dpi_check_ip6 (ip6_header_t * ip6, u16 payload_len) +{ + u16 ip_len = clib_net_to_host_u16 (ip6->payload_length); + return ip_len > (payload_len - sizeof (ip6_header_t)) + || ip6->hop_limit == 0 + || (ip6->ip_version_traffic_class_and_flow_label >> 28) != 0x6; +} + +always_inline uword +dpi_flow_input_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, u32 is_ip4) +{ + dpi_main_t *dm = &dpi_main; + u32 *from, *to_next, n_left_from, n_left_to_next, next_index; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0, next0 = DPI_FLOW_NEXT_IP4_LOOKUP; + vlib_buffer_t *b0; + ip4_header_t *ip40; + ip6_header_t *ip60; + tcp_header_t *tcp0; + udp_header_t *udp0; + u32 flow_id0 = ~0; + u32 flow_index0 = ~0; + u32 is_reverse0 = 0; + dpi_flow_entry_t *flow0; + u32 ip_len0, l4_len0, payload_len0; + u8 protocol0; + u8 *l4_pkt0, *payload0; + u16 dst_port = 0; + segment *seg = 0; + segment *prev_seg = 0; + + bi0 = to_next[0] = from[0]; + b0 = vlib_get_buffer (vm, bi0); + ip_len0 = vlib_buffer_length_in_chain (vm, b0); + + if (is_ip4) + { + ip40 = vlib_buffer_get_current (b0); + dpi_check_ip4 (ip40, ip_len0); + } + else + { + ip60 = vlib_buffer_get_current (b0); + dpi_check_ip6 (ip60, ip_len0); + } + + ASSERT (b0->flow_id != 0); + flow_id0 = b0->flow_id - dm->flow_id_start; + + is_reverse0 = (u32) ((flow_id0 >> 31) & 0x1); + flow_index0 = (u32) (flow_id0 & (u32) (~(1 << 31))); + flow0 = pool_elt_at_index (dm->dpi_flows, flow_index0); + + /* have detected successfully, directly return */ + if (flow0->info->detect_done) + goto enqueue0; + + /* check layer4 */ + if (is_ip4) + { + l4_pkt0 = (u8 *) (ip40 + 1); + l4_len0 = ip_len0 - sizeof (ip4_header_t); + protocol0 = ip40->protocol; + } + else + { + l4_pkt0 = (u8 *) (ip60 + 1); + l4_len0 = ip_len0 - sizeof (ip6_header_t); + protocol0 = ip60->protocol; + } + + if ((protocol0 == IP_PROTOCOL_TCP) && (l4_len0 >= 20)) + { + tcp0 = (tcp_header_t *) l4_pkt0; + payload_len0 = l4_len0 - tcp_doff (tcp0) * 4; + payload0 = l4_pkt0 + tcp_doff (tcp0) * 4; + dst_port = tcp0->dst_port; + } + else if ((protocol0 == IP_PROTOCOL_UDP) && (l4_len0 >= 8)) + { + udp0 = (udp_header_t *) l4_pkt0; + payload_len0 = l4_len0 - sizeof (udp_header_t); + payload0 = l4_pkt0 + sizeof (udp_header_t); + dst_port = udp0->dst_port; + } + else + { + payload_len0 = l4_len0; + payload0 = l4_pkt0; + } + + flow0->info->l4_protocol = protocol0; + flow0->info->dst_port = dst_port; + + /* TCP stream reassembly and detect a protocol pdu */ + if ((protocol0 == IP_PROTOCOL_TCP) && (flow0->reass_en)) + { + dpi_handle_tcp_stream (flow0, bi0, l4_pkt0, payload_len0, + is_reverse0); + + /* This packet has been consumed, retrieve next packet */ + if (flow0->consumed) + goto trace0; + + /* send out continuous scanned segments */ + seg = flow0->first_seg; + dpi_enqueue_tcp_segments (seg, vm, node, next_index, to_next, + n_left_to_next, bi0, next0); + flow0->first_seg = 0; + + /* Here detected successfully, send out remaining segments in seg_queue */ + if (flow0->info->detect_done) + { + seg = flow0->c2s.seg_queue; + dpi_enqueue_tcp_segments (seg, vm, node, next_index, + to_next, n_left_to_next, bi0, + next0); + flow0->c2s.seg_queue = 0; + + seg = flow0->s2c.seg_queue; + dpi_enqueue_tcp_segments (seg, vm, node, next_index, + to_next, n_left_to_next, bi0, + next0); + flow0->s2c.seg_queue = 0; + } + goto trace0; + } + else + { + /* detect layer 7 application for single packet */ + dpi_detect_application (payload0, payload_len0, flow0->info); + } + +enqueue0: + to_next[0] = bi0; + to_next++; + n_left_to_next--; + next0 = flow0->next_index; + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + +trace0: + if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) + { + dpi_rx_trace_t *tr + = vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->app_id = flow0->info->app_id; + tr->next_index = next0; + tr->error = b0->error; + tr->flow_id = flow_index0; + } + + from += 1; + n_left_from -= 1; + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + return frame->n_vectors; +} + + +VLIB_NODE_FN (dpi4_flow_input_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return dpi_flow_input_inline (vm, node, frame, /* is_ip4 */ 1); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (dpi4_flow_input_node) = { + .name = "dpi4-flow-input", + .type = VLIB_NODE_TYPE_INTERNAL, + .vector_size = sizeof (u32), + + .format_trace = format_dpi_rx_trace, + + .n_errors = DPI_FLOW_N_ERROR, + .error_strings = dpi_flow_error_strings, + + .n_next_nodes = DPI_FLOW_N_NEXT, + .next_nodes = { +#define _(s,n) [DPI_FLOW_NEXT_##s] = n, + foreach_dpi_flow_input_next +#undef _ + }, +}; +/* *INDENT-ON* */ + +/* Dummy init function to get us linked in. */ +static clib_error_t * +dpi4_flow_input_init (vlib_main_t * vm) +{ + return 0; +} + +VLIB_INIT_FUNCTION (dpi4_flow_input_init); + +VLIB_NODE_FN (dpi6_flow_input_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return dpi_flow_input_inline (vm, node, frame, /* is_ip4 */ 0); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (dpi6_flow_input_node) = { + .name = "dpi6-flow-input", + .type = VLIB_NODE_TYPE_INTERNAL, + .vector_size = sizeof (u32), + + .format_trace = format_dpi_rx_trace, + + .n_errors = DPI_FLOW_N_ERROR, + .error_strings = dpi_flow_error_strings, + + .n_next_nodes = DPI_FLOW_N_NEXT, + .next_nodes = { +#define _(s,n) [DPI_FLOW_NEXT_##s] = n, + foreach_dpi_flow_input_next +#undef _ + }, +}; +/* *INDENT-ON* */ + +/* Dummy init function to get us linked in. */ +static clib_error_t * +dpi6_flow_input_init (vlib_main_t * vm) +{ + return 0; +} + +VLIB_INIT_FUNCTION (dpi6_flow_input_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ |