Commit 6a7cc418 authored by Flavio Leitner's avatar Flavio Leitner Committed by David S. Miller

ipv6: send Change Status Report after DAD is completed

The RFC 3810 defines two type of messages for multicast
listeners. The "Current State Report" message, as the name
implies, refreshes the *current* state to the querier.
Since the querier sends Query messages periodically, there
is no need to retransmit the report.

On the other hand, any change should be reported immediately
using "State Change Report" messages. Since it's an event
triggered by a change and that it can be affected by packet
loss, the rfc states it should be retransmitted [RobVar] times
to make sure routers will receive timely.

Currently, we are sending "Current State Reports" after
DAD is completed.  Before that, we send messages using
unspecified address (::) which should be silently discarded
by routers.

This patch changes to send "State Change Report" messages
after DAD is completed fixing the behavior to be RFC compliant
and also to pass TAHI IPv6 testsuite.
Signed-off-by: default avatarFlavio Leitner <fbl@redhat.com>
Acked-by: default avatarHannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c3bc40e2
...@@ -1665,7 +1665,7 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc, ...@@ -1665,7 +1665,7 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc,
skb_tailroom(skb)) : 0) skb_tailroom(skb)) : 0)
static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc, static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,
int type, int gdeleted, int sdeleted) int type, int gdeleted, int sdeleted, int crsend)
{ {
struct inet6_dev *idev = pmc->idev; struct inet6_dev *idev = pmc->idev;
struct net_device *dev = idev->dev; struct net_device *dev = idev->dev;
...@@ -1757,7 +1757,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc, ...@@ -1757,7 +1757,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,
if (type == MLD2_ALLOW_NEW_SOURCES || if (type == MLD2_ALLOW_NEW_SOURCES ||
type == MLD2_BLOCK_OLD_SOURCES) type == MLD2_BLOCK_OLD_SOURCES)
return skb; return skb;
if (pmc->mca_crcount || isquery) { if (pmc->mca_crcount || isquery || crsend) {
/* make sure we have room for group header */ /* make sure we have room for group header */
if (skb && AVAILABLE(skb) < sizeof(struct mld2_grec)) { if (skb && AVAILABLE(skb) < sizeof(struct mld2_grec)) {
mld_sendpack(skb); mld_sendpack(skb);
...@@ -1789,7 +1789,7 @@ static void mld_send_report(struct inet6_dev *idev, struct ifmcaddr6 *pmc) ...@@ -1789,7 +1789,7 @@ static void mld_send_report(struct inet6_dev *idev, struct ifmcaddr6 *pmc)
type = MLD2_MODE_IS_EXCLUDE; type = MLD2_MODE_IS_EXCLUDE;
else else
type = MLD2_MODE_IS_INCLUDE; type = MLD2_MODE_IS_INCLUDE;
skb = add_grec(skb, pmc, type, 0, 0); skb = add_grec(skb, pmc, type, 0, 0, 0);
spin_unlock_bh(&pmc->mca_lock); spin_unlock_bh(&pmc->mca_lock);
} }
} else { } else {
...@@ -1798,7 +1798,7 @@ static void mld_send_report(struct inet6_dev *idev, struct ifmcaddr6 *pmc) ...@@ -1798,7 +1798,7 @@ static void mld_send_report(struct inet6_dev *idev, struct ifmcaddr6 *pmc)
type = MLD2_MODE_IS_EXCLUDE; type = MLD2_MODE_IS_EXCLUDE;
else else
type = MLD2_MODE_IS_INCLUDE; type = MLD2_MODE_IS_INCLUDE;
skb = add_grec(skb, pmc, type, 0, 0); skb = add_grec(skb, pmc, type, 0, 0, 0);
spin_unlock_bh(&pmc->mca_lock); spin_unlock_bh(&pmc->mca_lock);
} }
read_unlock_bh(&idev->lock); read_unlock_bh(&idev->lock);
...@@ -1843,13 +1843,13 @@ static void mld_send_cr(struct inet6_dev *idev) ...@@ -1843,13 +1843,13 @@ static void mld_send_cr(struct inet6_dev *idev)
if (pmc->mca_sfmode == MCAST_INCLUDE) { if (pmc->mca_sfmode == MCAST_INCLUDE) {
type = MLD2_BLOCK_OLD_SOURCES; type = MLD2_BLOCK_OLD_SOURCES;
dtype = MLD2_BLOCK_OLD_SOURCES; dtype = MLD2_BLOCK_OLD_SOURCES;
skb = add_grec(skb, pmc, type, 1, 0); skb = add_grec(skb, pmc, type, 1, 0, 0);
skb = add_grec(skb, pmc, dtype, 1, 1); skb = add_grec(skb, pmc, dtype, 1, 1, 0);
} }
if (pmc->mca_crcount) { if (pmc->mca_crcount) {
if (pmc->mca_sfmode == MCAST_EXCLUDE) { if (pmc->mca_sfmode == MCAST_EXCLUDE) {
type = MLD2_CHANGE_TO_INCLUDE; type = MLD2_CHANGE_TO_INCLUDE;
skb = add_grec(skb, pmc, type, 1, 0); skb = add_grec(skb, pmc, type, 1, 0, 0);
} }
pmc->mca_crcount--; pmc->mca_crcount--;
if (pmc->mca_crcount == 0) { if (pmc->mca_crcount == 0) {
...@@ -1880,8 +1880,8 @@ static void mld_send_cr(struct inet6_dev *idev) ...@@ -1880,8 +1880,8 @@ static void mld_send_cr(struct inet6_dev *idev)
type = MLD2_ALLOW_NEW_SOURCES; type = MLD2_ALLOW_NEW_SOURCES;
dtype = MLD2_BLOCK_OLD_SOURCES; dtype = MLD2_BLOCK_OLD_SOURCES;
} }
skb = add_grec(skb, pmc, type, 0, 0); skb = add_grec(skb, pmc, type, 0, 0, 0);
skb = add_grec(skb, pmc, dtype, 0, 1); /* deleted sources */ skb = add_grec(skb, pmc, dtype, 0, 1, 0); /* deleted sources */
/* filter mode changes */ /* filter mode changes */
if (pmc->mca_crcount) { if (pmc->mca_crcount) {
...@@ -1889,7 +1889,7 @@ static void mld_send_cr(struct inet6_dev *idev) ...@@ -1889,7 +1889,7 @@ static void mld_send_cr(struct inet6_dev *idev)
type = MLD2_CHANGE_TO_EXCLUDE; type = MLD2_CHANGE_TO_EXCLUDE;
else else
type = MLD2_CHANGE_TO_INCLUDE; type = MLD2_CHANGE_TO_INCLUDE;
skb = add_grec(skb, pmc, type, 0, 0); skb = add_grec(skb, pmc, type, 0, 0, 0);
pmc->mca_crcount--; pmc->mca_crcount--;
} }
spin_unlock_bh(&pmc->mca_lock); spin_unlock_bh(&pmc->mca_lock);
...@@ -1997,27 +1997,36 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) ...@@ -1997,27 +1997,36 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
goto out; goto out;
} }
static void mld_resend_report(struct inet6_dev *idev) static void mld_send_initial_cr(struct inet6_dev *idev)
{ {
if (mld_in_v1_mode(idev)) { struct sk_buff *skb;
struct ifmcaddr6 *mcaddr; struct ifmcaddr6 *pmc;
int type;
if (mld_in_v1_mode(idev))
return;
skb = NULL;
read_lock_bh(&idev->lock); read_lock_bh(&idev->lock);
for (mcaddr = idev->mc_list; mcaddr; mcaddr = mcaddr->next) { for (pmc=idev->mc_list; pmc; pmc=pmc->next) {
if (!(mcaddr->mca_flags & MAF_NOREPORT)) spin_lock_bh(&pmc->mca_lock);
igmp6_send(&mcaddr->mca_addr, idev->dev, if (pmc->mca_sfcount[MCAST_EXCLUDE])
ICMPV6_MGM_REPORT); type = MLD2_CHANGE_TO_EXCLUDE;
else
type = MLD2_CHANGE_TO_INCLUDE;
skb = add_grec(skb, pmc, type, 0, 0, 1);
spin_unlock_bh(&pmc->mca_lock);
} }
read_unlock_bh(&idev->lock); read_unlock_bh(&idev->lock);
} else { if (skb)
mld_send_report(idev, NULL); mld_sendpack(skb);
}
} }
void ipv6_mc_dad_complete(struct inet6_dev *idev) void ipv6_mc_dad_complete(struct inet6_dev *idev)
{ {
idev->mc_dad_count = idev->mc_qrv; idev->mc_dad_count = idev->mc_qrv;
if (idev->mc_dad_count) { if (idev->mc_dad_count) {
mld_resend_report(idev); mld_send_initial_cr(idev);
idev->mc_dad_count--; idev->mc_dad_count--;
if (idev->mc_dad_count) if (idev->mc_dad_count)
mld_dad_start_timer(idev, idev->mc_maxdelay); mld_dad_start_timer(idev, idev->mc_maxdelay);
...@@ -2028,7 +2037,7 @@ static void mld_dad_timer_expire(unsigned long data) ...@@ -2028,7 +2037,7 @@ static void mld_dad_timer_expire(unsigned long data)
{ {
struct inet6_dev *idev = (struct inet6_dev *)data; struct inet6_dev *idev = (struct inet6_dev *)data;
mld_resend_report(idev); mld_send_initial_cr(idev);
if (idev->mc_dad_count) { if (idev->mc_dad_count) {
idev->mc_dad_count--; idev->mc_dad_count--;
if (idev->mc_dad_count) if (idev->mc_dad_count)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment