blob: c33c4571ac93d68e8880b67d4eb11949f642ef86 [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;
pierventrea3989be2021-01-08 16:43:17 +010041import org.onosproject.net.flowobjective.Objective;
Srikanth Vavilapalli8c83f1d2015-05-22 13:47:31 -070042import org.onosproject.net.flowobjective.ObjectiveContext;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070043import org.onosproject.net.link.LinkService;
Saurav Dasfbe74572017-08-03 18:30:35 -070044import org.onosproject.segmentrouting.DefaultRoutingHandler;
Saurav Das62af8802015-12-04 10:52:59 -080045import org.onosproject.segmentrouting.SegmentRoutingManager;
Charles Chan319d1a22015-11-03 10:42:14 -080046import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
47import org.onosproject.segmentrouting.config.DeviceProperties;
pierventre2b102f92020-09-08 16:45:36 +020048import org.onosproject.segmentrouting.DeviceConfiguration;
Saurav Das261c3002017-06-13 15:35:54 -070049import org.onosproject.segmentrouting.storekey.DestinationSetNextObjectiveStoreKey;
Charles Chan1eaf4802016-04-18 13:44:03 -070050import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
Charles Chan10b0fb72017-02-02 16:20:42 -080051import org.onosproject.segmentrouting.storekey.VlanNextObjectiveStoreKey;
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +000052import org.onosproject.segmentrouting.storekey.MacVlanNextObjectiveStoreKey;
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -070053import org.onosproject.store.service.EventuallyConsistentMap;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070054import org.slf4j.Logger;
55
Pier Ventre229fd0b2016-10-31 16:49:19 -070056import java.net.URI;
Charles Chan90772a72017-02-08 15:52:08 -080057import java.util.Collection;
Pier Ventre229fd0b2016-10-31 16:49:19 -070058import java.util.Collections;
Saurav Das62ae6792017-05-15 15:34:25 -070059import java.util.HashMap;
Pier Ventre229fd0b2016-10-31 16:49:19 -070060import java.util.HashSet;
61import java.util.List;
62import java.util.Map;
63import java.util.Set;
pierventrea3989be2021-01-08 16:43:17 +010064import java.util.concurrent.CompletableFuture;
Pier Ventre229fd0b2016-10-31 16:49:19 -070065import java.util.concurrent.ConcurrentHashMap;
Saurav Das8a3022d2017-05-05 17:01:08 -070066import java.util.concurrent.ScheduledExecutorService;
67import java.util.concurrent.TimeUnit;
Pier Ventre229fd0b2016-10-31 16:49:19 -070068import java.util.stream.Collectors;
69
70import static com.google.common.base.Preconditions.checkNotNull;
Saurav Das8a3022d2017-05-05 17:01:08 -070071import static java.util.concurrent.Executors.newScheduledThreadPool;
72import static org.onlab.util.Tools.groupedThreads;
Pier Ventre229fd0b2016-10-31 16:49:19 -070073import static org.slf4j.LoggerFactory.getLogger;
74
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070075/**
Srikanth Vavilapalli64505482015-04-21 13:04:13 -070076 * Default ECMP group handler creation module. This component creates a set of
77 * ECMP groups for every neighbor that this device is connected to based on
78 * whether the current device is an edge device or a transit device.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070079 */
80public class DefaultGroupHandler {
Ray Milkey5247fa92018-01-12 14:22:06 -080081 private static final Logger log = getLogger(DefaultGroupHandler.class);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070082
Saurav Dasfbe74572017-08-03 18:30:35 -070083 private static final long VERIFY_INTERVAL = 30; // secs
84
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070085 protected final DeviceId deviceId;
86 protected final ApplicationId appId;
87 protected final DeviceProperties deviceConfig;
88 protected final List<Integer> allSegmentIds;
Pier Ventreadb4ae62016-11-23 09:57:42 -080089 protected int ipv4NodeSegmentId = -1;
90 protected int ipv6NodeSegmentId = -1;
Charles Chan319d1a22015-11-03 10:42:14 -080091 protected boolean isEdgeRouter = false;
92 protected MacAddress nodeMacAddr = null;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070093 protected LinkService linkService;
Srikanth Vavilapalli64505482015-04-21 13:04:13 -070094 protected FlowObjectiveService flowObjectiveService;
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +000095 private DeviceConfiguration config;
96
Saurav Das62ae6792017-05-15 15:34:25 -070097 /**
98 * local store for neighbor-device-ids and the set of ports on this device
99 * that connect to the same neighbor.
100 */
Saurav Das4c35fc42015-11-20 15:27:53 -0800101 protected ConcurrentHashMap<DeviceId, Set<PortNumber>> devicePortMap =
102 new ConcurrentHashMap<>();
Saurav Das62ae6792017-05-15 15:34:25 -0700103 /**
104 * local store for ports on this device connected to neighbor-device-id.
105 */
Saurav Das4c35fc42015-11-20 15:27:53 -0800106 protected ConcurrentHashMap<PortNumber, DeviceId> portDeviceMap =
107 new ConcurrentHashMap<>();
Saurav Das62ae6792017-05-15 15:34:25 -0700108
Saurav Das261c3002017-06-13 15:35:54 -0700109 // distributed store for (device+destination-set) mapped to next-id and neighbors
110 protected EventuallyConsistentMap<DestinationSetNextObjectiveStoreKey, NextNeighbors>
111 dsNextObjStore = null;
Saurav Dasf0f592d2016-11-18 15:21:57 -0800112 // distributed store for (device+subnet-ip-prefix) mapped to next-id
Charles Chan10b0fb72017-02-02 16:20:42 -0800113 protected EventuallyConsistentMap<VlanNextObjectiveStoreKey, Integer>
114 vlanNextObjStore = null;
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000115 // distributed store for (device+mac+vlan+treatment) mapped to next-id
116 protected EventuallyConsistentMap<MacVlanNextObjectiveStoreKey, Integer>
117 macVlanNextObjStore = null;
Saurav Dasf0f592d2016-11-18 15:21:57 -0800118 // distributed store for (device+port+treatment) mapped to next-id
Charles Chanb7f75ac2016-01-11 18:28:54 -0800119 protected EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
120 portNextObjStore = null;
Charles Chande6655c2015-12-23 00:15:11 -0800121 private SegmentRoutingManager srManager;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700122
Saurav Das8a3022d2017-05-05 17:01:08 -0700123 private ScheduledExecutorService executorService
Saurav Dasfbe74572017-08-03 18:30:35 -0700124 = newScheduledThreadPool(1, groupedThreads("bktCorrector", "bktC-%d", log));
Saurav Das8a3022d2017-05-05 17:01:08 -0700125
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700126 protected KryoNamespace.Builder kryo = new KryoNamespace.Builder()
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700127 .register(URI.class).register(HashSet.class)
Saurav Das261c3002017-06-13 15:35:54 -0700128 .register(DeviceId.class).register(PortNumber.class)
129 .register(DestinationSet.class).register(PolicyGroupIdentifier.class)
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700130 .register(PolicyGroupParams.class)
131 .register(GroupBucketIdentifier.class)
132 .register(GroupBucketIdentifier.BucketOutputType.class);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700133
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700134 protected DefaultGroupHandler(DeviceId deviceId, ApplicationId appId,
135 DeviceProperties config,
136 LinkService linkService,
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700137 FlowObjectiveService flowObjService,
Charles Chande6655c2015-12-23 00:15:11 -0800138 SegmentRoutingManager srManager) {
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700139 this.deviceId = checkNotNull(deviceId);
140 this.appId = checkNotNull(appId);
141 this.deviceConfig = checkNotNull(config);
142 this.linkService = checkNotNull(linkService);
Charles Chan319d1a22015-11-03 10:42:14 -0800143 this.allSegmentIds = checkNotNull(config.getAllDeviceSegmentIds());
144 try {
Pier Ventreadb4ae62016-11-23 09:57:42 -0800145 this.ipv4NodeSegmentId = config.getIPv4SegmentId(deviceId);
146 this.ipv6NodeSegmentId = config.getIPv6SegmentId(deviceId);
Charles Chan319d1a22015-11-03 10:42:14 -0800147 this.isEdgeRouter = config.isEdgeDevice(deviceId);
148 this.nodeMacAddr = checkNotNull(config.getDeviceMac(deviceId));
149 } catch (DeviceConfigNotFoundException e) {
150 log.warn(e.getMessage()
151 + " Skipping value assignment in DefaultGroupHandler");
152 }
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700153 this.flowObjectiveService = flowObjService;
Saurav Das261c3002017-06-13 15:35:54 -0700154 this.dsNextObjStore = srManager.dsNextObjStore();
Ray Milkeyb85de082017-04-05 09:42:04 -0700155 this.vlanNextObjStore = srManager.vlanNextObjStore();
156 this.portNextObjStore = srManager.portNextObjStore();
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000157 this.macVlanNextObjStore = srManager.macVlanNextObjStore();
Charles Chande6655c2015-12-23 00:15:11 -0800158 this.srManager = srManager;
Saurav Dasfbe74572017-08-03 18:30:35 -0700159 executorService.scheduleWithFixedDelay(new BucketCorrector(), 10,
160 VERIFY_INTERVAL,
161 TimeUnit.SECONDS);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700162 populateNeighborMaps();
163 }
164
165 /**
Saurav Dasfbe74572017-08-03 18:30:35 -0700166 * Gracefully shuts down a groupHandler. Typically called when the handler is
167 * no longer needed.
168 */
169 public void shutdown() {
170 executorService.shutdown();
171 }
172
173 /**
Saurav Das62ae6792017-05-15 15:34:25 -0700174 * Creates a group handler object.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700175 *
176 * @param deviceId device identifier
177 * @param appId application identifier
178 * @param config interface to retrieve the device properties
179 * @param linkService link service object
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700180 * @param flowObjService flow objective service object
Charles Chanb7f75ac2016-01-11 18:28:54 -0800181 * @param srManager segment routing manager
Charles Chan319d1a22015-11-03 10:42:14 -0800182 * @throws DeviceConfigNotFoundException if the device configuration is not found
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700183 * @return default group handler type
184 */
Saurav Das2d94d312015-11-24 23:21:05 -0800185 public static DefaultGroupHandler createGroupHandler(
Saurav Dasfbe74572017-08-03 18:30:35 -0700186 DeviceId deviceId,
187 ApplicationId appId,
188 DeviceProperties config,
189 LinkService linkService,
190 FlowObjectiveService flowObjService,
191 SegmentRoutingManager srManager)
192 throws DeviceConfigNotFoundException {
Saurav Das62ae6792017-05-15 15:34:25 -0700193 return new DefaultGroupHandler(deviceId, appId, config,
194 linkService,
195 flowObjService,
196 srManager);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700197 }
198
199 /**
Saurav Das97241862018-02-14 14:14:54 -0800200 * Updates local stores for link-src-device/port to neighbor (link-dst) for
201 * link that has come up.
Saurav Das62ae6792017-05-15 15:34:25 -0700202 *
203 * @param link the infrastructure link
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700204 */
Saurav Das62ae6792017-05-15 15:34:25 -0700205 public void portUpForLink(Link link) {
Saurav Dasfbe74572017-08-03 18:30:35 -0700206 if (!link.src().deviceId().equals(deviceId)) {
207 log.warn("linkUp: deviceId{} doesn't match with link src {}",
208 deviceId, link.src().deviceId());
209 return;
210 }
Saurav Das62ae6792017-05-15 15:34:25 -0700211
Saurav Dasfbe74572017-08-03 18:30:35 -0700212 log.info("* portUpForLink: Device {} linkUp at local port {} to "
213 + "neighbor {}", deviceId, link.src().port(), link.dst().deviceId());
214 // ensure local state is updated even if linkup is aborted later on
215 addNeighborAtPort(link.dst().deviceId(),
216 link.src().port());
217 }
Saurav Das62ae6792017-05-15 15:34:25 -0700218
Saurav Dasfbe74572017-08-03 18:30:35 -0700219 /**
Saurav Das97241862018-02-14 14:14:54 -0800220 * Updates local stores for link-src-device/port to neighbor (link-dst) for
221 * link that has gone down.
Saurav Dasfbe74572017-08-03 18:30:35 -0700222 *
Saurav Das97241862018-02-14 14:14:54 -0800223 * @param link the infrastructure link
Saurav Dasfbe74572017-08-03 18:30:35 -0700224 */
Saurav Das97241862018-02-14 14:14:54 -0800225 public void portDownForLink(Link link) {
226 PortNumber port = link.src().port();
Saurav Dasfbe74572017-08-03 18:30:35 -0700227 if (portDeviceMap.get(port) == null) {
228 log.warn("portDown: unknown port");
229 return;
230 }
Saurav Das62ae6792017-05-15 15:34:25 -0700231
Saurav Dasfbe74572017-08-03 18:30:35 -0700232 log.debug("Device {} portDown {} to neighbor {}", deviceId, port,
233 portDeviceMap.get(port));
234 devicePortMap.get(portDeviceMap.get(port)).remove(port);
235 portDeviceMap.remove(port);
236 }
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700237
238 /**
Saurav Das97241862018-02-14 14:14:54 -0800239 * Cleans up local stores for removed neighbor device.
240 *
241 * @param neighborId the device identifier for the neighbor device
242 */
243 public void cleanUpForNeighborDown(DeviceId neighborId) {
244 Set<PortNumber> ports = devicePortMap.remove(neighborId);
245 if (ports != null) {
246 ports.forEach(p -> portDeviceMap.remove(p));
247 }
248 }
249
250 /**
Saurav Das62ae6792017-05-15 15:34:25 -0700251 * Checks all groups in the src-device of link for neighbor sets that include
252 * the dst-device of link, and edits the hash groups according to link up
pierventre37dcf4c2021-09-16 18:43:06 +0200253 * or down. Should only be called by the instance leading the programming of
254 * the src-switch of link. Typically used when there are no route-path
255 * changes due to the link up or down, as the ECMPspg does not change.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700256 *
Saurav Das62ae6792017-05-15 15:34:25 -0700257 * @param link the infrastructure link that has gone down or come up
258 * @param linkDown true if link has gone down
259 * @param firstTime true if link has come up for the first time i.e a link
260 * not seen-before
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700261 */
Saurav Das62ae6792017-05-15 15:34:25 -0700262 public void retryHash(Link link, boolean linkDown, boolean firstTime) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700263 MacAddress neighborMac;
Charles Chan319d1a22015-11-03 10:42:14 -0800264 try {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700265 neighborMac = deviceConfig.getDeviceMac(link.dst().deviceId());
Charles Chan319d1a22015-11-03 10:42:14 -0800266 } catch (DeviceConfigNotFoundException e) {
Saurav Das62ae6792017-05-15 15:34:25 -0700267 log.warn(e.getMessage() + " Aborting retryHash.");
Charles Chan319d1a22015-11-03 10:42:14 -0800268 return;
269 }
Saurav Das261c3002017-06-13 15:35:54 -0700270 // find all the destinationSets related to link
271 Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet()
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700272 .stream()
Saurav Das261c3002017-06-13 15:35:54 -0700273 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Pier Luigiecb09f42018-01-14 21:56:11 +0100274 // Filter out PW transit groups or include them if MPLS ECMP is supported
Saurav Das97241862018-02-14 14:14:54 -0800275 .filter(entry -> !entry.getKey().destinationSet().notBos() ||
276 (entry.getKey().destinationSet().notBos() && srManager.getMplsEcmp()))
277 // Filter out simple SWAP groups or include them if MPLS ECMP is supported
278 .filter(entry -> !entry.getKey().destinationSet().swap() ||
279 (entry.getKey().destinationSet().swap() && srManager.getMplsEcmp()))
Saurav Das261c3002017-06-13 15:35:54 -0700280 .filter(entry -> entry.getValue().containsNextHop(link.dst().deviceId()))
281 .map(entry -> entry.getKey())
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700282 .collect(Collectors.toSet());
Saurav Das62ae6792017-05-15 15:34:25 -0700283
Saurav Das261c3002017-06-13 15:35:54 -0700284 log.debug("retryHash: dsNextObjStore contents for linkSrc {} -> linkDst {}: {}",
285 deviceId, link.dst().deviceId(), dsKeySet);
286
287 for (DestinationSetNextObjectiveStoreKey dsKey : dsKeySet) {
288 NextNeighbors nextHops = dsNextObjStore.get(dsKey);
289 if (nextHops == null) {
Saurav Das62ae6792017-05-15 15:34:25 -0700290 log.warn("retryHash in device {}, but global store has no record "
Saurav Das261c3002017-06-13 15:35:54 -0700291 + "for dsKey:{}", deviceId, dsKey);
Saurav Das62ae6792017-05-15 15:34:25 -0700292 continue;
293 }
Saurav Das261c3002017-06-13 15:35:54 -0700294 int nextId = nextHops.nextId();
295 Set<DeviceId> dstSet = nextHops.getDstForNextHop(link.dst().deviceId());
Saurav Das62ae6792017-05-15 15:34:25 -0700296 if (!linkDown) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700297 List<PortLabel> pl = Lists.newArrayList();
Saurav Das62ae6792017-05-15 15:34:25 -0700298 if (firstTime) {
299 // some links may have come up before the next-objective was created
300 // we take this opportunity to ensure other ports to same next-hop-dst
301 // are part of the hash group (see CORD-1180). Duplicate additions
302 // to the same hash group are avoided by the driver.
303 for (PortNumber p : devicePortMap.get(link.dst().deviceId())) {
Saurav Das261c3002017-06-13 15:35:54 -0700304 dstSet.forEach(dst -> {
305 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
Charles Chan223ae872018-08-22 17:56:47 -0700306 pl.add(new PortLabel(p, edgeLabel, popVlanInHashGroup(dsKey.destinationSet())));
Saurav Das261c3002017-06-13 15:35:54 -0700307 });
Saurav Das62ae6792017-05-15 15:34:25 -0700308 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700309 addToHashedNextObjective(pl, neighborMac, nextId);
310 } else {
311 // handle only the port that came up
312 dstSet.forEach(dst -> {
313 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
Charles Chan223ae872018-08-22 17:56:47 -0700314 pl.add(new PortLabel(link.src().port(), edgeLabel, popVlanInHashGroup(dsKey.destinationSet())));
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700315 });
316 addToHashedNextObjective(pl, neighborMac, nextId);
Saurav Das62ae6792017-05-15 15:34:25 -0700317 }
318 } else {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700319 // linkdown
320 List<PortLabel> pl = Lists.newArrayList();
Saurav Das261c3002017-06-13 15:35:54 -0700321 dstSet.forEach(dst -> {
322 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
Charles Chan223ae872018-08-22 17:56:47 -0700323 pl.add(new PortLabel(link.src().port(), edgeLabel, popVlanInHashGroup(dsKey.destinationSet())));
Saurav Das261c3002017-06-13 15:35:54 -0700324 });
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700325 removeFromHashedNextObjective(pl, neighborMac, nextId);
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700326 }
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700327 }
Saurav Das8a3022d2017-05-05 17:01:08 -0700328 }
329
Saurav Das62ae6792017-05-15 15:34:25 -0700330 /**
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700331 * Utility class for associating output ports and the corresponding MPLS
332 * labels to push. In dual-homing, there are different labels to push
333 * corresponding to the destination switches in an edge-pair. If both
334 * destinations are reachable via the same spine, then the output-port to
335 * the spine will be associated with two labels i.e. there will be two
336 * PortLabel objects for the same port but with different labels.
337 */
338 private class PortLabel {
339 PortNumber port;
340 int edgeLabel;
Charles Chan223ae872018-08-22 17:56:47 -0700341 boolean popVlan;
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700342
Charles Chan223ae872018-08-22 17:56:47 -0700343 PortLabel(PortNumber port, int edgeLabel, boolean popVlan) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700344 this.port = port;
345 this.edgeLabel = edgeLabel;
Charles Chan223ae872018-08-22 17:56:47 -0700346 this.popVlan = popVlan;
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700347 }
348
349 @Override
350 public String toString() {
Charles Chan223ae872018-08-22 17:56:47 -0700351 return port.toString() + "/" + String.valueOf(edgeLabel) + (popVlan ? "/popVlan" : "");
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700352 }
353 }
354
355 /**
356 * Makes a call to the FlowObjective service to add buckets to
357 * a hashed group. User must ensure that all the ports & labels are meant
358 * same neighbor (ie. dstMac).
Saurav Das62ae6792017-05-15 15:34:25 -0700359 *
Pier Luigiecb09f42018-01-14 21:56:11 +0100360 * @param portLabels a collection of port & label combinations to add
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700361 * to the hash group identified by the nextId
Saurav Das62ae6792017-05-15 15:34:25 -0700362 * @param dstMac destination mac address of next-hop
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700363 * @param nextId id for next-objective to which buckets will be added
Saurav Dasfbe74572017-08-03 18:30:35 -0700364 *
Saurav Das62ae6792017-05-15 15:34:25 -0700365 */
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700366 private void addToHashedNextObjective(Collection<PortLabel> portLabels,
367 MacAddress dstMac, Integer nextId) {
Saurav Das8a3022d2017-05-05 17:01:08 -0700368 // setup metadata to pass to nextObjective - indicate the vlan on egress
369 // if needed by the switch pipeline. Since hashed next-hops are always to
370 // other neighboring routers, there is no subnet assigned on those ports.
371 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
Saurav Das9bf49582018-08-13 15:34:26 -0700372 metabuilder.matchVlanId(srManager.getDefaultInternalVlan());
Saurav Das8a3022d2017-05-05 17:01:08 -0700373 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
374 .withId(nextId)
375 .withType(NextObjective.Type.HASHED)
Saurav Das8a3022d2017-05-05 17:01:08 -0700376 .withMeta(metabuilder.build())
377 .fromApp(appId);
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700378 // Create the new buckets to be updated
379 portLabels.forEach(pl -> {
380 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
381 tBuilder.setOutput(pl.port)
382 .setEthDst(dstMac)
383 .setEthSrc(nodeMacAddr);
Charles Chan223ae872018-08-22 17:56:47 -0700384 if (pl.popVlan) {
385 tBuilder.popVlan();
386 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700387 if (pl.edgeLabel != DestinationSet.NO_EDGE_LABEL) {
388 tBuilder.pushMpls()
389 .copyTtlOut()
390 .setMpls(MplsLabel.mplsLabel(pl.edgeLabel));
391 }
392 nextObjBuilder.addTreatment(tBuilder.build());
393 });
394
395 log.debug("addToHash in device {}: Adding Bucket with port/label {} "
396 + "to nextId {}", deviceId, portLabels, nextId);
Saurav Das8a3022d2017-05-05 17:01:08 -0700397
398 ObjectiveContext context = new DefaultObjectiveContext(
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700399 (objective) -> log.debug("addToHash port/label {} addedTo "
400 + "NextObj {} on {}", portLabels, nextId, deviceId),
Charles Chanfacfbef2018-08-23 14:30:33 -0700401 (objective, error) -> {
402 log.warn("addToHash failed to add port/label {} to NextObj {} on {}: {}",
403 portLabels, nextId, deviceId, error);
404 srManager.invalidateNextObj(objective.id());
405 });
Saurav Das8a3022d2017-05-05 17:01:08 -0700406 NextObjective nextObjective = nextObjBuilder.addToExisting(context);
407 flowObjectiveService.next(deviceId, nextObjective);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700408 }
409
410 /**
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700411 * Makes a call to the FlowObjective service to remove buckets from
412 * a hash group. User must ensure that all the ports & labels are meant
413 * same neighbor (ie. dstMac).
Saurav Dasfbe74572017-08-03 18:30:35 -0700414 *
Pier Luigiecb09f42018-01-14 21:56:11 +0100415 * @param portLabels a collection of port & label combinations to remove
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700416 * from the hash group identified by the nextId
Saurav Dasfbe74572017-08-03 18:30:35 -0700417 * @param dstMac destination mac address of next-hop
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700418 * @param nextId id for next-objective from which buckets will be removed
Saurav Dasfbe74572017-08-03 18:30:35 -0700419 */
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700420 private void removeFromHashedNextObjective(Collection<PortLabel> portLabels,
421 MacAddress dstMac, Integer nextId) {
Charles Chan8471a8d2018-08-30 19:49:23 -0700422 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
423 metabuilder.matchVlanId(srManager.getDefaultInternalVlan());
Saurav Dasfbe74572017-08-03 18:30:35 -0700424 NextObjective.Builder nextObjBuilder = DefaultNextObjective
425 .builder()
426 .withType(NextObjective.Type.HASHED) //same as original
Charles Chan8471a8d2018-08-30 19:49:23 -0700427 .withMeta(metabuilder.build())
Saurav Dasfbe74572017-08-03 18:30:35 -0700428 .withId(nextId)
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700429 .fromApp(appId);
430 // Create the buckets to be removed
431 portLabels.forEach(pl -> {
432 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
433 tBuilder.setOutput(pl.port)
434 .setEthDst(dstMac)
435 .setEthSrc(nodeMacAddr);
Charles Chan223ae872018-08-22 17:56:47 -0700436 if (pl.popVlan) {
437 tBuilder.popVlan();
438 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700439 if (pl.edgeLabel != DestinationSet.NO_EDGE_LABEL) {
440 tBuilder.pushMpls()
441 .copyTtlOut()
442 .setMpls(MplsLabel.mplsLabel(pl.edgeLabel));
443 }
444 nextObjBuilder.addTreatment(tBuilder.build());
445 });
446 log.debug("removeFromHash in device {}: Removing Bucket with port/label"
447 + " {} from nextId {}", deviceId, portLabels, nextId);
Saurav Das62ae6792017-05-15 15:34:25 -0700448
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700449 ObjectiveContext context = new DefaultObjectiveContext(
450 (objective) -> log.debug("port/label {} removedFrom NextObj"
451 + " {} on {}", portLabels, nextId, deviceId),
Charles Chanfacfbef2018-08-23 14:30:33 -0700452 (objective, error) -> {
453 log.warn("port/label {} failed to removeFrom NextObj {} on {}: {}",
454 portLabels, nextId, deviceId, error);
455 srManager.invalidateNextObj(objective.id());
456 });
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700457 NextObjective nextObjective = nextObjBuilder.removeFromExisting(context);
Saurav Dasfbe74572017-08-03 18:30:35 -0700458 flowObjectiveService.next(deviceId, nextObjective);
459 }
Saurav Das62ae6792017-05-15 15:34:25 -0700460
461 /**
462 * Checks all the hash-groups in the target-switch meant for the destination
463 * switch, and either adds or removes buckets to make the neighbor-set
pierventre37dcf4c2021-09-16 18:43:06 +0200464 * match the given next-hops. Typically called by the instance leading the programming
465 * of the destination switch, which may be different from the instance leading the
466 * programming of the target switch where hash-group changes are made.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700467 *
Saurav Das62ae6792017-05-15 15:34:25 -0700468 * @param targetSw the switch in which the hash groups will be edited
469 * @param nextHops the current next hops for the target switch to reach
470 * the dest sw
471 * @param destSw the destination switch
472 * @param revoke true if hash groups need to remove buckets from the
473 * the groups to match the current next hops
474 * @return true if calls are made to edit buckets, or if no edits are required
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700475 */
Saurav Das62ae6792017-05-15 15:34:25 -0700476 public boolean fixHashGroups(DeviceId targetSw, Set<DeviceId> nextHops,
477 DeviceId destSw, boolean revoke) {
478 // temporary storage of keys to be updated
Saurav Das261c3002017-06-13 15:35:54 -0700479 Map<DestinationSetNextObjectiveStoreKey, Set<DeviceId>> tempStore =
Saurav Das62ae6792017-05-15 15:34:25 -0700480 new HashMap<>();
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700481 boolean foundNextObjective = false, success = true;
Charles Chan319d1a22015-11-03 10:42:14 -0800482
Saurav Das261c3002017-06-13 15:35:54 -0700483 // retrieve hash-groups meant for destSw, which have destinationSets
Saurav Das62ae6792017-05-15 15:34:25 -0700484 // with different neighbors than the given next-hops
Saurav Das261c3002017-06-13 15:35:54 -0700485 for (DestinationSetNextObjectiveStoreKey dskey : dsNextObjStore.keySet()) {
486 if (!dskey.deviceId().equals(targetSw) ||
487 !dskey.destinationSet().getDestinationSwitches().contains(destSw)) {
Saurav Das62ae6792017-05-15 15:34:25 -0700488 continue;
489 }
490 foundNextObjective = true;
Saurav Das261c3002017-06-13 15:35:54 -0700491 NextNeighbors nhops = dsNextObjStore.get(dskey);
492 Set<DeviceId> currNeighbors = nhops.nextHops(destSw);
493 int edgeLabel = dskey.destinationSet().getEdgeLabel(destSw);
494 Integer nextId = nhops.nextId();
Saurav Dascea556f2018-03-05 14:37:16 -0800495 if (currNeighbors == null || nextHops == null) {
496 log.warn("fixing hash groups but found currNeighbors:{} or nextHops:{}"
497 + " in targetSw:{} for dstSw:{}", currNeighbors,
498 nextHops, targetSw, destSw);
499 success &= false;
500 continue;
501 }
Charles Chan319d1a22015-11-03 10:42:14 -0800502
Saurav Das97241862018-02-14 14:14:54 -0800503 // some store elements may not be hashed next-objectives - ignore them
504 if (isSimpleNextObjective(dskey)) {
505 log.debug("Ignoring {} of SIMPLE nextObj for targetSw:{}"
506 + " -> dstSw:{} with current nextHops:{} to new"
507 + " nextHops: {} in nextId:{}",
508 (revoke) ? "removal" : "addition", targetSw, destSw,
509 currNeighbors, nextHops, nextId);
Saurav Dascea556f2018-03-05 14:37:16 -0800510 if ((revoke && !nextHops.isEmpty())
511 || (!revoke && !nextHops.equals(currNeighbors))) {
Saurav Das68e1b6a2018-06-11 17:02:31 -0700512 log.debug("Simple next objective cannot be edited to "
Saurav Das97241862018-02-14 14:14:54 -0800513 + "move from {} to {}", currNeighbors, nextHops);
514 }
515 continue;
516 }
517
Saurav Das62ae6792017-05-15 15:34:25 -0700518 Set<DeviceId> diff;
519 if (revoke) {
520 diff = Sets.difference(currNeighbors, nextHops);
521 log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next "
522 + "hops:{} ..removing {}", targetSw, destSw, nextId,
523 currNeighbors, diff);
524 } else {
525 diff = Sets.difference(nextHops, currNeighbors);
526 log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next "
527 + "hops:{} ..adding {}", targetSw, destSw, nextId,
528 currNeighbors, diff);
529 }
Charles Chan223ae872018-08-22 17:56:47 -0700530 boolean suc = updateAllPortsToNextHop(diff, edgeLabel, nextId, popVlanInHashGroup(dskey.destinationSet()),
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700531 revoke);
532 if (suc) {
533 // to update neighbor set with changes made
Saurav Das62ae6792017-05-15 15:34:25 -0700534 if (revoke) {
Saurav Das261c3002017-06-13 15:35:54 -0700535 tempStore.put(dskey, Sets.difference(currNeighbors, diff));
Saurav Das62ae6792017-05-15 15:34:25 -0700536 } else {
Saurav Das261c3002017-06-13 15:35:54 -0700537 tempStore.put(dskey, Sets.union(currNeighbors, diff));
Saurav Das62ae6792017-05-15 15:34:25 -0700538 }
sangho2165d222015-05-01 09:38:25 -0700539 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700540 success &= suc;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700541 }
542
Saurav Das62ae6792017-05-15 15:34:25 -0700543 if (!foundNextObjective) {
544 log.debug("Cannot find any nextObjectives for route targetSw:{} "
545 + "-> dstSw:{}", targetSw, destSw);
pier572d4a92019-04-25 18:51:51 +0200546 return false; // nothing to do, return false so re-route will be performed
Saurav Das62ae6792017-05-15 15:34:25 -0700547 }
548
Saurav Das261c3002017-06-13 15:35:54 -0700549 // update the dsNextObjectiveStore with new destinationSet to nextId mappings
550 for (DestinationSetNextObjectiveStoreKey key : tempStore.keySet()) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700551 NextNeighbors currentNextHops = dsNextObjStore.get(key);
552 if (currentNextHops == null) {
553 log.warn("fixHashGroups could not update global store in "
554 + "device {} .. missing nextNeighbors for key {}",
555 deviceId, key);
Saurav Das62ae6792017-05-15 15:34:25 -0700556 continue;
557 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700558 Set<DeviceId> newNeighbors = new HashSet<>();
559 newNeighbors.addAll(tempStore.get(key));
560 Map<DeviceId, Set<DeviceId>> oldDstNextHops =
561 ImmutableMap.copyOf(currentNextHops.dstNextHops());
562 currentNextHops.dstNextHops().put(destSw, newNeighbors); //local change
563 log.debug("Updating nsNextObjStore target:{} -> dst:{} in key:{} nextId:{}",
564 targetSw, destSw, key, currentNextHops.nextId());
565 log.debug("Old dstNextHops: {}", oldDstNextHops);
566 log.debug("New dstNextHops: {}", currentNextHops.dstNextHops());
567 // update global store
568 dsNextObjStore.put(key,
569 new NextNeighbors(currentNextHops.dstNextHops(),
570 currentNextHops.nextId()));
Saurav Das62ae6792017-05-15 15:34:25 -0700571 }
Saurav Das97241862018-02-14 14:14:54 -0800572
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700573 // even if one fails and others succeed, return false so ECMPspg not updated
574 return success;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700575 }
576
Saurav Dasfbe74572017-08-03 18:30:35 -0700577 /**
578 * Updates the DestinationSetNextObjectiveStore with any per-destination nexthops
579 * that are not already in the store for the given DestinationSet. Note that
580 * this method does not remove existing next hops for the destinations in the
581 * DestinationSet.
582 *
583 * @param ds the DestinationSet for which the next hops need to be updated
584 * @param newDstNextHops a map of per-destination next hops to update the
585 * destinationSet with
586 * @return true if successful in updating all next hops
587 */
588 private boolean updateNextHops(DestinationSet ds,
Saurav Das261c3002017-06-13 15:35:54 -0700589 Map<DeviceId, Set<DeviceId>> newDstNextHops) {
590 DestinationSetNextObjectiveStoreKey key =
591 new DestinationSetNextObjectiveStoreKey(deviceId, ds);
592 NextNeighbors currNext = dsNextObjStore.get(key);
593 Map<DeviceId, Set<DeviceId>> currDstNextHops = currNext.dstNextHops();
594
595 // add newDstNextHops to currDstNextHops for each dst
596 boolean success = true;
597 for (DeviceId dstSw : ds.getDestinationSwitches()) {
598 Set<DeviceId> currNhops = currDstNextHops.get(dstSw);
599 Set<DeviceId> newNhops = newDstNextHops.get(dstSw);
600 currNhops = (currNhops == null) ? Sets.newHashSet() : currNhops;
601 newNhops = (newNhops == null) ? Sets.newHashSet() : newNhops;
602 int edgeLabel = ds.getEdgeLabel(dstSw);
603 int nextId = currNext.nextId();
604
605 // new next hops should be added
606 boolean suc = updateAllPortsToNextHop(Sets.difference(newNhops, currNhops),
Charles Chan223ae872018-08-22 17:56:47 -0700607 edgeLabel, nextId, popVlanInHashGroup(key.destinationSet()), false);
Saurav Das261c3002017-06-13 15:35:54 -0700608 if (suc) {
609 currNhops.addAll(newNhops);
610 currDstNextHops.put(dstSw, currNhops); // this is only a local change
611 }
612 success &= suc;
613 }
614
615 if (success) {
616 // update global store
617 dsNextObjStore.put(key, new NextNeighbors(currDstNextHops,
618 currNext.nextId()));
619 log.debug("Updated device:{} ds:{} new next-hops: {}", deviceId, ds,
620 dsNextObjStore.get(key));
621 }
622 return success;
623 }
624
Saurav Dasfbe74572017-08-03 18:30:35 -0700625 /**
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700626 * Adds or removes buckets for all ports to a set of neighbor devices. Caller
627 * needs to ensure that the given neighbors are all next hops towards the
628 * same destination (represented by the given edgeLabel).
Saurav Dasfbe74572017-08-03 18:30:35 -0700629 *
630 * @param neighbors set of neighbor device ids
631 * @param edgeLabel MPLS label to use in buckets
632 * @param nextId the nextObjective to change
Charles Chan223ae872018-08-22 17:56:47 -0700633 * @param popVlan this hash group bucket shuold includes a popVlan action
Saurav Dasfbe74572017-08-03 18:30:35 -0700634 * @param revoke true if buckets need to be removed, false if they need to
635 * be added
636 * @return true if successful in adding or removing buckets for all ports
637 * to the neighbors
638 */
639 private boolean updateAllPortsToNextHop(Set<DeviceId> neighbors, int edgeLabel,
Charles Chan223ae872018-08-22 17:56:47 -0700640 int nextId, boolean popVlan, boolean revoke) {
Saurav Dasfbe74572017-08-03 18:30:35 -0700641 for (DeviceId neighbor : neighbors) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700642 MacAddress neighborMac;
Saurav Das261c3002017-06-13 15:35:54 -0700643 try {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700644 neighborMac = deviceConfig.getDeviceMac(neighbor);
Saurav Das261c3002017-06-13 15:35:54 -0700645 } catch (DeviceConfigNotFoundException e) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700646 log.warn(e.getMessage() + " Aborting updateAllPortsToNextHop"
647 + " for nextId:" + nextId);
Saurav Das261c3002017-06-13 15:35:54 -0700648 return false;
649 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700650 Collection<PortNumber> portsToNeighbor = devicePortMap.get(neighbor);
651 if (portsToNeighbor == null || portsToNeighbor.isEmpty()) {
Saurav Das261c3002017-06-13 15:35:54 -0700652 log.warn("No ports found in dev:{} for neighbor:{} .. cannot "
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700653 + "updateAllPortsToNextHop for nextId: {}",
Saurav Das261c3002017-06-13 15:35:54 -0700654 deviceId, neighbor, nextId);
655 return false;
656 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700657 List<PortLabel> pl = Lists.newArrayList();
Charles Chan223ae872018-08-22 17:56:47 -0700658 portsToNeighbor.forEach(p -> pl.add(new PortLabel(p, edgeLabel, popVlan)));
Saurav Das261c3002017-06-13 15:35:54 -0700659 if (revoke) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700660 log.debug("updateAllPortsToNextHops in device {}: Removing Bucket(s) "
661 + "with Port/Label:{} to next object id {}",
662 deviceId, pl, nextId);
663 removeFromHashedNextObjective(pl, neighborMac, nextId);
Saurav Das261c3002017-06-13 15:35:54 -0700664 } else {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700665 log.debug("fixHashGroup in device {}: Adding Bucket(s) "
666 + "with Port/Label: {} to next object id {}",
667 deviceId, pl, nextId);
668 addToHashedNextObjective(pl, neighborMac, nextId);
Saurav Das261c3002017-06-13 15:35:54 -0700669 }
670 }
671 return true;
672 }
673
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700674 /**
Saurav Das97241862018-02-14 14:14:54 -0800675 * Returns true if the destination set is meant for swap or multi-labeled
676 * packet transport, and MPLS ECMP is not supported.
677 *
678 * @param dskey the key representing the destination set
679 * @return true if destination set is meant for simple next objectives
680 */
681 boolean isSimpleNextObjective(DestinationSetNextObjectiveStoreKey dskey) {
682 return (dskey.destinationSet().notBos() || dskey.destinationSet().swap())
683 && !srManager.getMplsEcmp();
684 }
685
686 /**
Saurav Das3fb28272017-03-04 16:08:47 -0800687 * Adds or removes a port that has been configured with a vlan to a broadcast group
pierventre37dcf4c2021-09-16 18:43:06 +0200688 * for bridging. Should only be called by the instance leading the programming
689 * for this device.
Saurav Dasf0f592d2016-11-18 15:21:57 -0800690 *
691 * @param port the port on this device that needs to be added/removed to a bcast group
Saurav Das3fb28272017-03-04 16:08:47 -0800692 * @param vlanId the vlan id corresponding to the broadcast domain/group
693 * @param popVlan indicates if packets should be sent out untagged or not out
694 * of the port. If true, indicates an access (untagged) or native vlan
695 * configuration. If false, indicates a trunk (tagged) vlan config.
Saurav Dasf0f592d2016-11-18 15:21:57 -0800696 * @param portUp true if port is enabled, false if disabled
Saurav Dasf0f592d2016-11-18 15:21:57 -0800697 */
Saurav Das3fb28272017-03-04 16:08:47 -0800698 public void processEdgePort(PortNumber port, VlanId vlanId,
699 boolean popVlan, boolean portUp) {
Saurav Dasf0f592d2016-11-18 15:21:57 -0800700 //get the next id for the subnet and edit it.
Charles Chan10b0fb72017-02-02 16:20:42 -0800701 Integer nextId = getVlanNextObjectiveId(vlanId);
Saurav Dasf0f592d2016-11-18 15:21:57 -0800702 if (nextId == -1) {
703 if (portUp) {
704 log.debug("**Creating flooding group for first port enabled in"
Saurav Das2b6a00f2017-12-05 15:00:23 -0800705 + " vlan {} on dev {} port {}", vlanId, deviceId, port);
Charles Chan10b0fb72017-02-02 16:20:42 -0800706 createBcastGroupFromVlan(vlanId, Collections.singleton(port));
Saurav Dasf0f592d2016-11-18 15:21:57 -0800707 } else {
708 log.warn("Could not find flooding group for subnet {} on dev:{} when"
Charles Chan10b0fb72017-02-02 16:20:42 -0800709 + " removing port:{}", vlanId, deviceId, port);
Saurav Dasf0f592d2016-11-18 15:21:57 -0800710 }
711 return;
712 }
713
714 log.info("**port{} in device {}: {} Bucket with Port {} to"
715 + " next-id {}", (portUp) ? "UP" : "DOWN", deviceId,
716 (portUp) ? "Adding" : "Removing",
717 port, nextId);
718 // Create the bucket to be added or removed
719 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Saurav Das3fb28272017-03-04 16:08:47 -0800720 if (popVlan) {
721 tBuilder.popVlan();
722 }
Saurav Dasf0f592d2016-11-18 15:21:57 -0800723 tBuilder.setOutput(port);
724
Saurav Dasf0f592d2016-11-18 15:21:57 -0800725 TrafficSelector metadata =
Saurav Das3fb28272017-03-04 16:08:47 -0800726 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
Saurav Dasf0f592d2016-11-18 15:21:57 -0800727
728 NextObjective.Builder nextObjBuilder = DefaultNextObjective
729 .builder().withId(nextId)
730 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
731 .addTreatment(tBuilder.build())
732 .withMeta(metadata);
733
734 ObjectiveContext context = new DefaultObjectiveContext(
735 (objective) -> log.debug("port {} successfully {} NextObj {} on {}",
736 port, (portUp) ? "addedTo" : "removedFrom",
737 nextId, deviceId),
Charles Chanfacfbef2018-08-23 14:30:33 -0700738 (objective, error) -> {
739 log.warn("port {} failed to {} NextObj {} on {}: {}",
740 port, (portUp) ? "addTo" : "removeFrom", nextId, deviceId, error);
741 srManager.invalidateNextObj(objective.id());
742 });
Saurav Dasf0f592d2016-11-18 15:21:57 -0800743
744 NextObjective nextObj = (portUp) ? nextObjBuilder.addToExisting(context)
745 : nextObjBuilder.removeFromExisting(context);
746 log.debug("edgePort processed: Submited next objective {} in device {}",
747 nextId, deviceId);
748 flowObjectiveService.next(deviceId, nextObj);
749 }
750
751 /**
Saurav Das97241862018-02-14 14:14:54 -0800752 * Returns the next objective of type hashed (or simple) associated with the
753 * destination set. In addition, updates the existing next-objective if new
754 * route-paths found have resulted in the addition of new next-hops to a
755 * particular destination. If there is no existing next objective for this
756 * destination set, this method would create a next objective and return the
757 * nextId. Optionally metadata can be passed in for the creation of the next
758 * objective. If the parameter simple is true then a simple next objective
759 * is created instead of a hashed one.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700760 *
Saurav Das261c3002017-06-13 15:35:54 -0700761 * @param ds destination set
762 * @param nextHops a map of per destination next hops
Saurav Das4c35fc42015-11-20 15:27:53 -0800763 * @param meta metadata passed into the creation of a Next Objective
Saurav Das97241862018-02-14 14:14:54 -0800764 * @param simple if true, a simple next objective will be created instead of
765 * a hashed next objective
Saurav Das4c35fc42015-11-20 15:27:53 -0800766 * @return int if found or -1 if there are errors in the creation of the
Saurav Das97241862018-02-14 14:14:54 -0800767 * neighbor set.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700768 */
Saurav Das261c3002017-06-13 15:35:54 -0700769 public int getNextObjectiveId(DestinationSet ds,
770 Map<DeviceId, Set<DeviceId>> nextHops,
Saurav Das97241862018-02-14 14:14:54 -0800771 TrafficSelector meta, boolean simple) {
Saurav Das261c3002017-06-13 15:35:54 -0700772 NextNeighbors next = dsNextObjStore.
773 get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
774 if (next == null) {
775 log.debug("getNextObjectiveId in device{}: Next objective id "
776 + "not found for {} ... creating", deviceId, ds);
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700777 log.trace("getNextObjectiveId: nsNextObjStore contents for device {}: {}",
778 deviceId,
Saurav Das261c3002017-06-13 15:35:54 -0700779 dsNextObjStore.entrySet()
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700780 .stream()
781 .filter((nsStoreEntry) ->
782 (nsStoreEntry.getKey().deviceId().equals(deviceId)))
783 .collect(Collectors.toList()));
Saurav Das261c3002017-06-13 15:35:54 -0700784
Saurav Das97241862018-02-14 14:14:54 -0800785 createGroupFromDestinationSet(ds, nextHops, meta, simple);
Saurav Das261c3002017-06-13 15:35:54 -0700786 next = dsNextObjStore.
787 get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
788 if (next == null) {
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700789 log.warn("getNextObjectiveId: unable to create next objective");
Saurav Das261c3002017-06-13 15:35:54 -0700790 // failure in creating group
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700791 return -1;
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700792 } else {
793 log.debug("getNextObjectiveId in device{}: Next objective id {} "
Saurav Das261c3002017-06-13 15:35:54 -0700794 + "created for {}", deviceId, next.nextId(), ds);
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700795 }
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700796 } else {
797 log.trace("getNextObjectiveId in device{}: Next objective id {} "
Saurav Das261c3002017-06-13 15:35:54 -0700798 + "found for {}", deviceId, next.nextId(), ds);
799 // should fix hash groups too if next-hops have changed
800 if (!next.dstNextHops().equals(nextHops)) {
801 log.debug("Nexthops have changed for dev:{} nextId:{} ..updating",
802 deviceId, next.nextId());
803 if (!updateNextHops(ds, nextHops)) {
804 // failure in updating group
805 return -1;
806 }
807 }
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700808 }
Saurav Das261c3002017-06-13 15:35:54 -0700809 return next.nextId();
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700810 }
811
sangho4a5c42a2015-05-20 22:16:38 -0700812 /**
Charles Chan10b0fb72017-02-02 16:20:42 -0800813 * Returns the next objective of type broadcast associated with the vlan,
Saurav Das2d94d312015-11-24 23:21:05 -0800814 * or -1 if no such objective exists. Note that this method does NOT create
815 * the next objective as a side-effect. It is expected that is objective is
Saurav Dasf0f592d2016-11-18 15:21:57 -0800816 * created at startup from network configuration. Typically this is used
817 * for L2 flooding within the subnet configured on the switch.
Charles Chan77277672015-10-20 16:24:19 -0700818 *
Charles Chan10b0fb72017-02-02 16:20:42 -0800819 * @param vlanId vlan id
Charles Chan77277672015-10-20 16:24:19 -0700820 * @return int if found or -1
821 */
Charles Chan10b0fb72017-02-02 16:20:42 -0800822 public int getVlanNextObjectiveId(VlanId vlanId) {
823 Integer nextId = vlanNextObjStore.
824 get(new VlanNextObjectiveStoreKey(deviceId, vlanId));
Charles Chanc6ad7752015-10-29 14:58:10 -0700825
826 return (nextId != null) ? nextId : -1;
Charles Chan77277672015-10-20 16:24:19 -0700827 }
828
829 /**
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000830 * Returns the next objective of type simple associated with the mac/vlan on the
831 * device, given the treatment. Different treatments to the same mac/vlan result
832 * in different next objectives. If no such objective exists, this method
833 * creates one (if requested) and returns the id. Optionally metadata can be passed in for
834 * the creation of the objective. Typically this is used for L2 and L3 forwarding
835 * to compute nodes and containers/VMs on the compute nodes directly attached
836 * to the switch.
837 *
838 * @param macAddr the mac addr for the simple next objective
839 * @param vlanId the vlan for the simple next objective
Ruchi Sahota07869322019-05-09 17:26:14 -0400840 * @param port port with which to create the Next Obj.
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000841 * @param createIfMissing true if a next object should be created if not found
842 * @return int if found or created, -1 if there are errors during the
843 * creation of the next objective.
844 */
Ruchi Sahota07869322019-05-09 17:26:14 -0400845 public int getMacVlanNextObjectiveId(MacAddress macAddr, VlanId vlanId, PortNumber port,
846 boolean createIfMissing) {
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000847
848 Integer nextId = macVlanNextObjStore
849 .get(new MacVlanNextObjectiveStoreKey(deviceId, macAddr, vlanId));
850
851 if (nextId != null) {
852 return nextId;
853 }
854
855 log.debug("getMacVlanNextObjectiveId in device {}: Next objective id "
856 + "not found for host : {}/{} .. {}", deviceId, macAddr, vlanId,
857 (createIfMissing) ? "creating" : "aborting");
858
859 if (!createIfMissing) {
860 return -1;
861 }
862
Ruchi Sahota07869322019-05-09 17:26:14 -0400863 MacAddress deviceMac;
864 try {
865 deviceMac = deviceConfig.getDeviceMac(deviceId);
866 } catch (DeviceConfigNotFoundException e) {
867 log.warn(e.getMessage() + " in getMacVlanNextObjectiveId");
868 return -1;
869 }
870
871 // since we are creating now, port cannot be null
872 if (port == null) {
873 log.debug("getMacVlanNextObjectiveId : port information cannot be null "
874 + "for device {}, host {}/{}", deviceId, macAddr, vlanId);
875 return -1;
876 }
877
878 TrafficSelector.Builder meta = DefaultTrafficSelector.builder();
879
880 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
881 treatment.deferred()
882 .setEthDst(macAddr)
883 .setEthSrc(deviceMac)
884 .setOutput(port);
885
886 ConnectPoint connectPoint = new ConnectPoint(deviceId, port);
887 VlanId untaggedVlan = srManager.interfaceService.getUntaggedVlanId(connectPoint);
888 Set<VlanId> taggedVlans = srManager.interfaceService.getTaggedVlanId(connectPoint);
889 VlanId nativeVlan = srManager.interfaceService.getNativeVlanId(connectPoint);
890
891 // Adjust the meta according to VLAN configuration
892 if (taggedVlans.contains(vlanId)) {
pierventre50e1bb82020-12-14 19:31:03 +0100893 meta.matchVlanId(vlanId);
Ruchi Sahota07869322019-05-09 17:26:14 -0400894 treatment.setVlanId(vlanId);
895 } else if (vlanId.equals(VlanId.NONE)) {
896 if (untaggedVlan != null) {
897 meta.matchVlanId(untaggedVlan);
pierventre50e1bb82020-12-14 19:31:03 +0100898 treatment.popVlan();
Ruchi Sahota07869322019-05-09 17:26:14 -0400899 } else if (nativeVlan != null) {
900 meta.matchVlanId(nativeVlan);
pierventre50e1bb82020-12-14 19:31:03 +0100901 treatment.popVlan();
Ruchi Sahota07869322019-05-09 17:26:14 -0400902 } else {
903 log.warn("Untagged nexthop {}/{} is not allowed on {} without untagged or native vlan",
904 macAddr, vlanId, connectPoint);
905 return -1;
906 }
907 } else {
908 log.warn("Tagged nexthop {}/{} is not allowed on {} without VLAN listed"
909 + " in tagged vlan", macAddr, vlanId, connectPoint);
910 return -1;
911 }
912
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000913 /* create missing next objective */
Ruchi Sahota07869322019-05-09 17:26:14 -0400914 nextId = createGroupFromMacVlan(macAddr, vlanId, treatment.build(), meta.build());
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000915 if (nextId == null) {
916 log.warn("getMacVlanNextObjectiveId: unable to create next obj"
917 + "for dev:{} host:{}/{}", deviceId, macAddr, vlanId);
918 return -1;
919 }
920 return nextId;
921 }
922
923
924 /**
Saurav Das2d94d312015-11-24 23:21:05 -0800925 * Returns the next objective of type simple associated with the port on the
926 * device, given the treatment. Different treatments to the same port result
927 * in different next objectives. If no such objective exists, this method
Saurav Das2cb38292017-03-29 19:09:17 -0700928 * creates one (if requested) and returns the id. Optionally metadata can be passed in for
Saurav Dasf0f592d2016-11-18 15:21:57 -0800929 * the creation of the objective. Typically this is used for L2 and L3 forwarding
930 * to compute nodes and containers/VMs on the compute nodes directly attached
931 * to the switch.
Saurav Das2d94d312015-11-24 23:21:05 -0800932 *
933 * @param portNum the port number for the simple next objective
934 * @param treatment the actions to apply on the packets (should include outport)
935 * @param meta optional metadata passed into the creation of the next objective
Saurav Das2cb38292017-03-29 19:09:17 -0700936 * @param createIfMissing true if a next object should be created if not found
Saurav Das2d94d312015-11-24 23:21:05 -0800937 * @return int if found or created, -1 if there are errors during the
938 * creation of the next objective.
939 */
940 public int getPortNextObjectiveId(PortNumber portNum, TrafficTreatment treatment,
Saurav Das2cb38292017-03-29 19:09:17 -0700941 TrafficSelector meta, boolean createIfMissing) {
Charles Chanb7f75ac2016-01-11 18:28:54 -0800942 Integer nextId = portNextObjStore
Saurav Das368cf212017-03-15 15:15:14 -0700943 .get(new PortNextObjectiveStoreKey(deviceId, portNum, treatment, meta));
Saurav Das2cb38292017-03-29 19:09:17 -0700944 if (nextId != null) {
945 return nextId;
946 }
947 log.debug("getPortNextObjectiveId in device {}: Next objective id "
948 + "not found for port: {} .. {}", deviceId, portNum,
949 (createIfMissing) ? "creating" : "aborting");
950 if (!createIfMissing) {
951 return -1;
952 }
953 // create missing next objective
954 createGroupFromPort(portNum, treatment, meta);
955 nextId = portNextObjStore.get(new PortNextObjectiveStoreKey(deviceId, portNum,
956 treatment, meta));
Saurav Das2d94d312015-11-24 23:21:05 -0800957 if (nextId == null) {
Saurav Das2cb38292017-03-29 19:09:17 -0700958 log.warn("getPortNextObjectiveId: unable to create next obj"
959 + "for dev:{} port:{}", deviceId, portNum);
960 return -1;
Charles Chanb7f75ac2016-01-11 18:28:54 -0800961 }
962 return nextId;
963 }
964
965 /**
sangho4a5c42a2015-05-20 22:16:38 -0700966 * Checks if the next objective ID (group) for the neighbor set exists or not.
967 *
968 * @param ns neighbor set to check
969 * @return true if it exists, false otherwise
970 */
Saurav Das261c3002017-06-13 15:35:54 -0700971 public boolean hasNextObjectiveId(DestinationSet ns) {
972 NextNeighbors nextHops = dsNextObjStore.
973 get(new DestinationSetNextObjectiveStoreKey(deviceId, ns));
974 if (nextHops == null) {
sangho4a5c42a2015-05-20 22:16:38 -0700975 return false;
976 }
977
978 return true;
979 }
980
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700981 private void populateNeighborMaps() {
982 Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700983 for (Link link : outgoingLinks) {
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700984 if (link.type() != Link.Type.DIRECT) {
985 continue;
986 }
987 addNeighborAtPort(link.dst().deviceId(), link.src().port());
988 }
989 }
990
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700991 protected void addNeighborAtPort(DeviceId neighborId,
992 PortNumber portToNeighbor) {
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700993 // Update DeviceToPort database
994 log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
995 deviceId, neighborId, portToNeighbor);
Saurav Das4c35fc42015-11-20 15:27:53 -0800996 Set<PortNumber> ports = Collections
997 .newSetFromMap(new ConcurrentHashMap<PortNumber, Boolean>());
998 ports.add(portToNeighbor);
999 Set<PortNumber> portnums = devicePortMap.putIfAbsent(neighborId, ports);
1000 if (portnums != null) {
1001 portnums.add(portToNeighbor);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -07001002 }
1003
1004 // Update portToDevice database
Saurav Das97241862018-02-14 14:14:54 -08001005 // should always update as neighbor could have changed on this port
1006 DeviceId prev = portDeviceMap.put(portToNeighbor, neighborId);
Saurav Das4c35fc42015-11-20 15:27:53 -08001007 if (prev != null) {
Saurav Dasdebcf882018-04-06 20:16:01 -07001008 log.warn("Device/port: {}/{} previous neighbor: {}, current neighbor: {} ",
Saurav Das62ae6792017-05-15 15:34:25 -07001009 deviceId, portToNeighbor, prev, neighborId);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -07001010 }
1011 }
1012
sangho27462c62015-05-14 00:39:53 -07001013 /**
Saurav Das261c3002017-06-13 15:35:54 -07001014 * Creates a NextObjective for a hash group in this device from a given
Saurav Das97241862018-02-14 14:14:54 -08001015 * DestinationSet. If the parameter simple is true, a simple next objective
1016 * is created instead.
sangho27462c62015-05-14 00:39:53 -07001017 *
Saurav Das261c3002017-06-13 15:35:54 -07001018 * @param ds the DestinationSet
1019 * @param neighbors a map for each destination and its next-hops
Saurav Das4c35fc42015-11-20 15:27:53 -08001020 * @param meta metadata passed into the creation of a Next Objective
Saurav Das97241862018-02-14 14:14:54 -08001021 * @param simple if true, a simple next objective will be created instead of
1022 * a hashed next objective
sangho27462c62015-05-14 00:39:53 -07001023 */
Saurav Das261c3002017-06-13 15:35:54 -07001024 public void createGroupFromDestinationSet(DestinationSet ds,
1025 Map<DeviceId, Set<DeviceId>> neighbors,
1026 TrafficSelector meta,
Saurav Das97241862018-02-14 14:14:54 -08001027 boolean simple) {
Saurav Das261c3002017-06-13 15:35:54 -07001028 int nextId = flowObjectiveService.allocateNextId();
Saurav Das97241862018-02-14 14:14:54 -08001029 NextObjective.Type type = (simple) ? NextObjective.Type.SIMPLE
1030 : NextObjective.Type.HASHED;
Saurav Das261c3002017-06-13 15:35:54 -07001031 if (neighbors == null || neighbors.isEmpty()) {
1032 log.warn("createGroupsFromDestinationSet: needs at least one neighbor"
1033 + "to create group in dev:{} for ds: {} with next-hops {}",
1034 deviceId, ds, neighbors);
1035 return;
1036 }
Saurav Das261c3002017-06-13 15:35:54 -07001037
1038 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1039 .builder()
1040 .withId(nextId)
1041 .withType(type)
1042 .fromApp(appId);
1043 if (meta != null) {
pierventre50e1bb82020-12-14 19:31:03 +01001044 // Udate the meta VLAN id to match the PW transport label
1045 if (!popVlanInHashGroup(ds)) {
1046 TrafficSelector newMeta = DefaultTrafficSelector.builder(meta)
1047 .matchVlanId(srManager.getPwTransportVlan())
1048 .build();
1049 meta = newMeta;
1050 }
Saurav Das261c3002017-06-13 15:35:54 -07001051 nextObjBuilder.withMeta(meta);
1052 }
1053
1054 // create treatment buckets for each neighbor for each dst Device
1055 // except in the special case where we only want to pick a single
Saurav Das97241862018-02-14 14:14:54 -08001056 // neighbor/port for a simple nextObj
Saurav Das261c3002017-06-13 15:35:54 -07001057 boolean foundSingleNeighbor = false;
1058 boolean treatmentAdded = false;
1059 Map<DeviceId, Set<DeviceId>> dstNextHops = new ConcurrentHashMap<>();
1060 for (DeviceId dst : ds.getDestinationSwitches()) {
1061 Set<DeviceId> nextHops = neighbors.get(dst);
1062 if (nextHops == null || nextHops.isEmpty()) {
1063 continue;
Pier Ventre229fd0b2016-10-31 16:49:19 -07001064 }
Saurav Das261c3002017-06-13 15:35:54 -07001065
1066 if (foundSingleNeighbor) {
1067 break;
1068 }
1069
1070 for (DeviceId neighborId : nextHops) {
Saurav Das4c35fc42015-11-20 15:27:53 -08001071 if (devicePortMap.get(neighborId) == null) {
1072 log.warn("Neighbor {} is not in the port map yet for dev:{}",
1073 neighborId, deviceId);
sangho2165d222015-05-01 09:38:25 -07001074 return;
Jon Hall31d84782017-01-18 20:15:44 -08001075 } else if (devicePortMap.get(neighborId).isEmpty()) {
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -07001076 log.warn("There are no ports for "
Saurav Das4c35fc42015-11-20 15:27:53 -08001077 + "the Device {} in the port map yet", neighborId);
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -07001078 return;
sangho2165d222015-05-01 09:38:25 -07001079 }
1080
Saurav Das4c35fc42015-11-20 15:27:53 -08001081 MacAddress neighborMac;
Charles Chan319d1a22015-11-03 10:42:14 -08001082 try {
Saurav Das4c35fc42015-11-20 15:27:53 -08001083 neighborMac = deviceConfig.getDeviceMac(neighborId);
Charles Chan319d1a22015-11-03 10:42:14 -08001084 } catch (DeviceConfigNotFoundException e) {
Saurav Das261c3002017-06-13 15:35:54 -07001085 log.warn(e.getMessage() + " Aborting createGroupsFromDestinationset.");
Charles Chan319d1a22015-11-03 10:42:14 -08001086 return;
1087 }
Saurav Das261c3002017-06-13 15:35:54 -07001088 // For each port to the neighbor, we create a new treatment
Pier Ventre229fd0b2016-10-31 16:49:19 -07001089 Set<PortNumber> neighborPorts = devicePortMap.get(neighborId);
Saurav Das97241862018-02-14 14:14:54 -08001090 // In this case we need a SIMPLE nextObj. We randomly pick a port
1091 if (simple) {
Pier Ventre229fd0b2016-10-31 16:49:19 -07001092 int size = devicePortMap.get(neighborId).size();
1093 int index = RandomUtils.nextInt(0, size);
1094 neighborPorts = Collections.singleton(
Saurav Das261c3002017-06-13 15:35:54 -07001095 Iterables.get(devicePortMap.get(neighborId),
1096 index));
1097 foundSingleNeighbor = true;
Pier Ventre229fd0b2016-10-31 16:49:19 -07001098 }
Andreas Pantelopoulos5e7be3d2017-10-23 12:18:25 -07001099
Pier Ventre229fd0b2016-10-31 16:49:19 -07001100 for (PortNumber sp : neighborPorts) {
Srikanth Vavilapalli64505482015-04-21 13:04:13 -07001101 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
1102 .builder();
Saurav Das261c3002017-06-13 15:35:54 -07001103 tBuilder.setEthDst(neighborMac).setEthSrc(nodeMacAddr);
1104 int edgeLabel = ds.getEdgeLabel(dst);
1105 if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
Saurav Das97241862018-02-14 14:14:54 -08001106 if (simple) {
1107 // swap label case
1108 tBuilder.setMpls(MplsLabel.mplsLabel(edgeLabel));
1109 } else {
1110 // ecmp with label push case
1111 tBuilder.pushMpls().copyTtlOut()
1112 .setMpls(MplsLabel.mplsLabel(edgeLabel));
1113 }
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -07001114 }
Charles Chan81ddf6f2018-08-12 15:19:01 -07001115
1116 // Set VLAN ID for PW transport. Otherwise pop vlan
Charles Chan223ae872018-08-22 17:56:47 -07001117 if (!popVlanInHashGroup(ds)) {
Saurav Das9bf49582018-08-13 15:34:26 -07001118 tBuilder.setVlanId(srManager.getPwTransportVlan());
Charles Chan81ddf6f2018-08-12 15:19:01 -07001119 } else {
1120 tBuilder.popVlan();
Andreas Pantelopoulosb281ae22018-05-01 14:56:05 -07001121 }
Charles Chan81ddf6f2018-08-12 15:19:01 -07001122
Saurav Das4c35fc42015-11-20 15:27:53 -08001123 tBuilder.setOutput(sp);
Srikanth Vavilapalli64505482015-04-21 13:04:13 -07001124 nextObjBuilder.addTreatment(tBuilder.build());
Saurav Das261c3002017-06-13 15:35:54 -07001125 treatmentAdded = true;
1126 //update store
1127 Set<DeviceId> existingNeighbors = dstNextHops.get(dst);
1128 if (existingNeighbors == null) {
1129 existingNeighbors = new HashSet<>();
1130 }
1131 existingNeighbors.add(neighborId);
1132 dstNextHops.put(dst, existingNeighbors);
1133 log.debug("creating treatment for port/label {}/{} in next:{}",
1134 sp, edgeLabel, nextId);
1135 }
1136
1137 if (foundSingleNeighbor) {
1138 break;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -07001139 }
1140 }
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -07001141 }
Saurav Das261c3002017-06-13 15:35:54 -07001142
1143 if (!treatmentAdded) {
1144 log.warn("Could not createGroup from DestinationSet {} without any"
1145 + "next hops {}", ds, neighbors);
1146 return;
1147 }
1148 ObjectiveContext context = new DefaultObjectiveContext(
1149 (objective) ->
1150 log.debug("createGroupsFromDestinationSet installed "
1151 + "NextObj {} on {}", nextId, deviceId),
Charles Chanfacfbef2018-08-23 14:30:33 -07001152 (objective, error) -> {
1153 log.warn("createGroupsFromDestinationSet failed to install NextObj {} on {}: {}",
1154 nextId, deviceId, error);
1155 srManager.invalidateNextObj(objective.id());
1156 });
Saurav Das261c3002017-06-13 15:35:54 -07001157 NextObjective nextObj = nextObjBuilder.add(context);
1158 log.debug(".. createGroupsFromDestinationSet: Submitted "
1159 + "next objective {} in device {}", nextId, deviceId);
1160 flowObjectiveService.next(deviceId, nextObj);
1161 //update store
1162 dsNextObjStore.put(new DestinationSetNextObjectiveStoreKey(deviceId, ds),
1163 new NextNeighbors(dstNextHops, nextId));
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -07001164 }
1165
Saurav Das2d94d312015-11-24 23:21:05 -08001166 /**
Saurav Dasf0f592d2016-11-18 15:21:57 -08001167 * Creates broadcast groups for all ports in the same subnet for
1168 * all configured subnets.
Saurav Das2d94d312015-11-24 23:21:05 -08001169 */
Charles Chan10b0fb72017-02-02 16:20:42 -08001170 public void createGroupsFromVlanConfig() {
Charles Chan90772a72017-02-08 15:52:08 -08001171 srManager.getVlanPortMap(deviceId).asMap().forEach((vlanId, ports) -> {
Charles Chan10b0fb72017-02-02 16:20:42 -08001172 createBcastGroupFromVlan(vlanId, ports);
Pier Ventreb6a7f342016-11-26 21:05:22 -08001173 });
Saurav Dasf0f592d2016-11-18 15:21:57 -08001174 }
Charles Chanc6ad7752015-10-29 14:58:10 -07001175
Saurav Dasf0f592d2016-11-18 15:21:57 -08001176 /**
Charles Chan10b0fb72017-02-02 16:20:42 -08001177 * Creates a single broadcast group from a given vlan id and list of ports.
Saurav Dasf0f592d2016-11-18 15:21:57 -08001178 *
Charles Chan10b0fb72017-02-02 16:20:42 -08001179 * @param vlanId vlan id
Saurav Dasf0f592d2016-11-18 15:21:57 -08001180 * @param ports list of ports in the subnet
1181 */
Charles Chan90772a72017-02-08 15:52:08 -08001182 public void createBcastGroupFromVlan(VlanId vlanId, Collection<PortNumber> ports) {
Charles Chan10b0fb72017-02-02 16:20:42 -08001183 VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
Charles Chanc6ad7752015-10-29 14:58:10 -07001184
Charles Chan10b0fb72017-02-02 16:20:42 -08001185 if (vlanNextObjStore.containsKey(key)) {
Saurav Dasf0f592d2016-11-18 15:21:57 -08001186 log.debug("Broadcast group for device {} and subnet {} exists",
Charles Chan10b0fb72017-02-02 16:20:42 -08001187 deviceId, vlanId);
Saurav Dasf0f592d2016-11-18 15:21:57 -08001188 return;
1189 }
Charles Chande6655c2015-12-23 00:15:11 -08001190
Saurav Dasf0f592d2016-11-18 15:21:57 -08001191 TrafficSelector metadata =
Charles Chan10b0fb72017-02-02 16:20:42 -08001192 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
Charles Chan77277672015-10-20 16:24:19 -07001193
Saurav Dasf0f592d2016-11-18 15:21:57 -08001194 int nextId = flowObjectiveService.allocateNextId();
Charles Chan77277672015-10-20 16:24:19 -07001195
Saurav Dasf0f592d2016-11-18 15:21:57 -08001196 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1197 .builder().withId(nextId)
1198 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1199 .withMeta(metadata);
Charles Chan77277672015-10-20 16:24:19 -07001200
Saurav Dasf0f592d2016-11-18 15:21:57 -08001201 ports.forEach(port -> {
1202 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Charles Chan90772a72017-02-08 15:52:08 -08001203 if (toPopVlan(port, vlanId)) {
1204 tBuilder.popVlan();
1205 }
Saurav Dasf0f592d2016-11-18 15:21:57 -08001206 tBuilder.setOutput(port);
1207 nextObjBuilder.addTreatment(tBuilder.build());
Charles Chan77277672015-10-20 16:24:19 -07001208 });
Saurav Dasf0f592d2016-11-18 15:21:57 -08001209
Saurav Das2cb38292017-03-29 19:09:17 -07001210 ObjectiveContext context = new DefaultObjectiveContext(
1211 (objective) ->
1212 log.debug("createBroadcastGroupFromVlan installed "
1213 + "NextObj {} on {}", nextId, deviceId),
Charles Chanfacfbef2018-08-23 14:30:33 -07001214 (objective, error) -> {
1215 log.warn("createBroadcastGroupFromVlan failed to install NextObj {} on {}: {}",
1216 nextId, deviceId, error);
1217 srManager.invalidateNextObj(objective.id());
1218 });
Saurav Das2cb38292017-03-29 19:09:17 -07001219 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Dasf0f592d2016-11-18 15:21:57 -08001220 flowObjectiveService.next(deviceId, nextObj);
Saurav Das2b6a00f2017-12-05 15:00:23 -08001221 log.debug("createBcastGroupFromVlan: Submitted next objective {} "
1222 + "for vlan: {} in device {}", nextId, vlanId, deviceId);
Saurav Dasf0f592d2016-11-18 15:21:57 -08001223
Charles Chan10b0fb72017-02-02 16:20:42 -08001224 vlanNextObjStore.put(key, nextId);
Charles Chan77277672015-10-20 16:24:19 -07001225 }
1226
Charles Chanb7f75ac2016-01-11 18:28:54 -08001227 /**
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001228 * Removes a single broadcast group from a given vlan id.
1229 * The group should be empty.
1230 * @param deviceId device Id to remove the group
1231 * @param portNum port number related to the group
1232 * @param vlanId vlan id of the broadcast group to remove
1233 * @param popVlan true if the TrafficTreatment involves pop vlan tag action
1234 */
1235 public void removeBcastGroupFromVlan(DeviceId deviceId, PortNumber portNum,
1236 VlanId vlanId, boolean popVlan) {
1237 VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
1238
1239 if (!vlanNextObjStore.containsKey(key)) {
1240 log.debug("Broadcast group for device {} and subnet {} does not exist",
1241 deviceId, vlanId);
1242 return;
1243 }
1244
1245 TrafficSelector metadata =
1246 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1247
1248 int nextId = vlanNextObjStore.get(key);
1249
1250 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1251 .builder().withId(nextId)
1252 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1253 .withMeta(metadata);
1254
1255 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1256 if (popVlan) {
1257 tBuilder.popVlan();
1258 }
1259 tBuilder.setOutput(portNum);
1260 nextObjBuilder.addTreatment(tBuilder.build());
1261
1262 ObjectiveContext context = new DefaultObjectiveContext(
1263 (objective) ->
1264 log.debug("removeBroadcastGroupFromVlan removed "
1265 + "NextObj {} on {}", nextId, deviceId),
Charles Chanfacfbef2018-08-23 14:30:33 -07001266 (objective, error) -> {
1267 log.warn("removeBroadcastGroupFromVlan failed to remove NextObj {} on {}: {}",
1268 nextId, deviceId, error);
1269 srManager.invalidateNextObj(objective.id());
1270 });
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001271 NextObjective nextObj = nextObjBuilder.remove(context);
1272 flowObjectiveService.next(deviceId, nextObj);
1273 log.debug("removeBcastGroupFromVlan: Submited next objective {} in device {}",
1274 nextId, deviceId);
1275
1276 vlanNextObjStore.remove(key, nextId);
1277 }
1278
1279 /**
Charles Chan90772a72017-02-08 15:52:08 -08001280 * Determine if we should pop given vlan before sending packets to the given port.
1281 *
1282 * @param portNumber port number
1283 * @param vlanId vlan id
1284 * @return true if the vlan id is not contained in any vlanTagged config
1285 */
1286 private boolean toPopVlan(PortNumber portNumber, VlanId vlanId) {
Saurav Das261c3002017-06-13 15:35:54 -07001287 return srManager.interfaceService
1288 .getInterfacesByPort(new ConnectPoint(deviceId, portNumber))
Charles Chan90772a72017-02-08 15:52:08 -08001289 .stream().noneMatch(intf -> intf.vlanTagged().contains(vlanId));
1290 }
1291
1292 /**
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +00001293 * Create simple next objective for an indirect host mac/vlan. The treatments can include
1294 * all outgoing actions that need to happen on the packet.
1295 *
1296 * @param macAddr the mac address of the host
1297 * @param vlanId the vlan of the host
1298 * @param treatment the actions to apply on the packets (should include outport)
1299 * @param meta optional data to pass to the driver
1300 * @return next objective ID
1301 */
1302 public int createGroupFromMacVlan(MacAddress macAddr, VlanId vlanId, TrafficTreatment treatment,
1303 TrafficSelector meta) {
1304 int nextId = flowObjectiveService.allocateNextId();
1305 MacVlanNextObjectiveStoreKey key = new MacVlanNextObjectiveStoreKey(deviceId, macAddr, vlanId);
1306
1307 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1308 .builder().withId(nextId)
1309 .withType(NextObjective.Type.SIMPLE)
1310 .addTreatment(treatment)
1311 .fromApp(appId)
1312 .withMeta(meta);
1313
1314 ObjectiveContext context = new DefaultObjectiveContext(
1315 (objective) ->
1316 log.debug("createGroupFromMacVlan installed "
1317 + "NextObj {} on {}", nextId, deviceId),
1318 (objective, error) -> {
1319 log.warn("createGroupFromMacVlan failed to install NextObj {} on {}: {}", nextId, deviceId, error);
1320 srManager.invalidateNextObj(objective.id());
1321 });
1322 NextObjective nextObj = nextObjBuilder.add(context);
1323 flowObjectiveService.next(deviceId, nextObj);
1324 log.debug("createGroupFromMacVlan: Submited next objective {} in device {} "
1325 + "for host {}/{}", nextId, deviceId, macAddr, vlanId);
1326
1327 macVlanNextObjStore.put(key, nextId);
1328 return nextId;
1329 }
1330
1331 /**
Saurav Das2d94d312015-11-24 23:21:05 -08001332 * Create simple next objective for a single port. The treatments can include
1333 * all outgoing actions that need to happen on the packet.
1334 *
1335 * @param portNum the outgoing port on the device
1336 * @param treatment the actions to apply on the packets (should include outport)
1337 * @param meta optional data to pass to the driver
1338 */
1339 public void createGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
1340 TrafficSelector meta) {
1341 int nextId = flowObjectiveService.allocateNextId();
1342 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
Saurav Das368cf212017-03-15 15:15:14 -07001343 deviceId, portNum, treatment, meta);
Saurav Das2d94d312015-11-24 23:21:05 -08001344
1345 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1346 .builder().withId(nextId)
1347 .withType(NextObjective.Type.SIMPLE)
1348 .addTreatment(treatment)
1349 .fromApp(appId)
1350 .withMeta(meta);
1351
Saurav Das2cb38292017-03-29 19:09:17 -07001352 ObjectiveContext context = new DefaultObjectiveContext(
1353 (objective) ->
1354 log.debug("createGroupFromPort installed "
1355 + "NextObj {} on {}", nextId, deviceId),
Charles Chanfacfbef2018-08-23 14:30:33 -07001356 (objective, error) -> {
1357 log.warn("createGroupFromPort failed to install NextObj {} on {}: {}", nextId, deviceId, error);
1358 srManager.invalidateNextObj(objective.id());
1359 });
Saurav Das2cb38292017-03-29 19:09:17 -07001360 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Das2d94d312015-11-24 23:21:05 -08001361 flowObjectiveService.next(deviceId, nextObj);
1362 log.debug("createGroupFromPort: Submited next objective {} in device {} "
1363 + "for port {}", nextId, deviceId, portNum);
1364
1365 portNextObjStore.put(key, nextId);
1366 }
1367
sangho27462c62015-05-14 00:39:53 -07001368 /**
Shibu Vijayakumar632dd642018-03-01 15:45:59 -08001369 * Creates simple next objective for a single port.
1370 *
1371 * @param deviceId device id that has the port to deal with
1372 * @param portNum the outgoing port on the device
1373 * @param vlanId vlan id associated with the port
1374 * @param popVlan true if POP_VLAN action is applied on the packets, false otherwise
1375 */
1376 public void createPortNextObjective(DeviceId deviceId, PortNumber portNum, VlanId vlanId, boolean popVlan) {
1377 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
1378 mbuilder.matchVlanId(vlanId);
1379
1380 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
1381 tbuilder.immediate().setOutput(portNum);
1382 if (popVlan) {
1383 tbuilder.immediate().popVlan();
1384 }
1385
1386 createGroupFromPort(portNum, tbuilder.build(), mbuilder.build());
1387 }
1388
1389 /**
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001390 * Removes simple next objective for a single port.
1391 *
1392 * @param deviceId device id that has the port to deal with
1393 * @param portNum the outgoing port on the device
1394 * @param vlanId vlan id associated with the port
1395 * @param popVlan true if POP_VLAN action is applied on the packets, false otherwise
1396 */
1397 public void removePortNextObjective(DeviceId deviceId, PortNumber portNum, VlanId vlanId, boolean popVlan) {
1398 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
1399 mbuilder.matchVlanId(vlanId);
1400
1401 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
1402 tbuilder.immediate().setOutput(portNum);
1403 if (popVlan) {
1404 tbuilder.immediate().popVlan();
1405 }
1406
1407 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, portNum,
1408 tbuilder.build(), mbuilder.build(), false);
1409
1410 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
1411 deviceId, portNum, tbuilder.build(), mbuilder.build());
1412 if (portNextObjId != -1 && portNextObjStore.containsKey(key)) {
1413 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1414 .builder().withId(portNextObjId)
pierventre50e1bb82020-12-14 19:31:03 +01001415 .withType(NextObjective.Type.SIMPLE)
1416 .addTreatment(tbuilder.build())
1417 .fromApp(appId)
1418 .withMeta(mbuilder.build());
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001419 ObjectiveContext context = new DefaultObjectiveContext(
1420 (objective) -> log.debug("removePortNextObjective removes NextObj {} on {}",
1421 portNextObjId, deviceId),
Charles Chanfacfbef2018-08-23 14:30:33 -07001422 (objective, error) -> {
1423 log.warn("removePortNextObjective failed to remove NextObj {} on {}: {}",
1424 portNextObjId, deviceId, error);
1425 srManager.invalidateNextObj(objective.id());
1426 });
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001427 NextObjective nextObjective = nextObjBuilder.remove(context);
1428 log.info("**removePortNextObjective: Submitted "
1429 + "next objective {} in device {}",
1430 portNextObjId, deviceId);
1431 flowObjectiveService.next(deviceId, nextObjective);
1432
1433 portNextObjStore.remove(key);
1434 }
1435 }
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +00001436
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001437 /**
sangho27462c62015-05-14 00:39:53 -07001438 * Removes groups for the next objective ID given.
1439 *
1440 * @param objectiveId next objective ID to remove
1441 * @return true if succeeds, false otherwise
1442 */
1443 public boolean removeGroup(int objectiveId) {
Saurav Das261c3002017-06-13 15:35:54 -07001444 for (Map.Entry<DestinationSetNextObjectiveStoreKey, NextNeighbors> e :
1445 dsNextObjStore.entrySet()) {
1446 if (e.getValue().nextId() != objectiveId) {
1447 continue;
1448 }
Pier Luigiecb09f42018-01-14 21:56:11 +01001449 // Right now it is just used in TunnelHandler
1450 // remember in future that PW transit groups could
1451 // be Indirect groups
sangho27462c62015-05-14 00:39:53 -07001452 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1453 .builder().withId(objectiveId)
1454 .withType(NextObjective.Type.HASHED).fromApp(appId);
Charles Chana4ee4f92016-04-23 14:48:16 -07001455 ObjectiveContext context = new DefaultObjectiveContext(
1456 (objective) -> log.debug("RemoveGroup removes NextObj {} on {}",
1457 objectiveId, deviceId),
Charles Chanfacfbef2018-08-23 14:30:33 -07001458 (objective, error) -> {
1459 log.warn("RemoveGroup failed to remove NextObj {} on {}: {}", objectiveId, deviceId, error);
1460 srManager.invalidateNextObj(objective.id());
1461 });
Charles Chana4ee4f92016-04-23 14:48:16 -07001462 NextObjective nextObjective = nextObjBuilder.remove(context);
Saurav Das4c35fc42015-11-20 15:27:53 -08001463 log.info("**removeGroup: Submited "
1464 + "next objective {} in device {}",
1465 objectiveId, deviceId);
sangho27462c62015-05-14 00:39:53 -07001466 flowObjectiveService.next(deviceId, nextObjective);
1467
Saurav Das261c3002017-06-13 15:35:54 -07001468 dsNextObjStore.remove(e.getKey());
sangho4a5c42a2015-05-20 22:16:38 -07001469 return true;
sangho27462c62015-05-14 00:39:53 -07001470 }
1471
1472 return false;
1473 }
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001474 /**
1475 * Remove simple next objective for a single port. The treatments can include
1476 * all outgoing actions that need to happen on the packet.
1477 *
1478 * @param portNum the outgoing port on the device
1479 * @param treatment the actions applied on the packets (should include outport)
1480 * @param meta optional data to pass to the driver
pierventrea3989be2021-01-08 16:43:17 +01001481 * @return a completable future that completes when the port has been removed
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001482 */
pierventrea3989be2021-01-08 16:43:17 +01001483 public CompletableFuture<Objective> removeGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
1484 TrafficSelector meta) {
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001485 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
1486 deviceId, portNum, treatment, meta);
1487 Integer nextId = portNextObjStore.get(key);
pierventrea3989be2021-01-08 16:43:17 +01001488 CompletableFuture<Objective> future = new CompletableFuture<>();
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001489
1490 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1491 .builder().withId(nextId)
1492 .withType(NextObjective.Type.SIMPLE)
1493 .addTreatment(treatment)
1494 .fromApp(appId)
1495 .withMeta(meta);
1496
1497 ObjectiveContext context = new DefaultObjectiveContext(
pierventrea3989be2021-01-08 16:43:17 +01001498 (objective) -> {
1499 log.info("removeGroupFromPort done " + "NextObj {} on {}", nextId, deviceId);
1500 future.complete(objective);
1501 },
Charles Chanfacfbef2018-08-23 14:30:33 -07001502 (objective, error) -> {
1503 log.warn("removeGroupFromPort failed to install NextObj {} on {}: {}", nextId, deviceId, error);
1504 srManager.invalidateNextObj(objective.id());
pierventrea3989be2021-01-08 16:43:17 +01001505 future.complete(null);
Charles Chanfacfbef2018-08-23 14:30:33 -07001506 }
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001507 );
1508 NextObjective nextObj = nextObjBuilder.remove(context);
1509 flowObjectiveService.next(deviceId, nextObj);
1510 log.info("removeGroupFromPort: Submitted next objective {} in device {} "
1511 + "for port {}", nextId, deviceId, portNum);
1512
1513 portNextObjStore.remove(key);
pierventrea3989be2021-01-08 16:43:17 +01001514 return future;
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001515 }
Srikanth Vavilapalli8c83f1d2015-05-22 13:47:31 -07001516
Charles Chanb7f75ac2016-01-11 18:28:54 -08001517 /**
1518 * Removes all groups from all next objective stores.
1519 */
Saurav Das261c3002017-06-13 15:35:54 -07001520 /*public void removeAllGroups() {
1521 for (Map.Entry<NeighborSetNextObjectiveStoreKey, NextNeighbors> entry:
Saurav Das62af8802015-12-04 10:52:59 -08001522 nsNextObjStore.entrySet()) {
Saurav Das261c3002017-06-13 15:35:54 -07001523 removeGroup(entry.getValue().nextId());
Saurav Das62af8802015-12-04 10:52:59 -08001524 }
1525 for (Map.Entry<PortNextObjectiveStoreKey, Integer> entry:
1526 portNextObjStore.entrySet()) {
1527 removeGroup(entry.getValue());
1528 }
Charles Chan10b0fb72017-02-02 16:20:42 -08001529 for (Map.Entry<VlanNextObjectiveStoreKey, Integer> entry:
1530 vlanNextObjStore.entrySet()) {
Saurav Das62af8802015-12-04 10:52:59 -08001531 removeGroup(entry.getValue());
1532 }
Saurav Das261c3002017-06-13 15:35:54 -07001533 }*/ //XXX revisit
1534
Saurav Dasfbe74572017-08-03 18:30:35 -07001535 /**
1536 * Triggers a one time bucket verification operation on all hash groups
1537 * on this device.
1538 */
1539 public void triggerBucketCorrector() {
1540 BucketCorrector bc = new BucketCorrector();
1541 bc.run();
1542 }
1543
Jonghwan Hyuna4ce0aa2018-02-12 16:43:45 +09001544 /**
1545 * Modifies L2IG bucket when the interface configuration is updated, especially
1546 * when the interface has same VLAN ID but the VLAN type is changed (e.g., from
1547 * vlan-tagged [10] to vlan-untagged 10), which requires changes on
1548 * TrafficTreatment in turn.
1549 *
1550 * @param portNumber the port on this device that needs to be updated
1551 * @param vlanId the vlan id corresponding to this port
1552 * @param pushVlan indicates if packets should be sent out untagged or not out
1553 * from the port. If true, updated TrafficTreatment involves
1554 * pop vlan tag action. If false, updated TrafficTreatment
1555 * does not involve pop vlan tag action.
1556 */
1557 public void updateL2InterfaceGroupBucket(PortNumber portNumber, VlanId vlanId, boolean pushVlan) {
Shibu Vijayakumar632dd642018-03-01 15:45:59 -08001558 TrafficTreatment.Builder oldTBuilder = DefaultTrafficTreatment.builder();
Jonghwan Hyuna4ce0aa2018-02-12 16:43:45 +09001559 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Shibu Vijayakumar632dd642018-03-01 15:45:59 -08001560 tBuilder.setOutput(portNumber);
1561 oldTBuilder.setOutput(portNumber);
Jonghwan Hyuna4ce0aa2018-02-12 16:43:45 +09001562 if (pushVlan) {
1563 tBuilder.popVlan();
Shibu Vijayakumar632dd642018-03-01 15:45:59 -08001564 } else {
1565 oldTBuilder.popVlan();
Jonghwan Hyuna4ce0aa2018-02-12 16:43:45 +09001566 }
Jonghwan Hyuna4ce0aa2018-02-12 16:43:45 +09001567
1568 TrafficSelector metadata =
1569 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1570
Shibu Vijayakumar632dd642018-03-01 15:45:59 -08001571 // Update portNextObjStore with new L2IG
1572 int nextId = getPortNextObjectiveId(portNumber, oldTBuilder.build(), metadata, false);
1573 portNextObjStore.remove(new PortNextObjectiveStoreKey(deviceId, portNumber, oldTBuilder.build(), metadata));
1574 portNextObjStore.put(new PortNextObjectiveStoreKey(deviceId, portNumber, tBuilder.build(), metadata), nextId);
Jonghwan Hyuna4ce0aa2018-02-12 16:43:45 +09001575
1576 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1577 .builder().withId(nextId)
1578 .withType(NextObjective.Type.SIMPLE).fromApp(appId)
1579 .addTreatment(tBuilder.build())
1580 .withMeta(metadata);
1581
1582 ObjectiveContext context = new DefaultObjectiveContext(
1583 (objective) -> log.debug("port {} successfully updated NextObj {} on {}",
1584 portNumber, nextId, deviceId),
Charles Chanfacfbef2018-08-23 14:30:33 -07001585 (objective, error) -> {
1586 log.warn("port {} failed to updated NextObj {} on {}: {}", portNumber, nextId, deviceId, error);
1587 srManager.invalidateNextObj(objective.id());
1588 });
Jonghwan Hyuna4ce0aa2018-02-12 16:43:45 +09001589
1590 flowObjectiveService.next(deviceId, nextObjBuilder.modify(context));
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001591 }
1592
Jonghwan Hyuna4ce0aa2018-02-12 16:43:45 +09001593 /**
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +00001594 * Updates the next objective for the given nextId .
1595 *
1596 * @param hostMac mac of host for which Next obj is to be updated.
1597 * @param hostVlanId vlan of host for which Next obj is to be updated.
1598 * @param port port with which to update the Next Obj.
1599 * @param nextId of Next Obj which needs to be updated.
1600 */
1601 public void updateL3UcastGroupBucket(MacAddress hostMac, VlanId hostVlanId, PortNumber port, int nextId) {
1602
1603 MacAddress deviceMac;
1604 try {
1605 deviceMac = deviceConfig.getDeviceMac(deviceId);
1606 } catch (DeviceConfigNotFoundException e) {
1607 log.warn(e.getMessage() + " in updateL3UcastGroupBucket");
1608 return;
1609 }
1610
pierventre50e1bb82020-12-14 19:31:03 +01001611 ConnectPoint connectPoint = new ConnectPoint(deviceId, port);
1612 VlanId untaggedVlan = srManager.interfaceService.getUntaggedVlanId(connectPoint);
1613 Set<VlanId> taggedVlans = srManager.interfaceService.getTaggedVlanId(connectPoint);
1614 VlanId nativeVlan = srManager.interfaceService.getNativeVlanId(connectPoint);
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +00001615
pierventre50e1bb82020-12-14 19:31:03 +01001616 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
1617 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder()
1618 .deferred()
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +00001619 .setEthDst(hostMac)
1620 .setEthSrc(deviceMac)
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +00001621 .setOutput(port);
1622
pierventre50e1bb82020-12-14 19:31:03 +01001623 if (taggedVlans.contains(hostVlanId)) {
1624 mbuilder.matchVlanId(hostVlanId);
1625 tbuilder.setVlanId(hostVlanId);
1626 } else if (hostVlanId.equals(VlanId.NONE)) {
1627 if (untaggedVlan != null) {
1628 mbuilder.matchVlanId(untaggedVlan);
1629 tbuilder.popVlan();
1630 } else if (nativeVlan != null) {
1631 mbuilder.matchVlanId(nativeVlan);
1632 tbuilder.popVlan();
1633 } else {
1634 log.warn("Untagged nexthop {}/{} is not allowed on {} without untagged or native vlan",
1635 hostMac, hostVlanId, connectPoint);
1636 return;
1637 }
pierventrea3989be2021-01-08 16:43:17 +01001638 } else {
1639 log.warn("Tagged nexthop {}/{} is not allowed on {} without VLAN listed"
1640 + " in tagged vlan", hostMac, hostVlanId, connectPoint);
1641 return;
pierventre50e1bb82020-12-14 19:31:03 +01001642 }
1643
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +00001644 log.debug(" update L3Ucast : deviceMac {}, port {}, host {}/{}, nextid {}, Treatment {} Meta {}",
pierventre50e1bb82020-12-14 19:31:03 +01001645 deviceMac, port, hostMac, hostVlanId, nextId, tbuilder.build(), mbuilder.build());
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +00001646
1647 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1648 .builder().withId(nextId)
1649 .withType(NextObjective.Type.SIMPLE).fromApp(appId)
1650 .addTreatment(tbuilder.build())
pierventre50e1bb82020-12-14 19:31:03 +01001651 .withMeta(mbuilder.build());
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +00001652
1653 ObjectiveContext context = new DefaultObjectiveContext(
1654 (objective) -> log.debug(" NextId {} successfully updated host {} vlan {} with port {}",
1655 nextId, hostMac, hostVlanId, port),
1656 (objective, error) -> {
1657 log.warn(" NextId {} failed to update host {} vlan {} with port {}, error : {}",
1658 nextId, hostMac, hostVlanId, port, error);
1659 srManager.invalidateNextObj(objective.id());
1660 });
1661
1662 NextObjective nextObj = nextObjBuilder.modify(context);
1663 flowObjectiveService.next(deviceId, nextObj);
1664
1665 }
1666
1667 /**
Jonghwan Hyuna4ce0aa2018-02-12 16:43:45 +09001668 * Adds a single port to the L2FG or removes it from the L2FG.
1669 *
1670 * @param vlanId the vlan id corresponding to this port
1671 * @param portNum the port on this device to be updated
1672 * @param nextId the next objective ID for the given vlan id
1673 * @param install if true, adds the port to L2FG. If false, removes it from L2FG.
1674 */
1675 public void updateGroupFromVlanConfiguration(VlanId vlanId, PortNumber portNum, int nextId, boolean install) {
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001676 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1677 if (toPopVlan(portNum, vlanId)) {
1678 tBuilder.popVlan();
1679 }
1680 tBuilder.setOutput(portNum);
1681
1682 TrafficSelector metadata =
1683 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1684
1685 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1686 .builder().withId(nextId)
1687 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1688 .addTreatment(tBuilder.build())
1689 .withMeta(metadata);
1690
1691 ObjectiveContext context = new DefaultObjectiveContext(
1692 (objective) -> log.debug("port {} successfully removedFrom NextObj {} on {}",
1693 portNum, nextId, deviceId),
Charles Chanfacfbef2018-08-23 14:30:33 -07001694 (objective, error) -> {
1695 log.warn("port {} failed to removedFrom NextObj {} on {}: {}", portNum, nextId, deviceId, error);
1696 srManager.invalidateNextObj(objective.id());
1697 });
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001698
1699 if (install) {
1700 flowObjectiveService.next(deviceId, nextObjBuilder.addToExisting(context));
1701 } else {
1702 flowObjectiveService.next(deviceId, nextObjBuilder.removeFromExisting(context));
1703 }
1704 }
Saurav Das8a3022d2017-05-05 17:01:08 -07001705
1706 /**
Saurav Dasfe0b05e2017-08-14 16:44:43 -07001707 * Performs bucket verification operation for all hash groups in this device.
1708 * Checks RouteHandler to ensure that routing is stable before attempting
1709 * verification. Verification involves creating a nextObjective with
1710 * operation VERIFY for existing next objectives in the store, and passing
1711 * it to the driver. It is the driver that actually performs the verification
1712 * by adding or removing buckets to match the verification next objective
1713 * created here.
Saurav Das8a3022d2017-05-05 17:01:08 -07001714 */
Saurav Dasfbe74572017-08-03 18:30:35 -07001715 protected final class BucketCorrector implements Runnable {
1716 Integer nextId;
Saurav Das8a3022d2017-05-05 17:01:08 -07001717
Saurav Dasfbe74572017-08-03 18:30:35 -07001718 BucketCorrector() {
1719 this.nextId = null;
1720 }
1721
Saurav Das8a3022d2017-05-05 17:01:08 -07001722 @Override
1723 public void run() {
Saurav Dasfbe74572017-08-03 18:30:35 -07001724 DefaultRoutingHandler rh = srManager.getRoutingHandler();
pierventre37dcf4c2021-09-16 18:43:06 +02001725 if (rh == null || !rh.isRoutingStable() || !rh.shouldProgram(deviceId)) {
Saurav Dasfbe74572017-08-03 18:30:35 -07001726 return;
1727 }
pierventre37dcf4c2021-09-16 18:43:06 +02001728
Saurav Dasfbe74572017-08-03 18:30:35 -07001729 rh.acquireRoutingLock();
1730 try {
Saurav Dasfe0b05e2017-08-14 16:44:43 -07001731 log.trace("running bucket corrector for dev: {}", deviceId);
Saurav Dasfbe74572017-08-03 18:30:35 -07001732 Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet()
1733 .stream()
1734 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Pier Luigiecb09f42018-01-14 21:56:11 +01001735 // Filter out PW transit groups or include them if MPLS ECMP is supported
Saurav Das97241862018-02-14 14:14:54 -08001736 .filter(entry -> !entry.getKey().destinationSet().notBos() ||
1737 (entry.getKey().destinationSet().notBos() && srManager.getMplsEcmp()))
1738 // Filter out simple SWAP groups or include them if MPLS ECMP is supported
1739 .filter(entry -> !entry.getKey().destinationSet().swap() ||
1740 (entry.getKey().destinationSet().swap() && srManager.getMplsEcmp()))
Saurav Dasfbe74572017-08-03 18:30:35 -07001741 .map(entry -> entry.getKey())
1742 .collect(Collectors.toSet());
1743 for (DestinationSetNextObjectiveStoreKey dsKey : dsKeySet) {
1744 NextNeighbors next = dsNextObjStore.get(dsKey);
1745 if (next == null) {
1746 continue;
1747 }
1748 int nid = next.nextId();
1749 if (nextId != null && nextId != nid) {
1750 continue;
1751 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -07001752 log.trace("bkt-corr: dsNextObjStore for device {}: {}",
Saurav Dasfbe74572017-08-03 18:30:35 -07001753 deviceId, dsKey, next);
1754 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
Saurav Das9bf49582018-08-13 15:34:26 -07001755 metabuilder.matchVlanId(srManager.getDefaultInternalVlan());
Saurav Dasfbe74572017-08-03 18:30:35 -07001756 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
1757 .withId(nid)
1758 .withType(NextObjective.Type.HASHED)
1759 .withMeta(metabuilder.build())
1760 .fromApp(appId);
1761
1762 next.dstNextHops().forEach((dstDev, nextHops) -> {
1763 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dstDev);
1764 nextHops.forEach(neighbor -> {
1765 MacAddress neighborMac;
1766 try {
1767 neighborMac = deviceConfig.getDeviceMac(neighbor);
1768 } catch (DeviceConfigNotFoundException e) {
1769 log.warn(e.getMessage() + " Aborting neighbor"
1770 + neighbor);
1771 return;
1772 }
1773 devicePortMap.get(neighbor).forEach(port -> {
Saurav Dasfe0b05e2017-08-14 16:44:43 -07001774 log.trace("verify in device {} nextId {}: bucket with"
Saurav Dasfbe74572017-08-03 18:30:35 -07001775 + " port/label {}/{} to dst {} via {}",
1776 deviceId, nid, port, edgeLabel,
1777 dstDev, neighbor);
Saurav Das97241862018-02-14 14:14:54 -08001778 nextObjBuilder
pierventre02fd3b02021-06-15 18:49:41 +02001779 .addTreatment(treatmentBuilder(port, neighborMac, dsKey.destinationSet().swap(),
1780 edgeLabel, popVlanInHashGroup(dsKey.destinationSet())));
Saurav Dasfbe74572017-08-03 18:30:35 -07001781 });
1782 });
1783 });
1784
1785 NextObjective nextObjective = nextObjBuilder.verify();
1786 flowObjectiveService.next(deviceId, nextObjective);
1787 }
1788 } finally {
1789 rh.releaseRoutingLock();
1790 }
1791
1792 }
1793
1794 TrafficTreatment treatmentBuilder(PortNumber outport, MacAddress dstMac,
pierventre02fd3b02021-06-15 18:49:41 +02001795 boolean swap, int edgeLabel, boolean popVlan) {
Saurav Dasfbe74572017-08-03 18:30:35 -07001796 TrafficTreatment.Builder tBuilder =
1797 DefaultTrafficTreatment.builder();
1798 tBuilder.setOutput(outport)
pierventre02fd3b02021-06-15 18:49:41 +02001799 .setEthDst(dstMac)
1800 .setEthSrc(nodeMacAddr);
1801
1802 if (popVlan) {
1803 tBuilder.popVlan();
1804 }
1805
Saurav Dasfbe74572017-08-03 18:30:35 -07001806 if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
Saurav Das97241862018-02-14 14:14:54 -08001807 if (swap) {
1808 // swap label case
1809 tBuilder.setMpls(MplsLabel.mplsLabel(edgeLabel));
1810 } else {
1811 // ecmp with label push case
1812 tBuilder.pushMpls()
1813 .copyTtlOut()
1814 .setMpls(MplsLabel.mplsLabel(edgeLabel));
1815 }
Saurav Dasfbe74572017-08-03 18:30:35 -07001816 }
1817 return tBuilder.build();
Saurav Das8a3022d2017-05-05 17:01:08 -07001818 }
1819 }
1820
Charles Chan223ae872018-08-22 17:56:47 -07001821 /**
1822 * Determines whether the hash group bucket should include a popVlan action.
1823 * We don't popVlan for PW.
1824 *
1825 * @param ds destination set
1826 * @return true if VLAN needs to be popped
1827 */
1828 private boolean popVlanInHashGroup(DestinationSet ds) {
1829 return (ds.getTypeOfDstSet() != DestinationSet.DestinationSetType.SWAP_NOT_BOS) &&
1830 (ds.getTypeOfDstSet() != DestinationSet.DestinationSetType.POP_NOT_BOS);
1831 }
Pier Luigiecb09f42018-01-14 21:56:11 +01001832}