LCOV - code coverage report
Current view: top level - vnet/ip - ip4_to_ip6.h (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 142 220 64.5 %
Date: 2023-10-26 01:39:38 Functions: 3 3 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2017 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             :  * @file
      17             :  * @brief IPv4 to IPv6 translation
      18             :  */
      19             : #ifndef __included_ip4_to_ip6_h__
      20             : #define __included_ip4_to_ip6_h__
      21             : 
      22             : #include <vnet/ip/ip.h>
      23             : 
      24             : 
      25             : /**
      26             :  * IPv4 to IPv6 set call back function type
      27             :  */
      28             : typedef int (*ip4_to_ip6_set_fn_t) (vlib_buffer_t * b, ip4_header_t * ip4,
      29             :                                     ip6_header_t * ip6, void *ctx);
      30             : 
      31             : /* *INDENT-OFF* */
      32             : static u8 icmp_to_icmp6_updater_pointer_table[] =
      33             :   { 0, 1, 4, 4, ~0,
      34             :     ~0, ~0, ~0, 7, 6,
      35             :     ~0, ~0, 8, 8, 8,
      36             :     8, 24, 24, 24, 24
      37             :   };
      38             : /* *INDENT-ON* */
      39             : 
      40             : #define frag_id_4to6(id) (id)
      41             : 
      42             : /**
      43             :  * @brief Get TCP/UDP port number or ICMP id from IPv4 packet.
      44             :  *
      45             :  * @param ip4        IPv4 header.
      46             :  * @param sender     1 get sender port, 0 get receiver port.
      47             :  *
      48             :  * @returns Port number on success, 0 otherwise.
      49             :  */
      50             : always_inline u16
      51       89785 : ip4_get_port (ip4_header_t * ip, u8 sender)
      52             : {
      53      179604 :   if (ip->ip_version_and_header_length != 0x45 ||
      54       89788 :       ip4_get_fragment_offset (ip))
      55           0 :     return 0;
      56             : 
      57       89816 :   if (PREDICT_TRUE ((ip->protocol == IP_PROTOCOL_TCP) ||
      58             :                     (ip->protocol == IP_PROTOCOL_UDP)))
      59             :     {
      60       73415 :       udp_header_t *udp = (void *) (ip + 1);
      61       73415 :       return (sender) ? udp->src_port : udp->dst_port;
      62             :     }
      63       16401 :   else if (ip->protocol == IP_PROTOCOL_ICMP)
      64             :     {
      65       16375 :       icmp46_header_t *icmp = (void *) (ip + 1);
      66       16375 :       if (icmp->type == ICMP4_echo_request || icmp->type == ICMP4_echo_reply)
      67             :         {
      68       16328 :           return *((u16 *) (icmp + 1));
      69             :         }
      70          47 :       else if (clib_net_to_host_u16 (ip->length) >= 64)
      71             :         {
      72          35 :           ip = (ip4_header_t *) (icmp + 2);
      73          35 :           if (PREDICT_TRUE ((ip->protocol == IP_PROTOCOL_TCP) ||
      74             :                             (ip->protocol == IP_PROTOCOL_UDP)))
      75             :             {
      76          31 :               udp_header_t *udp = (void *) (ip + 1);
      77          31 :               return (sender) ? udp->dst_port : udp->src_port;
      78             :             }
      79           4 :           else if (ip->protocol == IP_PROTOCOL_ICMP)
      80             :             {
      81           4 :               icmp46_header_t *icmp = (void *) (ip + 1);
      82           4 :               if (icmp->type == ICMP4_echo_request ||
      83           0 :                   icmp->type == ICMP4_echo_reply)
      84             :                 {
      85           4 :                   return *((u16 *) (icmp + 1));
      86             :                 }
      87             :             }
      88             :         }
      89             :     }
      90          38 :   return 0;
      91             : }
      92             : 
      93             : /**
      94             :  * @brief Convert type and code value from ICMP4 to ICMP6.
      95             :  *
      96             :  * @param icmp      ICMP header.
      97             :  * @param inner_ip4 Inner IPv4 header if present, 0 otherwise.
      98             :  *
      99             :  * @returns 0 on success, non-zero value otherwise.
     100             :  */
     101             : always_inline int
     102          15 : icmp_to_icmp6_header (icmp46_header_t * icmp, ip4_header_t ** inner_ip4)
     103             : {
     104          15 :   *inner_ip4 = NULL;
     105          15 :   switch (icmp->type)
     106             :     {
     107           8 :     case ICMP4_echo_reply:
     108           8 :       icmp->type = ICMP6_echo_reply;
     109           8 :       break;
     110           3 :     case ICMP4_echo_request:
     111           3 :       icmp->type = ICMP6_echo_request;
     112           3 :       break;
     113           3 :     case ICMP4_destination_unreachable:
     114           3 :       *inner_ip4 = (ip4_header_t *) (((u8 *) icmp) + 8);
     115             : 
     116           3 :       switch (icmp->code)
     117             :         {
     118           0 :         case ICMP4_destination_unreachable_destination_unreachable_net: //0
     119             :         case ICMP4_destination_unreachable_destination_unreachable_host:        //1
     120           0 :           icmp->type = ICMP6_destination_unreachable;
     121           0 :           icmp->code = ICMP6_destination_unreachable_no_route_to_destination;
     122           0 :           break;
     123           0 :         case ICMP4_destination_unreachable_protocol_unreachable:        //2
     124           0 :           icmp->type = ICMP6_parameter_problem;
     125           0 :           icmp->code = ICMP6_parameter_problem_unrecognized_next_header;
     126           0 :           break;
     127           0 :         case ICMP4_destination_unreachable_port_unreachable:    //3
     128           0 :           icmp->type = ICMP6_destination_unreachable;
     129           0 :           icmp->code = ICMP6_destination_unreachable_port_unreachable;
     130           0 :           break;
     131           0 :         case ICMP4_destination_unreachable_fragmentation_needed_and_dont_fragment_set:  //4
     132           0 :           icmp->type =
     133             :             ICMP6_packet_too_big;
     134           0 :           icmp->code = 0;
     135             :           {
     136           0 :             u32 advertised_mtu = clib_net_to_host_u32 (*((u32 *) (icmp + 1)));
     137           0 :             if (advertised_mtu)
     138           0 :               advertised_mtu += 20;
     139             :             else
     140           0 :               advertised_mtu = 1000;    //FIXME ! (RFC 1191 - plateau value)
     141             : 
     142             :             //FIXME: = minimum(advertised MTU+20, MTU_of_IPv6_nexthop, (MTU_of_IPv4_nexthop)+20)
     143           0 :             *((u32 *) (icmp + 1)) = clib_host_to_net_u32 (advertised_mtu);
     144             :           }
     145           0 :           break;
     146             : 
     147           0 :         case ICMP4_destination_unreachable_source_route_failed: //5
     148             :         case ICMP4_destination_unreachable_destination_network_unknown: //6
     149             :         case ICMP4_destination_unreachable_destination_host_unknown:    //7
     150             :         case ICMP4_destination_unreachable_source_host_isolated:        //8
     151             :         case ICMP4_destination_unreachable_network_unreachable_for_type_of_service:     //11
     152             :         case ICMP4_destination_unreachable_host_unreachable_for_type_of_service:        //12
     153           0 :           icmp->type =
     154             :             ICMP6_destination_unreachable;
     155           0 :           icmp->code = ICMP6_destination_unreachable_no_route_to_destination;
     156           0 :           break;
     157           3 :         case ICMP4_destination_unreachable_network_administratively_prohibited: //9
     158             :         case ICMP4_destination_unreachable_host_administratively_prohibited:    //10
     159             :         case ICMP4_destination_unreachable_communication_administratively_prohibited:   //13
     160             :         case ICMP4_destination_unreachable_precedence_cutoff_in_effect: //15
     161           3 :           icmp->type = ICMP6_destination_unreachable;
     162           3 :           icmp->code =
     163             :             ICMP6_destination_unreachable_destination_administratively_prohibited;
     164           3 :           break;
     165           0 :         case ICMP4_destination_unreachable_host_precedence_violation:   //14
     166             :         default:
     167           0 :           return -1;
     168             :         }
     169           3 :       break;
     170             : 
     171           1 :     case ICMP4_time_exceeded:   //11
     172           1 :       *inner_ip4 = (ip4_header_t *) (((u8 *) icmp) + 8);
     173           1 :       icmp->type = ICMP6_time_exceeded;
     174           1 :       break;
     175             : 
     176           0 :     case ICMP4_parameter_problem:
     177           0 :       *inner_ip4 = (ip4_header_t *) (((u8 *) icmp) + 8);
     178             : 
     179           0 :       switch (icmp->code)
     180             :         {
     181           0 :         case ICMP4_parameter_problem_pointer_indicates_error:
     182             :         case ICMP4_parameter_problem_bad_length:
     183           0 :           icmp->type = ICMP6_parameter_problem;
     184           0 :           icmp->code = ICMP6_parameter_problem_erroneous_header_field;
     185             :           {
     186           0 :             u8 ptr =
     187           0 :               icmp_to_icmp6_updater_pointer_table[*((u8 *) (icmp + 1))];
     188           0 :             if (ptr == 0xff)
     189           0 :               return -1;
     190             : 
     191           0 :             *((u32 *) (icmp + 1)) = clib_host_to_net_u32 (ptr);
     192             :           }
     193           0 :           break;
     194           0 :         default:
     195             :           //All other codes cause error
     196           0 :           return -1;
     197             :         }
     198           0 :       break;
     199             : 
     200           0 :     default:
     201             :       //All other types cause error
     202           0 :       return -1;
     203             :       break;
     204             :     }
     205          15 :   return 0;
     206             : }
     207             : 
     208             : /**
     209             :  * @brief Translate ICMP4 packet to ICMP6.
     210             :  *
     211             :  * @param p         Buffer to translate.
     212             :  * @param fn        The function to translate outer header.
     213             :  * @param ctx       A context passed in the outer header translate function.
     214             :  * @param inner_fn  The function to translate inner header.
     215             :  * @param inner_ctx A context passed in the inner header translate function.
     216             :  *
     217             :  * @returns 0 on success, non-zero value otherwise.
     218             :  */
     219             : always_inline int
     220          15 : icmp_to_icmp6 (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx,
     221             :                ip4_to_ip6_set_fn_t inner_fn, void *inner_ctx)
     222             : {
     223             :   ip4_header_t *ip4, *inner_ip4;
     224             :   ip6_header_t *ip6, *inner_ip6;
     225             :   u32 ip_len;
     226             :   icmp46_header_t *icmp;
     227             :   ip_csum_t csum;
     228             :   ip6_frag_hdr_t *inner_frag;
     229          15 :   ip6_frag_hdr_t *outer_frag= NULL;
     230             :   u32 inner_frag_id;
     231             :   u32 inner_frag_offset;
     232             :   u32 outer_frag_id;
     233             :   u8 inner_frag_more;
     234          15 :   u16 *inner_L4_checksum = 0;
     235             :   int rv;
     236             : 
     237          15 :   ip4 = vlib_buffer_get_current (p);
     238          15 :   ip_len = clib_net_to_host_u16 (ip4->length);
     239          15 :   ASSERT (ip_len <= p->current_length);
     240             : 
     241          15 :   icmp = (icmp46_header_t *) (ip4 + 1);
     242          15 :   if (icmp_to_icmp6_header (icmp, &inner_ip4))
     243           0 :     return -1;
     244             : 
     245          15 :   if (inner_ip4)
     246             :     {
     247             :       //We have 2 headers to translate.
     248             :       //We need to make some room in the middle of the packet
     249           4 :       if (PREDICT_FALSE (ip4_is_fragment (inner_ip4)))
     250             :         {
     251             :           //Here it starts getting really tricky
     252             :           //We will add a fragmentation header in the inner packet
     253             : 
     254           0 :           if (!ip4_is_first_fragment (inner_ip4))
     255             :             {
     256             :               //For now we do not handle unless it is the first fragment
     257             :               //Ideally we should handle the case as we are in slow path already
     258           0 :               return -1;
     259             :             }
     260             : 
     261           0 :           vlib_buffer_advance (p,
     262             :                                -2 * (sizeof (*ip6) - sizeof (*ip4)) -
     263             :                                sizeof (*inner_frag));
     264           0 :           ip6 = vlib_buffer_get_current (p);
     265           0 :           memmove (u8_ptr_add (ip6, sizeof (*ip6) - sizeof (*ip4)), ip4,
     266             :                    20 + 8);
     267           0 :           ip4 =
     268             :             (ip4_header_t *) u8_ptr_add (ip6, sizeof (*ip6) - sizeof (*ip4));
     269           0 :           icmp = (icmp46_header_t *) (ip4 + 1);
     270             : 
     271           0 :           inner_ip6 =
     272           0 :             (ip6_header_t *) u8_ptr_add (inner_ip4,
     273             :                                          sizeof (*ip4) - sizeof (*ip6) -
     274             :                                          sizeof (*inner_frag));
     275           0 :           inner_frag =
     276             :             (ip6_frag_hdr_t *) u8_ptr_add (inner_ip6, sizeof (*inner_ip6));
     277           0 :           ip6->payload_length =
     278           0 :             u16_net_add (ip4->length,
     279             :                          sizeof (*ip6) - 2 * sizeof (*ip4) +
     280             :                          sizeof (*inner_frag));
     281           0 :           inner_frag_id = frag_id_4to6 (inner_ip4->fragment_id);
     282           0 :           inner_frag_offset = ip4_get_fragment_offset (inner_ip4);
     283           0 :           inner_frag_more =
     284           0 :             ! !(inner_ip4->flags_and_fragment_offset &
     285           0 :                 clib_net_to_host_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS));
     286             :         }
     287             :       else
     288             :         {
     289           4 :           vlib_buffer_advance (p, -2 * (sizeof (*ip6) - sizeof (*ip4)));
     290           4 :           ip6 = vlib_buffer_get_current (p);
     291           4 :           memmove (u8_ptr_add (ip6, sizeof (*ip6) - sizeof (*ip4)), ip4,
     292             :                    20 + 8);
     293           4 :           ip4 =
     294             :             (ip4_header_t *) u8_ptr_add (ip6, sizeof (*ip6) - sizeof (*ip4));
     295           4 :           icmp = (icmp46_header_t *) u8_ptr_add (ip4, sizeof (*ip4));
     296           4 :           inner_ip6 =
     297           4 :             (ip6_header_t *) u8_ptr_add (inner_ip4,
     298             :                                          sizeof (*ip4) - sizeof (*ip6));
     299           4 :           ip6->payload_length =
     300           4 :             u16_net_add (ip4->length, sizeof (*ip6) - 2 * sizeof (*ip4));
     301           4 :           inner_frag = NULL;
     302             :         }
     303             : 
     304           4 :       if (PREDICT_TRUE (inner_ip4->protocol == IP_PROTOCOL_TCP))
     305             :         {
     306           1 :           inner_L4_checksum = &((tcp_header_t *) (inner_ip4 + 1))->checksum;
     307           1 :           *inner_L4_checksum =
     308           1 :             ip_csum_fold (ip_csum_sub_even
     309           1 :                           (*inner_L4_checksum,
     310           1 :                            *((u64 *) (&inner_ip4->src_address))));
     311             :         }
     312           3 :       else if (PREDICT_TRUE (inner_ip4->protocol == IP_PROTOCOL_UDP))
     313             :         {
     314           2 :           inner_L4_checksum = &((udp_header_t *) (inner_ip4 + 1))->checksum;
     315           2 :           if (*inner_L4_checksum)
     316           1 :             *inner_L4_checksum =
     317           1 :               ip_csum_fold (ip_csum_sub_even
     318           1 :                             (*inner_L4_checksum,
     319           1 :                              *((u64 *) (&inner_ip4->src_address))));
     320             :         }
     321           1 :       else if (inner_ip4->protocol == IP_PROTOCOL_ICMP)
     322             :         {
     323             :           //We have an ICMP inside an ICMP
     324             :           //It needs to be translated, but not for error ICMP messages
     325           1 :           icmp46_header_t *inner_icmp = (icmp46_header_t *) (inner_ip4 + 1);
     326             :           //Only types ICMP4_echo_request and ICMP4_echo_reply are handled by icmp_to_icmp6_header
     327           1 :           inner_icmp->type = (inner_icmp->type == ICMP4_echo_request) ?
     328             :             ICMP6_echo_request : ICMP6_echo_reply;
     329           1 :           inner_L4_checksum = &inner_icmp->checksum;
     330           1 :           inner_ip4->protocol = IP_PROTOCOL_ICMP6;
     331             :         }
     332             :       else
     333             :         {
     334             :           /* To shut up Coverity */
     335           0 :           os_panic ();
     336             :         }
     337             : 
     338           4 :       inner_ip6->ip_version_traffic_class_and_flow_label =
     339           4 :         clib_host_to_net_u32 ((6 << 28) + (inner_ip4->tos << 20));
     340           4 :       inner_ip6->payload_length =
     341           4 :         u16_net_add (inner_ip4->length, -sizeof (*inner_ip4));
     342           4 :       inner_ip6->hop_limit = inner_ip4->ttl;
     343           4 :       inner_ip6->protocol = inner_ip4->protocol;
     344             : 
     345           4 :       if ((rv = inner_fn (p, inner_ip4, inner_ip6, inner_ctx)) != 0)
     346           0 :         return rv;
     347             : 
     348           4 :       if (PREDICT_FALSE (inner_frag != NULL))
     349             :         {
     350           0 :           inner_frag->next_hdr = inner_ip6->protocol;
     351           0 :           inner_frag->identification = inner_frag_id;
     352           0 :           inner_frag->rsv = 0;
     353           0 :           inner_frag->fragment_offset_and_more =
     354           0 :             ip6_frag_hdr_offset_and_more (inner_frag_offset, inner_frag_more);
     355           0 :           inner_ip6->protocol = IP_PROTOCOL_IPV6_FRAGMENTATION;
     356           0 :           inner_ip6->payload_length =
     357           0 :             clib_host_to_net_u16 (clib_net_to_host_u16
     358           0 :                                   (inner_ip6->payload_length) +
     359             :                                   sizeof (*inner_frag));
     360             :         }
     361             : 
     362           4 :       csum = *inner_L4_checksum;
     363           4 :       if (inner_ip6->protocol == IP_PROTOCOL_ICMP6)
     364             :         {
     365             :           //Recompute ICMP checksum
     366           1 :           icmp46_header_t *inner_icmp = (icmp46_header_t *) (inner_ip4 + 1);
     367             : 
     368           1 :           inner_icmp->checksum = 0;
     369           1 :           csum = ip_csum_with_carry (0, inner_ip6->payload_length);
     370             :           csum =
     371           1 :             ip_csum_with_carry (csum,
     372           1 :                                 clib_host_to_net_u16 (inner_ip6->protocol));
     373           1 :           csum = ip_csum_with_carry (csum, inner_ip6->src_address.as_u64[0]);
     374           1 :           csum = ip_csum_with_carry (csum, inner_ip6->src_address.as_u64[1]);
     375           1 :           csum = ip_csum_with_carry (csum, inner_ip6->dst_address.as_u64[0]);
     376           1 :           csum = ip_csum_with_carry (csum, inner_ip6->dst_address.as_u64[1]);
     377             :           csum =
     378           1 :             ip_incremental_checksum (csum, inner_icmp,
     379           1 :                                      clib_net_to_host_u16
     380           1 :                                      (inner_ip6->payload_length));
     381           1 :           inner_icmp->checksum = ~ip_csum_fold (csum);
     382             :         }
     383             :       else
     384             :         {
     385             :           /* UDP checksum is optional */
     386           3 :           if (csum)
     387             :             {
     388             :               csum =
     389           2 :                 ip_csum_add_even (csum, inner_ip6->src_address.as_u64[0]);
     390             :               csum =
     391           2 :                 ip_csum_add_even (csum, inner_ip6->src_address.as_u64[1]);
     392             :               csum =
     393           2 :                 ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[0]);
     394             :               csum =
     395           2 :                 ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[1]);
     396           2 :               *inner_L4_checksum = ip_csum_fold (csum);
     397             :             }
     398             :         }
     399             :     }
     400             :   else
     401             :     {
     402          11 :       if (PREDICT_FALSE (ip4->flags_and_fragment_offset &
     403             :                          clib_host_to_net_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS)))
     404             :         {
     405           1 :           vlib_buffer_advance (p, sizeof (*ip4) - sizeof (*ip6) -
     406             :                                sizeof (*outer_frag));
     407           1 :           ip6 = vlib_buffer_get_current (p);
     408           1 :           outer_frag = (ip6_frag_hdr_t *) (ip6 + 1);
     409           1 :           outer_frag_id = frag_id_4to6 (ip4->fragment_id);
     410             :         }
     411             :       else
     412             :         {
     413          10 :           vlib_buffer_advance (p, sizeof (*ip4) - sizeof (*ip6));
     414          10 :           ip6 = vlib_buffer_get_current (p);
     415             :         }
     416          11 :       ip6->payload_length =
     417          11 :         clib_host_to_net_u16 (clib_net_to_host_u16 (ip4->length) -
     418             :                               sizeof (*ip4));
     419             :     }
     420             : 
     421             :   //Translate outer IPv6
     422          15 :   ip6->ip_version_traffic_class_and_flow_label =
     423          15 :     clib_host_to_net_u32 ((6 << 28) + (ip4->tos << 20));
     424             : 
     425          15 :   ip6->hop_limit = ip4->ttl;
     426          15 :   ip6->protocol = IP_PROTOCOL_ICMP6;
     427             : 
     428          15 :   if ((rv = fn (p, ip4, ip6, ctx)) != 0)
     429           0 :     return rv;
     430             : 
     431          15 :   if (PREDICT_FALSE (outer_frag != NULL))
     432             :     {
     433           1 :       outer_frag->next_hdr = ip6->protocol;
     434           1 :       outer_frag->identification = outer_frag_id;
     435           1 :       outer_frag->rsv = 0;
     436           1 :       outer_frag->fragment_offset_and_more = ip6_frag_hdr_offset_and_more (0, 1);
     437           1 :       ip6->protocol = IP_PROTOCOL_IPV6_FRAGMENTATION;
     438           1 :       ip6->payload_length = u16_net_add (ip6->payload_length,
     439             :                                          sizeof (*outer_frag));
     440             :     }
     441             : 
     442             :   //Truncate when ICMPv6 error message exceeds the minimal IPv6 MTU
     443          15 :   if (p->current_length > 1280 && icmp->type < 128)
     444             :     {
     445           0 :       ip6->payload_length = clib_host_to_net_u16 (1280 - sizeof (*ip6));
     446           0 :       p->current_length = 1280;      //Looks too simple to be correct...
     447             :     }
     448             : 
     449             :   //Recompute ICMP checksum
     450          15 :   icmp->checksum = 0;
     451          15 :   csum = ip_csum_with_carry (0, ip6->payload_length);
     452          15 :   csum = ip_csum_with_carry (csum, clib_host_to_net_u16 (ip6->protocol));
     453          15 :   csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[0]);
     454          15 :   csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[1]);
     455          15 :   csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[0]);
     456          15 :   csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[1]);
     457             :   csum =
     458          15 :     ip_incremental_checksum (csum, icmp,
     459          15 :                              clib_net_to_host_u16 (ip6->payload_length));
     460          15 :   icmp->checksum = ~ip_csum_fold (csum);
     461             : 
     462          15 :   return 0;
     463             : }
     464             : 
     465             : #endif /* __included_ip4_to_ip6_h__ */

Generated by: LCOV version 1.14