| 
									
										
										
										
											2010-04-18 12:42:05 +09:00
										 |  |  | #ifndef LINUX_MLD_H
 | 
					
						
							|  |  |  | #define LINUX_MLD_H
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/in6.h>
 | 
					
						
							|  |  |  | #include <linux/icmpv6.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* MLDv1 Query/Report/Done */ | 
					
						
							|  |  |  | struct mld_msg { | 
					
						
							|  |  |  | 	struct icmp6hdr		mld_hdr; | 
					
						
							|  |  |  | 	struct in6_addr		mld_mca; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define mld_type		mld_hdr.icmp6_type
 | 
					
						
							|  |  |  | #define mld_code		mld_hdr.icmp6_code
 | 
					
						
							|  |  |  | #define mld_cksum		mld_hdr.icmp6_cksum
 | 
					
						
							|  |  |  | #define mld_maxdelay		mld_hdr.icmp6_maxdelay
 | 
					
						
							|  |  |  | #define mld_reserved		mld_hdr.icmp6_dataun.un_data16[1]
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Multicast Listener Discovery version 2 headers */ | 
					
						
							|  |  |  | /* MLDv2 Report */ | 
					
						
							|  |  |  | struct mld2_grec { | 
					
						
							|  |  |  | 	__u8		grec_type; | 
					
						
							|  |  |  | 	__u8		grec_auxwords; | 
					
						
							|  |  |  | 	__be16		grec_nsrcs; | 
					
						
							|  |  |  | 	struct in6_addr	grec_mca; | 
					
						
							|  |  |  | 	struct in6_addr	grec_src[0]; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct mld2_report { | 
					
						
							|  |  |  | 	struct icmp6hdr		mld2r_hdr; | 
					
						
							|  |  |  | 	struct mld2_grec	mld2r_grec[0]; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define mld2r_type		mld2r_hdr.icmp6_type
 | 
					
						
							|  |  |  | #define mld2r_resv1		mld2r_hdr.icmp6_code
 | 
					
						
							|  |  |  | #define mld2r_cksum		mld2r_hdr.icmp6_cksum
 | 
					
						
							|  |  |  | #define mld2r_resv2		mld2r_hdr.icmp6_dataun.un_data16[0]
 | 
					
						
							|  |  |  | #define mld2r_ngrec		mld2r_hdr.icmp6_dataun.un_data16[1]
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* MLDv2 Query */ | 
					
						
							|  |  |  | struct mld2_query { | 
					
						
							|  |  |  | 	struct icmp6hdr		mld2q_hdr; | 
					
						
							|  |  |  | 	struct in6_addr		mld2q_mca; | 
					
						
							|  |  |  | #if defined(__LITTLE_ENDIAN_BITFIELD)
 | 
					
						
							|  |  |  | 	__u8			mld2q_qrv:3, | 
					
						
							|  |  |  | 				mld2q_suppress:1, | 
					
						
							|  |  |  | 				mld2q_resv2:4; | 
					
						
							|  |  |  | #elif defined(__BIG_ENDIAN_BITFIELD)
 | 
					
						
							|  |  |  | 	__u8			mld2q_resv2:4, | 
					
						
							|  |  |  | 				mld2q_suppress:1, | 
					
						
							|  |  |  | 				mld2q_qrv:3; | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | #error "Please fix <asm/byteorder.h>"
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	__u8			mld2q_qqic; | 
					
						
							|  |  |  | 	__be16			mld2q_nsrcs; | 
					
						
							|  |  |  | 	struct in6_addr		mld2q_srcs[0]; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define mld2q_type		mld2q_hdr.icmp6_type
 | 
					
						
							|  |  |  | #define mld2q_code		mld2q_hdr.icmp6_code
 | 
					
						
							|  |  |  | #define mld2q_cksum		mld2q_hdr.icmp6_cksum
 | 
					
						
							|  |  |  | #define mld2q_mrc		mld2q_hdr.icmp6_maxdelay
 | 
					
						
							|  |  |  | #define mld2q_resv1		mld2q_hdr.icmp6_dataun.un_data16[1]
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												net: ipv6: mld: fix v1/v2 switchback timeout to rfc3810, 9.12.
i) RFC3810, 9.2. Query Interval [QI] says:
   The Query Interval variable denotes the interval between General
   Queries sent by the Querier. Default value: 125 seconds. [...]
ii) RFC3810, 9.3. Query Response Interval [QRI] says:
  The Maximum Response Delay used to calculate the Maximum Response
  Code inserted into the periodic General Queries. Default value:
  10000 (10 seconds) [...] The number of seconds represented by the
  [Query Response Interval] must be less than the [Query Interval].
iii) RFC3810, 9.12. Older Version Querier Present Timeout [OVQPT] says:
  The Older Version Querier Present Timeout is the time-out for
  transitioning a host back to MLDv2 Host Compatibility Mode. When an
  MLDv1 query is received, MLDv2 hosts set their Older Version Querier
  Present Timer to [Older Version Querier Present Timeout].
  This value MUST be ([Robustness Variable] times (the [Query Interval]
  in the last Query received)) plus ([Query Response Interval]).
Hence, on *default* the timeout results in:
  [RV] = 2, [QI] = 125sec, [QRI] = 10sec
  [OVQPT] = [RV] * [QI] + [QRI] = 260sec
Having that said, we currently calculate [OVQPT] (here given as 'switchback'
variable) as ...
  switchback = (idev->mc_qrv + 1) * max_delay
RFC3810, 9.12. says "the [Query Interval] in the last Query received". In
section "9.14. Configuring timers", it is said:
  This section is meant to provide advice to network administrators on
  how to tune these settings to their network. Ambitious router
  implementations might tune these settings dynamically based upon
  changing characteristics of the network. [...]
iv) RFC38010, 9.14.2. Query Interval:
  The overall level of periodic MLD traffic is inversely proportional
  to the Query Interval. A longer Query Interval results in a lower
  overall level of MLD traffic. The value of the Query Interval MUST
  be equal to or greater than the Maximum Response Delay used to
  calculate the Maximum Response Code inserted in General Query
  messages.
I assume that was why switchback is calculated as is (3 * max_delay), although
this setting seems to be meant for routers only to configure their [QI]
interval for non-default intervals. So usage here like this is clearly wrong.
Concluding, the current behaviour in IPv6's multicast code is not conform
to the RFC as switch back is calculated wrongly. That is, it has a too small
value, so MLDv2 hosts switch back again to MLDv2 way too early, i.e. ~30secs
instead of ~260secs on default.
Hence, introduce necessary helper functions and fix this up properly as it
should be.
Introduced in 06da92283 ("[IPV6]: Add MLDv2 support."). Credits to Hannes
Frederic Sowa who also had a hand in this as well. Also thanks to Hangbin Liu
who did initial testing.
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Cc: David Stevens <dlstevens@us.ibm.com>
Cc: Hannes Frederic Sowa <hannes@stressinduktion.org>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
											
										 
											2013-09-04 00:19:37 +02:00
										 |  |  | /* RFC3810, 5.1.3. Maximum Response Code:
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * If Maximum Response Code >= 32768, Maximum Response Code represents a | 
					
						
							|  |  |  |  * floating-point value as follows: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  0 1 2 3 4 5 6 7 8 9 A B C D E F | 
					
						
							|  |  |  |  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 
					
						
							|  |  |  |  * |1| exp |          mant         | | 
					
						
							|  |  |  |  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #define MLDV2_MRC_EXP(value)	(((value) >> 12) & 0x0007)
 | 
					
						
							|  |  |  | #define MLDV2_MRC_MAN(value)	((value) & 0x0fff)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* RFC3810, 5.1.9. QQIC (Querier's Query Interval Code):
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * If QQIC >= 128, QQIC represents a floating-point value as follows: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  0 1 2 3 4 5 6 7 | 
					
						
							|  |  |  |  * +-+-+-+-+-+-+-+-+ | 
					
						
							|  |  |  |  * |1| exp | mant  | | 
					
						
							|  |  |  |  * +-+-+-+-+-+-+-+-+ | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #define MLDV2_QQIC_EXP(value)	(((value) >> 4) & 0x07)
 | 
					
						
							|  |  |  | #define MLDV2_QQIC_MAN(value)	((value) & 0x0f)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-04 00:19:39 +02:00
										 |  |  | static inline unsigned long mldv2_mrc(const struct mld2_query *mlh2) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* RFC3810, 5.1.3. Maximum Response Code */ | 
					
						
							|  |  |  | 	unsigned long ret, mc_mrc = ntohs(mlh2->mld2q_mrc); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (mc_mrc < 32768) { | 
					
						
							|  |  |  | 		ret = mc_mrc; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		unsigned long mc_man, mc_exp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		mc_exp = MLDV2_MRC_EXP(mc_mrc); | 
					
						
							|  |  |  | 		mc_man = MLDV2_MRC_MAN(mc_mrc); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ret = (mc_man | 0x1000) << (mc_exp + 3); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-18 12:42:05 +09:00
										 |  |  | #endif
 |