mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-12 13:55:32 +00:00
![Daniel Borkmann](/assets/img/avatar_default.png)
RFC2710 (MLDv1), section 3.7. says: The length of a received MLD message is computed by taking the IPv6 Payload Length value and subtracting the length of any IPv6 extension headers present between the IPv6 header and the MLD message. If that length is greater than 24 octets, that indicates that there are other fields present *beyond* the fields described above, perhaps belonging to a *future backwards-compatible* version of MLD. An implementation of the version of MLD specified in this document *MUST NOT* send an MLD message longer than 24 octets and MUST ignore anything past the first 24 octets of a received MLD message. RFC3810 (MLDv2), section 8.2.1. states for *listeners* regarding presence of MLDv1 routers: In order to be compatible with MLDv1 routers, MLDv2 hosts MUST operate in version 1 compatibility mode. [...] When Host Compatibility Mode is MLDv2, a host acts using the MLDv2 protocol on that interface. When Host Compatibility Mode is MLDv1, a host acts in MLDv1 compatibility mode, using *only* the MLDv1 protocol, on that interface. [...] While section 8.3.1. specifies *router* behaviour regarding presence of MLDv1 routers: MLDv2 routers may be placed on a network where there is at least one MLDv1 router. The following requirements apply: If an MLDv1 router is present on the link, the Querier MUST use the *lowest* version of MLD present on the network. This must be administratively assured. Routers that desire to be compatible with MLDv1 MUST have a configuration option to act in MLDv1 mode; if an MLDv1 router is present on the link, the system administrator must explicitly configure all MLDv2 routers to act in MLDv1 mode. When in MLDv1 mode, the Querier MUST send periodic General Queries truncated at the Multicast Address field (i.e., 24 bytes long), and SHOULD also warn about receiving an MLDv2 Query (such warnings must be rate-limited). The Querier MUST also fill in the Maximum Response Delay in the Maximum Response Code field, i.e., the exponential algorithm described in section 5.1.3. is not used. [...] That means that we should not get queries from different versions of MLD. When there's a MLDv1 router present, MLDv2 enforces truncation and MRC == MRD (both fields are overlapping within the 24 octet range). Section 8.3.2. specifies behaviour in the presence of MLDv1 multicast address *listeners*: MLDv2 routers may be placed on a network where there are hosts that have not yet been upgraded to MLDv2. In order to be compatible with MLDv1 hosts, MLDv2 routers MUST operate in version 1 compatibility mode. MLDv2 routers keep a compatibility mode per multicast address record. The compatibility mode of a multicast address is determined from the Multicast Address Compatibility Mode variable, which can be in one of the two following states: MLDv1 or MLDv2. The Multicast Address Compatibility Mode of a multicast address record is set to MLDv1 whenever an MLDv1 Multicast Listener Report is *received* for that multicast address. At the same time, the Older Version Host Present timer for the multicast address is set to Older Version Host Present Timeout seconds. The timer is re-set whenever a new MLDv1 Report is received for that multicast address. If the Older Version Host Present timer expires, the router switches back to Multicast Address Compatibility Mode of MLDv2 for that multicast address. [...] That means, what can happen is the following scenario, that hosts can act in MLDv1 compatibility mode when they previously have received an MLDv1 query (or, simply operate in MLDv1 mode-only); and at the same time, an MLDv2 router could start up and transmits MLDv2 startup query messages while being unaware of the current operational mode. Given RFC2710, section 3.7 we would need to answer to that with an MLDv1 listener report, so that the router according to RFC3810, section 8.3.2. would receive that and internally switch to MLDv1 compatibility as well. Right now, I believe since the initial implementation of MLDv2, Linux hosts would just silently drop such MLDv2 queries instead of replying with an MLDv1 listener report, which would prevent a MLDv2 router going into fallback mode (until it receives other MLDv1 queries). Since the mapping of MRC to MRD in exactly such cases can make use of the exponential algorithm from 5.1.3, we cannot [strictly speaking] be aware in MLDv1 of the encoding in MRC, it seems also not mentioned by the RFC. Since encodings are the same up to 32767, assume in such a situation this value as a hard upper limit we would clamp. We have asked one of the RFC authors on that regard, and he mentioned that there seem not to be any implementations that make use of that exponential algorithm on startup messages. In any case, this patch fixes this MLD interoperability issue. Signed-off-by: Daniel Borkmann <dborkman@redhat.com> Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org> Signed-off-by: David S. Miller <davem@davemloft.net>
113 lines
2.8 KiB
C
113 lines
2.8 KiB
C
#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]
|
|
|
|
/* 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)
|
|
|
|
#define MLD_EXP_MIN_LIMIT 32768UL
|
|
#define MLDV1_MRD_MAX_COMPAT (MLD_EXP_MIN_LIMIT - 1)
|
|
|
|
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 < MLD_EXP_MIN_LIMIT) {
|
|
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;
|
|
}
|
|
|
|
#endif
|