blob: 40b851417535af58032720b715a7b5bc3e2e8bd9 [file] [log] [blame]
Pierb0328e42018-03-27 11:29:42 -07001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.segmentrouting.mcast;
18
Pier96f63cb2018-04-17 16:29:56 +020019import com.google.common.collect.ImmutableMap;
Pierb0328e42018-03-27 11:29:42 -070020import com.google.common.collect.ImmutableSet;
Pier96f63cb2018-04-17 16:29:56 +020021import com.google.common.collect.Maps;
22import com.google.common.hash.HashFunction;
23import com.google.common.hash.Hashing;
Pierb0328e42018-03-27 11:29:42 -070024import org.onlab.packet.Ethernet;
25import org.onlab.packet.IpAddress;
26import org.onlab.packet.MacAddress;
27import org.onlab.packet.VlanId;
28import org.onosproject.cluster.NodeId;
29import org.onosproject.core.ApplicationId;
30import org.onosproject.mcast.api.McastRoute;
31import org.onosproject.net.ConnectPoint;
32import org.onosproject.net.DeviceId;
33import org.onosproject.net.HostId;
34import org.onosproject.net.PortNumber;
35import org.onosproject.net.config.basics.McastConfig;
36import org.onosproject.net.flow.DefaultTrafficSelector;
37import org.onosproject.net.flow.DefaultTrafficTreatment;
38import org.onosproject.net.flow.TrafficSelector;
39import org.onosproject.net.flow.TrafficTreatment;
40import org.onosproject.net.flow.criteria.Criteria;
41import org.onosproject.net.flow.criteria.VlanIdCriterion;
42import org.onosproject.net.flow.instructions.Instructions;
43import org.onosproject.net.flowobjective.DefaultFilteringObjective;
44import org.onosproject.net.flowobjective.DefaultForwardingObjective;
45import org.onosproject.net.flowobjective.DefaultNextObjective;
46import org.onosproject.net.flowobjective.DefaultObjectiveContext;
47import org.onosproject.net.flowobjective.FilteringObjective;
48import org.onosproject.net.flowobjective.ForwardingObjective;
49import org.onosproject.net.flowobjective.NextObjective;
50import org.onosproject.net.flowobjective.ObjectiveContext;
51import org.onosproject.segmentrouting.SegmentRoutingManager;
52import org.onosproject.segmentrouting.SegmentRoutingService;
53import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
54import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
55import org.slf4j.Logger;
56
57import java.util.Collection;
Pierb0328e42018-03-27 11:29:42 -070058import java.util.Map;
59import java.util.Set;
60import java.util.stream.Collectors;
61
62import static org.onosproject.net.flow.criteria.Criterion.Type.VLAN_VID;
63import static org.onosproject.segmentrouting.SegmentRoutingManager.INTERNAL_VLAN;
64
65/**
66 * Utility class for Multicast Handler.
67 */
68class McastUtils {
69
70 // Internal reference to the log
71 private final Logger log;
72 // Internal reference to SR Manager
73 private SegmentRoutingManager srManager;
74 // Internal reference to the app id
75 private ApplicationId coreAppId;
Pier96f63cb2018-04-17 16:29:56 +020076 // Hashing function for the multicast hasher
77 private static final HashFunction HASH_FN = Hashing.md5();
78 // Read only cache of the Mcast leader
79 private Map<IpAddress, NodeId> mcastLeaderCache;
Pierb0328e42018-03-27 11:29:42 -070080
81 /**
82 * Builds a new McastUtils object.
83 *
84 * @param srManager the SR manager
85 * @param coreAppId the core application id
86 * @param log log reference of the McastHandler
87 */
88 McastUtils(SegmentRoutingManager srManager, ApplicationId coreAppId, Logger log) {
89 this.srManager = srManager;
90 this.coreAppId = coreAppId;
91 this.log = log;
Pier96f63cb2018-04-17 16:29:56 +020092 this.mcastLeaderCache = Maps.newConcurrentMap();
Pierb0328e42018-03-27 11:29:42 -070093 }
94
95 /**
Pier477e0062018-04-20 14:14:34 +020096 * Clean up when deactivating the application.
97 */
98 public void terminate() {
99 mcastLeaderCache.clear();
100 }
101
102 /**
Pierb0328e42018-03-27 11:29:42 -0700103 * Get router mac using application config and the connect point.
104 *
105 * @param deviceId the device id
106 * @param port the port number
107 * @return the router mac if the port is configured, otherwise null
108 */
109 private MacAddress getRouterMac(DeviceId deviceId, PortNumber port) {
110 // Do nothing if the port is configured as suppressed
111 ConnectPoint connectPoint = new ConnectPoint(deviceId, port);
112 SegmentRoutingAppConfig appConfig = srManager.cfgService
113 .getConfig(srManager.appId(), SegmentRoutingAppConfig.class);
114 if (appConfig != null && appConfig.suppressSubnet().contains(connectPoint)) {
115 log.info("Ignore suppressed port {}", connectPoint);
116 return MacAddress.NONE;
117 }
118 // Get the router mac using the device configuration
119 MacAddress routerMac;
120 try {
121 routerMac = srManager.deviceConfiguration().getDeviceMac(deviceId);
122 } catch (DeviceConfigNotFoundException dcnfe) {
123 log.warn("Fail to push filtering objective since device is not configured. Abort");
124 return MacAddress.NONE;
125 }
126 return routerMac;
127 }
128
129 /**
130 * Adds filtering objective for given device and port.
131 *
132 * @param deviceId device ID
133 * @param port ingress port number
134 * @param assignedVlan assigned VLAN ID
135 * @param mcastIp the group address
136 * @param mcastRole the role of the device
137 */
138 void addFilterToDevice(DeviceId deviceId, PortNumber port, VlanId assignedVlan,
139 IpAddress mcastIp, McastRole mcastRole) {
140
141 MacAddress routerMac = getRouterMac(deviceId, port);
142 if (routerMac.equals(MacAddress.NONE)) {
143 return;
144 }
145
146 FilteringObjective.Builder filtObjBuilder = filterObjBuilder(port, assignedVlan, mcastIp,
147 routerMac, mcastRole);
148 ObjectiveContext context = new DefaultObjectiveContext(
149 (objective) -> log.debug("Successfully add filter on {}/{}, vlan {}",
150 deviceId, port.toLong(), assignedVlan),
151 (objective, error) ->
152 log.warn("Failed to add filter on {}/{}, vlan {}: {}",
153 deviceId, port.toLong(), assignedVlan, error));
154 srManager.flowObjectiveService.filter(deviceId, filtObjBuilder.add(context));
155 }
156
157 /**
Andrea Campanellab955fec2018-04-27 14:44:15 +0200158 * Adds filtering objectives to drop any unknown multicast.
159 *
160 * @param deviceId device ID
161 * @param ipv4 install a drop for Ipv4 addresses or Ipv6 addresses
162 * @param assignedVlans the vlans for wich to drop the mcast traffic
163 */
164 void addDropFiltersToDevice(DeviceId deviceId, boolean ipv4, Set<VlanId> assignedVlans) {
165 MacAddress routerMac;
166 try {
167 routerMac = srManager.deviceConfiguration().getDeviceMac(deviceId);
168 } catch (DeviceConfigNotFoundException dcnfe) {
169 log.warn("Fail to push filtering objective since device is not configured. Abort");
170 return;
171 }
172 assignedVlans.forEach(assignedVlan -> {
173 FilteringObjective.Builder filtObjBuilder = filterObjBuilderDrop(ipv4, routerMac, assignedVlan);
174 ObjectiveContext context = new DefaultObjectiveContext(
175 (objective) -> log.debug("Successfully add filter on {}",
176 deviceId),
177 (objective, error) ->
178 log.warn("Failed to add filter on {}: {}",
179 deviceId, error));
180 srManager.flowObjectiveService.filter(deviceId, filtObjBuilder.add(context));
181 });
182 }
183
184 /**
Pierb0328e42018-03-27 11:29:42 -0700185 * Removes filtering objective for given device and port.
186 *
187 * @param deviceId device ID
188 * @param port ingress port number
189 * @param assignedVlan assigned VLAN ID
190 * @param mcastIp multicast IP address
191 * @param mcastRole the multicast role of the device
192 */
193 void removeFilterToDevice(DeviceId deviceId, PortNumber port, VlanId assignedVlan,
194 IpAddress mcastIp, McastRole mcastRole) {
195
196 MacAddress routerMac = getRouterMac(deviceId, port);
197 if (routerMac.equals(MacAddress.NONE)) {
198 return;
199 }
200
201 FilteringObjective.Builder filtObjBuilder =
202 filterObjBuilder(port, assignedVlan, mcastIp, routerMac, mcastRole);
203 ObjectiveContext context = new DefaultObjectiveContext(
204 (objective) -> log.debug("Successfully removed filter on {}/{}, vlan {}",
205 deviceId, port.toLong(), assignedVlan),
206 (objective, error) ->
207 log.warn("Failed to remove filter on {}/{}, vlan {}: {}",
208 deviceId, port.toLong(), assignedVlan, error));
209 srManager.flowObjectiveService.filter(deviceId, filtObjBuilder.remove(context));
210 }
211
212 /**
213 * Gets assigned VLAN according to the value in the meta.
214 *
215 * @param nextObjective nextObjective to analyze
216 * @return assigned VLAN ID
217 */
218 VlanId assignedVlanFromNext(NextObjective nextObjective) {
219 return ((VlanIdCriterion) nextObjective.meta().getCriterion(VLAN_VID)).vlanId();
220 }
221
222 /**
223 * Gets ingress VLAN from McastConfig.
224 *
225 * @return ingress VLAN or VlanId.NONE if not configured
226 */
227 private VlanId ingressVlan() {
228 McastConfig mcastConfig =
229 srManager.cfgService.getConfig(coreAppId, McastConfig.class);
230 return (mcastConfig != null) ? mcastConfig.ingressVlan() : VlanId.NONE;
231 }
232
233 /**
234 * Gets egress VLAN from McastConfig.
235 *
236 * @return egress VLAN or VlanId.NONE if not configured
237 */
238 private VlanId egressVlan() {
239 McastConfig mcastConfig =
240 srManager.cfgService.getConfig(coreAppId, McastConfig.class);
241 return (mcastConfig != null) ? mcastConfig.egressVlan() : VlanId.NONE;
242 }
243
244 /**
245 * Gets assigned VLAN according to the value of egress VLAN.
246 * If connect point is specified, try to reuse the assigned VLAN on the connect point.
247 *
248 * @param cp connect point; Can be null if not specified
249 * @return assigned VLAN ID
250 */
251 VlanId assignedVlan(ConnectPoint cp) {
252 // Use the egressVlan if it is tagged
253 if (!egressVlan().equals(VlanId.NONE)) {
254 return egressVlan();
255 }
256 // Reuse unicast VLAN if the port has subnet configured
257 if (cp != null) {
258 VlanId untaggedVlan = srManager.getInternalVlanId(cp);
259 return (untaggedVlan != null) ? untaggedVlan : INTERNAL_VLAN;
260 }
261 // Use DEFAULT_VLAN if none of the above matches
262 return SegmentRoutingManager.INTERNAL_VLAN;
263 }
264
265 /**
266 * Gets source connect point of given multicast group.
267 *
268 * @param mcastIp multicast IP
269 * @return source connect point or null if not found
Pierb1fe7382018-04-17 17:25:22 +0200270 *
271 * @deprecated in 1.12 ("Magpie") release.
Pierb0328e42018-03-27 11:29:42 -0700272 */
Pierb1fe7382018-04-17 17:25:22 +0200273 @Deprecated
Pierb0328e42018-03-27 11:29:42 -0700274 ConnectPoint getSource(IpAddress mcastIp) {
Pierb0328e42018-03-27 11:29:42 -0700275 McastRoute mcastRoute = srManager.multicastRouteService.getRoutes().stream()
276 .filter(mcastRouteInternal -> mcastRouteInternal.group().equals(mcastIp))
277 .findFirst().orElse(null);
278 return mcastRoute == null ? null : srManager.multicastRouteService.sources(mcastRoute)
279 .stream()
280 .findFirst().orElse(null);
281 }
282
283 /**
Pierb1fe7382018-04-17 17:25:22 +0200284 * Gets sources connect points of given multicast group.
285 *
286 * @param mcastIp multicast IP
287 * @return sources connect points or empty set if not found
288 */
289 Set<ConnectPoint> getSources(IpAddress mcastIp) {
Pier3e793752018-04-19 16:47:06 +0200290 // TODO we should support different types of routes
Pierb1fe7382018-04-17 17:25:22 +0200291 McastRoute mcastRoute = srManager.multicastRouteService.getRoutes().stream()
292 .filter(mcastRouteInternal -> mcastRouteInternal.group().equals(mcastIp))
293 .findFirst().orElse(null);
294 return mcastRoute == null ? ImmutableSet.of() :
295 srManager.multicastRouteService.sources(mcastRoute);
296 }
297
298 /**
Pierb0328e42018-03-27 11:29:42 -0700299 * Gets sinks of given multicast group.
300 *
301 * @param mcastIp multicast IP
302 * @return map of sinks or empty map if not found
303 */
304 Map<HostId, Set<ConnectPoint>> getSinks(IpAddress mcastIp) {
Pier3e793752018-04-19 16:47:06 +0200305 // TODO we should support different types of routes
Pierb0328e42018-03-27 11:29:42 -0700306 McastRoute mcastRoute = srManager.multicastRouteService.getRoutes().stream()
307 .filter(mcastRouteInternal -> mcastRouteInternal.group().equals(mcastIp))
308 .findFirst().orElse(null);
309 return mcastRoute == null ?
Pierb1fe7382018-04-17 17:25:22 +0200310 ImmutableMap.of() :
Pierb0328e42018-03-27 11:29:42 -0700311 srManager.multicastRouteService.routeData(mcastRoute).sinks();
312 }
313
314 /**
315 * Get sinks affected by this egress device.
316 *
317 * @param egressDevice the egress device
318 * @param mcastIp the mcast ip address
319 * @return the map of the sinks affected
320 */
321 Map<HostId, Set<ConnectPoint>> getAffectedSinks(DeviceId egressDevice,
Pier3e793752018-04-19 16:47:06 +0200322 IpAddress mcastIp) {
Pierb0328e42018-03-27 11:29:42 -0700323 return getSinks(mcastIp).entrySet()
324 .stream()
325 .filter(hostIdSetEntry -> hostIdSetEntry.getValue().stream()
326 .map(ConnectPoint::deviceId)
327 .anyMatch(deviceId -> deviceId.equals(egressDevice))
328 ).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
329 }
330
331 /**
332 * Creates a next objective builder for multicast.
333 *
334 * @param mcastIp multicast group
335 * @param assignedVlan assigned VLAN ID
336 * @param outPorts set of output port numbers
337 * @param nextId the next id
338 * @return next objective builder
339 */
340 NextObjective.Builder nextObjBuilder(IpAddress mcastIp, VlanId assignedVlan,
341 Set<PortNumber> outPorts, Integer nextId) {
342 // If nextId is null allocate a new one
343 if (nextId == null) {
344 nextId = srManager.flowObjectiveService.allocateNextId();
345 }
346 // Build the meta selector with the fwd objective info
347 TrafficSelector metadata =
348 DefaultTrafficSelector.builder()
349 .matchVlanId(assignedVlan)
350 .matchIPDst(mcastIp.toIpPrefix())
351 .build();
352 // Define the nextobjective type
353 NextObjective.Builder nextObjBuilder = DefaultNextObjective
354 .builder().withId(nextId)
355 .withType(NextObjective.Type.BROADCAST)
356 .fromApp(srManager.appId())
357 .withMeta(metadata);
358 // Add the output ports
359 outPorts.forEach(port -> {
360 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
361 if (egressVlan().equals(VlanId.NONE)) {
362 tBuilder.popVlan();
363 }
364 tBuilder.setOutput(port);
365 nextObjBuilder.addTreatment(tBuilder.build());
366 });
367 // Done return the complete builder
368 return nextObjBuilder;
369 }
370
371 /**
372 * Creates a forwarding objective builder for multicast.
373 *
374 * @param mcastIp multicast group
375 * @param assignedVlan assigned VLAN ID
376 * @param nextId next ID of the L3 multicast group
377 * @return forwarding objective builder
378 */
379 ForwardingObjective.Builder fwdObjBuilder(IpAddress mcastIp,
380 VlanId assignedVlan, int nextId) {
381 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
382 // Let's the matching on the group address
383 // TODO SSM support in future
384 if (mcastIp.isIp6()) {
385 sbuilder.matchEthType(Ethernet.TYPE_IPV6);
386 sbuilder.matchIPv6Dst(mcastIp.toIpPrefix());
387 } else {
388 sbuilder.matchEthType(Ethernet.TYPE_IPV4);
389 sbuilder.matchIPDst(mcastIp.toIpPrefix());
390 }
391 // Then build the meta selector
392 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
393 metabuilder.matchVlanId(assignedVlan);
394 // Finally return the completed builder
395 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective.builder();
396 fwdBuilder.withSelector(sbuilder.build())
397 .withMeta(metabuilder.build())
398 .nextStep(nextId)
399 .withFlag(ForwardingObjective.Flag.SPECIFIC)
400 .fromApp(srManager.appId())
401 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY);
402 return fwdBuilder;
403 }
404
405 /**
406 * Creates a filtering objective builder for multicast.
407 *
408 * @param ingressPort ingress port of the multicast stream
409 * @param assignedVlan assigned VLAN ID
410 * @param mcastIp the group address
411 * @param routerMac router MAC. This is carried in metadata and used from some switches that
412 * need to put unicast entry before multicast entry in TMAC table.
413 * @param mcastRole the Multicast role
414 * @return filtering objective builder
415 */
416 private FilteringObjective.Builder filterObjBuilder(PortNumber ingressPort, VlanId assignedVlan,
Andrea Campanellab955fec2018-04-27 14:44:15 +0200417 IpAddress mcastIp, MacAddress routerMac, McastRole mcastRole) {
Pierb0328e42018-03-27 11:29:42 -0700418 FilteringObjective.Builder filtBuilder = DefaultFilteringObjective.builder();
419 // Let's add the in port matching and the priority
420 filtBuilder.withKey(Criteria.matchInPort(ingressPort))
421 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY);
422 // According to the mcast role we match on the proper vlan
423 // If the role is null we are on the transit or on the egress
424 if (mcastRole == null) {
425 filtBuilder.addCondition(Criteria.matchVlanId(egressVlan()));
426 } else {
427 filtBuilder.addCondition(Criteria.matchVlanId(ingressVlan()));
428 }
429 // According to the IP type we set the proper match on the mac address
430 if (mcastIp.isIp4()) {
431 filtBuilder.addCondition(Criteria.matchEthDstMasked(MacAddress.IPV4_MULTICAST,
Andrea Campanellab955fec2018-04-27 14:44:15 +0200432 MacAddress.IPV4_MULTICAST_MASK));
Pierb0328e42018-03-27 11:29:42 -0700433 } else {
434 filtBuilder.addCondition(Criteria.matchEthDstMasked(MacAddress.IPV6_MULTICAST,
Andrea Campanellab955fec2018-04-27 14:44:15 +0200435 MacAddress.IPV6_MULTICAST_MASK));
Pierb0328e42018-03-27 11:29:42 -0700436 }
437 // We finally build the meta treatment
438 TrafficTreatment tt = DefaultTrafficTreatment.builder()
439 .pushVlan().setVlanId(assignedVlan)
440 .setEthDst(routerMac)
441 .build();
442 filtBuilder.withMeta(tt);
443 // Done, we return a permit filtering objective
444 return filtBuilder.permit().fromApp(srManager.appId());
445 }
446
447 /**
Andrea Campanellab955fec2018-04-27 14:44:15 +0200448 * Creates a filtering objective builder for multicast drop.
449 *
450 * @param ipv4 do we need to install IPv4 or v6 ?
451 * @param routerMac router MAC. This is carried in metadata and used from some switches that
452 * need to put unicast entry before multicast entry in TMAC table.
453 * @param assignedVlan the vlanId to drop
454 * @return filtering objective builder
455 */
456 private FilteringObjective.Builder filterObjBuilderDrop(boolean ipv4, MacAddress routerMac, VlanId assignedVlan) {
457
458 FilteringObjective.Builder filtBuilder = DefaultFilteringObjective.builder();
459 // We match on the given vlan.
460 // If the traffic comes in tagged with an mcast specific vlan there is no rule
461 // in table 10 to handle it goes directly to 60 and it's dropped there.
462 filtBuilder.addCondition(Criteria.matchVlanId(assignedVlan));
463
464 // According to the IP type we set the proper match on the mac address
465 if (ipv4) {
466 filtBuilder.addCondition(Criteria.matchEthDstMasked(MacAddress.IPV4_MULTICAST,
467 MacAddress.IPV4_MULTICAST_MASK));
468 } else {
469 filtBuilder.addCondition(Criteria.matchEthDstMasked(MacAddress.IPV6_MULTICAST,
470 MacAddress.IPV6_MULTICAST_MASK));
471 }
472 // We finally build the meta treatment
473 TrafficTreatment tt = DefaultTrafficTreatment.builder()
474 .setEthDst(routerMac)
475 .build();
476 filtBuilder.withMeta(tt);
477 // Done, we return a permit filtering objective
478 return filtBuilder.permit().fromApp(srManager.appId());
479 }
480
481 /**
Pierb0328e42018-03-27 11:29:42 -0700482 * Gets output ports information from treatments.
483 *
484 * @param treatments collection of traffic treatments
485 * @return set of output port numbers
486 */
487 Set<PortNumber> getPorts(Collection<TrafficTreatment> treatments) {
488 ImmutableSet.Builder<PortNumber> builder = ImmutableSet.builder();
489 treatments.forEach(treatment -> treatment.allInstructions().stream()
490 .filter(instr -> instr instanceof Instructions.OutputInstruction)
491 .forEach(instr -> builder.add(((Instructions.OutputInstruction) instr).port())));
492 return builder.build();
493 }
Pier96f63cb2018-04-17 16:29:56 +0200494
495 /**
496 * Returns the hash of the group address.
497 *
498 * @param ipAddress the ip address
499 * @return the hash of the address
500 */
501 private Long hasher(IpAddress ipAddress) {
502 return HASH_FN.newHasher()
503 .putBytes(ipAddress.toOctets())
504 .hash()
505 .asLong();
506 }
507
508 /**
509 * Given a multicast group define a leader for it.
510 *
511 * @param mcastIp the group address
512 * @return true if the instance is the leader of the group
513 */
514 boolean isLeader(IpAddress mcastIp) {
515 // Get our id
516 final NodeId currentNodeId = srManager.clusterService.getLocalNode().id();
517 // Get the leader for this group using the ip address as key
518 final NodeId leader = srManager.workPartitionService.getLeader(mcastIp, this::hasher);
519 // If there is not a leader, let's send an error
520 if (leader == null) {
521 log.error("Fail to elect a leader for {}.", mcastIp);
522 return false;
523 }
524 // Update cache and return operation result
525 mcastLeaderCache.put(mcastIp, leader);
526 return currentNodeId.equals(leader);
527 }
528
529 /**
530 * Given a multicast group withdraw its leader.
531 *
532 * @param mcastIp the group address
533 */
534 void withdrawLeader(IpAddress mcastIp) {
535 // For now just update the cache
536 mcastLeaderCache.remove(mcastIp);
537 }
538
539 Map<IpAddress, NodeId> getMcastLeaders(IpAddress mcastIp) {
540 // If mcast ip is present
541 if (mcastIp != null) {
542 return mcastLeaderCache.entrySet().stream()
543 .filter(entry -> entry.getKey().equals(mcastIp))
544 .collect(Collectors.toMap(Map.Entry::getKey,
545 Map.Entry::getValue));
546 }
547 // Otherwise take all the groups
548 return ImmutableMap.copyOf(mcastLeaderCache);
549 }
Pierb0328e42018-03-27 11:29:42 -0700550}