LCOV - code coverage report
Current view: top level - net/ipv6 - exthdrs_core.c (source / functions) Hit Total Coverage
Test: landlock.info Lines: 0 119 0.0 %
Date: 2021-04-22 12:43:58 Functions: 0 4 0.0 %

          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           0 : bool ipv6_ext_hdr(u8 nexthdr)
      14             : {
      15             :         /*
      16             :          * find out if nexthdr is an extension header or a protocol
      17             :          */
      18           0 :         return   (nexthdr == NEXTHDR_HOP)       ||
      19           0 :                  (nexthdr == NEXTHDR_ROUTING)   ||
      20           0 :                  (nexthdr == NEXTHDR_FRAGMENT)  ||
      21           0 :                  (nexthdr == NEXTHDR_AUTH)      ||
      22           0 :                  (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           0 :                 struct ipv6_opt_hdr _hdr, *hp;
      81           0 :                 int hdrlen;
      82             : 
      83           0 :                 if (nexthdr == NEXTHDR_NONE)
      84           0 :                         return -1;
      85           0 :                 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
      86           0 :                 if (!hp)
      87             :                         return -1;
      88           0 :                 if (nexthdr == NEXTHDR_FRAGMENT) {
      89           0 :                         __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           0 :                         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           0 :         const unsigned char *nh = skb_network_header(skb);
     119           0 :         int packet_len = skb_tail_pointer(skb) - skb_network_header(skb);
     120           0 :         struct ipv6_opt_hdr *hdr;
     121           0 :         int len;
     122             : 
     123           0 :         if (offset + 2 > packet_len)
     124           0 :                 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           0 :                 goto bad;
     130             : 
     131           0 :         offset += 2;
     132           0 :         len -= 2;
     133             : 
     134           0 :         while (len > 0) {
     135           0 :                 int opttype = nh[offset];
     136           0 :                 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           0 :                 default:
     146           0 :                         optlen = nh[offset + 1] + 2;
     147           0 :                         if (optlen > len)
     148           0 :                                 goto bad;
     149             :                         break;
     150             :                 }
     151           0 :                 offset += optlen;
     152           0 :                 len -= optlen;
     153             :         }
     154             :         /* not_found */
     155           0 :  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           0 :         bool found;
     192             : 
     193           0 :         if (fragoff)
     194           0 :                 *fragoff = 0;
     195             : 
     196           0 :         if (*offset) {
     197           0 :                 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           0 :         do {
     207           0 :                 struct ipv6_opt_hdr _hdr, *hp;
     208           0 :                 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           0 :                         struct ipv6_rt_hdr _rh, *rh;
     223             : 
     224           0 :                         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           0 :                                 found = false;
     232             :                 }
     233             : 
     234           0 :                 if (nexthdr == NEXTHDR_FRAGMENT) {
     235           0 :                         unsigned short _frag_off;
     236           0 :                         __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           0 :                         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