From patchwork Wed Jun 13 06:32:31 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hangbin Liu X-Patchwork-Id: 928686 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="KJyLpRrg"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 415H5q6flqz9ry1 for ; Wed, 13 Jun 2018 16:34:07 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754475AbeFMGeE (ORCPT ); Wed, 13 Jun 2018 02:34:04 -0400 Received: from mail-pf0-f195.google.com ([209.85.192.195]:41957 "EHLO mail-pf0-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754416AbeFMGeD (ORCPT ); Wed, 13 Jun 2018 02:34:03 -0400 Received: by mail-pf0-f195.google.com with SMTP id a11-v6so844478pff.8 for ; Tue, 12 Jun 2018 23:34:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=VKaYlvvj0ZdnvPBMZZI6gsOkF4z4V33ytH4c17EZ34U=; b=KJyLpRrgQYxVD1aslllG9mD+h2bEeF8n70By11T4QhilsadN+O7w2g5kDaarwq3Sa9 JoGuKSzlfMXDfmC+nfNW143PGN/tkq1s7UCXFe3H2Vc2Cb64egVA5ZootICztWYp6/+o n3sequrjLFD71U0Y7waMS1vrsUhS+TXbrHtVdL2WDhUd953HdiIZuawnsBML11hytEFH nHNM9rL6JXxbFASiGGktY0luGnNBbPLfGYGEvW9N+aaPWA9HOjywplB9BLwghd1O8aqo QNoGRLW/pM7qi+g5HS8XzohLOzMhKnimGLLnl/0i5ai8aXxFBpoPWd0C4lL2EhjFwjr+ 93jA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=VKaYlvvj0ZdnvPBMZZI6gsOkF4z4V33ytH4c17EZ34U=; b=SD3R06/WhV1mLQq+IT4RywMecsmajwi5vOIMoanWhIfzMs8xN5VA4HMv017ChwQXBX EXAHsbmv2opuTweBXj9d7lWF1YgORhSfNQ9TkVNUUAQD8t2j2wwLJ+6yhtJzgMm/lPQt iHCXsTID4zqrBPRwccPU09uNc2ZDs6XpIJgC/L+WTpdQ7lVzNjKokHXU85FZCFE2pTJm TvsNQAAD7leDJi3M3JuW+KcwYsuBFgdD2sZ6iwRGQ9Dwh+jSd4msDc7qy/2hwtfdoRSE ToLMofCRk9Er6CROLHdXzQn+4gjj2ETUYYJxTiTLtyITnj4zwDe5df8pnTjd87Dplg+H g5uw== X-Gm-Message-State: APt69E0FQbc1hZcJqHndIuoeafEku029V0WuniRaIKEe/MW8g/6Apwbo mwWSZl6a5hw3i1q0sZCVlC2bqfC2 X-Google-Smtp-Source: ADUXVKLIJHB2ir3XpuOzbXH1BbMiyc7mjrBQaR+9jdaiHP+1916+5aLW5wIW6c+KtlS0sNPpFZJjOg== X-Received: by 2002:a62:da07:: with SMTP id c7-v6mr3594458pfh.106.1528871642653; Tue, 12 Jun 2018 23:34:02 -0700 (PDT) Received: from leo.usersys.redhat.com ([209.132.188.80]) by smtp.gmail.com with ESMTPSA id k12-v6sm2011689pgo.31.2018.06.12.23.33.59 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 12 Jun 2018 23:34:02 -0700 (PDT) From: Hangbin Liu To: netdev@vger.kernel.org Cc: "David S. Miller" , Paolo Abeni , Stefano Brivio , Daniel Borkmann , WANG Cong , , Hangbin Liu Subject: [PATCH net] net/multicast: clean change record if add new INCLUDE group Date: Wed, 13 Jun 2018 14:32:31 +0800 Message-Id: <1528871551-17879-1-git-send-email-liuhangbin@gmail.com> X-Mailer: git-send-email 2.5.5 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Based on RFC3376 5.1 and RFC3810 6.1: If no interface state existed for that multicast address before the change (i.e., the change consisted of creating a new per-interface record), or if no state exists after the change (i.e., the change consisted of deleting a per-interface record), then the "non-existent" state is considered to have a filter mode of INCLUDE and an empty source list. Which means a new multicast group should start with state IN(). That is exactly what we did with ip_mc_join_group()/ipv6_sock_mc_join(), which adds a group with state EX() and init crcount to mc_qrv. The kernel will send a TO_EX() report message after adding group. This is what IGMPv3/MLDv2 ASM(Any-Source Multicast) mode should look like. But for IGMPv3/MLDv2 SSM JOIN_SOURCE_GROUP mode, we split the group joining into two steps. First step we join the group like ASM, i.e. via ip_mc_join_group()/ipv6_sock_mc_join(). So the state changes from IN() to EX(). Then we add the Source-specific address with INCLUDE mode. So the state changes from EX() to IN(A). Before the first step sends a group change record, we finished the second step. So we will only send the second change record. i.e. TO_IN(A) Regarding the RFC stands, we should actually send an ALLOW(A) message for SSM JOIN_SOURCE_GROUP as the state should mimic the 'IN() to IN(A)' transition. The issue was exposed by commit a052517a8ff65 ("net/multicast: should not send source list records when have filter mode change"). Before this commit we will send both ALLOW(A) and TO_IN(A). After this commit we only send TO_IN(A). Fix it by adding a is_new key to clean the crcount when we add a new INCLUDE SSM group. Fixes: a052517a8ff65 ("net/multicast: should not send source list records when have filter mode change") Reviewed-by: Paolo Abeni Reviewed-by: Stefano Brivio Signed-off-by: Hangbin Liu --- include/linux/igmp.h | 2 +- include/net/ipv6.h | 2 +- net/ipv4/igmp.c | 27 ++++++++++++++++++++++++++- net/ipv4/ip_sockglue.c | 8 ++++++-- net/ipv6/ipv6_sockglue.c | 4 +++- net/ipv6/mcast.c | 25 ++++++++++++++++++++++++- 6 files changed, 61 insertions(+), 7 deletions(-) diff --git a/include/linux/igmp.h b/include/linux/igmp.h index f823185..32cb02b 100644 --- a/include/linux/igmp.h +++ b/include/linux/igmp.h @@ -112,7 +112,7 @@ extern int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr); extern int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr); extern void ip_mc_drop_socket(struct sock *sk); extern int ip_mc_source(int add, int omode, struct sock *sk, - struct ip_mreq_source *mreqs, int ifindex); + struct ip_mreq_source *mreqs, int ifindex, bool is_new); extern int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf,int ifindex); extern int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf, struct ip_msfilter __user *optval, int __user *optlen); diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 836f31a..754c5cb 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -1065,7 +1065,7 @@ struct group_source_req; struct group_filter; int ip6_mc_source(int add, int omode, struct sock *sk, - struct group_source_req *pgsr); + struct group_source_req *pgsr, bool is_new); int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf); int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf, struct group_filter __user *optval, int __user *optlen); diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index b26a81a..8d6ecc3 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -2249,8 +2249,27 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) } EXPORT_SYMBOL(ip_mc_leave_group); +static void ip_mc_clear_cr(struct in_device *in_dev, __be32 pmca) +{ +#ifdef CONFIG_IP_MULTICAST + struct ip_mc_list *pmc; + + rcu_read_lock(); + for_each_pmc_rcu(in_dev, pmc) { + if (pmca == pmc->multiaddr) + break; + } + if (pmc) { + spin_lock_bh(&pmc->lock); + pmc->crcount = 0; + spin_unlock_bh(&pmc->lock); + } + rcu_read_unlock(); +#endif +} + int ip_mc_source(int add, int omode, struct sock *sk, struct - ip_mreq_source *mreqs, int ifindex) + ip_mreq_source *mreqs, int ifindex, bool is_new) { int err; struct ip_mreqn imr; @@ -2301,6 +2320,12 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, pmc->sfmode, 0, NULL, 0); pmc->sfmode = omode; + /* Based on RFC3376 5.1, for newly added INCLUDE SSM, we should + * not send filter-mode change record as the mode should be + * from IN() to IN(A). + */ + if (is_new) + ip_mc_clear_cr(in_dev, mreqs->imr_multiaddr); } psl = rtnl_dereference(pmc->sflist); diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 57bbb06..8d8c0cd 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -962,6 +962,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, case IP_DROP_SOURCE_MEMBERSHIP: { struct ip_mreq_source mreqs; + bool is_new = false; int omode, add; if (optlen != sizeof(struct ip_mreq_source)) @@ -987,11 +988,12 @@ static int do_ip_setsockopt(struct sock *sk, int level, break; omode = MCAST_INCLUDE; add = 1; + is_new = true; } else /* IP_DROP_SOURCE_MEMBERSHIP */ { omode = MCAST_INCLUDE; add = 0; } - err = ip_mc_source(add, omode, sk, &mreqs, 0); + err = ip_mc_source(add, omode, sk, &mreqs, 0, is_new); break; } case MCAST_JOIN_GROUP: @@ -1027,6 +1029,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, struct group_source_req greqs; struct ip_mreq_source mreqs; struct sockaddr_in *psin; + bool is_new = false; int omode, add; if (optlen != sizeof(struct group_source_req)) @@ -1065,12 +1068,13 @@ static int do_ip_setsockopt(struct sock *sk, int level, greqs.gsr_interface = mreq.imr_ifindex; omode = MCAST_INCLUDE; add = 1; + is_new = true; } else /* MCAST_LEAVE_SOURCE_GROUP */ { omode = MCAST_INCLUDE; add = 0; } err = ip_mc_source(add, omode, sk, &mreqs, - greqs.gsr_interface); + greqs.gsr_interface, is_new); break; } case MCAST_MSFILTER: diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 4d780c7..36e7c40 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -695,6 +695,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, case MCAST_UNBLOCK_SOURCE: { struct group_source_req greqs; + bool is_new = false; int omode, add; if (optlen < sizeof(struct group_source_req)) @@ -725,11 +726,12 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, break; omode = MCAST_INCLUDE; add = 1; + is_new = true; } else /* MCAST_LEAVE_SOURCE_GROUP */ { omode = MCAST_INCLUDE; add = 0; } - retv = ip6_mc_source(add, omode, sk, &greqs); + retv = ip6_mc_source(add, omode, sk, &greqs, is_new); break; } case MCAST_MSFILTER: diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 793159d..f508a1c 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -315,8 +315,25 @@ void ipv6_sock_mc_close(struct sock *sk) rtnl_unlock(); } +static void ip6_mc_clear_cr(struct inet6_dev *idev, const struct in6_addr *pmca) +{ + struct ifmcaddr6 *pmc; + + read_lock_bh(&idev->lock); + for (pmc = idev->mc_list; pmc; pmc = pmc->next) { + if (ipv6_addr_equal(pmca, &pmc->mca_addr)) + break; + } + if (pmc) { + spin_lock_bh(&pmc->mca_lock); + pmc->mca_crcount = 0; + spin_unlock_bh(&pmc->mca_lock); + } + read_unlock_bh(&idev->lock); +} + int ip6_mc_source(int add, int omode, struct sock *sk, - struct group_source_req *pgsr) + struct group_source_req *pgsr, bool is_new) { struct in6_addr *source, *group; struct ipv6_mc_socklist *pmc; @@ -365,6 +382,12 @@ int ip6_mc_source(int add, int omode, struct sock *sk, ip6_mc_add_src(idev, group, omode, 0, NULL, 0); ip6_mc_del_src(idev, group, pmc->sfmode, 0, NULL, 0); pmc->sfmode = omode; + /* Based on RFC3810 6.1, for newly added INCLUDE SSM, we + * should not send filter-mode change record as the mode + * should be from IN() to IN(A). + */ + if (is_new) + ip6_mc_clear_cr(idev, group); } write_lock(&pmc->sflock);