docs: Use newer Ubuntu LTS in tutorial
[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 <vlib/vlib.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 typedef CLIB_PACKED (struct {
427   u8 data;
428 }) ip6_pad1_option_t;
429
430 typedef CLIB_PACKED (struct {
431   u8 type;
432   u8 len;
433   u8 data[0];
434 }) ip6_padN_option_t;
435
436 typedef CLIB_PACKED (struct {
437 #define IP6_MLDP_ALERT_TYPE  0x5
438   u8 type;
439   u8 len;
440   u16 value;
441 }) ip6_router_alert_option_t;
442
443 typedef CLIB_PACKED (struct {
444   u8 protocol;
445   u8 reserved;
446   u16 fragoff;
447   u32 id;
448 }) ip6_fragment_ext_header_t;
449
450 typedef CLIB_PACKED (struct {
451   u8 next_hdr;
452   /* Length of this header plus option data in 8 byte units. */
453   u8 n_data_u64s;
454 }) ip6_ext_header_t;
455
456 #define foreach_ext_hdr_type \
457   _(IP6_HOP_BY_HOP_OPTIONS) \
458   _(IPV6_ROUTE) \
459   _(IP6_DESTINATION_OPTIONS) \
460   _(MOBILITY) \
461   _(HIP) \
462   _(SHIM6)
463
464 always_inline u8
465 ip6_ext_hdr (u8 nexthdr)
466 {
467 #ifdef CLIB_HAVE_VEC128
468   static const u8x16 ext_hdr_types = {
469 #define _(x) IP_PROTOCOL_##x,
470     foreach_ext_hdr_type
471 #undef _
472   };
473
474   return !u8x16_is_all_zero (ext_hdr_types == u8x16_splat (nexthdr));
475 #else
476   /*
477    * find out if nexthdr is an extension header or a protocol
478    */
479   return 0
480 #define _(x) || (nexthdr == IP_PROTOCOL_##x)
481     foreach_ext_hdr_type;
482 #undef _
483 #endif
484 }
485
486 typedef CLIB_PACKED (struct {
487   u8 next_hdr;
488   /* Length of this header plus option data in 8 byte units. */
489   u8 n_data_u64s;
490   u8 data[0];
491 }) ip6_hop_by_hop_ext_t;
492
493 typedef CLIB_PACKED (struct {
494   u8 next_hdr;
495   u8 rsv;
496   u16 fragment_offset_and_more;
497   u32 identification;
498 }) ip6_frag_hdr_t;
499
500 #define ip6_frag_hdr_offset(hdr)                                              \
501   (clib_net_to_host_u16 ((hdr)->fragment_offset_and_more) >> 3)
502
503 #define ip6_frag_hdr_offset_bytes(hdr) (8 * ip6_frag_hdr_offset (hdr))
504
505 #define ip6_frag_hdr_more(hdr)                                                \
506   (clib_net_to_host_u16 ((hdr)->fragment_offset_and_more) & 0x1)
507
508 #define ip6_frag_hdr_offset_and_more(offset, more)                            \
509   clib_host_to_net_u16 (((offset) << 3) + !!(more))
510
511 #define ip6_ext_header_len(p)  ((((ip6_ext_header_t *)(p))->n_data_u64s+1) << 3)
512 #define ip6_ext_authhdr_len(p) ((((ip6_ext_header_t *)(p))->n_data_u64s+2) << 2)
513
514 static inline int
515 ip6_ext_header_len_s (ip_protocol_t nh, void *p)
516 {
517   if (ip6_ext_hdr (nh))
518     return ip6_ext_header_len (p);
519   switch (nh)
520     {
521     case IP_PROTOCOL_IPSEC_AH:
522       return ip6_ext_authhdr_len (p);
523     case IP_PROTOCOL_IPV6_FRAGMENTATION:
524       return sizeof (ip6_frag_hdr_t);
525     case IP_PROTOCOL_ICMP6:
526       return 4;
527     case IP_PROTOCOL_UDP:
528       return 8;
529     case IP_PROTOCOL_TCP:
530       return 20;
531     default: /* Caller is responsible for validating the length of terminating
532              protocols */
533              ;
534     }
535   return 0;
536 }
537
538 always_inline void *
539 ip6_ext_next_header (ip6_ext_header_t * ext_hdr)
540 {
541   return (void *) ((u8 *) ext_hdr + ip6_ext_header_len (ext_hdr));
542 }
543
544 always_inline void *
545 ip6_ext_next_header_offset (void *hdr, u16 offset)
546 {
547   return (hdr + offset);
548 }
549
550 always_inline int
551 vlib_object_within_buffer_data (vlib_main_t * vm, vlib_buffer_t * b,
552                                 void *obj, size_t len)
553 {
554   u8 *o = obj;
555   if (o < b->data ||
556       o + len > b->data + vlib_buffer_get_default_data_size (vm))
557     return 0;
558   return 1;
559 }
560
561 /* Returns the number of bytes left in buffer from p. */
562 static inline u32
563 vlib_bytes_left_in_buffer (vlib_buffer_t *b, void *obj)
564 {
565   return b->current_length - (((u8 *) obj - b->data) - b->current_data);
566 }
567
568 always_inline void *
569 ip6_ext_next_header_s (ip_protocol_t cur_nh, void *hdr, u32 max_offset,
570                        u32 *offset, int *res_nh, bool *last)
571 {
572   u16 hdrlen = 0;
573   int new_nh = -1;
574   void *res = 0;
575   if (ip6_ext_hdr (cur_nh))
576     {
577       hdrlen = ip6_ext_header_len (hdr);
578       new_nh = ((ip6_ext_header_t *) hdr)->next_hdr;
579       res = hdr + hdrlen;
580     }
581   else if (cur_nh == IP_PROTOCOL_IPV6_FRAGMENTATION)
582     {
583       ip6_frag_hdr_t *frag_hdr = (ip6_frag_hdr_t *) hdr;
584       if (ip6_frag_hdr_offset (frag_hdr) > 0)
585         *last = true;
586       new_nh = frag_hdr->next_hdr;
587       hdrlen = sizeof (ip6_frag_hdr_t);
588       res = hdr + hdrlen;
589     }
590   else if (cur_nh == IP_PROTOCOL_IPSEC_AH)
591     {
592       new_nh = ((ip6_ext_header_t *) hdr)->next_hdr;
593       hdrlen = ip6_ext_authhdr_len (hdr);
594       res = hdr + hdrlen;
595     }
596   else
597     {
598       ;
599     }
600
601   if (res && (*offset + hdrlen) >= max_offset)
602     {
603       return 0;
604     }
605   *res_nh = new_nh;
606   *offset += hdrlen;
607   return res;
608 }
609
610 #define IP6_EXT_HDR_MAX       (4)   /* Maximum number of headers */
611 #define IP6_EXT_HDR_MAX_DEPTH (256) /* Maximum header depth */
612 typedef struct
613 {
614   int length;
615   struct
616   {
617     u16 protocol;
618     u16 offset;
619   } eh[IP6_EXT_HDR_MAX];
620 } ip6_ext_hdr_chain_t;
621
622 /*
623  * Find ipv6 extension header within ipv6 header within
624  * whichever is smallest of buffer or IP6_EXT_HDR_MAX_DEPTH.
625  * The complete header chain must be in first buffer.
626  *
627  * The complete header chain (up to the terminating header) is
628  * returned in res.
629  * Returns the index of the find_hdr_type if > 0. Otherwise
630  * it returns the index of the last header.
631  */
632 always_inline int
633 ip6_ext_header_walk (vlib_buffer_t *b, ip6_header_t *ip, int find_hdr_type,
634                      ip6_ext_hdr_chain_t *res)
635 {
636   int i = 0;
637   int found = -1;
638   void *next_header = ip6_next_header (ip);
639   int next_proto = ip->protocol;
640   res->length = 0;
641   u32 n_bytes_this_buffer =
642     clib_min (vlib_bytes_left_in_buffer (b, ip), IP6_EXT_HDR_MAX_DEPTH);
643   u32 max_offset = clib_min (n_bytes_this_buffer,
644                              sizeof (ip6_header_t) +
645                                clib_net_to_host_u16 (ip->payload_length));
646   u32 offset = sizeof (ip6_header_t);
647   if ((ip6_ext_header_len_s (ip->protocol, next_header) + offset) > max_offset)
648     {
649       return -1;
650     }
651   bool last = false;
652   while (next_header)
653     {
654       /* Move on to next header */
655       res->eh[i].offset = offset;
656       res->eh[i].protocol = next_proto;
657       if (next_proto == find_hdr_type)
658         found = i;
659       i++;
660       if (last)
661         break;
662       if (i >= IP6_EXT_HDR_MAX)
663         break;
664       next_header = ip6_ext_next_header_s (next_proto, next_header, max_offset,
665                                            &offset, &next_proto, &last);
666     }
667   res->length = i;
668   if (find_hdr_type < 0)
669     return i - 1;
670   return found != -1 ? found : i - 1;
671 }
672
673 always_inline void *
674 ip6_ext_header_find (vlib_main_t *vm, vlib_buffer_t *b, ip6_header_t *ip,
675                      int find_hdr_type, ip6_ext_header_t **prev_ext_header)
676 {
677   ip6_ext_hdr_chain_t hdr_chain;
678   int res = ip6_ext_header_walk (b, ip, find_hdr_type, &hdr_chain);
679   if (res < 0)
680     return 0;
681
682   if (prev_ext_header)
683     {
684       if (res > 0)
685         {
686           *prev_ext_header =
687             ip6_ext_next_header_offset (ip, hdr_chain.eh[res - 1].offset);
688         }
689       else
690         {
691           *prev_ext_header = 0;
692         }
693     }
694   if (find_hdr_type == hdr_chain.eh[res].protocol)
695     return ip6_ext_next_header_offset (ip, hdr_chain.eh[res].offset);
696   return 0;
697 }
698
699 #endif /* included_ip6_packet_h */
700
701 /*
702  * fd.io coding-style-patch-verification: ON
703  *
704  * Local Variables:
705  * eval: (c-set-style "gnu")
706  * End:
707  */