IP fragmentation to handle buffer chains.
[vpp.git] / src / vnet / ip / ip_frag.c
1 /*---------------------------------------------------------------------------
2  * Copyright (c) 2009-2014 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 /*
17  * IPv4 Fragmentation Node
18  *
19  *
20  */
21
22 #include "ip_frag.h"
23
24 #include <vnet/ip/ip.h>
25
26
27 typedef struct
28 {
29   u8 ipv6;
30   u16 header_offset;
31   u16 mtu;
32   u8 next;
33   u16 n_fragments;
34 } ip_frag_trace_t;
35
36 static u8 *
37 format_ip_frag_trace (u8 * s, va_list * args)
38 {
39   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
40   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
41   ip_frag_trace_t *t = va_arg (*args, ip_frag_trace_t *);
42   s = format (s, "IPv%s offset: %u mtu: %u fragments: %u",
43               t->ipv6 ? "6" : "4", t->header_offset, t->mtu, t->n_fragments);
44   return s;
45 }
46
47 static u32 running_fragment_id;
48
49 /*
50  * Limitation: Does follow buffer chains in the packet to fragment,
51  * but does not generate buffer chains. I.e. a fragment is always
52  * contained with in a single buffer and limited to the max buffer
53  * size.
54  */
55 void
56 ip4_frag_do_fragment (vlib_main_t * vm, u32 from_bi, u32 ** buffer,
57                       ip_frag_error_t * error)
58 {
59   vlib_buffer_t *from_b;
60   ip4_header_t *ip4;
61   u16 mtu, len, max, rem, offset, ip_frag_id, ip_frag_offset;
62   u8 *org_from_packet, more;
63
64   from_b = vlib_get_buffer (vm, from_bi);
65   offset = vnet_buffer (from_b)->ip_frag.header_offset;
66   mtu = vnet_buffer (from_b)->ip_frag.mtu;
67   org_from_packet = vlib_buffer_get_current (from_b);
68   ip4 = (ip4_header_t *) vlib_buffer_get_current (from_b) + offset;
69
70   rem = clib_net_to_host_u16 (ip4->length) - sizeof (ip4_header_t);
71   max =
72     (mtu - sizeof (ip4_header_t) -
73      vnet_buffer (from_b)->ip_frag.header_offset) & ~0x7;
74
75   if (rem >
76       (vlib_buffer_length_in_chain (vm, from_b) - offset -
77        sizeof (ip4_header_t)))
78     {
79       *error = IP_FRAG_ERROR_MALFORMED;
80       return;
81     }
82
83   if (mtu < sizeof (ip4_header_t))
84     {
85       *error = IP_FRAG_ERROR_CANT_FRAGMENT_HEADER;
86       return;
87     }
88
89   if (ip4->flags_and_fragment_offset &
90       clib_host_to_net_u16 (IP4_HEADER_FLAG_DONT_FRAGMENT))
91     {
92       *error = IP_FRAG_ERROR_DONT_FRAGMENT_SET;
93       return;
94     }
95
96   if (ip4_is_fragment (ip4))
97     {
98       ip_frag_id = ip4->fragment_id;
99       ip_frag_offset = ip4_get_fragment_offset (ip4);
100       more =
101         !(!(ip4->flags_and_fragment_offset &
102             clib_host_to_net_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS)));
103     }
104   else
105     {
106       ip_frag_id = (++running_fragment_id);
107       ip_frag_offset = 0;
108       more = 0;
109     }
110
111   u8 *from_data = (void *) (ip4 + 1);
112   vlib_buffer_t *org_from_b = from_b;
113   u16 ptr = 0, fo = 0;
114   u16 left_in_from_buffer =
115     from_b->current_length - offset - sizeof (ip4_header_t);
116
117   /* Do the actual fragmentation */
118   while (rem)
119     {
120       u32 to_bi;
121       vlib_buffer_t *to_b;
122       ip4_header_t *to_ip4;
123       u8 *to_data;
124
125       len = (rem > (mtu - sizeof (ip4_header_t) - offset) ? max : rem);
126       if (len != rem)           /* Last fragment does not need to divisible by 8 */
127         len &= ~0x7;
128       if (!vlib_buffer_alloc (vm, &to_bi, 1))
129         {
130           *error = IP_FRAG_ERROR_MEMORY;
131           /* XXX: Free already allocated buffers? */
132           return;
133         }
134       vec_add1 (*buffer, to_bi);
135       to_b = vlib_get_buffer (vm, to_bi);
136       vnet_buffer (to_b)->sw_if_index[VLIB_RX] =
137         vnet_buffer (org_from_b)->sw_if_index[VLIB_RX];
138       vnet_buffer (to_b)->sw_if_index[VLIB_TX] =
139         vnet_buffer (org_from_b)->sw_if_index[VLIB_TX];
140       /* Copy adj_index in case DPO based node is sending for the
141        * fragmentation, the packet would be sent back to the proper
142        * DPO next node and Index
143        */
144       vnet_buffer (to_b)->ip.adj_index[VLIB_RX] =
145         vnet_buffer (org_from_b)->ip.adj_index[VLIB_RX];
146       vnet_buffer (to_b)->ip.adj_index[VLIB_TX] =
147         vnet_buffer (org_from_b)->ip.adj_index[VLIB_TX];
148
149       /* Copy offset and ip4 header */
150       clib_memcpy (to_b->data, org_from_packet,
151                    offset + sizeof (ip4_header_t));
152       to_ip4 = vlib_buffer_get_current (to_b) + offset;
153       to_data = (void *) (to_ip4 + 1);
154
155       /* Spin through buffer chain copying data */
156       // XXX: Make sure we don't overflow source buffer!!!
157       if (len > left_in_from_buffer)
158         {
159           clib_memcpy (to_data, from_data + ptr, left_in_from_buffer);
160
161           /* Move buffer */
162           if (!(from_b->flags & VLIB_BUFFER_NEXT_PRESENT))
163             {
164               *error = IP_FRAG_ERROR_MALFORMED;
165               return;
166             }
167           from_b = vlib_get_buffer (vm, from_b->next_buffer);
168           from_data = (u8 *) vlib_buffer_get_current (from_b);
169           clib_memcpy (to_data + left_in_from_buffer, from_data,
170                        len - left_in_from_buffer);
171           ptr = len - left_in_from_buffer;
172           left_in_from_buffer =
173             from_b->current_length - (len - left_in_from_buffer);
174         }
175       else
176         {
177           clib_memcpy (to_data, from_data + ptr, len);
178           left_in_from_buffer -= len;
179           ptr += len;
180         }
181       to_b->current_length = offset + len + sizeof (ip4_header_t);
182
183       to_ip4->fragment_id = ip_frag_id;
184       to_ip4->flags_and_fragment_offset =
185         clib_host_to_net_u16 ((fo >> 3) + ip_frag_offset);
186       to_ip4->flags_and_fragment_offset |=
187         clib_host_to_net_u16 (((len != rem) || more) << 13);
188       to_ip4->length = clib_host_to_net_u16 (len + sizeof (ip4_header_t));
189       to_ip4->checksum = ip4_header_checksum (to_ip4);
190
191       if (vnet_buffer (org_from_b)->ip_frag.flags & IP_FRAG_FLAG_IP4_HEADER)
192         {
193           /* Encapsulating ipv4 header */
194           ip4_header_t *encap_header4 =
195             (ip4_header_t *) vlib_buffer_get_current (to_b);
196           encap_header4->length = clib_host_to_net_u16 (to_b->current_length);
197           encap_header4->checksum = ip4_header_checksum (encap_header4);
198         }
199       else if (vnet_buffer (org_from_b)->
200                ip_frag.flags & IP_FRAG_FLAG_IP6_HEADER)
201         {
202           /* Encapsulating ipv6 header */
203           ip6_header_t *encap_header6 =
204             (ip6_header_t *) vlib_buffer_get_current (to_b);
205           encap_header6->payload_length =
206             clib_host_to_net_u16 (to_b->current_length -
207                                   sizeof (*encap_header6));
208         }
209       rem -= len;
210       fo += len;
211     }
212   /* Free original packet chain */
213   vlib_buffer_free_one (vm, from_bi);
214 }
215
216 void
217 ip_frag_set_vnet_buffer (vlib_buffer_t * b, u16 offset, u16 mtu,
218                          u8 next_index, u8 flags)
219 {
220   vnet_buffer (b)->ip_frag.header_offset = offset;
221   vnet_buffer (b)->ip_frag.mtu = mtu;
222   vnet_buffer (b)->ip_frag.next_index = next_index;
223   vnet_buffer (b)->ip_frag.flags = flags;
224 }
225
226 static uword
227 ip4_frag (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
228 {
229   u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
230   vlib_node_runtime_t *error_node =
231     vlib_node_get_runtime (vm, ip4_frag_node.index);
232   from = vlib_frame_vector_args (frame);
233   n_left_from = frame->n_vectors;
234   next_index = node->cached_next_index;
235   u32 frag_sent = 0, small_packets = 0;
236   u32 *buffer = 0;
237
238   while (n_left_from > 0)
239     {
240       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
241
242       while (n_left_from > 0 && n_left_to_next > 0)
243         {
244           u32 pi0, *frag_from, frag_left;
245           vlib_buffer_t *p0;
246           ip_frag_error_t error0;
247           ip4_frag_next_t next0;
248
249           //Note: The packet is not enqueued now.
250           //It is instead put in a vector where other fragments
251           //will be put as well.
252           pi0 = from[0];
253           from += 1;
254           n_left_from -= 1;
255           error0 = IP_FRAG_ERROR_NONE;
256
257           p0 = vlib_get_buffer (vm, pi0);
258           ip4_frag_do_fragment (vm, pi0, &buffer, &error0);
259
260           if (PREDICT_FALSE (p0->flags & VLIB_BUFFER_IS_TRACED))
261             {
262               ip_frag_trace_t *tr =
263                 vlib_add_trace (vm, node, p0, sizeof (*tr));
264               tr->header_offset = vnet_buffer (p0)->ip_frag.header_offset;
265               tr->mtu = vnet_buffer (p0)->ip_frag.mtu;
266               tr->ipv6 = 0;
267               tr->n_fragments = vec_len (buffer);
268               tr->next = vnet_buffer (p0)->ip_frag.next_index;
269             }
270
271           if (error0 == IP_FRAG_ERROR_DONT_FRAGMENT_SET)
272             {
273               icmp4_error_set_vnet_buffer (p0, ICMP4_destination_unreachable,
274                                            ICMP4_destination_unreachable_fragmentation_needed_and_dont_fragment_set,
275                                            vnet_buffer (p0)->ip_frag.mtu);
276               vlib_buffer_advance (p0,
277                                    vnet_buffer (p0)->ip_frag.header_offset);
278               next0 = IP4_FRAG_NEXT_ICMP_ERROR;
279             }
280           else
281             {
282               /* *INDENT-OFF* */
283               next0 = (error0 == IP_FRAG_ERROR_NONE) ? vnet_buffer (p0)->
284                 ip_frag.next_index : IP4_FRAG_NEXT_DROP;
285               /* *INDENT-ON* */
286             }
287
288           if (error0 == IP_FRAG_ERROR_NONE)
289             {
290               frag_sent += vec_len (buffer);
291               small_packets += (vec_len (buffer) == 1);
292             }
293           else
294             vlib_error_count (vm, ip4_frag_node.index, error0, 1);
295
296           //Send fragments that were added in the frame
297           frag_from = buffer;
298           frag_left = vec_len (buffer);
299
300           while (frag_left > 0)
301             {
302               while (frag_left > 0 && n_left_to_next > 0)
303                 {
304                   u32 i;
305                   i = to_next[0] = frag_from[0];
306                   frag_from += 1;
307                   frag_left -= 1;
308                   to_next += 1;
309                   n_left_to_next -= 1;
310
311                   vlib_get_buffer (vm, i)->error = error_node->errors[error0];
312                   vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
313                                                    to_next, n_left_to_next, i,
314                                                    next0);
315                 }
316               vlib_put_next_frame (vm, node, next_index, n_left_to_next);
317               vlib_get_next_frame (vm, node, next_index, to_next,
318                                    n_left_to_next);
319             }
320           vec_reset_length (buffer);
321         }
322       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
323     }
324   vec_free (buffer);
325
326   vlib_node_increment_counter (vm, ip4_frag_node.index,
327                                IP_FRAG_ERROR_FRAGMENT_SENT, frag_sent);
328   vlib_node_increment_counter (vm, ip4_frag_node.index,
329                                IP_FRAG_ERROR_SMALL_PACKET, small_packets);
330
331   return frame->n_vectors;
332 }
333
334
335 void
336 ip6_frag_do_fragment (vlib_main_t * vm, u32 pi, u32 ** buffer,
337                       ip_frag_error_t * error)
338 {
339   vlib_buffer_t *p;
340   ip6_header_t *ip6_hdr;
341   ip6_frag_hdr_t *frag_hdr;
342   u8 *payload, *next_header;
343
344   p = vlib_get_buffer (vm, pi);
345
346   //Parsing the IPv6 headers
347   ip6_hdr =
348     vlib_buffer_get_current (p) + vnet_buffer (p)->ip_frag.header_offset;
349   payload = (u8 *) (ip6_hdr + 1);
350   next_header = &ip6_hdr->protocol;
351   if (*next_header == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS)
352     {
353       next_header = payload;
354       payload += payload[1] * 8;
355     }
356
357   if (*next_header == IP_PROTOCOL_IP6_DESTINATION_OPTIONS)
358     {
359       next_header = payload;
360       payload += payload[1] * 8;
361     }
362
363   if (*next_header == IP_PROTOCOL_IPV6_ROUTE)
364     {
365       next_header = payload;
366       payload += payload[1] * 8;
367     }
368
369   if (PREDICT_FALSE
370       (payload >= (u8 *) vlib_buffer_get_current (p) + p->current_length))
371     {
372       //A malicious packet could set an extension header with a too big size
373       //and make us modify another vlib_buffer
374       *error = IP_FRAG_ERROR_MALFORMED;
375       return;
376     }
377
378   if (p->flags & VLIB_BUFFER_NEXT_PRESENT)
379     {
380       *error = IP_FRAG_ERROR_MALFORMED;
381       return;
382     }
383
384   u8 has_more;
385   u16 initial_offset;
386   if (*next_header == IP_PROTOCOL_IPV6_FRAGMENTATION)
387     {
388       //The fragmentation header is already there
389       frag_hdr = (ip6_frag_hdr_t *) payload;
390       has_more = ip6_frag_hdr_more (frag_hdr);
391       initial_offset = ip6_frag_hdr_offset (frag_hdr);
392     }
393   else
394     {
395       //Insert a fragmentation header in the packet
396       u8 nh = *next_header;
397       *next_header = IP_PROTOCOL_IPV6_FRAGMENTATION;
398       vlib_buffer_advance (p, -sizeof (*frag_hdr));
399       u8 *start = vlib_buffer_get_current (p);
400       memmove (start, start + sizeof (*frag_hdr),
401                payload - (start + sizeof (*frag_hdr)));
402       frag_hdr = (ip6_frag_hdr_t *) (payload - sizeof (*frag_hdr));
403       frag_hdr->identification = ++running_fragment_id;
404       frag_hdr->next_hdr = nh;
405       frag_hdr->rsv = 0;
406       has_more = 0;
407       initial_offset = 0;
408     }
409   payload = (u8 *) (frag_hdr + 1);
410
411   u16 headers_len = payload - (u8 *) vlib_buffer_get_current (p);
412   u16 max_payload = vnet_buffer (p)->ip_frag.mtu - headers_len;
413   u16 rem = p->current_length - headers_len;
414   u16 ptr = 0;
415
416   if (max_payload < 8)
417     {
418       *error = IP_FRAG_ERROR_CANT_FRAGMENT_HEADER;
419       return;
420     }
421
422   while (rem)
423     {
424       u32 bi;
425       vlib_buffer_t *b;
426       u16 len = (rem > max_payload) ? (max_payload & ~0x7) : rem;
427       rem -= len;
428
429       if (ptr != 0)
430         {
431           if (!vlib_buffer_alloc (vm, &bi, 1))
432             {
433               *error = IP_FRAG_ERROR_MEMORY;
434               return;
435             }
436           b = vlib_get_buffer (vm, bi);
437           vnet_buffer (b)->sw_if_index[VLIB_RX] =
438             vnet_buffer (p)->sw_if_index[VLIB_RX];
439           vnet_buffer (b)->sw_if_index[VLIB_TX] =
440             vnet_buffer (p)->sw_if_index[VLIB_TX];
441
442           /* Copy Adj_index in case DPO based node is sending for the fragmentation,
443              the packet would be sent back to the proper DPO next node and Index */
444           vnet_buffer (b)->ip.adj_index[VLIB_RX] =
445             vnet_buffer (p)->ip.adj_index[VLIB_RX];
446           vnet_buffer (b)->ip.adj_index[VLIB_TX] =
447             vnet_buffer (p)->ip.adj_index[VLIB_TX];
448
449           clib_memcpy (vlib_buffer_get_current (b),
450                        vlib_buffer_get_current (p), headers_len);
451           clib_memcpy (vlib_buffer_get_current (b) + headers_len,
452                        payload + ptr, len);
453           frag_hdr =
454             vlib_buffer_get_current (b) + headers_len - sizeof (*frag_hdr);
455         }
456       else
457         {
458           bi = pi;
459           b = vlib_get_buffer (vm, bi);
460           //frag_hdr already set here
461         }
462
463       ip6_hdr =
464         vlib_buffer_get_current (b) + vnet_buffer (p)->ip_frag.header_offset;
465       frag_hdr->fragment_offset_and_more =
466         ip6_frag_hdr_offset_and_more (initial_offset + (ptr >> 3),
467                                       (rem || has_more));
468       b->current_length = headers_len + len;
469       ip6_hdr->payload_length =
470         clib_host_to_net_u16 (b->current_length -
471                               vnet_buffer (p)->ip_frag.header_offset -
472                               sizeof (*ip6_hdr));
473
474       if (vnet_buffer (p)->ip_frag.flags & IP_FRAG_FLAG_IP4_HEADER)
475         {
476           //Encapsulating ipv4 header
477           ip4_header_t *encap_header4 =
478             (ip4_header_t *) vlib_buffer_get_current (b);
479           encap_header4->length = clib_host_to_net_u16 (b->current_length);
480           encap_header4->checksum = ip4_header_checksum (encap_header4);
481         }
482       else if (vnet_buffer (p)->ip_frag.flags & IP_FRAG_FLAG_IP6_HEADER)
483         {
484           //Encapsulating ipv6 header
485           ip6_header_t *encap_header6 =
486             (ip6_header_t *) vlib_buffer_get_current (b);
487           encap_header6->payload_length =
488             clib_host_to_net_u16 (b->current_length -
489                                   sizeof (*encap_header6));
490         }
491
492       vec_add1 (*buffer, bi);
493
494       ptr += len;
495     }
496 }
497
498 static uword
499 ip6_frag (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
500 {
501   u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
502   vlib_node_runtime_t *error_node =
503     vlib_node_get_runtime (vm, ip6_frag_node.index);
504   from = vlib_frame_vector_args (frame);
505   n_left_from = frame->n_vectors;
506   next_index = node->cached_next_index;
507   u32 frag_sent = 0, small_packets = 0;
508   u32 *buffer = 0;
509
510   while (n_left_from > 0)
511     {
512       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
513
514       while (n_left_from > 0 && n_left_to_next > 0)
515         {
516           u32 pi0, *frag_from, frag_left;
517           vlib_buffer_t *p0;
518           ip_frag_error_t error0;
519           ip6_frag_next_t next0;
520
521           pi0 = from[0];
522           from += 1;
523           n_left_from -= 1;
524           error0 = IP_FRAG_ERROR_NONE;
525
526           p0 = vlib_get_buffer (vm, pi0);
527           ip6_frag_do_fragment (vm, pi0, &buffer, &error0);
528
529           if (PREDICT_FALSE (p0->flags & VLIB_BUFFER_IS_TRACED))
530             {
531               ip_frag_trace_t *tr =
532                 vlib_add_trace (vm, node, p0, sizeof (*tr));
533               tr->header_offset = vnet_buffer (p0)->ip_frag.header_offset;
534               tr->mtu = vnet_buffer (p0)->ip_frag.mtu;
535               tr->ipv6 = 1;
536               tr->n_fragments = vec_len (buffer);
537               tr->next = vnet_buffer (p0)->ip_frag.next_index;
538             }
539
540           /* *INDENT-OFF* */
541           next0 = (error0 == IP_FRAG_ERROR_NONE) ? vnet_buffer (p0)->
542             ip_frag.next_index : IP6_FRAG_NEXT_DROP;
543           /* *INDENT-ON* */
544
545           frag_sent += vec_len (buffer);
546           small_packets += (vec_len (buffer) == 1);
547
548           //Send fragments that were added in the frame
549           frag_from = buffer;
550           frag_left = vec_len (buffer);
551           while (frag_left > 0)
552             {
553               while (frag_left > 0 && n_left_to_next > 0)
554                 {
555                   u32 i;
556                   i = to_next[0] = frag_from[0];
557                   frag_from += 1;
558                   frag_left -= 1;
559                   to_next += 1;
560                   n_left_to_next -= 1;
561
562                   vlib_get_buffer (vm, i)->error = error_node->errors[error0];
563                   vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
564                                                    to_next, n_left_to_next, i,
565                                                    next0);
566                 }
567               vlib_put_next_frame (vm, node, next_index, n_left_to_next);
568               vlib_get_next_frame (vm, node, next_index, to_next,
569                                    n_left_to_next);
570             }
571           vec_reset_length (buffer);
572         }
573       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
574     }
575   vec_free (buffer);
576   vlib_node_increment_counter (vm, ip6_frag_node.index,
577                                IP_FRAG_ERROR_FRAGMENT_SENT, frag_sent);
578   vlib_node_increment_counter (vm, ip6_frag_node.index,
579                                IP_FRAG_ERROR_SMALL_PACKET, small_packets);
580
581   return frame->n_vectors;
582 }
583
584 static char *ip4_frag_error_strings[] = {
585 #define _(sym,string) string,
586   foreach_ip_frag_error
587 #undef _
588 };
589
590 /* *INDENT-OFF* */
591 VLIB_REGISTER_NODE (ip4_frag_node) = {
592   .function = ip4_frag,
593   .name = IP4_FRAG_NODE_NAME,
594   .vector_size = sizeof (u32),
595   .format_trace = format_ip_frag_trace,
596   .type = VLIB_NODE_TYPE_INTERNAL,
597
598   .n_errors = IP_FRAG_N_ERROR,
599   .error_strings = ip4_frag_error_strings,
600
601   .n_next_nodes = IP4_FRAG_N_NEXT,
602   .next_nodes = {
603     [IP4_FRAG_NEXT_IP4_LOOKUP] = "ip4-lookup",
604     [IP4_FRAG_NEXT_IP6_LOOKUP] = "ip6-lookup",
605     [IP4_FRAG_NEXT_ICMP_ERROR] = "ip4-icmp-error",
606     [IP4_FRAG_NEXT_DROP] = "ip4-drop"
607   },
608 };
609 /* *INDENT-ON* */
610
611 /* *INDENT-OFF* */
612 VLIB_REGISTER_NODE (ip6_frag_node) = {
613   .function = ip6_frag,
614   .name = IP6_FRAG_NODE_NAME,
615   .vector_size = sizeof (u32),
616   .format_trace = format_ip_frag_trace,
617   .type = VLIB_NODE_TYPE_INTERNAL,
618
619   .n_errors = IP_FRAG_N_ERROR,
620   .error_strings = ip4_frag_error_strings,
621
622   .n_next_nodes = IP6_FRAG_N_NEXT,
623   .next_nodes = {
624     [IP6_FRAG_NEXT_IP4_LOOKUP] = "ip4-lookup",
625     [IP6_FRAG_NEXT_IP6_LOOKUP] = "ip6-lookup",
626     [IP6_FRAG_NEXT_DROP] = "ip6-drop"
627   },
628 };
629 /* *INDENT-ON* */
630
631 /*
632  * fd.io coding-style-patch-verification: ON
633  *
634  * Local Variables:
635  * eval: (c-set-style "gnu")
636  * End:
637  */