blob: adfe3f14a6c558f303bb89ecdb784261e1c891ba [file] [log] [blame]
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -08003 *
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 */
Srikanth Vavilapalli4db76e32015-04-07 15:12:32 -070016package org.onosproject.segmentrouting.grouphandler;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080017
Saurav Das9df5b7c2017-08-14 16:44:43 -070018import com.google.common.collect.ImmutableMap;
Ray Milkey1c5467b2018-03-06 09:26:34 -080019import com.google.common.collect.ImmutableSet;
Pier Ventre917127a2016-10-31 16:49:19 -070020import com.google.common.collect.Iterables;
Saurav Das9df5b7c2017-08-14 16:44:43 -070021import com.google.common.collect.Lists;
Saurav Dasc88d4662017-05-15 15:34:25 -070022import com.google.common.collect.Sets;
23
Pier Ventre917127a2016-10-31 16:49:19 -070024import org.apache.commons.lang3.RandomUtils;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080025import org.onlab.packet.MacAddress;
sangho32a59322015-02-17 12:07:41 -080026import org.onlab.packet.MplsLabel;
Saurav Das423fe2b2015-12-04 10:52:59 -080027import org.onlab.packet.VlanId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070028import org.onlab.util.KryoNamespace;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080029import org.onosproject.core.ApplicationId;
Charles Chan59cc16d2017-02-02 16:20:42 -080030import org.onosproject.net.ConnectPoint;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080031import org.onosproject.net.DeviceId;
32import org.onosproject.net.Link;
33import org.onosproject.net.PortNumber;
Saurav Das423fe2b2015-12-04 10:52:59 -080034import org.onosproject.net.flow.DefaultTrafficSelector;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080035import org.onosproject.net.flow.DefaultTrafficTreatment;
Saurav Das8a0732e2015-11-20 15:27:53 -080036import org.onosproject.net.flow.TrafficSelector;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080037import org.onosproject.net.flow.TrafficTreatment;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070038import org.onosproject.net.flowobjective.DefaultNextObjective;
Charles Chan216e3c82016-04-23 14:48:16 -070039import org.onosproject.net.flowobjective.DefaultObjectiveContext;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070040import org.onosproject.net.flowobjective.FlowObjectiveService;
41import org.onosproject.net.flowobjective.NextObjective;
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -070042import org.onosproject.net.flowobjective.ObjectiveContext;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080043import org.onosproject.net.link.LinkService;
Saurav Dasceccf242017-08-03 18:30:35 -070044import org.onosproject.segmentrouting.DefaultRoutingHandler;
Saurav Das423fe2b2015-12-04 10:52:59 -080045import org.onosproject.segmentrouting.SegmentRoutingManager;
Charles Chan0b4e6182015-11-03 10:42:14 -080046import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
47import org.onosproject.segmentrouting.config.DeviceProperties;
Saurav Das7bcbe702017-06-13 15:35:54 -070048import org.onosproject.segmentrouting.storekey.DestinationSetNextObjectiveStoreKey;
Charles Chand2990362016-04-18 13:44:03 -070049import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
Charles Chan59cc16d2017-02-02 16:20:42 -080050import org.onosproject.segmentrouting.storekey.VlanNextObjectiveStoreKey;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070051import org.onosproject.store.service.EventuallyConsistentMap;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080052import org.slf4j.Logger;
53
Pier Ventre917127a2016-10-31 16:49:19 -070054import java.net.URI;
Charles Chan7ffd81f2017-02-08 15:52:08 -080055import java.util.Collection;
Pier Ventre917127a2016-10-31 16:49:19 -070056import java.util.Collections;
Saurav Dasc88d4662017-05-15 15:34:25 -070057import java.util.HashMap;
Pier Ventre917127a2016-10-31 16:49:19 -070058import java.util.HashSet;
59import java.util.List;
60import java.util.Map;
61import java.util.Set;
62import java.util.concurrent.ConcurrentHashMap;
Saurav Das1547b3f2017-05-05 17:01:08 -070063import java.util.concurrent.ScheduledExecutorService;
64import java.util.concurrent.TimeUnit;
Pier Ventre917127a2016-10-31 16:49:19 -070065import java.util.stream.Collectors;
66
67import static com.google.common.base.Preconditions.checkNotNull;
Saurav Das1547b3f2017-05-05 17:01:08 -070068import static java.util.concurrent.Executors.newScheduledThreadPool;
69import static org.onlab.util.Tools.groupedThreads;
Charles Chan59cc16d2017-02-02 16:20:42 -080070import static org.onosproject.segmentrouting.SegmentRoutingManager.INTERNAL_VLAN;
Pier Ventre917127a2016-10-31 16:49:19 -070071import static org.slf4j.LoggerFactory.getLogger;
72
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080073/**
Srikanth Vavilapallif5b234a2015-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 Vavilapallied12ae52015-02-09 14:43:19 -080077 */
78public class DefaultGroupHandler {
Ray Milkey9c9cde42018-01-12 14:22:06 -080079 private static final Logger log = getLogger(DefaultGroupHandler.class);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080080
Saurav Dasceccf242017-08-03 18:30:35 -070081 private static final long VERIFY_INTERVAL = 30; // secs
82
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080083 protected final DeviceId deviceId;
84 protected final ApplicationId appId;
85 protected final DeviceProperties deviceConfig;
86 protected final List<Integer> allSegmentIds;
Pier Ventree0ae7a32016-11-23 09:57:42 -080087 protected int ipv4NodeSegmentId = -1;
88 protected int ipv6NodeSegmentId = -1;
Charles Chan0b4e6182015-11-03 10:42:14 -080089 protected boolean isEdgeRouter = false;
90 protected MacAddress nodeMacAddr = null;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080091 protected LinkService linkService;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070092 protected FlowObjectiveService flowObjectiveService;
Saurav Dasc88d4662017-05-15 15:34:25 -070093 /**
94 * local store for neighbor-device-ids and the set of ports on this device
95 * that connect to the same neighbor.
96 */
Saurav Das8a0732e2015-11-20 15:27:53 -080097 protected ConcurrentHashMap<DeviceId, Set<PortNumber>> devicePortMap =
98 new ConcurrentHashMap<>();
Saurav Dasc88d4662017-05-15 15:34:25 -070099 /**
100 * local store for ports on this device connected to neighbor-device-id.
101 */
Saurav Das8a0732e2015-11-20 15:27:53 -0800102 protected ConcurrentHashMap<PortNumber, DeviceId> portDeviceMap =
103 new ConcurrentHashMap<>();
Saurav Dasc88d4662017-05-15 15:34:25 -0700104
Saurav Das7bcbe702017-06-13 15:35:54 -0700105 // distributed store for (device+destination-set) mapped to next-id and neighbors
106 protected EventuallyConsistentMap<DestinationSetNextObjectiveStoreKey, NextNeighbors>
107 dsNextObjStore = null;
Saurav Das1a129a02016-11-18 15:21:57 -0800108 // distributed store for (device+subnet-ip-prefix) mapped to next-id
Charles Chan59cc16d2017-02-02 16:20:42 -0800109 protected EventuallyConsistentMap<VlanNextObjectiveStoreKey, Integer>
110 vlanNextObjStore = null;
Saurav Das1a129a02016-11-18 15:21:57 -0800111 // distributed store for (device+port+treatment) mapped to next-id
Charles Chane849c192016-01-11 18:28:54 -0800112 protected EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
113 portNextObjStore = null;
Charles Chan188ebf52015-12-23 00:15:11 -0800114 private SegmentRoutingManager srManager;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800115
Saurav Das1547b3f2017-05-05 17:01:08 -0700116 private ScheduledExecutorService executorService
Saurav Dasceccf242017-08-03 18:30:35 -0700117 = newScheduledThreadPool(1, groupedThreads("bktCorrector", "bktC-%d", log));
Saurav Das1547b3f2017-05-05 17:01:08 -0700118
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700119 protected KryoNamespace.Builder kryo = new KryoNamespace.Builder()
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700120 .register(URI.class).register(HashSet.class)
Saurav Das7bcbe702017-06-13 15:35:54 -0700121 .register(DeviceId.class).register(PortNumber.class)
122 .register(DestinationSet.class).register(PolicyGroupIdentifier.class)
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700123 .register(PolicyGroupParams.class)
124 .register(GroupBucketIdentifier.class)
125 .register(GroupBucketIdentifier.BucketOutputType.class);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800126
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700127 protected DefaultGroupHandler(DeviceId deviceId, ApplicationId appId,
128 DeviceProperties config,
129 LinkService linkService,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700130 FlowObjectiveService flowObjService,
Charles Chan188ebf52015-12-23 00:15:11 -0800131 SegmentRoutingManager srManager) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800132 this.deviceId = checkNotNull(deviceId);
133 this.appId = checkNotNull(appId);
134 this.deviceConfig = checkNotNull(config);
135 this.linkService = checkNotNull(linkService);
Charles Chan0b4e6182015-11-03 10:42:14 -0800136 this.allSegmentIds = checkNotNull(config.getAllDeviceSegmentIds());
137 try {
Pier Ventree0ae7a32016-11-23 09:57:42 -0800138 this.ipv4NodeSegmentId = config.getIPv4SegmentId(deviceId);
139 this.ipv6NodeSegmentId = config.getIPv6SegmentId(deviceId);
Charles Chan0b4e6182015-11-03 10:42:14 -0800140 this.isEdgeRouter = config.isEdgeDevice(deviceId);
141 this.nodeMacAddr = checkNotNull(config.getDeviceMac(deviceId));
142 } catch (DeviceConfigNotFoundException e) {
143 log.warn(e.getMessage()
144 + " Skipping value assignment in DefaultGroupHandler");
145 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700146 this.flowObjectiveService = flowObjService;
Saurav Das7bcbe702017-06-13 15:35:54 -0700147 this.dsNextObjStore = srManager.dsNextObjStore();
Ray Milkeye4afdb52017-04-05 09:42:04 -0700148 this.vlanNextObjStore = srManager.vlanNextObjStore();
149 this.portNextObjStore = srManager.portNextObjStore();
Charles Chan188ebf52015-12-23 00:15:11 -0800150 this.srManager = srManager;
Saurav Dasceccf242017-08-03 18:30:35 -0700151 executorService.scheduleWithFixedDelay(new BucketCorrector(), 10,
152 VERIFY_INTERVAL,
153 TimeUnit.SECONDS);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800154 populateNeighborMaps();
155 }
156
157 /**
Saurav Dasceccf242017-08-03 18:30:35 -0700158 * Gracefully shuts down a groupHandler. Typically called when the handler is
159 * no longer needed.
160 */
161 public void shutdown() {
162 executorService.shutdown();
163 }
164
165 /**
Saurav Dasc88d4662017-05-15 15:34:25 -0700166 * Creates a group handler object.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800167 *
168 * @param deviceId device identifier
169 * @param appId application identifier
170 * @param config interface to retrieve the device properties
171 * @param linkService link service object
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700172 * @param flowObjService flow objective service object
Charles Chane849c192016-01-11 18:28:54 -0800173 * @param srManager segment routing manager
Charles Chan0b4e6182015-11-03 10:42:14 -0800174 * @throws DeviceConfigNotFoundException if the device configuration is not found
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800175 * @return default group handler type
176 */
Saurav Das4ce45962015-11-24 23:21:05 -0800177 public static DefaultGroupHandler createGroupHandler(
Saurav Dasceccf242017-08-03 18:30:35 -0700178 DeviceId deviceId,
179 ApplicationId appId,
180 DeviceProperties config,
181 LinkService linkService,
182 FlowObjectiveService flowObjService,
183 SegmentRoutingManager srManager)
184 throws DeviceConfigNotFoundException {
Saurav Dasc88d4662017-05-15 15:34:25 -0700185 return new DefaultGroupHandler(deviceId, appId, config,
186 linkService,
187 flowObjService,
188 srManager);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800189 }
190
191 /**
Saurav Dasa4020382018-02-14 14:14:54 -0800192 * Updates local stores for link-src-device/port to neighbor (link-dst) for
193 * link that has come up.
Saurav Dasc88d4662017-05-15 15:34:25 -0700194 *
195 * @param link the infrastructure link
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800196 */
Saurav Dasc88d4662017-05-15 15:34:25 -0700197 public void portUpForLink(Link link) {
Saurav Dasceccf242017-08-03 18:30:35 -0700198 if (!link.src().deviceId().equals(deviceId)) {
199 log.warn("linkUp: deviceId{} doesn't match with link src {}",
200 deviceId, link.src().deviceId());
201 return;
202 }
Saurav Dasc88d4662017-05-15 15:34:25 -0700203
Saurav Dasceccf242017-08-03 18:30:35 -0700204 log.info("* portUpForLink: Device {} linkUp at local port {} to "
205 + "neighbor {}", deviceId, link.src().port(), link.dst().deviceId());
206 // ensure local state is updated even if linkup is aborted later on
207 addNeighborAtPort(link.dst().deviceId(),
208 link.src().port());
209 }
Saurav Dasc88d4662017-05-15 15:34:25 -0700210
Saurav Dasceccf242017-08-03 18:30:35 -0700211 /**
Saurav Dasa4020382018-02-14 14:14:54 -0800212 * Updates local stores for link-src-device/port to neighbor (link-dst) for
213 * link that has gone down.
Saurav Dasceccf242017-08-03 18:30:35 -0700214 *
Saurav Dasa4020382018-02-14 14:14:54 -0800215 * @param link the infrastructure link
Saurav Dasceccf242017-08-03 18:30:35 -0700216 */
Saurav Dasa4020382018-02-14 14:14:54 -0800217 public void portDownForLink(Link link) {
218 PortNumber port = link.src().port();
Saurav Dasceccf242017-08-03 18:30:35 -0700219 if (portDeviceMap.get(port) == null) {
220 log.warn("portDown: unknown port");
221 return;
222 }
Saurav Dasc88d4662017-05-15 15:34:25 -0700223
Saurav Dasceccf242017-08-03 18:30:35 -0700224 log.debug("Device {} portDown {} to neighbor {}", deviceId, port,
225 portDeviceMap.get(port));
226 devicePortMap.get(portDeviceMap.get(port)).remove(port);
227 portDeviceMap.remove(port);
228 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800229
230 /**
Saurav Dasa4020382018-02-14 14:14:54 -0800231 * Cleans up local stores for removed neighbor device.
232 *
233 * @param neighborId the device identifier for the neighbor device
234 */
235 public void cleanUpForNeighborDown(DeviceId neighborId) {
236 Set<PortNumber> ports = devicePortMap.remove(neighborId);
237 if (ports != null) {
238 ports.forEach(p -> portDeviceMap.remove(p));
239 }
240 }
241
242 /**
Saurav Dasc88d4662017-05-15 15:34:25 -0700243 * Checks all groups in the src-device of link for neighbor sets that include
244 * the dst-device of link, and edits the hash groups according to link up
245 * or down. Should only be called by the master instance of the src-switch
246 * of link. Typically used when there are no route-path changes due to the
247 * link up or down, as the ECMPspg does not change.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800248 *
Saurav Dasc88d4662017-05-15 15:34:25 -0700249 * @param link the infrastructure link that has gone down or come up
250 * @param linkDown true if link has gone down
251 * @param firstTime true if link has come up for the first time i.e a link
252 * not seen-before
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800253 */
Saurav Dasc88d4662017-05-15 15:34:25 -0700254 public void retryHash(Link link, boolean linkDown, boolean firstTime) {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700255 MacAddress neighborMac;
Charles Chan0b4e6182015-11-03 10:42:14 -0800256 try {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700257 neighborMac = deviceConfig.getDeviceMac(link.dst().deviceId());
Charles Chan0b4e6182015-11-03 10:42:14 -0800258 } catch (DeviceConfigNotFoundException e) {
Saurav Dasc88d4662017-05-15 15:34:25 -0700259 log.warn(e.getMessage() + " Aborting retryHash.");
Charles Chan0b4e6182015-11-03 10:42:14 -0800260 return;
261 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700262 // find all the destinationSets related to link
263 Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet()
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700264 .stream()
Saurav Das7bcbe702017-06-13 15:35:54 -0700265 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Pier Luigi63edd932018-01-14 21:56:11 +0100266 // Filter out PW transit groups or include them if MPLS ECMP is supported
Saurav Dasa4020382018-02-14 14:14:54 -0800267 .filter(entry -> !entry.getKey().destinationSet().notBos() ||
268 (entry.getKey().destinationSet().notBos() && srManager.getMplsEcmp()))
269 // Filter out simple SWAP groups or include them if MPLS ECMP is supported
270 .filter(entry -> !entry.getKey().destinationSet().swap() ||
271 (entry.getKey().destinationSet().swap() && srManager.getMplsEcmp()))
Saurav Das7bcbe702017-06-13 15:35:54 -0700272 .filter(entry -> entry.getValue().containsNextHop(link.dst().deviceId()))
273 .map(entry -> entry.getKey())
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700274 .collect(Collectors.toSet());
Saurav Dasc88d4662017-05-15 15:34:25 -0700275
Saurav Das7bcbe702017-06-13 15:35:54 -0700276 log.debug("retryHash: dsNextObjStore contents for linkSrc {} -> linkDst {}: {}",
277 deviceId, link.dst().deviceId(), dsKeySet);
278
279 for (DestinationSetNextObjectiveStoreKey dsKey : dsKeySet) {
280 NextNeighbors nextHops = dsNextObjStore.get(dsKey);
281 if (nextHops == null) {
Saurav Dasc88d4662017-05-15 15:34:25 -0700282 log.warn("retryHash in device {}, but global store has no record "
Saurav Das7bcbe702017-06-13 15:35:54 -0700283 + "for dsKey:{}", deviceId, dsKey);
Saurav Dasc88d4662017-05-15 15:34:25 -0700284 continue;
285 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700286 int nextId = nextHops.nextId();
287 Set<DeviceId> dstSet = nextHops.getDstForNextHop(link.dst().deviceId());
Saurav Dasc88d4662017-05-15 15:34:25 -0700288 if (!linkDown) {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700289 List<PortLabel> pl = Lists.newArrayList();
Saurav Dasc88d4662017-05-15 15:34:25 -0700290 if (firstTime) {
291 // some links may have come up before the next-objective was created
292 // we take this opportunity to ensure other ports to same next-hop-dst
293 // are part of the hash group (see CORD-1180). Duplicate additions
294 // to the same hash group are avoided by the driver.
295 for (PortNumber p : devicePortMap.get(link.dst().deviceId())) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700296 dstSet.forEach(dst -> {
297 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
Saurav Das9df5b7c2017-08-14 16:44:43 -0700298 pl.add(new PortLabel(p, edgeLabel));
Saurav Das7bcbe702017-06-13 15:35:54 -0700299 });
Saurav Dasc88d4662017-05-15 15:34:25 -0700300 }
Saurav Das9df5b7c2017-08-14 16:44:43 -0700301 addToHashedNextObjective(pl, neighborMac, nextId);
302 } else {
303 // handle only the port that came up
304 dstSet.forEach(dst -> {
305 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
306 pl.add(new PortLabel(link.src().port(), edgeLabel));
307 });
308 addToHashedNextObjective(pl, neighborMac, nextId);
Saurav Dasc88d4662017-05-15 15:34:25 -0700309 }
310 } else {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700311 // linkdown
312 List<PortLabel> pl = Lists.newArrayList();
Saurav Das7bcbe702017-06-13 15:35:54 -0700313 dstSet.forEach(dst -> {
314 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
Saurav Das9df5b7c2017-08-14 16:44:43 -0700315 pl.add(new PortLabel(link.src().port(), edgeLabel));
Saurav Das7bcbe702017-06-13 15:35:54 -0700316 });
Saurav Das9df5b7c2017-08-14 16:44:43 -0700317 removeFromHashedNextObjective(pl, neighborMac, nextId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700318 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800319 }
Saurav Das1547b3f2017-05-05 17:01:08 -0700320 }
321
Saurav Dasc88d4662017-05-15 15:34:25 -0700322 /**
Saurav Das9df5b7c2017-08-14 16:44:43 -0700323 * Utility class for associating output ports and the corresponding MPLS
324 * labels to push. In dual-homing, there are different labels to push
325 * corresponding to the destination switches in an edge-pair. If both
326 * destinations are reachable via the same spine, then the output-port to
327 * the spine will be associated with two labels i.e. there will be two
328 * PortLabel objects for the same port but with different labels.
329 */
330 private class PortLabel {
331 PortNumber port;
332 int edgeLabel;
333
334 PortLabel(PortNumber port, int edgeLabel) {
335 this.port = port;
336 this.edgeLabel = edgeLabel;
337 }
338
339 @Override
340 public String toString() {
341 return port.toString() + "/" + String.valueOf(edgeLabel);
342 }
343 }
344
345 /**
346 * Makes a call to the FlowObjective service to add buckets to
347 * a hashed group. User must ensure that all the ports & labels are meant
348 * same neighbor (ie. dstMac).
Saurav Dasc88d4662017-05-15 15:34:25 -0700349 *
Pier Luigi63edd932018-01-14 21:56:11 +0100350 * @param portLabels a collection of port & label combinations to add
Saurav Das9df5b7c2017-08-14 16:44:43 -0700351 * to the hash group identified by the nextId
Saurav Dasc88d4662017-05-15 15:34:25 -0700352 * @param dstMac destination mac address of next-hop
Saurav Das9df5b7c2017-08-14 16:44:43 -0700353 * @param nextId id for next-objective to which buckets will be added
Saurav Dasceccf242017-08-03 18:30:35 -0700354 *
Saurav Dasc88d4662017-05-15 15:34:25 -0700355 */
Saurav Das9df5b7c2017-08-14 16:44:43 -0700356 private void addToHashedNextObjective(Collection<PortLabel> portLabels,
357 MacAddress dstMac, Integer nextId) {
Saurav Das1547b3f2017-05-05 17:01:08 -0700358 // setup metadata to pass to nextObjective - indicate the vlan on egress
359 // if needed by the switch pipeline. Since hashed next-hops are always to
360 // other neighboring routers, there is no subnet assigned on those ports.
361 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
362 metabuilder.matchVlanId(INTERNAL_VLAN);
Saurav Das1547b3f2017-05-05 17:01:08 -0700363 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
364 .withId(nextId)
365 .withType(NextObjective.Type.HASHED)
Saurav Das1547b3f2017-05-05 17:01:08 -0700366 .withMeta(metabuilder.build())
367 .fromApp(appId);
Saurav Das9df5b7c2017-08-14 16:44:43 -0700368 // Create the new buckets to be updated
369 portLabels.forEach(pl -> {
370 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
371 tBuilder.setOutput(pl.port)
372 .setEthDst(dstMac)
373 .setEthSrc(nodeMacAddr);
374 if (pl.edgeLabel != DestinationSet.NO_EDGE_LABEL) {
375 tBuilder.pushMpls()
376 .copyTtlOut()
377 .setMpls(MplsLabel.mplsLabel(pl.edgeLabel));
378 }
379 nextObjBuilder.addTreatment(tBuilder.build());
380 });
381
382 log.debug("addToHash in device {}: Adding Bucket with port/label {} "
383 + "to nextId {}", deviceId, portLabels, nextId);
Saurav Das1547b3f2017-05-05 17:01:08 -0700384
385 ObjectiveContext context = new DefaultObjectiveContext(
Saurav Das9df5b7c2017-08-14 16:44:43 -0700386 (objective) -> log.debug("addToHash port/label {} addedTo "
387 + "NextObj {} on {}", portLabels, nextId, deviceId),
Saurav Das1547b3f2017-05-05 17:01:08 -0700388 (objective, error) ->
Saurav Das9df5b7c2017-08-14 16:44:43 -0700389 log.warn("addToHash failed to add port/label {} to"
390 + " NextObj {} on {}: {}", portLabels,
Saurav Dasc88d4662017-05-15 15:34:25 -0700391 nextId, deviceId, error));
Saurav Das1547b3f2017-05-05 17:01:08 -0700392 NextObjective nextObjective = nextObjBuilder.addToExisting(context);
393 flowObjectiveService.next(deviceId, nextObjective);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800394 }
395
396 /**
Saurav Das9df5b7c2017-08-14 16:44:43 -0700397 * Makes a call to the FlowObjective service to remove buckets from
398 * a hash group. User must ensure that all the ports & labels are meant
399 * same neighbor (ie. dstMac).
Saurav Dasceccf242017-08-03 18:30:35 -0700400 *
Pier Luigi63edd932018-01-14 21:56:11 +0100401 * @param portLabels a collection of port & label combinations to remove
Saurav Das9df5b7c2017-08-14 16:44:43 -0700402 * from the hash group identified by the nextId
Saurav Dasceccf242017-08-03 18:30:35 -0700403 * @param dstMac destination mac address of next-hop
Saurav Das9df5b7c2017-08-14 16:44:43 -0700404 * @param nextId id for next-objective from which buckets will be removed
Saurav Dasceccf242017-08-03 18:30:35 -0700405 */
Saurav Das9df5b7c2017-08-14 16:44:43 -0700406 private void removeFromHashedNextObjective(Collection<PortLabel> portLabels,
407 MacAddress dstMac, Integer nextId) {
Saurav Dasceccf242017-08-03 18:30:35 -0700408 NextObjective.Builder nextObjBuilder = DefaultNextObjective
409 .builder()
410 .withType(NextObjective.Type.HASHED) //same as original
411 .withId(nextId)
Saurav Das9df5b7c2017-08-14 16:44:43 -0700412 .fromApp(appId);
413 // Create the buckets to be removed
414 portLabels.forEach(pl -> {
415 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
416 tBuilder.setOutput(pl.port)
417 .setEthDst(dstMac)
418 .setEthSrc(nodeMacAddr);
419 if (pl.edgeLabel != DestinationSet.NO_EDGE_LABEL) {
420 tBuilder.pushMpls()
421 .copyTtlOut()
422 .setMpls(MplsLabel.mplsLabel(pl.edgeLabel));
423 }
424 nextObjBuilder.addTreatment(tBuilder.build());
425 });
426 log.debug("removeFromHash in device {}: Removing Bucket with port/label"
427 + " {} from nextId {}", deviceId, portLabels, nextId);
Saurav Dasc88d4662017-05-15 15:34:25 -0700428
Saurav Das9df5b7c2017-08-14 16:44:43 -0700429 ObjectiveContext context = new DefaultObjectiveContext(
430 (objective) -> log.debug("port/label {} removedFrom NextObj"
431 + " {} on {}", portLabels, nextId, deviceId),
432 (objective, error) ->
433 log.warn("port/label {} failed to removeFrom NextObj {} on "
434 + "{}: {}", portLabels, nextId, deviceId, error));
435 NextObjective nextObjective = nextObjBuilder.removeFromExisting(context);
Saurav Dasceccf242017-08-03 18:30:35 -0700436 flowObjectiveService.next(deviceId, nextObjective);
437 }
Saurav Dasc88d4662017-05-15 15:34:25 -0700438
439 /**
440 * Checks all the hash-groups in the target-switch meant for the destination
441 * switch, and either adds or removes buckets to make the neighbor-set
442 * match the given next-hops. Typically called by the master instance of the
443 * destination switch, which may be different from the master instance of the
444 * target switch where hash-group changes are made.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800445 *
Saurav Dasc88d4662017-05-15 15:34:25 -0700446 * @param targetSw the switch in which the hash groups will be edited
447 * @param nextHops the current next hops for the target switch to reach
448 * the dest sw
449 * @param destSw the destination switch
450 * @param revoke true if hash groups need to remove buckets from the
451 * the groups to match the current next hops
452 * @return true if calls are made to edit buckets, or if no edits are required
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800453 */
Saurav Dasc88d4662017-05-15 15:34:25 -0700454 public boolean fixHashGroups(DeviceId targetSw, Set<DeviceId> nextHops,
455 DeviceId destSw, boolean revoke) {
456 // temporary storage of keys to be updated
Saurav Das7bcbe702017-06-13 15:35:54 -0700457 Map<DestinationSetNextObjectiveStoreKey, Set<DeviceId>> tempStore =
Saurav Dasc88d4662017-05-15 15:34:25 -0700458 new HashMap<>();
Saurav Das9df5b7c2017-08-14 16:44:43 -0700459 boolean foundNextObjective = false, success = true;
Charles Chan0b4e6182015-11-03 10:42:14 -0800460
Saurav Das7bcbe702017-06-13 15:35:54 -0700461 // retrieve hash-groups meant for destSw, which have destinationSets
Saurav Dasc88d4662017-05-15 15:34:25 -0700462 // with different neighbors than the given next-hops
Saurav Das7bcbe702017-06-13 15:35:54 -0700463 for (DestinationSetNextObjectiveStoreKey dskey : dsNextObjStore.keySet()) {
464 if (!dskey.deviceId().equals(targetSw) ||
465 !dskey.destinationSet().getDestinationSwitches().contains(destSw)) {
Saurav Dasc88d4662017-05-15 15:34:25 -0700466 continue;
467 }
468 foundNextObjective = true;
Saurav Das7bcbe702017-06-13 15:35:54 -0700469 NextNeighbors nhops = dsNextObjStore.get(dskey);
470 Set<DeviceId> currNeighbors = nhops.nextHops(destSw);
471 int edgeLabel = dskey.destinationSet().getEdgeLabel(destSw);
472 Integer nextId = nhops.nextId();
Charles Chan0b4e6182015-11-03 10:42:14 -0800473
Saurav Dasa4020382018-02-14 14:14:54 -0800474 // some store elements may not be hashed next-objectives - ignore them
475 if (isSimpleNextObjective(dskey)) {
Ray Milkey1c5467b2018-03-06 09:26:34 -0800476 Set<DeviceId> displayNextHops = nextHops == null ? ImmutableSet.of() : nextHops;
Saurav Dasa4020382018-02-14 14:14:54 -0800477 log.debug("Ignoring {} of SIMPLE nextObj for targetSw:{}"
478 + " -> dstSw:{} with current nextHops:{} to new"
479 + " nextHops: {} in nextId:{}",
480 (revoke) ? "removal" : "addition", targetSw, destSw,
481 currNeighbors, nextHops, nextId);
Ray Milkey1c5467b2018-03-06 09:26:34 -0800482 if ((revoke && !displayNextHops.isEmpty())
483 || (!revoke && !displayNextHops.equals(currNeighbors))) {
Saurav Dasa4020382018-02-14 14:14:54 -0800484 log.warn("Simple next objective cannot be edited to "
485 + "move from {} to {}", currNeighbors, nextHops);
486 }
487 continue;
488 }
489
Saurav Dasb805f1a2017-12-13 16:19:35 -0800490 if (currNeighbors == null || nextHops == null) {
491 log.warn("fixing hash groups but found currNeighbors:{} or nextHops:{}"
492 + " in targetSw:{} for dstSw:{}", currNeighbors, nextHops,
493 targetSw, destSw);
494 success &= false;
495 continue;
496 }
497
Saurav Dasc88d4662017-05-15 15:34:25 -0700498 Set<DeviceId> diff;
499 if (revoke) {
500 diff = Sets.difference(currNeighbors, nextHops);
501 log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next "
502 + "hops:{} ..removing {}", targetSw, destSw, nextId,
503 currNeighbors, diff);
504 } else {
505 diff = Sets.difference(nextHops, currNeighbors);
506 log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next "
507 + "hops:{} ..adding {}", targetSw, destSw, nextId,
508 currNeighbors, diff);
509 }
Saurav Das9df5b7c2017-08-14 16:44:43 -0700510 boolean suc = updateAllPortsToNextHop(diff, edgeLabel, nextId,
511 revoke);
512 if (suc) {
513 // to update neighbor set with changes made
Saurav Dasc88d4662017-05-15 15:34:25 -0700514 if (revoke) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700515 tempStore.put(dskey, Sets.difference(currNeighbors, diff));
Saurav Dasc88d4662017-05-15 15:34:25 -0700516 } else {
Saurav Das7bcbe702017-06-13 15:35:54 -0700517 tempStore.put(dskey, Sets.union(currNeighbors, diff));
Saurav Dasc88d4662017-05-15 15:34:25 -0700518 }
sangho834e4b02015-05-01 09:38:25 -0700519 }
Saurav Das9df5b7c2017-08-14 16:44:43 -0700520 success &= suc;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800521 }
522
Saurav Dasc88d4662017-05-15 15:34:25 -0700523 if (!foundNextObjective) {
524 log.debug("Cannot find any nextObjectives for route targetSw:{} "
525 + "-> dstSw:{}", targetSw, destSw);
526 return true; // nothing to do, return true so ECMPspg is updated
527 }
528
Saurav Das7bcbe702017-06-13 15:35:54 -0700529 // update the dsNextObjectiveStore with new destinationSet to nextId mappings
530 for (DestinationSetNextObjectiveStoreKey key : tempStore.keySet()) {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700531 NextNeighbors currentNextHops = dsNextObjStore.get(key);
532 if (currentNextHops == null) {
533 log.warn("fixHashGroups could not update global store in "
534 + "device {} .. missing nextNeighbors for key {}",
535 deviceId, key);
Saurav Dasc88d4662017-05-15 15:34:25 -0700536 continue;
537 }
Saurav Das9df5b7c2017-08-14 16:44:43 -0700538 Set<DeviceId> newNeighbors = new HashSet<>();
539 newNeighbors.addAll(tempStore.get(key));
540 Map<DeviceId, Set<DeviceId>> oldDstNextHops =
541 ImmutableMap.copyOf(currentNextHops.dstNextHops());
542 currentNextHops.dstNextHops().put(destSw, newNeighbors); //local change
543 log.debug("Updating nsNextObjStore target:{} -> dst:{} in key:{} nextId:{}",
544 targetSw, destSw, key, currentNextHops.nextId());
545 log.debug("Old dstNextHops: {}", oldDstNextHops);
546 log.debug("New dstNextHops: {}", currentNextHops.dstNextHops());
547 // update global store
548 dsNextObjStore.put(key,
549 new NextNeighbors(currentNextHops.dstNextHops(),
550 currentNextHops.nextId()));
Saurav Dasc88d4662017-05-15 15:34:25 -0700551 }
Saurav Dasa4020382018-02-14 14:14:54 -0800552
Saurav Das9df5b7c2017-08-14 16:44:43 -0700553 // even if one fails and others succeed, return false so ECMPspg not updated
554 return success;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800555 }
556
Saurav Dasceccf242017-08-03 18:30:35 -0700557 /**
558 * Updates the DestinationSetNextObjectiveStore with any per-destination nexthops
559 * that are not already in the store for the given DestinationSet. Note that
560 * this method does not remove existing next hops for the destinations in the
561 * DestinationSet.
562 *
563 * @param ds the DestinationSet for which the next hops need to be updated
564 * @param newDstNextHops a map of per-destination next hops to update the
565 * destinationSet with
566 * @return true if successful in updating all next hops
567 */
568 private boolean updateNextHops(DestinationSet ds,
Saurav Das7bcbe702017-06-13 15:35:54 -0700569 Map<DeviceId, Set<DeviceId>> newDstNextHops) {
570 DestinationSetNextObjectiveStoreKey key =
571 new DestinationSetNextObjectiveStoreKey(deviceId, ds);
572 NextNeighbors currNext = dsNextObjStore.get(key);
573 Map<DeviceId, Set<DeviceId>> currDstNextHops = currNext.dstNextHops();
574
575 // add newDstNextHops to currDstNextHops for each dst
576 boolean success = true;
577 for (DeviceId dstSw : ds.getDestinationSwitches()) {
578 Set<DeviceId> currNhops = currDstNextHops.get(dstSw);
579 Set<DeviceId> newNhops = newDstNextHops.get(dstSw);
580 currNhops = (currNhops == null) ? Sets.newHashSet() : currNhops;
581 newNhops = (newNhops == null) ? Sets.newHashSet() : newNhops;
582 int edgeLabel = ds.getEdgeLabel(dstSw);
583 int nextId = currNext.nextId();
584
585 // new next hops should be added
586 boolean suc = updateAllPortsToNextHop(Sets.difference(newNhops, currNhops),
587 edgeLabel, nextId, false);
588 if (suc) {
589 currNhops.addAll(newNhops);
590 currDstNextHops.put(dstSw, currNhops); // this is only a local change
591 }
592 success &= suc;
593 }
594
595 if (success) {
596 // update global store
597 dsNextObjStore.put(key, new NextNeighbors(currDstNextHops,
598 currNext.nextId()));
599 log.debug("Updated device:{} ds:{} new next-hops: {}", deviceId, ds,
600 dsNextObjStore.get(key));
601 }
602 return success;
603 }
604
Saurav Dasceccf242017-08-03 18:30:35 -0700605 /**
Saurav Das9df5b7c2017-08-14 16:44:43 -0700606 * Adds or removes buckets for all ports to a set of neighbor devices. Caller
607 * needs to ensure that the given neighbors are all next hops towards the
608 * same destination (represented by the given edgeLabel).
Saurav Dasceccf242017-08-03 18:30:35 -0700609 *
610 * @param neighbors set of neighbor device ids
611 * @param edgeLabel MPLS label to use in buckets
612 * @param nextId the nextObjective to change
613 * @param revoke true if buckets need to be removed, false if they need to
614 * be added
615 * @return true if successful in adding or removing buckets for all ports
616 * to the neighbors
617 */
618 private boolean updateAllPortsToNextHop(Set<DeviceId> neighbors, int edgeLabel,
Saurav Das7bcbe702017-06-13 15:35:54 -0700619 int nextId, boolean revoke) {
Saurav Dasceccf242017-08-03 18:30:35 -0700620 for (DeviceId neighbor : neighbors) {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700621 MacAddress neighborMac;
Saurav Das7bcbe702017-06-13 15:35:54 -0700622 try {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700623 neighborMac = deviceConfig.getDeviceMac(neighbor);
Saurav Das7bcbe702017-06-13 15:35:54 -0700624 } catch (DeviceConfigNotFoundException e) {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700625 log.warn(e.getMessage() + " Aborting updateAllPortsToNextHop"
626 + " for nextId:" + nextId);
Saurav Das7bcbe702017-06-13 15:35:54 -0700627 return false;
628 }
Saurav Das9df5b7c2017-08-14 16:44:43 -0700629 Collection<PortNumber> portsToNeighbor = devicePortMap.get(neighbor);
630 if (portsToNeighbor == null || portsToNeighbor.isEmpty()) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700631 log.warn("No ports found in dev:{} for neighbor:{} .. cannot "
Saurav Das9df5b7c2017-08-14 16:44:43 -0700632 + "updateAllPortsToNextHop for nextId: {}",
Saurav Das7bcbe702017-06-13 15:35:54 -0700633 deviceId, neighbor, nextId);
634 return false;
635 }
Saurav Das9df5b7c2017-08-14 16:44:43 -0700636 List<PortLabel> pl = Lists.newArrayList();
637 portsToNeighbor.forEach(p -> pl.add(new PortLabel(p, edgeLabel)));
Saurav Das7bcbe702017-06-13 15:35:54 -0700638 if (revoke) {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700639 log.debug("updateAllPortsToNextHops in device {}: Removing Bucket(s) "
640 + "with Port/Label:{} to next object id {}",
641 deviceId, pl, nextId);
642 removeFromHashedNextObjective(pl, neighborMac, nextId);
Saurav Das7bcbe702017-06-13 15:35:54 -0700643 } else {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700644 log.debug("fixHashGroup in device {}: Adding Bucket(s) "
645 + "with Port/Label: {} to next object id {}",
646 deviceId, pl, nextId);
647 addToHashedNextObjective(pl, neighborMac, nextId);
Saurav Das7bcbe702017-06-13 15:35:54 -0700648 }
649 }
650 return true;
651 }
652
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800653 /**
Saurav Dasa4020382018-02-14 14:14:54 -0800654 * Returns true if the destination set is meant for swap or multi-labeled
655 * packet transport, and MPLS ECMP is not supported.
656 *
657 * @param dskey the key representing the destination set
658 * @return true if destination set is meant for simple next objectives
659 */
660 boolean isSimpleNextObjective(DestinationSetNextObjectiveStoreKey dskey) {
661 return (dskey.destinationSet().notBos() || dskey.destinationSet().swap())
662 && !srManager.getMplsEcmp();
663 }
664
665 /**
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800666 * Adds or removes a port that has been configured with a vlan to a broadcast group
667 * for bridging. Should only be called by the master instance for this device.
Saurav Das1a129a02016-11-18 15:21:57 -0800668 *
669 * @param port the port on this device that needs to be added/removed to a bcast group
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800670 * @param vlanId the vlan id corresponding to the broadcast domain/group
671 * @param popVlan indicates if packets should be sent out untagged or not out
672 * of the port. If true, indicates an access (untagged) or native vlan
673 * configuration. If false, indicates a trunk (tagged) vlan config.
Saurav Das1a129a02016-11-18 15:21:57 -0800674 * @param portUp true if port is enabled, false if disabled
Saurav Das1a129a02016-11-18 15:21:57 -0800675 */
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800676 public void processEdgePort(PortNumber port, VlanId vlanId,
677 boolean popVlan, boolean portUp) {
Saurav Das1a129a02016-11-18 15:21:57 -0800678 //get the next id for the subnet and edit it.
Charles Chan59cc16d2017-02-02 16:20:42 -0800679 Integer nextId = getVlanNextObjectiveId(vlanId);
Saurav Das1a129a02016-11-18 15:21:57 -0800680 if (nextId == -1) {
681 if (portUp) {
682 log.debug("**Creating flooding group for first port enabled in"
Saurav Dasf14d9ef2017-12-05 15:00:23 -0800683 + " vlan {} on dev {} port {}", vlanId, deviceId, port);
Charles Chan59cc16d2017-02-02 16:20:42 -0800684 createBcastGroupFromVlan(vlanId, Collections.singleton(port));
Saurav Das1a129a02016-11-18 15:21:57 -0800685 } else {
686 log.warn("Could not find flooding group for subnet {} on dev:{} when"
Charles Chan59cc16d2017-02-02 16:20:42 -0800687 + " removing port:{}", vlanId, deviceId, port);
Saurav Das1a129a02016-11-18 15:21:57 -0800688 }
689 return;
690 }
691
692 log.info("**port{} in device {}: {} Bucket with Port {} to"
693 + " next-id {}", (portUp) ? "UP" : "DOWN", deviceId,
694 (portUp) ? "Adding" : "Removing",
695 port, nextId);
696 // Create the bucket to be added or removed
697 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800698 if (popVlan) {
699 tBuilder.popVlan();
700 }
Saurav Das1a129a02016-11-18 15:21:57 -0800701 tBuilder.setOutput(port);
702
Saurav Das1a129a02016-11-18 15:21:57 -0800703 TrafficSelector metadata =
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800704 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
Saurav Das1a129a02016-11-18 15:21:57 -0800705
706 NextObjective.Builder nextObjBuilder = DefaultNextObjective
707 .builder().withId(nextId)
708 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
709 .addTreatment(tBuilder.build())
710 .withMeta(metadata);
711
712 ObjectiveContext context = new DefaultObjectiveContext(
713 (objective) -> log.debug("port {} successfully {} NextObj {} on {}",
714 port, (portUp) ? "addedTo" : "removedFrom",
715 nextId, deviceId),
716 (objective, error) ->
717 log.warn("port {} failed to {} NextObj {} on {}: {}",
718 port, (portUp) ? "addTo" : "removeFrom",
719 nextId, deviceId, error));
720
721 NextObjective nextObj = (portUp) ? nextObjBuilder.addToExisting(context)
722 : nextObjBuilder.removeFromExisting(context);
723 log.debug("edgePort processed: Submited next objective {} in device {}",
724 nextId, deviceId);
725 flowObjectiveService.next(deviceId, nextObj);
726 }
727
728 /**
Saurav Dasa4020382018-02-14 14:14:54 -0800729 * Returns the next objective of type hashed (or simple) associated with the
730 * destination set. In addition, updates the existing next-objective if new
731 * route-paths found have resulted in the addition of new next-hops to a
732 * particular destination. If there is no existing next objective for this
733 * destination set, this method would create a next objective and return the
734 * nextId. Optionally metadata can be passed in for the creation of the next
735 * objective. If the parameter simple is true then a simple next objective
736 * is created instead of a hashed one.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800737 *
Saurav Das7bcbe702017-06-13 15:35:54 -0700738 * @param ds destination set
739 * @param nextHops a map of per destination next hops
Saurav Das8a0732e2015-11-20 15:27:53 -0800740 * @param meta metadata passed into the creation of a Next Objective
Saurav Dasa4020382018-02-14 14:14:54 -0800741 * @param simple if true, a simple next objective will be created instead of
742 * a hashed next objective
Saurav Das8a0732e2015-11-20 15:27:53 -0800743 * @return int if found or -1 if there are errors in the creation of the
Saurav Dasa4020382018-02-14 14:14:54 -0800744 * neighbor set.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800745 */
Saurav Das7bcbe702017-06-13 15:35:54 -0700746 public int getNextObjectiveId(DestinationSet ds,
747 Map<DeviceId, Set<DeviceId>> nextHops,
Saurav Dasa4020382018-02-14 14:14:54 -0800748 TrafficSelector meta, boolean simple) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700749 NextNeighbors next = dsNextObjStore.
750 get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
751 if (next == null) {
752 log.debug("getNextObjectiveId in device{}: Next objective id "
753 + "not found for {} ... creating", deviceId, ds);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700754 log.trace("getNextObjectiveId: nsNextObjStore contents for device {}: {}",
755 deviceId,
Saurav Das7bcbe702017-06-13 15:35:54 -0700756 dsNextObjStore.entrySet()
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700757 .stream()
758 .filter((nsStoreEntry) ->
759 (nsStoreEntry.getKey().deviceId().equals(deviceId)))
760 .collect(Collectors.toList()));
Saurav Das7bcbe702017-06-13 15:35:54 -0700761
Saurav Dasa4020382018-02-14 14:14:54 -0800762 createGroupFromDestinationSet(ds, nextHops, meta, simple);
Saurav Das7bcbe702017-06-13 15:35:54 -0700763 next = dsNextObjStore.
764 get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
765 if (next == null) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700766 log.warn("getNextObjectiveId: unable to create next objective");
Saurav Das7bcbe702017-06-13 15:35:54 -0700767 // failure in creating group
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700768 return -1;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700769 } else {
770 log.debug("getNextObjectiveId in device{}: Next objective id {} "
Saurav Das7bcbe702017-06-13 15:35:54 -0700771 + "created for {}", deviceId, next.nextId(), ds);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700772 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700773 } else {
774 log.trace("getNextObjectiveId in device{}: Next objective id {} "
Saurav Das7bcbe702017-06-13 15:35:54 -0700775 + "found for {}", deviceId, next.nextId(), ds);
776 // should fix hash groups too if next-hops have changed
777 if (!next.dstNextHops().equals(nextHops)) {
778 log.debug("Nexthops have changed for dev:{} nextId:{} ..updating",
779 deviceId, next.nextId());
780 if (!updateNextHops(ds, nextHops)) {
781 // failure in updating group
782 return -1;
783 }
784 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700785 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700786 return next.nextId();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800787 }
788
sangho0b2b6d12015-05-20 22:16:38 -0700789 /**
Charles Chan59cc16d2017-02-02 16:20:42 -0800790 * Returns the next objective of type broadcast associated with the vlan,
Saurav Das4ce45962015-11-24 23:21:05 -0800791 * or -1 if no such objective exists. Note that this method does NOT create
792 * the next objective as a side-effect. It is expected that is objective is
Saurav Das1a129a02016-11-18 15:21:57 -0800793 * created at startup from network configuration. Typically this is used
794 * for L2 flooding within the subnet configured on the switch.
Charles Chanc42e84e2015-10-20 16:24:19 -0700795 *
Charles Chan59cc16d2017-02-02 16:20:42 -0800796 * @param vlanId vlan id
Charles Chanc42e84e2015-10-20 16:24:19 -0700797 * @return int if found or -1
798 */
Charles Chan59cc16d2017-02-02 16:20:42 -0800799 public int getVlanNextObjectiveId(VlanId vlanId) {
800 Integer nextId = vlanNextObjStore.
801 get(new VlanNextObjectiveStoreKey(deviceId, vlanId));
Charles Chan9f676b62015-10-29 14:58:10 -0700802
803 return (nextId != null) ? nextId : -1;
Charles Chanc42e84e2015-10-20 16:24:19 -0700804 }
805
806 /**
Saurav Das4ce45962015-11-24 23:21:05 -0800807 * Returns the next objective of type simple associated with the port on the
808 * device, given the treatment. Different treatments to the same port result
809 * in different next objectives. If no such objective exists, this method
Saurav Das961beb22017-03-29 19:09:17 -0700810 * creates one (if requested) and returns the id. Optionally metadata can be passed in for
Saurav Das1a129a02016-11-18 15:21:57 -0800811 * the creation of the objective. Typically this is used for L2 and L3 forwarding
812 * to compute nodes and containers/VMs on the compute nodes directly attached
813 * to the switch.
Saurav Das4ce45962015-11-24 23:21:05 -0800814 *
815 * @param portNum the port number for the simple next objective
816 * @param treatment the actions to apply on the packets (should include outport)
817 * @param meta optional metadata passed into the creation of the next objective
Saurav Das961beb22017-03-29 19:09:17 -0700818 * @param createIfMissing true if a next object should be created if not found
Saurav Das4ce45962015-11-24 23:21:05 -0800819 * @return int if found or created, -1 if there are errors during the
820 * creation of the next objective.
821 */
822 public int getPortNextObjectiveId(PortNumber portNum, TrafficTreatment treatment,
Saurav Das961beb22017-03-29 19:09:17 -0700823 TrafficSelector meta, boolean createIfMissing) {
Charles Chane849c192016-01-11 18:28:54 -0800824 Integer nextId = portNextObjStore
Saurav Das76ae6812017-03-15 15:15:14 -0700825 .get(new PortNextObjectiveStoreKey(deviceId, portNum, treatment, meta));
Saurav Das961beb22017-03-29 19:09:17 -0700826 if (nextId != null) {
827 return nextId;
828 }
829 log.debug("getPortNextObjectiveId in device {}: Next objective id "
830 + "not found for port: {} .. {}", deviceId, portNum,
831 (createIfMissing) ? "creating" : "aborting");
832 if (!createIfMissing) {
833 return -1;
834 }
835 // create missing next objective
836 createGroupFromPort(portNum, treatment, meta);
837 nextId = portNextObjStore.get(new PortNextObjectiveStoreKey(deviceId, portNum,
838 treatment, meta));
Saurav Das4ce45962015-11-24 23:21:05 -0800839 if (nextId == null) {
Saurav Das961beb22017-03-29 19:09:17 -0700840 log.warn("getPortNextObjectiveId: unable to create next obj"
841 + "for dev:{} port:{}", deviceId, portNum);
842 return -1;
Charles Chane849c192016-01-11 18:28:54 -0800843 }
844 return nextId;
845 }
846
847 /**
sangho0b2b6d12015-05-20 22:16:38 -0700848 * Checks if the next objective ID (group) for the neighbor set exists or not.
849 *
850 * @param ns neighbor set to check
851 * @return true if it exists, false otherwise
852 */
Saurav Das7bcbe702017-06-13 15:35:54 -0700853 public boolean hasNextObjectiveId(DestinationSet ns) {
854 NextNeighbors nextHops = dsNextObjStore.
855 get(new DestinationSetNextObjectiveStoreKey(deviceId, ns));
856 if (nextHops == null) {
sangho0b2b6d12015-05-20 22:16:38 -0700857 return false;
858 }
859
860 return true;
861 }
862
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800863 private void populateNeighborMaps() {
864 Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700865 for (Link link : outgoingLinks) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800866 if (link.type() != Link.Type.DIRECT) {
867 continue;
868 }
869 addNeighborAtPort(link.dst().deviceId(), link.src().port());
870 }
871 }
872
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700873 protected void addNeighborAtPort(DeviceId neighborId,
874 PortNumber portToNeighbor) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800875 // Update DeviceToPort database
876 log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
877 deviceId, neighborId, portToNeighbor);
Saurav Das8a0732e2015-11-20 15:27:53 -0800878 Set<PortNumber> ports = Collections
879 .newSetFromMap(new ConcurrentHashMap<PortNumber, Boolean>());
880 ports.add(portToNeighbor);
881 Set<PortNumber> portnums = devicePortMap.putIfAbsent(neighborId, ports);
882 if (portnums != null) {
883 portnums.add(portToNeighbor);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800884 }
885
886 // Update portToDevice database
Saurav Dasa4020382018-02-14 14:14:54 -0800887 // should always update as neighbor could have changed on this port
888 DeviceId prev = portDeviceMap.put(portToNeighbor, neighborId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800889 if (prev != null) {
Saurav Dasa4020382018-02-14 14:14:54 -0800890 log.debug("Device/port: {}/{} previous neighbor: {}, current neighbor: {} ",
Saurav Dasc88d4662017-05-15 15:34:25 -0700891 deviceId, portToNeighbor, prev, neighborId);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800892 }
893 }
894
sangho1e575652015-05-14 00:39:53 -0700895 /**
Saurav Das7bcbe702017-06-13 15:35:54 -0700896 * Creates a NextObjective for a hash group in this device from a given
Saurav Dasa4020382018-02-14 14:14:54 -0800897 * DestinationSet. If the parameter simple is true, a simple next objective
898 * is created instead.
sangho1e575652015-05-14 00:39:53 -0700899 *
Saurav Das7bcbe702017-06-13 15:35:54 -0700900 * @param ds the DestinationSet
901 * @param neighbors a map for each destination and its next-hops
Saurav Das8a0732e2015-11-20 15:27:53 -0800902 * @param meta metadata passed into the creation of a Next Objective
Saurav Dasa4020382018-02-14 14:14:54 -0800903 * @param simple if true, a simple next objective will be created instead of
904 * a hashed next objective
sangho1e575652015-05-14 00:39:53 -0700905 */
Saurav Das7bcbe702017-06-13 15:35:54 -0700906 public void createGroupFromDestinationSet(DestinationSet ds,
907 Map<DeviceId, Set<DeviceId>> neighbors,
908 TrafficSelector meta,
Saurav Dasa4020382018-02-14 14:14:54 -0800909 boolean simple) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700910 int nextId = flowObjectiveService.allocateNextId();
Saurav Dasa4020382018-02-14 14:14:54 -0800911 NextObjective.Type type = (simple) ? NextObjective.Type.SIMPLE
912 : NextObjective.Type.HASHED;
Saurav Das7bcbe702017-06-13 15:35:54 -0700913 if (neighbors == null || neighbors.isEmpty()) {
914 log.warn("createGroupsFromDestinationSet: needs at least one neighbor"
915 + "to create group in dev:{} for ds: {} with next-hops {}",
916 deviceId, ds, neighbors);
917 return;
918 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700919
920 NextObjective.Builder nextObjBuilder = DefaultNextObjective
921 .builder()
922 .withId(nextId)
923 .withType(type)
924 .fromApp(appId);
925 if (meta != null) {
926 nextObjBuilder.withMeta(meta);
927 }
928
929 // create treatment buckets for each neighbor for each dst Device
930 // except in the special case where we only want to pick a single
Saurav Dasa4020382018-02-14 14:14:54 -0800931 // neighbor/port for a simple nextObj
Saurav Das7bcbe702017-06-13 15:35:54 -0700932 boolean foundSingleNeighbor = false;
933 boolean treatmentAdded = false;
934 Map<DeviceId, Set<DeviceId>> dstNextHops = new ConcurrentHashMap<>();
935 for (DeviceId dst : ds.getDestinationSwitches()) {
936 Set<DeviceId> nextHops = neighbors.get(dst);
937 if (nextHops == null || nextHops.isEmpty()) {
938 continue;
Pier Ventre917127a2016-10-31 16:49:19 -0700939 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700940
941 if (foundSingleNeighbor) {
942 break;
943 }
944
945 for (DeviceId neighborId : nextHops) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800946 if (devicePortMap.get(neighborId) == null) {
947 log.warn("Neighbor {} is not in the port map yet for dev:{}",
948 neighborId, deviceId);
sangho834e4b02015-05-01 09:38:25 -0700949 return;
Jon Hallcbd1b392017-01-18 20:15:44 -0800950 } else if (devicePortMap.get(neighborId).isEmpty()) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700951 log.warn("There are no ports for "
Saurav Das8a0732e2015-11-20 15:27:53 -0800952 + "the Device {} in the port map yet", neighborId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700953 return;
sangho834e4b02015-05-01 09:38:25 -0700954 }
955
Saurav Das8a0732e2015-11-20 15:27:53 -0800956 MacAddress neighborMac;
Charles Chan0b4e6182015-11-03 10:42:14 -0800957 try {
Saurav Das8a0732e2015-11-20 15:27:53 -0800958 neighborMac = deviceConfig.getDeviceMac(neighborId);
Charles Chan0b4e6182015-11-03 10:42:14 -0800959 } catch (DeviceConfigNotFoundException e) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700960 log.warn(e.getMessage() + " Aborting createGroupsFromDestinationset.");
Charles Chan0b4e6182015-11-03 10:42:14 -0800961 return;
962 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700963 // For each port to the neighbor, we create a new treatment
Pier Ventre917127a2016-10-31 16:49:19 -0700964 Set<PortNumber> neighborPorts = devicePortMap.get(neighborId);
Saurav Dasa4020382018-02-14 14:14:54 -0800965 // In this case we need a SIMPLE nextObj. We randomly pick a port
966 if (simple) {
Pier Ventre917127a2016-10-31 16:49:19 -0700967 int size = devicePortMap.get(neighborId).size();
968 int index = RandomUtils.nextInt(0, size);
969 neighborPorts = Collections.singleton(
Saurav Das7bcbe702017-06-13 15:35:54 -0700970 Iterables.get(devicePortMap.get(neighborId),
971 index));
972 foundSingleNeighbor = true;
Pier Ventre917127a2016-10-31 16:49:19 -0700973 }
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700974
Pier Ventre917127a2016-10-31 16:49:19 -0700975 for (PortNumber sp : neighborPorts) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700976 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
977 .builder();
Saurav Das7bcbe702017-06-13 15:35:54 -0700978 tBuilder.setEthDst(neighborMac).setEthSrc(nodeMacAddr);
979 int edgeLabel = ds.getEdgeLabel(dst);
980 if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
Saurav Dasa4020382018-02-14 14:14:54 -0800981 if (simple) {
982 // swap label case
983 tBuilder.setMpls(MplsLabel.mplsLabel(edgeLabel));
984 } else {
985 // ecmp with label push case
986 tBuilder.pushMpls().copyTtlOut()
987 .setMpls(MplsLabel.mplsLabel(edgeLabel));
988 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700989 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800990 tBuilder.setOutput(sp);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700991 nextObjBuilder.addTreatment(tBuilder.build());
Saurav Das7bcbe702017-06-13 15:35:54 -0700992 treatmentAdded = true;
993 //update store
994 Set<DeviceId> existingNeighbors = dstNextHops.get(dst);
995 if (existingNeighbors == null) {
996 existingNeighbors = new HashSet<>();
997 }
998 existingNeighbors.add(neighborId);
999 dstNextHops.put(dst, existingNeighbors);
1000 log.debug("creating treatment for port/label {}/{} in next:{}",
1001 sp, edgeLabel, nextId);
1002 }
1003
1004 if (foundSingleNeighbor) {
1005 break;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -08001006 }
1007 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -08001008 }
Saurav Das7bcbe702017-06-13 15:35:54 -07001009
1010 if (!treatmentAdded) {
1011 log.warn("Could not createGroup from DestinationSet {} without any"
1012 + "next hops {}", ds, neighbors);
1013 return;
1014 }
1015 ObjectiveContext context = new DefaultObjectiveContext(
1016 (objective) ->
1017 log.debug("createGroupsFromDestinationSet installed "
1018 + "NextObj {} on {}", nextId, deviceId),
1019 (objective, error) ->
1020 log.warn("createGroupsFromDestinationSet failed to install"
1021 + " NextObj {} on {}: {}", nextId, deviceId, error)
1022 );
1023 NextObjective nextObj = nextObjBuilder.add(context);
1024 log.debug(".. createGroupsFromDestinationSet: Submitted "
1025 + "next objective {} in device {}", nextId, deviceId);
1026 flowObjectiveService.next(deviceId, nextObj);
1027 //update store
1028 dsNextObjStore.put(new DestinationSetNextObjectiveStoreKey(deviceId, ds),
1029 new NextNeighbors(dstNextHops, nextId));
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -08001030 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001031
Saurav Das4ce45962015-11-24 23:21:05 -08001032 /**
Saurav Das1a129a02016-11-18 15:21:57 -08001033 * Creates broadcast groups for all ports in the same subnet for
1034 * all configured subnets.
Saurav Das4ce45962015-11-24 23:21:05 -08001035 */
Charles Chan59cc16d2017-02-02 16:20:42 -08001036 public void createGroupsFromVlanConfig() {
Charles Chan7ffd81f2017-02-08 15:52:08 -08001037 srManager.getVlanPortMap(deviceId).asMap().forEach((vlanId, ports) -> {
Charles Chan59cc16d2017-02-02 16:20:42 -08001038 createBcastGroupFromVlan(vlanId, ports);
Pier Ventre10bd8d12016-11-26 21:05:22 -08001039 });
Saurav Das1a129a02016-11-18 15:21:57 -08001040 }
Charles Chan9f676b62015-10-29 14:58:10 -07001041
Saurav Das1a129a02016-11-18 15:21:57 -08001042 /**
Charles Chan59cc16d2017-02-02 16:20:42 -08001043 * Creates a single broadcast group from a given vlan id and list of ports.
Saurav Das1a129a02016-11-18 15:21:57 -08001044 *
Charles Chan59cc16d2017-02-02 16:20:42 -08001045 * @param vlanId vlan id
Saurav Das1a129a02016-11-18 15:21:57 -08001046 * @param ports list of ports in the subnet
1047 */
Charles Chan7ffd81f2017-02-08 15:52:08 -08001048 public void createBcastGroupFromVlan(VlanId vlanId, Collection<PortNumber> ports) {
Charles Chan59cc16d2017-02-02 16:20:42 -08001049 VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
Charles Chan9f676b62015-10-29 14:58:10 -07001050
Charles Chan59cc16d2017-02-02 16:20:42 -08001051 if (vlanNextObjStore.containsKey(key)) {
Saurav Das1a129a02016-11-18 15:21:57 -08001052 log.debug("Broadcast group for device {} and subnet {} exists",
Charles Chan59cc16d2017-02-02 16:20:42 -08001053 deviceId, vlanId);
Saurav Das1a129a02016-11-18 15:21:57 -08001054 return;
1055 }
Charles Chan188ebf52015-12-23 00:15:11 -08001056
Saurav Das1a129a02016-11-18 15:21:57 -08001057 TrafficSelector metadata =
Charles Chan59cc16d2017-02-02 16:20:42 -08001058 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
Charles Chanc42e84e2015-10-20 16:24:19 -07001059
Saurav Das1a129a02016-11-18 15:21:57 -08001060 int nextId = flowObjectiveService.allocateNextId();
Charles Chanc42e84e2015-10-20 16:24:19 -07001061
Saurav Das1a129a02016-11-18 15:21:57 -08001062 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1063 .builder().withId(nextId)
1064 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1065 .withMeta(metadata);
Charles Chanc42e84e2015-10-20 16:24:19 -07001066
Saurav Das1a129a02016-11-18 15:21:57 -08001067 ports.forEach(port -> {
1068 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Charles Chan7ffd81f2017-02-08 15:52:08 -08001069 if (toPopVlan(port, vlanId)) {
1070 tBuilder.popVlan();
1071 }
Saurav Das1a129a02016-11-18 15:21:57 -08001072 tBuilder.setOutput(port);
1073 nextObjBuilder.addTreatment(tBuilder.build());
Charles Chanc42e84e2015-10-20 16:24:19 -07001074 });
Saurav Das1a129a02016-11-18 15:21:57 -08001075
Saurav Das961beb22017-03-29 19:09:17 -07001076 ObjectiveContext context = new DefaultObjectiveContext(
1077 (objective) ->
1078 log.debug("createBroadcastGroupFromVlan installed "
1079 + "NextObj {} on {}", nextId, deviceId),
1080 (objective, error) ->
1081 log.warn("createBroadcastGroupFromVlan failed to install"
1082 + " NextObj {} on {}: {}", nextId, deviceId, error)
1083 );
1084 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Das1a129a02016-11-18 15:21:57 -08001085 flowObjectiveService.next(deviceId, nextObj);
Saurav Dasf14d9ef2017-12-05 15:00:23 -08001086 log.debug("createBcastGroupFromVlan: Submitted next objective {} "
1087 + "for vlan: {} in device {}", nextId, vlanId, deviceId);
Saurav Das1a129a02016-11-18 15:21:57 -08001088
Charles Chan59cc16d2017-02-02 16:20:42 -08001089 vlanNextObjStore.put(key, nextId);
Charles Chanc42e84e2015-10-20 16:24:19 -07001090 }
1091
Charles Chane849c192016-01-11 18:28:54 -08001092 /**
Jonghwan Hyun42fe1052017-08-25 17:48:36 -07001093 * Removes a single broadcast group from a given vlan id.
1094 * The group should be empty.
1095 * @param deviceId device Id to remove the group
1096 * @param portNum port number related to the group
1097 * @param vlanId vlan id of the broadcast group to remove
1098 * @param popVlan true if the TrafficTreatment involves pop vlan tag action
1099 */
1100 public void removeBcastGroupFromVlan(DeviceId deviceId, PortNumber portNum,
1101 VlanId vlanId, boolean popVlan) {
1102 VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
1103
1104 if (!vlanNextObjStore.containsKey(key)) {
1105 log.debug("Broadcast group for device {} and subnet {} does not exist",
1106 deviceId, vlanId);
1107 return;
1108 }
1109
1110 TrafficSelector metadata =
1111 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1112
1113 int nextId = vlanNextObjStore.get(key);
1114
1115 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1116 .builder().withId(nextId)
1117 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1118 .withMeta(metadata);
1119
1120 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1121 if (popVlan) {
1122 tBuilder.popVlan();
1123 }
1124 tBuilder.setOutput(portNum);
1125 nextObjBuilder.addTreatment(tBuilder.build());
1126
1127 ObjectiveContext context = new DefaultObjectiveContext(
1128 (objective) ->
1129 log.debug("removeBroadcastGroupFromVlan removed "
1130 + "NextObj {} on {}", nextId, deviceId),
1131 (objective, error) ->
1132 log.warn("removeBroadcastGroupFromVlan failed to remove "
1133 + " NextObj {} on {}: {}", nextId, deviceId, error)
1134 );
1135 NextObjective nextObj = nextObjBuilder.remove(context);
1136 flowObjectiveService.next(deviceId, nextObj);
1137 log.debug("removeBcastGroupFromVlan: Submited next objective {} in device {}",
1138 nextId, deviceId);
1139
1140 vlanNextObjStore.remove(key, nextId);
1141 }
1142
1143 /**
Charles Chan7ffd81f2017-02-08 15:52:08 -08001144 * Determine if we should pop given vlan before sending packets to the given port.
1145 *
1146 * @param portNumber port number
1147 * @param vlanId vlan id
1148 * @return true if the vlan id is not contained in any vlanTagged config
1149 */
1150 private boolean toPopVlan(PortNumber portNumber, VlanId vlanId) {
Saurav Das7bcbe702017-06-13 15:35:54 -07001151 return srManager.interfaceService
1152 .getInterfacesByPort(new ConnectPoint(deviceId, portNumber))
Charles Chan7ffd81f2017-02-08 15:52:08 -08001153 .stream().noneMatch(intf -> intf.vlanTagged().contains(vlanId));
1154 }
1155
1156 /**
Saurav Das4ce45962015-11-24 23:21:05 -08001157 * Create simple next objective for a single port. The treatments can include
1158 * all outgoing actions that need to happen on the packet.
1159 *
1160 * @param portNum the outgoing port on the device
1161 * @param treatment the actions to apply on the packets (should include outport)
1162 * @param meta optional data to pass to the driver
1163 */
1164 public void createGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
1165 TrafficSelector meta) {
1166 int nextId = flowObjectiveService.allocateNextId();
1167 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
Saurav Das76ae6812017-03-15 15:15:14 -07001168 deviceId, portNum, treatment, meta);
Saurav Das4ce45962015-11-24 23:21:05 -08001169
1170 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1171 .builder().withId(nextId)
1172 .withType(NextObjective.Type.SIMPLE)
1173 .addTreatment(treatment)
1174 .fromApp(appId)
1175 .withMeta(meta);
1176
Saurav Das961beb22017-03-29 19:09:17 -07001177 ObjectiveContext context = new DefaultObjectiveContext(
1178 (objective) ->
1179 log.debug("createGroupFromPort installed "
1180 + "NextObj {} on {}", nextId, deviceId),
1181 (objective, error) ->
1182 log.warn("createGroupFromPort failed to install"
1183 + " NextObj {} on {}: {}", nextId, deviceId, error)
1184 );
1185 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Das4ce45962015-11-24 23:21:05 -08001186 flowObjectiveService.next(deviceId, nextObj);
1187 log.debug("createGroupFromPort: Submited next objective {} in device {} "
1188 + "for port {}", nextId, deviceId, portNum);
1189
1190 portNextObjStore.put(key, nextId);
1191 }
1192
sangho1e575652015-05-14 00:39:53 -07001193 /**
Jonghwan Hyun42fe1052017-08-25 17:48:36 -07001194 * Removes simple next objective for a single port.
1195 *
1196 * @param deviceId device id that has the port to deal with
1197 * @param portNum the outgoing port on the device
1198 * @param vlanId vlan id associated with the port
1199 * @param popVlan true if POP_VLAN action is applied on the packets, false otherwise
1200 */
1201 public void removePortNextObjective(DeviceId deviceId, PortNumber portNum, VlanId vlanId, boolean popVlan) {
1202 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
1203 mbuilder.matchVlanId(vlanId);
1204
1205 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
1206 tbuilder.immediate().setOutput(portNum);
1207 if (popVlan) {
1208 tbuilder.immediate().popVlan();
1209 }
1210
1211 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, portNum,
1212 tbuilder.build(), mbuilder.build(), false);
1213
1214 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
1215 deviceId, portNum, tbuilder.build(), mbuilder.build());
1216 if (portNextObjId != -1 && portNextObjStore.containsKey(key)) {
1217 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1218 .builder().withId(portNextObjId)
1219 .withType(NextObjective.Type.SIMPLE).fromApp(appId);
1220 ObjectiveContext context = new DefaultObjectiveContext(
1221 (objective) -> log.debug("removePortNextObjective removes NextObj {} on {}",
1222 portNextObjId, deviceId),
1223 (objective, error) ->
1224 log.warn("removePortNextObjective failed to remove NextObj {} on {}: {}",
1225 portNextObjId, deviceId, error));
1226 NextObjective nextObjective = nextObjBuilder.remove(context);
1227 log.info("**removePortNextObjective: Submitted "
1228 + "next objective {} in device {}",
1229 portNextObjId, deviceId);
1230 flowObjectiveService.next(deviceId, nextObjective);
1231
1232 portNextObjStore.remove(key);
1233 }
1234 }
1235 /**
sangho1e575652015-05-14 00:39:53 -07001236 * Removes groups for the next objective ID given.
1237 *
1238 * @param objectiveId next objective ID to remove
1239 * @return true if succeeds, false otherwise
1240 */
1241 public boolean removeGroup(int objectiveId) {
Saurav Das7bcbe702017-06-13 15:35:54 -07001242 for (Map.Entry<DestinationSetNextObjectiveStoreKey, NextNeighbors> e :
1243 dsNextObjStore.entrySet()) {
1244 if (e.getValue().nextId() != objectiveId) {
1245 continue;
1246 }
Pier Luigi63edd932018-01-14 21:56:11 +01001247 // Right now it is just used in TunnelHandler
1248 // remember in future that PW transit groups could
1249 // be Indirect groups
sangho1e575652015-05-14 00:39:53 -07001250 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1251 .builder().withId(objectiveId)
1252 .withType(NextObjective.Type.HASHED).fromApp(appId);
Charles Chan216e3c82016-04-23 14:48:16 -07001253 ObjectiveContext context = new DefaultObjectiveContext(
1254 (objective) -> log.debug("RemoveGroup removes NextObj {} on {}",
1255 objectiveId, deviceId),
1256 (objective, error) ->
1257 log.warn("RemoveGroup failed to remove NextObj {} on {}: {}",
1258 objectiveId, deviceId, error));
1259 NextObjective nextObjective = nextObjBuilder.remove(context);
Saurav Das8a0732e2015-11-20 15:27:53 -08001260 log.info("**removeGroup: Submited "
1261 + "next objective {} in device {}",
1262 objectiveId, deviceId);
sangho1e575652015-05-14 00:39:53 -07001263 flowObjectiveService.next(deviceId, nextObjective);
1264
Saurav Das7bcbe702017-06-13 15:35:54 -07001265 dsNextObjStore.remove(e.getKey());
sangho0b2b6d12015-05-20 22:16:38 -07001266 return true;
sangho1e575652015-05-14 00:39:53 -07001267 }
1268
1269 return false;
1270 }
Jonghwan Hyun42fe1052017-08-25 17:48:36 -07001271 /**
1272 * Remove simple next objective for a single port. The treatments can include
1273 * all outgoing actions that need to happen on the packet.
1274 *
1275 * @param portNum the outgoing port on the device
1276 * @param treatment the actions applied on the packets (should include outport)
1277 * @param meta optional data to pass to the driver
1278 */
1279 public void removeGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
1280 TrafficSelector meta) {
1281 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
1282 deviceId, portNum, treatment, meta);
1283 Integer nextId = portNextObjStore.get(key);
1284
1285 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1286 .builder().withId(nextId)
1287 .withType(NextObjective.Type.SIMPLE)
1288 .addTreatment(treatment)
1289 .fromApp(appId)
1290 .withMeta(meta);
1291
1292 ObjectiveContext context = new DefaultObjectiveContext(
1293 (objective) ->
1294 log.info("removeGroupFromPort installed "
1295 + "NextObj {} on {}", nextId, deviceId),
1296 (objective, error) ->
1297 log.warn("removeGroupFromPort failed to install"
1298 + " NextObj {} on {}: {}", nextId, deviceId, error)
1299 );
1300 NextObjective nextObj = nextObjBuilder.remove(context);
1301 flowObjectiveService.next(deviceId, nextObj);
1302 log.info("removeGroupFromPort: Submitted next objective {} in device {} "
1303 + "for port {}", nextId, deviceId, portNum);
1304
1305 portNextObjStore.remove(key);
1306 }
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -07001307
Charles Chane849c192016-01-11 18:28:54 -08001308 /**
1309 * Removes all groups from all next objective stores.
1310 */
Saurav Das7bcbe702017-06-13 15:35:54 -07001311 /*public void removeAllGroups() {
1312 for (Map.Entry<NeighborSetNextObjectiveStoreKey, NextNeighbors> entry:
Saurav Das423fe2b2015-12-04 10:52:59 -08001313 nsNextObjStore.entrySet()) {
Saurav Das7bcbe702017-06-13 15:35:54 -07001314 removeGroup(entry.getValue().nextId());
Saurav Das423fe2b2015-12-04 10:52:59 -08001315 }
1316 for (Map.Entry<PortNextObjectiveStoreKey, Integer> entry:
1317 portNextObjStore.entrySet()) {
1318 removeGroup(entry.getValue());
1319 }
Charles Chan59cc16d2017-02-02 16:20:42 -08001320 for (Map.Entry<VlanNextObjectiveStoreKey, Integer> entry:
1321 vlanNextObjStore.entrySet()) {
Saurav Das423fe2b2015-12-04 10:52:59 -08001322 removeGroup(entry.getValue());
1323 }
Saurav Das7bcbe702017-06-13 15:35:54 -07001324 }*/ //XXX revisit
1325
Saurav Dasceccf242017-08-03 18:30:35 -07001326 /**
1327 * Triggers a one time bucket verification operation on all hash groups
1328 * on this device.
1329 */
1330 public void triggerBucketCorrector() {
1331 BucketCorrector bc = new BucketCorrector();
1332 bc.run();
1333 }
1334
Jonghwan Hyunf810a7a2018-02-12 16:43:45 +09001335 /**
1336 * Modifies L2IG bucket when the interface configuration is updated, especially
1337 * when the interface has same VLAN ID but the VLAN type is changed (e.g., from
1338 * vlan-tagged [10] to vlan-untagged 10), which requires changes on
1339 * TrafficTreatment in turn.
1340 *
1341 * @param portNumber the port on this device that needs to be updated
1342 * @param vlanId the vlan id corresponding to this port
1343 * @param pushVlan indicates if packets should be sent out untagged or not out
1344 * from the port. If true, updated TrafficTreatment involves
1345 * pop vlan tag action. If false, updated TrafficTreatment
1346 * does not involve pop vlan tag action.
1347 */
1348 public void updateL2InterfaceGroupBucket(PortNumber portNumber, VlanId vlanId, boolean pushVlan) {
1349 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1350 if (pushVlan) {
1351 tBuilder.popVlan();
1352 }
1353 tBuilder.setOutput(portNumber);
1354
1355 TrafficSelector metadata =
1356 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1357
1358 int nextId = getVlanNextObjectiveId(vlanId);
1359
1360 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1361 .builder().withId(nextId)
1362 .withType(NextObjective.Type.SIMPLE).fromApp(appId)
1363 .addTreatment(tBuilder.build())
1364 .withMeta(metadata);
1365
1366 ObjectiveContext context = new DefaultObjectiveContext(
1367 (objective) -> log.debug("port {} successfully updated NextObj {} on {}",
1368 portNumber, nextId, deviceId),
1369 (objective, error) ->
1370 log.warn("port {} failed to updated NextObj {} on {}: {}",
1371 portNumber, nextId, deviceId, error));
1372
1373 flowObjectiveService.next(deviceId, nextObjBuilder.modify(context));
Jonghwan Hyun42fe1052017-08-25 17:48:36 -07001374 }
1375
Jonghwan Hyunf810a7a2018-02-12 16:43:45 +09001376 /**
1377 * Adds a single port to the L2FG or removes it from the L2FG.
1378 *
1379 * @param vlanId the vlan id corresponding to this port
1380 * @param portNum the port on this device to be updated
1381 * @param nextId the next objective ID for the given vlan id
1382 * @param install if true, adds the port to L2FG. If false, removes it from L2FG.
1383 */
1384 public void updateGroupFromVlanConfiguration(VlanId vlanId, PortNumber portNum, int nextId, boolean install) {
Jonghwan Hyun42fe1052017-08-25 17:48:36 -07001385 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1386 if (toPopVlan(portNum, vlanId)) {
1387 tBuilder.popVlan();
1388 }
1389 tBuilder.setOutput(portNum);
1390
1391 TrafficSelector metadata =
1392 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1393
1394 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1395 .builder().withId(nextId)
1396 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1397 .addTreatment(tBuilder.build())
1398 .withMeta(metadata);
1399
1400 ObjectiveContext context = new DefaultObjectiveContext(
1401 (objective) -> log.debug("port {} successfully removedFrom NextObj {} on {}",
1402 portNum, nextId, deviceId),
1403 (objective, error) ->
1404 log.warn("port {} failed to removedFrom NextObj {} on {}: {}",
1405 portNum, nextId, deviceId, error));
1406
1407 if (install) {
1408 flowObjectiveService.next(deviceId, nextObjBuilder.addToExisting(context));
1409 } else {
1410 flowObjectiveService.next(deviceId, nextObjBuilder.removeFromExisting(context));
1411 }
1412 }
Saurav Das1547b3f2017-05-05 17:01:08 -07001413
1414 /**
Saurav Das9df5b7c2017-08-14 16:44:43 -07001415 * Performs bucket verification operation for all hash groups in this device.
1416 * Checks RouteHandler to ensure that routing is stable before attempting
1417 * verification. Verification involves creating a nextObjective with
1418 * operation VERIFY for existing next objectives in the store, and passing
1419 * it to the driver. It is the driver that actually performs the verification
1420 * by adding or removing buckets to match the verification next objective
1421 * created here.
Saurav Das1547b3f2017-05-05 17:01:08 -07001422 */
Saurav Dasceccf242017-08-03 18:30:35 -07001423 protected final class BucketCorrector implements Runnable {
1424 Integer nextId;
Saurav Das1547b3f2017-05-05 17:01:08 -07001425
Saurav Dasceccf242017-08-03 18:30:35 -07001426 BucketCorrector() {
1427 this.nextId = null;
1428 }
1429
1430 BucketCorrector(Integer nextId) {
1431 this.nextId = nextId;
Saurav Das1547b3f2017-05-05 17:01:08 -07001432 }
1433
1434 @Override
1435 public void run() {
Saurav Dasceccf242017-08-03 18:30:35 -07001436 if (!srManager.mastershipService.isLocalMaster(deviceId)) {
1437 return;
Saurav Das1547b3f2017-05-05 17:01:08 -07001438 }
Saurav Dasceccf242017-08-03 18:30:35 -07001439 DefaultRoutingHandler rh = srManager.getRoutingHandler();
1440 if (rh == null) {
1441 return;
1442 }
1443 if (!rh.isRoutingStable()) {
1444 return;
1445 }
1446 rh.acquireRoutingLock();
1447 try {
Saurav Das9df5b7c2017-08-14 16:44:43 -07001448 log.trace("running bucket corrector for dev: {}", deviceId);
Saurav Dasceccf242017-08-03 18:30:35 -07001449 Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet()
1450 .stream()
1451 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Pier Luigi63edd932018-01-14 21:56:11 +01001452 // Filter out PW transit groups or include them if MPLS ECMP is supported
Saurav Dasa4020382018-02-14 14:14:54 -08001453 .filter(entry -> !entry.getKey().destinationSet().notBos() ||
1454 (entry.getKey().destinationSet().notBos() && srManager.getMplsEcmp()))
1455 // Filter out simple SWAP groups or include them if MPLS ECMP is supported
1456 .filter(entry -> !entry.getKey().destinationSet().swap() ||
1457 (entry.getKey().destinationSet().swap() && srManager.getMplsEcmp()))
Saurav Dasceccf242017-08-03 18:30:35 -07001458 .map(entry -> entry.getKey())
1459 .collect(Collectors.toSet());
1460 for (DestinationSetNextObjectiveStoreKey dsKey : dsKeySet) {
1461 NextNeighbors next = dsNextObjStore.get(dsKey);
1462 if (next == null) {
1463 continue;
1464 }
1465 int nid = next.nextId();
1466 if (nextId != null && nextId != nid) {
1467 continue;
1468 }
Saurav Das9df5b7c2017-08-14 16:44:43 -07001469 log.trace("bkt-corr: dsNextObjStore for device {}: {}",
Saurav Dasceccf242017-08-03 18:30:35 -07001470 deviceId, dsKey, next);
1471 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
1472 metabuilder.matchVlanId(INTERNAL_VLAN);
1473 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
1474 .withId(nid)
1475 .withType(NextObjective.Type.HASHED)
1476 .withMeta(metabuilder.build())
1477 .fromApp(appId);
1478
1479 next.dstNextHops().forEach((dstDev, nextHops) -> {
1480 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dstDev);
1481 nextHops.forEach(neighbor -> {
1482 MacAddress neighborMac;
1483 try {
1484 neighborMac = deviceConfig.getDeviceMac(neighbor);
1485 } catch (DeviceConfigNotFoundException e) {
1486 log.warn(e.getMessage() + " Aborting neighbor"
1487 + neighbor);
1488 return;
1489 }
1490 devicePortMap.get(neighbor).forEach(port -> {
Saurav Das9df5b7c2017-08-14 16:44:43 -07001491 log.trace("verify in device {} nextId {}: bucket with"
Saurav Dasceccf242017-08-03 18:30:35 -07001492 + " port/label {}/{} to dst {} via {}",
1493 deviceId, nid, port, edgeLabel,
1494 dstDev, neighbor);
Saurav Dasa4020382018-02-14 14:14:54 -08001495 nextObjBuilder
1496 .addTreatment(treatmentBuilder(port,
1497 neighborMac,
1498 dsKey.destinationSet().swap(),
1499 edgeLabel));
Saurav Dasceccf242017-08-03 18:30:35 -07001500 });
1501 });
1502 });
1503
1504 NextObjective nextObjective = nextObjBuilder.verify();
1505 flowObjectiveService.next(deviceId, nextObjective);
1506 }
1507 } finally {
1508 rh.releaseRoutingLock();
1509 }
1510
1511 }
1512
1513 TrafficTreatment treatmentBuilder(PortNumber outport, MacAddress dstMac,
Saurav Dasa4020382018-02-14 14:14:54 -08001514 boolean swap, int edgeLabel) {
Saurav Dasceccf242017-08-03 18:30:35 -07001515 TrafficTreatment.Builder tBuilder =
1516 DefaultTrafficTreatment.builder();
1517 tBuilder.setOutput(outport)
1518 .setEthDst(dstMac)
1519 .setEthSrc(nodeMacAddr);
1520 if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
Saurav Dasa4020382018-02-14 14:14:54 -08001521 if (swap) {
1522 // swap label case
1523 tBuilder.setMpls(MplsLabel.mplsLabel(edgeLabel));
1524 } else {
1525 // ecmp with label push case
1526 tBuilder.pushMpls()
1527 .copyTtlOut()
1528 .setMpls(MplsLabel.mplsLabel(edgeLabel));
1529 }
Saurav Dasceccf242017-08-03 18:30:35 -07001530 }
1531 return tBuilder.build();
Saurav Das1547b3f2017-05-05 17:01:08 -07001532 }
1533 }
1534
Pier Luigi63edd932018-01-14 21:56:11 +01001535}