ip: coverity illegal access in ip6_ext_header_walk
[vpp.git] / src / vnet / ip / ip6_packet.h
1 /*
2  * Copyright (c) 2015 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  * ip6/packet.h: ip6 packet format
17  *
18  * Copyright (c) 2008 Eliot Dresselhaus
19  *
20  * Permission is hereby granted, free of charge, to any person obtaining
21  * a copy of this software and associated documentation files (the
22  * "Software"), to deal in the Software without restriction, including
23  * without limitation the rights to use, copy, modify, merge, publish,
24  * distribute, sublicense, and/or sell copies of the Software, and to
25  * permit persons to whom the Software is furnished to do so, subject to
26  * the following conditions:
27  *
28  * The above copyright notice and this permission notice shall be
29  * included in all copies or substantial portions of the Software.
30  *
31  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34  *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35  *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36  *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38  */
39
40 #ifndef included_ip6_packet_h
41 #define included_ip6_packet_h
42
43 #include <vnet/tcp/tcp_packet.h>
44 #include <vnet/ip/ip4_packet.h>
45 #include <stdbool.h>
46
47 typedef union
48 {
49   u8 as_u8[16];
50   u16 as_u16[8];
51   u32 as_u32[4];
52   u64 as_u64[2];
53   u64x2 as_u128;
54   uword as_uword[16 / sizeof (uword)];
55 }
56 __clib_packed ip6_address_t;
57
58 STATIC_ASSERT_SIZEOF (ip6_address_t, 16);
59
60 typedef struct
61 {
62   ip6_address_t addr, mask;
63 } ip6_address_and_mask_t;
64
65 /* Packed so that the mhash key doesn't include uninitialized pad bytes */
66 typedef CLIB_PACKED (struct {
67   /* IP address must be first for ip_interface_address_get_address() to work */
68   ip6_address_t ip6_addr;
69   u32 fib_index;
70 }) ip6_address_fib_t;
71
72 always_inline void
73 ip6_addr_fib_init (ip6_address_fib_t * addr_fib,
74                    const ip6_address_t * address, u32 fib_index)
75 {
76   addr_fib->ip6_addr = *address;
77   addr_fib->fib_index = fib_index;
78 }
79
80 /* Special addresses:
81    unspecified          ::/128
82    loopback             ::1/128
83    global unicast       2000::/3
84    unique local unicast fc00::/7
85    link local unicast   fe80::/10
86    multicast            ff00::/8
87    ietf reserved        everything else. */
88
89 #define foreach_ip6_multicast_address_scope     \
90   _ (loopback, 0x1)                             \
91   _ (link_local, 0x2)                           \
92   _ (admin_local, 0x4)                          \
93   _ (site_local, 0x5)                           \
94   _ (organization_local, 0x8)                   \
95   _ (global, 0xe)
96
97 #define foreach_ip6_multicast_link_local_group_id       \
98   _ (all_hosts, 0x1)                                    \
99   _ (all_routers, 0x2)                                  \
100   _ (rip_routers, 0x9)                                  \
101   _ (eigrp_routers, 0xa)                                \
102   _ (pim_routers, 0xd)                            \
103  _ (mldv2_routers, 0x16)
104
105 typedef enum
106 {
107 #define _(f,n) IP6_MULTICAST_SCOPE_##f = n,
108   foreach_ip6_multicast_address_scope
109 #undef _
110 } ip6_multicast_address_scope_t;
111
112 typedef enum
113 {
114 #define _(f,n) IP6_MULTICAST_GROUP_ID_##f = n,
115   foreach_ip6_multicast_link_local_group_id
116 #undef _
117 } ip6_multicast_link_local_group_id_t;
118
119 always_inline uword
120 ip6_address_is_multicast (const ip6_address_t * a)
121 {
122   return a->as_u8[0] == 0xff;
123 }
124
125 always_inline void
126 ip6_address_copy (ip6_address_t * dst, const ip6_address_t * src)
127 {
128   dst->as_u64[0] = src->as_u64[0];
129   dst->as_u64[1] = src->as_u64[1];
130 }
131
132 always_inline void
133 ip6_set_reserved_multicast_address (ip6_address_t * a,
134                                     ip6_multicast_address_scope_t scope,
135                                     u16 id)
136 {
137   a->as_u64[0] = a->as_u64[1] = 0;
138   a->as_u16[0] = clib_host_to_net_u16 (0xff00 | scope);
139   a->as_u16[7] = clib_host_to_net_u16 (id);
140 }
141
142 always_inline void
143 ip6_set_solicited_node_multicast_address (ip6_address_t * a, u32 id)
144 {
145   /* 0xff02::1:ffXX:XXXX. */
146   a->as_u64[0] = a->as_u64[1] = 0;
147   a->as_u16[0] = clib_host_to_net_u16 (0xff02);
148   a->as_u8[11] = 1;
149   ASSERT ((id >> 24) == 0);
150   id |= 0xff << 24;
151   a->as_u32[3] = clib_host_to_net_u32 (id);
152 }
153
154 always_inline void
155 ip6_multicast_ethernet_address (u8 * ethernet_address, u32 group_id)
156 {
157   ethernet_address[0] = 0x33;
158   ethernet_address[1] = 0x33;
159   ethernet_address[2] = ((group_id >> 24) & 0xff);
160   ethernet_address[3] = ((group_id >> 16) & 0xff);
161   ethernet_address[4] = ((group_id >> 8) & 0xff);
162   ethernet_address[5] = ((group_id >> 0) & 0xff);
163 }
164
165 always_inline uword
166 ip6_address_is_equal (const ip6_address_t * a, const ip6_address_t * b)
167 {
168   int i;
169   for (i = 0; i < ARRAY_LEN (a->as_uword); i++)
170     if (a->as_uword[i] != b->as_uword[i])
171       return 0;
172   return 1;
173 }
174
175 always_inline uword
176 ip6_address_is_equal_masked (const ip6_address_t * a,
177                              const ip6_address_t * b,
178                              const ip6_address_t * mask)
179 {
180   int i;
181   for (i = 0; i < ARRAY_LEN (a->as_uword); i++)
182     {
183       uword a_masked, b_masked;
184       a_masked = a->as_uword[i] & mask->as_uword[i];
185       b_masked = b->as_uword[i] & mask->as_uword[i];
186
187       if (a_masked != b_masked)
188         return 0;
189     }
190   return 1;
191 }
192
193 always_inline void
194 ip6_address_mask (ip6_address_t * a, const ip6_address_t * mask)
195 {
196   int i;
197   for (i = 0; i < ARRAY_LEN (a->as_uword); i++)
198     a->as_uword[i] &= mask->as_uword[i];
199 }
200
201 always_inline void
202 ip6_address_set_zero (ip6_address_t * a)
203 {
204   int i;
205   for (i = 0; i < ARRAY_LEN (a->as_uword); i++)
206     a->as_uword[i] = 0;
207 }
208
209 always_inline void
210 ip6_address_mask_from_width (ip6_address_t * a, u32 width)
211 {
212   int i, byte, bit, bitnum;
213   ASSERT (width <= 128);
214   clib_memset (a, 0, sizeof (a[0]));
215   for (i = 0; i < width; i++)
216     {
217       bitnum = (7 - (i & 7));
218       byte = i / 8;
219       bit = 1 << bitnum;
220       a->as_u8[byte] |= bit;
221     }
222 }
223
224 always_inline uword
225 ip6_address_is_zero (const ip6_address_t * a)
226 {
227   int i;
228   for (i = 0; i < ARRAY_LEN (a->as_uword); i++)
229     if (a->as_uword[i] != 0)
230       return 0;
231   return 1;
232 }
233
234 /* Check for unspecified address ::0 */
235 always_inline uword
236 ip6_address_is_unspecified (const ip6_address_t * a)
237 {
238   return ip6_address_is_zero (a);
239 }
240
241 /* Check for loopback address ::1 */
242 always_inline uword
243 ip6_address_is_loopback (const ip6_address_t * a)
244 {
245   return (a->as_u64[0] == 0 &&
246           a->as_u32[2] == 0 &&
247           a->as_u16[6] == 0 && a->as_u8[14] == 0 && a->as_u8[15] == 1);
248 }
249
250 /* Check for link local unicast fe80::/10. */
251 always_inline uword
252 ip6_address_is_link_local_unicast (const ip6_address_t * a)
253 {
254   return a->as_u8[0] == 0xfe && (a->as_u8[1] & 0xc0) == 0x80;
255 }
256
257 /* Check for unique local unicast fc00::/7. */
258 always_inline uword
259 ip6_address_is_local_unicast (const ip6_address_t * a)
260 {
261   return (a->as_u8[0] & 0xfe) == 0xfc;
262 }
263
264 /* Check for unique global unicast 2000::/3. */
265 always_inline uword
266 ip6_address_is_global_unicast (const ip6_address_t * a)
267 {
268   return (a->as_u8[0] & 0xe0) == 0x20;
269 }
270
271 /* Check for solicited node multicast 0xff02::1:ff00:0/104 */
272 always_inline uword
273 ip6_is_solicited_node_multicast_address (const ip6_address_t * a)
274 {
275   return (a->as_u32[0] == clib_host_to_net_u32 (0xff020000)
276           && a->as_u32[1] == 0
277           && a->as_u32[2] == clib_host_to_net_u32 (1)
278           && a->as_u8[12] == 0xff);
279 }
280
281 always_inline u32
282 ip6_address_hash_to_u32 (const ip6_address_t * a)
283 {
284   return (a->as_u32[0] ^ a->as_u32[1] ^ a->as_u32[2] ^ a->as_u32[3]);
285 }
286
287 always_inline u64
288 ip6_address_hash_to_u64 (const ip6_address_t * a)
289 {
290   return (a->as_u64[0] ^ a->as_u64[1]);
291 }
292
293 typedef struct
294 {
295   /* 4 bit version, 8 bit traffic class and 20 bit flow label. */
296   u32 ip_version_traffic_class_and_flow_label;
297
298   /* Total packet length not including this header (but including
299      any extension headers if present). */
300   u16 payload_length;
301
302   /* Protocol for next header. */
303   u8 protocol;
304
305   /* Hop limit decremented by router at each hop. */
306   u8 hop_limit;
307
308   /* Source and destination address. */
309   ip6_address_t src_address, dst_address;
310 } ip6_header_t;
311
312 #define IP6_PACKET_TC_MASK 0x0FF00000
313 #define IP6_PACKET_DSCP_MASK 0x0FC00000
314 #define IP6_PACKET_ECN_MASK 0x00300000
315 #define IP6_PACKET_FL_MASK   0x000FFFFF
316
317 always_inline ip_dscp_t
318 ip6_traffic_class (const ip6_header_t * i)
319 {
320   return (i->ip_version_traffic_class_and_flow_label & IP6_PACKET_TC_MASK) >>
321     20;
322 }
323
324 static_always_inline ip_dscp_t
325 ip6_traffic_class_network_order (const ip6_header_t * ip6)
326 {
327   return (clib_net_to_host_u32 (ip6->ip_version_traffic_class_and_flow_label)
328           & IP6_PACKET_TC_MASK) >> 20;
329 }
330
331 static_always_inline ip_dscp_t
332 ip6_dscp_network_order (const ip6_header_t * ip6)
333 {
334   return (clib_net_to_host_u32 (ip6->ip_version_traffic_class_and_flow_label)
335           & IP6_PACKET_DSCP_MASK) >> 22;
336 }
337
338 static_always_inline ip_ecn_t
339 ip6_ecn_network_order (const ip6_header_t * ip6)
340 {
341   return (clib_net_to_host_u32 (ip6->ip_version_traffic_class_and_flow_label)
342           & IP6_PACKET_ECN_MASK) >> 20;
343 }
344
345 static_always_inline void
346 ip6_set_traffic_class_network_order (ip6_header_t * ip6, ip_dscp_t dscp)
347 {
348   u32 tmp =
349     clib_net_to_host_u32 (ip6->ip_version_traffic_class_and_flow_label);
350   tmp &= 0xf00fffff;
351   tmp |= (dscp << 20);
352   ip6->ip_version_traffic_class_and_flow_label = clib_host_to_net_u32 (tmp);
353 }
354
355 static_always_inline void
356 ip6_set_dscp_network_order (ip6_header_t * ip6, ip_dscp_t dscp)
357 {
358   u32 tmp =
359     clib_net_to_host_u32 (ip6->ip_version_traffic_class_and_flow_label);
360   tmp &= 0xf03fffff;
361   tmp |= (dscp << 22);
362   ip6->ip_version_traffic_class_and_flow_label = clib_host_to_net_u32 (tmp);
363 }
364
365 static_always_inline void
366 ip6_set_ecn_network_order (ip6_header_t * ip6, ip_ecn_t ecn)
367 {
368   u32 tmp =
369     clib_net_to_host_u32 (ip6->ip_version_traffic_class_and_flow_label);
370   tmp &= 0xffcfffff;
371   tmp |= ((0x3 & ecn) << 20);
372   ip6->ip_version_traffic_class_and_flow_label = clib_host_to_net_u32 (tmp);
373 }
374
375 static_always_inline u32
376 ip6_flow_label_network_order (const ip6_header_t *ip6)
377 {
378   u32 tmp =
379     clib_net_to_host_u32 (ip6->ip_version_traffic_class_and_flow_label);
380   return (tmp & 0xfffff);
381 }
382
383 static_always_inline void
384 ip6_set_flow_label_network_order (ip6_header_t *ip6, u32 flow_label)
385 {
386   u32 tmp =
387     clib_net_to_host_u32 (ip6->ip_version_traffic_class_and_flow_label);
388   tmp &= 0xfff00000;
389   tmp |= flow_label & 0x000fffff;
390   ip6->ip_version_traffic_class_and_flow_label = clib_host_to_net_u32 (tmp);
391 }
392
393 static_always_inline u32
394 ip6_hop_limit_network_order (const ip6_header_t *ip6)
395 {
396   return (ip6->hop_limit);
397 }
398
399 static_always_inline void
400 ip6_set_hop_limit_network_order (ip6_header_t *ip6, u8 hop_limit)
401 {
402   ip6->hop_limit = hop_limit;
403 }
404
405 always_inline void *
406 ip6_next_header (ip6_header_t * i)
407 {
408   return (void *) (i + 1);
409 }
410
411 always_inline void
412 ip6_copy_header (ip6_header_t * dst, const ip6_header_t * src)
413 {
414   dst->ip_version_traffic_class_and_flow_label =
415     src->ip_version_traffic_class_and_flow_label;
416   dst->payload_length = src->payload_length;
417   dst->protocol = src->protocol;
418   dst->hop_limit = src->hop_limit;
419
420   dst->src_address.as_uword[0] = src->src_address.as_uword[0];
421   dst->src_address.as_uword[1] = src->src_address.as_uword[1];
422   dst->dst_address.as_uword[0] = src->dst_address.as_uword[0];
423   dst->dst_address.as_uword[1] = src->dst_address.as_uword[1];
424 }
425
426 always_inline void
427 ip6_tcp_reply_x1 (ip6_header_t * ip0, tcp_header_t * tcp0)
428 {
429   {
430     ip6_address_t src0, dst0;
431
432     src0 = ip0->src_address;
433     dst0 = ip0->dst_address;
434     ip0->src_address = dst0;
435     ip0->dst_address = src0;
436   }
437
438   {
439     u16 src0, dst0;
440
441     src0 = tcp0->src;
442     dst0 = tcp0->dst;
443     tcp0->src = dst0;
444     tcp0->dst = src0;
445   }
446 }
447
448 always_inline void
449 ip6_tcp_reply_x2 (ip6_header_t * ip0, ip6_header_t * ip1,
450                   tcp_header_t * tcp0, tcp_header_t * tcp1)
451 {
452   {
453     ip6_address_t src0, dst0, src1, dst1;
454
455     src0 = ip0->src_address;
456     src1 = ip1->src_address;
457     dst0 = ip0->dst_address;
458     dst1 = ip1->dst_address;
459     ip0->src_address = dst0;
460     ip1->src_address = dst1;
461     ip0->dst_address = src0;
462     ip1->dst_address = src1;
463   }
464
465   {
466     u16 src0, dst0, src1, dst1;
467
468     src0 = tcp0->src;
469     src1 = tcp1->src;
470     dst0 = tcp0->dst;
471     dst1 = tcp1->dst;
472     tcp0->src = dst0;
473     tcp1->src = dst1;
474     tcp0->dst = src0;
475     tcp1->dst = src1;
476   }
477 }
478
479 typedef CLIB_PACKED (struct {
480   u8 data;
481 }) ip6_pad1_option_t;
482
483 typedef CLIB_PACKED (struct {
484   u8 type;
485   u8 len;
486   u8 data[0];
487 }) ip6_padN_option_t;
488
489 typedef CLIB_PACKED (struct {
490 #define IP6_MLDP_ALERT_TYPE  0x5
491   u8 type;
492   u8 len;
493   u16 value;
494 }) ip6_router_alert_option_t;
495
496 typedef CLIB_PACKED (struct {
497   u8 next_hdr;
498   /* Length of this header plus option data in 8 byte units. */
499   u8 n_data_u64s;
500 }) ip6_ext_header_t;
501
502 #define foreach_ext_hdr_type \
503   _(IP6_HOP_BY_HOP_OPTIONS) \
504   _(IPV6_ROUTE) \
505   _(IP6_DESTINATION_OPTIONS) \
506   _(MOBILITY) \
507   _(HIP) \
508   _(SHIM6)
509
510 always_inline u8
511 ip6_ext_hdr (u8 nexthdr)
512 {
513 #ifdef CLIB_HAVE_VEC128
514   static const u8x16 ext_hdr_types = {
515 #define _(x) IP_PROTOCOL_##x,
516     foreach_ext_hdr_type
517 #undef _
518   };
519
520   return !u8x16_is_all_zero (ext_hdr_types == u8x16_splat (nexthdr));
521 #else
522   /*
523    * find out if nexthdr is an extension header or a protocol
524    */
525   return 0
526 #define _(x) || (nexthdr == IP_PROTOCOL_##x)
527     foreach_ext_hdr_type;
528 #undef _
529 #endif
530 }
531
532 typedef CLIB_PACKED (struct {
533   u8 next_hdr;
534   /* Length of this header plus option data in 8 byte units. */
535   u8 n_data_u64s;
536   u8 data[0];
537 }) ip6_hop_by_hop_ext_t;
538
539 typedef CLIB_PACKED (struct {
540   u8 next_hdr;
541   u8 rsv;
542   u16 fragment_offset_and_more;
543   u32 identification;
544 }) ip6_frag_hdr_t;
545
546 #define ip6_frag_hdr_offset(hdr)                                              \
547   (clib_net_to_host_u16 ((hdr)->fragment_offset_and_more) >> 3)
548
549 #define ip6_frag_hdr_offset_bytes(hdr) (8 * ip6_frag_hdr_offset (hdr))
550
551 #define ip6_frag_hdr_more(hdr)                                                \
552   (clib_net_to_host_u16 ((hdr)->fragment_offset_and_more) & 0x1)
553
554 #define ip6_frag_hdr_offset_and_more(offset, more)                            \
555   clib_host_to_net_u16 (((offset) << 3) + !!(more))
556
557 #define ip6_ext_header_len(p)  ((((ip6_ext_header_t *)(p))->n_data_u64s+1) << 3)
558 #define ip6_ext_authhdr_len(p) ((((ip6_ext_header_t *)(p))->n_data_u64s+2) << 2)
559
560 static inline int
561 ip6_ext_header_len_s (ip_protocol_t nh, void *p)
562 {
563   if (ip6_ext_hdr (nh))
564     return ip6_ext_header_len (p);
565   switch (nh)
566     {
567     case IP_PROTOCOL_IPSEC_AH:
568       return ip6_ext_authhdr_len (p);
569     case IP_PROTOCOL_IPV6_FRAGMENTATION:
570       return sizeof (ip6_frag_hdr_t);
571     case IP_PROTOCOL_ICMP6:
572       return 4;
573     case IP_PROTOCOL_UDP:
574       return 8;
575     case IP_PROTOCOL_TCP:
576       return 20;
577     default: /* Caller is responsible for validating the length of terminating
578              protocols */
579              ;
580     }
581   return 0;
582 }
583
584 always_inline void *
585 ip6_ext_next_header (ip6_ext_header_t * ext_hdr)
586 {
587   return (void *) ((u8 *) ext_hdr + ip6_ext_header_len (ext_hdr));
588 }
589
590 always_inline void *
591 ip6_ext_next_header_offset (void *hdr, u16 offset)
592 {
593   return (hdr + offset);
594 }
595
596 always_inline int
597 vlib_object_within_buffer_data (vlib_main_t * vm, vlib_buffer_t * b,
598                                 void *obj, size_t len)
599 {
600   u8 *o = obj;
601   if (o < b->data ||
602       o + len > b->data + vlib_buffer_get_default_data_size (vm))
603     return 0;
604   return 1;
605 }
606
607 /* Returns the number of bytes left in buffer from p. */
608 static inline u32
609 vlib_bytes_left_in_buffer (vlib_buffer_t *b, void *obj)
610 {
611   return b->current_length - (((u8 *) obj - b->data) - b->current_data);
612 }
613
614 always_inline void *
615 ip6_ext_next_header_s (ip_protocol_t cur_nh, void *hdr, u32 max_offset,
616                        u32 *offset, int *res_nh, bool *last)
617 {
618   u16 hdrlen = 0;
619   int new_nh = -1;
620   void *res = 0;
621   if (ip6_ext_hdr (cur_nh))
622     {
623       hdrlen = ip6_ext_header_len (hdr);
624       new_nh = ((ip6_ext_header_t *) hdr)->next_hdr;
625       res = hdr + hdrlen;
626     }
627   else if (cur_nh == IP_PROTOCOL_IPV6_FRAGMENTATION)
628     {
629       ip6_frag_hdr_t *frag_hdr = (ip6_frag_hdr_t *) hdr;
630       if (ip6_frag_hdr_offset (frag_hdr) > 0)
631         *last = true;
632       new_nh = frag_hdr->next_hdr;
633       hdrlen = sizeof (ip6_frag_hdr_t);
634       res = hdr + hdrlen;
635     }
636   else if (cur_nh == IP_PROTOCOL_IPSEC_AH)
637     {
638       new_nh = ((ip6_ext_header_t *) hdr)->next_hdr;
639       hdrlen = ip6_ext_authhdr_len (hdr);
640       res = hdr + hdrlen;
641     }
642   else
643     {
644       ;
645     }
646
647   if (res && (*offset + hdrlen) >= max_offset)
648     {
649       return 0;
650     }
651   *res_nh = new_nh;
652   *offset += hdrlen;
653   return res;
654 }
655
656 #define IP6_EXT_HDR_MAX       (4)   /* Maximum number of headers */
657 #define IP6_EXT_HDR_MAX_DEPTH (256) /* Maximum header depth */
658 typedef struct
659 {
660   int length;
661   struct
662   {
663     u16 protocol;
664     u16 offset;
665   } eh[IP6_EXT_HDR_MAX];
666 } ip6_ext_hdr_chain_t;
667
668 /*
669  * Find ipv6 extension header within ipv6 header within
670  * whichever is smallest of buffer or IP6_EXT_HDR_MAX_DEPTH.
671  * The complete header chain must be in first buffer.
672  *
673  * The complete header chain (up to the terminating header) is
674  * returned in res.
675  * Returns the index of the find_hdr_type if > 0. Otherwise
676  * it returns the index of the last header.
677  */
678 always_inline int
679 ip6_ext_header_walk (vlib_buffer_t *b, ip6_header_t *ip, int find_hdr_type,
680                      ip6_ext_hdr_chain_t *res)
681 {
682   int i = 0;
683   int found = -1;
684   void *next_header = ip6_next_header (ip);
685   int next_proto = ip->protocol;
686   res->length = 0;
687   u32 n_bytes_this_buffer =
688     clib_min (vlib_bytes_left_in_buffer (b, ip), IP6_EXT_HDR_MAX_DEPTH);
689   u32 max_offset = clib_min (n_bytes_this_buffer,
690                              sizeof (ip6_header_t) +
691                                clib_net_to_host_u16 (ip->payload_length));
692   u32 offset = sizeof (ip6_header_t);
693   if ((ip6_ext_header_len_s (ip->protocol, next_header) + offset) > max_offset)
694     {
695       return -1;
696     }
697   bool last = false;
698   while (next_header)
699     {
700       /* Move on to next header */
701       res->eh[i].offset = offset;
702       res->eh[i].protocol = next_proto;
703       if (next_proto == find_hdr_type)
704         found = i;
705       i++;
706       if (last)
707         break;
708       if (i > IP6_EXT_HDR_MAX)
709         break;
710       next_header = ip6_ext_next_header_s (next_proto, next_header, max_offset,
711                                            &offset, &next_proto, &last);
712     }
713   res->length = i;
714   if (find_hdr_type < 0)
715     return i - 1;
716   return found != -1 ? found : i - 1;
717 }
718
719 always_inline void *
720 ip6_ext_header_find (vlib_main_t *vm, vlib_buffer_t *b, ip6_header_t *ip,
721                      int find_hdr_type, ip6_ext_header_t **prev_ext_header)
722 {
723   ip6_ext_hdr_chain_t hdr_chain;
724   int res = ip6_ext_header_walk (b, ip, find_hdr_type, &hdr_chain);
725   if (res < 0)
726     return 0;
727
728   if (prev_ext_header)
729     {
730       if (res > 0)
731         {
732           *prev_ext_header =
733             ip6_ext_next_header_offset (ip, hdr_chain.eh[res - 1].offset);
734         }
735       else
736         {
737           *prev_ext_header = 0;
738         }
739     }
740   if (find_hdr_type == hdr_chain.eh[res].protocol)
741     return ip6_ext_next_header_offset (ip, hdr_chain.eh[res].offset);
742   return 0;
743 }
744
745 #endif /* included_ip6_packet_h */
746
747 /*
748  * fd.io coding-style-patch-verification: ON
749  *
750  * Local Variables:
751  * eval: (c-set-style "gnu")
752  * End:
753  */