LCOV - code coverage report
Current view: top level - vnet/ip - ip4_packet.h (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 73 98 74.5 %
Date: 2023-10-26 01:39:38 Functions: 20 22 90.9 %

          Line data    Source code
       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             :  * ip4/packet.h: ip4 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_ip4_packet_h
      41             : #define included_ip4_packet_h
      42             : 
      43             : #include <vnet/ip/ip_packet.h>    /* for ip_csum_t */
      44             : #include <vppinfra/byte_order.h>  /* for clib_net_to_host_u16 */
      45             : #include <vppinfra/warnings.h>    /* for WARN_OFF/WARN_ON macro */
      46             : 
      47             : /* IP4 address which can be accessed either as 4 bytes
      48             :    or as a 32-bit number. */
      49             : typedef union
      50             : {
      51             :   u8 data[4];
      52             :   u32 data_u32;
      53             :   /* Aliases. */
      54             :   u8 as_u8[4];
      55             :   u16 as_u16[2];
      56             :   u32 as_u32;
      57             : } ip4_address_t;
      58             : 
      59             : typedef struct
      60             : {
      61             :   /* IP address must be first for ip_interface_address_get_address() to work */
      62             :   ip4_address_t ip4_addr;
      63             :   u32 fib_index;
      64             : } ip4_address_fib_t;
      65             : 
      66             : always_inline void
      67        4845 : ip4_addr_fib_init (ip4_address_fib_t * addr_fib,
      68             :                    const ip4_address_t * address, u32 fib_index)
      69             : {
      70        4845 :   clib_memcpy_fast (&addr_fib->ip4_addr, address,
      71             :                     sizeof (addr_fib->ip4_addr));
      72        4845 :   addr_fib->fib_index = fib_index;
      73        4845 : }
      74             : 
      75             : /* (src,dst) pair of addresses as found in packet header. */
      76             : typedef struct
      77             : {
      78             :   ip4_address_t src, dst;
      79             : } ip4_address_pair_t;
      80             : 
      81             : typedef struct
      82             : {
      83             :   ip4_address_t addr, mask;
      84             : } ip4_address_and_mask_t;
      85             : 
      86             : typedef union
      87             : {
      88             :   struct
      89             :   {
      90             :     /* 4 bit packet length (in 32bit units) and version VVVVLLLL.
      91             :        e.g. for packets w/ no options ip_version_and_header_length == 0x45. */
      92             :     u8 ip_version_and_header_length;
      93             : 
      94             :     /* Type of service. */
      95             :     ip_dscp_t tos;
      96             : 
      97             :     /* Total layer 3 packet length including this header. */
      98             :     u16 length;
      99             : 
     100             :     /* Fragmentation ID. */
     101             :     u16 fragment_id;
     102             : 
     103             :     /* 3 bits of flags and 13 bits of fragment offset (in units
     104             :        of 8 byte quantities). */
     105             :     u16 flags_and_fragment_offset;
     106             : #define IP4_HEADER_FLAG_MORE_FRAGMENTS (1 << 13)
     107             : #define IP4_HEADER_FLAG_DONT_FRAGMENT (1 << 14)
     108             : #define IP4_HEADER_FLAG_CONGESTION (1 << 15)
     109             : 
     110             :     /* Time to live decremented by router at each hop. */
     111             :     u8 ttl;
     112             : 
     113             :     /* Next level protocol packet. */
     114             :     u8 protocol;
     115             : 
     116             :     /* Checksum. */
     117             :     u16 checksum;
     118             : 
     119             :     /* Source and destination address. */
     120             :     union
     121             :     {
     122             :       struct
     123             :       {
     124             :         ip4_address_t src_address, dst_address;
     125             :       };
     126             :       ip4_address_pair_t address_pair;
     127             :     };
     128             :   };
     129             : 
     130             :   /* For checksumming we'll want to access IP header in word sized chunks. */
     131             :   /* For 64 bit machines. */
     132             :   /* *INDENT-OFF* */
     133             :   CLIB_PACKED (struct {
     134             :     u64 checksum_data_64[2];
     135             :     u32 checksum_data_64_32[1];
     136             :   });
     137             :   /* *INDENT-ON* */
     138             : 
     139             :   /* For 32 bit machines. */
     140             :   /* *INDENT-OFF* */
     141             :   CLIB_PACKED (struct {
     142             :     u32 checksum_data_32[5];
     143             :   });
     144             :   /* *INDENT-ON* */
     145             : } ip4_header_t;
     146             : 
     147             : /* Value of ip_version_and_header_length for packets w/o options. */
     148             : #define IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS \
     149             :   ((4 << 4) | (sizeof (ip4_header_t) / sizeof (u32)))
     150             : 
     151             : #define IP4_ROUTER_ALERT_OPTION 20
     152             : 
     153             : always_inline u16
     154    13742211 : ip4_get_fragment_offset (const ip4_header_t * i)
     155             : {
     156    13742211 :   return clib_net_to_host_u16 (i->flags_and_fragment_offset) & 0x1fff;
     157             : }
     158             : 
     159             : always_inline u16
     160       74800 : ip4_get_fragment_more (const ip4_header_t * i)
     161             : {
     162       74800 :   return clib_net_to_host_u16 (i->flags_and_fragment_offset) &
     163             :     IP4_HEADER_FLAG_MORE_FRAGMENTS;
     164             : }
     165             : 
     166             : always_inline int
     167     1296327 : ip4_is_fragment (const ip4_header_t * i)
     168             : {
     169     1296327 :   return (i->flags_and_fragment_offset &
     170     1296327 :           clib_net_to_host_u16 (0x1fff | IP4_HEADER_FLAG_MORE_FRAGMENTS));
     171             : }
     172             : 
     173             : always_inline int
     174           0 : ip4_is_first_fragment (const ip4_header_t * i)
     175             : {
     176           0 :   return (i->flags_and_fragment_offset &
     177           0 :           clib_net_to_host_u16 (0x1fff | IP4_HEADER_FLAG_MORE_FRAGMENTS)) ==
     178           0 :     clib_net_to_host_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS);
     179             : }
     180             : 
     181             : /* Fragment offset in bytes. */
     182             : always_inline int
     183       45052 : ip4_get_fragment_offset_bytes (const ip4_header_t * i)
     184             : {
     185       45052 :   return 8 * ip4_get_fragment_offset (i);
     186             : }
     187             : 
     188             : always_inline int
     189    51728716 : ip4_header_bytes (const ip4_header_t * i)
     190             : {
     191    51728716 :   return sizeof (u32) * (i->ip_version_and_header_length & 0xf);
     192             : }
     193             : 
     194             : always_inline void *
     195     1454798 : ip4_next_header (ip4_header_t * i)
     196             : {
     197     1454798 :   return (void *) i + ip4_header_bytes (i);
     198             : }
     199             : 
     200             : /* Turn off array bounds check due to ip4_header_t
     201             :    option field operations. */
     202             : 
     203             : /* *INDENT-OFF* */
     204             : WARN_OFF(array-bounds)
     205             : /* *INDENT-ON* */
     206             : 
     207             : static_always_inline u16
     208    18495201 : ip4_header_checksum_inline (ip4_header_t * i, int with_checksum)
     209             : {
     210    18495201 :   int option_len = (i->ip_version_and_header_length & 0xf) - 5;
     211    18495201 :   uword sum = 0;
     212             : #if uword_bits == 64
     213    18495201 :   u32 *iphdr = (u32 *) i;
     214             : 
     215    18495201 :   sum += iphdr[0];
     216    18495201 :   sum += iphdr[1];
     217    18495201 :   sum += with_checksum ? iphdr[2] : *(u16 *) (iphdr + 2);
     218             :   /* skip checksum */
     219    18495201 :   sum += iphdr[3];
     220    18495201 :   sum += iphdr[4];
     221             : 
     222    18495201 :   if (PREDICT_FALSE (option_len > 0))
     223          82 :     switch (option_len)
     224             :       {
     225           0 :       case 10:
     226           0 :         sum += iphdr[14];
     227           0 :       case 9:
     228           0 :         sum += iphdr[13];
     229           0 :       case 8:
     230           0 :         sum += iphdr[12];
     231           0 :       case 7:
     232           0 :         sum += iphdr[11];
     233           0 :       case 6:
     234           0 :         sum += iphdr[10];
     235           0 :       case 5:
     236           0 :         sum += iphdr[9];
     237           0 :       case 4:
     238           0 :         sum += iphdr[8];
     239           0 :       case 3:
     240           0 :         sum += iphdr[7];
     241           0 :       case 2:
     242           0 :         sum += iphdr[6];
     243          82 :       case 1:
     244          82 :         sum += iphdr[5];
     245          82 :       default:
     246          82 :         break;
     247             :       }
     248             : 
     249    18495122 :   sum = ((u32) sum) + (sum >> 32);
     250             : #else
     251             :   u16 *iphdr = (u16 *) i;
     252             : 
     253             :   sum += iphdr[0];
     254             :   sum += iphdr[1];
     255             :   sum += iphdr[2];
     256             :   sum += iphdr[3];
     257             :   sum += iphdr[4];
     258             :   if (with_checksum)
     259             :     sum += iphdr[5];
     260             :   sum += iphdr[6];
     261             :   sum += iphdr[7];
     262             :   sum += iphdr[8];
     263             :   sum += iphdr[9];
     264             : 
     265             :   if (PREDICT_FALSE (option_len > 0))
     266             :     switch (option_len)
     267             :       {
     268             :       case 10:
     269             :         sum += iphdr[28];
     270             :         sum += iphdr[29];
     271             :       case 9:
     272             :         sum += iphdr[26];
     273             :         sum += iphdr[27];
     274             :       case 8:
     275             :         sum += iphdr[24];
     276             :         sum += iphdr[25];
     277             :       case 7:
     278             :         sum += iphdr[22];
     279             :         sum += iphdr[23];
     280             :       case 6:
     281             :         sum += iphdr[20];
     282             :         sum += iphdr[21];
     283             :       case 5:
     284             :         sum += iphdr[18];
     285             :         sum += iphdr[19];
     286             :       case 4:
     287             :         sum += iphdr[16];
     288             :         sum += iphdr[17];
     289             :       case 3:
     290             :         sum += iphdr[14];
     291             :         sum += iphdr[15];
     292             :       case 2:
     293             :         sum += iphdr[12];
     294             :         sum += iphdr[13];
     295             :       case 1:
     296             :         sum += iphdr[10];
     297             :         sum += iphdr[11];
     298             :       default:
     299             :         break;
     300             :       }
     301             : #endif
     302             : 
     303    18495201 :   sum = ((u16) sum) + (sum >> 16);
     304    18495201 :   sum = ((u16) sum) + (sum >> 16);
     305    18495201 :   return ~((u16) sum);
     306             : }
     307             : 
     308             : /* *INDENT-OFF* */
     309             : WARN_ON(array-bounds)
     310             : /* *INDENT-ON* */
     311             : 
     312             : always_inline u16
     313     3716387 : ip4_header_checksum (ip4_header_t * i)
     314             : {
     315     3716387 :   return ip4_header_checksum_inline (i, /* with_checksum */ 0);
     316             : }
     317             : 
     318             : always_inline void
     319     1123112 : ip4_header_set_dscp (ip4_header_t * ip4, ip_dscp_t dscp)
     320             : {
     321     1123112 :   ip4->tos &= ~0xfc;
     322             :   /* not masking the dscp value to save th instruction
     323             :    * it shouldn't b necessary since the argument is an enum
     324             :    * whose range is therefore constrained in the CP. in the
     325             :    * DP it will have been taken from another packet, so again
     326             :    * constrained in  value */
     327     1123112 :   ip4->tos |= dscp << IP_PACKET_TC_FIELD_DSCP_BIT_SHIFT;
     328     1123112 : }
     329             : 
     330             : always_inline void
     331     1122836 : ip4_header_set_ecn (ip4_header_t * ip4, ip_ecn_t ecn)
     332             : {
     333     1122836 :   ip4->tos &= ~IP_PACKET_TC_FIELD_ECN_MASK;
     334     1122836 :   ip4->tos |= ecn;
     335     1122836 : }
     336             : 
     337             : always_inline void
     338         504 : ip4_header_set_ecn_w_chksum (ip4_header_t * ip4, ip_ecn_t ecn)
     339             : {
     340         504 :   ip_csum_t sum = ip4->checksum;
     341         504 :   u8 old = ip4->tos;
     342         504 :   u8 new = (old & ~IP_PACKET_TC_FIELD_ECN_MASK) | ecn;
     343             : 
     344         504 :   sum = ip_csum_update (sum, old, new, ip4_header_t, tos);
     345         504 :   ip4->checksum = ip_csum_fold (sum);
     346         504 :   ip4->tos = new;
     347         504 : }
     348             : 
     349             : always_inline ip_dscp_t
     350     1398425 : ip4_header_get_dscp (const ip4_header_t * ip4)
     351             : {
     352     1398425 :   return (ip4->tos >> IP_PACKET_TC_FIELD_DSCP_BIT_SHIFT);
     353             : }
     354             : 
     355             : always_inline ip_ecn_t
     356     1398769 : ip4_header_get_ecn (const ip4_header_t * ip4)
     357             : {
     358     1398769 :   return (ip4->tos & IP_PACKET_TC_FIELD_ECN_MASK);
     359             : }
     360             : 
     361             : always_inline u8
     362         127 : ip4_header_get_ttl (const ip4_header_t *ip4)
     363             : {
     364         127 :   return (ip4->ttl);
     365             : }
     366             : 
     367             : always_inline void
     368           0 : ip4_header_set_ttl (ip4_header_t *ip4, u8 ttl)
     369             : {
     370           0 :   ip4->ttl = ttl;
     371           0 : }
     372             : 
     373             : always_inline void
     374          65 : ip4_header_set_df (ip4_header_t * ip4)
     375             : {
     376          65 :   ip4->flags_and_fragment_offset |=
     377          65 :     clib_host_to_net_u16 (IP4_HEADER_FLAG_DONT_FRAGMENT);
     378          65 : }
     379             : 
     380             : always_inline void
     381             : ip4_header_clear_df (ip4_header_t * ip4)
     382             : {
     383             :   ip4->flags_and_fragment_offset &=
     384             :     ~clib_host_to_net_u16 (IP4_HEADER_FLAG_DONT_FRAGMENT);
     385             : }
     386             : 
     387             : always_inline u8
     388         252 : ip4_header_get_df (const ip4_header_t * ip4)
     389             : {
     390         252 :   return (! !(ip4->flags_and_fragment_offset &
     391         252 :               clib_host_to_net_u16 (IP4_HEADER_FLAG_DONT_FRAGMENT)));
     392             : }
     393             : 
     394             : static inline uword
     395    14778751 : ip4_header_checksum_is_valid (ip4_header_t * i)
     396             : {
     397    14778751 :   return ip4_header_checksum_inline (i, /* with_checksum */ 1) == 0;
     398             : }
     399             : 
     400             : #define ip4_partial_header_checksum_x1(ip0,sum0)                        \
     401             : do {                                                                    \
     402             :   if (BITS (ip_csum_t) > 32)                                         \
     403             :     {                                                                   \
     404             :       sum0 = ip0->checksum_data_64[0];                                       \
     405             :       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_64[1]);    \
     406             :       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_64_32[0]); \
     407             :     }                                                                   \
     408             :   else                                                                  \
     409             :     {                                                                   \
     410             :       sum0 = ip0->checksum_data_32[0];                                       \
     411             :       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_32[1]);    \
     412             :       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_32[2]);    \
     413             :       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_32[3]);    \
     414             :       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_32[4]);    \
     415             :     }                                                                   \
     416             : } while (0)
     417             : 
     418             : #define ip4_partial_header_checksum_x2(ip0,ip1,sum0,sum1)               \
     419             : do {                                                                    \
     420             :   if (BITS (ip_csum_t) > 32)                                         \
     421             :     {                                                                   \
     422             :       sum0 = ip0->checksum_data_64[0];                                       \
     423             :       sum1 = ip1->checksum_data_64[0];                                       \
     424             :       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_64[1]);    \
     425             :       sum1 = ip_csum_with_carry (sum1, ip1->checksum_data_64[1]);    \
     426             :       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_64_32[0]); \
     427             :       sum1 = ip_csum_with_carry (sum1, ip1->checksum_data_64_32[0]); \
     428             :     }                                                                   \
     429             :   else                                                                  \
     430             :     {                                                                   \
     431             :       sum0 = ip0->checksum_data_32[0];                                       \
     432             :       sum1 = ip1->checksum_data_32[0];                                       \
     433             :       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_32[1]);    \
     434             :       sum1 = ip_csum_with_carry (sum1, ip1->checksum_data_32[1]);    \
     435             :       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_32[2]);    \
     436             :       sum1 = ip_csum_with_carry (sum1, ip1->checksum_data_32[2]);    \
     437             :       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_32[3]);    \
     438             :       sum1 = ip_csum_with_carry (sum1, ip1->checksum_data_32[3]);    \
     439             :       sum0 = ip_csum_with_carry (sum0, ip0->checksum_data_32[4]);    \
     440             :       sum1 = ip_csum_with_carry (sum1, ip1->checksum_data_32[4]);    \
     441             :     }                                                                   \
     442             : } while (0)
     443             : 
     444             : always_inline uword
     445    13573989 : ip4_address_is_multicast (const ip4_address_t * a)
     446             : {
     447    13573989 :   return (a->data[0] & 0xf0) == 0xe0;
     448             : }
     449             : 
     450             : always_inline uword
     451         819 : ip4_address_is_global_broadcast (const ip4_address_t * a)
     452             : {
     453         819 :   return (a->as_u32) == 0xffffffff;
     454             : }
     455             : 
     456             : always_inline void
     457             : ip4_multicast_address_set_for_group (ip4_address_t * a,
     458             :                                      ip_multicast_group_t g)
     459             : {
     460             :   ASSERT ((u32) g < (1 << 28));
     461             :   a->as_u32 = clib_host_to_net_u32 ((0xe << 28) + g);
     462             : }
     463             : 
     464             : always_inline void
     465             : ip4_multicast_ethernet_address (u8 * ethernet_address,
     466             :                                 const ip4_address_t * a)
     467             : {
     468             :   const u8 *d = a->as_u8;
     469             : 
     470             :   ethernet_address[0] = 0x01;
     471             :   ethernet_address[1] = 0x00;
     472             :   ethernet_address[2] = 0x5e;
     473             :   ethernet_address[3] = d[1] & 0x7f;
     474             :   ethernet_address[4] = d[2];
     475             :   ethernet_address[5] = d[3];
     476             : }
     477             : 
     478             : #endif /* included_ip4_packet_h */
     479             : 
     480             : /*
     481             :  * fd.io coding-style-patch-verification: ON
     482             :  *
     483             :  * Local Variables:
     484             :  * eval: (c-set-style "gnu")
     485             :  * End:
     486             :  */

Generated by: LCOV version 1.14