#include <linux/skbuff.h>
 #include <linux/timer.h>
 #include <linux/in.h>
+#include <linux/ip.h>
 #include <linux/refcount.h>
 #include <uapi/linux/igmp.h>
 
 #define IGMPV3_QQIC(value) IGMPV3_EXP(0x80, 4, 3, value)
 #define IGMPV3_MRC(value) IGMPV3_EXP(0x80, 4, 3, value)
 
+static inline int ip_mc_may_pull(struct sk_buff *skb, unsigned int len)
+{
+       if (skb_transport_offset(skb) + ip_transport_len(skb) < len)
+               return -EINVAL;
+
+       return pskb_may_pull(skb, len);
+}
+
 extern int ip_check_mc_rcu(struct in_device *dev, __be32 mc_addr, __be32 src_addr, u8 proto);
 extern int igmp_rcv(struct sk_buff *);
 extern int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr);
 extern void ip_mc_remap(struct in_device *);
 extern void ip_mc_dec_group(struct in_device *in_dev, __be32 addr);
 extern void ip_mc_inc_group(struct in_device *in_dev, __be32 addr);
-int ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed);
+int ip_mc_check_igmp(struct sk_buff *skb);
 
 #endif
 
 {
        return (struct iphdr *)skb_transport_header(skb);
 }
+
+static inline unsigned int ip_transport_len(const struct sk_buff *skb)
+{
+       return ntohs(ip_hdr(skb)->tot_len) - skb_network_header_len(skb);
+}
 #endif /* _LINUX_IP_H */
 
        return (struct ipv6hdr *)skb_transport_header(skb);
 }
 
+static inline unsigned int ipv6_transport_len(const struct sk_buff *skb)
+{
+       return ntohs(ipv6_hdr(skb)->payload_len) + sizeof(struct ipv6hdr) -
+              skb_network_header_len(skb);
+}
+
 /* 
    This structure contains results of exthdrs parsing
    as offsets from skb->nh.
 
        struct in6_addr         prefix;
 };
 
+#include <linux/ipv6.h>
 #include <linux/netdevice.h>
 #include <net/if_inet6.h>
 #include <net/ipv6.h>
 /*
  *     multicast prototypes (mcast.c)
  */
+static inline int ipv6_mc_may_pull(struct sk_buff *skb,
+                                  unsigned int len)
+{
+       if (skb_transport_offset(skb) + ipv6_transport_len(skb) < len)
+               return -EINVAL;
+
+       return pskb_may_pull(skb, len);
+}
+
 int ipv6_sock_mc_join(struct sock *sk, int ifindex,
                      const struct in6_addr *addr);
 int ipv6_sock_mc_drop(struct sock *sk, int ifindex,
 void ipv6_mc_remap(struct inet6_dev *idev);
 void ipv6_mc_init_dev(struct inet6_dev *idev);
 void ipv6_mc_destroy_dev(struct inet6_dev *idev);
-int ipv6_mc_check_mld(struct sk_buff *skb, struct sk_buff **skb_trimmed);
+int ipv6_mc_check_mld(struct sk_buff *skb);
 void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp);
 
 bool ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group,
 
  */
 static bool batadv_mcast_is_report_ipv4(struct sk_buff *skb)
 {
-       if (ip_mc_check_igmp(skb, NULL) < 0)
+       if (ip_mc_check_igmp(skb) < 0)
                return false;
 
        switch (igmp_hdr(skb)->type) {
  */
 static bool batadv_mcast_is_report_ipv6(struct sk_buff *skb)
 {
-       if (ipv6_mc_check_mld(skb, NULL) < 0)
+       if (ipv6_mc_check_mld(skb) < 0)
                return false;
 
        switch (icmp6_hdr(skb)->icmp6_type) {
 
 
        for (i = 0; i < num; i++) {
                len += sizeof(*grec);
-               if (!pskb_may_pull(skb, len))
+               if (!ip_mc_may_pull(skb, len))
                        return -EINVAL;
 
                grec = (void *)(skb->data + len - sizeof(*grec));
                type = grec->grec_type;
 
                len += ntohs(grec->grec_nsrcs) * 4;
-               if (!pskb_may_pull(skb, len))
+               if (!ip_mc_may_pull(skb, len))
                        return -EINVAL;
 
                /* We treat this as an IGMPv2 report for now. */
                                        struct sk_buff *skb,
                                        u16 vid)
 {
+       unsigned int nsrcs_offset;
        const unsigned char *src;
        struct icmp6hdr *icmp6h;
        struct mld2_grec *grec;
+       unsigned int grec_len;
        int i;
        int len;
        int num;
        int err = 0;
 
-       if (!pskb_may_pull(skb, sizeof(*icmp6h)))
+       if (!ipv6_mc_may_pull(skb, sizeof(*icmp6h)))
                return -EINVAL;
 
        icmp6h = icmp6_hdr(skb);
        for (i = 0; i < num; i++) {
                __be16 *nsrcs, _nsrcs;
 
-               nsrcs = skb_header_pointer(skb,
-                                          len + offsetof(struct mld2_grec,
-                                                         grec_nsrcs),
+               nsrcs_offset = len + offsetof(struct mld2_grec, grec_nsrcs);
+
+               if (skb_transport_offset(skb) + ipv6_transport_len(skb) <
+                   nsrcs_offset + sizeof(_nsrcs))
+                       return -EINVAL;
+
+               nsrcs = skb_header_pointer(skb, nsrcs_offset,
                                           sizeof(_nsrcs), &_nsrcs);
                if (!nsrcs)
                        return -EINVAL;
 
-               if (!pskb_may_pull(skb,
-                                  len + sizeof(*grec) +
-                                  sizeof(struct in6_addr) * ntohs(*nsrcs)))
+               grec_len = sizeof(*grec) +
+                          sizeof(struct in6_addr) * ntohs(*nsrcs);
+
+               if (!ipv6_mc_may_pull(skb, len + grec_len))
                        return -EINVAL;
 
                grec = (struct mld2_grec *)(skb->data + len);
-               len += sizeof(*grec) +
-                      sizeof(struct in6_addr) * ntohs(*nsrcs);
+               len += grec_len;
 
                /* We treat these as MLDv1 reports for now. */
                switch (grec->grec_type) {
                                   struct sk_buff *skb,
                                   u16 vid)
 {
+       unsigned int transport_len = ip_transport_len(skb);
        const struct iphdr *iph = ip_hdr(skb);
        struct igmphdr *ih = igmp_hdr(skb);
        struct net_bridge_mdb_entry *mp;
        struct br_ip saddr;
        unsigned long max_delay;
        unsigned long now = jiffies;
-       unsigned int offset = skb_transport_offset(skb);
        __be32 group;
 
        spin_lock(&br->multicast_lock);
 
        group = ih->group;
 
-       if (skb->len == offset + sizeof(*ih)) {
+       if (transport_len == sizeof(*ih)) {
                max_delay = ih->code * (HZ / IGMP_TIMER_SCALE);
 
                if (!max_delay) {
                        max_delay = 10 * HZ;
                        group = 0;
                }
-       } else if (skb->len >= offset + sizeof(*ih3)) {
+       } else if (transport_len >= sizeof(*ih3)) {
                ih3 = igmpv3_query_hdr(skb);
                if (ih3->nsrcs)
                        goto out;
                                  struct sk_buff *skb,
                                  u16 vid)
 {
+       unsigned int transport_len = ipv6_transport_len(skb);
        const struct ipv6hdr *ip6h = ipv6_hdr(skb);
        struct mld_msg *mld;
        struct net_bridge_mdb_entry *mp;
            (port && port->state == BR_STATE_DISABLED))
                goto out;
 
-       if (skb->len == offset + sizeof(*mld)) {
+       if (transport_len == sizeof(*mld)) {
                if (!pskb_may_pull(skb, offset + sizeof(*mld))) {
                        err = -EINVAL;
                        goto out;
                                 struct sk_buff *skb,
                                 u16 vid)
 {
-       struct sk_buff *skb_trimmed = NULL;
        const unsigned char *src;
        struct igmphdr *ih;
        int err;
 
-       err = ip_mc_check_igmp(skb, &skb_trimmed);
+       err = ip_mc_check_igmp(skb);
 
        if (err == -ENOMSG) {
                if (!ipv4_is_local_multicast(ip_hdr(skb)->daddr)) {
                err = br_ip4_multicast_add_group(br, port, ih->group, vid, src);
                break;
        case IGMPV3_HOST_MEMBERSHIP_REPORT:
-               err = br_ip4_multicast_igmp3_report(br, port, skb_trimmed, vid);
+               err = br_ip4_multicast_igmp3_report(br, port, skb, vid);
                break;
        case IGMP_HOST_MEMBERSHIP_QUERY:
-               br_ip4_multicast_query(br, port, skb_trimmed, vid);
+               br_ip4_multicast_query(br, port, skb, vid);
                break;
        case IGMP_HOST_LEAVE_MESSAGE:
                br_ip4_multicast_leave_group(br, port, ih->group, vid, src);
                break;
        }
 
-       if (skb_trimmed && skb_trimmed != skb)
-               kfree_skb(skb_trimmed);
-
        br_multicast_count(br, port, skb, BR_INPUT_SKB_CB(skb)->igmp,
                           BR_MCAST_DIR_RX);
 
                                 struct sk_buff *skb,
                                 u16 vid)
 {
-       struct sk_buff *skb_trimmed = NULL;
        const unsigned char *src;
        struct mld_msg *mld;
        int err;
 
-       err = ipv6_mc_check_mld(skb, &skb_trimmed);
+       err = ipv6_mc_check_mld(skb);
 
        if (err == -ENOMSG) {
                if (!ipv6_addr_is_ll_all_nodes(&ipv6_hdr(skb)->daddr))
                                                 src);
                break;
        case ICMPV6_MLD2_REPORT:
-               err = br_ip6_multicast_mld2_report(br, port, skb_trimmed, vid);
+               err = br_ip6_multicast_mld2_report(br, port, skb, vid);
                break;
        case ICMPV6_MGM_QUERY:
-               err = br_ip6_multicast_query(br, port, skb_trimmed, vid);
+               err = br_ip6_multicast_query(br, port, skb, vid);
                break;
        case ICMPV6_MGM_REDUCTION:
                src = eth_hdr(skb)->h_source;
                break;
        }
 
-       if (skb_trimmed && skb_trimmed != skb)
-               kfree_skb(skb_trimmed);
-
        br_multicast_count(br, port, skb, BR_INPUT_SKB_CB(skb)->igmp,
                           BR_MCAST_DIR_RX);
 
 
        return skb_checksum_simple_validate(skb);
 }
 
-static int __ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed)
+static int __ip_mc_check_igmp(struct sk_buff *skb)
 
 {
        struct sk_buff *skb_chk;
        if (ret)
                goto err;
 
-       if (skb_trimmed)
-               *skb_trimmed = skb_chk;
-       /* free now unneeded clone */
-       else if (skb_chk != skb)
-               kfree_skb(skb_chk);
-
        ret = 0;
 
 err:
-       if (ret && skb_chk && skb_chk != skb)
+       if (skb_chk && skb_chk != skb)
                kfree_skb(skb_chk);
 
        return ret;
 /**
  * ip_mc_check_igmp - checks whether this is a sane IGMP packet
  * @skb: the skb to validate
- * @skb_trimmed: to store an skb pointer trimmed to IPv4 packet tail (optional)
  *
  * Checks whether an IPv4 packet is a valid IGMP packet. If so sets
  * skb transport header accordingly and returns zero.
  * -ENOMSG: IP header validation succeeded but it is not an IGMP packet.
  * -ENOMEM: A memory allocation failure happened.
  *
- * Optionally, an skb pointer might be provided via skb_trimmed (or set it
- * to NULL): After parsing an IGMP packet successfully it will point to
- * an skb which has its tail aligned to the IP packet end. This might
- * either be the originally provided skb or a trimmed, cloned version if
- * the skb frame had data beyond the IP packet. A cloned skb allows us
- * to leave the original skb and its full frame unchanged (which might be
- * desirable for layer 2 frame jugglers).
- *
  * Caller needs to set the skb network header and free any returned skb if it
  * differs from the provided skb.
  */
-int ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed)
+int ip_mc_check_igmp(struct sk_buff *skb)
 {
        int ret = ip_mc_check_iphdr(skb);
 
        if (ip_hdr(skb)->protocol != IPPROTO_IGMP)
                return -ENOMSG;
 
-       return __ip_mc_check_igmp(skb, skb_trimmed);
+       return __ip_mc_check_igmp(skb);
 }
 EXPORT_SYMBOL(ip_mc_check_igmp);
 
 
        return skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo);
 }
 
-static int __ipv6_mc_check_mld(struct sk_buff *skb,
-                              struct sk_buff **skb_trimmed)
+static int __ipv6_mc_check_mld(struct sk_buff *skb)
 
 {
        struct sk_buff *skb_chk = NULL;
        if (ret)
                goto err;
 
-       if (skb_trimmed)
-               *skb_trimmed = skb_chk;
-       /* free now unneeded clone */
-       else if (skb_chk != skb)
-               kfree_skb(skb_chk);
-
        ret = 0;
 
 err:
-       if (ret && skb_chk && skb_chk != skb)
+       if (skb_chk && skb_chk != skb)
                kfree_skb(skb_chk);
 
        return ret;
 /**
  * ipv6_mc_check_mld - checks whether this is a sane MLD packet
  * @skb: the skb to validate
- * @skb_trimmed: to store an skb pointer trimmed to IPv6 packet tail (optional)
  *
  * Checks whether an IPv6 packet is a valid MLD packet. If so sets
  * skb transport header accordingly and returns zero.
  * -ENOMSG: IP header validation succeeded but it is not an MLD packet.
  * -ENOMEM: A memory allocation failure happened.
  *
- * Optionally, an skb pointer might be provided via skb_trimmed (or set it
- * to NULL): After parsing an MLD packet successfully it will point to
- * an skb which has its tail aligned to the IP packet end. This might
- * either be the originally provided skb or a trimmed, cloned version if
- * the skb frame had data beyond the IP packet. A cloned skb allows us
- * to leave the original skb and its full frame unchanged (which might be
- * desirable for layer 2 frame jugglers).
- *
  * Caller needs to set the skb network header and free any returned skb if it
  * differs from the provided skb.
  */
-int ipv6_mc_check_mld(struct sk_buff *skb, struct sk_buff **skb_trimmed)
+int ipv6_mc_check_mld(struct sk_buff *skb)
 {
        int ret;
 
        if (ret < 0)
                return ret;
 
-       return __ipv6_mc_check_mld(skb, skb_trimmed);
+       return __ipv6_mc_check_mld(skb);
 }
 EXPORT_SYMBOL(ipv6_mc_check_mld);