LCOV - code coverage report
Current view: top level - net/ipv6 - exthdrs_core.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 89 0.0 %
Date: 2014-04-07 Functions: 0 4 0.0 %
Branches: 0 108 0.0 %

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

Generated by: LCOV version 1.9