blob: f8d30538eb03db86a25e7f5ffd8c73cf7cb127d2 [file] [log] [blame]
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -07001/*
Brian O'Connor0947d7e2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -07003 *
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 */
16package org.onosproject.segmentrouting.grouphandler;
17
Saurav Dasfe0b05e2017-08-14 16:44:43 -070018import com.google.common.collect.ImmutableMap;
Pier Ventre229fd0b2016-10-31 16:49:19 -070019import com.google.common.collect.Iterables;
Saurav Dasfe0b05e2017-08-14 16:44:43 -070020import com.google.common.collect.Lists;
Saurav Das62ae6792017-05-15 15:34:25 -070021import com.google.common.collect.Sets;
22
Pier Ventre229fd0b2016-10-31 16:49:19 -070023import org.apache.commons.lang3.RandomUtils;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070024import org.onlab.packet.MacAddress;
25import org.onlab.packet.MplsLabel;
Saurav Das62af8802015-12-04 10:52:59 -080026import org.onlab.packet.VlanId;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070027import org.onlab.util.KryoNamespace;
28import org.onosproject.core.ApplicationId;
Charles Chan10b0fb72017-02-02 16:20:42 -080029import org.onosproject.net.ConnectPoint;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070030import org.onosproject.net.DeviceId;
31import org.onosproject.net.Link;
32import org.onosproject.net.PortNumber;
Saurav Das62af8802015-12-04 10:52:59 -080033import org.onosproject.net.flow.DefaultTrafficSelector;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070034import org.onosproject.net.flow.DefaultTrafficTreatment;
Saurav Das4c35fc42015-11-20 15:27:53 -080035import org.onosproject.net.flow.TrafficSelector;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070036import org.onosproject.net.flow.TrafficTreatment;
Srikanth Vavilapalli64505482015-04-21 13:04:13 -070037import org.onosproject.net.flowobjective.DefaultNextObjective;
Charles Chana4ee4f92016-04-23 14:48:16 -070038import org.onosproject.net.flowobjective.DefaultObjectiveContext;
Srikanth Vavilapalli64505482015-04-21 13:04:13 -070039import org.onosproject.net.flowobjective.FlowObjectiveService;
40import org.onosproject.net.flowobjective.NextObjective;
Srikanth Vavilapalli8c83f1d2015-05-22 13:47:31 -070041import org.onosproject.net.flowobjective.ObjectiveContext;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070042import org.onosproject.net.link.LinkService;
Saurav Dasfbe74572017-08-03 18:30:35 -070043import org.onosproject.segmentrouting.DefaultRoutingHandler;
Saurav Das62af8802015-12-04 10:52:59 -080044import org.onosproject.segmentrouting.SegmentRoutingManager;
Charles Chan319d1a22015-11-03 10:42:14 -080045import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
46import org.onosproject.segmentrouting.config.DeviceProperties;
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +000047import org.onosproject.segmentrouting.config.DeviceConfiguration;
Saurav Das261c3002017-06-13 15:35:54 -070048import org.onosproject.segmentrouting.storekey.DestinationSetNextObjectiveStoreKey;
Charles Chan1eaf4802016-04-18 13:44:03 -070049import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
Charles Chan10b0fb72017-02-02 16:20:42 -080050import org.onosproject.segmentrouting.storekey.VlanNextObjectiveStoreKey;
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +000051import org.onosproject.segmentrouting.storekey.MacVlanNextObjectiveStoreKey;
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -070052import org.onosproject.store.service.EventuallyConsistentMap;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070053import org.slf4j.Logger;
54
Pier Ventre229fd0b2016-10-31 16:49:19 -070055import java.net.URI;
Charles Chan90772a72017-02-08 15:52:08 -080056import java.util.Collection;
Pier Ventre229fd0b2016-10-31 16:49:19 -070057import java.util.Collections;
Saurav Das62ae6792017-05-15 15:34:25 -070058import java.util.HashMap;
Pier Ventre229fd0b2016-10-31 16:49:19 -070059import java.util.HashSet;
60import java.util.List;
61import java.util.Map;
62import java.util.Set;
63import java.util.concurrent.ConcurrentHashMap;
Saurav Das8a3022d2017-05-05 17:01:08 -070064import java.util.concurrent.ScheduledExecutorService;
65import java.util.concurrent.TimeUnit;
Pier Ventre229fd0b2016-10-31 16:49:19 -070066import java.util.stream.Collectors;
67
68import static com.google.common.base.Preconditions.checkNotNull;
Saurav Das8a3022d2017-05-05 17:01:08 -070069import static java.util.concurrent.Executors.newScheduledThreadPool;
70import static org.onlab.util.Tools.groupedThreads;
Pier Ventre229fd0b2016-10-31 16:49:19 -070071import static org.slf4j.LoggerFactory.getLogger;
72
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070073/**
Srikanth Vavilapalli64505482015-04-21 13:04:13 -070074 * Default ECMP group handler creation module. This component creates a set of
75 * ECMP groups for every neighbor that this device is connected to based on
76 * whether the current device is an edge device or a transit device.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070077 */
78public class DefaultGroupHandler {
Ray Milkey5247fa92018-01-12 14:22:06 -080079 private static final Logger log = getLogger(DefaultGroupHandler.class);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070080
Saurav Dasfbe74572017-08-03 18:30:35 -070081 private static final long VERIFY_INTERVAL = 30; // secs
82
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070083 protected final DeviceId deviceId;
84 protected final ApplicationId appId;
85 protected final DeviceProperties deviceConfig;
86 protected final List<Integer> allSegmentIds;
Pier Ventreadb4ae62016-11-23 09:57:42 -080087 protected int ipv4NodeSegmentId = -1;
88 protected int ipv6NodeSegmentId = -1;
Charles Chan319d1a22015-11-03 10:42:14 -080089 protected boolean isEdgeRouter = false;
90 protected MacAddress nodeMacAddr = null;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070091 protected LinkService linkService;
Srikanth Vavilapalli64505482015-04-21 13:04:13 -070092 protected FlowObjectiveService flowObjectiveService;
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +000093 private DeviceConfiguration config;
94
Saurav Das62ae6792017-05-15 15:34:25 -070095 /**
96 * local store for neighbor-device-ids and the set of ports on this device
97 * that connect to the same neighbor.
98 */
Saurav Das4c35fc42015-11-20 15:27:53 -080099 protected ConcurrentHashMap<DeviceId, Set<PortNumber>> devicePortMap =
100 new ConcurrentHashMap<>();
Saurav Das62ae6792017-05-15 15:34:25 -0700101 /**
102 * local store for ports on this device connected to neighbor-device-id.
103 */
Saurav Das4c35fc42015-11-20 15:27:53 -0800104 protected ConcurrentHashMap<PortNumber, DeviceId> portDeviceMap =
105 new ConcurrentHashMap<>();
Saurav Das62ae6792017-05-15 15:34:25 -0700106
Saurav Das261c3002017-06-13 15:35:54 -0700107 // distributed store for (device+destination-set) mapped to next-id and neighbors
108 protected EventuallyConsistentMap<DestinationSetNextObjectiveStoreKey, NextNeighbors>
109 dsNextObjStore = null;
Saurav Dasf0f592d2016-11-18 15:21:57 -0800110 // distributed store for (device+subnet-ip-prefix) mapped to next-id
Charles Chan10b0fb72017-02-02 16:20:42 -0800111 protected EventuallyConsistentMap<VlanNextObjectiveStoreKey, Integer>
112 vlanNextObjStore = null;
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000113 // distributed store for (device+mac+vlan+treatment) mapped to next-id
114 protected EventuallyConsistentMap<MacVlanNextObjectiveStoreKey, Integer>
115 macVlanNextObjStore = null;
Saurav Dasf0f592d2016-11-18 15:21:57 -0800116 // distributed store for (device+port+treatment) mapped to next-id
Charles Chanb7f75ac2016-01-11 18:28:54 -0800117 protected EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
118 portNextObjStore = null;
Charles Chande6655c2015-12-23 00:15:11 -0800119 private SegmentRoutingManager srManager;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700120
Saurav Das8a3022d2017-05-05 17:01:08 -0700121 private ScheduledExecutorService executorService
Saurav Dasfbe74572017-08-03 18:30:35 -0700122 = newScheduledThreadPool(1, groupedThreads("bktCorrector", "bktC-%d", log));
Saurav Das8a3022d2017-05-05 17:01:08 -0700123
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700124 protected KryoNamespace.Builder kryo = new KryoNamespace.Builder()
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700125 .register(URI.class).register(HashSet.class)
Saurav Das261c3002017-06-13 15:35:54 -0700126 .register(DeviceId.class).register(PortNumber.class)
127 .register(DestinationSet.class).register(PolicyGroupIdentifier.class)
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700128 .register(PolicyGroupParams.class)
129 .register(GroupBucketIdentifier.class)
130 .register(GroupBucketIdentifier.BucketOutputType.class);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700131
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700132 protected DefaultGroupHandler(DeviceId deviceId, ApplicationId appId,
133 DeviceProperties config,
134 LinkService linkService,
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700135 FlowObjectiveService flowObjService,
Charles Chande6655c2015-12-23 00:15:11 -0800136 SegmentRoutingManager srManager) {
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700137 this.deviceId = checkNotNull(deviceId);
138 this.appId = checkNotNull(appId);
139 this.deviceConfig = checkNotNull(config);
140 this.linkService = checkNotNull(linkService);
Charles Chan319d1a22015-11-03 10:42:14 -0800141 this.allSegmentIds = checkNotNull(config.getAllDeviceSegmentIds());
142 try {
Pier Ventreadb4ae62016-11-23 09:57:42 -0800143 this.ipv4NodeSegmentId = config.getIPv4SegmentId(deviceId);
144 this.ipv6NodeSegmentId = config.getIPv6SegmentId(deviceId);
Charles Chan319d1a22015-11-03 10:42:14 -0800145 this.isEdgeRouter = config.isEdgeDevice(deviceId);
146 this.nodeMacAddr = checkNotNull(config.getDeviceMac(deviceId));
147 } catch (DeviceConfigNotFoundException e) {
148 log.warn(e.getMessage()
149 + " Skipping value assignment in DefaultGroupHandler");
150 }
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700151 this.flowObjectiveService = flowObjService;
Saurav Das261c3002017-06-13 15:35:54 -0700152 this.dsNextObjStore = srManager.dsNextObjStore();
Ray Milkeyb85de082017-04-05 09:42:04 -0700153 this.vlanNextObjStore = srManager.vlanNextObjStore();
154 this.portNextObjStore = srManager.portNextObjStore();
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000155 this.macVlanNextObjStore = srManager.macVlanNextObjStore();
Charles Chande6655c2015-12-23 00:15:11 -0800156 this.srManager = srManager;
Saurav Dasfbe74572017-08-03 18:30:35 -0700157 executorService.scheduleWithFixedDelay(new BucketCorrector(), 10,
158 VERIFY_INTERVAL,
159 TimeUnit.SECONDS);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700160 populateNeighborMaps();
161 }
162
163 /**
Saurav Dasfbe74572017-08-03 18:30:35 -0700164 * Gracefully shuts down a groupHandler. Typically called when the handler is
165 * no longer needed.
166 */
167 public void shutdown() {
168 executorService.shutdown();
169 }
170
171 /**
Saurav Das62ae6792017-05-15 15:34:25 -0700172 * Creates a group handler object.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700173 *
174 * @param deviceId device identifier
175 * @param appId application identifier
176 * @param config interface to retrieve the device properties
177 * @param linkService link service object
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700178 * @param flowObjService flow objective service object
Charles Chanb7f75ac2016-01-11 18:28:54 -0800179 * @param srManager segment routing manager
Charles Chan319d1a22015-11-03 10:42:14 -0800180 * @throws DeviceConfigNotFoundException if the device configuration is not found
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700181 * @return default group handler type
182 */
Saurav Das2d94d312015-11-24 23:21:05 -0800183 public static DefaultGroupHandler createGroupHandler(
Saurav Dasfbe74572017-08-03 18:30:35 -0700184 DeviceId deviceId,
185 ApplicationId appId,
186 DeviceProperties config,
187 LinkService linkService,
188 FlowObjectiveService flowObjService,
189 SegmentRoutingManager srManager)
190 throws DeviceConfigNotFoundException {
Saurav Das62ae6792017-05-15 15:34:25 -0700191 return new DefaultGroupHandler(deviceId, appId, config,
192 linkService,
193 flowObjService,
194 srManager);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700195 }
196
197 /**
Saurav Das97241862018-02-14 14:14:54 -0800198 * Updates local stores for link-src-device/port to neighbor (link-dst) for
199 * link that has come up.
Saurav Das62ae6792017-05-15 15:34:25 -0700200 *
201 * @param link the infrastructure link
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700202 */
Saurav Das62ae6792017-05-15 15:34:25 -0700203 public void portUpForLink(Link link) {
Saurav Dasfbe74572017-08-03 18:30:35 -0700204 if (!link.src().deviceId().equals(deviceId)) {
205 log.warn("linkUp: deviceId{} doesn't match with link src {}",
206 deviceId, link.src().deviceId());
207 return;
208 }
Saurav Das62ae6792017-05-15 15:34:25 -0700209
Saurav Dasfbe74572017-08-03 18:30:35 -0700210 log.info("* portUpForLink: Device {} linkUp at local port {} to "
211 + "neighbor {}", deviceId, link.src().port(), link.dst().deviceId());
212 // ensure local state is updated even if linkup is aborted later on
213 addNeighborAtPort(link.dst().deviceId(),
214 link.src().port());
215 }
Saurav Das62ae6792017-05-15 15:34:25 -0700216
Saurav Dasfbe74572017-08-03 18:30:35 -0700217 /**
Saurav Das97241862018-02-14 14:14:54 -0800218 * Updates local stores for link-src-device/port to neighbor (link-dst) for
219 * link that has gone down.
Saurav Dasfbe74572017-08-03 18:30:35 -0700220 *
Saurav Das97241862018-02-14 14:14:54 -0800221 * @param link the infrastructure link
Saurav Dasfbe74572017-08-03 18:30:35 -0700222 */
Saurav Das97241862018-02-14 14:14:54 -0800223 public void portDownForLink(Link link) {
224 PortNumber port = link.src().port();
Saurav Dasfbe74572017-08-03 18:30:35 -0700225 if (portDeviceMap.get(port) == null) {
226 log.warn("portDown: unknown port");
227 return;
228 }
Saurav Das62ae6792017-05-15 15:34:25 -0700229
Saurav Dasfbe74572017-08-03 18:30:35 -0700230 log.debug("Device {} portDown {} to neighbor {}", deviceId, port,
231 portDeviceMap.get(port));
232 devicePortMap.get(portDeviceMap.get(port)).remove(port);
233 portDeviceMap.remove(port);
234 }
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700235
236 /**
Saurav Das97241862018-02-14 14:14:54 -0800237 * Cleans up local stores for removed neighbor device.
238 *
239 * @param neighborId the device identifier for the neighbor device
240 */
241 public void cleanUpForNeighborDown(DeviceId neighborId) {
242 Set<PortNumber> ports = devicePortMap.remove(neighborId);
243 if (ports != null) {
244 ports.forEach(p -> portDeviceMap.remove(p));
245 }
246 }
247
248 /**
Saurav Das62ae6792017-05-15 15:34:25 -0700249 * Checks all groups in the src-device of link for neighbor sets that include
250 * the dst-device of link, and edits the hash groups according to link up
251 * or down. Should only be called by the master instance of the src-switch
252 * of link. Typically used when there are no route-path changes due to the
253 * link up or down, as the ECMPspg does not change.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700254 *
Saurav Das62ae6792017-05-15 15:34:25 -0700255 * @param link the infrastructure link that has gone down or come up
256 * @param linkDown true if link has gone down
257 * @param firstTime true if link has come up for the first time i.e a link
258 * not seen-before
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700259 */
Saurav Das62ae6792017-05-15 15:34:25 -0700260 public void retryHash(Link link, boolean linkDown, boolean firstTime) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700261 MacAddress neighborMac;
Charles Chan319d1a22015-11-03 10:42:14 -0800262 try {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700263 neighborMac = deviceConfig.getDeviceMac(link.dst().deviceId());
Charles Chan319d1a22015-11-03 10:42:14 -0800264 } catch (DeviceConfigNotFoundException e) {
Saurav Das62ae6792017-05-15 15:34:25 -0700265 log.warn(e.getMessage() + " Aborting retryHash.");
Charles Chan319d1a22015-11-03 10:42:14 -0800266 return;
267 }
Saurav Das261c3002017-06-13 15:35:54 -0700268 // find all the destinationSets related to link
269 Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet()
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700270 .stream()
Saurav Das261c3002017-06-13 15:35:54 -0700271 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Pier Luigiecb09f42018-01-14 21:56:11 +0100272 // Filter out PW transit groups or include them if MPLS ECMP is supported
Saurav Das97241862018-02-14 14:14:54 -0800273 .filter(entry -> !entry.getKey().destinationSet().notBos() ||
274 (entry.getKey().destinationSet().notBos() && srManager.getMplsEcmp()))
275 // Filter out simple SWAP groups or include them if MPLS ECMP is supported
276 .filter(entry -> !entry.getKey().destinationSet().swap() ||
277 (entry.getKey().destinationSet().swap() && srManager.getMplsEcmp()))
Saurav Das261c3002017-06-13 15:35:54 -0700278 .filter(entry -> entry.getValue().containsNextHop(link.dst().deviceId()))
279 .map(entry -> entry.getKey())
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700280 .collect(Collectors.toSet());
Saurav Das62ae6792017-05-15 15:34:25 -0700281
Saurav Das261c3002017-06-13 15:35:54 -0700282 log.debug("retryHash: dsNextObjStore contents for linkSrc {} -> linkDst {}: {}",
283 deviceId, link.dst().deviceId(), dsKeySet);
284
285 for (DestinationSetNextObjectiveStoreKey dsKey : dsKeySet) {
286 NextNeighbors nextHops = dsNextObjStore.get(dsKey);
287 if (nextHops == null) {
Saurav Das62ae6792017-05-15 15:34:25 -0700288 log.warn("retryHash in device {}, but global store has no record "
Saurav Das261c3002017-06-13 15:35:54 -0700289 + "for dsKey:{}", deviceId, dsKey);
Saurav Das62ae6792017-05-15 15:34:25 -0700290 continue;
291 }
Saurav Das261c3002017-06-13 15:35:54 -0700292 int nextId = nextHops.nextId();
293 Set<DeviceId> dstSet = nextHops.getDstForNextHop(link.dst().deviceId());
Saurav Das62ae6792017-05-15 15:34:25 -0700294 if (!linkDown) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700295 List<PortLabel> pl = Lists.newArrayList();
Saurav Das62ae6792017-05-15 15:34:25 -0700296 if (firstTime) {
297 // some links may have come up before the next-objective was created
298 // we take this opportunity to ensure other ports to same next-hop-dst
299 // are part of the hash group (see CORD-1180). Duplicate additions
300 // to the same hash group are avoided by the driver.
301 for (PortNumber p : devicePortMap.get(link.dst().deviceId())) {
Saurav Das261c3002017-06-13 15:35:54 -0700302 dstSet.forEach(dst -> {
303 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
Charles Chan223ae872018-08-22 17:56:47 -0700304 pl.add(new PortLabel(p, edgeLabel, popVlanInHashGroup(dsKey.destinationSet())));
Saurav Das261c3002017-06-13 15:35:54 -0700305 });
Saurav Das62ae6792017-05-15 15:34:25 -0700306 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700307 addToHashedNextObjective(pl, neighborMac, nextId);
308 } else {
309 // handle only the port that came up
310 dstSet.forEach(dst -> {
311 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
Charles Chan223ae872018-08-22 17:56:47 -0700312 pl.add(new PortLabel(link.src().port(), edgeLabel, popVlanInHashGroup(dsKey.destinationSet())));
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700313 });
314 addToHashedNextObjective(pl, neighborMac, nextId);
Saurav Das62ae6792017-05-15 15:34:25 -0700315 }
316 } else {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700317 // linkdown
318 List<PortLabel> pl = Lists.newArrayList();
Saurav Das261c3002017-06-13 15:35:54 -0700319 dstSet.forEach(dst -> {
320 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
Charles Chan223ae872018-08-22 17:56:47 -0700321 pl.add(new PortLabel(link.src().port(), edgeLabel, popVlanInHashGroup(dsKey.destinationSet())));
Saurav Das261c3002017-06-13 15:35:54 -0700322 });
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700323 removeFromHashedNextObjective(pl, neighborMac, nextId);
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700324 }
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700325 }
Saurav Das8a3022d2017-05-05 17:01:08 -0700326 }
327
Saurav Das62ae6792017-05-15 15:34:25 -0700328 /**
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700329 * Utility class for associating output ports and the corresponding MPLS
330 * labels to push. In dual-homing, there are different labels to push
331 * corresponding to the destination switches in an edge-pair. If both
332 * destinations are reachable via the same spine, then the output-port to
333 * the spine will be associated with two labels i.e. there will be two
334 * PortLabel objects for the same port but with different labels.
335 */
336 private class PortLabel {
337 PortNumber port;
338 int edgeLabel;
Charles Chan223ae872018-08-22 17:56:47 -0700339 boolean popVlan;
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700340
Charles Chan223ae872018-08-22 17:56:47 -0700341 PortLabel(PortNumber port, int edgeLabel, boolean popVlan) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700342 this.port = port;
343 this.edgeLabel = edgeLabel;
Charles Chan223ae872018-08-22 17:56:47 -0700344 this.popVlan = popVlan;
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700345 }
346
347 @Override
348 public String toString() {
Charles Chan223ae872018-08-22 17:56:47 -0700349 return port.toString() + "/" + String.valueOf(edgeLabel) + (popVlan ? "/popVlan" : "");
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700350 }
351 }
352
353 /**
354 * Makes a call to the FlowObjective service to add buckets to
355 * a hashed group. User must ensure that all the ports & labels are meant
356 * same neighbor (ie. dstMac).
Saurav Das62ae6792017-05-15 15:34:25 -0700357 *
Pier Luigiecb09f42018-01-14 21:56:11 +0100358 * @param portLabels a collection of port & label combinations to add
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700359 * to the hash group identified by the nextId
Saurav Das62ae6792017-05-15 15:34:25 -0700360 * @param dstMac destination mac address of next-hop
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700361 * @param nextId id for next-objective to which buckets will be added
Saurav Dasfbe74572017-08-03 18:30:35 -0700362 *
Saurav Das62ae6792017-05-15 15:34:25 -0700363 */
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700364 private void addToHashedNextObjective(Collection<PortLabel> portLabels,
365 MacAddress dstMac, Integer nextId) {
Saurav Das8a3022d2017-05-05 17:01:08 -0700366 // setup metadata to pass to nextObjective - indicate the vlan on egress
367 // if needed by the switch pipeline. Since hashed next-hops are always to
368 // other neighboring routers, there is no subnet assigned on those ports.
369 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
Saurav Das9bf49582018-08-13 15:34:26 -0700370 metabuilder.matchVlanId(srManager.getDefaultInternalVlan());
Saurav Das8a3022d2017-05-05 17:01:08 -0700371 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
372 .withId(nextId)
373 .withType(NextObjective.Type.HASHED)
Saurav Das8a3022d2017-05-05 17:01:08 -0700374 .withMeta(metabuilder.build())
375 .fromApp(appId);
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700376 // Create the new buckets to be updated
377 portLabels.forEach(pl -> {
378 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
379 tBuilder.setOutput(pl.port)
380 .setEthDst(dstMac)
381 .setEthSrc(nodeMacAddr);
Charles Chan223ae872018-08-22 17:56:47 -0700382 if (pl.popVlan) {
383 tBuilder.popVlan();
384 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700385 if (pl.edgeLabel != DestinationSet.NO_EDGE_LABEL) {
386 tBuilder.pushMpls()
387 .copyTtlOut()
388 .setMpls(MplsLabel.mplsLabel(pl.edgeLabel));
389 }
390 nextObjBuilder.addTreatment(tBuilder.build());
391 });
392
393 log.debug("addToHash in device {}: Adding Bucket with port/label {} "
394 + "to nextId {}", deviceId, portLabels, nextId);
Saurav Das8a3022d2017-05-05 17:01:08 -0700395
396 ObjectiveContext context = new DefaultObjectiveContext(
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700397 (objective) -> log.debug("addToHash port/label {} addedTo "
398 + "NextObj {} on {}", portLabels, nextId, deviceId),
Charles Chanfacfbef2018-08-23 14:30:33 -0700399 (objective, error) -> {
400 log.warn("addToHash failed to add port/label {} to NextObj {} on {}: {}",
401 portLabels, nextId, deviceId, error);
402 srManager.invalidateNextObj(objective.id());
403 });
Saurav Das8a3022d2017-05-05 17:01:08 -0700404 NextObjective nextObjective = nextObjBuilder.addToExisting(context);
405 flowObjectiveService.next(deviceId, nextObjective);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700406 }
407
408 /**
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700409 * Makes a call to the FlowObjective service to remove buckets from
410 * a hash group. User must ensure that all the ports & labels are meant
411 * same neighbor (ie. dstMac).
Saurav Dasfbe74572017-08-03 18:30:35 -0700412 *
Pier Luigiecb09f42018-01-14 21:56:11 +0100413 * @param portLabels a collection of port & label combinations to remove
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700414 * from the hash group identified by the nextId
Saurav Dasfbe74572017-08-03 18:30:35 -0700415 * @param dstMac destination mac address of next-hop
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700416 * @param nextId id for next-objective from which buckets will be removed
Saurav Dasfbe74572017-08-03 18:30:35 -0700417 */
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700418 private void removeFromHashedNextObjective(Collection<PortLabel> portLabels,
419 MacAddress dstMac, Integer nextId) {
Charles Chan8471a8d2018-08-30 19:49:23 -0700420 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
421 metabuilder.matchVlanId(srManager.getDefaultInternalVlan());
Saurav Dasfbe74572017-08-03 18:30:35 -0700422 NextObjective.Builder nextObjBuilder = DefaultNextObjective
423 .builder()
424 .withType(NextObjective.Type.HASHED) //same as original
Charles Chan8471a8d2018-08-30 19:49:23 -0700425 .withMeta(metabuilder.build())
Saurav Dasfbe74572017-08-03 18:30:35 -0700426 .withId(nextId)
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700427 .fromApp(appId);
428 // Create the buckets to be removed
429 portLabels.forEach(pl -> {
430 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
431 tBuilder.setOutput(pl.port)
432 .setEthDst(dstMac)
433 .setEthSrc(nodeMacAddr);
Charles Chan223ae872018-08-22 17:56:47 -0700434 if (pl.popVlan) {
435 tBuilder.popVlan();
436 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700437 if (pl.edgeLabel != DestinationSet.NO_EDGE_LABEL) {
438 tBuilder.pushMpls()
439 .copyTtlOut()
440 .setMpls(MplsLabel.mplsLabel(pl.edgeLabel));
441 }
442 nextObjBuilder.addTreatment(tBuilder.build());
443 });
444 log.debug("removeFromHash in device {}: Removing Bucket with port/label"
445 + " {} from nextId {}", deviceId, portLabels, nextId);
Saurav Das62ae6792017-05-15 15:34:25 -0700446
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700447 ObjectiveContext context = new DefaultObjectiveContext(
448 (objective) -> log.debug("port/label {} removedFrom NextObj"
449 + " {} on {}", portLabels, nextId, deviceId),
Charles Chanfacfbef2018-08-23 14:30:33 -0700450 (objective, error) -> {
451 log.warn("port/label {} failed to removeFrom NextObj {} on {}: {}",
452 portLabels, nextId, deviceId, error);
453 srManager.invalidateNextObj(objective.id());
454 });
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700455 NextObjective nextObjective = nextObjBuilder.removeFromExisting(context);
Saurav Dasfbe74572017-08-03 18:30:35 -0700456 flowObjectiveService.next(deviceId, nextObjective);
457 }
Saurav Das62ae6792017-05-15 15:34:25 -0700458
459 /**
460 * Checks all the hash-groups in the target-switch meant for the destination
461 * switch, and either adds or removes buckets to make the neighbor-set
462 * match the given next-hops. Typically called by the master instance of the
463 * destination switch, which may be different from the master instance of the
464 * target switch where hash-group changes are made.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700465 *
Saurav Das62ae6792017-05-15 15:34:25 -0700466 * @param targetSw the switch in which the hash groups will be edited
467 * @param nextHops the current next hops for the target switch to reach
468 * the dest sw
469 * @param destSw the destination switch
470 * @param revoke true if hash groups need to remove buckets from the
471 * the groups to match the current next hops
472 * @return true if calls are made to edit buckets, or if no edits are required
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700473 */
Saurav Das62ae6792017-05-15 15:34:25 -0700474 public boolean fixHashGroups(DeviceId targetSw, Set<DeviceId> nextHops,
475 DeviceId destSw, boolean revoke) {
476 // temporary storage of keys to be updated
Saurav Das261c3002017-06-13 15:35:54 -0700477 Map<DestinationSetNextObjectiveStoreKey, Set<DeviceId>> tempStore =
Saurav Das62ae6792017-05-15 15:34:25 -0700478 new HashMap<>();
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700479 boolean foundNextObjective = false, success = true;
Charles Chan319d1a22015-11-03 10:42:14 -0800480
Saurav Das261c3002017-06-13 15:35:54 -0700481 // retrieve hash-groups meant for destSw, which have destinationSets
Saurav Das62ae6792017-05-15 15:34:25 -0700482 // with different neighbors than the given next-hops
Saurav Das261c3002017-06-13 15:35:54 -0700483 for (DestinationSetNextObjectiveStoreKey dskey : dsNextObjStore.keySet()) {
484 if (!dskey.deviceId().equals(targetSw) ||
485 !dskey.destinationSet().getDestinationSwitches().contains(destSw)) {
Saurav Das62ae6792017-05-15 15:34:25 -0700486 continue;
487 }
488 foundNextObjective = true;
Saurav Das261c3002017-06-13 15:35:54 -0700489 NextNeighbors nhops = dsNextObjStore.get(dskey);
490 Set<DeviceId> currNeighbors = nhops.nextHops(destSw);
491 int edgeLabel = dskey.destinationSet().getEdgeLabel(destSw);
492 Integer nextId = nhops.nextId();
Saurav Dascea556f2018-03-05 14:37:16 -0800493 if (currNeighbors == null || nextHops == null) {
494 log.warn("fixing hash groups but found currNeighbors:{} or nextHops:{}"
495 + " in targetSw:{} for dstSw:{}", currNeighbors,
496 nextHops, targetSw, destSw);
497 success &= false;
498 continue;
499 }
Charles Chan319d1a22015-11-03 10:42:14 -0800500
Saurav Das97241862018-02-14 14:14:54 -0800501 // some store elements may not be hashed next-objectives - ignore them
502 if (isSimpleNextObjective(dskey)) {
503 log.debug("Ignoring {} of SIMPLE nextObj for targetSw:{}"
504 + " -> dstSw:{} with current nextHops:{} to new"
505 + " nextHops: {} in nextId:{}",
506 (revoke) ? "removal" : "addition", targetSw, destSw,
507 currNeighbors, nextHops, nextId);
Saurav Dascea556f2018-03-05 14:37:16 -0800508 if ((revoke && !nextHops.isEmpty())
509 || (!revoke && !nextHops.equals(currNeighbors))) {
Saurav Das68e1b6a2018-06-11 17:02:31 -0700510 log.debug("Simple next objective cannot be edited to "
Saurav Das97241862018-02-14 14:14:54 -0800511 + "move from {} to {}", currNeighbors, nextHops);
512 }
513 continue;
514 }
515
Saurav Das62ae6792017-05-15 15:34:25 -0700516 Set<DeviceId> diff;
517 if (revoke) {
518 diff = Sets.difference(currNeighbors, nextHops);
519 log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next "
520 + "hops:{} ..removing {}", targetSw, destSw, nextId,
521 currNeighbors, diff);
522 } else {
523 diff = Sets.difference(nextHops, currNeighbors);
524 log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next "
525 + "hops:{} ..adding {}", targetSw, destSw, nextId,
526 currNeighbors, diff);
527 }
Charles Chan223ae872018-08-22 17:56:47 -0700528 boolean suc = updateAllPortsToNextHop(diff, edgeLabel, nextId, popVlanInHashGroup(dskey.destinationSet()),
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700529 revoke);
530 if (suc) {
531 // to update neighbor set with changes made
Saurav Das62ae6792017-05-15 15:34:25 -0700532 if (revoke) {
Saurav Das261c3002017-06-13 15:35:54 -0700533 tempStore.put(dskey, Sets.difference(currNeighbors, diff));
Saurav Das62ae6792017-05-15 15:34:25 -0700534 } else {
Saurav Das261c3002017-06-13 15:35:54 -0700535 tempStore.put(dskey, Sets.union(currNeighbors, diff));
Saurav Das62ae6792017-05-15 15:34:25 -0700536 }
sangho2165d222015-05-01 09:38:25 -0700537 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700538 success &= suc;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700539 }
540
Saurav Das62ae6792017-05-15 15:34:25 -0700541 if (!foundNextObjective) {
542 log.debug("Cannot find any nextObjectives for route targetSw:{} "
543 + "-> dstSw:{}", targetSw, destSw);
pier572d4a92019-04-25 18:51:51 +0200544 return false; // nothing to do, return false so re-route will be performed
Saurav Das62ae6792017-05-15 15:34:25 -0700545 }
546
Saurav Das261c3002017-06-13 15:35:54 -0700547 // update the dsNextObjectiveStore with new destinationSet to nextId mappings
548 for (DestinationSetNextObjectiveStoreKey key : tempStore.keySet()) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700549 NextNeighbors currentNextHops = dsNextObjStore.get(key);
550 if (currentNextHops == null) {
551 log.warn("fixHashGroups could not update global store in "
552 + "device {} .. missing nextNeighbors for key {}",
553 deviceId, key);
Saurav Das62ae6792017-05-15 15:34:25 -0700554 continue;
555 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700556 Set<DeviceId> newNeighbors = new HashSet<>();
557 newNeighbors.addAll(tempStore.get(key));
558 Map<DeviceId, Set<DeviceId>> oldDstNextHops =
559 ImmutableMap.copyOf(currentNextHops.dstNextHops());
560 currentNextHops.dstNextHops().put(destSw, newNeighbors); //local change
561 log.debug("Updating nsNextObjStore target:{} -> dst:{} in key:{} nextId:{}",
562 targetSw, destSw, key, currentNextHops.nextId());
563 log.debug("Old dstNextHops: {}", oldDstNextHops);
564 log.debug("New dstNextHops: {}", currentNextHops.dstNextHops());
565 // update global store
566 dsNextObjStore.put(key,
567 new NextNeighbors(currentNextHops.dstNextHops(),
568 currentNextHops.nextId()));
Saurav Das62ae6792017-05-15 15:34:25 -0700569 }
Saurav Das97241862018-02-14 14:14:54 -0800570
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700571 // even if one fails and others succeed, return false so ECMPspg not updated
572 return success;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700573 }
574
Saurav Dasfbe74572017-08-03 18:30:35 -0700575 /**
576 * Updates the DestinationSetNextObjectiveStore with any per-destination nexthops
577 * that are not already in the store for the given DestinationSet. Note that
578 * this method does not remove existing next hops for the destinations in the
579 * DestinationSet.
580 *
581 * @param ds the DestinationSet for which the next hops need to be updated
582 * @param newDstNextHops a map of per-destination next hops to update the
583 * destinationSet with
584 * @return true if successful in updating all next hops
585 */
586 private boolean updateNextHops(DestinationSet ds,
Saurav Das261c3002017-06-13 15:35:54 -0700587 Map<DeviceId, Set<DeviceId>> newDstNextHops) {
588 DestinationSetNextObjectiveStoreKey key =
589 new DestinationSetNextObjectiveStoreKey(deviceId, ds);
590 NextNeighbors currNext = dsNextObjStore.get(key);
591 Map<DeviceId, Set<DeviceId>> currDstNextHops = currNext.dstNextHops();
592
593 // add newDstNextHops to currDstNextHops for each dst
594 boolean success = true;
595 for (DeviceId dstSw : ds.getDestinationSwitches()) {
596 Set<DeviceId> currNhops = currDstNextHops.get(dstSw);
597 Set<DeviceId> newNhops = newDstNextHops.get(dstSw);
598 currNhops = (currNhops == null) ? Sets.newHashSet() : currNhops;
599 newNhops = (newNhops == null) ? Sets.newHashSet() : newNhops;
600 int edgeLabel = ds.getEdgeLabel(dstSw);
601 int nextId = currNext.nextId();
602
603 // new next hops should be added
604 boolean suc = updateAllPortsToNextHop(Sets.difference(newNhops, currNhops),
Charles Chan223ae872018-08-22 17:56:47 -0700605 edgeLabel, nextId, popVlanInHashGroup(key.destinationSet()), false);
Saurav Das261c3002017-06-13 15:35:54 -0700606 if (suc) {
607 currNhops.addAll(newNhops);
608 currDstNextHops.put(dstSw, currNhops); // this is only a local change
609 }
610 success &= suc;
611 }
612
613 if (success) {
614 // update global store
615 dsNextObjStore.put(key, new NextNeighbors(currDstNextHops,
616 currNext.nextId()));
617 log.debug("Updated device:{} ds:{} new next-hops: {}", deviceId, ds,
618 dsNextObjStore.get(key));
619 }
620 return success;
621 }
622
Saurav Dasfbe74572017-08-03 18:30:35 -0700623 /**
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700624 * Adds or removes buckets for all ports to a set of neighbor devices. Caller
625 * needs to ensure that the given neighbors are all next hops towards the
626 * same destination (represented by the given edgeLabel).
Saurav Dasfbe74572017-08-03 18:30:35 -0700627 *
628 * @param neighbors set of neighbor device ids
629 * @param edgeLabel MPLS label to use in buckets
630 * @param nextId the nextObjective to change
Charles Chan223ae872018-08-22 17:56:47 -0700631 * @param popVlan this hash group bucket shuold includes a popVlan action
Saurav Dasfbe74572017-08-03 18:30:35 -0700632 * @param revoke true if buckets need to be removed, false if they need to
633 * be added
634 * @return true if successful in adding or removing buckets for all ports
635 * to the neighbors
636 */
637 private boolean updateAllPortsToNextHop(Set<DeviceId> neighbors, int edgeLabel,
Charles Chan223ae872018-08-22 17:56:47 -0700638 int nextId, boolean popVlan, boolean revoke) {
Saurav Dasfbe74572017-08-03 18:30:35 -0700639 for (DeviceId neighbor : neighbors) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700640 MacAddress neighborMac;
Saurav Das261c3002017-06-13 15:35:54 -0700641 try {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700642 neighborMac = deviceConfig.getDeviceMac(neighbor);
Saurav Das261c3002017-06-13 15:35:54 -0700643 } catch (DeviceConfigNotFoundException e) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700644 log.warn(e.getMessage() + " Aborting updateAllPortsToNextHop"
645 + " for nextId:" + nextId);
Saurav Das261c3002017-06-13 15:35:54 -0700646 return false;
647 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700648 Collection<PortNumber> portsToNeighbor = devicePortMap.get(neighbor);
649 if (portsToNeighbor == null || portsToNeighbor.isEmpty()) {
Saurav Das261c3002017-06-13 15:35:54 -0700650 log.warn("No ports found in dev:{} for neighbor:{} .. cannot "
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700651 + "updateAllPortsToNextHop for nextId: {}",
Saurav Das261c3002017-06-13 15:35:54 -0700652 deviceId, neighbor, nextId);
653 return false;
654 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700655 List<PortLabel> pl = Lists.newArrayList();
Charles Chan223ae872018-08-22 17:56:47 -0700656 portsToNeighbor.forEach(p -> pl.add(new PortLabel(p, edgeLabel, popVlan)));
Saurav Das261c3002017-06-13 15:35:54 -0700657 if (revoke) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700658 log.debug("updateAllPortsToNextHops in device {}: Removing Bucket(s) "
659 + "with Port/Label:{} to next object id {}",
660 deviceId, pl, nextId);
661 removeFromHashedNextObjective(pl, neighborMac, nextId);
Saurav Das261c3002017-06-13 15:35:54 -0700662 } else {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700663 log.debug("fixHashGroup in device {}: Adding Bucket(s) "
664 + "with Port/Label: {} to next object id {}",
665 deviceId, pl, nextId);
666 addToHashedNextObjective(pl, neighborMac, nextId);
Saurav Das261c3002017-06-13 15:35:54 -0700667 }
668 }
669 return true;
670 }
671
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700672 /**
Saurav Das97241862018-02-14 14:14:54 -0800673 * Returns true if the destination set is meant for swap or multi-labeled
674 * packet transport, and MPLS ECMP is not supported.
675 *
676 * @param dskey the key representing the destination set
677 * @return true if destination set is meant for simple next objectives
678 */
679 boolean isSimpleNextObjective(DestinationSetNextObjectiveStoreKey dskey) {
680 return (dskey.destinationSet().notBos() || dskey.destinationSet().swap())
681 && !srManager.getMplsEcmp();
682 }
683
684 /**
Saurav Das3fb28272017-03-04 16:08:47 -0800685 * Adds or removes a port that has been configured with a vlan to a broadcast group
686 * for bridging. Should only be called by the master instance for this device.
Saurav Dasf0f592d2016-11-18 15:21:57 -0800687 *
688 * @param port the port on this device that needs to be added/removed to a bcast group
Saurav Das3fb28272017-03-04 16:08:47 -0800689 * @param vlanId the vlan id corresponding to the broadcast domain/group
690 * @param popVlan indicates if packets should be sent out untagged or not out
691 * of the port. If true, indicates an access (untagged) or native vlan
692 * configuration. If false, indicates a trunk (tagged) vlan config.
Saurav Dasf0f592d2016-11-18 15:21:57 -0800693 * @param portUp true if port is enabled, false if disabled
Saurav Dasf0f592d2016-11-18 15:21:57 -0800694 */
Saurav Das3fb28272017-03-04 16:08:47 -0800695 public void processEdgePort(PortNumber port, VlanId vlanId,
696 boolean popVlan, boolean portUp) {
Saurav Dasf0f592d2016-11-18 15:21:57 -0800697 //get the next id for the subnet and edit it.
Charles Chan10b0fb72017-02-02 16:20:42 -0800698 Integer nextId = getVlanNextObjectiveId(vlanId);
Saurav Dasf0f592d2016-11-18 15:21:57 -0800699 if (nextId == -1) {
700 if (portUp) {
701 log.debug("**Creating flooding group for first port enabled in"
Saurav Das2b6a00f2017-12-05 15:00:23 -0800702 + " vlan {} on dev {} port {}", vlanId, deviceId, port);
Charles Chan10b0fb72017-02-02 16:20:42 -0800703 createBcastGroupFromVlan(vlanId, Collections.singleton(port));
Saurav Dasf0f592d2016-11-18 15:21:57 -0800704 } else {
705 log.warn("Could not find flooding group for subnet {} on dev:{} when"
Charles Chan10b0fb72017-02-02 16:20:42 -0800706 + " removing port:{}", vlanId, deviceId, port);
Saurav Dasf0f592d2016-11-18 15:21:57 -0800707 }
708 return;
709 }
710
711 log.info("**port{} in device {}: {} Bucket with Port {} to"
712 + " next-id {}", (portUp) ? "UP" : "DOWN", deviceId,
713 (portUp) ? "Adding" : "Removing",
714 port, nextId);
715 // Create the bucket to be added or removed
716 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Saurav Das3fb28272017-03-04 16:08:47 -0800717 if (popVlan) {
718 tBuilder.popVlan();
719 }
Saurav Dasf0f592d2016-11-18 15:21:57 -0800720 tBuilder.setOutput(port);
721
Saurav Dasf0f592d2016-11-18 15:21:57 -0800722 TrafficSelector metadata =
Saurav Das3fb28272017-03-04 16:08:47 -0800723 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
Saurav Dasf0f592d2016-11-18 15:21:57 -0800724
725 NextObjective.Builder nextObjBuilder = DefaultNextObjective
726 .builder().withId(nextId)
727 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
728 .addTreatment(tBuilder.build())
729 .withMeta(metadata);
730
731 ObjectiveContext context = new DefaultObjectiveContext(
732 (objective) -> log.debug("port {} successfully {} NextObj {} on {}",
733 port, (portUp) ? "addedTo" : "removedFrom",
734 nextId, deviceId),
Charles Chanfacfbef2018-08-23 14:30:33 -0700735 (objective, error) -> {
736 log.warn("port {} failed to {} NextObj {} on {}: {}",
737 port, (portUp) ? "addTo" : "removeFrom", nextId, deviceId, error);
738 srManager.invalidateNextObj(objective.id());
739 });
Saurav Dasf0f592d2016-11-18 15:21:57 -0800740
741 NextObjective nextObj = (portUp) ? nextObjBuilder.addToExisting(context)
742 : nextObjBuilder.removeFromExisting(context);
743 log.debug("edgePort processed: Submited next objective {} in device {}",
744 nextId, deviceId);
745 flowObjectiveService.next(deviceId, nextObj);
746 }
747
748 /**
Saurav Das97241862018-02-14 14:14:54 -0800749 * Returns the next objective of type hashed (or simple) associated with the
750 * destination set. In addition, updates the existing next-objective if new
751 * route-paths found have resulted in the addition of new next-hops to a
752 * particular destination. If there is no existing next objective for this
753 * destination set, this method would create a next objective and return the
754 * nextId. Optionally metadata can be passed in for the creation of the next
755 * objective. If the parameter simple is true then a simple next objective
756 * is created instead of a hashed one.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700757 *
Saurav Das261c3002017-06-13 15:35:54 -0700758 * @param ds destination set
759 * @param nextHops a map of per destination next hops
Saurav Das4c35fc42015-11-20 15:27:53 -0800760 * @param meta metadata passed into the creation of a Next Objective
Saurav Das97241862018-02-14 14:14:54 -0800761 * @param simple if true, a simple next objective will be created instead of
762 * a hashed next objective
Saurav Das4c35fc42015-11-20 15:27:53 -0800763 * @return int if found or -1 if there are errors in the creation of the
Saurav Das97241862018-02-14 14:14:54 -0800764 * neighbor set.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700765 */
Saurav Das261c3002017-06-13 15:35:54 -0700766 public int getNextObjectiveId(DestinationSet ds,
767 Map<DeviceId, Set<DeviceId>> nextHops,
Saurav Das97241862018-02-14 14:14:54 -0800768 TrafficSelector meta, boolean simple) {
Saurav Das261c3002017-06-13 15:35:54 -0700769 NextNeighbors next = dsNextObjStore.
770 get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
771 if (next == null) {
772 log.debug("getNextObjectiveId in device{}: Next objective id "
773 + "not found for {} ... creating", deviceId, ds);
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700774 log.trace("getNextObjectiveId: nsNextObjStore contents for device {}: {}",
775 deviceId,
Saurav Das261c3002017-06-13 15:35:54 -0700776 dsNextObjStore.entrySet()
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700777 .stream()
778 .filter((nsStoreEntry) ->
779 (nsStoreEntry.getKey().deviceId().equals(deviceId)))
780 .collect(Collectors.toList()));
Saurav Das261c3002017-06-13 15:35:54 -0700781
Saurav Das97241862018-02-14 14:14:54 -0800782 createGroupFromDestinationSet(ds, nextHops, meta, simple);
Saurav Das261c3002017-06-13 15:35:54 -0700783 next = dsNextObjStore.
784 get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
785 if (next == null) {
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700786 log.warn("getNextObjectiveId: unable to create next objective");
Saurav Das261c3002017-06-13 15:35:54 -0700787 // failure in creating group
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700788 return -1;
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700789 } else {
790 log.debug("getNextObjectiveId in device{}: Next objective id {} "
Saurav Das261c3002017-06-13 15:35:54 -0700791 + "created for {}", deviceId, next.nextId(), ds);
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700792 }
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700793 } else {
794 log.trace("getNextObjectiveId in device{}: Next objective id {} "
Saurav Das261c3002017-06-13 15:35:54 -0700795 + "found for {}", deviceId, next.nextId(), ds);
796 // should fix hash groups too if next-hops have changed
797 if (!next.dstNextHops().equals(nextHops)) {
798 log.debug("Nexthops have changed for dev:{} nextId:{} ..updating",
799 deviceId, next.nextId());
800 if (!updateNextHops(ds, nextHops)) {
801 // failure in updating group
802 return -1;
803 }
804 }
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700805 }
Saurav Das261c3002017-06-13 15:35:54 -0700806 return next.nextId();
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700807 }
808
sangho4a5c42a2015-05-20 22:16:38 -0700809 /**
Charles Chan10b0fb72017-02-02 16:20:42 -0800810 * Returns the next objective of type broadcast associated with the vlan,
Saurav Das2d94d312015-11-24 23:21:05 -0800811 * or -1 if no such objective exists. Note that this method does NOT create
812 * the next objective as a side-effect. It is expected that is objective is
Saurav Dasf0f592d2016-11-18 15:21:57 -0800813 * created at startup from network configuration. Typically this is used
814 * for L2 flooding within the subnet configured on the switch.
Charles Chan77277672015-10-20 16:24:19 -0700815 *
Charles Chan10b0fb72017-02-02 16:20:42 -0800816 * @param vlanId vlan id
Charles Chan77277672015-10-20 16:24:19 -0700817 * @return int if found or -1
818 */
Charles Chan10b0fb72017-02-02 16:20:42 -0800819 public int getVlanNextObjectiveId(VlanId vlanId) {
820 Integer nextId = vlanNextObjStore.
821 get(new VlanNextObjectiveStoreKey(deviceId, vlanId));
Charles Chanc6ad7752015-10-29 14:58:10 -0700822
823 return (nextId != null) ? nextId : -1;
Charles Chan77277672015-10-20 16:24:19 -0700824 }
825
826 /**
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000827 * Returns the next objective of type simple associated with the mac/vlan on the
828 * device, given the treatment. Different treatments to the same mac/vlan result
829 * in different next objectives. If no such objective exists, this method
830 * creates one (if requested) and returns the id. Optionally metadata can be passed in for
831 * the creation of the objective. Typically this is used for L2 and L3 forwarding
832 * to compute nodes and containers/VMs on the compute nodes directly attached
833 * to the switch.
834 *
835 * @param macAddr the mac addr for the simple next objective
836 * @param vlanId the vlan for the simple next objective
Ruchi Sahota07869322019-05-09 17:26:14 -0400837 * @param port port with which to create the Next Obj.
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000838 * @param createIfMissing true if a next object should be created if not found
839 * @return int if found or created, -1 if there are errors during the
840 * creation of the next objective.
841 */
Ruchi Sahota07869322019-05-09 17:26:14 -0400842 public int getMacVlanNextObjectiveId(MacAddress macAddr, VlanId vlanId, PortNumber port,
843 boolean createIfMissing) {
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000844
845 Integer nextId = macVlanNextObjStore
846 .get(new MacVlanNextObjectiveStoreKey(deviceId, macAddr, vlanId));
847
848 if (nextId != null) {
849 return nextId;
850 }
851
852 log.debug("getMacVlanNextObjectiveId in device {}: Next objective id "
853 + "not found for host : {}/{} .. {}", deviceId, macAddr, vlanId,
854 (createIfMissing) ? "creating" : "aborting");
855
856 if (!createIfMissing) {
857 return -1;
858 }
859
Ruchi Sahota07869322019-05-09 17:26:14 -0400860 MacAddress deviceMac;
861 try {
862 deviceMac = deviceConfig.getDeviceMac(deviceId);
863 } catch (DeviceConfigNotFoundException e) {
864 log.warn(e.getMessage() + " in getMacVlanNextObjectiveId");
865 return -1;
866 }
867
868 // since we are creating now, port cannot be null
869 if (port == null) {
870 log.debug("getMacVlanNextObjectiveId : port information cannot be null "
871 + "for device {}, host {}/{}", deviceId, macAddr, vlanId);
872 return -1;
873 }
874
875 TrafficSelector.Builder meta = DefaultTrafficSelector.builder();
876
877 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
878 treatment.deferred()
879 .setEthDst(macAddr)
880 .setEthSrc(deviceMac)
881 .setOutput(port);
882
883 ConnectPoint connectPoint = new ConnectPoint(deviceId, port);
884 VlanId untaggedVlan = srManager.interfaceService.getUntaggedVlanId(connectPoint);
885 Set<VlanId> taggedVlans = srManager.interfaceService.getTaggedVlanId(connectPoint);
886 VlanId nativeVlan = srManager.interfaceService.getNativeVlanId(connectPoint);
887
888 // Adjust the meta according to VLAN configuration
889 if (taggedVlans.contains(vlanId)) {
890 treatment.setVlanId(vlanId);
891 } else if (vlanId.equals(VlanId.NONE)) {
892 if (untaggedVlan != null) {
893 meta.matchVlanId(untaggedVlan);
894 } else if (nativeVlan != null) {
895 meta.matchVlanId(nativeVlan);
896 } else {
897 log.warn("Untagged nexthop {}/{} is not allowed on {} without untagged or native vlan",
898 macAddr, vlanId, connectPoint);
899 return -1;
900 }
901 } else {
902 log.warn("Tagged nexthop {}/{} is not allowed on {} without VLAN listed"
903 + " in tagged vlan", macAddr, vlanId, connectPoint);
904 return -1;
905 }
906
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000907 /* create missing next objective */
Ruchi Sahota07869322019-05-09 17:26:14 -0400908 nextId = createGroupFromMacVlan(macAddr, vlanId, treatment.build(), meta.build());
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000909 if (nextId == null) {
910 log.warn("getMacVlanNextObjectiveId: unable to create next obj"
911 + "for dev:{} host:{}/{}", deviceId, macAddr, vlanId);
912 return -1;
913 }
914 return nextId;
915 }
916
917
918 /**
Saurav Das2d94d312015-11-24 23:21:05 -0800919 * Returns the next objective of type simple associated with the port on the
920 * device, given the treatment. Different treatments to the same port result
921 * in different next objectives. If no such objective exists, this method
Saurav Das2cb38292017-03-29 19:09:17 -0700922 * creates one (if requested) and returns the id. Optionally metadata can be passed in for
Saurav Dasf0f592d2016-11-18 15:21:57 -0800923 * the creation of the objective. Typically this is used for L2 and L3 forwarding
924 * to compute nodes and containers/VMs on the compute nodes directly attached
925 * to the switch.
Saurav Das2d94d312015-11-24 23:21:05 -0800926 *
927 * @param portNum the port number for the simple next objective
928 * @param treatment the actions to apply on the packets (should include outport)
929 * @param meta optional metadata passed into the creation of the next objective
Saurav Das2cb38292017-03-29 19:09:17 -0700930 * @param createIfMissing true if a next object should be created if not found
Saurav Das2d94d312015-11-24 23:21:05 -0800931 * @return int if found or created, -1 if there are errors during the
932 * creation of the next objective.
933 */
934 public int getPortNextObjectiveId(PortNumber portNum, TrafficTreatment treatment,
Saurav Das2cb38292017-03-29 19:09:17 -0700935 TrafficSelector meta, boolean createIfMissing) {
Charles Chanb7f75ac2016-01-11 18:28:54 -0800936 Integer nextId = portNextObjStore
Saurav Das368cf212017-03-15 15:15:14 -0700937 .get(new PortNextObjectiveStoreKey(deviceId, portNum, treatment, meta));
Saurav Das2cb38292017-03-29 19:09:17 -0700938 if (nextId != null) {
939 return nextId;
940 }
941 log.debug("getPortNextObjectiveId in device {}: Next objective id "
942 + "not found for port: {} .. {}", deviceId, portNum,
943 (createIfMissing) ? "creating" : "aborting");
944 if (!createIfMissing) {
945 return -1;
946 }
947 // create missing next objective
948 createGroupFromPort(portNum, treatment, meta);
949 nextId = portNextObjStore.get(new PortNextObjectiveStoreKey(deviceId, portNum,
950 treatment, meta));
Saurav Das2d94d312015-11-24 23:21:05 -0800951 if (nextId == null) {
Saurav Das2cb38292017-03-29 19:09:17 -0700952 log.warn("getPortNextObjectiveId: unable to create next obj"
953 + "for dev:{} port:{}", deviceId, portNum);
954 return -1;
Charles Chanb7f75ac2016-01-11 18:28:54 -0800955 }
956 return nextId;
957 }
958
959 /**
sangho4a5c42a2015-05-20 22:16:38 -0700960 * Checks if the next objective ID (group) for the neighbor set exists or not.
961 *
962 * @param ns neighbor set to check
963 * @return true if it exists, false otherwise
964 */
Saurav Das261c3002017-06-13 15:35:54 -0700965 public boolean hasNextObjectiveId(DestinationSet ns) {
966 NextNeighbors nextHops = dsNextObjStore.
967 get(new DestinationSetNextObjectiveStoreKey(deviceId, ns));
968 if (nextHops == null) {
sangho4a5c42a2015-05-20 22:16:38 -0700969 return false;
970 }
971
972 return true;
973 }
974
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700975 private void populateNeighborMaps() {
976 Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700977 for (Link link : outgoingLinks) {
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700978 if (link.type() != Link.Type.DIRECT) {
979 continue;
980 }
981 addNeighborAtPort(link.dst().deviceId(), link.src().port());
982 }
983 }
984
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700985 protected void addNeighborAtPort(DeviceId neighborId,
986 PortNumber portToNeighbor) {
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700987 // Update DeviceToPort database
988 log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
989 deviceId, neighborId, portToNeighbor);
Saurav Das4c35fc42015-11-20 15:27:53 -0800990 Set<PortNumber> ports = Collections
991 .newSetFromMap(new ConcurrentHashMap<PortNumber, Boolean>());
992 ports.add(portToNeighbor);
993 Set<PortNumber> portnums = devicePortMap.putIfAbsent(neighborId, ports);
994 if (portnums != null) {
995 portnums.add(portToNeighbor);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700996 }
997
998 // Update portToDevice database
Saurav Das97241862018-02-14 14:14:54 -0800999 // should always update as neighbor could have changed on this port
1000 DeviceId prev = portDeviceMap.put(portToNeighbor, neighborId);
Saurav Das4c35fc42015-11-20 15:27:53 -08001001 if (prev != null) {
Saurav Dasdebcf882018-04-06 20:16:01 -07001002 log.warn("Device/port: {}/{} previous neighbor: {}, current neighbor: {} ",
Saurav Das62ae6792017-05-15 15:34:25 -07001003 deviceId, portToNeighbor, prev, neighborId);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -07001004 }
1005 }
1006
sangho27462c62015-05-14 00:39:53 -07001007 /**
Saurav Das261c3002017-06-13 15:35:54 -07001008 * Creates a NextObjective for a hash group in this device from a given
Saurav Das97241862018-02-14 14:14:54 -08001009 * DestinationSet. If the parameter simple is true, a simple next objective
1010 * is created instead.
sangho27462c62015-05-14 00:39:53 -07001011 *
Saurav Das261c3002017-06-13 15:35:54 -07001012 * @param ds the DestinationSet
1013 * @param neighbors a map for each destination and its next-hops
Saurav Das4c35fc42015-11-20 15:27:53 -08001014 * @param meta metadata passed into the creation of a Next Objective
Saurav Das97241862018-02-14 14:14:54 -08001015 * @param simple if true, a simple next objective will be created instead of
1016 * a hashed next objective
sangho27462c62015-05-14 00:39:53 -07001017 */
Saurav Das261c3002017-06-13 15:35:54 -07001018 public void createGroupFromDestinationSet(DestinationSet ds,
1019 Map<DeviceId, Set<DeviceId>> neighbors,
1020 TrafficSelector meta,
Saurav Das97241862018-02-14 14:14:54 -08001021 boolean simple) {
Saurav Das261c3002017-06-13 15:35:54 -07001022 int nextId = flowObjectiveService.allocateNextId();
Saurav Das97241862018-02-14 14:14:54 -08001023 NextObjective.Type type = (simple) ? NextObjective.Type.SIMPLE
1024 : NextObjective.Type.HASHED;
Saurav Das261c3002017-06-13 15:35:54 -07001025 if (neighbors == null || neighbors.isEmpty()) {
1026 log.warn("createGroupsFromDestinationSet: needs at least one neighbor"
1027 + "to create group in dev:{} for ds: {} with next-hops {}",
1028 deviceId, ds, neighbors);
1029 return;
1030 }
Saurav Das261c3002017-06-13 15:35:54 -07001031
1032 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1033 .builder()
1034 .withId(nextId)
1035 .withType(type)
1036 .fromApp(appId);
1037 if (meta != null) {
1038 nextObjBuilder.withMeta(meta);
1039 }
1040
1041 // create treatment buckets for each neighbor for each dst Device
1042 // except in the special case where we only want to pick a single
Saurav Das97241862018-02-14 14:14:54 -08001043 // neighbor/port for a simple nextObj
Saurav Das261c3002017-06-13 15:35:54 -07001044 boolean foundSingleNeighbor = false;
1045 boolean treatmentAdded = false;
1046 Map<DeviceId, Set<DeviceId>> dstNextHops = new ConcurrentHashMap<>();
1047 for (DeviceId dst : ds.getDestinationSwitches()) {
1048 Set<DeviceId> nextHops = neighbors.get(dst);
1049 if (nextHops == null || nextHops.isEmpty()) {
1050 continue;
Pier Ventre229fd0b2016-10-31 16:49:19 -07001051 }
Saurav Das261c3002017-06-13 15:35:54 -07001052
1053 if (foundSingleNeighbor) {
1054 break;
1055 }
1056
1057 for (DeviceId neighborId : nextHops) {
Saurav Das4c35fc42015-11-20 15:27:53 -08001058 if (devicePortMap.get(neighborId) == null) {
1059 log.warn("Neighbor {} is not in the port map yet for dev:{}",
1060 neighborId, deviceId);
sangho2165d222015-05-01 09:38:25 -07001061 return;
Jon Hall31d84782017-01-18 20:15:44 -08001062 } else if (devicePortMap.get(neighborId).isEmpty()) {
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -07001063 log.warn("There are no ports for "
Saurav Das4c35fc42015-11-20 15:27:53 -08001064 + "the Device {} in the port map yet", neighborId);
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -07001065 return;
sangho2165d222015-05-01 09:38:25 -07001066 }
1067
Saurav Das4c35fc42015-11-20 15:27:53 -08001068 MacAddress neighborMac;
Charles Chan319d1a22015-11-03 10:42:14 -08001069 try {
Saurav Das4c35fc42015-11-20 15:27:53 -08001070 neighborMac = deviceConfig.getDeviceMac(neighborId);
Charles Chan319d1a22015-11-03 10:42:14 -08001071 } catch (DeviceConfigNotFoundException e) {
Saurav Das261c3002017-06-13 15:35:54 -07001072 log.warn(e.getMessage() + " Aborting createGroupsFromDestinationset.");
Charles Chan319d1a22015-11-03 10:42:14 -08001073 return;
1074 }
Saurav Das261c3002017-06-13 15:35:54 -07001075 // For each port to the neighbor, we create a new treatment
Pier Ventre229fd0b2016-10-31 16:49:19 -07001076 Set<PortNumber> neighborPorts = devicePortMap.get(neighborId);
Saurav Das97241862018-02-14 14:14:54 -08001077 // In this case we need a SIMPLE nextObj. We randomly pick a port
1078 if (simple) {
Pier Ventre229fd0b2016-10-31 16:49:19 -07001079 int size = devicePortMap.get(neighborId).size();
1080 int index = RandomUtils.nextInt(0, size);
1081 neighborPorts = Collections.singleton(
Saurav Das261c3002017-06-13 15:35:54 -07001082 Iterables.get(devicePortMap.get(neighborId),
1083 index));
1084 foundSingleNeighbor = true;
Pier Ventre229fd0b2016-10-31 16:49:19 -07001085 }
Andreas Pantelopoulos5e7be3d2017-10-23 12:18:25 -07001086
Pier Ventre229fd0b2016-10-31 16:49:19 -07001087 for (PortNumber sp : neighborPorts) {
Srikanth Vavilapalli64505482015-04-21 13:04:13 -07001088 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
1089 .builder();
Saurav Das261c3002017-06-13 15:35:54 -07001090 tBuilder.setEthDst(neighborMac).setEthSrc(nodeMacAddr);
1091 int edgeLabel = ds.getEdgeLabel(dst);
1092 if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
Saurav Das97241862018-02-14 14:14:54 -08001093 if (simple) {
1094 // swap label case
1095 tBuilder.setMpls(MplsLabel.mplsLabel(edgeLabel));
1096 } else {
1097 // ecmp with label push case
1098 tBuilder.pushMpls().copyTtlOut()
1099 .setMpls(MplsLabel.mplsLabel(edgeLabel));
1100 }
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -07001101 }
Charles Chan81ddf6f2018-08-12 15:19:01 -07001102
1103 // Set VLAN ID for PW transport. Otherwise pop vlan
Charles Chan223ae872018-08-22 17:56:47 -07001104 if (!popVlanInHashGroup(ds)) {
Saurav Das9bf49582018-08-13 15:34:26 -07001105 tBuilder.setVlanId(srManager.getPwTransportVlan());
Charles Chan81ddf6f2018-08-12 15:19:01 -07001106 } else {
1107 tBuilder.popVlan();
Andreas Pantelopoulosb281ae22018-05-01 14:56:05 -07001108 }
Charles Chan81ddf6f2018-08-12 15:19:01 -07001109
Saurav Das4c35fc42015-11-20 15:27:53 -08001110 tBuilder.setOutput(sp);
Srikanth Vavilapalli64505482015-04-21 13:04:13 -07001111 nextObjBuilder.addTreatment(tBuilder.build());
Saurav Das261c3002017-06-13 15:35:54 -07001112 treatmentAdded = true;
1113 //update store
1114 Set<DeviceId> existingNeighbors = dstNextHops.get(dst);
1115 if (existingNeighbors == null) {
1116 existingNeighbors = new HashSet<>();
1117 }
1118 existingNeighbors.add(neighborId);
1119 dstNextHops.put(dst, existingNeighbors);
1120 log.debug("creating treatment for port/label {}/{} in next:{}",
1121 sp, edgeLabel, nextId);
1122 }
1123
1124 if (foundSingleNeighbor) {
1125 break;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -07001126 }
1127 }
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -07001128 }
Saurav Das261c3002017-06-13 15:35:54 -07001129
1130 if (!treatmentAdded) {
1131 log.warn("Could not createGroup from DestinationSet {} without any"
1132 + "next hops {}", ds, neighbors);
1133 return;
1134 }
1135 ObjectiveContext context = new DefaultObjectiveContext(
1136 (objective) ->
1137 log.debug("createGroupsFromDestinationSet installed "
1138 + "NextObj {} on {}", nextId, deviceId),
Charles Chanfacfbef2018-08-23 14:30:33 -07001139 (objective, error) -> {
1140 log.warn("createGroupsFromDestinationSet failed to install NextObj {} on {}: {}",
1141 nextId, deviceId, error);
1142 srManager.invalidateNextObj(objective.id());
1143 });
Saurav Das261c3002017-06-13 15:35:54 -07001144 NextObjective nextObj = nextObjBuilder.add(context);
1145 log.debug(".. createGroupsFromDestinationSet: Submitted "
1146 + "next objective {} in device {}", nextId, deviceId);
1147 flowObjectiveService.next(deviceId, nextObj);
1148 //update store
1149 dsNextObjStore.put(new DestinationSetNextObjectiveStoreKey(deviceId, ds),
1150 new NextNeighbors(dstNextHops, nextId));
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -07001151 }
1152
Saurav Das2d94d312015-11-24 23:21:05 -08001153 /**
Saurav Dasf0f592d2016-11-18 15:21:57 -08001154 * Creates broadcast groups for all ports in the same subnet for
1155 * all configured subnets.
Saurav Das2d94d312015-11-24 23:21:05 -08001156 */
Charles Chan10b0fb72017-02-02 16:20:42 -08001157 public void createGroupsFromVlanConfig() {
Charles Chan90772a72017-02-08 15:52:08 -08001158 srManager.getVlanPortMap(deviceId).asMap().forEach((vlanId, ports) -> {
Charles Chan10b0fb72017-02-02 16:20:42 -08001159 createBcastGroupFromVlan(vlanId, ports);
Pier Ventreb6a7f342016-11-26 21:05:22 -08001160 });
Saurav Dasf0f592d2016-11-18 15:21:57 -08001161 }
Charles Chanc6ad7752015-10-29 14:58:10 -07001162
Saurav Dasf0f592d2016-11-18 15:21:57 -08001163 /**
Charles Chan10b0fb72017-02-02 16:20:42 -08001164 * Creates a single broadcast group from a given vlan id and list of ports.
Saurav Dasf0f592d2016-11-18 15:21:57 -08001165 *
Charles Chan10b0fb72017-02-02 16:20:42 -08001166 * @param vlanId vlan id
Saurav Dasf0f592d2016-11-18 15:21:57 -08001167 * @param ports list of ports in the subnet
1168 */
Charles Chan90772a72017-02-08 15:52:08 -08001169 public void createBcastGroupFromVlan(VlanId vlanId, Collection<PortNumber> ports) {
Charles Chan10b0fb72017-02-02 16:20:42 -08001170 VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
Charles Chanc6ad7752015-10-29 14:58:10 -07001171
Charles Chan10b0fb72017-02-02 16:20:42 -08001172 if (vlanNextObjStore.containsKey(key)) {
Saurav Dasf0f592d2016-11-18 15:21:57 -08001173 log.debug("Broadcast group for device {} and subnet {} exists",
Charles Chan10b0fb72017-02-02 16:20:42 -08001174 deviceId, vlanId);
Saurav Dasf0f592d2016-11-18 15:21:57 -08001175 return;
1176 }
Charles Chande6655c2015-12-23 00:15:11 -08001177
Saurav Dasf0f592d2016-11-18 15:21:57 -08001178 TrafficSelector metadata =
Charles Chan10b0fb72017-02-02 16:20:42 -08001179 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
Charles Chan77277672015-10-20 16:24:19 -07001180
Saurav Dasf0f592d2016-11-18 15:21:57 -08001181 int nextId = flowObjectiveService.allocateNextId();
Charles Chan77277672015-10-20 16:24:19 -07001182
Saurav Dasf0f592d2016-11-18 15:21:57 -08001183 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1184 .builder().withId(nextId)
1185 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1186 .withMeta(metadata);
Charles Chan77277672015-10-20 16:24:19 -07001187
Saurav Dasf0f592d2016-11-18 15:21:57 -08001188 ports.forEach(port -> {
1189 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Charles Chan90772a72017-02-08 15:52:08 -08001190 if (toPopVlan(port, vlanId)) {
1191 tBuilder.popVlan();
1192 }
Saurav Dasf0f592d2016-11-18 15:21:57 -08001193 tBuilder.setOutput(port);
1194 nextObjBuilder.addTreatment(tBuilder.build());
Charles Chan77277672015-10-20 16:24:19 -07001195 });
Saurav Dasf0f592d2016-11-18 15:21:57 -08001196
Saurav Das2cb38292017-03-29 19:09:17 -07001197 ObjectiveContext context = new DefaultObjectiveContext(
1198 (objective) ->
1199 log.debug("createBroadcastGroupFromVlan installed "
1200 + "NextObj {} on {}", nextId, deviceId),
Charles Chanfacfbef2018-08-23 14:30:33 -07001201 (objective, error) -> {
1202 log.warn("createBroadcastGroupFromVlan failed to install NextObj {} on {}: {}",
1203 nextId, deviceId, error);
1204 srManager.invalidateNextObj(objective.id());
1205 });
Saurav Das2cb38292017-03-29 19:09:17 -07001206 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Dasf0f592d2016-11-18 15:21:57 -08001207 flowObjectiveService.next(deviceId, nextObj);
Saurav Das2b6a00f2017-12-05 15:00:23 -08001208 log.debug("createBcastGroupFromVlan: Submitted next objective {} "
1209 + "for vlan: {} in device {}", nextId, vlanId, deviceId);
Saurav Dasf0f592d2016-11-18 15:21:57 -08001210
Charles Chan10b0fb72017-02-02 16:20:42 -08001211 vlanNextObjStore.put(key, nextId);
Charles Chan77277672015-10-20 16:24:19 -07001212 }
1213
Charles Chanb7f75ac2016-01-11 18:28:54 -08001214 /**
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001215 * Removes a single broadcast group from a given vlan id.
1216 * The group should be empty.
1217 * @param deviceId device Id to remove the group
1218 * @param portNum port number related to the group
1219 * @param vlanId vlan id of the broadcast group to remove
1220 * @param popVlan true if the TrafficTreatment involves pop vlan tag action
1221 */
1222 public void removeBcastGroupFromVlan(DeviceId deviceId, PortNumber portNum,
1223 VlanId vlanId, boolean popVlan) {
1224 VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
1225
1226 if (!vlanNextObjStore.containsKey(key)) {
1227 log.debug("Broadcast group for device {} and subnet {} does not exist",
1228 deviceId, vlanId);
1229 return;
1230 }
1231
1232 TrafficSelector metadata =
1233 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1234
1235 int nextId = vlanNextObjStore.get(key);
1236
1237 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1238 .builder().withId(nextId)
1239 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1240 .withMeta(metadata);
1241
1242 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1243 if (popVlan) {
1244 tBuilder.popVlan();
1245 }
1246 tBuilder.setOutput(portNum);
1247 nextObjBuilder.addTreatment(tBuilder.build());
1248
1249 ObjectiveContext context = new DefaultObjectiveContext(
1250 (objective) ->
1251 log.debug("removeBroadcastGroupFromVlan removed "
1252 + "NextObj {} on {}", nextId, deviceId),
Charles Chanfacfbef2018-08-23 14:30:33 -07001253 (objective, error) -> {
1254 log.warn("removeBroadcastGroupFromVlan failed to remove NextObj {} on {}: {}",
1255 nextId, deviceId, error);
1256 srManager.invalidateNextObj(objective.id());
1257 });
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001258 NextObjective nextObj = nextObjBuilder.remove(context);
1259 flowObjectiveService.next(deviceId, nextObj);
1260 log.debug("removeBcastGroupFromVlan: Submited next objective {} in device {}",
1261 nextId, deviceId);
1262
1263 vlanNextObjStore.remove(key, nextId);
1264 }
1265
1266 /**
Charles Chan90772a72017-02-08 15:52:08 -08001267 * Determine if we should pop given vlan before sending packets to the given port.
1268 *
1269 * @param portNumber port number
1270 * @param vlanId vlan id
1271 * @return true if the vlan id is not contained in any vlanTagged config
1272 */
1273 private boolean toPopVlan(PortNumber portNumber, VlanId vlanId) {
Saurav Das261c3002017-06-13 15:35:54 -07001274 return srManager.interfaceService
1275 .getInterfacesByPort(new ConnectPoint(deviceId, portNumber))
Charles Chan90772a72017-02-08 15:52:08 -08001276 .stream().noneMatch(intf -> intf.vlanTagged().contains(vlanId));
1277 }
1278
1279 /**
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +00001280 * Create simple next objective for an indirect host mac/vlan. The treatments can include
1281 * all outgoing actions that need to happen on the packet.
1282 *
1283 * @param macAddr the mac address of the host
1284 * @param vlanId the vlan of the host
1285 * @param treatment the actions to apply on the packets (should include outport)
1286 * @param meta optional data to pass to the driver
1287 * @return next objective ID
1288 */
1289 public int createGroupFromMacVlan(MacAddress macAddr, VlanId vlanId, TrafficTreatment treatment,
1290 TrafficSelector meta) {
1291 int nextId = flowObjectiveService.allocateNextId();
1292 MacVlanNextObjectiveStoreKey key = new MacVlanNextObjectiveStoreKey(deviceId, macAddr, vlanId);
1293
1294 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1295 .builder().withId(nextId)
1296 .withType(NextObjective.Type.SIMPLE)
1297 .addTreatment(treatment)
1298 .fromApp(appId)
1299 .withMeta(meta);
1300
1301 ObjectiveContext context = new DefaultObjectiveContext(
1302 (objective) ->
1303 log.debug("createGroupFromMacVlan installed "
1304 + "NextObj {} on {}", nextId, deviceId),
1305 (objective, error) -> {
1306 log.warn("createGroupFromMacVlan failed to install NextObj {} on {}: {}", nextId, deviceId, error);
1307 srManager.invalidateNextObj(objective.id());
1308 });
1309 NextObjective nextObj = nextObjBuilder.add(context);
1310 flowObjectiveService.next(deviceId, nextObj);
1311 log.debug("createGroupFromMacVlan: Submited next objective {} in device {} "
1312 + "for host {}/{}", nextId, deviceId, macAddr, vlanId);
1313
1314 macVlanNextObjStore.put(key, nextId);
1315 return nextId;
1316 }
1317
1318 /**
Saurav Das2d94d312015-11-24 23:21:05 -08001319 * Create simple next objective for a single port. The treatments can include
1320 * all outgoing actions that need to happen on the packet.
1321 *
1322 * @param portNum the outgoing port on the device
1323 * @param treatment the actions to apply on the packets (should include outport)
1324 * @param meta optional data to pass to the driver
1325 */
1326 public void createGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
1327 TrafficSelector meta) {
1328 int nextId = flowObjectiveService.allocateNextId();
1329 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
Saurav Das368cf212017-03-15 15:15:14 -07001330 deviceId, portNum, treatment, meta);
Saurav Das2d94d312015-11-24 23:21:05 -08001331
1332 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1333 .builder().withId(nextId)
1334 .withType(NextObjective.Type.SIMPLE)
1335 .addTreatment(treatment)
1336 .fromApp(appId)
1337 .withMeta(meta);
1338
Saurav Das2cb38292017-03-29 19:09:17 -07001339 ObjectiveContext context = new DefaultObjectiveContext(
1340 (objective) ->
1341 log.debug("createGroupFromPort installed "
1342 + "NextObj {} on {}", nextId, deviceId),
Charles Chanfacfbef2018-08-23 14:30:33 -07001343 (objective, error) -> {
1344 log.warn("createGroupFromPort failed to install NextObj {} on {}: {}", nextId, deviceId, error);
1345 srManager.invalidateNextObj(objective.id());
1346 });
Saurav Das2cb38292017-03-29 19:09:17 -07001347 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Das2d94d312015-11-24 23:21:05 -08001348 flowObjectiveService.next(deviceId, nextObj);
1349 log.debug("createGroupFromPort: Submited next objective {} in device {} "
1350 + "for port {}", nextId, deviceId, portNum);
1351
1352 portNextObjStore.put(key, nextId);
1353 }
1354
sangho27462c62015-05-14 00:39:53 -07001355 /**
Shibu Vijayakumar632dd642018-03-01 15:45:59 -08001356 * Creates simple next objective for a single port.
1357 *
1358 * @param deviceId device id that has the port to deal with
1359 * @param portNum the outgoing port on the device
1360 * @param vlanId vlan id associated with the port
1361 * @param popVlan true if POP_VLAN action is applied on the packets, false otherwise
1362 */
1363 public void createPortNextObjective(DeviceId deviceId, PortNumber portNum, VlanId vlanId, boolean popVlan) {
1364 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
1365 mbuilder.matchVlanId(vlanId);
1366
1367 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
1368 tbuilder.immediate().setOutput(portNum);
1369 if (popVlan) {
1370 tbuilder.immediate().popVlan();
1371 }
1372
1373 createGroupFromPort(portNum, tbuilder.build(), mbuilder.build());
1374 }
1375
1376 /**
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001377 * Removes simple next objective for a single port.
1378 *
1379 * @param deviceId device id that has the port to deal with
1380 * @param portNum the outgoing port on the device
1381 * @param vlanId vlan id associated with the port
1382 * @param popVlan true if POP_VLAN action is applied on the packets, false otherwise
1383 */
1384 public void removePortNextObjective(DeviceId deviceId, PortNumber portNum, VlanId vlanId, boolean popVlan) {
1385 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
1386 mbuilder.matchVlanId(vlanId);
1387
1388 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
1389 tbuilder.immediate().setOutput(portNum);
1390 if (popVlan) {
1391 tbuilder.immediate().popVlan();
1392 }
1393
1394 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, portNum,
1395 tbuilder.build(), mbuilder.build(), false);
1396
1397 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
1398 deviceId, portNum, tbuilder.build(), mbuilder.build());
1399 if (portNextObjId != -1 && portNextObjStore.containsKey(key)) {
1400 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1401 .builder().withId(portNextObjId)
1402 .withType(NextObjective.Type.SIMPLE).fromApp(appId);
1403 ObjectiveContext context = new DefaultObjectiveContext(
1404 (objective) -> log.debug("removePortNextObjective removes NextObj {} on {}",
1405 portNextObjId, deviceId),
Charles Chanfacfbef2018-08-23 14:30:33 -07001406 (objective, error) -> {
1407 log.warn("removePortNextObjective failed to remove NextObj {} on {}: {}",
1408 portNextObjId, deviceId, error);
1409 srManager.invalidateNextObj(objective.id());
1410 });
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001411 NextObjective nextObjective = nextObjBuilder.remove(context);
1412 log.info("**removePortNextObjective: Submitted "
1413 + "next objective {} in device {}",
1414 portNextObjId, deviceId);
1415 flowObjectiveService.next(deviceId, nextObjective);
1416
1417 portNextObjStore.remove(key);
1418 }
1419 }
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +00001420
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001421 /**
sangho27462c62015-05-14 00:39:53 -07001422 * Removes groups for the next objective ID given.
1423 *
1424 * @param objectiveId next objective ID to remove
1425 * @return true if succeeds, false otherwise
1426 */
1427 public boolean removeGroup(int objectiveId) {
Saurav Das261c3002017-06-13 15:35:54 -07001428 for (Map.Entry<DestinationSetNextObjectiveStoreKey, NextNeighbors> e :
1429 dsNextObjStore.entrySet()) {
1430 if (e.getValue().nextId() != objectiveId) {
1431 continue;
1432 }
Pier Luigiecb09f42018-01-14 21:56:11 +01001433 // Right now it is just used in TunnelHandler
1434 // remember in future that PW transit groups could
1435 // be Indirect groups
sangho27462c62015-05-14 00:39:53 -07001436 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1437 .builder().withId(objectiveId)
1438 .withType(NextObjective.Type.HASHED).fromApp(appId);
Charles Chana4ee4f92016-04-23 14:48:16 -07001439 ObjectiveContext context = new DefaultObjectiveContext(
1440 (objective) -> log.debug("RemoveGroup removes NextObj {} on {}",
1441 objectiveId, deviceId),
Charles Chanfacfbef2018-08-23 14:30:33 -07001442 (objective, error) -> {
1443 log.warn("RemoveGroup failed to remove NextObj {} on {}: {}", objectiveId, deviceId, error);
1444 srManager.invalidateNextObj(objective.id());
1445 });
Charles Chana4ee4f92016-04-23 14:48:16 -07001446 NextObjective nextObjective = nextObjBuilder.remove(context);
Saurav Das4c35fc42015-11-20 15:27:53 -08001447 log.info("**removeGroup: Submited "
1448 + "next objective {} in device {}",
1449 objectiveId, deviceId);
sangho27462c62015-05-14 00:39:53 -07001450 flowObjectiveService.next(deviceId, nextObjective);
1451
Saurav Das261c3002017-06-13 15:35:54 -07001452 dsNextObjStore.remove(e.getKey());
sangho4a5c42a2015-05-20 22:16:38 -07001453 return true;
sangho27462c62015-05-14 00:39:53 -07001454 }
1455
1456 return false;
1457 }
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001458 /**
1459 * Remove simple next objective for a single port. The treatments can include
1460 * all outgoing actions that need to happen on the packet.
1461 *
1462 * @param portNum the outgoing port on the device
1463 * @param treatment the actions applied on the packets (should include outport)
1464 * @param meta optional data to pass to the driver
1465 */
1466 public void removeGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
1467 TrafficSelector meta) {
1468 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
1469 deviceId, portNum, treatment, meta);
1470 Integer nextId = portNextObjStore.get(key);
1471
1472 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1473 .builder().withId(nextId)
1474 .withType(NextObjective.Type.SIMPLE)
1475 .addTreatment(treatment)
1476 .fromApp(appId)
1477 .withMeta(meta);
1478
1479 ObjectiveContext context = new DefaultObjectiveContext(
1480 (objective) ->
1481 log.info("removeGroupFromPort installed "
1482 + "NextObj {} on {}", nextId, deviceId),
Charles Chanfacfbef2018-08-23 14:30:33 -07001483 (objective, error) -> {
1484 log.warn("removeGroupFromPort failed to install NextObj {} on {}: {}", nextId, deviceId, error);
1485 srManager.invalidateNextObj(objective.id());
1486 }
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001487 );
1488 NextObjective nextObj = nextObjBuilder.remove(context);
1489 flowObjectiveService.next(deviceId, nextObj);
1490 log.info("removeGroupFromPort: Submitted next objective {} in device {} "
1491 + "for port {}", nextId, deviceId, portNum);
1492
1493 portNextObjStore.remove(key);
1494 }
Srikanth Vavilapalli8c83f1d2015-05-22 13:47:31 -07001495
Charles Chanb7f75ac2016-01-11 18:28:54 -08001496 /**
1497 * Removes all groups from all next objective stores.
1498 */
Saurav Das261c3002017-06-13 15:35:54 -07001499 /*public void removeAllGroups() {
1500 for (Map.Entry<NeighborSetNextObjectiveStoreKey, NextNeighbors> entry:
Saurav Das62af8802015-12-04 10:52:59 -08001501 nsNextObjStore.entrySet()) {
Saurav Das261c3002017-06-13 15:35:54 -07001502 removeGroup(entry.getValue().nextId());
Saurav Das62af8802015-12-04 10:52:59 -08001503 }
1504 for (Map.Entry<PortNextObjectiveStoreKey, Integer> entry:
1505 portNextObjStore.entrySet()) {
1506 removeGroup(entry.getValue());
1507 }
Charles Chan10b0fb72017-02-02 16:20:42 -08001508 for (Map.Entry<VlanNextObjectiveStoreKey, Integer> entry:
1509 vlanNextObjStore.entrySet()) {
Saurav Das62af8802015-12-04 10:52:59 -08001510 removeGroup(entry.getValue());
1511 }
Saurav Das261c3002017-06-13 15:35:54 -07001512 }*/ //XXX revisit
1513
Saurav Dasfbe74572017-08-03 18:30:35 -07001514 /**
1515 * Triggers a one time bucket verification operation on all hash groups
1516 * on this device.
1517 */
1518 public void triggerBucketCorrector() {
1519 BucketCorrector bc = new BucketCorrector();
1520 bc.run();
1521 }
1522
Jonghwan Hyuna4ce0aa2018-02-12 16:43:45 +09001523 /**
1524 * Modifies L2IG bucket when the interface configuration is updated, especially
1525 * when the interface has same VLAN ID but the VLAN type is changed (e.g., from
1526 * vlan-tagged [10] to vlan-untagged 10), which requires changes on
1527 * TrafficTreatment in turn.
1528 *
1529 * @param portNumber the port on this device that needs to be updated
1530 * @param vlanId the vlan id corresponding to this port
1531 * @param pushVlan indicates if packets should be sent out untagged or not out
1532 * from the port. If true, updated TrafficTreatment involves
1533 * pop vlan tag action. If false, updated TrafficTreatment
1534 * does not involve pop vlan tag action.
1535 */
1536 public void updateL2InterfaceGroupBucket(PortNumber portNumber, VlanId vlanId, boolean pushVlan) {
Shibu Vijayakumar632dd642018-03-01 15:45:59 -08001537 TrafficTreatment.Builder oldTBuilder = DefaultTrafficTreatment.builder();
Jonghwan Hyuna4ce0aa2018-02-12 16:43:45 +09001538 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Shibu Vijayakumar632dd642018-03-01 15:45:59 -08001539 tBuilder.setOutput(portNumber);
1540 oldTBuilder.setOutput(portNumber);
Jonghwan Hyuna4ce0aa2018-02-12 16:43:45 +09001541 if (pushVlan) {
1542 tBuilder.popVlan();
Shibu Vijayakumar632dd642018-03-01 15:45:59 -08001543 } else {
1544 oldTBuilder.popVlan();
Jonghwan Hyuna4ce0aa2018-02-12 16:43:45 +09001545 }
Jonghwan Hyuna4ce0aa2018-02-12 16:43:45 +09001546
1547 TrafficSelector metadata =
1548 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1549
Shibu Vijayakumar632dd642018-03-01 15:45:59 -08001550 // Update portNextObjStore with new L2IG
1551 int nextId = getPortNextObjectiveId(portNumber, oldTBuilder.build(), metadata, false);
1552 portNextObjStore.remove(new PortNextObjectiveStoreKey(deviceId, portNumber, oldTBuilder.build(), metadata));
1553 portNextObjStore.put(new PortNextObjectiveStoreKey(deviceId, portNumber, tBuilder.build(), metadata), nextId);
Jonghwan Hyuna4ce0aa2018-02-12 16:43:45 +09001554
1555 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1556 .builder().withId(nextId)
1557 .withType(NextObjective.Type.SIMPLE).fromApp(appId)
1558 .addTreatment(tBuilder.build())
1559 .withMeta(metadata);
1560
1561 ObjectiveContext context = new DefaultObjectiveContext(
1562 (objective) -> log.debug("port {} successfully updated NextObj {} on {}",
1563 portNumber, nextId, deviceId),
Charles Chanfacfbef2018-08-23 14:30:33 -07001564 (objective, error) -> {
1565 log.warn("port {} failed to updated NextObj {} on {}: {}", portNumber, nextId, deviceId, error);
1566 srManager.invalidateNextObj(objective.id());
1567 });
Jonghwan Hyuna4ce0aa2018-02-12 16:43:45 +09001568
1569 flowObjectiveService.next(deviceId, nextObjBuilder.modify(context));
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001570 }
1571
Jonghwan Hyuna4ce0aa2018-02-12 16:43:45 +09001572 /**
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +00001573 * Updates the next objective for the given nextId .
1574 *
1575 * @param hostMac mac of host for which Next obj is to be updated.
1576 * @param hostVlanId vlan of host for which Next obj is to be updated.
1577 * @param port port with which to update the Next Obj.
1578 * @param nextId of Next Obj which needs to be updated.
1579 */
1580 public void updateL3UcastGroupBucket(MacAddress hostMac, VlanId hostVlanId, PortNumber port, int nextId) {
1581
1582 MacAddress deviceMac;
1583 try {
1584 deviceMac = deviceConfig.getDeviceMac(deviceId);
1585 } catch (DeviceConfigNotFoundException e) {
1586 log.warn(e.getMessage() + " in updateL3UcastGroupBucket");
1587 return;
1588 }
1589
1590 TrafficSelector metadata =
1591 DefaultTrafficSelector.builder().matchVlanId(hostVlanId).build();
1592
1593 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
1594 tbuilder.deferred()
1595 .setEthDst(hostMac)
1596 .setEthSrc(deviceMac)
1597 .setVlanId(hostVlanId)
1598 .setOutput(port);
1599
1600 log.debug(" update L3Ucast : deviceMac {}, port {}, host {}/{}, nextid {}, Treatment {} Meta {}",
1601 deviceMac, port, hostMac, hostVlanId, nextId, tbuilder.build(), metadata);
1602
1603 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1604 .builder().withId(nextId)
1605 .withType(NextObjective.Type.SIMPLE).fromApp(appId)
1606 .addTreatment(tbuilder.build())
1607 .withMeta(metadata);
1608
1609 ObjectiveContext context = new DefaultObjectiveContext(
1610 (objective) -> log.debug(" NextId {} successfully updated host {} vlan {} with port {}",
1611 nextId, hostMac, hostVlanId, port),
1612 (objective, error) -> {
1613 log.warn(" NextId {} failed to update host {} vlan {} with port {}, error : {}",
1614 nextId, hostMac, hostVlanId, port, error);
1615 srManager.invalidateNextObj(objective.id());
1616 });
1617
1618 NextObjective nextObj = nextObjBuilder.modify(context);
1619 flowObjectiveService.next(deviceId, nextObj);
1620
1621 }
1622
1623 /**
Jonghwan Hyuna4ce0aa2018-02-12 16:43:45 +09001624 * Adds a single port to the L2FG or removes it from the L2FG.
1625 *
1626 * @param vlanId the vlan id corresponding to this port
1627 * @param portNum the port on this device to be updated
1628 * @param nextId the next objective ID for the given vlan id
1629 * @param install if true, adds the port to L2FG. If false, removes it from L2FG.
1630 */
1631 public void updateGroupFromVlanConfiguration(VlanId vlanId, PortNumber portNum, int nextId, boolean install) {
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001632 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1633 if (toPopVlan(portNum, vlanId)) {
1634 tBuilder.popVlan();
1635 }
1636 tBuilder.setOutput(portNum);
1637
1638 TrafficSelector metadata =
1639 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1640
1641 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1642 .builder().withId(nextId)
1643 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1644 .addTreatment(tBuilder.build())
1645 .withMeta(metadata);
1646
1647 ObjectiveContext context = new DefaultObjectiveContext(
1648 (objective) -> log.debug("port {} successfully removedFrom NextObj {} on {}",
1649 portNum, nextId, deviceId),
Charles Chanfacfbef2018-08-23 14:30:33 -07001650 (objective, error) -> {
1651 log.warn("port {} failed to removedFrom NextObj {} on {}: {}", portNum, nextId, deviceId, error);
1652 srManager.invalidateNextObj(objective.id());
1653 });
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001654
1655 if (install) {
1656 flowObjectiveService.next(deviceId, nextObjBuilder.addToExisting(context));
1657 } else {
1658 flowObjectiveService.next(deviceId, nextObjBuilder.removeFromExisting(context));
1659 }
1660 }
Saurav Das8a3022d2017-05-05 17:01:08 -07001661
1662 /**
Saurav Dasfe0b05e2017-08-14 16:44:43 -07001663 * Performs bucket verification operation for all hash groups in this device.
1664 * Checks RouteHandler to ensure that routing is stable before attempting
1665 * verification. Verification involves creating a nextObjective with
1666 * operation VERIFY for existing next objectives in the store, and passing
1667 * it to the driver. It is the driver that actually performs the verification
1668 * by adding or removing buckets to match the verification next objective
1669 * created here.
Saurav Das8a3022d2017-05-05 17:01:08 -07001670 */
Saurav Dasfbe74572017-08-03 18:30:35 -07001671 protected final class BucketCorrector implements Runnable {
1672 Integer nextId;
Saurav Das8a3022d2017-05-05 17:01:08 -07001673
Saurav Dasfbe74572017-08-03 18:30:35 -07001674 BucketCorrector() {
1675 this.nextId = null;
1676 }
1677
1678 BucketCorrector(Integer nextId) {
1679 this.nextId = nextId;
Saurav Das8a3022d2017-05-05 17:01:08 -07001680 }
1681
1682 @Override
1683 public void run() {
Saurav Dasfbe74572017-08-03 18:30:35 -07001684 if (!srManager.mastershipService.isLocalMaster(deviceId)) {
1685 return;
Saurav Das8a3022d2017-05-05 17:01:08 -07001686 }
Saurav Dasfbe74572017-08-03 18:30:35 -07001687 DefaultRoutingHandler rh = srManager.getRoutingHandler();
1688 if (rh == null) {
1689 return;
1690 }
1691 if (!rh.isRoutingStable()) {
1692 return;
1693 }
1694 rh.acquireRoutingLock();
1695 try {
Saurav Dasfe0b05e2017-08-14 16:44:43 -07001696 log.trace("running bucket corrector for dev: {}", deviceId);
Saurav Dasfbe74572017-08-03 18:30:35 -07001697 Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet()
1698 .stream()
1699 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Pier Luigiecb09f42018-01-14 21:56:11 +01001700 // Filter out PW transit groups or include them if MPLS ECMP is supported
Saurav Das97241862018-02-14 14:14:54 -08001701 .filter(entry -> !entry.getKey().destinationSet().notBos() ||
1702 (entry.getKey().destinationSet().notBos() && srManager.getMplsEcmp()))
1703 // Filter out simple SWAP groups or include them if MPLS ECMP is supported
1704 .filter(entry -> !entry.getKey().destinationSet().swap() ||
1705 (entry.getKey().destinationSet().swap() && srManager.getMplsEcmp()))
Saurav Dasfbe74572017-08-03 18:30:35 -07001706 .map(entry -> entry.getKey())
1707 .collect(Collectors.toSet());
1708 for (DestinationSetNextObjectiveStoreKey dsKey : dsKeySet) {
1709 NextNeighbors next = dsNextObjStore.get(dsKey);
1710 if (next == null) {
1711 continue;
1712 }
1713 int nid = next.nextId();
1714 if (nextId != null && nextId != nid) {
1715 continue;
1716 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -07001717 log.trace("bkt-corr: dsNextObjStore for device {}: {}",
Saurav Dasfbe74572017-08-03 18:30:35 -07001718 deviceId, dsKey, next);
1719 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
Saurav Das9bf49582018-08-13 15:34:26 -07001720 metabuilder.matchVlanId(srManager.getDefaultInternalVlan());
Saurav Dasfbe74572017-08-03 18:30:35 -07001721 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
1722 .withId(nid)
1723 .withType(NextObjective.Type.HASHED)
1724 .withMeta(metabuilder.build())
1725 .fromApp(appId);
1726
1727 next.dstNextHops().forEach((dstDev, nextHops) -> {
1728 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dstDev);
1729 nextHops.forEach(neighbor -> {
1730 MacAddress neighborMac;
1731 try {
1732 neighborMac = deviceConfig.getDeviceMac(neighbor);
1733 } catch (DeviceConfigNotFoundException e) {
1734 log.warn(e.getMessage() + " Aborting neighbor"
1735 + neighbor);
1736 return;
1737 }
1738 devicePortMap.get(neighbor).forEach(port -> {
Saurav Dasfe0b05e2017-08-14 16:44:43 -07001739 log.trace("verify in device {} nextId {}: bucket with"
Saurav Dasfbe74572017-08-03 18:30:35 -07001740 + " port/label {}/{} to dst {} via {}",
1741 deviceId, nid, port, edgeLabel,
1742 dstDev, neighbor);
Saurav Das97241862018-02-14 14:14:54 -08001743 nextObjBuilder
1744 .addTreatment(treatmentBuilder(port,
1745 neighborMac,
1746 dsKey.destinationSet().swap(),
1747 edgeLabel));
Saurav Dasfbe74572017-08-03 18:30:35 -07001748 });
1749 });
1750 });
1751
1752 NextObjective nextObjective = nextObjBuilder.verify();
1753 flowObjectiveService.next(deviceId, nextObjective);
1754 }
1755 } finally {
1756 rh.releaseRoutingLock();
1757 }
1758
1759 }
1760
1761 TrafficTreatment treatmentBuilder(PortNumber outport, MacAddress dstMac,
Saurav Das97241862018-02-14 14:14:54 -08001762 boolean swap, int edgeLabel) {
Saurav Dasfbe74572017-08-03 18:30:35 -07001763 TrafficTreatment.Builder tBuilder =
1764 DefaultTrafficTreatment.builder();
1765 tBuilder.setOutput(outport)
1766 .setEthDst(dstMac)
1767 .setEthSrc(nodeMacAddr);
1768 if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
Saurav Das97241862018-02-14 14:14:54 -08001769 if (swap) {
1770 // swap label case
1771 tBuilder.setMpls(MplsLabel.mplsLabel(edgeLabel));
1772 } else {
1773 // ecmp with label push case
1774 tBuilder.pushMpls()
1775 .copyTtlOut()
1776 .setMpls(MplsLabel.mplsLabel(edgeLabel));
1777 }
Saurav Dasfbe74572017-08-03 18:30:35 -07001778 }
1779 return tBuilder.build();
Saurav Das8a3022d2017-05-05 17:01:08 -07001780 }
1781 }
1782
Charles Chan223ae872018-08-22 17:56:47 -07001783 /**
1784 * Determines whether the hash group bucket should include a popVlan action.
1785 * We don't popVlan for PW.
1786 *
1787 * @param ds destination set
1788 * @return true if VLAN needs to be popped
1789 */
1790 private boolean popVlanInHashGroup(DestinationSet ds) {
1791 return (ds.getTypeOfDstSet() != DestinationSet.DestinationSetType.SWAP_NOT_BOS) &&
1792 (ds.getTypeOfDstSet() != DestinationSet.DestinationSetType.POP_NOT_BOS);
1793 }
Pier Luigiecb09f42018-01-14 21:56:11 +01001794}