static void __mld_query_work(struct sk_buff *skb)
{
	struct mld2_query *mlh2 = NULL;
	const struct in6_addr *group;
	unsigned long max_delay;
	struct inet6_dev *idev;
	struct ifmcaddr6 *ma;
	struct mld_msg *mld;
	int group_type;
	int mark = 0;
	int len, err;

	if (!pskb_may_pull(skb, sizeof(struct in6_addr)))
		goto kfree_skb;

	/* compute payload length excluding extension headers */
	len = ntohs(ipv6_hdr(skb)->payload_len) + sizeof(struct ipv6hdr);
	len -= skb_network_header_len(skb);

	/* RFC3810 6.2
	 * Upon reception of an MLD message that contains a Query, the node
	 * checks if the source address of the message is a valid link-local
	 * address, if the Hop Limit is set to 1, and if the Router Alert
	 * option is present in the Hop-By-Hop Options header of the IPv6
	 * packet.  If any of these checks fails, the packet is dropped.
	 */
	if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL) ||
	    ipv6_hdr(skb)->hop_limit != 1 ||
	    !(IP6CB(skb)->flags & IP6SKB_ROUTERALERT) ||
	    IP6CB(skb)->ra != htons(IPV6_OPT_ROUTERALERT_MLD))
		goto kfree_skb;

	idev = in6_dev_get(skb->dev);
	if (!idev)
		goto kfree_skb;

	mld = (struct mld_msg *)icmp6_hdr(skb);
	group = &mld->mld_mca;
	group_type = ipv6_addr_type(group);

	if (group_type != IPV6_ADDR_ANY &&
	    !(group_type&IPV6_ADDR_MULTICAST))
		goto out;

	if (len < MLD_V1_QUERY_LEN) {
		goto out;
	} else if (len == MLD_V1_QUERY_LEN || mld_in_v1_mode(idev)) {
		err = mld_process_v1(idev, mld, &max_delay,
				     len == MLD_V1_QUERY_LEN);
		if (err < 0)
			goto out;
	} else if (len >= MLD_V2_QUERY_LEN_MIN) {
		int srcs_offset = sizeof(struct mld2_query) -
				  sizeof(struct icmp6hdr);

		if (!pskb_may_pull(skb, srcs_offset))
			goto out;

		mlh2 = (struct mld2_query *)skb_transport_header(skb);

		mld_process_v2(idev, mlh2, &max_delay);

		if (group_type == IPV6_ADDR_ANY) { /* general query */
			if (mlh2->mld2q_nsrcs)
				goto out; /* no sources allowed */

			mld_gq_start_work(idev);
			goto out;
		}
		/* mark sources to include, if group & source-specific */
		if (mlh2->mld2q_nsrcs != 0) {
			if (!pskb_may_pull(skb, srcs_offset +
			    ntohs(mlh2->mld2q_nsrcs) * sizeof(struct in6_addr)))
				goto out;

			mlh2 = (struct mld2_query *)skb_transport_header(skb);
			mark = 1;
		}
	} else {
		goto out;
	}

	if (group_type == IPV6_ADDR_ANY) {
		for_each_mc_mclock(idev, ma) {
			igmp6_group_queried(ma, max_delay);
		}
	} else {
		for_each_mc_mclock(idev, ma) {
			if (!ipv6_addr_equal(group, &ma->mca_addr))
				continue;
			if (ma->mca_flags & MAF_TIMER_RUNNING) {
				/* gsquery <- gsquery && mark */
				if (!mark)
					ma->mca_flags &= ~MAF_GSQUERY;
			} else {
				/* gsquery <- mark */
				if (mark)
					ma->mca_flags |= MAF_GSQUERY;
				else
					ma->mca_flags &= ~MAF_GSQUERY;
			}
			if (!(ma->mca_flags & MAF_GSQUERY) ||
			    mld_marksources(ma, ntohs(mlh2->mld2q_nsrcs), mlh2->mld2q_srcs))
				igmp6_group_queried(ma, max_delay);
			break;
		}
	}

out:
	in6_dev_put(idev);
kfree_skb:
	consume_skb(skb);
}
static void __mld_query_work(struct sk_buff *skb)
{
	struct mld2_query *mlh2 = NULL;
	const struct in6_addr *group;
	unsigned long max_delay;
	struct inet6_dev *idev;
	struct ifmcaddr6 *ma;
	struct mld_msg *mld;
	int group_type;
	int mark = 0;
	int len, err;

	if (!pskb_may_pull(skb, sizeof(struct in6_addr)))
		goto kfree_skb;

	 
	len = ntohs(ipv6_hdr(skb)->payload_len) + sizeof(struct ipv6hdr);
	len -= skb_network_header_len(skb);

	 
	if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL) ||
	    ipv6_hdr(skb)->hop_limit != 1 ||
	    !(IP6CB(skb)->flags & IP6SKB_ROUTERALERT) ||
	    IP6CB(skb)->ra != htons(IPV6_OPT_ROUTERALERT_MLD))
		goto kfree_skb;

	idev = in6_dev_get(skb->dev);
	if (!idev)
		goto kfree_skb;

	mld = (struct mld_msg *)icmp6_hdr(skb);
	group = &mld->mld_mca;
	group_type = ipv6_addr_type(group);

	if (group_type != IPV6_ADDR_ANY &&
	    !(group_type&IPV6_ADDR_MULTICAST))
		goto out;

	if (len < MLD_V1_QUERY_LEN) {
		goto out;
	} else if (len == MLD_V1_QUERY_LEN || mld_in_v1_mode(idev)) {
		err = mld_process_v1(idev, mld, &max_delay,
				     len == MLD_V1_QUERY_LEN);
		if (err < 0)
			goto out;
	} else if (len >= MLD_V2_QUERY_LEN_MIN) {
		int srcs_offset = sizeof(struct mld2_query) -
				  sizeof(struct icmp6hdr);

		if (!pskb_may_pull(skb, srcs_offset))
			goto out;

		mlh2 = (struct mld2_query *)skb_transport_header(skb);

		mld_process_v2(idev, mlh2, &max_delay);

		if (group_type == IPV6_ADDR_ANY) {  
			if (mlh2->mld2q_nsrcs)
				goto out;  

			mld_gq_start_work(idev);
			goto out;
		}
		 
		if (mlh2->mld2q_nsrcs != 0) {
			if (!pskb_may_pull(skb, srcs_offset +
			    ntohs(mlh2->mld2q_nsrcs) * sizeof(struct in6_addr)))
				goto out;

			mlh2 = (struct mld2_query *)skb_transport_header(skb);
			mark = 1;
		}
	} else {
		goto out;
	}

	if (group_type == IPV6_ADDR_ANY) {
		for_each_mc_mclock(idev, ma) {
			igmp6_group_queried(ma, max_delay);
		}
	} else {
		for_each_mc_mclock(idev, ma) {
			if (!ipv6_addr_equal(group, &ma->mca_addr))
				continue;
			if (ma->mca_flags & MAF_TIMER_RUNNING) {
				 
				if (!mark)
					ma->mca_flags &= ~MAF_GSQUERY;
			} else {
				 
				if (mark)
					ma->mca_flags |= MAF_GSQUERY;
				else
					ma->mca_flags &= ~MAF_GSQUERY;
			}
			if (!(ma->mca_flags & MAF_GSQUERY) ||
			    mld_marksources(ma, ntohs(mlh2->mld2q_nsrcs), mlh2->mld2q_srcs))
				igmp6_group_queried(ma, max_delay);
			break;
		}
	}

out:
	in6_dev_put(idev);
kfree_skb:
	consume_skb(skb);
}