aboutsummaryrefslogtreecommitdiffstats
path: root/vnet/vnet/ip/ip6_forward.c
diff options
context:
space:
mode:
Diffstat (limited to 'vnet/vnet/ip/ip6_forward.c')
-rw-r--r--vnet/vnet/ip/ip6_forward.c155
1 files changed, 129 insertions, 26 deletions
diff --git a/vnet/vnet/ip/ip6_forward.c b/vnet/vnet/ip/ip6_forward.c
index a4ce65a6396..6b74b7c9135 100644
--- a/vnet/vnet/ip/ip6_forward.c
+++ b/vnet/vnet/ip/ip6_forward.c
@@ -1230,6 +1230,77 @@ u32 ip6_tcp_udp_icmp_validate_checksum (vlib_main_t * vm, vlib_buffer_t * p0)
return p0->flags;
}
+/* ip6_locate_header
+ *
+ * This function is to search for the header specified by the find_hdr number.
+ * 1. If the find_hdr < 0 then it finds and returns the protocol number and
+ * offset stored in *offset of the transport or ESP header in the chain if
+ * found.
+ * 2. If a header with find_hdr > 0 protocol number is found then the
+ * offset is stored in *offset and protocol number of the header is
+ * returned.
+ * 3. If find_hdr header is not found or packet is malformed or
+ * it is a non-first fragment -1 is returned.
+ */
+always_inline int ip6_locate_header (vlib_buffer_t *p0,
+ ip6_header_t *ip0,
+ int find_hdr,
+ u32 *offset)
+{
+ u8 next_proto = ip0->protocol;
+ u8 *next_header;
+ u8 done = 0;
+ u32 cur_offset;
+ u8 *temp_nxthdr = 0;
+ u32 exthdr_len = 0;
+
+ next_header = ip6_next_header(ip0);
+ cur_offset = sizeof(ip6_header_t);
+ while(1)
+ {
+ done = (next_proto == find_hdr);
+ if (PREDICT_FALSE(next_header >= (u8 *)vlib_buffer_get_current(p0) + p0->current_length))
+ {
+ //A malicious packet could set an extension header with a too big size
+ return(-1);
+ }
+ if (done)
+ break;
+ if ((!ip6_ext_hdr(next_proto)) || next_proto == IP_PROTOCOL_IP6_NONXT)
+ {
+ if (find_hdr < 0)
+ break;
+ return -1;
+ }
+ if (next_proto == IP_PROTOCOL_IPV6_FRAGMENTATION)
+ {
+ ip6_frag_hdr_t *frag_hdr = (ip6_frag_hdr_t *)next_header;
+ u16 frag_off = ip6_frag_hdr_offset(frag_hdr);
+ /* Non first fragment return -1 */
+ if (frag_off)
+ return(-1);
+ exthdr_len = sizeof(ip6_frag_hdr_t);
+ temp_nxthdr = next_header + exthdr_len;
+ }
+ else if (next_proto == IP_PROTOCOL_IPSEC_AH)
+ {
+ exthdr_len = ip6_ext_authhdr_len(((ip6_ext_header_t *)next_header));
+ temp_nxthdr = next_header + exthdr_len;
+ }
+ else
+ {
+ exthdr_len = ip6_ext_header_len(((ip6_ext_header_t *)next_header));
+ temp_nxthdr = next_header + exthdr_len;
+ }
+ next_proto = ((ip6_ext_header_t *)next_header)->next_hdr;
+ next_header = temp_nxthdr;
+ cur_offset += exthdr_len;
+ }
+
+ *offset = cur_offset;
+ return(next_proto);
+}
+
static uword
ip6_local (vlib_main_t * vm,
vlib_node_runtime_t * node,
@@ -1263,6 +1334,7 @@ ip6_local (vlib_main_t * vm,
i32 len_diff0, len_diff1;
u8 error0, type0, good_l4_checksum0;
u8 error1, type1, good_l4_checksum1;
+ u32 udp_offset0, udp_offset1;
pi0 = to_next[0] = from[0];
pi1 = to_next[1] = from[1];
@@ -1288,26 +1360,48 @@ ip6_local (vlib_main_t * vm,
good_l4_checksum0 = (flags0 & IP_BUFFER_L4_CHECKSUM_CORRECT) != 0;
good_l4_checksum1 = (flags1 & IP_BUFFER_L4_CHECKSUM_CORRECT) != 0;
+ len_diff0 = 0;
+ len_diff1 = 0;
- udp0 = ip6_next_header (ip0);
- udp1 = ip6_next_header (ip1);
-
- /* Don't verify UDP checksum for packets with explicit zero checksum. */
- good_l4_checksum0 |= type0 == IP_BUILTIN_PROTOCOL_UDP && udp0->checksum == 0;
- good_l4_checksum1 |= type1 == IP_BUILTIN_PROTOCOL_UDP && udp1->checksum == 0;
+ /* Skip HBH local processing */
+ if (PREDICT_FALSE (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
+ {
+ ip6_hop_by_hop_ext_t *ext_hdr = (ip6_hop_by_hop_ext_t *)ip6_next_header(ip0);
+ next0 = lm->local_next_by_ip_protocol[ext_hdr->next_hdr];
+ type0 = lm->builtin_protocol_by_ip_protocol[ext_hdr->next_hdr];
+ }
+ if (PREDICT_FALSE (ip1->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
+ {
+ ip6_hop_by_hop_ext_t *ext_hdr = (ip6_hop_by_hop_ext_t *)ip6_next_header(ip1);
+ next1 = lm->local_next_by_ip_protocol[ext_hdr->next_hdr];
+ type1 = lm->builtin_protocol_by_ip_protocol[ext_hdr->next_hdr];
+ }
+ if (PREDICT_TRUE(IP_PROTOCOL_UDP == ip6_locate_header(p0, ip0,
+ IP_PROTOCOL_UDP, &udp_offset0)))
+ {
+ udp0 = (udp_header_t *)((u8 *)ip0 + udp_offset0);
+ /* Don't verify UDP checksum for packets with explicit zero checksum. */
+ good_l4_checksum0 |= type0 == IP_BUILTIN_PROTOCOL_UDP && udp0->checksum == 0;
+ /* Verify UDP length. */
+ ip_len0 = clib_net_to_host_u16 (ip0->payload_length);
+ udp_len0 = clib_net_to_host_u16 (udp0->length);
+ len_diff0 = ip_len0 - udp_len0;
+ }
+ if (PREDICT_TRUE(IP_PROTOCOL_UDP == ip6_locate_header(p1, ip1,
+ IP_PROTOCOL_UDP, &udp_offset1)))
+ {
+ udp1 = (udp_header_t *)((u8 *)ip1 + udp_offset1);
+ /* Don't verify UDP checksum for packets with explicit zero checksum. */
+ good_l4_checksum1 |= type1 == IP_BUILTIN_PROTOCOL_UDP && udp1->checksum == 0;
+ /* Verify UDP length. */
+ ip_len1 = clib_net_to_host_u16 (ip1->payload_length);
+ udp_len1 = clib_net_to_host_u16 (udp1->length);
+ len_diff1 = ip_len1 - udp_len1;
+ }
good_l4_checksum0 |= type0 == IP_BUILTIN_PROTOCOL_UNKNOWN;
good_l4_checksum1 |= type1 == IP_BUILTIN_PROTOCOL_UNKNOWN;
- /* Verify UDP length. */
- ip_len0 = clib_net_to_host_u16 (ip0->payload_length);
- ip_len1 = clib_net_to_host_u16 (ip1->payload_length);
- udp_len0 = clib_net_to_host_u16 (udp0->length);
- udp_len1 = clib_net_to_host_u16 (udp1->length);
-
- len_diff0 = ip_len0 - udp_len0;
- len_diff1 = ip_len1 - udp_len1;
-
len_diff0 = type0 == IP_BUILTIN_PROTOCOL_UDP ? len_diff0 : 0;
len_diff1 = type1 == IP_BUILTIN_PROTOCOL_UDP ? len_diff1 : 0;
@@ -1382,6 +1476,7 @@ ip6_local (vlib_main_t * vm,
u32 pi0, ip_len0, udp_len0, flags0, next0;
i32 len_diff0;
u8 error0, type0, good_l4_checksum0;
+ u32 udp_offset0;
pi0 = to_next[0] = from[0];
from += 1;
@@ -1399,20 +1494,28 @@ ip6_local (vlib_main_t * vm,
flags0 = p0->flags;
good_l4_checksum0 = (flags0 & IP_BUFFER_L4_CHECKSUM_CORRECT) != 0;
+ len_diff0 = 0;
- udp0 = ip6_next_header (ip0);
-
- /* Don't verify UDP checksum for packets with explicit zero checksum. */
- good_l4_checksum0 |= type0 == IP_BUILTIN_PROTOCOL_UDP && udp0->checksum == 0;
+ /* Skip HBH local processing */
+ if (PREDICT_FALSE (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
+ {
+ ip6_hop_by_hop_ext_t *ext_hdr = (ip6_hop_by_hop_ext_t *)ip6_next_header(ip0);
+ next0 = lm->local_next_by_ip_protocol[ext_hdr->next_hdr];
+ type0 = lm->builtin_protocol_by_ip_protocol[ext_hdr->next_hdr];
+ }
+ if (PREDICT_TRUE(IP_PROTOCOL_UDP == ip6_locate_header(p0, ip0,
+ IP_PROTOCOL_UDP, &udp_offset0)))
+ {
+ udp0 = (udp_header_t *)((u8 *)ip0 + udp_offset0);
+ /* Don't verify UDP checksum for packets with explicit zero checksum. */
+ good_l4_checksum0 |= type0 == IP_BUILTIN_PROTOCOL_UDP && udp0->checksum == 0;
+ /* Verify UDP length. */
+ ip_len0 = clib_net_to_host_u16 (ip0->payload_length);
+ udp_len0 = clib_net_to_host_u16 (udp0->length);
+ len_diff0 = ip_len0 - udp_len0;
+ }
good_l4_checksum0 |= type0 == IP_BUILTIN_PROTOCOL_UNKNOWN;
-
- /* Verify UDP length. */
- ip_len0 = clib_net_to_host_u16 (ip0->payload_length);
- udp_len0 = clib_net_to_host_u16 (udp0->length);
-
- len_diff0 = ip_len0 - udp_len0;
-
len_diff0 = type0 == IP_BUILTIN_PROTOCOL_UDP ? len_diff0 : 0;
if (PREDICT_FALSE (type0 != IP_BUILTIN_PROTOCOL_UNKNOWN