c9464bdc06351a83cfd80a115210614e5cc19feb
[vpp.git] / src / vnet / gso / gro_func.h
1 /*
2  * Copyright (c) 2020 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_gro_func_h
17 #define included_gro_func_h
18
19 #include <vnet/ethernet/ethernet.h>
20 #include <vnet/gso/gro.h>
21 #include <vnet/gso/hdr_offset_parser.h>
22 #include <vnet/ip/ip4.h>
23 #include <vnet/ip/ip6.h>
24 #include <vnet/ip/ip6_inlines.h>
25 #include <vnet/udp/udp_packet.h>
26 #include <vnet/tcp/tcp_packet.h>
27 #include <vnet/vnet.h>
28
29 #define GRO_MIN_PACKET_SIZE    256
30 #define GRO_PADDED_PACKET_SIZE 64
31
32 static_always_inline u8
33 gro_is_bad_packet (vlib_buffer_t * b, u8 flags, i16 l234_sz)
34 {
35   if (((b->current_length - l234_sz) <= 0) ||
36       ((flags &= ~(TCP_FLAG_ACK | TCP_FLAG_PSH)) != 0))
37     return 1;
38   return 0;
39 }
40
41 static_always_inline void
42 gro_get_ip4_flow_from_packet (u32 * sw_if_index,
43                               ip4_header_t * ip4, tcp_header_t * tcp,
44                               gro_flow_key_t * flow_key, int is_l2)
45 {
46   flow_key->sw_if_index[VLIB_RX] = sw_if_index[VLIB_RX];
47   flow_key->sw_if_index[VLIB_TX] = sw_if_index[VLIB_TX];
48   ip46_address_set_ip4 (&flow_key->src_address, &ip4->src_address);
49   ip46_address_set_ip4 (&flow_key->dst_address, &ip4->dst_address);
50   flow_key->src_port = tcp->src_port;
51   flow_key->dst_port = tcp->dst_port;
52 }
53
54 static_always_inline void
55 gro_get_ip6_flow_from_packet (u32 * sw_if_index,
56                               ip6_header_t * ip6, tcp_header_t * tcp,
57                               gro_flow_key_t * flow_key, int is_l2)
58 {
59   flow_key->sw_if_index[VLIB_RX] = sw_if_index[VLIB_RX];
60   flow_key->sw_if_index[VLIB_TX] = sw_if_index[VLIB_TX];
61   ip46_address_set_ip6 (&flow_key->src_address, &ip6->src_address);
62   ip46_address_set_ip6 (&flow_key->dst_address, &ip6->dst_address);
63   flow_key->src_port = tcp->src_port;
64   flow_key->dst_port = tcp->dst_port;
65 }
66
67 static_always_inline u32
68 gro_is_ip4_or_ip6_packet (vlib_buffer_t *b0, u8 is_l2)
69 {
70   if (b0->flags & VNET_BUFFER_F_IS_IP4)
71     return VNET_BUFFER_F_IS_IP4;
72   if (b0->flags & VNET_BUFFER_F_IS_IP6)
73     return VNET_BUFFER_F_IS_IP6;
74   if (is_l2)
75     {
76       ethernet_header_t *eh =
77         (ethernet_header_t *) vlib_buffer_get_current (b0);
78       u16 ethertype = clib_net_to_host_u16 (eh->type);
79
80       if (ethernet_frame_is_tagged (ethertype))
81         {
82           ethernet_vlan_header_t *vlan = (ethernet_vlan_header_t *) (eh + 1);
83
84           ethertype = clib_net_to_host_u16 (vlan->type);
85           if (ethertype == ETHERNET_TYPE_VLAN)
86             {
87               vlan++;
88               ethertype = clib_net_to_host_u16 (vlan->type);
89             }
90         }
91       if (ethertype == ETHERNET_TYPE_IP4)
92         return VNET_BUFFER_F_IS_IP4;
93       if (ethertype == ETHERNET_TYPE_IP6)
94         return VNET_BUFFER_F_IS_IP6;
95     }
96   else
97     {
98       if ((((u8 *) vlib_buffer_get_current (b0))[0] & 0xf0) == 0x40)
99         return VNET_BUFFER_F_IS_IP4;
100       if ((((u8 *) vlib_buffer_get_current (b0))[0] & 0xf0) == 0x60)
101         return VNET_BUFFER_F_IS_IP6;
102     }
103
104   return 0;
105 }
106
107 typedef enum
108 {
109   GRO_PACKET_ACTION_NONE = 0,
110   GRO_PACKET_ACTION_ENQUEUE = 1,
111   GRO_PACKET_ACTION_FLUSH = 2,
112 } gro_packet_action_t;
113
114 static_always_inline gro_packet_action_t
115 gro_tcp_sequence_check (tcp_header_t * tcp0, tcp_header_t * tcp1,
116                         u32 payload_len0)
117 {
118   u32 next_tcp_seq0 = clib_net_to_host_u32 (tcp0->seq_number);
119   u32 next_tcp_seq1 = clib_net_to_host_u32 (tcp1->seq_number);
120
121   /* next packet, enqueue */
122   if (PREDICT_TRUE (next_tcp_seq0 + payload_len0 == next_tcp_seq1))
123     return GRO_PACKET_ACTION_ENQUEUE;
124   /* flush all packets */
125   else
126     return GRO_PACKET_ACTION_FLUSH;
127 }
128
129 static_always_inline void
130 gro_merge_buffers (vlib_main_t * vm, vlib_buffer_t * b0,
131                    vlib_buffer_t * b1, u32 bi1, u32 payload_len1,
132                    u16 l234_sz1)
133 {
134   vlib_buffer_t *pb = b0;
135
136   if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_NEXT_PRESENT) == 0))
137     b0->total_length_not_including_first_buffer = 0;
138
139   while (pb->flags & VLIB_BUFFER_NEXT_PRESENT)
140     pb = vlib_get_buffer (vm, pb->next_buffer);
141
142   vlib_buffer_advance (b1, l234_sz1);
143   pb->flags |= VLIB_BUFFER_NEXT_PRESENT;
144   pb->next_buffer = bi1;
145   b0->total_length_not_including_first_buffer += payload_len1;
146   b0->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
147 }
148
149 static_always_inline u32
150 gro_validate_checksum (vlib_main_t * vm, vlib_buffer_t * b0,
151                        generic_header_offset_t * gho0, int is_ip4)
152 {
153   u32 flags = 0;
154
155   if (b0->flags & VNET_BUFFER_F_OFFLOAD)
156     return VNET_BUFFER_F_L4_CHECKSUM_CORRECT;
157   vlib_buffer_advance (b0, gho0->l3_hdr_offset);
158   if (is_ip4)
159     flags = ip4_tcp_udp_validate_checksum (vm, b0);
160   else
161     flags = ip6_tcp_udp_icmp_validate_checksum (vm, b0);
162   vlib_buffer_advance (b0, -gho0->l3_hdr_offset);
163   return flags;
164 }
165
166 static_always_inline u32
167 gro_fix_padded_packet_len (vlib_buffer_t *b0, generic_header_offset_t *gho0,
168                            ip4_header_t *ip4_0, ip6_header_t *ip6_0,
169                            u32 pkt_len0, u16 l234_sz0)
170 {
171   u32 tcp_payload_len0 = 0;
172   if (gho0->gho_flags & GHO_F_IP4)
173     {
174       tcp_payload_len0 = clib_net_to_host_u16 (ip4_0->length) -
175                          ip4_header_bytes (ip4_0) - gho0->l4_hdr_sz;
176     }
177   else
178     {
179       tcp_payload_len0 =
180         clib_net_to_host_u16 (ip6_0->payload_length) - gho0->l4_hdr_sz;
181     }
182
183   ASSERT (l234_sz0 + tcp_payload_len0 <= pkt_len0);
184
185   if (PREDICT_FALSE (l234_sz0 + tcp_payload_len0 < pkt_len0))
186     {
187       /* small packet with padding at the end, remove padding */
188       b0->current_length = l234_sz0 + tcp_payload_len0;
189       pkt_len0 = b0->current_length;
190     }
191   return pkt_len0;
192 }
193
194 static_always_inline u32
195 gro_get_packet_data (vlib_main_t *vm, vlib_buffer_t *b0,
196                      generic_header_offset_t *gho0, gro_flow_key_t *flow_key0,
197                      u8 is_l2)
198 {
199   ip4_header_t *ip4_0 = 0;
200   ip6_header_t *ip6_0 = 0;
201   tcp_header_t *tcp0 = 0;
202   u32 flags = 0;
203   u32 pkt_len0 = 0;
204   u16 l234_sz0 = 0;
205   u32 sw_if_index0[VLIB_N_RX_TX] = { ~0 };
206
207   u32 is_ip0 = gro_is_ip4_or_ip6_packet (b0, is_l2);
208
209   if (is_ip0 & VNET_BUFFER_F_IS_IP4)
210     vnet_generic_header_offset_parser (b0, gho0, is_l2, 1 /* is_ip4 */ ,
211                                        0 /* is_ip6 */ );
212   else if (is_ip0 & VNET_BUFFER_F_IS_IP6)
213     vnet_generic_header_offset_parser (b0, gho0, is_l2, 0 /* is_ip4 */ ,
214                                        1 /* is_ip6 */ );
215   else
216     return 0;
217
218   if (PREDICT_FALSE ((gho0->gho_flags & GHO_F_TCP) == 0))
219     return 0;
220
221   ip4_0 =
222     (ip4_header_t *) (vlib_buffer_get_current (b0) + gho0->l3_hdr_offset);
223   ip6_0 =
224     (ip6_header_t *) (vlib_buffer_get_current (b0) + gho0->l3_hdr_offset);
225   tcp0 =
226     (tcp_header_t *) (vlib_buffer_get_current (b0) + gho0->l4_hdr_offset);
227
228   l234_sz0 = gho0->hdr_sz;
229   if (PREDICT_FALSE (gro_is_bad_packet (b0, tcp0->flags, l234_sz0)))
230     return 0;
231
232   sw_if_index0[VLIB_RX] = vnet_buffer (b0)->sw_if_index[VLIB_RX];
233   sw_if_index0[VLIB_TX] = vnet_buffer (b0)->sw_if_index[VLIB_TX];
234
235   if (gho0->gho_flags & GHO_F_IP4)
236     {
237       flags = gro_validate_checksum (vm, b0, gho0, 1);
238       gro_get_ip4_flow_from_packet (sw_if_index0, ip4_0, tcp0, flow_key0,
239                                     is_l2);
240     }
241   else if (gho0->gho_flags & GHO_F_IP6)
242     {
243       flags = gro_validate_checksum (vm, b0, gho0, 0);
244       gro_get_ip6_flow_from_packet (sw_if_index0, ip6_0, tcp0, flow_key0,
245                                     is_l2);
246     }
247   else
248     return 0;
249
250   if (PREDICT_FALSE ((flags & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) == 0))
251     return 0;
252
253   pkt_len0 = vlib_buffer_length_in_chain (vm, b0);
254   if (PREDICT_FALSE (pkt_len0 >= TCP_MAX_GSO_SZ))
255     return 0;
256
257   if (PREDICT_FALSE (pkt_len0 <= GRO_PADDED_PACKET_SIZE))
258     {
259       pkt_len0 =
260         gro_fix_padded_packet_len (b0, gho0, ip4_0, ip6_0, pkt_len0, l234_sz0);
261     }
262   return pkt_len0;
263 }
264
265 static_always_inline u32
266 gro_coalesce_buffers (vlib_main_t *vm, vlib_buffer_t *b0, vlib_buffer_t *b1,
267                       u32 bi1, u8 is_l2)
268 {
269   generic_header_offset_t gho0 = { 0 };
270   generic_header_offset_t gho1 = { 0 };
271   gro_flow_key_t flow_key0, flow_key1;
272   ip4_header_t *ip4_0, *ip4_1;
273   ip6_header_t *ip6_0, *ip6_1;
274   tcp_header_t *tcp0, *tcp1;
275   u16 l234_sz0, l234_sz1;
276   u32 pkt_len0, pkt_len1, payload_len0, payload_len1;
277   u32 sw_if_index0[VLIB_N_RX_TX] = { ~0 };
278   u32 sw_if_index1[VLIB_N_RX_TX] = { ~0 };
279
280   u32 is_ip0 = gro_is_ip4_or_ip6_packet (b0, is_l2);
281   u32 is_ip1 = gro_is_ip4_or_ip6_packet (b1, is_l2);
282
283   if (is_ip0 & VNET_BUFFER_F_IS_IP4)
284     vnet_generic_header_offset_parser (b0, &gho0, is_l2, 1 /* is_ip4 */ ,
285                                        0 /* is_ip6 */ );
286   else if (is_ip0 & VNET_BUFFER_F_IS_IP6)
287     vnet_generic_header_offset_parser (b0, &gho0, is_l2, 0 /* is_ip4 */ ,
288                                        1 /* is_ip6 */ );
289   else
290     return 0;
291
292   if (is_ip1 & VNET_BUFFER_F_IS_IP4)
293     vnet_generic_header_offset_parser (b1, &gho1, is_l2, 1 /* is_ip4 */ ,
294                                        0 /* is_ip6 */ );
295   else if (is_ip1 & VNET_BUFFER_F_IS_IP6)
296     vnet_generic_header_offset_parser (b1, &gho1, is_l2, 0 /* is_ip4 */ ,
297                                        1 /* is_ip6 */ );
298   else
299     return 0;
300
301   pkt_len0 = vlib_buffer_length_in_chain (vm, b0);
302   pkt_len1 = vlib_buffer_length_in_chain (vm, b1);
303
304   if (((gho0.gho_flags & GHO_F_TCP) == 0 || pkt_len0 <= GRO_MIN_PACKET_SIZE) ||
305       ((gho1.gho_flags & GHO_F_TCP) == 0 || pkt_len1 <= GRO_MIN_PACKET_SIZE))
306     return 0;
307
308   ip4_0 =
309     (ip4_header_t *) (vlib_buffer_get_current (b0) + gho0.l3_hdr_offset);
310   ip4_1 =
311     (ip4_header_t *) (vlib_buffer_get_current (b1) + gho1.l3_hdr_offset);
312   ip6_0 =
313     (ip6_header_t *) (vlib_buffer_get_current (b0) + gho0.l3_hdr_offset);
314   ip6_1 =
315     (ip6_header_t *) (vlib_buffer_get_current (b1) + gho1.l3_hdr_offset);
316
317   tcp0 = (tcp_header_t *) (vlib_buffer_get_current (b0) + gho0.l4_hdr_offset);
318   tcp1 = (tcp_header_t *) (vlib_buffer_get_current (b1) + gho1.l4_hdr_offset);
319
320   l234_sz0 = gho0.hdr_sz;
321   l234_sz1 = gho1.hdr_sz;
322
323   if (gro_is_bad_packet (b0, tcp0->flags, l234_sz0)
324       || gro_is_bad_packet (b1, tcp1->flags, l234_sz1))
325     return 0;
326
327   sw_if_index0[VLIB_RX] = vnet_buffer (b0)->sw_if_index[VLIB_RX];
328   sw_if_index0[VLIB_TX] = vnet_buffer (b0)->sw_if_index[VLIB_TX];
329
330   sw_if_index1[VLIB_RX] = vnet_buffer (b1)->sw_if_index[VLIB_RX];
331   sw_if_index1[VLIB_TX] = vnet_buffer (b1)->sw_if_index[VLIB_TX];
332
333   if ((gho0.gho_flags & GHO_F_IP4) && (gho1.gho_flags & GHO_F_IP4))
334     {
335       gro_get_ip4_flow_from_packet (sw_if_index0, ip4_0, tcp0, &flow_key0,
336                                     is_l2);
337       gro_get_ip4_flow_from_packet (sw_if_index1, ip4_1, tcp1, &flow_key1,
338                                     is_l2);
339     }
340   else if ((gho0.gho_flags & GHO_F_IP6) && (gho1.gho_flags & GHO_F_IP6))
341     {
342       gro_get_ip6_flow_from_packet (sw_if_index0, ip6_0, tcp0, &flow_key0,
343                                     is_l2);
344       gro_get_ip6_flow_from_packet (sw_if_index1, ip6_1, tcp1, &flow_key1,
345                                     is_l2);
346     }
347   else
348     return 0;
349
350   if (gro_flow_is_equal (&flow_key0, &flow_key1) == 0)
351     return 0;
352
353   payload_len0 = pkt_len0 - l234_sz0;
354   payload_len1 = pkt_len1 - l234_sz1;
355
356   if (pkt_len0 >= TCP_MAX_GSO_SZ || pkt_len1 >= TCP_MAX_GSO_SZ
357       || (pkt_len0 + payload_len1) >= TCP_MAX_GSO_SZ)
358     return 0;
359
360   if (gro_tcp_sequence_check (tcp0, tcp1, payload_len0) ==
361       GRO_PACKET_ACTION_ENQUEUE)
362     {
363       gro_merge_buffers (vm, b0, b1, bi1, payload_len1, l234_sz1);
364       tcp0->flags |= tcp1->flags;
365       return tcp1->ack_number;
366     }
367
368   return 0;
369 }
370
371 static_always_inline void
372 gro_fixup_header (vlib_main_t *vm, vlib_buffer_t *b0, u32 ack_number, u8 is_l2)
373 {
374   generic_header_offset_t gho0 = { 0 };
375
376   u32 is_ip0 = gro_is_ip4_or_ip6_packet (b0, is_l2);
377
378   if (is_ip0 & VNET_BUFFER_F_IS_IP4)
379     vnet_generic_header_offset_parser (b0, &gho0, is_l2, 1 /* is_ip4 */ ,
380                                        0 /* is_ip6 */ );
381   else if (is_ip0 & VNET_BUFFER_F_IS_IP6)
382     vnet_generic_header_offset_parser (b0, &gho0, is_l2, 0 /* is_ip4 */ ,
383                                        1 /* is_ip6 */ );
384
385   vnet_buffer2 (b0)->gso_size = b0->current_length - gho0.hdr_sz;
386
387   if (gho0.gho_flags & GHO_F_IP4)
388     {
389       ip4_header_t *ip4 =
390         (ip4_header_t *) (vlib_buffer_get_current (b0) + gho0.l3_hdr_offset);
391       ip4->length =
392         clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0) -
393                               gho0.l3_hdr_offset);
394       b0->flags |= (VNET_BUFFER_F_GSO | VNET_BUFFER_F_IS_IP4);
395       vnet_buffer_offload_flags_set (b0, (VNET_BUFFER_OFFLOAD_F_TCP_CKSUM |
396                                           VNET_BUFFER_OFFLOAD_F_IP_CKSUM));
397     }
398   else if (gho0.gho_flags & GHO_F_IP6)
399     {
400       ip6_header_t *ip6 =
401         (ip6_header_t *) (vlib_buffer_get_current (b0) + gho0.l3_hdr_offset);
402       ip6->payload_length =
403         clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0) -
404                               gho0.l4_hdr_offset);
405       b0->flags |= (VNET_BUFFER_F_GSO | VNET_BUFFER_F_IS_IP6);
406       vnet_buffer_offload_flags_set (b0, VNET_BUFFER_OFFLOAD_F_TCP_CKSUM);
407     }
408
409   tcp_header_t *tcp0 =
410     (tcp_header_t *) (vlib_buffer_get_current (b0) + gho0.l4_hdr_offset);
411   tcp0->ack_number = ack_number;
412   b0->flags &= ~VLIB_BUFFER_IS_TRACED;
413 }
414
415 static_always_inline u32
416 vnet_gro_flow_table_flush (vlib_main_t * vm, gro_flow_table_t * flow_table,
417                            u32 * to)
418 {
419   if (flow_table->flow_table_size > 0)
420     {
421       gro_flow_t *gro_flow;
422       u32 i = 0, j = 0;
423       while (i < GRO_FLOW_TABLE_MAX_SIZE)
424         {
425           gro_flow = &flow_table->gro_flow[i];
426           if (gro_flow->n_buffers && gro_flow_is_timeout (vm, gro_flow))
427             {
428               // flush the packet
429               vlib_buffer_t *b0 =
430                 vlib_get_buffer (vm, gro_flow->buffer_index);
431               gro_fixup_header (vm, b0, gro_flow->last_ack_number,
432                                 flow_table->is_l2);
433               to[j] = gro_flow->buffer_index;
434               gro_flow_table_reset_flow (flow_table, gro_flow);
435               flow_table->n_vectors++;
436               j++;
437             }
438           i++;
439         }
440
441       return j;
442     }
443   return 0;
444 }
445
446 static_always_inline void
447 vnet_gro_flow_table_schedule_node_on_dispatcher (vlib_main_t * vm,
448                                                  gro_flow_table_t *
449                                                  flow_table)
450 {
451   if (gro_flow_table_is_timeout (vm, flow_table))
452     {
453       u32 to[GRO_FLOW_TABLE_MAX_SIZE] = { 0 };
454       u32 n_to = vnet_gro_flow_table_flush (vm, flow_table, to);
455
456       if (n_to > 0)
457         {
458           u32 node_index = flow_table->node_index;
459           vlib_frame_t *f = vlib_get_frame_to_node (vm, node_index);
460           u32 *f_to = vlib_frame_vector_args (f);
461           u32 i = 0;
462
463           while (i < n_to)
464             {
465               f_to[f->n_vectors] = to[i];
466               i++;
467               f->n_vectors++;
468             }
469           vlib_put_frame_to_node (vm, node_index, f);
470         }
471       gro_flow_table_set_timeout (vm, flow_table, GRO_FLOW_TABLE_FLUSH);
472     }
473 }
474
475 static_always_inline u32
476 vnet_gro_flush_all_packets (vlib_main_t *vm, gro_flow_table_t *flow_table,
477                             gro_flow_t *gro_flow, vlib_buffer_t *b_s, u32 *to,
478                             u32 bi_s, u32 bi0, u8 is_l2)
479 {
480   flow_table->n_vectors++;
481   flow_table->total_vectors++;
482   gro_fixup_header (vm, b_s, gro_flow->last_ack_number, is_l2);
483   gro_flow->n_buffers = 0;
484   gro_flow_table_reset_flow (flow_table, gro_flow);
485   to[0] = bi_s;
486   to[1] = bi0;
487   return 2;
488 }
489
490 static_always_inline u32
491 vnet_gro_flow_table_inline (vlib_main_t * vm, gro_flow_table_t * flow_table,
492                             u32 bi0, u32 * to)
493 {
494   vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0);
495   generic_header_offset_t gho0 = { 0 };
496   gro_flow_t *gro_flow = 0;
497   gro_flow_key_t flow_key0 = { };
498   tcp_header_t *tcp0 = 0;
499   u32 pkt_len0 = 0;
500   u32 is_flush = 0;
501   u8 is_l2 = flow_table->is_l2;
502
503   if (!gro_flow_table_is_enable (flow_table))
504     {
505       to[0] = bi0;
506       return 1;
507     }
508
509   if (PREDICT_FALSE (b0->flags & VNET_BUFFER_F_GSO))
510     {
511       to[0] = bi0;
512       return 1;
513     }
514
515   pkt_len0 = gro_get_packet_data (vm, b0, &gho0, &flow_key0, is_l2);
516   if (pkt_len0 == 0)
517     {
518       to[0] = bi0;
519       return 1;
520     }
521
522   tcp0 = (tcp_header_t *) (vlib_buffer_get_current (b0) + gho0.l4_hdr_offset);
523   if (PREDICT_TRUE (((tcp0->flags & TCP_FLAG_PSH) == 0) &&
524                     (pkt_len0 > GRO_MIN_PACKET_SIZE)))
525     gro_flow = gro_flow_table_find_or_add_flow (flow_table, &flow_key0);
526   else
527     {
528       is_flush = 1;
529       gro_flow = gro_flow_table_get_flow (flow_table, &flow_key0);
530     }
531
532   if (!gro_flow)
533     {
534       to[0] = bi0;
535       return 1;
536     }
537
538   if (PREDICT_FALSE (gro_flow->n_buffers == 0))
539     {
540       flow_table->total_vectors++;
541       gro_flow_store_packet (gro_flow, bi0);
542       gro_flow->last_ack_number = tcp0->ack_number;
543       gro_flow_set_timeout (vm, gro_flow, GRO_FLOW_TIMEOUT);
544       return 0;
545     }
546   else
547     {
548       generic_header_offset_t gho_s = { 0 };
549       tcp_header_t *tcp_s;
550       u16 l234_sz0, l234_sz_s;
551       u32 pkt_len_s, payload_len0, payload_len_s;
552       u32 bi_s = gro_flow->buffer_index;
553
554       vlib_buffer_t *b_s = vlib_get_buffer (vm, bi_s);
555       u32 is_ip_s = gro_is_ip4_or_ip6_packet (b_s, is_l2);
556       if (is_ip_s & VNET_BUFFER_F_IS_IP4)
557         vnet_generic_header_offset_parser (b_s, &gho_s, is_l2,
558                                            1 /* is_ip4 */ , 0 /* is_ip6 */ );
559       else if (is_ip_s & VNET_BUFFER_F_IS_IP6)
560         vnet_generic_header_offset_parser (b_s, &gho_s, is_l2,
561                                            0 /* is_ip4 */ , 1 /* is_ip6 */ );
562
563       tcp_s =
564         (tcp_header_t *) (vlib_buffer_get_current (b_s) +
565                           gho_s.l4_hdr_offset);
566       pkt_len_s = vlib_buffer_length_in_chain (vm, b_s);
567       l234_sz0 = gho0.hdr_sz;
568       l234_sz_s = gho_s.hdr_sz;
569       payload_len0 = pkt_len0 - l234_sz0;
570       payload_len_s = pkt_len_s - l234_sz_s;
571       gro_packet_action_t action =
572         gro_tcp_sequence_check (tcp_s, tcp0, payload_len_s);
573
574       if (PREDICT_TRUE (action == GRO_PACKET_ACTION_ENQUEUE))
575         {
576           if (PREDICT_TRUE (((pkt_len_s + payload_len0) < TCP_MAX_GSO_SZ) &&
577                             (gro_flow->n_buffers < GRO_FLOW_N_BUFFERS)))
578             {
579               flow_table->total_vectors++;
580               gro_merge_buffers (vm, b_s, b0, bi0, payload_len0, l234_sz0);
581               gro_flow_store_packet (gro_flow, bi0);
582               gro_flow->last_ack_number = tcp0->ack_number;
583               if (PREDICT_FALSE (is_flush))
584                 {
585                   flow_table->n_vectors++;
586                   tcp_s->flags |= tcp0->flags;
587                   gro_fixup_header (vm, b_s, gro_flow->last_ack_number, is_l2);
588                   gro_flow->n_buffers = 0;
589                   gro_flow_table_reset_flow (flow_table, gro_flow);
590                   to[0] = bi_s;
591                   return 1;
592                 }
593               return 0;
594             }
595           else if (PREDICT_FALSE (is_flush))
596             // flush the all (current and stored) packets
597             return vnet_gro_flush_all_packets (vm, flow_table, gro_flow, b_s,
598                                                to, bi_s, bi0, is_l2);
599           else
600             {
601               // flush the stored GSO size packet and buffer the current packet
602               flow_table->n_vectors++;
603               flow_table->total_vectors++;
604               gro_fixup_header (vm, b_s, gro_flow->last_ack_number, is_l2);
605               gro_flow->n_buffers = 0;
606               gro_flow_store_packet (gro_flow, bi0);
607               gro_flow->last_ack_number = tcp0->ack_number;
608               gro_flow_set_timeout (vm, gro_flow, GRO_FLOW_TIMEOUT);
609               to[0] = bi_s;
610               return 1;
611             }
612         }
613       else
614         {
615           // flush the all (current and stored) packets
616           return vnet_gro_flush_all_packets (vm, flow_table, gro_flow, b_s, to,
617                                              bi_s, bi0, is_l2);
618         }
619     }
620 }
621
622 /**
623  * coalesce buffers with flow tables
624  */
625 static_always_inline u32
626 vnet_gro_inline (vlib_main_t * vm, gro_flow_table_t * flow_table, u32 * from,
627                  u16 n_left_from, u32 * to)
628 {
629   u16 count = 0, i = 0;
630
631   for (i = 0; i < n_left_from; i++)
632     count += vnet_gro_flow_table_inline (vm, flow_table, from[i], &to[count]);
633
634   return count;
635 }
636
637 /**
638  * coalesce buffers in opportunistic way without flow tables
639  */
640 static_always_inline u32
641 vnet_gro_simple_inline (vlib_main_t * vm, u32 * from, u16 n_left_from,
642                         int is_l2)
643 {
644   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
645   vlib_get_buffers (vm, from, b, n_left_from);
646   u32 bi = 1, ack_number = 0;
647   if (PREDICT_TRUE (((b[0]->flags & VNET_BUFFER_F_GSO) == 0)))
648     {
649       while (n_left_from > 1)
650         {
651           if (PREDICT_TRUE (((b[bi]->flags & VNET_BUFFER_F_GSO) == 0)))
652             {
653               u32 ret;
654               if ((ret =
655                    gro_coalesce_buffers (vm, b[0], b[bi], from[bi],
656                                          is_l2)) != 0)
657                 {
658                   n_left_from -= 1;
659                   bi += 1;
660                   ack_number = ret;
661                   continue;
662                 }
663               else
664                 break;
665             }
666           else
667             break;
668         }
669
670       if (bi >= 2)
671         {
672           gro_fixup_header (vm, b[0], ack_number, is_l2);
673         }
674     }
675   return bi;
676 }
677 #endif /* included_gro_func_h */
678
679 /*
680  * fd.io coding-style-patch-verification: ON
681  *
682  * Local Variables:
683  * eval: (c-set-style "gnu")
684  * End:
685  */