LCOV - code coverage report
Current view: top level - net/ipv6 - exthdrs_core.c (source / functions) Hit Total Coverage
Test: Real Lines: 6 93 6.5 %
Date: 2020-10-17 15:46:16 Functions: 0 4 0.0 %
Legend: Neither, QEMU, Real, Both Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0-only
       2                 :            : /*
       3                 :            :  * IPv6 library code, needed by static components when full IPv6 support is
       4                 :            :  * not configured or static.
       5                 :            :  */
       6                 :            : #include <linux/export.h>
       7                 :            : #include <net/ipv6.h>
       8                 :            : 
       9                 :            : /*
      10                 :            :  * find out if nexthdr is a well-known extension header or a protocol
      11                 :            :  */
      12                 :            : 
      13                 :          1 : bool ipv6_ext_hdr(u8 nexthdr)
      14                 :            : {
      15                 :            :         /*
      16                 :            :          * find out if nexthdr is an extension header or a protocol
      17                 :            :          */
      18                 :          1 :         return   (nexthdr == NEXTHDR_HOP)       ||
      19                 :          1 :                  (nexthdr == NEXTHDR_ROUTING)   ||
      20                 :          1 :                  (nexthdr == NEXTHDR_FRAGMENT)  ||
      21                 :          1 :                  (nexthdr == NEXTHDR_AUTH)      ||
      22                 :          1 :                  (nexthdr == NEXTHDR_NONE)      ||
      23                 :            :                  (nexthdr == NEXTHDR_DEST);
      24                 :            : }
      25                 :            : EXPORT_SYMBOL(ipv6_ext_hdr);
      26                 :            : 
      27                 :            : /*
      28                 :            :  * Skip any extension headers. This is used by the ICMP module.
      29                 :            :  *
      30                 :            :  * Note that strictly speaking this conflicts with RFC 2460 4.0:
      31                 :            :  * ...The contents and semantics of each extension header determine whether
      32                 :            :  * or not to proceed to the next header.  Therefore, extension headers must
      33                 :            :  * be processed strictly in the order they appear in the packet; a
      34                 :            :  * receiver must not, for example, scan through a packet looking for a
      35                 :            :  * particular kind of extension header and process that header prior to
      36                 :            :  * processing all preceding ones.
      37                 :            :  *
      38                 :            :  * We do exactly this. This is a protocol bug. We can't decide after a
      39                 :            :  * seeing an unknown discard-with-error flavour TLV option if it's a
      40                 :            :  * ICMP error message or not (errors should never be send in reply to
      41                 :            :  * ICMP error messages).
      42                 :            :  *
      43                 :            :  * But I see no other way to do this. This might need to be reexamined
      44                 :            :  * when Linux implements ESP (and maybe AUTH) headers.
      45                 :            :  * --AK
      46                 :            :  *
      47                 :            :  * This function parses (probably truncated) exthdr set "hdr".
      48                 :            :  * "nexthdrp" initially points to some place,
      49                 :            :  * where type of the first header can be found.
      50                 :            :  *
      51                 :            :  * It skips all well-known exthdrs, and returns pointer to the start
      52                 :            :  * of unparsable area i.e. the first header with unknown type.
      53                 :            :  * If it is not NULL *nexthdr is updated by type/protocol of this header.
      54                 :            :  *
      55                 :            :  * NOTES: - if packet terminated with NEXTHDR_NONE it returns NULL.
      56                 :            :  *        - it may return pointer pointing beyond end of packet,
      57                 :            :  *          if the last recognized header is truncated in the middle.
      58                 :            :  *        - if packet is truncated, so that all parsed headers are skipped,
      59                 :            :  *          it returns NULL.
      60                 :            :  *        - First fragment header is skipped, not-first ones
      61                 :            :  *          are considered as unparsable.
      62                 :            :  *        - Reports the offset field of the final fragment header so it is
      63                 :            :  *          possible to tell whether this is a first fragment, later fragment,
      64                 :            :  *          or not fragmented.
      65                 :            :  *        - ESP is unparsable for now and considered like
      66                 :            :  *          normal payload protocol.
      67                 :            :  *        - Note also special handling of AUTH header. Thanks to IPsec wizards.
      68                 :            :  *
      69                 :            :  * --ANK (980726)
      70                 :            :  */
      71                 :            : 
      72                 :          0 : int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp,
      73                 :            :                      __be16 *frag_offp)
      74                 :            : {
      75                 :          0 :         u8 nexthdr = *nexthdrp;
      76                 :            : 
      77                 :          0 :         *frag_offp = 0;
      78                 :            : 
      79                 :          0 :         while (ipv6_ext_hdr(nexthdr)) {
      80                 :            :                 struct ipv6_opt_hdr _hdr, *hp;
      81                 :            :                 int hdrlen;
      82                 :            : 
      83                 :          0 :                 if (nexthdr == NEXTHDR_NONE)
      84                 :          0 :                         return -1;
      85                 :            :                 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
      86                 :          0 :                 if (!hp)
      87                 :            :                         return -1;
      88                 :          0 :                 if (nexthdr == NEXTHDR_FRAGMENT) {
      89                 :            :                         __be16 _frag_off, *fp;
      90                 :          0 :                         fp = skb_header_pointer(skb,
      91                 :          0 :                                                 start+offsetof(struct frag_hdr,
      92                 :            :                                                                frag_off),
      93                 :            :                                                 sizeof(_frag_off),
      94                 :            :                                                 &_frag_off);
      95                 :          0 :                         if (!fp)
      96                 :          0 :                                 return -1;
      97                 :            : 
      98                 :          0 :                         *frag_offp = *fp;
      99                 :          0 :                         if (ntohs(*frag_offp) & ~0x7)
     100                 :            :                                 break;
     101                 :            :                         hdrlen = 8;
     102                 :          0 :                 } else if (nexthdr == NEXTHDR_AUTH)
     103                 :          0 :                         hdrlen = ipv6_authlen(hp);
     104                 :            :                 else
     105                 :          0 :                         hdrlen = ipv6_optlen(hp);
     106                 :            : 
     107                 :          0 :                 nexthdr = hp->nexthdr;
     108                 :          0 :                 start += hdrlen;
     109                 :            :         }
     110                 :            : 
     111                 :          0 :         *nexthdrp = nexthdr;
     112                 :          0 :         return start;
     113                 :            : }
     114                 :            : EXPORT_SYMBOL(ipv6_skip_exthdr);
     115                 :            : 
     116                 :          0 : int ipv6_find_tlv(const struct sk_buff *skb, int offset, int type)
     117                 :            : {
     118                 :            :         const unsigned char *nh = skb_network_header(skb);
     119                 :          0 :         int packet_len = skb_tail_pointer(skb) - skb_network_header(skb);
     120                 :            :         struct ipv6_opt_hdr *hdr;
     121                 :            :         int len;
     122                 :            : 
     123                 :          0 :         if (offset + 2 > packet_len)
     124                 :            :                 goto bad;
     125                 :          0 :         hdr = (struct ipv6_opt_hdr *)(nh + offset);
     126                 :          0 :         len = ((hdr->hdrlen + 1) << 3);
     127                 :            : 
     128                 :          0 :         if (offset + len > packet_len)
     129                 :            :                 goto bad;
     130                 :            : 
     131                 :            :         offset += 2;
     132                 :          0 :         len -= 2;
     133                 :            : 
     134                 :          0 :         while (len > 0) {
     135                 :          0 :                 int opttype = nh[offset];
     136                 :            :                 int optlen;
     137                 :            : 
     138                 :          0 :                 if (opttype == type)
     139                 :          0 :                         return offset;
     140                 :            : 
     141                 :          0 :                 switch (opttype) {
     142                 :            :                 case IPV6_TLV_PAD1:
     143                 :            :                         optlen = 1;
     144                 :            :                         break;
     145                 :            :                 default:
     146                 :          0 :                         optlen = nh[offset + 1] + 2;
     147                 :          0 :                         if (optlen > len)
     148                 :            :                                 goto bad;
     149                 :            :                         break;
     150                 :            :                 }
     151                 :          0 :                 offset += optlen;
     152                 :          0 :                 len -= optlen;
     153                 :            :         }
     154                 :            :         /* not_found */
     155                 :            :  bad:
     156                 :            :         return -1;
     157                 :            : }
     158                 :            : EXPORT_SYMBOL_GPL(ipv6_find_tlv);
     159                 :            : 
     160                 :            : /*
     161                 :            :  * find the offset to specified header or the protocol number of last header
     162                 :            :  * if target < 0. "last header" is transport protocol header, ESP, or
     163                 :            :  * "No next header".
     164                 :            :  *
     165                 :            :  * Note that *offset is used as input/output parameter, and if it is not zero,
     166                 :            :  * then it must be a valid offset to an inner IPv6 header. This can be used
     167                 :            :  * to explore inner IPv6 header, eg. ICMPv6 error messages.
     168                 :            :  *
     169                 :            :  * If target header is found, its offset is set in *offset and return protocol
     170                 :            :  * number. Otherwise, return -1.
     171                 :            :  *
     172                 :            :  * If the first fragment doesn't contain the final protocol header or
     173                 :            :  * NEXTHDR_NONE it is considered invalid.
     174                 :            :  *
     175                 :            :  * Note that non-1st fragment is special case that "the protocol number
     176                 :            :  * of last header" is "next header" field in Fragment header. In this case,
     177                 :            :  * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
     178                 :            :  * isn't NULL.
     179                 :            :  *
     180                 :            :  * if flags is not NULL and it's a fragment, then the frag flag
     181                 :            :  * IP6_FH_F_FRAG will be set. If it's an AH header, the
     182                 :            :  * IP6_FH_F_AUTH flag is set and target < 0, then this function will
     183                 :            :  * stop at the AH header. If IP6_FH_F_SKIP_RH flag was passed, then this
     184                 :            :  * function will skip all those routing headers, where segements_left was 0.
     185                 :            :  */
     186                 :          0 : int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
     187                 :            :                   int target, unsigned short *fragoff, int *flags)
     188                 :            : {
     189                 :          0 :         unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
     190                 :          0 :         u8 nexthdr = ipv6_hdr(skb)->nexthdr;
     191                 :            :         bool found;
     192                 :            : 
     193                 :          0 :         if (fragoff)
     194                 :          0 :                 *fragoff = 0;
     195                 :            : 
     196                 :          0 :         if (*offset) {
     197                 :            :                 struct ipv6hdr _ip6, *ip6;
     198                 :            : 
     199                 :          0 :                 ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6);
     200                 :          0 :                 if (!ip6 || (ip6->version != 6))
     201                 :          0 :                         return -EBADMSG;
     202                 :          0 :                 start = *offset + sizeof(struct ipv6hdr);
     203                 :          0 :                 nexthdr = ip6->nexthdr;
     204                 :            :         }
     205                 :            : 
     206                 :            :         do {
     207                 :            :                 struct ipv6_opt_hdr _hdr, *hp;
     208                 :            :                 unsigned int hdrlen;
     209                 :          0 :                 found = (nexthdr == target);
     210                 :            : 
     211                 :          0 :                 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
     212                 :          0 :                         if (target < 0 || found)
     213                 :            :                                 break;
     214                 :          0 :                         return -ENOENT;
     215                 :            :                 }
     216                 :            : 
     217                 :          0 :                 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
     218                 :          0 :                 if (!hp)
     219                 :            :                         return -EBADMSG;
     220                 :            : 
     221                 :          0 :                 if (nexthdr == NEXTHDR_ROUTING) {
     222                 :            :                         struct ipv6_rt_hdr _rh, *rh;
     223                 :            : 
     224                 :            :                         rh = skb_header_pointer(skb, start, sizeof(_rh),
     225                 :            :                                                 &_rh);
     226                 :          0 :                         if (!rh)
     227                 :          0 :                                 return -EBADMSG;
     228                 :            : 
     229                 :          0 :                         if (flags && (*flags & IP6_FH_F_SKIP_RH) &&
     230                 :          0 :                             rh->segments_left == 0)
     231                 :            :                                 found = false;
     232                 :            :                 }
     233                 :            : 
     234                 :          0 :                 if (nexthdr == NEXTHDR_FRAGMENT) {
     235                 :            :                         unsigned short _frag_off;
     236                 :            :                         __be16 *fp;
     237                 :            : 
     238                 :          0 :                         if (flags)      /* Indicate that this is a fragment */
     239                 :          0 :                                 *flags |= IP6_FH_F_FRAG;
     240                 :          0 :                         fp = skb_header_pointer(skb,
     241                 :          0 :                                                 start+offsetof(struct frag_hdr,
     242                 :            :                                                                frag_off),
     243                 :            :                                                 sizeof(_frag_off),
     244                 :            :                                                 &_frag_off);
     245                 :          0 :                         if (!fp)
     246                 :          0 :                                 return -EBADMSG;
     247                 :            : 
     248                 :          0 :                         _frag_off = ntohs(*fp) & ~0x7;
     249                 :          0 :                         if (_frag_off) {
     250                 :          0 :                                 if (target < 0 &&
     251                 :          0 :                                     ((!ipv6_ext_hdr(hp->nexthdr)) ||
     252                 :            :                                      hp->nexthdr == NEXTHDR_NONE)) {
     253                 :          0 :                                         if (fragoff)
     254                 :          0 :                                                 *fragoff = _frag_off;
     255                 :          0 :                                         return hp->nexthdr;
     256                 :            :                                 }
     257                 :          0 :                                 if (!found)
     258                 :            :                                         return -ENOENT;
     259                 :          0 :                                 if (fragoff)
     260                 :          0 :                                         *fragoff = _frag_off;
     261                 :          0 :                                 break;
     262                 :            :                         }
     263                 :            :                         hdrlen = 8;
     264                 :          0 :                 } else if (nexthdr == NEXTHDR_AUTH) {
     265                 :          0 :                         if (flags && (*flags & IP6_FH_F_AUTH) && (target < 0))
     266                 :            :                                 break;
     267                 :          0 :                         hdrlen = ipv6_authlen(hp);
     268                 :            :                 } else
     269                 :          0 :                         hdrlen = ipv6_optlen(hp);
     270                 :            : 
     271                 :          0 :                 if (!found) {
     272                 :          0 :                         nexthdr = hp->nexthdr;
     273                 :          0 :                         start += hdrlen;
     274                 :            :                 }
     275                 :          0 :         } while (!found);
     276                 :            : 
     277                 :          0 :         *offset = start;
     278                 :          0 :         return nexthdr;
     279                 :            : }
     280                 :            : EXPORT_SYMBOL(ipv6_find_hdr);
    

Generated by: LCOV version 1.14