只有在v3查询时才会调用函数timer_expire(),但在v1查询中没有类似的情况?

huangapple go评论142阅读模式
英文:

Understanding Linux Kernel Code: net/ipv4/igmp.c - Only on a v3 query the function timer_expire() is called but nothing similar on a v1 query?

问题

在net/ipv4/igmp.c文件中,您在IGMP v1查询的情况下找不到负责响应IGMP v1查询的代码行。您已经在igmp.c的每个函数的开头写了printk。当您发送一个IGMP v3查询时,您可以通过printk消息直接观察到函数调用顺序如下:

igmp_heard_query() -> igmp_gq_start_timer() -> igmp_gq_timer_expire() -> igmpv3_send_report()

这是您预期的结果,也是您正在寻找的,但只有在v3查询时才能观察到这一点。

当您发送IGMP v1查询时,您只能观察到以下调用:
igmp_rcv() -> igmp_heard_query()

这对我来说毫无意义,因为一个函数必须发送报告(我假设是igmp_send_report())。

您的设置似乎没有问题,您可以在Wireshark中看到v1查询/报告,并验证它们已发送到您自己编译的Linux内核。您没有意外地将查询发送到自己的笔记本电脑。

console_loglevel设置为8:

# cat /proc/sys/kernel/printk
8       4       1       7

在igmp_heard_query()函数中,我没有看到与v1相关的以下内容:

  1. 启动定时器
  2. 触发定时器到期
  3. 调用igmp_send_report()的内容

唯一的假设是更改in_dev->mr_v1_seen将自动触发in_dev->timer吗?但即使如此,仍然不清楚为什么我在printk中没有看到igmp_timer_expire()的调用。

英文:

I have trouble to find the code line in net/ipv4/igmp.c, which responds to a IGMP v1 query with a IGMP report.

I wrote printk in the beginning of each function in igmp.c. When I send a IGMP v3 query, I can observe via the printk messages straight forward the function calls:

igmp_heard_query() -> igmp_gq_start_timer() -> igmp_gq_timer_expire() -> igmpv3_send_report()

This is what I expected and what I'm looking for, except that I only can observe this on a v3 query.

When I send a IGMP v1 query I can only observe the calls
igmp_rcv() -> igmp_heard_query()

That makes absolutely no sense to me, because one function have to send the report. (I assume igmp_send_report())

My setup is fine, I can see the v1 queries/reports in wireshark and verified, that they are sent to my self compiled linux kernel. I'm not sending accidentally the query to my notebook.

console_loglevel is set to 8:

# cat /proc/sys/kernel/printk
8       4       1       7

igmp_heard_query()
https://github.com/torvalds/linux/blob/8032bf1233a74627ce69b803608e650f3f35971c/net/ipv4/igmp.c#L940

In this whole function, I don't see anything related for v1 to

  1. starting a timer
  2. trigger a timer to expire
  3. something which then calls igmp_send_report()

/* return true if packet was dropped */
static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
	int len)
{
	struct igmphdr 		*ih = igmp_hdr(skb);
	struct igmpv3_query *ih3 = igmpv3_query_hdr(skb);
	struct ip_mc_list	*im;
	__be32			group = ih->group;
	int			max_delay;
	int			mark = 0;
	struct net		*net = dev_net(in_dev->dev);


	if (len == 8) {
		if (ih->code == 0) {
			/* Alas, old v1 router presents here. */

			max_delay = IGMP_QUERY_RESPONSE_INTERVAL;
			in_dev->mr_v1_seen = jiffies +
				(in_dev->mr_qrv * in_dev->mr_qi) +
				in_dev->mr_qri;
			group = 0;
		} else {
			/* v2 router present */
			max_delay = ih->code*(HZ/IGMP_TIMER_SCALE);
			in_dev->mr_v2_seen = jiffies +
				(in_dev->mr_qrv * in_dev->mr_qi) +
				in_dev->mr_qri;
		}
		/* cancel the interface change timer */
		WRITE_ONCE(in_dev->mr_ifc_count, 0);
		if (del_timer(&in_dev->mr_ifc_timer))
			__in_dev_put(in_dev);
		/* clear deleted report items */
		igmpv3_clear_delrec(in_dev);
	} else if (len < 12) {
		return true;	/* ignore bogus packet; freed by caller */
	} else if (IGMP_V1_SEEN(in_dev)) {
		/* This is a v3 query with v1 queriers present */
		max_delay = IGMP_QUERY_RESPONSE_INTERVAL;
		group = 0;
	} else if (IGMP_V2_SEEN(in_dev)) {
		/* this is a v3 query with v2 queriers present;
		 * Interpretation of the max_delay code is problematic here.
		 * A real v2 host would use ih_code directly, while v3 has a
		 * different encoding. We use the v3 encoding as more likely
		 * to be intended in a v3 query.
		 */
		max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE);
		if (!max_delay)
			max_delay = 1;	/* can't mod w/ 0 */
	} else { /* v3 */
		if (!pskb_may_pull(skb, sizeof(struct igmpv3_query)))
			return true;

		ih3 = igmpv3_query_hdr(skb);
		if (ih3->nsrcs) {
			if (!pskb_may_pull(skb, sizeof(struct igmpv3_query)
					   + ntohs(ih3->nsrcs)*sizeof(__be32)))
				return true;
			ih3 = igmpv3_query_hdr(skb);
		}

		max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE);
		if (!max_delay)
			max_delay = 1;	/* can't mod w/ 0 */
		in_dev->mr_maxdelay = max_delay;

		/* RFC3376, 4.1.6. QRV and 4.1.7. QQIC, when the most recently
		 * received value was zero, use the default or statically
		 * configured value.
		 */
		in_dev->mr_qrv = ih3->qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv);
		in_dev->mr_qi = IGMPV3_QQIC(ih3->qqic)*HZ ?: IGMP_QUERY_INTERVAL;

		/* RFC3376, 8.3. Query Response Interval:
		 * The number of seconds represented by the [Query Response
		 * Interval] must be less than the [Query Interval].
		 */
		if (in_dev->mr_qri >= in_dev->mr_qi)
			in_dev->mr_qri = (in_dev->mr_qi/HZ - 1)*HZ;

		if (!group) { /* general query */
			if (ih3->nsrcs)
				return true;	/* no sources allowed */
			igmp_gq_start_timer(in_dev);
			return false;
		}
		/* mark sources to include, if group & source-specific */
		mark = ih3->nsrcs != 0;
	}

	/*
	 * - Start the timers in all of our membership records
	 *   that the query applies to for the interface on
	 *   which the query arrived excl. those that belong
	 *   to a "local" group (224.0.0.X)
	 * - For timers already running check if they need to
	 *   be reset.
	 * - Use the igmp->igmp_code field as the maximum
	 *   delay possible
	 */
	rcu_read_lock();
	for_each_pmc_rcu(in_dev, im) {
		int changed;

		if (group && group != im->multiaddr)
			continue;
		if (im->multiaddr == IGMP_ALL_HOSTS)
			continue;
		if (ipv4_is_local_multicast(im->multiaddr) &&
		    !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports))
			continue;
		spin_lock_bh(&im->lock);
		if (im->tm_running)
			im->gsquery = im->gsquery && mark;
		else
			im->gsquery = mark;
		changed = !im->gsquery ||
			igmp_marksources(im, ntohs(ih3->nsrcs), ih3->srcs);
		spin_unlock_bh(&im->lock);
		if (changed)
			igmp_mod_timer(im, max_delay);
	}
	rcu_read_unlock();
	return false;
}

The only assumption I have, is that changing in_dev->mr_v1_seen will automatically trigger the in_dev->timer? But then still it is unclear, why I don't see the call of igmp_timer_expire() with my printk.

/*
 *	A socket has joined a multicast group on device dev.
 */
static void ____ip_mc_inc_group(struct in_device *in_dev, __be32 addr,
				unsigned int mode, gfp_t gfp)
...
timer_setup(&im->timer, igmp_timer_expire, 0);

答案1

得分: 0

我找到了解决方案:
这就是行为,如果我没有加入一个多播组。

英文:

I found the solution:
That's the behavior, if I didn't joined a multicast group.

huangapple
  • 本文由 发表于 2023年1月9日 00:52:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/75049646.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定