LCOV - code coverage report
Current view: top level - net/ipv6 - mcast_snoop.c (source / functions) Hit Total Coverage
Test: Real Lines: 0 51 0.0 %
Date: 2020-10-17 15:46:16 Functions: 0 8 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                 :            : /* Copyright (C) 2010: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
       3                 :            :  * Copyright (C) 2015: Linus Lüssing <linus.luessing@c0d3.blue>
       4                 :            :  *
       5                 :            :  * Based on the MLD support added to br_multicast.c by YOSHIFUJI Hideaki.
       6                 :            :  */
       7                 :            : 
       8                 :            : #include <linux/skbuff.h>
       9                 :            : #include <net/ipv6.h>
      10                 :            : #include <net/mld.h>
      11                 :            : #include <net/addrconf.h>
      12                 :            : #include <net/ip6_checksum.h>
      13                 :            : 
      14                 :          0 : static int ipv6_mc_check_ip6hdr(struct sk_buff *skb)
      15                 :            : {
      16                 :            :         const struct ipv6hdr *ip6h;
      17                 :            :         unsigned int len;
      18                 :          0 :         unsigned int offset = skb_network_offset(skb) + sizeof(*ip6h);
      19                 :            : 
      20                 :          0 :         if (!pskb_may_pull(skb, offset))
      21                 :            :                 return -EINVAL;
      22                 :            : 
      23                 :            :         ip6h = ipv6_hdr(skb);
      24                 :            : 
      25                 :          0 :         if (ip6h->version != 6)
      26                 :            :                 return -EINVAL;
      27                 :            : 
      28                 :          0 :         len = offset + ntohs(ip6h->payload_len);
      29                 :          0 :         if (skb->len < len || len <= offset)
      30                 :            :                 return -EINVAL;
      31                 :            : 
      32                 :            :         skb_set_transport_header(skb, offset);
      33                 :            : 
      34                 :          0 :         return 0;
      35                 :            : }
      36                 :            : 
      37                 :          0 : static int ipv6_mc_check_exthdrs(struct sk_buff *skb)
      38                 :            : {
      39                 :            :         const struct ipv6hdr *ip6h;
      40                 :            :         int offset;
      41                 :            :         u8 nexthdr;
      42                 :            :         __be16 frag_off;
      43                 :            : 
      44                 :            :         ip6h = ipv6_hdr(skb);
      45                 :            : 
      46                 :          0 :         if (ip6h->nexthdr != IPPROTO_HOPOPTS)
      47                 :            :                 return -ENOMSG;
      48                 :            : 
      49                 :          0 :         nexthdr = ip6h->nexthdr;
      50                 :          0 :         offset = skb_network_offset(skb) + sizeof(*ip6h);
      51                 :          0 :         offset = ipv6_skip_exthdr(skb, offset, &nexthdr, &frag_off);
      52                 :            : 
      53                 :          0 :         if (offset < 0)
      54                 :            :                 return -EINVAL;
      55                 :            : 
      56                 :          0 :         if (nexthdr != IPPROTO_ICMPV6)
      57                 :            :                 return -ENOMSG;
      58                 :            : 
      59                 :            :         skb_set_transport_header(skb, offset);
      60                 :            : 
      61                 :          0 :         return 0;
      62                 :            : }
      63                 :            : 
      64                 :          0 : static int ipv6_mc_check_mld_reportv2(struct sk_buff *skb)
      65                 :            : {
      66                 :          0 :         unsigned int len = skb_transport_offset(skb);
      67                 :            : 
      68                 :          0 :         len += sizeof(struct mld2_report);
      69                 :            : 
      70                 :          0 :         return ipv6_mc_may_pull(skb, len) ? 0 : -EINVAL;
      71                 :            : }
      72                 :            : 
      73                 :          0 : static int ipv6_mc_check_mld_query(struct sk_buff *skb)
      74                 :            : {
      75                 :            :         unsigned int transport_len = ipv6_transport_len(skb);
      76                 :            :         struct mld_msg *mld;
      77                 :            :         unsigned int len;
      78                 :            : 
      79                 :            :         /* RFC2710+RFC3810 (MLDv1+MLDv2) require link-local source addresses */
      80                 :          0 :         if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL))
      81                 :            :                 return -EINVAL;
      82                 :            : 
      83                 :            :         /* MLDv1? */
      84                 :          0 :         if (transport_len != sizeof(struct mld_msg)) {
      85                 :            :                 /* or MLDv2? */
      86                 :          0 :                 if (transport_len < sizeof(struct mld2_query))
      87                 :            :                         return -EINVAL;
      88                 :            : 
      89                 :          0 :                 len = skb_transport_offset(skb) + sizeof(struct mld2_query);
      90                 :          0 :                 if (!ipv6_mc_may_pull(skb, len))
      91                 :            :                         return -EINVAL;
      92                 :            :         }
      93                 :            : 
      94                 :            :         mld = (struct mld_msg *)skb_transport_header(skb);
      95                 :            : 
      96                 :            :         /* RFC2710+RFC3810 (MLDv1+MLDv2) require the multicast link layer
      97                 :            :          * all-nodes destination address (ff02::1) for general queries
      98                 :            :          */
      99                 :          0 :         if (ipv6_addr_any(&mld->mld_mca) &&
     100                 :            :             !ipv6_addr_is_ll_all_nodes(&ipv6_hdr(skb)->daddr))
     101                 :            :                 return -EINVAL;
     102                 :            : 
     103                 :          0 :         return 0;
     104                 :            : }
     105                 :            : 
     106                 :          0 : static int ipv6_mc_check_mld_msg(struct sk_buff *skb)
     107                 :            : {
     108                 :          0 :         unsigned int len = skb_transport_offset(skb) + sizeof(struct mld_msg);
     109                 :            :         struct mld_msg *mld;
     110                 :            : 
     111                 :          0 :         if (!ipv6_mc_may_pull(skb, len))
     112                 :            :                 return -EINVAL;
     113                 :            : 
     114                 :            :         mld = (struct mld_msg *)skb_transport_header(skb);
     115                 :            : 
     116                 :          0 :         switch (mld->mld_type) {
     117                 :            :         case ICMPV6_MGM_REDUCTION:
     118                 :            :         case ICMPV6_MGM_REPORT:
     119                 :            :                 return 0;
     120                 :            :         case ICMPV6_MLD2_REPORT:
     121                 :          0 :                 return ipv6_mc_check_mld_reportv2(skb);
     122                 :            :         case ICMPV6_MGM_QUERY:
     123                 :          0 :                 return ipv6_mc_check_mld_query(skb);
     124                 :            :         default:
     125                 :          0 :                 return -ENOMSG;
     126                 :            :         }
     127                 :            : }
     128                 :            : 
     129                 :          0 : static inline __sum16 ipv6_mc_validate_checksum(struct sk_buff *skb)
     130                 :            : {
     131                 :          0 :         return skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo);
     132                 :            : }
     133                 :            : 
     134                 :          0 : int ipv6_mc_check_icmpv6(struct sk_buff *skb)
     135                 :            : {
     136                 :          0 :         unsigned int len = skb_transport_offset(skb) + sizeof(struct icmp6hdr);
     137                 :            :         unsigned int transport_len = ipv6_transport_len(skb);
     138                 :            :         struct sk_buff *skb_chk;
     139                 :            : 
     140                 :          0 :         if (!ipv6_mc_may_pull(skb, len))
     141                 :            :                 return -EINVAL;
     142                 :            : 
     143                 :          0 :         skb_chk = skb_checksum_trimmed(skb, transport_len,
     144                 :            :                                        ipv6_mc_validate_checksum);
     145                 :          0 :         if (!skb_chk)
     146                 :            :                 return -EINVAL;
     147                 :            : 
     148                 :          0 :         if (skb_chk != skb)
     149                 :          0 :                 kfree_skb(skb_chk);
     150                 :            : 
     151                 :            :         return 0;
     152                 :            : }
     153                 :            : EXPORT_SYMBOL(ipv6_mc_check_icmpv6);
     154                 :            : 
     155                 :            : /**
     156                 :            :  * ipv6_mc_check_mld - checks whether this is a sane MLD packet
     157                 :            :  * @skb: the skb to validate
     158                 :            :  *
     159                 :            :  * Checks whether an IPv6 packet is a valid MLD packet. If so sets
     160                 :            :  * skb transport header accordingly and returns zero.
     161                 :            :  *
     162                 :            :  * -EINVAL: A broken packet was detected, i.e. it violates some internet
     163                 :            :  *  standard
     164                 :            :  * -ENOMSG: IP header validation succeeded but it is not an MLD packet.
     165                 :            :  * -ENOMEM: A memory allocation failure happened.
     166                 :            :  *
     167                 :            :  * Caller needs to set the skb network header and free any returned skb if it
     168                 :            :  * differs from the provided skb.
     169                 :            :  */
     170                 :          0 : int ipv6_mc_check_mld(struct sk_buff *skb)
     171                 :            : {
     172                 :            :         int ret;
     173                 :            : 
     174                 :          0 :         ret = ipv6_mc_check_ip6hdr(skb);
     175                 :          0 :         if (ret < 0)
     176                 :            :                 return ret;
     177                 :            : 
     178                 :          0 :         ret = ipv6_mc_check_exthdrs(skb);
     179                 :          0 :         if (ret < 0)
     180                 :            :                 return ret;
     181                 :            : 
     182                 :          0 :         ret = ipv6_mc_check_icmpv6(skb);
     183                 :          0 :         if (ret < 0)
     184                 :            :                 return ret;
     185                 :            : 
     186                 :          0 :         return ipv6_mc_check_mld_msg(skb);
     187                 :            : }
     188                 :            : EXPORT_SYMBOL(ipv6_mc_check_mld);
    

Generated by: LCOV version 1.14