/* * Copyright (c) 2019 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. */ #ifndef included_hdr_offset_parser_h #define included_hdr_offset_parser_h #include <vnet/ethernet/ethernet.h> #include <vnet/ip/ip4_packet.h> #include <vnet/ip/ip6_packet.h> #include <vnet/udp/udp_local.h> #include <vnet/udp/udp_packet.h> #include <vnet/vnet.h> #include <vnet/vxlan/vxlan_packet.h> #define foreach_gho_flag \ _( 0, IP4) \ _( 1, IP6) \ _( 2, TCP) \ _( 3, UDP) \ _( 4, OUTER_IP4) \ _( 5, OUTER_IP6) \ _( 6, OUTER_TCP) \ _( 7, OUTER_UDP) \ _( 8, VXLAN_TUNNEL) \ _( 9, GRE_TUNNEL) \ _( 10, IPIP_TUNNEL) \ _( 11, IPIP6_TUNNEL) \ _( 12, GENEVE_TUNNEL) typedef enum gho_flag_t_ { #define _(bit, name) GHO_F_##name = (1 << bit), foreach_gho_flag #undef _ } gho_flag_t; #define GHO_F_TUNNEL (GHO_F_VXLAN_TUNNEL | \ GHO_F_GENEVE_TUNNEL | \ GHO_F_IPIP_TUNNEL | \ GHO_F_IPIP6_TUNNEL | \ GHO_F_GRE_TUNNEL) #define GHO_F_OUTER_HDR (GHO_F_OUTER_IP4 | \ GHO_F_OUTER_IP6 | \ GHO_F_OUTER_TCP | \ GHO_F_OUTER_UDP) #define GHO_F_INNER_HDR (GHO_F_IP4 | \ GHO_F_IP6 | \ GHO_F_UDP | \ GHO_F_TCP) typedef struct { i16 outer_l2_hdr_offset; i16 outer_l3_hdr_offset; i16 outer_l4_hdr_offset; u16 outer_l4_hdr_sz; u16 outer_hdr_sz; i16 l2_hdr_offset; i16 l3_hdr_offset; i16 l4_hdr_offset; u16 l4_hdr_sz; u16 hdr_sz; gho_flag_t gho_flags; } generic_header_offset_t; static_always_inline u8 * format_generic_header_offset (u8 * s, va_list * args) { generic_header_offset_t *gho = va_arg (*args, generic_header_offset_t *); if (gho->gho_flags & GHO_F_TUNNEL) { if (gho->gho_flags & GHO_F_VXLAN_TUNNEL) s = format (s, "vxlan-tunnel "); else if (gho->gho_flags & GHO_F_IPIP_TUNNEL) s = format (s, "ipip-tunnel "); else if (gho->gho_flags & GHO_F_GRE_TUNNEL) s = format (s, "gre-tunnel "); else if (gho->gho_flags & GHO_F_GENEVE_TUNNEL) s = format (s, "geneve-tunnel "); if (gho->gho_flags & GHO_F_OUTER_IP4) s = format (s, "outer-ipv4 "); else if (gho->gho_flags & GHO_F_OUTER_IP6) s = format (s, "outer-ipv6 "); if (gho->gho_flags & GHO_F_OUTER_UDP) s = format (s, "outer-udp "); else if (gho->gho_flags & GHO_F_OUTER_TCP) s = format (s, "outer-tcp "); s = format (s, "outer-hdr-sz %u outer-l2-hdr-offset %d " "outer-l3-hdr-offset %d outer-l4-hdr-offset %d " "outer-l4-hdr-sz %u\n\t", gho->outer_hdr_sz, gho->outer_l2_hdr_offset, gho->outer_l3_hdr_offset, gho->outer_l4_hdr_offset, gho->outer_l4_hdr_sz); } if (gho->gho_flags & GHO_F_IP4) s = format (s, "ipv4 "); else if (gho->gho_flags & GHO_F_IP6) s = format (s, "ipv6 "); if (gho->gho_flags & GHO_F_TCP) s = format (s, "tcp "); else if (gho->gho_flags & GHO_F_UDP) s = format (s, "udp "); s = format (s, "hdr-sz %u l2-hdr-offset %d " "l3-hdr-offset %d l4-hdr-offset %d " "l4-hdr-sz %u", gho->hdr_sz, gho->l2_hdr_offset, gho->l3_hdr_offset, gho->l4_hdr_offset, gho->l4_hdr_sz); return s; } static_always_inline void vnet_get_inner_header (vlib_buffer_t * b0, generic_header_offset_t * gho) { if ((gho->gho_flags & GHO_F_TUNNEL) && (gho->gho_flags & GHO_F_OUTER_HDR) && (b0->current_data == gho->outer_l2_hdr_offset)) vlib_buffer_advance (b0, gho->outer_hdr_sz); } static_always_inline void vnet_get_outer_header (vlib_buffer_t * b0, generic_header_offset_t * gho) { if ((gho->gho_flags & GHO_F_TUNNEL) && (gho->gho_flags & GHO_F_OUTER_HDR) && (b0->current_data == gho->l2_hdr_offset)) vlib_buffer_advance (b0, -gho->outer_hdr_sz); } static_always_inline void vnet_geneve_inner_header_parser_inline (vlib_buffer_t * b0, generic_header_offset_t * gho) { /* not supported yet */ if ((gho->gho_flags & GHO_F_GENEVE_TUNNEL) == 0) return; ASSERT (0); } static_always_inline void vnet_gre_inner_header_parser_inline (vlib_buffer_t * b0, generic_header_offset_t * gho) { /* not supported yet */ if ((gho->gho_flags & GHO_F_GRE_TUNNEL) == 0) return; ASSERT (0); } static_always_inline void vnet_ipip_inner_header_parser_inline (vlib_buffer_t * b0, generic_header_offset_t * gho) { if ((gho->gho_flags & (GHO_F_IPIP_TUNNEL | GHO_F_IPIP6_TUNNEL)) == 0) return; u8 l4_proto = 0; u8 l4_hdr_sz = 0; gho->outer_l2_hdr_offset = gho->l2_hdr_offset; gho->outer_l3_hdr_offset = gho->l3_hdr_offset; gho->outer_l4_hdr_offset = gho->l4_hdr_offset; gho->outer_l4_hdr_sz = gho->l4_hdr_sz; gho->outer_hdr_sz = gho->hdr_sz; gho->l2_hdr_offset = 0; gho->l3_hdr_offset = 0; gho->l4_hdr_offset = 0; gho->l4_hdr_sz = 0; gho->hdr_sz = 0; if (gho->gho_flags & GHO_F_IP4) { gho->gho_flags |= GHO_F_OUTER_IP4; } else if (gho->gho_flags & GHO_F_IP6) { gho->gho_flags |= GHO_F_OUTER_IP6; } gho->gho_flags &= ~GHO_F_INNER_HDR; vnet_get_inner_header (b0, gho); gho->l2_hdr_offset = b0->current_data; gho->l3_hdr_offset = 0; if (PREDICT_TRUE (gho->gho_flags & GHO_F_IPIP_TUNNEL)) { ip4_header_t *ip4 = (ip4_header_t *) vlib_buffer_get_current (b0); gho->l4_hdr_offset = ip4_header_bytes (ip4); l4_proto = ip4->protocol; gho->gho_flags |= GHO_F_IP4; } else if (PREDICT_TRUE (gho->gho_flags & GHO_F_IPIP6_TUNNEL)) { ip6_header_t *ip6 = (ip6_header_t *) vlib_buffer_get_current (b0); /* FIXME IPv6 EH traversal */ gho->l4_hdr_offset = sizeof (ip6_header_t); l4_proto = ip6->protocol; gho->gho_flags |= GHO_F_IP6; } if (l4_proto == IP_PROTOCOL_TCP) { tcp_header_t *tcp = (tcp_header_t *) (vlib_buffer_get_current (b0) + gho->l4_hdr_offset); l4_hdr_sz = tcp_header_bytes (tcp); gho->gho_flags |= GHO_F_TCP; } else if (l4_proto == IP_PROTOCOL_UDP) { udp_header_t *udp = (udp_header_t *) (vlib_buffer_get_current (b0) + gho->l4_hdr_offset); l4_hdr_sz = sizeof (*udp); gho->gho_flags |= GHO_F_UDP; } gho->l4_hdr_sz = l4_hdr_sz; gho->hdr_sz += gho->l4_hdr_offset + l4_hdr_sz; vnet_get_outer_header (b0, gho); } static_always_inline void vnet_vxlan_inner_header_parser_inline (vlib_buffer_t * b0, generic_header_offset_t * gho) { u8 l4_proto = 0; u8 l4_hdr_sz = 0; if ((gho->gho_flags & GHO_F_VXLAN_TUNNEL) == 0) return; gho->outer_l2_hdr_offset = gho->l2_hdr_offset; gho->outer_l3_hdr_offset = gho->l3_hdr_offset; gho->outer_l4_hdr_offset = gho->l4_hdr_offset; gho->outer_l4_hdr_sz = gho->l4_hdr_sz; gho->outer_hdr_sz = gho->hdr_sz; gho->l2_hdr_offset = 0; gho->l3_hdr_offset = 0; gho->l4_hdr_offset = 0; gho->l4_hdr_sz = 0; gho->hdr_sz = 0; if (gho->gho_flags & GHO_F_IP4) { gho->gho_flags |= GHO_F_OUTER_IP4; } else if (gho->gho_flags & GHO_F_IP6) { gho->gho_flags |= GHO_F_OUTER_IP6; } if (gho->gho_flags & GHO_F_UDP) { gho->gho_flags |= GHO_F_OUTER_UDP; } gho->gho_flags &= ~GHO_F_INNER_HDR; vnet_get_inner_header (b0, gho); gho->l2_hdr_offset = b0->current_data; ethernet_header_t *eh = (ethernet_header_t *) vlib_buffer_get_current (b0); u16 ethertype = clib_net_to_host_u16 (eh->type); u16 l2hdr_sz = sizeof (ethernet_header_t); if (ethernet_frame_is_tagged (ethertype)) { ethernet_vlan_header_t *vlan = (ethernet_vlan_header_t *) (eh + 1); ethertype = clib_net_to_host_u16 (vlan->type); l2hdr_sz += sizeof (*vlan); if (ethertype == ETHERNET_TYPE_VLAN) { vlan++; ethertype = clib_net_to_host_u16 (vlan->type); l2hdr_sz += sizeof (*vlan); } } gho->l3_hdr_offset = l2hdr_sz; if (PREDICT_TRUE (ethertype == ETHERNET_TYPE_IP4)) { ip4_header_t *ip4 = (ip4_header_t *) (vlib_buffer_get_current (b0) + gho->l3_hdr_offset); gho->l4_hdr_offset = gho->l3_hdr_offset + ip4_header_bytes (ip4); l4_proto = ip4->protocol; gho->gho_flags |= GHO_F_IP4; } else if (PREDICT_TRUE (ethertype == ETHERNET_TYPE_IP6)) { ip6_header_t *ip6 = (ip6_header_t *) (vlib_buffer_get_current (b0) + gho->l3_hdr_offset); /* FIXME IPv6 EH traversal */ gho->l4_hdr_offset = gho->l3_hdr_offset + sizeof (ip6_header_t); l4_proto = ip6->protocol; gho->gho_flags |= GHO_F_IP6; } if (l4_proto == IP_PROTOCOL_TCP) { tcp_header_t *tcp = (tcp_header_t *) (vlib_buffer_get_current (b0) + gho->l4_hdr_offset); l4_hdr_sz = tcp_header_bytes (tcp); gho->gho_flags |= GHO_F_TCP; } else if (l4_proto == IP_PROTOCOL_UDP) { udp_header_t *udp = (udp_header_t *) (vlib_buffer_get_current (b0) + gho->l4_hdr_offset); l4_hdr_sz = sizeof (*udp); gho->gho_flags |= GHO_F_UDP; } gho->l4_hdr_sz = l4_hdr_sz; gho->hdr_sz += gho->l4_hdr_offset + l4_hdr_sz; vnet_get_outer_header (b0, gho); } static_always_inline void vnet_generic_inner_header_parser_inline (vlib_buffer_t * b0, generic_header_offset_t * gho) { if (gho->gho_flags & GHO_F_VXLAN_TUNNEL) vnet_vxlan_inner_header_parser_inline (b0, gho); else if (gho->gho_flags & (GHO_F_IPIP_TUNNEL | GHO_F_IPIP6_TUNNEL)) vnet_ipip_inner_header_parser_inline (b0, gho); else if (gho->gho_flags & GHO_F_GRE_TUNNEL) vnet_gre_inner_header_parser_inline (b0, gho); else if (gho->gho_flags & GHO_F_GENEVE_TUNNEL) vnet_geneve_inner_header_parser_inline (b0, gho); } static_always_inline void vnet_generic_outer_header_parser_inline (vlib_buffer_t * b0, generic_header_offset_t * gho, int is_l2, int is_ip4, int is_ip6) { u8 l4_proto = 0; u8 l4_hdr_sz = 0; u16 ethertype = 0; u16 l2hdr_sz = 0; ASSERT (!(is_ip4 && is_ip6)); if (is_l2) { ethernet_header_t *eh = (ethernet_header_t *) vlib_buffer_get_current (b0); ethertype = clib_net_to_host_u16 (eh->type); l2hdr_sz = sizeof (ethernet_header_t); if (ethernet_frame_is_tagged (ethertype)) { ethernet_vlan_header_t *vlan = (ethernet_vlan_header_t *) (eh + 1); ethertype = clib_net_to_host_u16 (vlan->type); l2hdr_sz += sizeof (*vlan); if (ethertype == ETHERNET_TYPE_VLAN) { vlan++; ethertype = clib_net_to_host_u16 (vlan->type); l2hdr_sz += sizeof (*vlan); } } } else l2hdr_sz = vnet_buffer (b0)->ip.save_rewrite_length; gho->l2_hdr_offset = b0->current_data; gho->l3_hdr_offset = l2hdr_sz; if (PREDICT_TRUE (is_ip4)) { ip4_header_t *ip4 = (ip4_header_t *) (vlib_buffer_get_current (b0) + l2hdr_sz); gho->l4_hdr_offset = l2hdr_sz + ip4_header_bytes (ip4); l4_proto = ip4->protocol; gho->gho_flags |= GHO_F_IP4; } else if (PREDICT_TRUE (is_ip6)) { ip6_header_t *ip6 = (ip6_header_t *) (vlib_buffer_get_current (b0) + l2hdr_sz); /* FIXME IPv6 EH traversal */ gho->l4_hdr_offset = l2hdr_sz + sizeof (ip6_header_t); l4_proto = ip6->protocol; gho->gho_flags |= GHO_F_IP6; } if (l4_proto == IP_PROTOCOL_TCP) { tcp_header_t *tcp = (tcp_header_t *) (vlib_buffer_get_current (b0) + gho->l4_hdr_offset); l4_hdr_sz = tcp_header_bytes (tcp); gho->gho_flags |= GHO_F_TCP; } else if (l4_proto == IP_PROTOCOL_UDP) { udp_header_t *udp = (udp_header_t *) (vlib_buffer_get_current (b0) + gho->l4_hdr_offset); l4_hdr_sz = sizeof (*udp); gho->gho_flags |= GHO_F_UDP; if (UDP_DST_PORT_vxlan == clib_net_to_host_u16 (udp->dst_port)) { gho->gho_flags |= GHO_F_VXLAN_TUNNEL; gho->hdr_sz += sizeof (vxlan_header_t); } else if (UDP_DST_PORT_geneve == clib_net_to_host_u16 (udp->dst_port)) { gho->gho_flags |= GHO_F_GENEVE_TUNNEL; } } else if (l4_proto == IP_PROTOCOL_IP_IN_IP) { l4_hdr_sz = 0; gho->gho_flags |= GHO_F_IPIP_TUNNEL; } else if (l4_proto == IP_PROTOCOL_IPV6) { l4_hdr_sz = 0; gho->gho_flags |= GHO_F_IPIP6_TUNNEL; } else if (l4_proto == IP_PROTOCOL_GRE) { l4_hdr_sz = 0; gho->gho_flags |= GHO_F_GRE_TUNNEL; } gho->l4_hdr_sz = l4_hdr_sz; gho->hdr_sz += gho->l4_hdr_offset + l4_hdr_sz; } static_always_inline void vnet_generic_header_offset_parser (vlib_buffer_t * b0, generic_header_offset_t * gho, int is_l2, int is_ip4, int is_ip6) { vnet_generic_outer_header_parser_inline (b0, gho, is_l2, is_ip4, is_ip6); if (gho->gho_flags & GHO_F_TUNNEL) { vnet_generic_inner_header_parser_inline (b0, gho); } } #endif /* included_hdr_offset_parser_h */ /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */