blob: c4e1ad4c0ab90f72aad23ebb17fab89f9c89b0d6 [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;
Pierb0328e42018-03-27 11:29:42 -070044import org.onosproject.net.flow.instructions.Instructions;
45import org.onosproject.net.flowobjective.DefaultFilteringObjective;
46import org.onosproject.net.flowobjective.DefaultForwardingObjective;
47import org.onosproject.net.flowobjective.DefaultNextObjective;
48import org.onosproject.net.flowobjective.DefaultObjectiveContext;
49import org.onosproject.net.flowobjective.FilteringObjective;
50import org.onosproject.net.flowobjective.ForwardingObjective;
51import org.onosproject.net.flowobjective.NextObjective;
52import org.onosproject.net.flowobjective.ObjectiveContext;
53import org.onosproject.segmentrouting.SegmentRoutingManager;
54import org.onosproject.segmentrouting.SegmentRoutingService;
55import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
56import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
57import org.slf4j.Logger;
58
59import java.util.Collection;
Charles Chand5814aa2018-08-19 19:21:46 -070060import java.util.List;
Pierb0328e42018-03-27 11:29:42 -070061import java.util.Map;
62import java.util.Set;
63import java.util.stream.Collectors;
64
Pierb0328e42018-03-27 11:29:42 -070065/**
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) {
Esin Karamandd26b212019-07-24 11:26:39 +0000123 log.warn("Failed to get device MAC since the device {} is not configured", deviceId);
124 return null;
Pierb0328e42018-03-27 11:29:42 -0700125 }
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
Harshada Chaundkarb42abd42019-07-02 16:01:24 +0000137 * @param matchOnMac match or not on macaddress
Pierb0328e42018-03-27 11:29:42 -0700138 */
139 void addFilterToDevice(DeviceId deviceId, PortNumber port, VlanId assignedVlan,
Harshada Chaundkarb42abd42019-07-02 16:01:24 +0000140 IpAddress mcastIp, McastRole mcastRole, boolean matchOnMac) {
Vignesh Ethirajaeecd042019-08-26 12:18:42 +0000141 if (!srManager.deviceConfiguration().isConfigured(deviceId)) {
142 log.debug("skip update of fitering objective for unconfigured device: {}", deviceId);
143 return;
144 }
Pierb0328e42018-03-27 11:29:42 -0700145 MacAddress routerMac = getRouterMac(deviceId, port);
Esin Karamandd26b212019-07-24 11:26:39 +0000146
147 if (MacAddress.NONE.equals(routerMac)) {
Pierb0328e42018-03-27 11:29:42 -0700148 return;
149 }
Pierb0328e42018-03-27 11:29:42 -0700150 FilteringObjective.Builder filtObjBuilder = filterObjBuilder(port, assignedVlan, mcastIp,
Harshada Chaundkarb42abd42019-07-02 16:01:24 +0000151 routerMac, mcastRole, matchOnMac);
Pierb0328e42018-03-27 11:29:42 -0700152 ObjectiveContext context = new DefaultObjectiveContext(
153 (objective) -> log.debug("Successfully add filter on {}/{}, vlan {}",
154 deviceId, port.toLong(), assignedVlan),
155 (objective, error) ->
156 log.warn("Failed to add filter on {}/{}, vlan {}: {}",
157 deviceId, port.toLong(), assignedVlan, error));
158 srManager.flowObjectiveService.filter(deviceId, filtObjBuilder.add(context));
159 }
160
161 /**
162 * Removes filtering objective for given device and port.
163 *
164 * @param deviceId device ID
165 * @param port ingress port number
166 * @param assignedVlan assigned VLAN ID
167 * @param mcastIp multicast IP address
168 * @param mcastRole the multicast role of the device
169 */
170 void removeFilterToDevice(DeviceId deviceId, PortNumber port, VlanId assignedVlan,
171 IpAddress mcastIp, McastRole mcastRole) {
Vignesh Ethirajaeecd042019-08-26 12:18:42 +0000172 if (!srManager.deviceConfiguration().isConfigured(deviceId)) {
173 log.debug("skip update of fitering objective for unconfigured device: {}", deviceId);
174 return;
175 }
Pierb0328e42018-03-27 11:29:42 -0700176 MacAddress routerMac = getRouterMac(deviceId, port);
Esin Karamandd26b212019-07-24 11:26:39 +0000177
178 if (MacAddress.NONE.equals(routerMac)) {
Pierb0328e42018-03-27 11:29:42 -0700179 return;
180 }
Pierb0328e42018-03-27 11:29:42 -0700181 FilteringObjective.Builder filtObjBuilder =
Harshada Chaundkarb42abd42019-07-02 16:01:24 +0000182 filterObjBuilder(port, assignedVlan, mcastIp, routerMac, mcastRole, false);
Pierb0328e42018-03-27 11:29:42 -0700183 ObjectiveContext context = new DefaultObjectiveContext(
184 (objective) -> log.debug("Successfully removed filter on {}/{}, vlan {}",
185 deviceId, port.toLong(), assignedVlan),
186 (objective, error) ->
187 log.warn("Failed to remove filter on {}/{}, vlan {}: {}",
188 deviceId, port.toLong(), assignedVlan, error));
189 srManager.flowObjectiveService.filter(deviceId, filtObjBuilder.remove(context));
190 }
191
192 /**
Pierb0328e42018-03-27 11:29:42 -0700193 * Gets ingress VLAN from McastConfig.
194 *
195 * @return ingress VLAN or VlanId.NONE if not configured
196 */
197 private VlanId ingressVlan() {
198 McastConfig mcastConfig =
199 srManager.cfgService.getConfig(coreAppId, McastConfig.class);
200 return (mcastConfig != null) ? mcastConfig.ingressVlan() : VlanId.NONE;
201 }
202
203 /**
204 * Gets egress VLAN from McastConfig.
205 *
206 * @return egress VLAN or VlanId.NONE if not configured
207 */
208 private VlanId egressVlan() {
209 McastConfig mcastConfig =
210 srManager.cfgService.getConfig(coreAppId, McastConfig.class);
211 return (mcastConfig != null) ? mcastConfig.egressVlan() : VlanId.NONE;
212 }
213
214 /**
215 * Gets assigned VLAN according to the value of egress VLAN.
216 * If connect point is specified, try to reuse the assigned VLAN on the connect point.
217 *
218 * @param cp connect point; Can be null if not specified
219 * @return assigned VLAN ID
220 */
221 VlanId assignedVlan(ConnectPoint cp) {
222 // Use the egressVlan if it is tagged
223 if (!egressVlan().equals(VlanId.NONE)) {
224 return egressVlan();
225 }
226 // Reuse unicast VLAN if the port has subnet configured
227 if (cp != null) {
228 VlanId untaggedVlan = srManager.getInternalVlanId(cp);
Saurav Das09c2c4d2018-08-13 15:34:26 -0700229 return (untaggedVlan != null) ? untaggedVlan
230 : srManager.getDefaultInternalVlan();
Pierb0328e42018-03-27 11:29:42 -0700231 }
232 // Use DEFAULT_VLAN if none of the above matches
Saurav Das09c2c4d2018-08-13 15:34:26 -0700233 return srManager.getDefaultInternalVlan();
Pierb0328e42018-03-27 11:29:42 -0700234 }
235
236 /**
Pierb1fe7382018-04-17 17:25:22 +0200237 * Gets sources connect points of given multicast group.
238 *
239 * @param mcastIp multicast IP
240 * @return sources connect points or empty set if not found
241 */
242 Set<ConnectPoint> getSources(IpAddress mcastIp) {
Pier3e793752018-04-19 16:47:06 +0200243 // TODO we should support different types of routes
Pierb1fe7382018-04-17 17:25:22 +0200244 McastRoute mcastRoute = srManager.multicastRouteService.getRoutes().stream()
245 .filter(mcastRouteInternal -> mcastRouteInternal.group().equals(mcastIp))
246 .findFirst().orElse(null);
247 return mcastRoute == null ? ImmutableSet.of() :
248 srManager.multicastRouteService.sources(mcastRoute);
249 }
250
251 /**
Pierb0328e42018-03-27 11:29:42 -0700252 * Gets sinks of given multicast group.
253 *
254 * @param mcastIp multicast IP
255 * @return map of sinks or empty map if not found
256 */
257 Map<HostId, Set<ConnectPoint>> getSinks(IpAddress mcastIp) {
Pier3e793752018-04-19 16:47:06 +0200258 // TODO we should support different types of routes
Pierb0328e42018-03-27 11:29:42 -0700259 McastRoute mcastRoute = srManager.multicastRouteService.getRoutes().stream()
260 .filter(mcastRouteInternal -> mcastRouteInternal.group().equals(mcastIp))
261 .findFirst().orElse(null);
262 return mcastRoute == null ?
Pierb1fe7382018-04-17 17:25:22 +0200263 ImmutableMap.of() :
Pierb0328e42018-03-27 11:29:42 -0700264 srManager.multicastRouteService.routeData(mcastRoute).sinks();
265 }
266
267 /**
268 * Get sinks affected by this egress device.
269 *
270 * @param egressDevice the egress device
271 * @param mcastIp the mcast ip address
272 * @return the map of the sinks affected
273 */
274 Map<HostId, Set<ConnectPoint>> getAffectedSinks(DeviceId egressDevice,
Pier3e793752018-04-19 16:47:06 +0200275 IpAddress mcastIp) {
Pierb0328e42018-03-27 11:29:42 -0700276 return getSinks(mcastIp).entrySet()
277 .stream()
278 .filter(hostIdSetEntry -> hostIdSetEntry.getValue().stream()
279 .map(ConnectPoint::deviceId)
280 .anyMatch(deviceId -> deviceId.equals(egressDevice))
281 ).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
282 }
283
284 /**
285 * Creates a next objective builder for multicast.
286 *
287 * @param mcastIp multicast group
288 * @param assignedVlan assigned VLAN ID
289 * @param outPorts set of output port numbers
290 * @param nextId the next id
291 * @return next objective builder
292 */
293 NextObjective.Builder nextObjBuilder(IpAddress mcastIp, VlanId assignedVlan,
294 Set<PortNumber> outPorts, Integer nextId) {
295 // If nextId is null allocate a new one
296 if (nextId == null) {
297 nextId = srManager.flowObjectiveService.allocateNextId();
298 }
299 // Build the meta selector with the fwd objective info
300 TrafficSelector metadata =
301 DefaultTrafficSelector.builder()
302 .matchVlanId(assignedVlan)
303 .matchIPDst(mcastIp.toIpPrefix())
304 .build();
305 // Define the nextobjective type
306 NextObjective.Builder nextObjBuilder = DefaultNextObjective
307 .builder().withId(nextId)
308 .withType(NextObjective.Type.BROADCAST)
309 .fromApp(srManager.appId())
310 .withMeta(metadata);
311 // Add the output ports
312 outPorts.forEach(port -> {
313 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
314 if (egressVlan().equals(VlanId.NONE)) {
315 tBuilder.popVlan();
316 }
317 tBuilder.setOutput(port);
318 nextObjBuilder.addTreatment(tBuilder.build());
319 });
320 // Done return the complete builder
321 return nextObjBuilder;
322 }
323
324 /**
325 * Creates a forwarding objective builder for multicast.
326 *
327 * @param mcastIp multicast group
328 * @param assignedVlan assigned VLAN ID
329 * @param nextId next ID of the L3 multicast group
330 * @return forwarding objective builder
331 */
332 ForwardingObjective.Builder fwdObjBuilder(IpAddress mcastIp,
333 VlanId assignedVlan, int nextId) {
334 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
335 // Let's the matching on the group address
336 // TODO SSM support in future
337 if (mcastIp.isIp6()) {
338 sbuilder.matchEthType(Ethernet.TYPE_IPV6);
339 sbuilder.matchIPv6Dst(mcastIp.toIpPrefix());
340 } else {
341 sbuilder.matchEthType(Ethernet.TYPE_IPV4);
342 sbuilder.matchIPDst(mcastIp.toIpPrefix());
343 }
344 // Then build the meta selector
345 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
346 metabuilder.matchVlanId(assignedVlan);
347 // Finally return the completed builder
348 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective.builder();
349 fwdBuilder.withSelector(sbuilder.build())
350 .withMeta(metabuilder.build())
351 .nextStep(nextId)
352 .withFlag(ForwardingObjective.Flag.SPECIFIC)
353 .fromApp(srManager.appId())
354 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY);
355 return fwdBuilder;
356 }
357
358 /**
359 * Creates a filtering objective builder for multicast.
360 *
361 * @param ingressPort ingress port of the multicast stream
362 * @param assignedVlan assigned VLAN ID
363 * @param mcastIp the group address
364 * @param routerMac router MAC. This is carried in metadata and used from some switches that
365 * need to put unicast entry before multicast entry in TMAC table.
366 * @param mcastRole the Multicast role
Harshada Chaundkarb42abd42019-07-02 16:01:24 +0000367 * @param matchOnMac match or not on macaddress
Pierb0328e42018-03-27 11:29:42 -0700368 * @return filtering objective builder
369 */
370 private FilteringObjective.Builder filterObjBuilder(PortNumber ingressPort, VlanId assignedVlan,
Harshada Chaundkarb42abd42019-07-02 16:01:24 +0000371 IpAddress mcastIp, MacAddress routerMac, McastRole mcastRole,
372 boolean matchOnMac) {
Pierb0328e42018-03-27 11:29:42 -0700373 FilteringObjective.Builder filtBuilder = DefaultFilteringObjective.builder();
374 // Let's add the in port matching and the priority
375 filtBuilder.withKey(Criteria.matchInPort(ingressPort))
376 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY);
377 // According to the mcast role we match on the proper vlan
378 // If the role is null we are on the transit or on the egress
379 if (mcastRole == null) {
380 filtBuilder.addCondition(Criteria.matchVlanId(egressVlan()));
381 } else {
382 filtBuilder.addCondition(Criteria.matchVlanId(ingressVlan()));
383 }
Harshada Chaundkarb42abd42019-07-02 16:01:24 +0000384 // Add vlan info to the treatment builder
385 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder()
386 .pushVlan().setVlanId(assignedVlan);
387 // Additionally match on mac address and augment the treatment
388 if (matchOnMac) {
389 // According to the IP type we set the proper match on the mac address
390 if (mcastIp.isIp4()) {
391 filtBuilder.addCondition(Criteria.matchEthDstMasked(MacAddress.IPV4_MULTICAST,
392 MacAddress.IPV4_MULTICAST_MASK));
393 } else {
394 filtBuilder.addCondition(Criteria.matchEthDstMasked(MacAddress.IPV6_MULTICAST,
395 MacAddress.IPV6_MULTICAST_MASK));
396 }
397 // We set mac address to the treatment
398 if (routerMac != null && !routerMac.equals(MacAddress.NONE)) {
399 ttb.setEthDst(routerMac);
400 }
Pierb0328e42018-03-27 11:29:42 -0700401 }
402 // We finally build the meta treatment
Harshada Chaundkarb42abd42019-07-02 16:01:24 +0000403 TrafficTreatment tt = ttb.build();
404 filtBuilder.withMeta(tt);
Pierb0328e42018-03-27 11:29:42 -0700405 // Done, we return a permit filtering objective
406 return filtBuilder.permit().fromApp(srManager.appId());
407 }
408
409 /**
410 * Gets output ports information from treatments.
411 *
412 * @param treatments collection of traffic treatments
413 * @return set of output port numbers
414 */
415 Set<PortNumber> getPorts(Collection<TrafficTreatment> treatments) {
416 ImmutableSet.Builder<PortNumber> builder = ImmutableSet.builder();
417 treatments.forEach(treatment -> treatment.allInstructions().stream()
418 .filter(instr -> instr instanceof Instructions.OutputInstruction)
419 .forEach(instr -> builder.add(((Instructions.OutputInstruction) instr).port())));
420 return builder.build();
421 }
Pier96f63cb2018-04-17 16:29:56 +0200422
423 /**
424 * Returns the hash of the group address.
425 *
426 * @param ipAddress the ip address
427 * @return the hash of the address
428 */
429 private Long hasher(IpAddress ipAddress) {
430 return HASH_FN.newHasher()
431 .putBytes(ipAddress.toOctets())
432 .hash()
433 .asLong();
434 }
435
436 /**
437 * Given a multicast group define a leader for it.
438 *
439 * @param mcastIp the group address
440 * @return true if the instance is the leader of the group
441 */
442 boolean isLeader(IpAddress mcastIp) {
443 // Get our id
444 final NodeId currentNodeId = srManager.clusterService.getLocalNode().id();
445 // Get the leader for this group using the ip address as key
446 final NodeId leader = srManager.workPartitionService.getLeader(mcastIp, this::hasher);
447 // If there is not a leader, let's send an error
448 if (leader == null) {
449 log.error("Fail to elect a leader for {}.", mcastIp);
450 return false;
451 }
452 // Update cache and return operation result
453 mcastLeaderCache.put(mcastIp, leader);
454 return currentNodeId.equals(leader);
455 }
456
457 /**
458 * Given a multicast group withdraw its leader.
459 *
460 * @param mcastIp the group address
461 */
462 void withdrawLeader(IpAddress mcastIp) {
463 // For now just update the cache
464 mcastLeaderCache.remove(mcastIp);
465 }
466
467 Map<IpAddress, NodeId> getMcastLeaders(IpAddress mcastIp) {
468 // If mcast ip is present
469 if (mcastIp != null) {
470 return mcastLeaderCache.entrySet().stream()
471 .filter(entry -> entry.getKey().equals(mcastIp))
472 .collect(Collectors.toMap(Map.Entry::getKey,
473 Map.Entry::getValue));
474 }
475 // Otherwise take all the groups
476 return ImmutableMap.copyOf(mcastLeaderCache);
477 }
Charles Chand5814aa2018-08-19 19:21:46 -0700478
479 /**
480 * Build recursively the mcast paths.
481 *
482 * @param mcastNextObjStore mcast next obj store
483 * @param toVisit the node to visit
484 * @param visited the visited nodes
485 * @param mcastPaths the current mcast paths
486 * @param currentPath the current path
487 * @param mcastIp the group ip
488 * @param source the source
489 */
490 void buildMcastPaths(Map<McastStoreKey, NextObjective> mcastNextObjStore,
491 DeviceId toVisit, Set<DeviceId> visited,
492 Map<ConnectPoint, List<ConnectPoint>> mcastPaths,
493 List<ConnectPoint> currentPath, IpAddress mcastIp,
494 ConnectPoint source) {
pier000af642020-02-03 13:50:53 +0100495 log.debug("Building Multicast paths recursively for {} - next device to visit is {}",
496 mcastIp, toVisit);
Charles Chand5814aa2018-08-19 19:21:46 -0700497 // If we have visited the node to visit there is a loop
498 if (visited.contains(toVisit)) {
499 return;
500 }
501 // Visit next-hop
502 visited.add(toVisit);
503 VlanId assignedVlan = assignedVlan(toVisit.equals(source.deviceId()) ? source : null);
504 McastStoreKey mcastStoreKey = new McastStoreKey(mcastIp, toVisit, assignedVlan);
505 // Looking for next-hops
506 if (mcastNextObjStore.containsKey(mcastStoreKey)) {
507 // Build egress connect points, get ports and build relative cps
508 NextObjective nextObjective = mcastNextObjStore.get(mcastStoreKey);
509 Set<PortNumber> outputPorts = getPorts(nextObjective.next());
510 ImmutableSet.Builder<ConnectPoint> cpBuilder = ImmutableSet.builder();
511 outputPorts.forEach(portNumber -> cpBuilder.add(new ConnectPoint(toVisit, portNumber)));
512 Set<ConnectPoint> egressPoints = cpBuilder.build();
513 Set<Link> egressLinks;
514 List<ConnectPoint> newCurrentPath;
515 Set<DeviceId> newVisited;
516 DeviceId newToVisit;
517 for (ConnectPoint egressPoint : egressPoints) {
518 egressLinks = srManager.linkService.getEgressLinks(egressPoint);
519 // If it does not have egress links, stop
520 if (egressLinks.isEmpty()) {
521 // Add the connect points to the path
522 newCurrentPath = Lists.newArrayList(currentPath);
523 newCurrentPath.add(0, egressPoint);
524 mcastPaths.put(egressPoint, newCurrentPath);
525 } else {
526 newVisited = Sets.newHashSet(visited);
527 // Iterate over the egress links for the next hops
528 for (Link egressLink : egressLinks) {
529 newToVisit = egressLink.dst().deviceId();
530 newCurrentPath = Lists.newArrayList(currentPath);
531 newCurrentPath.add(0, egressPoint);
532 newCurrentPath.add(0, egressLink.dst());
533 buildMcastPaths(mcastNextObjStore, newToVisit, newVisited, mcastPaths, newCurrentPath, mcastIp,
534 source);
535 }
536 }
537 }
538 }
539 }
Pierb0328e42018-03-27 11:29:42 -0700540}