blob: 6d3f93868db3b11bc43fb544568ee69814aeb66c [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;
Charles Chand5814aa2018-08-19 19:21:46 -070021import com.google.common.collect.Lists;
Pier96f63cb2018-04-17 16:29:56 +020022import com.google.common.collect.Maps;
Charles Chand5814aa2018-08-19 19:21:46 -070023import com.google.common.collect.Sets;
Pier96f63cb2018-04-17 16:29:56 +020024import com.google.common.hash.HashFunction;
25import com.google.common.hash.Hashing;
Pierb0328e42018-03-27 11:29:42 -070026import org.onlab.packet.Ethernet;
27import org.onlab.packet.IpAddress;
28import org.onlab.packet.MacAddress;
29import org.onlab.packet.VlanId;
30import org.onosproject.cluster.NodeId;
31import org.onosproject.core.ApplicationId;
32import org.onosproject.mcast.api.McastRoute;
33import org.onosproject.net.ConnectPoint;
34import org.onosproject.net.DeviceId;
35import org.onosproject.net.HostId;
Charles Chand5814aa2018-08-19 19:21:46 -070036import org.onosproject.net.Link;
Pierb0328e42018-03-27 11:29:42 -070037import org.onosproject.net.PortNumber;
38import org.onosproject.net.config.basics.McastConfig;
39import org.onosproject.net.flow.DefaultTrafficSelector;
40import org.onosproject.net.flow.DefaultTrafficTreatment;
41import org.onosproject.net.flow.TrafficSelector;
42import org.onosproject.net.flow.TrafficTreatment;
43import org.onosproject.net.flow.criteria.Criteria;
44import org.onosproject.net.flow.criteria.VlanIdCriterion;
45import org.onosproject.net.flow.instructions.Instructions;
46import org.onosproject.net.flowobjective.DefaultFilteringObjective;
47import org.onosproject.net.flowobjective.DefaultForwardingObjective;
48import org.onosproject.net.flowobjective.DefaultNextObjective;
49import org.onosproject.net.flowobjective.DefaultObjectiveContext;
50import org.onosproject.net.flowobjective.FilteringObjective;
51import org.onosproject.net.flowobjective.ForwardingObjective;
52import org.onosproject.net.flowobjective.NextObjective;
53import org.onosproject.net.flowobjective.ObjectiveContext;
54import org.onosproject.segmentrouting.SegmentRoutingManager;
55import org.onosproject.segmentrouting.SegmentRoutingService;
56import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
57import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
58import org.slf4j.Logger;
59
60import java.util.Collection;
Charles Chand5814aa2018-08-19 19:21:46 -070061import java.util.List;
Pierb0328e42018-03-27 11:29:42 -070062import java.util.Map;
63import java.util.Set;
64import java.util.stream.Collectors;
65
66import static org.onosproject.net.flow.criteria.Criterion.Type.VLAN_VID;
Pierb0328e42018-03-27 11:29:42 -070067
68/**
69 * Utility class for Multicast Handler.
70 */
71class McastUtils {
72
73 // Internal reference to the log
74 private final Logger log;
75 // Internal reference to SR Manager
76 private SegmentRoutingManager srManager;
77 // Internal reference to the app id
78 private ApplicationId coreAppId;
Pier96f63cb2018-04-17 16:29:56 +020079 // Hashing function for the multicast hasher
80 private static final HashFunction HASH_FN = Hashing.md5();
81 // Read only cache of the Mcast leader
82 private Map<IpAddress, NodeId> mcastLeaderCache;
Pierb0328e42018-03-27 11:29:42 -070083
84 /**
85 * Builds a new McastUtils object.
86 *
87 * @param srManager the SR manager
88 * @param coreAppId the core application id
89 * @param log log reference of the McastHandler
90 */
91 McastUtils(SegmentRoutingManager srManager, ApplicationId coreAppId, Logger log) {
92 this.srManager = srManager;
93 this.coreAppId = coreAppId;
94 this.log = log;
Pier96f63cb2018-04-17 16:29:56 +020095 this.mcastLeaderCache = Maps.newConcurrentMap();
Pierb0328e42018-03-27 11:29:42 -070096 }
97
98 /**
Pier477e0062018-04-20 14:14:34 +020099 * Clean up when deactivating the application.
100 */
101 public void terminate() {
102 mcastLeaderCache.clear();
103 }
104
105 /**
Pierb0328e42018-03-27 11:29:42 -0700106 * Get router mac using application config and the connect point.
107 *
108 * @param deviceId the device id
109 * @param port the port number
110 * @return the router mac if the port is configured, otherwise null
111 */
112 private MacAddress getRouterMac(DeviceId deviceId, PortNumber port) {
113 // Do nothing if the port is configured as suppressed
114 ConnectPoint connectPoint = new ConnectPoint(deviceId, port);
115 SegmentRoutingAppConfig appConfig = srManager.cfgService
116 .getConfig(srManager.appId(), SegmentRoutingAppConfig.class);
117 if (appConfig != null && appConfig.suppressSubnet().contains(connectPoint)) {
118 log.info("Ignore suppressed port {}", connectPoint);
119 return MacAddress.NONE;
120 }
121 // Get the router mac using the device configuration
122 MacAddress routerMac;
123 try {
124 routerMac = srManager.deviceConfiguration().getDeviceMac(deviceId);
125 } catch (DeviceConfigNotFoundException dcnfe) {
Esin Karamandd26b212019-07-24 11:26:39 +0000126 log.warn("Failed to get device MAC since the device {} is not configured", deviceId);
127 return null;
Pierb0328e42018-03-27 11:29:42 -0700128 }
129 return routerMac;
130 }
131
132 /**
133 * Adds filtering objective for given device and port.
134 *
135 * @param deviceId device ID
136 * @param port ingress port number
137 * @param assignedVlan assigned VLAN ID
138 * @param mcastIp the group address
139 * @param mcastRole the role of the device
140 */
141 void addFilterToDevice(DeviceId deviceId, PortNumber port, VlanId assignedVlan,
142 IpAddress mcastIp, McastRole mcastRole) {
143
Vignesh Ethirajaeecd042019-08-26 12:18:42 +0000144 if (!srManager.deviceConfiguration().isConfigured(deviceId)) {
145 log.debug("skip update of fitering objective for unconfigured device: {}", deviceId);
146 return;
147 }
Pierb0328e42018-03-27 11:29:42 -0700148 MacAddress routerMac = getRouterMac(deviceId, port);
Esin Karamandd26b212019-07-24 11:26:39 +0000149
150 if (MacAddress.NONE.equals(routerMac)) {
Pierb0328e42018-03-27 11:29:42 -0700151 return;
152 }
153
154 FilteringObjective.Builder filtObjBuilder = filterObjBuilder(port, assignedVlan, mcastIp,
155 routerMac, mcastRole);
156 ObjectiveContext context = new DefaultObjectiveContext(
157 (objective) -> log.debug("Successfully add filter on {}/{}, vlan {}",
158 deviceId, port.toLong(), assignedVlan),
159 (objective, error) ->
160 log.warn("Failed to add filter on {}/{}, vlan {}: {}",
161 deviceId, port.toLong(), assignedVlan, error));
162 srManager.flowObjectiveService.filter(deviceId, filtObjBuilder.add(context));
163 }
164
165 /**
166 * Removes filtering objective for given device and port.
167 *
168 * @param deviceId device ID
169 * @param port ingress port number
170 * @param assignedVlan assigned VLAN ID
171 * @param mcastIp multicast IP address
172 * @param mcastRole the multicast role of the device
173 */
174 void removeFilterToDevice(DeviceId deviceId, PortNumber port, VlanId assignedVlan,
175 IpAddress mcastIp, McastRole mcastRole) {
176
Vignesh Ethirajaeecd042019-08-26 12:18:42 +0000177 if (!srManager.deviceConfiguration().isConfigured(deviceId)) {
178 log.debug("skip update of fitering objective for unconfigured device: {}", deviceId);
179 return;
180 }
Pierb0328e42018-03-27 11:29:42 -0700181 MacAddress routerMac = getRouterMac(deviceId, port);
Esin Karamandd26b212019-07-24 11:26:39 +0000182
183 if (MacAddress.NONE.equals(routerMac)) {
Pierb0328e42018-03-27 11:29:42 -0700184 return;
185 }
186
187 FilteringObjective.Builder filtObjBuilder =
188 filterObjBuilder(port, assignedVlan, mcastIp, routerMac, mcastRole);
189 ObjectiveContext context = new DefaultObjectiveContext(
190 (objective) -> log.debug("Successfully removed filter on {}/{}, vlan {}",
191 deviceId, port.toLong(), assignedVlan),
192 (objective, error) ->
193 log.warn("Failed to remove filter on {}/{}, vlan {}: {}",
194 deviceId, port.toLong(), assignedVlan, error));
195 srManager.flowObjectiveService.filter(deviceId, filtObjBuilder.remove(context));
196 }
197
198 /**
199 * Gets assigned VLAN according to the value in the meta.
200 *
201 * @param nextObjective nextObjective to analyze
202 * @return assigned VLAN ID
203 */
204 VlanId assignedVlanFromNext(NextObjective nextObjective) {
205 return ((VlanIdCriterion) nextObjective.meta().getCriterion(VLAN_VID)).vlanId();
206 }
207
208 /**
209 * Gets ingress VLAN from McastConfig.
210 *
211 * @return ingress VLAN or VlanId.NONE if not configured
212 */
213 private VlanId ingressVlan() {
214 McastConfig mcastConfig =
215 srManager.cfgService.getConfig(coreAppId, McastConfig.class);
216 return (mcastConfig != null) ? mcastConfig.ingressVlan() : VlanId.NONE;
217 }
218
219 /**
220 * Gets egress VLAN from McastConfig.
221 *
222 * @return egress VLAN or VlanId.NONE if not configured
223 */
224 private VlanId egressVlan() {
225 McastConfig mcastConfig =
226 srManager.cfgService.getConfig(coreAppId, McastConfig.class);
227 return (mcastConfig != null) ? mcastConfig.egressVlan() : VlanId.NONE;
228 }
229
230 /**
231 * Gets assigned VLAN according to the value of egress VLAN.
232 * If connect point is specified, try to reuse the assigned VLAN on the connect point.
233 *
234 * @param cp connect point; Can be null if not specified
235 * @return assigned VLAN ID
236 */
237 VlanId assignedVlan(ConnectPoint cp) {
238 // Use the egressVlan if it is tagged
239 if (!egressVlan().equals(VlanId.NONE)) {
240 return egressVlan();
241 }
242 // Reuse unicast VLAN if the port has subnet configured
243 if (cp != null) {
244 VlanId untaggedVlan = srManager.getInternalVlanId(cp);
Saurav Das09c2c4d2018-08-13 15:34:26 -0700245 return (untaggedVlan != null) ? untaggedVlan
246 : srManager.getDefaultInternalVlan();
Pierb0328e42018-03-27 11:29:42 -0700247 }
248 // Use DEFAULT_VLAN if none of the above matches
Saurav Das09c2c4d2018-08-13 15:34:26 -0700249 return srManager.getDefaultInternalVlan();
Pierb0328e42018-03-27 11:29:42 -0700250 }
251
252 /**
253 * Gets source connect point of given multicast group.
254 *
255 * @param mcastIp multicast IP
256 * @return source connect point or null if not found
Pierb1fe7382018-04-17 17:25:22 +0200257 *
258 * @deprecated in 1.12 ("Magpie") release.
Pierb0328e42018-03-27 11:29:42 -0700259 */
Pierb1fe7382018-04-17 17:25:22 +0200260 @Deprecated
Pierb0328e42018-03-27 11:29:42 -0700261 ConnectPoint getSource(IpAddress mcastIp) {
Pierb0328e42018-03-27 11:29:42 -0700262 McastRoute mcastRoute = srManager.multicastRouteService.getRoutes().stream()
263 .filter(mcastRouteInternal -> mcastRouteInternal.group().equals(mcastIp))
264 .findFirst().orElse(null);
265 return mcastRoute == null ? null : srManager.multicastRouteService.sources(mcastRoute)
266 .stream()
267 .findFirst().orElse(null);
268 }
269
270 /**
Pierb1fe7382018-04-17 17:25:22 +0200271 * Gets sources connect points of given multicast group.
272 *
273 * @param mcastIp multicast IP
274 * @return sources connect points or empty set if not found
275 */
276 Set<ConnectPoint> getSources(IpAddress mcastIp) {
Pier3e793752018-04-19 16:47:06 +0200277 // TODO we should support different types of routes
Pierb1fe7382018-04-17 17:25:22 +0200278 McastRoute mcastRoute = srManager.multicastRouteService.getRoutes().stream()
279 .filter(mcastRouteInternal -> mcastRouteInternal.group().equals(mcastIp))
280 .findFirst().orElse(null);
281 return mcastRoute == null ? ImmutableSet.of() :
282 srManager.multicastRouteService.sources(mcastRoute);
283 }
284
285 /**
Pierb0328e42018-03-27 11:29:42 -0700286 * Gets sinks of given multicast group.
287 *
288 * @param mcastIp multicast IP
289 * @return map of sinks or empty map if not found
290 */
291 Map<HostId, Set<ConnectPoint>> getSinks(IpAddress mcastIp) {
Pier3e793752018-04-19 16:47:06 +0200292 // TODO we should support different types of routes
Pierb0328e42018-03-27 11:29:42 -0700293 McastRoute mcastRoute = srManager.multicastRouteService.getRoutes().stream()
294 .filter(mcastRouteInternal -> mcastRouteInternal.group().equals(mcastIp))
295 .findFirst().orElse(null);
296 return mcastRoute == null ?
Pierb1fe7382018-04-17 17:25:22 +0200297 ImmutableMap.of() :
Pierb0328e42018-03-27 11:29:42 -0700298 srManager.multicastRouteService.routeData(mcastRoute).sinks();
299 }
300
301 /**
302 * Get sinks affected by this egress device.
303 *
304 * @param egressDevice the egress device
305 * @param mcastIp the mcast ip address
306 * @return the map of the sinks affected
307 */
308 Map<HostId, Set<ConnectPoint>> getAffectedSinks(DeviceId egressDevice,
Pier3e793752018-04-19 16:47:06 +0200309 IpAddress mcastIp) {
Pierb0328e42018-03-27 11:29:42 -0700310 return getSinks(mcastIp).entrySet()
311 .stream()
312 .filter(hostIdSetEntry -> hostIdSetEntry.getValue().stream()
313 .map(ConnectPoint::deviceId)
314 .anyMatch(deviceId -> deviceId.equals(egressDevice))
315 ).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
316 }
317
318 /**
319 * Creates a next objective builder for multicast.
320 *
321 * @param mcastIp multicast group
322 * @param assignedVlan assigned VLAN ID
323 * @param outPorts set of output port numbers
324 * @param nextId the next id
325 * @return next objective builder
326 */
327 NextObjective.Builder nextObjBuilder(IpAddress mcastIp, VlanId assignedVlan,
328 Set<PortNumber> outPorts, Integer nextId) {
329 // If nextId is null allocate a new one
330 if (nextId == null) {
331 nextId = srManager.flowObjectiveService.allocateNextId();
332 }
333 // Build the meta selector with the fwd objective info
334 TrafficSelector metadata =
335 DefaultTrafficSelector.builder()
336 .matchVlanId(assignedVlan)
337 .matchIPDst(mcastIp.toIpPrefix())
338 .build();
339 // Define the nextobjective type
340 NextObjective.Builder nextObjBuilder = DefaultNextObjective
341 .builder().withId(nextId)
342 .withType(NextObjective.Type.BROADCAST)
343 .fromApp(srManager.appId())
344 .withMeta(metadata);
345 // Add the output ports
346 outPorts.forEach(port -> {
347 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
348 if (egressVlan().equals(VlanId.NONE)) {
349 tBuilder.popVlan();
350 }
351 tBuilder.setOutput(port);
352 nextObjBuilder.addTreatment(tBuilder.build());
353 });
354 // Done return the complete builder
355 return nextObjBuilder;
356 }
357
358 /**
359 * Creates a forwarding objective builder for multicast.
360 *
361 * @param mcastIp multicast group
362 * @param assignedVlan assigned VLAN ID
363 * @param nextId next ID of the L3 multicast group
364 * @return forwarding objective builder
365 */
366 ForwardingObjective.Builder fwdObjBuilder(IpAddress mcastIp,
367 VlanId assignedVlan, int nextId) {
368 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
369 // Let's the matching on the group address
370 // TODO SSM support in future
371 if (mcastIp.isIp6()) {
372 sbuilder.matchEthType(Ethernet.TYPE_IPV6);
373 sbuilder.matchIPv6Dst(mcastIp.toIpPrefix());
374 } else {
375 sbuilder.matchEthType(Ethernet.TYPE_IPV4);
376 sbuilder.matchIPDst(mcastIp.toIpPrefix());
377 }
378 // Then build the meta selector
379 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
380 metabuilder.matchVlanId(assignedVlan);
381 // Finally return the completed builder
382 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective.builder();
383 fwdBuilder.withSelector(sbuilder.build())
384 .withMeta(metabuilder.build())
385 .nextStep(nextId)
386 .withFlag(ForwardingObjective.Flag.SPECIFIC)
387 .fromApp(srManager.appId())
388 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY);
389 return fwdBuilder;
390 }
391
392 /**
393 * Creates a filtering objective builder for multicast.
394 *
395 * @param ingressPort ingress port of the multicast stream
396 * @param assignedVlan assigned VLAN ID
397 * @param mcastIp the group address
398 * @param routerMac router MAC. This is carried in metadata and used from some switches that
399 * need to put unicast entry before multicast entry in TMAC table.
400 * @param mcastRole the Multicast role
401 * @return filtering objective builder
402 */
403 private FilteringObjective.Builder filterObjBuilder(PortNumber ingressPort, VlanId assignedVlan,
Charles Chan056e0c12018-05-10 22:19:49 +0000404 IpAddress mcastIp, MacAddress routerMac, McastRole mcastRole) {
Pierb0328e42018-03-27 11:29:42 -0700405 FilteringObjective.Builder filtBuilder = DefaultFilteringObjective.builder();
406 // Let's add the in port matching and the priority
407 filtBuilder.withKey(Criteria.matchInPort(ingressPort))
408 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY);
409 // According to the mcast role we match on the proper vlan
410 // If the role is null we are on the transit or on the egress
411 if (mcastRole == null) {
412 filtBuilder.addCondition(Criteria.matchVlanId(egressVlan()));
413 } else {
414 filtBuilder.addCondition(Criteria.matchVlanId(ingressVlan()));
415 }
416 // According to the IP type we set the proper match on the mac address
417 if (mcastIp.isIp4()) {
418 filtBuilder.addCondition(Criteria.matchEthDstMasked(MacAddress.IPV4_MULTICAST,
Charles Chan056e0c12018-05-10 22:19:49 +0000419 MacAddress.IPV4_MULTICAST_MASK));
Pierb0328e42018-03-27 11:29:42 -0700420 } else {
421 filtBuilder.addCondition(Criteria.matchEthDstMasked(MacAddress.IPV6_MULTICAST,
Charles Chan056e0c12018-05-10 22:19:49 +0000422 MacAddress.IPV6_MULTICAST_MASK));
Pierb0328e42018-03-27 11:29:42 -0700423 }
424 // We finally build the meta treatment
Esin Karamandd26b212019-07-24 11:26:39 +0000425 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
426 tBuilder.pushVlan().setVlanId(assignedVlan);
427
428 if (routerMac != null && !routerMac.equals(MacAddress.NONE)) {
429 tBuilder.setEthDst(routerMac);
430 }
431
432 filtBuilder.withMeta(tBuilder.build());
Pierb0328e42018-03-27 11:29:42 -0700433 // Done, we return a permit filtering objective
434 return filtBuilder.permit().fromApp(srManager.appId());
435 }
436
437 /**
438 * Gets output ports information from treatments.
439 *
440 * @param treatments collection of traffic treatments
441 * @return set of output port numbers
442 */
443 Set<PortNumber> getPorts(Collection<TrafficTreatment> treatments) {
444 ImmutableSet.Builder<PortNumber> builder = ImmutableSet.builder();
445 treatments.forEach(treatment -> treatment.allInstructions().stream()
446 .filter(instr -> instr instanceof Instructions.OutputInstruction)
447 .forEach(instr -> builder.add(((Instructions.OutputInstruction) instr).port())));
448 return builder.build();
449 }
Pier96f63cb2018-04-17 16:29:56 +0200450
451 /**
452 * Returns the hash of the group address.
453 *
454 * @param ipAddress the ip address
455 * @return the hash of the address
456 */
457 private Long hasher(IpAddress ipAddress) {
458 return HASH_FN.newHasher()
459 .putBytes(ipAddress.toOctets())
460 .hash()
461 .asLong();
462 }
463
464 /**
465 * Given a multicast group define a leader for it.
466 *
467 * @param mcastIp the group address
468 * @return true if the instance is the leader of the group
469 */
470 boolean isLeader(IpAddress mcastIp) {
471 // Get our id
472 final NodeId currentNodeId = srManager.clusterService.getLocalNode().id();
473 // Get the leader for this group using the ip address as key
474 final NodeId leader = srManager.workPartitionService.getLeader(mcastIp, this::hasher);
475 // If there is not a leader, let's send an error
476 if (leader == null) {
477 log.error("Fail to elect a leader for {}.", mcastIp);
478 return false;
479 }
480 // Update cache and return operation result
481 mcastLeaderCache.put(mcastIp, leader);
482 return currentNodeId.equals(leader);
483 }
484
485 /**
486 * Given a multicast group withdraw its leader.
487 *
488 * @param mcastIp the group address
489 */
490 void withdrawLeader(IpAddress mcastIp) {
491 // For now just update the cache
492 mcastLeaderCache.remove(mcastIp);
493 }
494
495 Map<IpAddress, NodeId> getMcastLeaders(IpAddress mcastIp) {
496 // If mcast ip is present
497 if (mcastIp != null) {
498 return mcastLeaderCache.entrySet().stream()
499 .filter(entry -> entry.getKey().equals(mcastIp))
500 .collect(Collectors.toMap(Map.Entry::getKey,
501 Map.Entry::getValue));
502 }
503 // Otherwise take all the groups
504 return ImmutableMap.copyOf(mcastLeaderCache);
505 }
Charles Chand5814aa2018-08-19 19:21:46 -0700506
507 /**
508 * Build recursively the mcast paths.
509 *
510 * @param mcastNextObjStore mcast next obj store
511 * @param toVisit the node to visit
512 * @param visited the visited nodes
513 * @param mcastPaths the current mcast paths
514 * @param currentPath the current path
515 * @param mcastIp the group ip
516 * @param source the source
517 */
518 void buildMcastPaths(Map<McastStoreKey, NextObjective> mcastNextObjStore,
519 DeviceId toVisit, Set<DeviceId> visited,
520 Map<ConnectPoint, List<ConnectPoint>> mcastPaths,
521 List<ConnectPoint> currentPath, IpAddress mcastIp,
522 ConnectPoint source) {
523 // If we have visited the node to visit there is a loop
524 if (visited.contains(toVisit)) {
525 return;
526 }
527 // Visit next-hop
528 visited.add(toVisit);
529 VlanId assignedVlan = assignedVlan(toVisit.equals(source.deviceId()) ? source : null);
530 McastStoreKey mcastStoreKey = new McastStoreKey(mcastIp, toVisit, assignedVlan);
531 // Looking for next-hops
532 if (mcastNextObjStore.containsKey(mcastStoreKey)) {
533 // Build egress connect points, get ports and build relative cps
534 NextObjective nextObjective = mcastNextObjStore.get(mcastStoreKey);
535 Set<PortNumber> outputPorts = getPorts(nextObjective.next());
536 ImmutableSet.Builder<ConnectPoint> cpBuilder = ImmutableSet.builder();
537 outputPorts.forEach(portNumber -> cpBuilder.add(new ConnectPoint(toVisit, portNumber)));
538 Set<ConnectPoint> egressPoints = cpBuilder.build();
539 Set<Link> egressLinks;
540 List<ConnectPoint> newCurrentPath;
541 Set<DeviceId> newVisited;
542 DeviceId newToVisit;
543 for (ConnectPoint egressPoint : egressPoints) {
544 egressLinks = srManager.linkService.getEgressLinks(egressPoint);
545 // If it does not have egress links, stop
546 if (egressLinks.isEmpty()) {
547 // Add the connect points to the path
548 newCurrentPath = Lists.newArrayList(currentPath);
549 newCurrentPath.add(0, egressPoint);
550 mcastPaths.put(egressPoint, newCurrentPath);
551 } else {
552 newVisited = Sets.newHashSet(visited);
553 // Iterate over the egress links for the next hops
554 for (Link egressLink : egressLinks) {
555 newToVisit = egressLink.dst().deviceId();
556 newCurrentPath = Lists.newArrayList(currentPath);
557 newCurrentPath.add(0, egressPoint);
558 newCurrentPath.add(0, egressLink.dst());
559 buildMcastPaths(mcastNextObjStore, newToVisit, newVisited, mcastPaths, newCurrentPath, mcastIp,
560 source);
561 }
562 }
563 }
564 }
565 }
Pierb0328e42018-03-27 11:29:42 -0700566}