Refactor IP input checks for re-use at MPLS disposition

Change-Id: I7aafdecd6f370411138e6ab67b2ff72cda6e0666
Signed-off-by: Neale Ranns <nranns@cisco.com>
This commit is contained in:
Neale Ranns
2017-10-21 09:37:55 -07:00
committed by Damjan Marion
parent b3d1b20357
commit 4c7c8e55b0
8 changed files with 698 additions and 280 deletions

View File

@ -13,7 +13,8 @@
* limitations under the License.
*/
#include <vnet/ip/ip.h>
#include <vnet/ip/ip4_input.h>
#include <vnet/ip/ip6_input.h>
#include <vnet/dpo/mpls_disposition.h>
#include <vnet/mpls/mpls.h>
@ -115,6 +116,9 @@ typedef struct mpls_label_disposition_trace_t_
index_t mdd;
} mpls_label_disposition_trace_t;
extern vlib_node_registration_t ip4_mpls_label_disposition_node;
extern vlib_node_registration_t ip6_mpls_label_disposition_node;
always_inline uword
mpls_label_disposition_inline (vlib_main_t * vm,
vlib_node_runtime_t * node,
@ -123,6 +127,12 @@ mpls_label_disposition_inline (vlib_main_t * vm,
u8 payload_is_ip6)
{
u32 n_left_from, next_index, * from, * to_next;
vlib_node_runtime_t *error_node;
if (payload_is_ip4)
error_node = vlib_node_get_runtime (vm, ip4_mpls_label_disposition_node.index);
else
error_node = vlib_node_get_runtime (vm, ip6_mpls_label_disposition_node.index);
from = vlib_frame_vector_args (from_frame);
n_left_from = from_frame->n_vectors;
@ -173,21 +183,39 @@ mpls_label_disposition_inline (vlib_main_t * vm,
mdd0 = mpls_disp_dpo_get(mddi0);
mdd1 = mpls_disp_dpo_get(mddi1);
next0 = mdd0->mdd_dpo.dpoi_next_node;
next1 = mdd1->mdd_dpo.dpoi_next_node;
if (payload_is_ip4)
{
ip4_header_t *ip0, *ip1;
ip0 = vlib_buffer_get_current (b0);
ip1 = vlib_buffer_get_current (b1);
/*
* decrement the TTL on ingress to the LSP
* IPv4 input checks on the exposed IP header
* including checksum
*/
ip4_input_check_x2 (vm, error_node,
b0, b1, ip0, ip1,
&next0, &next1, 1);
}
else if (payload_is_ip6)
{
ip6_header_t *ip0, *ip1;
ip0 = vlib_buffer_get_current (b0);
ip1 = vlib_buffer_get_current (b1);
/*
* decrement the TTL on ingress to the LSP
* IPv6 input checks on the exposed IP header
*/
ip6_input_check_x2 (vm, error_node,
b0, b1, ip0, ip1,
&next0, &next1);
}
next0 = mdd0->mdd_dpo.dpoi_next_node;
next1 = mdd1->mdd_dpo.dpoi_next_node;
vnet_buffer(b0)->ip.adj_index[VLIB_TX] = mdd0->mdd_dpo.dpoi_index;
vnet_buffer(b1)->ip.adj_index[VLIB_TX] = mdd1->mdd_dpo.dpoi_index;
vnet_buffer(b0)->ip.rpf_id = mdd0->mdd_rpf_id;
@ -231,24 +259,32 @@ mpls_label_disposition_inline (vlib_main_t * vm,
/* dst lookup was done by ip4 lookup */
mddi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
mdd0 = mpls_disp_dpo_get(mddi0);
next0 = mdd0->mdd_dpo.dpoi_next_node;
if (payload_is_ip4)
{
ip4_header_t *ip0;
ip0 = vlib_buffer_get_current (b0);
/*
* decrement the TTL on ingress to the LSP
* IPv4 input checks on the exposed IP header
* including checksum
*/
ip4_input_check_x1 (vm, error_node, b0, ip0, &next0, 1);
}
else if (payload_is_ip6)
{
ip6_header_t *ip0;
ip0 = vlib_buffer_get_current (b0);
/*
* decrement the TTL on ingress to the LSP
* IPv6 input checks on the exposed IP header
*/
}
else
{
ip6_input_check_x1 (vm, error_node, b0, ip0, &next0);
}
next0 = mdd0->mdd_dpo.dpoi_next_node;
vnet_buffer(b0)->ip.adj_index[VLIB_TX] = mdd0->mdd_dpo.dpoi_index;
vnet_buffer(b0)->ip.rpf_id = mdd0->mdd_rpf_id;
@ -294,10 +330,9 @@ VLIB_REGISTER_NODE (ip4_mpls_label_disposition_node) = {
.vector_size = sizeof (u32),
.format_trace = format_mpls_label_disposition_trace,
.n_next_nodes = 1,
.next_nodes = {
[0] = "ip4-drop",
}
.sibling_of = "ip4-input",
.n_errors = IP4_N_ERROR,
.error_strings = ip4_error_strings,
};
VLIB_NODE_FUNCTION_MULTIARCH (ip4_mpls_label_disposition_node,
ip4_mpls_label_disposition)
@ -316,10 +351,9 @@ VLIB_REGISTER_NODE (ip6_mpls_label_disposition_node) = {
.vector_size = sizeof (u32),
.format_trace = format_mpls_label_disposition_trace,
.n_next_nodes = 1,
.next_nodes = {
[0] = "ip6-drop",
}
.sibling_of = "ip6-input",
.n_errors = IP6_N_ERROR,
.error_strings = ip6_error_strings,
};
VLIB_NODE_FUNCTION_MULTIARCH (ip6_mpls_label_disposition_node,
ip6_mpls_label_disposition)

View File

@ -37,7 +37,7 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <vnet/ip/ip.h>
#include <vnet/ip/ip4_input.h>
#include <vnet/ethernet/ethernet.h>
#include <vnet/ppp/ppp.h>
#include <vnet/hdlc/hdlc.h>
@ -60,16 +60,6 @@ format_ip4_input_trace (u8 * s, va_list * va)
return s;
}
typedef enum
{
IP4_INPUT_NEXT_DROP,
IP4_INPUT_NEXT_PUNT,
IP4_INPUT_NEXT_LOOKUP,
IP4_INPUT_NEXT_LOOKUP_MULTICAST,
IP4_INPUT_NEXT_ICMP_ERROR,
IP4_INPUT_N_NEXT,
} ip4_input_next_t;
/* Validate IP v4 packets and pass them either to forwarding code
or drop/punt exception packets. */
always_inline uword
@ -109,10 +99,9 @@ ip4_input_inline (vlib_main_t * vm,
{
vlib_buffer_t *p0, *p1;
ip4_header_t *ip0, *ip1;
u32 sw_if_index0, pi0, ip_len0, cur_len0, next0;
u32 sw_if_index1, pi1, ip_len1, cur_len1, next1;
i32 len_diff0, len_diff1;
u8 error0, error1, arc0, arc1;
u32 sw_if_index0, pi0, next0;
u32 sw_if_index1, pi1, next1;
u8 arc0, arc1;
/* Prefetch next iteration. */
{
@ -144,8 +133,6 @@ ip4_input_inline (vlib_main_t * vm,
sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
sw_if_index1 = vnet_buffer (p1)->sw_if_index[VLIB_RX];
error0 = error1 = IP4_ERROR_NONE;
if (PREDICT_FALSE (ip4_address_is_multicast (&ip0->dst_address)))
{
arc0 = lm->mcast_feature_arc_index;
@ -155,8 +142,6 @@ ip4_input_inline (vlib_main_t * vm,
{
arc0 = lm->ucast_feature_arc_index;
next0 = IP4_INPUT_NEXT_LOOKUP;
if (PREDICT_FALSE (ip0->ttl < 1))
error0 = IP4_ERROR_TIME_EXPIRED;
}
if (PREDICT_FALSE (ip4_address_is_multicast (&ip1->dst_address)))
@ -168,8 +153,6 @@ ip4_input_inline (vlib_main_t * vm,
{
arc1 = lm->ucast_feature_arc_index;
next1 = IP4_INPUT_NEXT_LOOKUP;
if (PREDICT_FALSE (ip1->ttl < 1))
error1 = IP4_ERROR_TIME_EXPIRED;
}
vnet_buffer (p0)->ip.adj_index[VLIB_RX] = ~0;
@ -180,82 +163,9 @@ ip4_input_inline (vlib_main_t * vm,
vlib_increment_simple_counter (cm, thread_index, sw_if_index0, 1);
vlib_increment_simple_counter (cm, thread_index, sw_if_index1, 1);
/* Punt packets with options or wrong version. */
if (PREDICT_FALSE (ip0->ip_version_and_header_length != 0x45))
error0 = (ip0->ip_version_and_header_length & 0xf) != 5 ?
IP4_ERROR_OPTIONS : IP4_ERROR_VERSION;
if (PREDICT_FALSE (ip1->ip_version_and_header_length != 0x45))
error1 = (ip1->ip_version_and_header_length & 0xf) != 5 ?
IP4_ERROR_OPTIONS : IP4_ERROR_VERSION;
/* Verify header checksum. */
if (verify_checksum)
{
ip_csum_t sum0, sum1;
ip4_partial_header_checksum_x1 (ip0, sum0);
ip4_partial_header_checksum_x1 (ip1, sum1);
error0 = 0xffff != ip_csum_fold (sum0) ?
IP4_ERROR_BAD_CHECKSUM : error0;
error1 = 0xffff != ip_csum_fold (sum1) ?
IP4_ERROR_BAD_CHECKSUM : error1;
}
/* Drop fragmentation offset 1 packets. */
error0 = ip4_get_fragment_offset (ip0) == 1 ?
IP4_ERROR_FRAGMENT_OFFSET_ONE : error0;
error1 = ip4_get_fragment_offset (ip1) == 1 ?
IP4_ERROR_FRAGMENT_OFFSET_ONE : error1;
/* Verify lengths. */
ip_len0 = clib_net_to_host_u16 (ip0->length);
ip_len1 = clib_net_to_host_u16 (ip1->length);
/* IP length must be at least minimal IP header. */
error0 = ip_len0 < sizeof (ip0[0]) ? IP4_ERROR_TOO_SHORT : error0;
error1 = ip_len1 < sizeof (ip1[0]) ? IP4_ERROR_TOO_SHORT : error1;
cur_len0 = vlib_buffer_length_in_chain (vm, p0);
cur_len1 = vlib_buffer_length_in_chain (vm, p1);
len_diff0 = cur_len0 - ip_len0;
len_diff1 = cur_len1 - ip_len1;
error0 = len_diff0 < 0 ? IP4_ERROR_BAD_LENGTH : error0;
error1 = len_diff1 < 0 ? IP4_ERROR_BAD_LENGTH : error1;
p0->error = error_node->errors[error0];
p1->error = error_node->errors[error1];
if (PREDICT_FALSE (error0 != IP4_ERROR_NONE))
{
if (error0 == IP4_ERROR_TIME_EXPIRED)
{
icmp4_error_set_vnet_buffer (p0, ICMP4_time_exceeded,
ICMP4_time_exceeded_ttl_exceeded_in_transit,
0);
next0 = IP4_INPUT_NEXT_ICMP_ERROR;
}
else
next0 = error0 != IP4_ERROR_OPTIONS ?
IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_PUNT;
}
if (PREDICT_FALSE (error1 != IP4_ERROR_NONE))
{
if (error1 == IP4_ERROR_TIME_EXPIRED)
{
icmp4_error_set_vnet_buffer (p1, ICMP4_time_exceeded,
ICMP4_time_exceeded_ttl_exceeded_in_transit,
0);
next1 = IP4_INPUT_NEXT_ICMP_ERROR;
}
else
next1 = error1 != IP4_ERROR_OPTIONS ?
IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_PUNT;
}
ip4_input_check_x2 (vm, error_node,
p0, p1, ip0, ip1,
&next0, &next1, verify_checksum);
vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
to_next, n_left_to_next,
@ -265,9 +175,8 @@ ip4_input_inline (vlib_main_t * vm,
{
vlib_buffer_t *p0;
ip4_header_t *ip0;
u32 sw_if_index0, pi0, ip_len0, cur_len0, next0;
i32 len_diff0;
u8 error0, arc0;
u32 sw_if_index0, pi0, next0;
u8 arc0;
pi0 = from[0];
to_next[0] = pi0;
@ -281,8 +190,6 @@ ip4_input_inline (vlib_main_t * vm,
sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
error0 = IP4_ERROR_NONE;
if (PREDICT_FALSE (ip4_address_is_multicast (&ip0->dst_address)))
{
arc0 = lm->mcast_feature_arc_index;
@ -292,60 +199,14 @@ ip4_input_inline (vlib_main_t * vm,
{
arc0 = lm->ucast_feature_arc_index;
next0 = IP4_INPUT_NEXT_LOOKUP;
if (PREDICT_FALSE (ip0->ttl < 1))
error0 = IP4_ERROR_TIME_EXPIRED;
}
vnet_buffer (p0)->ip.adj_index[VLIB_RX] = ~0;
vnet_feature_arc_start (arc0, sw_if_index0, &next0, p0);
vlib_increment_simple_counter (cm, thread_index, sw_if_index0, 1);
/* Punt packets with options or wrong version. */
if (PREDICT_FALSE (ip0->ip_version_and_header_length != 0x45))
error0 = (ip0->ip_version_and_header_length & 0xf) != 5 ?
IP4_ERROR_OPTIONS : IP4_ERROR_VERSION;
/* Verify header checksum. */
if (verify_checksum)
{
ip_csum_t sum0;
ip4_partial_header_checksum_x1 (ip0, sum0);
error0 =
0xffff !=
ip_csum_fold (sum0) ? IP4_ERROR_BAD_CHECKSUM : error0;
}
/* Drop fragmentation offset 1 packets. */
error0 =
ip4_get_fragment_offset (ip0) ==
1 ? IP4_ERROR_FRAGMENT_OFFSET_ONE : error0;
/* Verify lengths. */
ip_len0 = clib_net_to_host_u16 (ip0->length);
/* IP length must be at least minimal IP header. */
error0 = ip_len0 < sizeof (ip0[0]) ? IP4_ERROR_TOO_SHORT : error0;
cur_len0 = vlib_buffer_length_in_chain (vm, p0);
len_diff0 = cur_len0 - ip_len0;
error0 = len_diff0 < 0 ? IP4_ERROR_BAD_LENGTH : error0;
p0->error = error_node->errors[error0];
if (PREDICT_FALSE (error0 != IP4_ERROR_NONE))
{
if (error0 == IP4_ERROR_TIME_EXPIRED)
{
icmp4_error_set_vnet_buffer (p0, ICMP4_time_exceeded,
ICMP4_time_exceeded_ttl_exceeded_in_transit,
0);
next0 = IP4_INPUT_NEXT_ICMP_ERROR;
}
else
next0 = error0 != IP4_ERROR_OPTIONS ?
IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_PUNT;
}
ip4_input_check_x1 (vm, error_node, p0, ip0, &next0,
verify_checksum);
vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
to_next, n_left_to_next,
@ -406,7 +267,7 @@ ip4_input_no_checksum (vlib_main_t * vm,
return ip4_input_inline (vm, node, frame, /* verify_checksum */ 0);
}
static char *ip4_error_strings[] = {
char *ip4_error_strings[] = {
#define _(sym,string) string,
foreach_ip4_error
#undef _

223
src/vnet/ip/ip4_input.h Normal file
View File

@ -0,0 +1,223 @@
/*
* Copyright (c) 2017 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.
*/
/*
* ip/ip4_input.c: IP v4 input node
*
* Copyright (c) 2008 Eliot Dresselhaus
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef included_ip_input_h
#define included_ip_input_h
#include <vnet/ip/ip.h>
#include <vnet/ethernet/ethernet.h>
extern char *ip4_error_strings[];
typedef enum
{
IP4_INPUT_NEXT_DROP,
IP4_INPUT_NEXT_PUNT,
IP4_INPUT_NEXT_LOOKUP,
IP4_INPUT_NEXT_LOOKUP_MULTICAST,
IP4_INPUT_NEXT_ICMP_ERROR,
IP4_INPUT_N_NEXT,
} ip4_input_next_t;
always_inline void
ip4_input_check_x2 (vlib_main_t * vm,
vlib_node_runtime_t * error_node,
vlib_buffer_t * p0, vlib_buffer_t * p1,
ip4_header_t * ip0, ip4_header_t * ip1,
u32 * next0, u32 * next1, int verify_checksum)
{
u8 error0, error1;
u32 ip_len0, cur_len0;
u32 ip_len1, cur_len1;
i32 len_diff0, len_diff1;
error0 = error1 = IP4_ERROR_NONE;
/* Punt packets with options or wrong version. */
if (PREDICT_FALSE (ip0->ip_version_and_header_length != 0x45))
error0 = (ip0->ip_version_and_header_length & 0xf) != 5 ?
IP4_ERROR_OPTIONS : IP4_ERROR_VERSION;
if (PREDICT_FALSE (ip1->ip_version_and_header_length != 0x45))
error1 = (ip1->ip_version_and_header_length & 0xf) != 5 ?
IP4_ERROR_OPTIONS : IP4_ERROR_VERSION;
if (PREDICT_FALSE (ip0->ttl < 1))
error0 = IP4_ERROR_TIME_EXPIRED;
if (PREDICT_FALSE (ip1->ttl < 1))
error1 = IP4_ERROR_TIME_EXPIRED;
/* Verify header checksum. */
if (verify_checksum)
{
ip_csum_t sum0, sum1;
ip4_partial_header_checksum_x1 (ip0, sum0);
ip4_partial_header_checksum_x1 (ip1, sum1);
error0 = 0xffff != ip_csum_fold (sum0) ?
IP4_ERROR_BAD_CHECKSUM : error0;
error1 = 0xffff != ip_csum_fold (sum1) ?
IP4_ERROR_BAD_CHECKSUM : error1;
}
/* Drop fragmentation offset 1 packets. */
error0 = ip4_get_fragment_offset (ip0) == 1 ?
IP4_ERROR_FRAGMENT_OFFSET_ONE : error0;
error1 = ip4_get_fragment_offset (ip1) == 1 ?
IP4_ERROR_FRAGMENT_OFFSET_ONE : error1;
/* Verify lengths. */
ip_len0 = clib_net_to_host_u16 (ip0->length);
ip_len1 = clib_net_to_host_u16 (ip1->length);
/* IP length must be at least minimal IP header. */
error0 = ip_len0 < sizeof (ip0[0]) ? IP4_ERROR_TOO_SHORT : error0;
error1 = ip_len1 < sizeof (ip1[0]) ? IP4_ERROR_TOO_SHORT : error1;
cur_len0 = vlib_buffer_length_in_chain (vm, p0);
cur_len1 = vlib_buffer_length_in_chain (vm, p1);
len_diff0 = cur_len0 - ip_len0;
len_diff1 = cur_len1 - ip_len1;
error0 = len_diff0 < 0 ? IP4_ERROR_BAD_LENGTH : error0;
error1 = len_diff1 < 0 ? IP4_ERROR_BAD_LENGTH : error1;
if (PREDICT_FALSE (error0 != IP4_ERROR_NONE))
{
if (error0 == IP4_ERROR_TIME_EXPIRED)
{
icmp4_error_set_vnet_buffer (p0, ICMP4_time_exceeded,
ICMP4_time_exceeded_ttl_exceeded_in_transit,
0);
*next0 = IP4_INPUT_NEXT_ICMP_ERROR;
}
else
*next0 = error0 != IP4_ERROR_OPTIONS ?
IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_PUNT;
}
if (PREDICT_FALSE (error1 != IP4_ERROR_NONE))
{
if (error1 == IP4_ERROR_TIME_EXPIRED)
{
icmp4_error_set_vnet_buffer (p1, ICMP4_time_exceeded,
ICMP4_time_exceeded_ttl_exceeded_in_transit,
0);
*next1 = IP4_INPUT_NEXT_ICMP_ERROR;
}
else
*next1 = error1 != IP4_ERROR_OPTIONS ?
IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_PUNT;
}
p0->error = error_node->errors[error0];
p1->error = error_node->errors[error1];
}
always_inline void
ip4_input_check_x1 (vlib_main_t * vm,
vlib_node_runtime_t * error_node,
vlib_buffer_t * p0,
ip4_header_t * ip0, u32 * next0, int verify_checksum)
{
u32 ip_len0, cur_len0;
i32 len_diff0;
u8 error0;
error0 = IP4_ERROR_NONE;
/* Punt packets with options or wrong version. */
if (PREDICT_FALSE (ip0->ip_version_and_header_length != 0x45))
error0 = (ip0->ip_version_and_header_length & 0xf) != 5 ?
IP4_ERROR_OPTIONS : IP4_ERROR_VERSION;
/* Verify header checksum. */
if (verify_checksum)
{
ip_csum_t sum0;
ip4_partial_header_checksum_x1 (ip0, sum0);
error0 = 0xffff != ip_csum_fold (sum0) ?
IP4_ERROR_BAD_CHECKSUM : error0;
}
/* Drop fragmentation offset 1 packets. */
error0 = ip4_get_fragment_offset (ip0) == 1 ?
IP4_ERROR_FRAGMENT_OFFSET_ONE : error0;
/* Verify lengths. */
ip_len0 = clib_net_to_host_u16 (ip0->length);
/* IP length must be at least minimal IP header. */
error0 = ip_len0 < sizeof (ip0[0]) ? IP4_ERROR_TOO_SHORT : error0;
cur_len0 = vlib_buffer_length_in_chain (vm, p0);
len_diff0 = cur_len0 - ip_len0;
error0 = len_diff0 < 0 ? IP4_ERROR_BAD_LENGTH : error0;
if (PREDICT_FALSE (error0 != IP4_ERROR_NONE))
{
if (error0 == IP4_ERROR_TIME_EXPIRED)
{
icmp4_error_set_vnet_buffer (p0, ICMP4_time_exceeded,
ICMP4_time_exceeded_ttl_exceeded_in_transit,
0);
*next0 = IP4_INPUT_NEXT_ICMP_ERROR;
}
else
*next0 = error0 != IP4_ERROR_OPTIONS ?
IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_PUNT;
}
p0->error = error_node->errors[error0];
}
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/
#endif

View File

@ -37,7 +37,7 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <vnet/ip/ip.h>
#include <vnet/ip/ip6_input.h>
#include <vnet/ethernet/ethernet.h>
#include <vnet/ppp/ppp.h>
#include <vnet/hdlc/hdlc.h>
@ -60,15 +60,6 @@ format_ip6_input_trace (u8 * s, va_list * va)
return s;
}
typedef enum
{
IP6_INPUT_NEXT_DROP,
IP6_INPUT_NEXT_LOOKUP,
IP6_INPUT_NEXT_LOOKUP_MULTICAST,
IP6_INPUT_NEXT_ICMP_ERROR,
IP6_INPUT_N_NEXT,
} ip6_input_next_t;
/* Validate IP v6 packets and pass them either to forwarding code
or drop exception packets. */
static uword
@ -108,7 +99,7 @@ ip6_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
ip6_header_t *ip0, *ip1;
u32 pi0, sw_if_index0, next0 = 0;
u32 pi1, sw_if_index1, next1 = 0;
u8 error0, error1, arc0, arc1;
u8 arc0, arc1;
/* Prefetch next iteration. */
{
@ -173,65 +164,8 @@ ip6_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
vlib_increment_simple_counter (cm, thread_index, sw_if_index0, 1);
vlib_increment_simple_counter (cm, thread_index, sw_if_index1, 1);
error0 = error1 = IP6_ERROR_NONE;
/* Version != 6? Drop it. */
error0 =
(clib_net_to_host_u32
(ip0->ip_version_traffic_class_and_flow_label) >> 28) !=
6 ? IP6_ERROR_VERSION : error0;
error1 =
(clib_net_to_host_u32
(ip1->ip_version_traffic_class_and_flow_label) >> 28) !=
6 ? IP6_ERROR_VERSION : error1;
/* hop limit < 1? Drop it. for link-local broadcast packets,
* like dhcpv6 packets from client has hop-limit 1, which should not
* be dropped.
*/
error0 = ip0->hop_limit < 1 ? IP6_ERROR_TIME_EXPIRED : error0;
error1 = ip1->hop_limit < 1 ? IP6_ERROR_TIME_EXPIRED : error1;
/* L2 length must be at least minimal IP header. */
error0 =
p0->current_length <
sizeof (ip0[0]) ? IP6_ERROR_TOO_SHORT : error0;
error1 =
p1->current_length <
sizeof (ip1[0]) ? IP6_ERROR_TOO_SHORT : error1;
if (PREDICT_FALSE (error0 != IP6_ERROR_NONE))
{
if (error0 == IP6_ERROR_TIME_EXPIRED)
{
icmp6_error_set_vnet_buffer (p0, ICMP6_time_exceeded,
ICMP6_time_exceeded_ttl_exceeded_in_transit,
0);
next0 = IP6_INPUT_NEXT_ICMP_ERROR;
}
else
{
next0 = IP6_INPUT_NEXT_DROP;
}
}
if (PREDICT_FALSE (error1 != IP6_ERROR_NONE))
{
if (error1 == IP6_ERROR_TIME_EXPIRED)
{
icmp6_error_set_vnet_buffer (p1, ICMP6_time_exceeded,
ICMP6_time_exceeded_ttl_exceeded_in_transit,
0);
next1 = IP6_INPUT_NEXT_ICMP_ERROR;
}
else
{
next1 = IP6_INPUT_NEXT_DROP;
}
}
p0->error = error_node->errors[error0];
p1->error = error_node->errors[error1];
ip6_input_check_x2 (vm, error_node,
p0, p1, ip0, ip1, &next0, &next1);
vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
to_next, n_left_to_next,
@ -243,7 +177,7 @@ ip6_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
vlib_buffer_t *p0;
ip6_header_t *ip0;
u32 pi0, sw_if_index0, next0 = 0;
u8 error0, arc0;
u8 arc0;
pi0 = from[0];
to_next[0] = pi0;
@ -271,40 +205,7 @@ ip6_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
vnet_feature_arc_start (arc0, sw_if_index0, &next0, p0);
vlib_increment_simple_counter (cm, thread_index, sw_if_index0, 1);
error0 = IP6_ERROR_NONE;
/* Version != 6? Drop it. */
error0 =
(clib_net_to_host_u32
(ip0->ip_version_traffic_class_and_flow_label) >> 28) !=
6 ? IP6_ERROR_VERSION : error0;
/* hop limit < 1? Drop it. for link-local broadcast packets,
* like dhcpv6 packets from client has hop-limit 1, which should not
* be dropped.
*/
error0 = ip0->hop_limit < 1 ? IP6_ERROR_TIME_EXPIRED : error0;
/* L2 length must be at least minimal IP header. */
error0 =
p0->current_length <
sizeof (ip0[0]) ? IP6_ERROR_TOO_SHORT : error0;
if (PREDICT_FALSE (error0 != IP6_ERROR_NONE))
{
if (error0 == IP6_ERROR_TIME_EXPIRED)
{
icmp6_error_set_vnet_buffer (p0, ICMP6_time_exceeded,
ICMP6_time_exceeded_ttl_exceeded_in_transit,
0);
next0 = IP6_INPUT_NEXT_ICMP_ERROR;
}
else
{
next0 = IP6_INPUT_NEXT_DROP;
}
}
p0->error = error_node->errors[error0];
ip6_input_check_x1 (vm, error_node, p0, ip0, &next0);
vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
to_next, n_left_to_next,
@ -317,7 +218,7 @@ ip6_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
return frame->n_vectors;
}
static char *ip6_error_strings[] = {
char *ip6_error_strings[] = {
#define _(sym,string) string,
foreach_ip6_error
#undef _

169
src/vnet/ip/ip6_input.h Normal file
View File

@ -0,0 +1,169 @@
/*
* Copyright (c) 2017 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.
*/
/*
* ip/ip6_input.c: IP v6 input node
*
* Copyright (c) 2008 Eliot Dresselhaus
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef included_ip6_input_h
#define included_ip6_input_h
#include <vnet/ip/ip.h>
extern char *ip6_error_strings[];
typedef enum
{
IP6_INPUT_NEXT_DROP,
IP6_INPUT_NEXT_LOOKUP,
IP6_INPUT_NEXT_LOOKUP_MULTICAST,
IP6_INPUT_NEXT_ICMP_ERROR,
IP6_INPUT_N_NEXT,
} ip6_input_next_t;
always_inline void
ip6_input_check_x2 (vlib_main_t * vm,
vlib_node_runtime_t * error_node,
vlib_buffer_t * p0, vlib_buffer_t * p1,
ip6_header_t * ip0, ip6_header_t * ip1,
u32 * next0, u32 * next1)
{
u8 error0, error1;
error0 = error1 = IP6_ERROR_NONE;
/* Version != 6? Drop it. */
error0 =
(clib_net_to_host_u32
(ip0->ip_version_traffic_class_and_flow_label) >> 28) !=
6 ? IP6_ERROR_VERSION : error0;
error1 =
(clib_net_to_host_u32
(ip1->ip_version_traffic_class_and_flow_label) >> 28) !=
6 ? IP6_ERROR_VERSION : error1;
/* hop limit < 1? Drop it. for link-local broadcast packets,
* like dhcpv6 packets from client has hop-limit 1, which should not
* be dropped.
*/
error0 = ip0->hop_limit < 1 ? IP6_ERROR_TIME_EXPIRED : error0;
error1 = ip1->hop_limit < 1 ? IP6_ERROR_TIME_EXPIRED : error1;
/* L2 length must be at least minimal IP header. */
error0 =
p0->current_length < sizeof (ip0[0]) ? IP6_ERROR_TOO_SHORT : error0;
error1 =
p1->current_length < sizeof (ip1[0]) ? IP6_ERROR_TOO_SHORT : error1;
if (PREDICT_FALSE (error0 != IP6_ERROR_NONE))
{
if (error0 == IP6_ERROR_TIME_EXPIRED)
{
icmp6_error_set_vnet_buffer (p0, ICMP6_time_exceeded,
ICMP6_time_exceeded_ttl_exceeded_in_transit,
0);
*next0 = IP6_INPUT_NEXT_ICMP_ERROR;
}
else
{
*next0 = IP6_INPUT_NEXT_DROP;
}
}
if (PREDICT_FALSE (error1 != IP6_ERROR_NONE))
{
if (error1 == IP6_ERROR_TIME_EXPIRED)
{
icmp6_error_set_vnet_buffer (p1, ICMP6_time_exceeded,
ICMP6_time_exceeded_ttl_exceeded_in_transit,
0);
*next1 = IP6_INPUT_NEXT_ICMP_ERROR;
}
else
{
*next1 = IP6_INPUT_NEXT_DROP;
}
}
}
always_inline void
ip6_input_check_x1 (vlib_main_t * vm,
vlib_node_runtime_t * error_node,
vlib_buffer_t * p0, ip6_header_t * ip0, u32 * next0)
{
u8 error0;
error0 = IP6_ERROR_NONE;
/* Version != 6? Drop it. */
error0 =
(clib_net_to_host_u32
(ip0->ip_version_traffic_class_and_flow_label) >> 28) !=
6 ? IP6_ERROR_VERSION : error0;
/* hop limit < 1? Drop it. for link-local broadcast packets,
* like dhcpv6 packets from client has hop-limit 1, which should not
* be dropped.
*/
error0 = ip0->hop_limit < 1 ? IP6_ERROR_TIME_EXPIRED : error0;
/* L2 length must be at least minimal IP header. */
error0 =
p0->current_length < sizeof (ip0[0]) ? IP6_ERROR_TOO_SHORT : error0;
if (PREDICT_FALSE (error0 != IP6_ERROR_NONE))
{
if (error0 == IP6_ERROR_TIME_EXPIRED)
{
icmp6_error_set_vnet_buffer (p0, ICMP6_time_exceeded,
ICMP6_time_exceeded_ttl_exceeded_in_transit,
0);
*next0 = IP6_INPUT_NEXT_ICMP_ERROR;
}
else
{
*next0 = IP6_INPUT_NEXT_DROP;
}
}
}
#endif
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/

View File

@ -1227,5 +1227,140 @@ class TestIPDeag(VppTestCase):
self.send_and_expect(self.pg0, pkts_src, self.pg2)
class TestIPInput(VppTestCase):
""" IPv4 Input Exceptions """
def setUp(self):
super(TestIPInput, self).setUp()
self.create_pg_interfaces(range(2))
for i in self.pg_interfaces:
i.admin_up()
i.config_ip4()
i.resolve_arp()
def tearDown(self):
super(TestIPInput, self).tearDown()
for i in self.pg_interfaces:
i.unconfig_ip4()
i.admin_down()
def send_and_expect(self, input, pkts, output):
self.vapi.cli("clear trace")
input.add_stream(pkts)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = output.get_capture(len(pkts))
return rx
def send_and_assert_no_replies(self, intf, pkts, remark):
self.vapi.cli("clear trace")
intf.add_stream(pkts)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
for i in self.pg_interfaces:
i.get_capture(0)
i.assert_nothing_captured(remark=remark)
def test_ip_input(self):
""" IP Input Exceptions """
# i can't find a way in scapy to construct an IP packet
# with a length less than the IP header length
#
# Packet too short - this is forwarded
#
p_short = (Ether(src=self.pg0.remote_mac,
dst=self.pg0.local_mac) /
IP(src=self.pg0.remote_ip4,
dst=self.pg1.remote_ip4,
len=40) /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
rx = self.send_and_expect(self.pg0, p_short * 65, self.pg1)
#
# Packet too long - this is dropped
#
p_long = (Ether(src=self.pg0.remote_mac,
dst=self.pg0.local_mac) /
IP(src=self.pg0.remote_ip4,
dst=self.pg1.remote_ip4,
len=400) /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
rx = self.send_and_assert_no_replies(self.pg0, p_long * 65,
"too long")
#
# bad chksum - this is dropped
#
p_chksum = (Ether(src=self.pg0.remote_mac,
dst=self.pg0.local_mac) /
IP(src=self.pg0.remote_ip4,
dst=self.pg1.remote_ip4,
chksum=400) /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
rx = self.send_and_assert_no_replies(self.pg0, p_chksum * 65,
"bad checksum")
#
# bad version - this is dropped
#
p_ver = (Ether(src=self.pg0.remote_mac,
dst=self.pg0.local_mac) /
IP(src=self.pg0.remote_ip4,
dst=self.pg1.remote_ip4,
version=3) /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
rx = self.send_and_assert_no_replies(self.pg0, p_ver * 65,
"funky version")
#
# fragment offset 1 - this is dropped
#
p_frag = (Ether(src=self.pg0.remote_mac,
dst=self.pg0.local_mac) /
IP(src=self.pg0.remote_ip4,
dst=self.pg1.remote_ip4,
frag=1) /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
rx = self.send_and_assert_no_replies(self.pg0, p_frag * 65,
"frag offset")
#
# TTL expired packet
#
p_ttl = (Ether(src=self.pg0.remote_mac,
dst=self.pg0.local_mac) /
IP(src=self.pg0.remote_ip4,
dst=self.pg1.remote_ip4,
ttl=1) /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
rx = self.send_and_expect(self.pg0, p_ttl * 65, self.pg0)
rx = rx[0]
icmp = rx[ICMP]
self.assertEqual(icmptypes[icmp.type], "time-exceeded")
self.assertEqual(icmpcodes[icmp.type][icmp.code],
"ttl-zero-during-transit")
self.assertEqual(icmp.src, self.pg0.remote_ip4)
self.assertEqual(icmp.dst, self.pg1.remote_ip4)
self.logger.error(self.vapi.cli("sh error"))
if __name__ == '__main__':
unittest.main(testRunner=VppTestRunner)

View File

@ -16,7 +16,8 @@ from scapy.layers.l2 import Ether, Dot1Q
from scapy.layers.inet6 import IPv6, UDP, TCP, ICMPv6ND_NS, ICMPv6ND_RS, \
ICMPv6ND_RA, ICMPv6NDOptSrcLLAddr, getmacbyip6, ICMPv6MRD_Solicitation, \
ICMPv6NDOptMTU, ICMPv6NDOptSrcLLAddr, ICMPv6NDOptPrefixInfo, \
ICMPv6ND_NA, ICMPv6NDOptDstLLAddr, ICMPv6DestUnreach, icmp6types
ICMPv6ND_NA, ICMPv6NDOptDstLLAddr, ICMPv6DestUnreach, icmp6types, \
ICMPv6TimeExceeded
from util import ppp
from scapy.utils6 import in6_getnsma, in6_getnsmac, in6_ptop, in6_islladdr, \
@ -1618,5 +1619,80 @@ class TestIP6Punt(VppTestCase):
is_add=0,
is_ip6=1)
class TestIP6Input(VppTestCase):
""" IPv6 Input Exceptions """
def setUp(self):
super(TestIP6Input, self).setUp()
self.create_pg_interfaces(range(2))
for i in self.pg_interfaces:
i.admin_up()
i.config_ip6()
i.resolve_ndp()
def tearDown(self):
super(TestIP6Input, self).tearDown()
for i in self.pg_interfaces:
i.unconfig_ip6()
i.admin_down()
def send_and_expect(self, input, pkts, output):
self.vapi.cli("clear trace")
input.add_stream(pkts)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = output.get_capture(len(pkts))
return rx
def send_and_assert_no_replies(self, intf, pkts, remark):
self.vapi.cli("clear trace")
intf.add_stream(pkts)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
for i in self.pg_interfaces:
i.get_capture(0)
i.assert_nothing_captured(remark=remark)
def test_ip_input(self):
""" IP6 Input Exceptions """
#
# bad version - this is dropped
#
p_version = (Ether(src=self.pg0.remote_mac,
dst=self.pg0.local_mac) /
IPv6(src=self.pg0.remote_ip6,
dst=self.pg1.remote_ip6,
version=3) /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
self.send_and_assert_no_replies(self.pg0, p_version * 65,
"funky version")
#
# hop limit - IMCP replies
#
p_version = (Ether(src=self.pg0.remote_mac,
dst=self.pg0.local_mac) /
IPv6(src=self.pg0.remote_ip6,
dst=self.pg1.remote_ip6,
hlim=1) /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
rx = self.send_and_expect(self.pg0, p_version * 65, self.pg0)
rx = rx[0]
icmp = rx[ICMPv6TimeExceeded]
self.assertEqual(icmp.type, 3)
# 0: "hop limit exceeded in transit",
self.assertEqual(icmp.code, 0)
self.logger.error(self.vapi.cli("sh error"))
if __name__ == '__main__':
unittest.main(testRunner=VppTestRunner)

View File

@ -106,6 +106,7 @@ class TestMPLS(VppTestCase):
ping=0,
ip_itf=None,
dst_ip=None,
chksum=None,
n=257):
self.reset_packet_infos()
pkts = []
@ -133,6 +134,8 @@ class TestMPLS(VppTestCase):
dst=ip_itf.local_ip4) /
ICMP())
if chksum:
p[IP].chksum = chksum
info.data = p.copy()
pkts.append(p)
return pkts
@ -152,7 +155,7 @@ class TestMPLS(VppTestCase):
return pkts
def create_stream_labelled_ip6(self, src_if, mpls_label, mpls_ttl,
dst_ip=None):
dst_ip=None, hlim=64):
if dst_ip is None:
dst_ip = src_if.remote_ip6
self.reset_packet_infos()
@ -162,7 +165,7 @@ class TestMPLS(VppTestCase):
payload = self.info_to_payload(info)
p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
MPLS(label=mpls_label, ttl=mpls_ttl) /
IPv6(src=src_if.remote_ip6, dst=dst_ip) /
IPv6(src=src_if.remote_ip6, dst=dst_ip, hlim=hlim) /
UDP(sport=1234, dport=1234) /
Raw(payload))
info.data = p.copy()
@ -1025,6 +1028,14 @@ class TestMPLS(VppTestCase):
rx = self.pg1.get_capture(257)
self.verify_capture_ip4(self.pg1, rx, tx)
#
# disposed packets have an invalid IPv4 checkusm
#
tx = self.create_stream_labelled_ip4(self.pg0, [34],
dst_ip="232.1.1.1", n=65,
chksum=1)
self.send_and_assert_no_replies(self.pg0, tx, "Invalid Checksum")
#
# set the RPF-ID of the enrtry to not match the input packet's
#
@ -1032,6 +1043,7 @@ class TestMPLS(VppTestCase):
tx = self.create_stream_labelled_ip4(self.pg0, [34],
dst_ip="232.1.1.1")
self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop 56")
self.logger.error(self.vapi.cli("sh error"))
def test_mcast_ip6_tail(self):
""" MPLS IPv6 Multicast Tail """
@ -1091,6 +1103,13 @@ class TestMPLS(VppTestCase):
rx = self.pg1.get_capture(257)
self.verify_capture_ip6(self.pg1, rx, tx)
#
# disposed packets have hop-limit = 1
#
tx = self.create_stream_labelled_ip6(self.pg0, [34], 255,
dst_ip="ff01::1", hlim=1)
self.send_and_assert_no_replies(self.pg0, tx, "Hop Limt Expired")
#
# set the RPF-ID of the enrtry to not match the input packet's
#