b9b885cc58cecceb338e3b8e0091c29545ea470f
[vpp.git] / src / vnet / gso / hdr_offset_parser.h
1 /*
2  * Copyright (c) 2019 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #ifndef included_hdr_offset_parser_h
17 #define included_hdr_offset_parser_h
18
19 #include <vnet/ethernet/ethernet.h>
20 #include <vnet/ip/ip4_packet.h>
21 #include <vnet/ip/ip6_packet.h>
22 #include <vnet/udp/udp_local.h>
23 #include <vnet/udp/udp_packet.h>
24 #include <vnet/vnet.h>
25 #include <vnet/vxlan/vxlan_packet.h>
26
27 #define foreach_gho_flag        \
28   _( 0, IP4)                    \
29   _( 1, IP6)                    \
30   _( 2, TCP)                    \
31   _( 3, UDP)                    \
32   _( 4, OUTER_IP4)              \
33   _( 5, OUTER_IP6)              \
34   _( 6, OUTER_TCP)              \
35   _( 7, OUTER_UDP)              \
36   _( 8, VXLAN_TUNNEL)           \
37   _( 9, GRE_TUNNEL)             \
38   _( 10, IPIP_TUNNEL)           \
39   _( 11, IPIP6_TUNNEL)          \
40   _( 12, GENEVE_TUNNEL)
41
42 typedef enum gho_flag_t_
43 {
44 #define _(bit, name) GHO_F_##name  = (1 << bit),
45   foreach_gho_flag
46 #undef _
47 } gho_flag_t;
48
49 #define GHO_F_TUNNEL (GHO_F_VXLAN_TUNNEL  |  \
50                       GHO_F_GENEVE_TUNNEL |  \
51                       GHO_F_IPIP_TUNNEL   |  \
52                       GHO_F_IPIP6_TUNNEL  |  \
53                       GHO_F_GRE_TUNNEL)
54
55 #define GHO_F_OUTER_HDR (GHO_F_OUTER_IP4 | \
56                          GHO_F_OUTER_IP6 | \
57                          GHO_F_OUTER_TCP | \
58                          GHO_F_OUTER_UDP)
59
60 #define GHO_F_INNER_HDR (GHO_F_IP4 | \
61                          GHO_F_IP6 | \
62                          GHO_F_UDP | \
63                          GHO_F_TCP)
64
65 typedef struct
66 {
67   i16 outer_l2_hdr_offset;
68   i16 outer_l3_hdr_offset;
69   i16 outer_l4_hdr_offset;
70   u16 outer_l4_hdr_sz;
71   u16 outer_hdr_sz;
72   i16 l2_hdr_offset;
73   i16 l3_hdr_offset;
74   i16 l4_hdr_offset;
75   u16 l4_hdr_sz;
76   u16 hdr_sz;
77   gho_flag_t gho_flags;
78 } generic_header_offset_t;
79
80 static_always_inline u8 *
81 format_generic_header_offset (u8 * s, va_list * args)
82 {
83   generic_header_offset_t *gho = va_arg (*args, generic_header_offset_t *);
84
85   if (gho->gho_flags & GHO_F_TUNNEL)
86     {
87       if (gho->gho_flags & GHO_F_VXLAN_TUNNEL)
88         s = format (s, "vxlan-tunnel ");
89       else if (gho->gho_flags & GHO_F_IPIP_TUNNEL)
90         s = format (s, "ipip-tunnel ");
91       else if (gho->gho_flags & GHO_F_GRE_TUNNEL)
92         s = format (s, "gre-tunnel ");
93       else if (gho->gho_flags & GHO_F_GENEVE_TUNNEL)
94         s = format (s, "geneve-tunnel ");
95
96       if (gho->gho_flags & GHO_F_OUTER_IP4)
97         s = format (s, "outer-ipv4 ");
98       else if (gho->gho_flags & GHO_F_OUTER_IP6)
99         s = format (s, "outer-ipv6 ");
100
101       if (gho->gho_flags & GHO_F_OUTER_UDP)
102         s = format (s, "outer-udp ");
103       else if (gho->gho_flags & GHO_F_OUTER_TCP)
104         s = format (s, "outer-tcp ");
105
106       s = format (s, "outer-hdr-sz %u outer-l2-hdr-offset %d "
107                   "outer-l3-hdr-offset %d outer-l4-hdr-offset %d "
108                   "outer-l4-hdr-sz %u\n\t",
109                   gho->outer_hdr_sz, gho->outer_l2_hdr_offset,
110                   gho->outer_l3_hdr_offset, gho->outer_l4_hdr_offset,
111                   gho->outer_l4_hdr_sz);
112     }
113
114   if (gho->gho_flags & GHO_F_IP4)
115     s = format (s, "ipv4 ");
116   else if (gho->gho_flags & GHO_F_IP6)
117     s = format (s, "ipv6 ");
118
119   if (gho->gho_flags & GHO_F_TCP)
120     s = format (s, "tcp ");
121   else if (gho->gho_flags & GHO_F_UDP)
122     s = format (s, "udp ");
123
124   s = format (s, "hdr-sz %u l2-hdr-offset %d "
125               "l3-hdr-offset %d l4-hdr-offset %d "
126               "l4-hdr-sz %u",
127               gho->hdr_sz, gho->l2_hdr_offset, gho->l3_hdr_offset,
128               gho->l4_hdr_offset, gho->l4_hdr_sz);
129
130   return s;
131 }
132
133 static_always_inline void
134 vnet_get_inner_header (vlib_buffer_t * b0, generic_header_offset_t * gho)
135 {
136   if ((gho->gho_flags & GHO_F_TUNNEL)
137       && (gho->gho_flags & GHO_F_OUTER_HDR)
138       && (b0->current_data == gho->outer_l2_hdr_offset))
139     vlib_buffer_advance (b0, gho->outer_hdr_sz);
140 }
141
142 static_always_inline void
143 vnet_get_outer_header (vlib_buffer_t * b0, generic_header_offset_t * gho)
144 {
145   if ((gho->gho_flags & GHO_F_TUNNEL)
146       && (gho->gho_flags & GHO_F_OUTER_HDR)
147       && (b0->current_data == gho->l2_hdr_offset))
148     vlib_buffer_advance (b0, -gho->outer_hdr_sz);
149 }
150
151 static_always_inline void
152 vnet_geneve_inner_header_parser_inline (vlib_buffer_t * b0,
153                                         generic_header_offset_t * gho)
154 {
155   /* not supported yet */
156   if ((gho->gho_flags & GHO_F_GENEVE_TUNNEL) == 0)
157     return;
158 }
159
160 static_always_inline void
161 vnet_gre_inner_header_parser_inline (vlib_buffer_t * b0,
162                                      generic_header_offset_t * gho)
163 {
164   /* not supported yet */
165   if ((gho->gho_flags & GHO_F_GRE_TUNNEL) == 0)
166     return;
167 }
168
169 static_always_inline void
170 vnet_ipip_inner_header_parser_inline (vlib_buffer_t * b0,
171                                       generic_header_offset_t * gho)
172 {
173   if ((gho->gho_flags & (GHO_F_IPIP_TUNNEL | GHO_F_IPIP6_TUNNEL)) == 0)
174     return;
175
176   u8 l4_proto = 0;
177   u8 l4_hdr_sz = 0;
178
179   gho->outer_l2_hdr_offset = gho->l2_hdr_offset;
180   gho->outer_l3_hdr_offset = gho->l3_hdr_offset;
181   gho->outer_l4_hdr_offset = gho->l4_hdr_offset;
182   gho->outer_l4_hdr_sz = gho->l4_hdr_sz;
183   gho->outer_hdr_sz = gho->hdr_sz;
184
185   gho->l2_hdr_offset = 0;
186   gho->l3_hdr_offset = 0;
187   gho->l4_hdr_offset = 0;
188   gho->l4_hdr_sz = 0;
189   gho->hdr_sz = 0;
190
191   if (gho->gho_flags & GHO_F_IP4)
192     {
193       gho->gho_flags |= GHO_F_OUTER_IP4;
194     }
195   else if (gho->gho_flags & GHO_F_IP6)
196     {
197       gho->gho_flags |= GHO_F_OUTER_IP6;
198     }
199
200   gho->gho_flags &= ~GHO_F_INNER_HDR;
201
202   vnet_get_inner_header (b0, gho);
203
204   gho->l2_hdr_offset = b0->current_data;
205   gho->l3_hdr_offset = 0;
206
207   if (PREDICT_TRUE (gho->gho_flags & GHO_F_IPIP_TUNNEL))
208     {
209       ip4_header_t *ip4 = (ip4_header_t *) vlib_buffer_get_current (b0);
210       gho->l4_hdr_offset = ip4_header_bytes (ip4);
211       l4_proto = ip4->protocol;
212       gho->gho_flags |= GHO_F_IP4;
213     }
214   else if (PREDICT_TRUE (gho->gho_flags & GHO_F_IPIP6_TUNNEL))
215     {
216       ip6_header_t *ip6 = (ip6_header_t *) vlib_buffer_get_current (b0);
217       /* FIXME IPv6 EH traversal */
218       gho->l4_hdr_offset = sizeof (ip6_header_t);
219       l4_proto = ip6->protocol;
220       gho->gho_flags |= GHO_F_IP6;
221     }
222   if (l4_proto == IP_PROTOCOL_TCP)
223     {
224       tcp_header_t *tcp = (tcp_header_t *) (vlib_buffer_get_current (b0) +
225                                             gho->l4_hdr_offset);
226       l4_hdr_sz = tcp_header_bytes (tcp);
227
228       gho->gho_flags |= GHO_F_TCP;
229
230     }
231   else if (l4_proto == IP_PROTOCOL_UDP)
232     {
233       udp_header_t *udp = (udp_header_t *) (vlib_buffer_get_current (b0) +
234                                             gho->l4_hdr_offset);
235       l4_hdr_sz = sizeof (*udp);
236
237       gho->gho_flags |= GHO_F_UDP;
238     }
239
240   gho->l4_hdr_sz = l4_hdr_sz;
241   gho->hdr_sz += gho->l4_hdr_offset + l4_hdr_sz;
242
243   vnet_get_outer_header (b0, gho);
244 }
245
246 static_always_inline void
247 vnet_vxlan_inner_header_parser_inline (vlib_buffer_t * b0,
248                                        generic_header_offset_t * gho)
249 {
250   u8 l4_proto = 0;
251   u8 l4_hdr_sz = 0;
252
253   if ((gho->gho_flags & GHO_F_VXLAN_TUNNEL) == 0)
254     return;
255
256   gho->outer_l2_hdr_offset = gho->l2_hdr_offset;
257   gho->outer_l3_hdr_offset = gho->l3_hdr_offset;
258   gho->outer_l4_hdr_offset = gho->l4_hdr_offset;
259   gho->outer_l4_hdr_sz = gho->l4_hdr_sz;
260   gho->outer_hdr_sz = gho->hdr_sz;
261
262   gho->l2_hdr_offset = 0;
263   gho->l3_hdr_offset = 0;
264   gho->l4_hdr_offset = 0;
265   gho->l4_hdr_sz = 0;
266   gho->hdr_sz = 0;
267
268   if (gho->gho_flags & GHO_F_IP4)
269     {
270       gho->gho_flags |= GHO_F_OUTER_IP4;
271     }
272   else if (gho->gho_flags & GHO_F_IP6)
273     {
274       gho->gho_flags |= GHO_F_OUTER_IP6;
275     }
276
277   if (gho->gho_flags & GHO_F_UDP)
278     {
279       gho->gho_flags |= GHO_F_OUTER_UDP;
280     }
281
282   gho->gho_flags &= ~GHO_F_INNER_HDR;
283
284   vnet_get_inner_header (b0, gho);
285
286   gho->l2_hdr_offset = b0->current_data;
287
288   ethernet_header_t *eh = (ethernet_header_t *) vlib_buffer_get_current (b0);
289   u16 ethertype = clib_net_to_host_u16 (eh->type);
290   u16 l2hdr_sz = sizeof (ethernet_header_t);
291
292   if (ethernet_frame_is_tagged (ethertype))
293     {
294       ethernet_vlan_header_t *vlan = (ethernet_vlan_header_t *) (eh + 1);
295
296       ethertype = clib_net_to_host_u16 (vlan->type);
297       l2hdr_sz += sizeof (*vlan);
298       if (ethertype == ETHERNET_TYPE_VLAN)
299         {
300           vlan++;
301           ethertype = clib_net_to_host_u16 (vlan->type);
302           l2hdr_sz += sizeof (*vlan);
303         }
304     }
305
306   gho->l3_hdr_offset = l2hdr_sz;
307
308   if (PREDICT_TRUE (ethertype == ETHERNET_TYPE_IP4))
309     {
310       ip4_header_t *ip4 =
311         (ip4_header_t *) (vlib_buffer_get_current (b0) + gho->l3_hdr_offset);
312       gho->l4_hdr_offset = gho->l3_hdr_offset + ip4_header_bytes (ip4);
313       l4_proto = ip4->protocol;
314       gho->gho_flags |= GHO_F_IP4;
315     }
316   else if (PREDICT_TRUE (ethertype == ETHERNET_TYPE_IP6))
317     {
318       ip6_header_t *ip6 =
319         (ip6_header_t *) (vlib_buffer_get_current (b0) + gho->l3_hdr_offset);
320       /* FIXME IPv6 EH traversal */
321       gho->l4_hdr_offset = gho->l3_hdr_offset + sizeof (ip6_header_t);
322       l4_proto = ip6->protocol;
323       gho->gho_flags |= GHO_F_IP6;
324     }
325   if (l4_proto == IP_PROTOCOL_TCP)
326     {
327       tcp_header_t *tcp = (tcp_header_t *) (vlib_buffer_get_current (b0) +
328                                             gho->l4_hdr_offset);
329       l4_hdr_sz = tcp_header_bytes (tcp);
330
331       gho->gho_flags |= GHO_F_TCP;
332
333     }
334   else if (l4_proto == IP_PROTOCOL_UDP)
335     {
336       udp_header_t *udp = (udp_header_t *) (vlib_buffer_get_current (b0) +
337                                             gho->l4_hdr_offset);
338       l4_hdr_sz = sizeof (*udp);
339
340       gho->gho_flags |= GHO_F_UDP;
341     }
342
343   gho->l4_hdr_sz = l4_hdr_sz;
344   gho->hdr_sz += gho->l4_hdr_offset + l4_hdr_sz;
345
346   vnet_get_outer_header (b0, gho);
347 }
348
349 static_always_inline void
350 vnet_generic_inner_header_parser_inline (vlib_buffer_t * b0,
351                                          generic_header_offset_t * gho)
352 {
353
354   if (gho->gho_flags & GHO_F_VXLAN_TUNNEL)
355     vnet_vxlan_inner_header_parser_inline (b0, gho);
356   else if (gho->gho_flags & (GHO_F_IPIP_TUNNEL | GHO_F_IPIP6_TUNNEL))
357     vnet_ipip_inner_header_parser_inline (b0, gho);
358   else if (gho->gho_flags & GHO_F_GRE_TUNNEL)
359     vnet_gre_inner_header_parser_inline (b0, gho);
360   else if (gho->gho_flags & GHO_F_GENEVE_TUNNEL)
361     vnet_geneve_inner_header_parser_inline (b0, gho);
362 }
363
364 static_always_inline void
365 vnet_generic_outer_header_parser_inline (vlib_buffer_t * b0,
366                                          generic_header_offset_t * gho,
367                                          int is_l2, int is_ip4, int is_ip6)
368 {
369   u8 l4_proto = 0;
370   u8 l4_hdr_sz = 0;
371   u16 ethertype = 0;
372   u16 l2hdr_sz = 0;
373
374   ASSERT (!(is_ip4 && is_ip6));
375
376   if (is_l2)
377     {
378       ethernet_header_t *eh =
379         (ethernet_header_t *) vlib_buffer_get_current (b0);
380       ethertype = clib_net_to_host_u16 (eh->type);
381       l2hdr_sz = sizeof (ethernet_header_t);
382
383       if (ethernet_frame_is_tagged (ethertype))
384         {
385           ethernet_vlan_header_t *vlan = (ethernet_vlan_header_t *) (eh + 1);
386
387           ethertype = clib_net_to_host_u16 (vlan->type);
388           l2hdr_sz += sizeof (*vlan);
389           if (ethertype == ETHERNET_TYPE_VLAN)
390             {
391               vlan++;
392               ethertype = clib_net_to_host_u16 (vlan->type);
393               l2hdr_sz += sizeof (*vlan);
394             }
395         }
396     }
397   else
398     l2hdr_sz = vnet_buffer (b0)->ip.save_rewrite_length;
399
400   gho->l2_hdr_offset = b0->current_data;
401   gho->l3_hdr_offset = l2hdr_sz;
402
403   if (PREDICT_TRUE (is_ip4))
404     {
405       ip4_header_t *ip4 =
406         (ip4_header_t *) (vlib_buffer_get_current (b0) + l2hdr_sz);
407       gho->l4_hdr_offset = l2hdr_sz + ip4_header_bytes (ip4);
408       l4_proto = ip4->protocol;
409       gho->gho_flags |= GHO_F_IP4;
410     }
411   else if (PREDICT_TRUE (is_ip6))
412     {
413       ip6_header_t *ip6 =
414         (ip6_header_t *) (vlib_buffer_get_current (b0) + l2hdr_sz);
415       /* FIXME IPv6 EH traversal */
416       gho->l4_hdr_offset = l2hdr_sz + sizeof (ip6_header_t);
417       l4_proto = ip6->protocol;
418       gho->gho_flags |= GHO_F_IP6;
419     }
420   if (l4_proto == IP_PROTOCOL_TCP)
421     {
422       tcp_header_t *tcp = (tcp_header_t *) (vlib_buffer_get_current (b0) +
423                                             gho->l4_hdr_offset);
424       l4_hdr_sz = tcp_header_bytes (tcp);
425
426       gho->gho_flags |= GHO_F_TCP;
427     }
428   else if (l4_proto == IP_PROTOCOL_UDP)
429     {
430       udp_header_t *udp = (udp_header_t *) (vlib_buffer_get_current (b0) +
431                                             gho->l4_hdr_offset);
432       l4_hdr_sz = sizeof (*udp);
433
434       gho->gho_flags |= GHO_F_UDP;
435
436       if (UDP_DST_PORT_vxlan == clib_net_to_host_u16 (udp->dst_port))
437         {
438           gho->gho_flags |= GHO_F_VXLAN_TUNNEL;
439           gho->hdr_sz += sizeof (vxlan_header_t);
440         }
441       else if (UDP_DST_PORT_geneve == clib_net_to_host_u16 (udp->dst_port))
442         {
443           gho->gho_flags |= GHO_F_GENEVE_TUNNEL;
444         }
445     }
446   else if (l4_proto == IP_PROTOCOL_IP_IN_IP)
447     {
448       l4_hdr_sz = 0;
449       gho->gho_flags |= GHO_F_IPIP_TUNNEL;
450     }
451   else if (l4_proto == IP_PROTOCOL_IPV6)
452     {
453       l4_hdr_sz = 0;
454       gho->gho_flags |= GHO_F_IPIP6_TUNNEL;
455     }
456   else if (l4_proto == IP_PROTOCOL_GRE)
457     {
458       l4_hdr_sz = 0;
459       gho->gho_flags |= GHO_F_GRE_TUNNEL;
460     }
461
462   gho->l4_hdr_sz = l4_hdr_sz;
463   gho->hdr_sz += gho->l4_hdr_offset + l4_hdr_sz;
464 }
465
466 static_always_inline void
467 vnet_generic_header_offset_parser (vlib_buffer_t * b0,
468                                    generic_header_offset_t * gho, int is_l2,
469                                    int is_ip4, int is_ip6)
470 {
471   vnet_generic_outer_header_parser_inline (b0, gho, is_l2, is_ip4, is_ip6);
472
473   if (gho->gho_flags & GHO_F_TUNNEL)
474     {
475       vnet_generic_inner_header_parser_inline (b0, gho);
476     }
477 }
478
479 #endif /* included_hdr_offset_parser_h */
480
481 /*
482  * fd.io coding-style-patch-verification: ON
483  *
484  * Local Variables:
485  * eval: (c-set-style "gnu")
486  * End:
487  */