From patchwork Mon Aug 31 15:08:42 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nikolay Aleksandrov X-Patchwork-Id: 261730 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.1 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0D1FDC433E6 for ; Mon, 31 Aug 2020 15:12:01 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id CB2A12083E for ; Mon, 31 Aug 2020 15:12:00 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=cumulusnetworks.com header.i=@cumulusnetworks.com header.b="bxhjZXQL" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728210AbgHaPL4 (ORCPT ); Mon, 31 Aug 2020 11:11:56 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52062 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728301AbgHaPKO (ORCPT ); Mon, 31 Aug 2020 11:10:14 -0400 Received: from mail-wm1-x341.google.com (mail-wm1-x341.google.com [IPv6:2a00:1450:4864:20::341]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 20F54C061575 for ; Mon, 31 Aug 2020 08:10:10 -0700 (PDT) Received: by mail-wm1-x341.google.com with SMTP id a9so1902737wmm.2 for ; Mon, 31 Aug 2020 08:10:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cumulusnetworks.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=cqBBFmMS19JIRuY9JV073peAHW//ng5aHlsxA/FxST8=; b=bxhjZXQLNJCttNhDbbj4DJhzqfZRvEk5zWGaQskCA4AeON2LX4QnH7hG8Ik9x2Evgs ndnzVe5z7bDT0wVpUJrr8KJsjLVAZizPvCbjaEpDV9kM2UCSVsXxjpObiH8JnLQ+0cvo U7PCEwR7syXc3aTWZzDa4YBLAFuJslH9WO8sk= 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:in-reply-to :references:mime-version:content-transfer-encoding; bh=cqBBFmMS19JIRuY9JV073peAHW//ng5aHlsxA/FxST8=; b=VIEb6FjvYgl3h+oCetXSZ+jt6eA73XfZ/BKZIBuvecnfSJpLplClMaqf0YkjoAPzIP uatQ2EKh/kBW10dKJGLCKbllXxK6QXRb7KNbvgbgZqcLVKGHtCNm69q+qGGkrCfwxgDQ U8CUgNX8TUTgLCJxTfXSTl7Vk8yPWjHg7eKp/5B8XFC/QIYcU09fuYBeqLihLi3PR8Jj e+mdxF3ZhHzf/TTKZKHuKFAmcCp6nuSVbTyHcphjGA4WMh1fdRemZsJbEAbtl0WT8Pp8 PX965zugA8l2qPl3Av4oseiCtszWEU4dMg89e3d0e8ocqq3NM7YKM/HwEXd60tvFuVML 7d1w== X-Gm-Message-State: AOAM530pQu5tsG+ZX+CHfFhic/bp2KJ95jsyUNBzl2ShLvoK+4Ow8Hqn eLcketHqhaoB40KZzlt8QetnEK69h7opYR+C X-Google-Smtp-Source: ABdhPJwUncgu+olXr0NC7lAt61fzkB8F+D0eiAKw0nbWPewXzwKFtWJnvFVhdQvXmYr4d3sBslZIpA== X-Received: by 2002:a1c:1b8f:: with SMTP id b137mr1846929wmb.151.1598886608371; Mon, 31 Aug 2020 08:10:08 -0700 (PDT) Received: from localhost.localdomain (84-238-136-197.ip.btc-net.bg. [84.238.136.197]) by smtp.gmail.com with ESMTPSA id f6sm14181636wme.32.2020.08.31.08.10.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2020 08:10:07 -0700 (PDT) From: Nikolay Aleksandrov To: netdev@vger.kernel.org Cc: roopa@nvidia.com, bridge@lists.linux-foundation.org, davem@davemloft.net, Nikolay Aleksandrov Subject: [PATCH net-next 12/15] net: bridge: mcast: support for IGMPV3_CHANGE_TO_INCLUDE/EXCLUDE report Date: Mon, 31 Aug 2020 18:08:42 +0300 Message-Id: <20200831150845.1062447-13-nikolay@cumulusnetworks.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: <20200831150845.1062447-1-nikolay@cumulusnetworks.com> References: <20200831150845.1062447-1-nikolay@cumulusnetworks.com> MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org In order to process IGMPV3_CHANGE_TO_INCLUDE/EXCLUDE report types we need new helpers which allow us to mark entries based on their timer state and to query only marked entries. Signed-off-by: Nikolay Aleksandrov --- net/bridge/br_multicast.c | 273 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 2ba43d497515..2777e3eb07b9 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -1172,6 +1172,17 @@ static void __grp_src_modify_flags_all(struct net_bridge_port_group *pg, __grp_src_modify_flags(ent, set_flags, clear_flags); } +static void __grp_src_modify_flags_timer(struct net_bridge_port_group *pg, + u8 set_flags, u8 clear_flags, + bool pending) +{ + struct net_bridge_group_src *ent; + + hlist_for_each_entry(ent, &pg->src_list, node) + if (timer_pending(&ent->timer) == pending) + __grp_src_modify_flags(ent, set_flags, clear_flags); +} + static int __grp_src_delete_marked(struct net_bridge_port_group *pg) { struct net_bridge_group_src *ent; @@ -1187,6 +1198,61 @@ static int __grp_src_delete_marked(struct net_bridge_port_group *pg) return deleted; } +static void __grp_src_query_marked_and_rexmit(struct net_bridge_port_group *pg) +{ + struct net_bridge *br = pg->port->br; + u32 lmqc = br->multicast_last_member_count; + unsigned long lmqt, lmi, now = jiffies; + struct net_bridge_group_src *ent; + + lmqt = now + br_multicast_lmqt(br); + hlist_for_each_entry(ent, &pg->src_list, node) { + if (ent->flags & BR_SGRP_F_SEND) { + __grp_src_modify_flags(ent, 0, BR_SGRP_F_SEND); + if (ent->timer.expires > lmqt) { + if (br_opt_get(br, BROPT_MULTICAST_QUERIER) && + !timer_pending(&br->ip4_other_query.timer)) + ent->src_query_rexmit_cnt = lmqc; + mod_timer(&ent->timer, lmqt); + } + } + } + + if (!br_opt_get(br, BROPT_MULTICAST_QUERIER) || + timer_pending(&br->ip4_other_query.timer)) + return; + + __br_multicast_send_query(br, pg->port, pg, &pg->addr, + &pg->addr, true, 1, NULL); + + lmi = now + br->multicast_last_member_interval; + if (!timer_pending(&pg->rexmit_timer) || + time_after(pg->rexmit_timer.expires, lmi)) + mod_timer(&pg->rexmit_timer, lmi); +} + +static void __grp_send_query_and_rexmit(struct net_bridge_port_group *pg) +{ + struct net_bridge *br = pg->port->br; + unsigned long now = jiffies, lmi; + + if (br_opt_get(br, BROPT_MULTICAST_QUERIER) && + timer_pending(&br->ip4_other_query.timer)) { + lmi = now + br->multicast_last_member_interval; + pg->grp_query_rexmit_cnt = br->multicast_last_member_count - 1; + __br_multicast_send_query(br, pg->port, pg, &pg->addr, + &pg->addr, false, 0, NULL); + if (!timer_pending(&pg->rexmit_timer) || + time_after(pg->rexmit_timer.expires, lmi)) + mod_timer(&pg->rexmit_timer, lmi); + } + + if (pg->filter_mode == MCAST_EXCLUDE && + (!timer_pending(&pg->timer) || + time_after(pg->timer.expires, now + br_multicast_lmqt(br)))) + mod_timer(&pg->timer, now + br_multicast_lmqt(br)); +} + /* State Msg type New state Actions * INCLUDE (A) IS_IN (B) INCLUDE (A+B) (B)=GMI * INCLUDE (A) ALLOW (B) INCLUDE (A+B) (B)=GMI @@ -1311,6 +1377,207 @@ static bool br_multicast_isexc(struct net_bridge_port_group *pg, return changed; } +/* State Msg type New state Actions + * INCLUDE (A) TO_IN (B) INCLUDE (A+B) (B)=GMI + * Send Q(G,A-B) + */ +static bool __grp_src_toin_incl(struct net_bridge_port_group *pg, + __be32 *srcs, u32 nsrcs) +{ + struct net_bridge *br = pg->port->br; + u32 src_idx, to_send = pg->src_ents; + struct net_bridge_group_src *ent; + unsigned long now = jiffies; + bool changed = false; + struct br_ip src_ip; + + __grp_src_modify_flags_all(pg, BR_SGRP_F_SEND, 0); + + memset(&src_ip, 0, sizeof(src_ip)); + src_ip.proto = htons(ETH_P_IP); + for (src_idx = 0; src_idx < nsrcs; src_idx++) { + src_ip.u.ip4 = srcs[src_idx]; + ent = br_multicast_find_group_src(pg, &src_ip); + if (ent) { + __grp_src_modify_flags(ent, 0, BR_SGRP_F_SEND); + to_send--; + } else { + ent = br_multicast_new_group_src(pg, &src_ip); + if (ent) + changed = true; + } + if (ent) + mod_timer(&ent->timer, now + br_multicast_gmi(br)); + } + + if (to_send) + __grp_src_query_marked_and_rexmit(pg); + + return changed; +} + +/* State Msg type New state Actions + * EXCLUDE (X,Y) TO_IN (A) EXCLUDE (X+A,Y-A) (A)=GMI + * Send Q(G,X-A) + * Send Q(G) + */ +static bool __grp_src_toin_excl(struct net_bridge_port_group *pg, + __be32 *srcs, u32 nsrcs) +{ + struct net_bridge *br = pg->port->br; + u32 src_idx, to_send = pg->src_ents; + struct net_bridge_group_src *ent; + unsigned long now = jiffies; + bool changed = false; + struct br_ip src_ip; + + __grp_src_modify_flags_timer(pg, BR_SGRP_F_SEND, 0, true); + + memset(&src_ip, 0, sizeof(src_ip)); + src_ip.proto = htons(ETH_P_IP); + for (src_idx = 0; src_idx < nsrcs; src_idx++) { + src_ip.u.ip4 = srcs[src_idx]; + ent = br_multicast_find_group_src(pg, &src_ip); + if (ent) { + if (timer_pending(&ent->timer)) { + __grp_src_modify_flags(ent, 0, BR_SGRP_F_SEND); + to_send--; + } + } else { + ent = br_multicast_new_group_src(pg, &src_ip); + if (ent) + changed = true; + } + if (ent) + mod_timer(&ent->timer, now + br_multicast_gmi(br)); + } + + if (to_send) + __grp_src_query_marked_and_rexmit(pg); + + __grp_send_query_and_rexmit(pg); + + return changed; +} + +static bool br_multicast_toin(struct net_bridge_port_group *pg, + __be32 *srcs, u32 nsrcs) +{ + bool changed = false; + + switch (pg->filter_mode) { + case MCAST_INCLUDE: + changed = __grp_src_toin_incl(pg, srcs, nsrcs); + break; + case MCAST_EXCLUDE: + changed = __grp_src_toin_excl(pg, srcs, nsrcs); + break; + } + + return changed; +} + +/* State Msg type New state Actions + * INCLUDE (A) TO_EX (B) EXCLUDE (A*B,B-A) (B-A)=0 + * Delete (A-B) + * Send Q(G,A*B) + * Group Timer=GMI + */ +static void __grp_src_toex_incl(struct net_bridge_port_group *pg, + __be32 *srcs, u32 nsrcs) +{ + struct net_bridge_group_src *ent; + u32 src_idx, to_send = 0; + struct br_ip src_ip; + + __grp_src_modify_flags_all(pg, BR_SGRP_F_DELETE, BR_SGRP_F_SEND); + + memset(&src_ip, 0, sizeof(src_ip)); + src_ip.proto = htons(ETH_P_IP); + for (src_idx = 0; src_idx < nsrcs; src_idx++) { + src_ip.u.ip4 = srcs[src_idx]; + ent = br_multicast_find_group_src(pg, &src_ip); + if (ent) { + __grp_src_modify_flags(ent, BR_SGRP_F_SEND, + BR_SGRP_F_DELETE); + to_send++; + } else { + br_multicast_new_group_src(pg, &src_ip); + } + } + + __grp_src_delete_marked(pg); + if (to_send) + __grp_src_query_marked_and_rexmit(pg); +} + +/* State Msg type New state Actions + * EXCLUDE (X,Y) TO_EX (A) EXCLUDE (A-Y,Y*A) (A-X-Y)=Group Timer + * Delete (X-A) + * Delete (Y-A) + * Send Q(G,A-Y) + * Group Timer=GMI + */ +static bool __grp_src_toex_excl(struct net_bridge_port_group *pg, + __be32 *srcs, u32 nsrcs) +{ + struct net_bridge_group_src *ent; + u32 src_idx, to_send = 0; + bool changed = false; + struct br_ip src_ip; + + __grp_src_modify_flags_all(pg, BR_SGRP_F_DELETE, BR_SGRP_F_SEND); + + memset(&src_ip, 0, sizeof(src_ip)); + src_ip.proto = htons(ETH_P_IP); + for (src_idx = 0; src_idx < nsrcs; src_idx++) { + src_ip.u.ip4 = srcs[src_idx]; + ent = br_multicast_find_group_src(pg, &src_ip); + if (ent) { + __grp_src_modify_flags(ent, 0, BR_SGRP_F_DELETE); + } else { + ent = br_multicast_new_group_src(pg, &src_ip); + if (ent) { + mod_timer(&ent->timer, pg->timer.expires); + changed = true; + } + } + if (ent && timer_pending(&ent->timer)) { + __grp_src_modify_flags(ent, BR_SGRP_F_SEND, 0); + to_send++; + } + } + + if (__grp_src_delete_marked(pg)) + changed = true; + if (to_send) + __grp_src_query_marked_and_rexmit(pg); + + return changed; +} + +static bool br_multicast_toex(struct net_bridge_port_group *pg, + __be32 *srcs, u32 nsrcs) +{ + struct net_bridge *br = pg->port->br; + bool changed = false; + + switch (pg->filter_mode) { + case MCAST_INCLUDE: + __grp_src_toex_incl(pg, srcs, nsrcs); + changed = true; + break; + case MCAST_EXCLUDE: + __grp_src_toex_excl(pg, srcs, nsrcs); + break; + } + + pg->filter_mode = MCAST_EXCLUDE; + mod_timer(&pg->timer, jiffies + br_multicast_gmi(br)); + + return changed; +} + static struct net_bridge_port_group * br_multicast_find_port(struct net_bridge_mdb_entry *mp, struct net_bridge_port *p, @@ -1413,6 +1680,12 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br, case IGMPV3_MODE_IS_EXCLUDE: changed = br_multicast_isexc(pg, grec->grec_src, nsrcs); break; + case IGMPV3_CHANGE_TO_INCLUDE: + changed = br_multicast_toin(pg, grec->grec_src, nsrcs); + break; + case IGMPV3_CHANGE_TO_EXCLUDE: + changed = br_multicast_toex(pg, grec->grec_src, nsrcs); + break; } if (changed) br_mdb_notify(br->dev, mdst, pg, RTM_NEWMDB);