diff options
Diffstat (limited to 'src/plugins/snat/in2out.c')
-rw-r--r-- | src/plugins/snat/in2out.c | 214 |
1 files changed, 205 insertions, 9 deletions
diff --git a/src/plugins/snat/in2out.c b/src/plugins/snat/in2out.c index f5443762336..89054a01b64 100644 --- a/src/plugins/snat/in2out.c +++ b/src/plugins/snat/in2out.c @@ -472,11 +472,12 @@ snat_in2out_error_t icmp_get_key(ip4_header_t *ip0, * @param[out] p_value address and port after NAT translation * @param[out] p_dont_translate if packet should not be translated * @param d optional parameter + * @param e optional parameter */ u32 icmp_match_in2out_slow(snat_main_t *sm, vlib_node_runtime_t *node, u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, snat_session_key_t *p_value, - u8 *p_dont_translate, void *d) + u8 *p_dont_translate, void *d, void *e) { ip4_header_t *ip0; icmp46_header_t *icmp0; @@ -559,11 +560,12 @@ out: * @param[out] p_value address and port after NAT translation * @param[out] p_dont_translate if packet should not be translated * @param d optional parameter + * @param e optional parameter */ u32 icmp_match_in2out_fast(snat_main_t *sm, vlib_node_runtime_t *node, u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, snat_session_key_t *p_value, - u8 *p_dont_translate, void *d) + u8 *p_dont_translate, void *d, void *e) { ip4_header_t *ip0; icmp46_header_t *icmp0; @@ -637,7 +639,8 @@ static inline u32 icmp_in2out (snat_main_t *sm, vlib_node_runtime_t * node, u32 next0, u32 thread_index, - void *d) + void *d, + void *e) { snat_session_key_t sm0; u8 protocol; @@ -655,7 +658,7 @@ static inline u32 icmp_in2out (snat_main_t *sm, echo0 = (icmp_echo_header_t *)(icmp0+1); next0_tmp = sm->icmp_match_in2out_cb(sm, node, thread_index, b0, - &protocol, &sm0, &dont_translate, d); + &protocol, &sm0, &dont_translate, d, e); if (next0_tmp != ~0) next0 = next0_tmp; if (next0 == SNAT_IN2OUT_NEXT_DROP || dont_translate) @@ -864,7 +867,7 @@ static inline u32 icmp_in2out_slow_path (snat_main_t *sm, snat_session_t ** p_s0) { next0 = icmp_in2out(sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, - next0, thread_index, p_s0); + next0, thread_index, p_s0, 0); snat_session_t * s0 = *p_s0; if (PREDICT_TRUE(next0 != SNAT_IN2OUT_NEXT_DROP && s0)) { @@ -1508,6 +1511,7 @@ snat_det_in2out_node_fn (vlib_main_t * vm, u32 pkts_processed = 0; snat_main_t * sm = &snat_main; u32 now = (u32) vlib_time_now (vm); + u32 thread_index = os_get_cpu_number (); from = vlib_frame_vector_args (frame); n_left_from = frame->n_vectors; @@ -1537,6 +1541,8 @@ snat_det_in2out_node_fn (vlib_main_t * vm, snat_det_out_key_t key0, key1; snat_det_map_t * dm0, * dm1; snat_det_session_t * ses0 = 0, * ses1 = 0; + u32 rx_fib_index0, rx_fib_index1; + icmp46_header_t * icmp0, * icmp1; /* Prefetch next iteration. */ { @@ -1582,6 +1588,19 @@ snat_det_in2out_node_fn (vlib_main_t * vm, goto trace0; } + proto0 = ip_proto_to_snat_proto (ip0->protocol); + + if (PREDICT_FALSE(proto0 == SNAT_PROTOCOL_ICMP)) + { + rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0); + icmp0 = (icmp46_header_t *) udp0; + + next0 = icmp_in2out(sm, b0, ip0, icmp0, sw_if_index0, + rx_fib_index0, node, next0, thread_index, + &ses0, &dm0); + goto trace0; + } + dm0 = snat_det_map_by_user(sm, &ip0->src_address); if (PREDICT_FALSE(!dm0)) { @@ -1619,7 +1638,6 @@ snat_det_in2out_node_fn (vlib_main_t * vm, } new_port0 = ses0->out.out_port; - proto0 = ip_proto_to_snat_proto (ip0->protocol); old_addr0.as_u32 = ip0->src_address.as_u32; ip0->src_address.as_u32 = new_addr0.as_u32; @@ -1714,6 +1732,19 @@ snat_det_in2out_node_fn (vlib_main_t * vm, goto trace1; } + proto1 = ip_proto_to_snat_proto (ip1->protocol); + + if (PREDICT_FALSE(proto1 == SNAT_PROTOCOL_ICMP)) + { + rx_fib_index1 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index1); + icmp1 = (icmp46_header_t *) udp1; + + next1 = icmp_in2out(sm, b1, ip1, icmp1, sw_if_index1, + rx_fib_index1, node, next1, thread_index, + &ses1, &dm1); + goto trace1; + } + dm1 = snat_det_map_by_user(sm, &ip1->src_address); if (PREDICT_FALSE(!dm1)) { @@ -1751,7 +1782,6 @@ snat_det_in2out_node_fn (vlib_main_t * vm, } new_port1 = ses1->out.out_port; - proto1 = ip_proto_to_snat_proto (ip1->protocol); old_addr1.as_u32 = ip1->src_address.as_u32; ip1->src_address.as_u32 = new_addr1.as_u32; @@ -1852,6 +1882,8 @@ snat_det_in2out_node_fn (vlib_main_t * vm, snat_det_out_key_t key0; snat_det_map_t * dm0; snat_det_session_t * ses0 = 0; + u32 rx_fib_index0; + icmp46_header_t * icmp0; /* speculatively enqueue b0 to the current next frame */ bi0 = from[0]; @@ -1880,6 +1912,19 @@ snat_det_in2out_node_fn (vlib_main_t * vm, goto trace00; } + proto0 = ip_proto_to_snat_proto (ip0->protocol); + + if (PREDICT_FALSE(proto0 == SNAT_PROTOCOL_ICMP)) + { + rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0); + icmp0 = (icmp46_header_t *) udp0; + + next0 = icmp_in2out(sm, b0, ip0, icmp0, sw_if_index0, + rx_fib_index0, node, next0, thread_index, + &ses0, &dm0); + goto trace00; + } + dm0 = snat_det_map_by_user(sm, &ip0->src_address); if (PREDICT_FALSE(!dm0)) { @@ -1917,7 +1962,6 @@ snat_det_in2out_node_fn (vlib_main_t * vm, } new_port0 = ses0->out.out_port; - proto0 = ip_proto_to_snat_proto (ip0->protocol); old_addr0.as_u32 = ip0->src_address.as_u32; ip0->src_address.as_u32 = new_addr0.as_u32; @@ -2035,6 +2079,158 @@ VLIB_REGISTER_NODE (snat_det_in2out_node) = { VLIB_NODE_FUNCTION_MULTIARCH (snat_det_in2out_node, snat_det_in2out_node_fn); +/** + * Get address and port values to be used for packet SNAT translation + * and create session if needed + * + * @param[in,out] sm SNAT main + * @param[in,out] node SNAT node runtime + * @param[in] thread_index thread index + * @param[in,out] b0 buffer containing packet to be translated + * @param[out] p_proto protocol used for matching + * @param[out] p_value address and port after NAT translation + * @param[out] p_dont_translate if packet should not be translated + * @param d optional parameter + * @param e optional parameter + */ +u32 icmp_match_in2out_det(snat_main_t *sm, vlib_node_runtime_t *node, + u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + snat_session_key_t *p_value, + u8 *p_dont_translate, void *d, void *e) +{ + ip4_header_t *ip0; + icmp46_header_t *icmp0; + u32 sw_if_index0; + u32 rx_fib_index0; + u8 protocol; + snat_det_out_key_t key0; + u8 dont_translate = 0; + u32 next0 = ~0; + icmp_echo_header_t *echo0, *inner_echo0 = 0; + ip4_header_t *inner_ip0; + void *l4_header = 0; + icmp46_header_t *inner_icmp0; + snat_det_map_t * dm0 = 0; + ip4_address_t new_addr0; + u16 lo_port0, i0; + snat_det_session_t * ses0 = 0; + ip4_address_t in_addr; + u16 in_port; + + ip0 = vlib_buffer_get_current (b0); + icmp0 = (icmp46_header_t *) ip4_next_header (ip0); + echo0 = (icmp_echo_header_t *)(icmp0+1); + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0); + + if (!icmp_is_error_message (icmp0)) + { + protocol = SNAT_PROTOCOL_ICMP; + in_addr = ip0->src_address; + in_port = echo0->identifier; + } + else + { + inner_ip0 = (ip4_header_t *)(echo0+1); + l4_header = ip4_next_header (inner_ip0); + protocol = ip_proto_to_snat_proto (inner_ip0->protocol); + in_addr = inner_ip0->dst_address; + switch (protocol) + { + case SNAT_PROTOCOL_ICMP: + inner_icmp0 = (icmp46_header_t*)l4_header; + inner_echo0 = (icmp_echo_header_t *)(inner_icmp0+1); + in_port = inner_echo0->identifier; + break; + case SNAT_PROTOCOL_UDP: + case SNAT_PROTOCOL_TCP: + in_port = ((tcp_udp_header_t*)l4_header)->dst_port; + break; + default: + b0->error = node->errors[SNAT_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL]; + next0 = SNAT_IN2OUT_NEXT_DROP; + goto out; + } + } + + dm0 = snat_det_map_by_user(sm, &in_addr); + if (PREDICT_FALSE(!dm0)) + { + clib_warning("no match for internal host %U", + format_ip4_address, &in_addr); + if (PREDICT_FALSE(snat_not_translate_fast(sm, node, sw_if_index0, ip0, + IP_PROTOCOL_ICMP, rx_fib_index0))) + { + dont_translate = 1; + goto out; + } + next0 = SNAT_IN2OUT_NEXT_DROP; + b0->error = node->errors[SNAT_IN2OUT_ERROR_NO_TRANSLATION]; + goto out; + } + + snat_det_forward(dm0, &in_addr, &new_addr0, &lo_port0); + + ses0 = snat_det_find_ses_by_in(dm0, &in_addr, in_port); + if (PREDICT_FALSE(!ses0)) + { + if (PREDICT_FALSE(snat_not_translate_fast(sm, node, sw_if_index0, ip0, + IP_PROTOCOL_ICMP, rx_fib_index0))) + { + dont_translate = 1; + goto out; + } + if (icmp0->type != ICMP4_echo_request) + { + b0->error = node->errors[SNAT_IN2OUT_ERROR_BAD_ICMP_TYPE]; + next0 = SNAT_IN2OUT_NEXT_DROP; + goto out; + } + key0.ext_host_addr = ip0->dst_address; + key0.ext_host_port = 0; + for (i0 = 0; i0 < dm0->ports_per_host; i0++) + { + key0.out_port = clib_host_to_net_u16 (lo_port0 + + ((i0 + clib_net_to_host_u16 (echo0->identifier)) % dm0->ports_per_host)); + + if (snat_det_get_ses_by_out (dm0, &in_addr, key0.as_u64)) + continue; + + ses0 = snat_det_ses_create(dm0, &in_addr, echo0->identifier, &key0); + break; + } + if (PREDICT_FALSE(!ses0)) + { + next0 = SNAT_IN2OUT_NEXT_DROP; + b0->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS]; + goto out; + } + } + + if (PREDICT_FALSE(icmp0->type != ICMP4_echo_request && + !icmp_is_error_message (icmp0))) + { + b0->error = node->errors[SNAT_IN2OUT_ERROR_BAD_ICMP_TYPE]; + next0 = SNAT_IN2OUT_NEXT_DROP; + goto out; + } + +out: + *p_proto = protocol; + if (ses0) + { + p_value->addr = new_addr0; + p_value->fib_index = sm->outside_fib_index; + p_value->port = ses0->out.out_port; + } + *p_dont_translate = dont_translate; + if (d) + *(snat_det_session_t**)d = ses0; + if (e) + *(snat_det_map_t**)e = dm0; + return next0; +} + /**********************/ /*** worker handoff ***/ /**********************/ @@ -2276,7 +2472,7 @@ snat_in2out_fast_static_map_fn (vlib_main_t * vm, if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP)) { next0 = icmp_in2out(sm, b0, ip0, icmp0, sw_if_index0, - rx_fib_index0, node, next0, ~0, 0); + rx_fib_index0, node, next0, ~0, 0, 0); goto trace0; } |