blob: 46231b0915df4d20d91329937a379bfe777a147b [file] [log] [blame]
Charles Chand2990362016-04-18 13:44:03 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Charles Chand2990362016-04-18 13:44:03 -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 */
16
17package org.onosproject.segmentrouting;
18
Charles Chan9797ebb2020-02-14 13:23:57 -080019import com.google.common.collect.Lists;
Jonghwan Hyun800d9d02018-04-09 09:40:50 -070020import org.onlab.packet.EthType;
Charles Chand2990362016-04-18 13:44:03 -070021import org.onlab.packet.IpAddress;
Jonghwan Hyun42fe1052017-08-25 17:48:36 -070022import org.onlab.packet.IpPrefix;
Charles Chand2990362016-04-18 13:44:03 -070023import org.onlab.packet.MacAddress;
24import org.onlab.packet.VlanId;
pierb84e9192019-05-17 20:47:06 +020025import org.onlab.util.PredictableExecutor;
Charles Chan9797ebb2020-02-14 13:23:57 -080026import org.onlab.util.Tools;
Charles Chan59cc16d2017-02-02 16:20:42 -080027import org.onosproject.net.ConnectPoint;
Charles Chand2990362016-04-18 13:44:03 -070028import org.onosproject.net.DeviceId;
Charles Chan6ea94fc2016-05-10 17:29:47 -070029import org.onosproject.net.Host;
Charles Chan93e71ba2016-04-29 14:38:22 -070030import org.onosproject.net.HostLocation;
Charles Chand2990362016-04-18 13:44:03 -070031import org.onosproject.net.PortNumber;
Charles Chan9797ebb2020-02-14 13:23:57 -080032import org.onosproject.net.flowobjective.Objective;
Charles Chand2990362016-04-18 13:44:03 -070033import org.onosproject.net.host.HostEvent;
34import org.onosproject.net.host.HostService;
Charles Chanff79dd92018-06-01 16:33:48 -070035import org.onosproject.net.host.ProbeMode;
Charles Chan9797ebb2020-02-14 13:23:57 -080036import org.onosproject.segmentrouting.phasedrecovery.api.Phase;
37import org.onosproject.segmentrouting.phasedrecovery.api.PhasedRecoveryService;
Charles Chand2990362016-04-18 13:44:03 -070038import org.slf4j.Logger;
39import org.slf4j.LoggerFactory;
40
Saurav Das018605f2017-02-18 14:05:44 -080041import com.google.common.collect.Sets;
Charles Chan65238242017-06-22 18:03:14 -070042
Saurav Das45f48152018-01-18 12:07:33 -080043import java.util.HashSet;
Charles Chan9797ebb2020-02-14 13:23:57 -080044import java.util.List;
Charles Chan65238242017-06-22 18:03:14 -070045import java.util.Optional;
Charles Chand2990362016-04-18 13:44:03 -070046import java.util.Set;
Charles Chan9797ebb2020-02-14 13:23:57 -080047import java.util.concurrent.CompletableFuture;
48import java.util.concurrent.ExecutionException;
49import java.util.concurrent.TimeUnit;
Charles Chanf9a52702017-06-16 15:19:24 -070050import java.util.stream.Collectors;
51
52import static com.google.common.base.Preconditions.checkArgument;
pierb84e9192019-05-17 20:47:06 +020053import static org.onlab.util.Tools.groupedThreads;
Charles Chand2990362016-04-18 13:44:03 -070054
55/**
56 * Handles host-related events.
57 */
58public class HostHandler {
59 private static final Logger log = LoggerFactory.getLogger(HostHandler.class);
Charles Chan47933752017-11-30 15:37:50 -080060
Charles Chan2e2e3402017-06-19 14:00:53 -070061 protected final SegmentRoutingManager srManager;
Charles Chand2990362016-04-18 13:44:03 -070062 private HostService hostService;
pierb84e9192019-05-17 20:47:06 +020063 // Host workers - 0 will leverage available processors
64 private static final int DEFAULT_THREADS = 0;
65 protected PredictableExecutor hostWorkers;
Charles Chand2990362016-04-18 13:44:03 -070066
67 /**
68 * Constructs the HostHandler.
69 *
70 * @param srManager Segment Routing manager
71 */
Charles Chanf9a52702017-06-16 15:19:24 -070072 HostHandler(SegmentRoutingManager srManager) {
Charles Chand2990362016-04-18 13:44:03 -070073 this.srManager = srManager;
Charles Chand2990362016-04-18 13:44:03 -070074 hostService = srManager.hostService;
pierb84e9192019-05-17 20:47:06 +020075 this.hostWorkers = new PredictableExecutor(DEFAULT_THREADS,
76 groupedThreads("onos/sr", "h-worker-%d", log));
77 }
78
79 /**
80 * Shutdowns the workers.
81 */
82 void terminate() {
83 hostWorkers.shutdown();
Charles Chand2990362016-04-18 13:44:03 -070084 }
85
Charles Chan03a73e02016-10-24 14:52:01 -070086 protected void init(DeviceId devId) {
Charles Chan9797ebb2020-02-14 13:23:57 -080087 log.info("Initializing hosts on {}", devId);
88 List<CompletableFuture<Void>> hostFutures = Lists.newArrayList();
89
pierb84e9192019-05-17 20:47:06 +020090 // Init hosts in parallel using hostWorkers executor
Charles Chan9797ebb2020-02-14 13:23:57 -080091 hostService.getHosts().forEach(host -> {
92 hostFutures.add(hostWorkers.submit(() -> initHost(host, devId), host.id().hashCode()));
93 });
94
95 log.debug("{} hostFutures for {}", hostFutures.size(), devId);
96 CompletableFuture<Void> allHostFuture = CompletableFuture.allOf(hostFutures.toArray(new CompletableFuture[0]));
97 CompletableFuture<Void> timeoutFuture =
98 Tools.completeAfter(PhasedRecoveryService.PAIR_TIMEOUT, TimeUnit.SECONDS);
99
100 allHostFuture.runAfterEitherAsync(timeoutFuture, () -> {
101 if (allHostFuture.isDone()) {
102 log.info("{} hosts initialized. Move {} to the next phase", hostFutures.size(), devId);
103 } else {
104 log.info("Timeout reached. Move {} to the next phase", devId);
105 }
106 srManager.phasedRecoveryService.setPhase(devId, Phase.INFRA);
107 });
Charles Chan93e71ba2016-04-29 14:38:22 -0700108 }
Charles Chand2990362016-04-18 13:44:03 -0700109
pierb84e9192019-05-17 20:47:06 +0200110 private void initHost(Host host, DeviceId deviceId) {
Charles Chan9797ebb2020-02-14 13:23:57 -0800111 List<CompletableFuture<Objective>> locationFutures = Lists.newArrayList();
112
Charles Chand7e62142019-11-25 09:47:22 -0800113 effectiveLocations(host).forEach(location -> {
pierb84e9192019-05-17 20:47:06 +0200114 if (location.deviceId().equals(deviceId) ||
115 location.deviceId().equals(srManager.getPairDeviceId(deviceId).orElse(null))) {
Charles Chan9797ebb2020-02-14 13:23:57 -0800116 locationFutures.addAll(processHostAddedAtLocation(host, location));
pierb84e9192019-05-17 20:47:06 +0200117 }
118 });
Charles Chan9797ebb2020-02-14 13:23:57 -0800119
120 log.debug("{} locationFutures for {}", locationFutures.size(), host);
121
122 // Waiting for all locationFutures to be completed.
123 // This is a blocking operation but it is fine since this is run in a separate thread
124 try {
125 CompletableFuture.allOf(locationFutures.toArray(new CompletableFuture[0]))
126 .thenApply(objectives -> locationFutures.stream()
127 .map(CompletableFuture::join)
128 .collect(Collectors.toList())
129 )
130 .get();
131 } catch (InterruptedException | ExecutionException e) {
132 log.warn("Exception caught when executing locationFutures");
133 locationFutures.forEach(future -> future.cancel(false));
134 }
pierb84e9192019-05-17 20:47:06 +0200135 }
136
Charles Chanf9a52702017-06-16 15:19:24 -0700137 void processHostAddedEvent(HostEvent event) {
pierb84e9192019-05-17 20:47:06 +0200138 Host host = event.subject();
139 hostWorkers.execute(() -> processHostAdded(host), host.id().hashCode());
Charles Chan93e71ba2016-04-29 14:38:22 -0700140 }
141
Charles Chanf9a52702017-06-16 15:19:24 -0700142 private void processHostAdded(Host host) {
Charles Chand7e62142019-11-25 09:47:22 -0800143 effectiveLocations(host).forEach(location -> processHostAddedAtLocation(host, location));
Saurav Das45f48152018-01-18 12:07:33 -0800144 // ensure dual-homed host locations have viable uplinks
Charles Chand7e62142019-11-25 09:47:22 -0800145 if (effectiveLocations(host).size() > 1 || srManager.singleHomedDown) {
146 effectiveLocations(host).forEach(loc -> {
Saurav Das45f48152018-01-18 12:07:33 -0800147 if (srManager.mastershipService.isLocalMaster(loc.deviceId())) {
Saurav Das9a554292018-04-27 18:42:30 -0700148 srManager.linkHandler.checkUplinksForHost(loc);
Saurav Das45f48152018-01-18 12:07:33 -0800149 }
150 });
151 }
Charles Chand2990362016-04-18 13:44:03 -0700152 }
153
Charles Chan9797ebb2020-02-14 13:23:57 -0800154 List<CompletableFuture<Objective>> processHostAddedAtLocation(Host host, HostLocation location) {
Charles Chand7e62142019-11-25 09:47:22 -0800155 checkArgument(effectiveLocations(host).contains(location), "{} is not a location of {}", location, host);
Charles Chanf9a52702017-06-16 15:19:24 -0700156
Charles Chan8e786b52017-09-12 18:57:47 -0700157 MacAddress hostMac = host.mac();
158 VlanId hostVlanId = host.vlan();
Charles Chand7e62142019-11-25 09:47:22 -0800159 Set<HostLocation> locations = effectiveLocations(host);
Charles Chanf9a52702017-06-16 15:19:24 -0700160 Set<IpAddress> ips = host.ipAddresses();
Charles Chan8e786b52017-09-12 18:57:47 -0700161 log.info("Host {}/{} is added at {}", hostMac, hostVlanId, locations);
Charles Chanf9a52702017-06-16 15:19:24 -0700162
Charles Chan9797ebb2020-02-14 13:23:57 -0800163 List<CompletableFuture<Objective>> objectiveFutures = Lists.newArrayList();
164
165 // TODO Phased recovery does not trace double tagged hosts
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700166 if (isDoubleTaggedHost(host)) {
167 ips.forEach(ip ->
168 processDoubleTaggedRoutingRule(location.deviceId(), location.port(), hostMac,
169 host.innerVlan(), hostVlanId, host.tpid(), ip, false)
170 );
171 } else {
Charles Chan9797ebb2020-02-14 13:23:57 -0800172 objectiveFutures.add(
173 processBridgingRule(location.deviceId(), location.port(), hostMac, hostVlanId, false)
174 );
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700175 ips.forEach(ip ->
Charles Chan9797ebb2020-02-14 13:23:57 -0800176 objectiveFutures.add(
177 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, false)
178 )
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700179 );
180 }
Charles Chan8e786b52017-09-12 18:57:47 -0700181
182 // Use the pair link temporarily before the second location of a dual-homed host shows up.
183 // This do not affect single-homed hosts since the flow will be blocked in
184 // processBridgingRule or processRoutingRule due to VLAN or IP mismatch respectively
185 srManager.getPairDeviceId(location.deviceId()).ifPresent(pairDeviceId -> {
Charles Chand7e62142019-11-25 09:47:22 -0800186 if (effectiveLocations(host).stream().noneMatch(l -> l.deviceId().equals(pairDeviceId))) {
Saurav Das9a554292018-04-27 18:42:30 -0700187 srManager.getPairLocalPort(pairDeviceId).ifPresent(pairRemotePort -> {
Charles Chan8e786b52017-09-12 18:57:47 -0700188 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
189 // when the host is untagged
Charles Chanf76de302018-06-15 18:54:18 -0700190 VlanId vlanId = vlanForPairPort(hostVlanId, location);
191 if (vlanId == null) {
192 return;
193 }
Charles Chan8e786b52017-09-12 18:57:47 -0700194
Charles Chan9797ebb2020-02-14 13:23:57 -0800195 objectiveFutures.add(
196 processBridgingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, false)
197 );
198 ips.forEach(ip ->
199 objectiveFutures.add(
200 processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, false)
201 )
202 );
Charles Chan47933752017-11-30 15:37:50 -0800203
204 if (srManager.activeProbing) {
205 probe(host, location, pairDeviceId, pairRemotePort);
206 }
Charles Chan8e786b52017-09-12 18:57:47 -0700207 });
208 }
209 });
Ruchi Sahotac8aa9f32019-05-09 17:26:14 -0400210
211 int nextId = srManager.getMacVlanNextObjectiveId(location.deviceId(), hostMac, hostVlanId, null, false);
212 if (nextId != -1) {
213 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(hostVlanId);
214 log.debug(" Updating next objective for device {}, host {}/{}, port {}, nextid {}",
215 location.deviceId(), hostMac, vlanId, location.port(), nextId);
216 srManager.updateMacVlanTreatment(location.deviceId(), hostMac, vlanId,
217 location.port(), nextId);
218 }
Charles Chan9797ebb2020-02-14 13:23:57 -0800219
220 log.debug("{} objectiveFutures for {}", objectiveFutures.size(), location);
221 return objectiveFutures;
Charles Chanf9a52702017-06-16 15:19:24 -0700222 }
223
224 void processHostRemovedEvent(HostEvent event) {
pierb84e9192019-05-17 20:47:06 +0200225 Host host = event.subject();
226 hostWorkers.execute(() -> processHostRemoved(host), host.id().hashCode());
Charles Chan35fd1a72016-06-13 18:54:31 -0700227 }
228
Charles Chanf9a52702017-06-16 15:19:24 -0700229 private void processHostRemoved(Host host) {
Charles Chan65238242017-06-22 18:03:14 -0700230 MacAddress hostMac = host.mac();
231 VlanId hostVlanId = host.vlan();
Charles Chand7e62142019-11-25 09:47:22 -0800232 Set<HostLocation> locations = effectiveLocations(host);
Charles Chan35fd1a72016-06-13 18:54:31 -0700233 Set<IpAddress> ips = host.ipAddresses();
Charles Chan65238242017-06-22 18:03:14 -0700234 log.info("Host {}/{} is removed from {}", hostMac, hostVlanId, locations);
Charles Chan93e71ba2016-04-29 14:38:22 -0700235
Charles Chan2fde6d42017-08-23 14:46:43 -0700236 locations.forEach(location -> {
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700237 if (isDoubleTaggedHost(host)) {
238 ips.forEach(ip ->
239 processDoubleTaggedRoutingRule(location.deviceId(), location.port(), hostMac,
240 host.innerVlan(), hostVlanId, host.tpid(), ip, true)
241 );
242 } else {
243 processBridgingRule(location.deviceId(), location.port(), hostMac, hostVlanId, true);
244 ips.forEach(ip ->
Charles Chan2ff1bac2018-03-29 16:03:41 -0700245 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, true)
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700246 );
247 }
Charles Chan65238242017-06-22 18:03:14 -0700248
249 // Also remove redirection flows on the pair device if exists.
250 Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(location.deviceId());
Saurav Das9a554292018-04-27 18:42:30 -0700251 Optional<PortNumber> pairLocalPort = srManager.getPairLocalPort(location.deviceId());
Charles Chan2ff1bac2018-03-29 16:03:41 -0700252 if (pairDeviceId.isPresent() && pairLocalPort.isPresent()) {
Charles Chan65238242017-06-22 18:03:14 -0700253 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
254 // when the host is untagged
Charles Chanf76de302018-06-15 18:54:18 -0700255 VlanId vlanId = vlanForPairPort(hostVlanId, location);
256 if (vlanId == null) {
257 return;
258 }
Charles Chan65238242017-06-22 18:03:14 -0700259
260 processBridgingRule(pairDeviceId.get(), pairLocalPort.get(), hostMac, vlanId, true);
261 ips.forEach(ip ->
262 processRoutingRule(pairDeviceId.get(), pairLocalPort.get(), hostMac, vlanId,
263 ip, true));
264 }
Charles Chan4d639392018-06-21 19:07:12 -0700265
266 // Delete prefix from sr-device-subnet when the next hop host is removed
267 srManager.routeService.getRouteTables().forEach(tableId -> {
268 srManager.routeService.getRoutes(tableId).forEach(routeInfo -> {
269 if (routeInfo.allRoutes().stream().anyMatch(rr -> ips.contains(rr.nextHop()))) {
270 log.debug("HostRemoved. removeSubnet {}, {}", location, routeInfo.prefix());
271 srManager.deviceConfiguration.removeSubnet(location, routeInfo.prefix());
272 }
273 });
274 });
Ruchi Sahotac8aa9f32019-05-09 17:26:14 -0400275
Charles Chanf9a52702017-06-16 15:19:24 -0700276 });
Charles Chan93e71ba2016-04-29 14:38:22 -0700277 }
278
Charles Chanf9a52702017-06-16 15:19:24 -0700279 void processHostMovedEvent(HostEvent event) {
Charles Chan9bd0e5a2018-04-25 18:51:46 -0400280 Host host = event.subject();
pierb84e9192019-05-17 20:47:06 +0200281 hostWorkers.execute(() -> processHostMovedEventInternal(event), host.id().hashCode());
282 }
283
284 private void processHostMovedEventInternal(HostEvent event) {
Charles Chand7e62142019-11-25 09:47:22 -0800285 // This method will be called when one of the following value has changed:
286 // (1) locations (2) auxLocations or (3) both locations and auxLocations.
287 // We only need to proceed when effectiveLocation has changed.
288 Set<HostLocation> newLocations = effectiveLocations(event.subject());
289 Set<HostLocation> prevLocations = effectiveLocations(event.prevSubject());
290
291 if (newLocations.equals(prevLocations)) {
292 log.info("effectiveLocations of {} has not changed. Skipping {}", event.subject().id(), event);
293 return;
294 }
295
pierb84e9192019-05-17 20:47:06 +0200296 Host host = event.subject();
Charles Chand7e62142019-11-25 09:47:22 -0800297 Host prevHost = event.prevSubject();
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700298 MacAddress hostMac = host.mac();
299 VlanId hostVlanId = host.vlan();
Charles Chand7e62142019-11-25 09:47:22 -0800300 Set<IpAddress> prevIps = prevHost.ipAddresses();
Charles Chan9bd0e5a2018-04-25 18:51:46 -0400301 Set<IpAddress> newIps = host.ipAddresses();
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700302 EthType hostTpid = host.tpid();
303 boolean doubleTaggedHost = isDoubleTaggedHost(host);
Charles Chan9bd0e5a2018-04-25 18:51:46 -0400304
Charles Chan47933752017-11-30 15:37:50 -0800305 log.info("Host {}/{} is moved from {} to {}", hostMac, hostVlanId, prevLocations, newLocations);
Charles Chanf9a52702017-06-16 15:19:24 -0700306 Set<DeviceId> newDeviceIds = newLocations.stream().map(HostLocation::deviceId)
307 .collect(Collectors.toSet());
Charles Chan93e71ba2016-04-29 14:38:22 -0700308
Charles Chanf9a52702017-06-16 15:19:24 -0700309 // For each old location
Charles Chan2ff1bac2018-03-29 16:03:41 -0700310 Sets.difference(prevLocations, newLocations).forEach(prevLocation -> {
Charles Chan65238242017-06-22 18:03:14 -0700311 // Remove routing rules for old IPs
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700312 Sets.difference(prevIps, newIps).forEach(ip -> {
313 if (doubleTaggedHost) {
314 processDoubleTaggedRoutingRule(prevLocation.deviceId(), prevLocation.port(),
315 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, true);
316 } else {
Charles Chan65238242017-06-22 18:03:14 -0700317 processRoutingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId,
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700318 ip, true);
319 }
320 });
Charles Chan65238242017-06-22 18:03:14 -0700321
322 // Redirect the flows to pair link if configured
323 // Note: Do not continue removing any rule
324 Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(prevLocation.deviceId());
Saurav Das9a554292018-04-27 18:42:30 -0700325 Optional<PortNumber> pairLocalPort = srManager.getPairLocalPort(prevLocation.deviceId());
Charles Chan5145de22018-10-11 12:35:36 -0700326 if (pairDeviceId.isPresent() && pairLocalPort.isPresent() &&
327 newLocations.stream().anyMatch(location -> location.deviceId().equals(pairDeviceId.get())) &&
328 newLocations.stream().noneMatch(location -> location.deviceId().equals(prevLocation.deviceId()))) {
Charles Chan65238242017-06-22 18:03:14 -0700329 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
330 // when the host is untagged
331 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(prevLocation)).orElse(hostVlanId);
332
333 processBridgingRule(prevLocation.deviceId(), pairLocalPort.get(), hostMac, vlanId, false);
334 newIps.forEach(ip ->
335 processRoutingRule(prevLocation.deviceId(), pairLocalPort.get(), hostMac, vlanId,
336 ip, false));
337 return;
338 }
Charles Chanf9a52702017-06-16 15:19:24 -0700339
Charles Chan50bb6ef2018-04-18 18:41:05 -0700340 // Remove flows for unchanged IPs only when the host moves from a switch to another.
Charles Chanf9a52702017-06-16 15:19:24 -0700341 // Otherwise, do not remove and let the adding part update the old flow
342 if (!newDeviceIds.contains(prevLocation.deviceId())) {
Charles Chan65238242017-06-22 18:03:14 -0700343 processBridgingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId, true);
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700344 Sets.intersection(prevIps, newIps).forEach(ip -> {
345 if (doubleTaggedHost) {
346 processDoubleTaggedRoutingRule(prevLocation.deviceId(), prevLocation.port(),
347 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, true);
348 } else {
349 processRoutingRule(prevLocation.deviceId(), prevLocation.port(),
350 hostMac, hostVlanId, ip, true);
351 }
352 });
Charles Chanf9a52702017-06-16 15:19:24 -0700353 }
354
355 // Remove bridging rules if new interface vlan is different from old interface vlan
356 // Otherwise, do not remove and let the adding part update the old flow
357 if (newLocations.stream().noneMatch(newLocation -> {
358 VlanId oldAssignedVlan = srManager.getInternalVlanId(prevLocation);
359 VlanId newAssignedVlan = srManager.getInternalVlanId(newLocation);
360 // Host is tagged and the new location has the host vlan in vlan-tagged
Charles Chan971d7ba2018-05-01 11:50:20 -0700361 return srManager.interfaceService.getTaggedVlanId(newLocation).contains(hostVlanId) ||
Charles Chanf9a52702017-06-16 15:19:24 -0700362 (oldAssignedVlan != null && newAssignedVlan != null &&
363 // Host is untagged and the new location has the same assigned vlan
364 oldAssignedVlan.equals(newAssignedVlan));
365 })) {
Charles Chan65238242017-06-22 18:03:14 -0700366 processBridgingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId, true);
Charles Chanf9a52702017-06-16 15:19:24 -0700367 }
368
369 // Remove routing rules for unchanged IPs if none of the subnet of new location contains
370 // the IP. Otherwise, do not remove and let the adding part update the old flow
371 Sets.intersection(prevIps, newIps).forEach(ip -> {
372 if (newLocations.stream().noneMatch(newLocation ->
373 srManager.deviceConfiguration.inSameSubnet(newLocation, ip))) {
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700374 if (doubleTaggedHost) {
375 processDoubleTaggedRoutingRule(prevLocation.deviceId(), prevLocation.port(),
376 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, true);
377 } else {
378 processRoutingRule(prevLocation.deviceId(), prevLocation.port(),
379 hostMac, hostVlanId, ip, true);
380 }
Charles Chanf9a52702017-06-16 15:19:24 -0700381 }
Charles Chan93e71ba2016-04-29 14:38:22 -0700382 });
Charles Chanf9a52702017-06-16 15:19:24 -0700383 });
384
385 // For each new location, add all new IPs.
Charles Chan50bb6ef2018-04-18 18:41:05 -0700386 Sets.difference(newLocations, prevLocations).forEach(newLocation -> {
Charles Chan65238242017-06-22 18:03:14 -0700387 processBridgingRule(newLocation.deviceId(), newLocation.port(), hostMac, hostVlanId, false);
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700388 newIps.forEach(ip -> {
389 if (doubleTaggedHost) {
390 processDoubleTaggedRoutingRule(newLocation.deviceId(), newLocation.port(),
391 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, false);
392 } else {
Charles Chan65238242017-06-22 18:03:14 -0700393 processRoutingRule(newLocation.deviceId(), newLocation.port(), hostMac, hostVlanId,
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700394 ip, false);
395 }
396 });
Charles Chan9bd0e5a2018-04-25 18:51:46 -0400397
398 // Probe on pair device when host move
399 // Majorly for the 2nd step of [1A/x, 1B/x] -> [1A/x, 1B/y] -> [1A/y, 1B/y]
400 // But will also cover [1A/x] -> [1A/y] -> [1A/y, 1B/y]
401 if (srManager.activeProbing) {
Charles Chanf82d1552018-07-23 15:27:34 -0700402
Charles Chan9bd0e5a2018-04-25 18:51:46 -0400403 srManager.getPairDeviceId(newLocation.deviceId()).ifPresent(pairDeviceId ->
404 srManager.getPairLocalPort(pairDeviceId).ifPresent(pairRemotePort ->
405 probe(host, newLocation, pairDeviceId, pairRemotePort)
406 )
407 );
408 }
Charles Chanf9a52702017-06-16 15:19:24 -0700409 });
410
411 // For each unchanged location, add new IPs and remove old IPs.
Charles Chan50bb6ef2018-04-18 18:41:05 -0700412 Sets.intersection(newLocations, prevLocations).forEach(unchangedLocation -> {
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700413 Sets.difference(prevIps, newIps).forEach(ip -> {
414 if (doubleTaggedHost) {
415 processDoubleTaggedRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(),
416 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, true);
417 } else {
418 processRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(),
419 hostMac, hostVlanId, ip, true);
420 }
421 });
Charles Chanf9a52702017-06-16 15:19:24 -0700422
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700423 Sets.difference(newIps, prevIps).forEach(ip -> {
424 if (doubleTaggedHost) {
425 processDoubleTaggedRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(),
426 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, false);
427 } else {
428 processRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(),
429 hostMac, hostVlanId, ip, false);
430 }
431 });
Charles Chanf82d1552018-07-23 15:27:34 -0700432
433 // Verify existing location and see if it is still valid
434 srManager.probingService.probeHost(host, unchangedLocation, ProbeMode.VERIFY);
Charles Chanf9a52702017-06-16 15:19:24 -0700435 });
Saurav Das45f48152018-01-18 12:07:33 -0800436
437 // ensure dual-homed host locations have viable uplinks
Saurav Das9a554292018-04-27 18:42:30 -0700438 if (newLocations.size() > prevLocations.size() || srManager.singleHomedDown) {
Saurav Das45f48152018-01-18 12:07:33 -0800439 newLocations.forEach(loc -> {
440 if (srManager.mastershipService.isLocalMaster(loc.deviceId())) {
Saurav Das9a554292018-04-27 18:42:30 -0700441 srManager.linkHandler.checkUplinksForHost(loc);
Saurav Das45f48152018-01-18 12:07:33 -0800442 }
443 });
444 }
Charles Chan93e71ba2016-04-29 14:38:22 -0700445 }
446
Charles Chanf9a52702017-06-16 15:19:24 -0700447 void processHostUpdatedEvent(HostEvent event) {
Charles Chan47933752017-11-30 15:37:50 -0800448 Host host = event.subject();
pierb84e9192019-05-17 20:47:06 +0200449 hostWorkers.execute(() -> processHostUpdatedEventInternal(event), host.id().hashCode());
450 }
451
452 private void processHostUpdatedEventInternal(HostEvent event) {
453 Host host = event.subject();
Charles Chan47933752017-11-30 15:37:50 -0800454 MacAddress hostMac = host.mac();
455 VlanId hostVlanId = host.vlan();
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700456 EthType hostTpid = host.tpid();
Charles Chand7e62142019-11-25 09:47:22 -0800457 Set<HostLocation> locations = effectiveLocations(host);
Charles Chan93e71ba2016-04-29 14:38:22 -0700458 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
Charles Chan47933752017-11-30 15:37:50 -0800459 Set<IpAddress> newIps = host.ipAddresses();
Charles Chan3d650a62017-11-20 08:46:24 -0800460 log.info("Host {}/{} is updated", hostMac, hostVlanId);
Charles Chan93e71ba2016-04-29 14:38:22 -0700461
Charles Chan2ff1bac2018-03-29 16:03:41 -0700462 locations.forEach(location -> {
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700463 Sets.difference(prevIps, newIps).forEach(ip -> {
464 if (isDoubleTaggedHost(host)) {
465 processDoubleTaggedRoutingRule(location.deviceId(), location.port(), hostMac,
466 host.innerVlan(), hostVlanId, hostTpid, ip, true);
467 } else {
468 processRoutingRule(location.deviceId(), location.port(), hostMac,
469 hostVlanId, ip, true);
470 }
471 });
472 Sets.difference(newIps, prevIps).forEach(ip -> {
473 if (isDoubleTaggedHost(host)) {
474 processDoubleTaggedRoutingRule(location.deviceId(), location.port(), hostMac,
475 host.innerVlan(), hostVlanId, hostTpid, ip, false);
476 } else {
477 processRoutingRule(location.deviceId(), location.port(), hostMac,
478 hostVlanId, ip, false);
479 }
480 });
Charles Chanf9a52702017-06-16 15:19:24 -0700481 });
Charles Chan3d650a62017-11-20 08:46:24 -0800482
483 // Use the pair link temporarily before the second location of a dual-homed host shows up.
484 // This do not affect single-homed hosts since the flow will be blocked in
485 // processBridgingRule or processRoutingRule due to VLAN or IP mismatch respectively
Charles Chan47933752017-11-30 15:37:50 -0800486 locations.forEach(location ->
Charles Chan3d650a62017-11-20 08:46:24 -0800487 srManager.getPairDeviceId(location.deviceId()).ifPresent(pairDeviceId -> {
Charles Chan2ff1bac2018-03-29 16:03:41 -0700488 if (locations.stream().noneMatch(l -> l.deviceId().equals(pairDeviceId))) {
Charles Chan47933752017-11-30 15:37:50 -0800489 Set<IpAddress> ipsToAdd = Sets.difference(newIps, prevIps);
490 Set<IpAddress> ipsToRemove = Sets.difference(prevIps, newIps);
491
Saurav Das9a554292018-04-27 18:42:30 -0700492 srManager.getPairLocalPort(pairDeviceId).ifPresent(pairRemotePort -> {
Charles Chan3d650a62017-11-20 08:46:24 -0800493 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
494 // when the host is untagged
Charles Chanf76de302018-06-15 18:54:18 -0700495 VlanId vlanId = vlanForPairPort(hostVlanId, location);
496 if (vlanId == null) {
497 return;
498 }
Charles Chan3d650a62017-11-20 08:46:24 -0800499
Charles Chan47933752017-11-30 15:37:50 -0800500 ipsToRemove.forEach(ip ->
Charles Chan3d650a62017-11-20 08:46:24 -0800501 processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, true)
502 );
Charles Chan47933752017-11-30 15:37:50 -0800503 ipsToAdd.forEach(ip ->
Charles Chan3d650a62017-11-20 08:46:24 -0800504 processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, false)
505 );
Charles Chan47933752017-11-30 15:37:50 -0800506
507 if (srManager.activeProbing) {
508 probe(host, location, pairDeviceId, pairRemotePort);
509 }
Charles Chan3d650a62017-11-20 08:46:24 -0800510 });
511 }
Charles Chan47933752017-11-30 15:37:50 -0800512 })
513 );
514 }
515
516 /**
517 * When a non-pair port comes up, probe each host on the pair device if
518 * (1) the host is tagged and the tagged vlan of current port contains host vlan; or
519 * (2) the host is untagged and the internal vlan is the same on the host port and current port.
520 *
521 * @param cp connect point
522 */
Charles Chand7e62142019-11-25 09:47:22 -0800523 // TODO Current implementation does not handle dual-homed hosts with auxiliary locations.
Charles Chan47933752017-11-30 15:37:50 -0800524 void processPortUp(ConnectPoint cp) {
Saurav Das9a554292018-04-27 18:42:30 -0700525 if (cp.port().equals(srManager.getPairLocalPort(cp.deviceId()).orElse(null))) {
Charles Chan47933752017-11-30 15:37:50 -0800526 return;
527 }
528 if (srManager.activeProbing) {
529 srManager.getPairDeviceId(cp.deviceId())
pierb84e9192019-05-17 20:47:06 +0200530 .ifPresent(pairDeviceId -> srManager.hostService.getConnectedHosts(pairDeviceId).forEach(
531 host -> hostWorkers.execute(() -> probingIfNecessary(host, pairDeviceId, cp),
532 host.id().hashCode())));
533 }
534 }
535
Charles Chand7e62142019-11-25 09:47:22 -0800536 // TODO Current implementation does not handle dual-homed hosts with auxiliary locations.
pierb84e9192019-05-17 20:47:06 +0200537 private void probingIfNecessary(Host host, DeviceId pairDeviceId, ConnectPoint cp) {
538 if (isHostInVlanOfPort(host, pairDeviceId, cp)) {
539 srManager.probingService.probeHost(host, cp, ProbeMode.DISCOVER);
Charles Chan47933752017-11-30 15:37:50 -0800540 }
541 }
542
543 /**
544 * Checks if given host located on given device id matches VLAN config of current port.
545 *
546 * @param host host to check
547 * @param deviceId device id to check
548 * @param cp current connect point
549 * @return true if the host located at deviceId matches the VLAN config on cp
550 */
551 private boolean isHostInVlanOfPort(Host host, DeviceId deviceId, ConnectPoint cp) {
552 VlanId internalVlan = srManager.getInternalVlanId(cp);
Charles Chan971d7ba2018-05-01 11:50:20 -0700553 Set<VlanId> taggedVlan = srManager.interfaceService.getTaggedVlanId(cp);
Charles Chan47933752017-11-30 15:37:50 -0800554
555 return taggedVlan.contains(host.vlan()) ||
Charles Chand7e62142019-11-25 09:47:22 -0800556 (internalVlan != null && effectiveLocations(host).stream()
Charles Chan47933752017-11-30 15:37:50 -0800557 .filter(l -> l.deviceId().equals(deviceId))
558 .map(srManager::getInternalVlanId)
559 .anyMatch(internalVlan::equals));
560 }
561
562 /**
563 * Send a probe on all locations with the same VLAN on pair device, excluding pair port.
564 *
565 * @param host host to probe
566 * @param location newly discovered host location
567 * @param pairDeviceId pair device id
568 * @param pairRemotePort pair remote port
569 */
Charles Chand7e62142019-11-25 09:47:22 -0800570 // TODO Current implementation does not handle dual-homed hosts with auxiliary locations.
Charles Chan47933752017-11-30 15:37:50 -0800571 private void probe(Host host, ConnectPoint location, DeviceId pairDeviceId, PortNumber pairRemotePort) {
Mayank Tiwaria61b0a82018-10-29 18:27:35 -0400572 //Check if the host still exists in the host store
573 if (hostService.getHost(host.id()) == null) {
574 log.debug("Host entry for host {} no more present. Aborting hostprobe discover for this host", host.id());
575 return;
576 }
577
Charles Chan47933752017-11-30 15:37:50 -0800578 VlanId vlanToProbe = host.vlan().equals(VlanId.NONE) ?
579 srManager.getInternalVlanId(location) : host.vlan();
Mayank Tiwaria61b0a82018-10-29 18:27:35 -0400580 if (srManager.symmetricProbing) {
581 srManager.interfaceService.getInterfaces().stream()
582 .filter(i -> i.vlanTagged().contains(vlanToProbe) ||
583 i.vlanUntagged().equals(vlanToProbe) ||
584 i.vlanNative().equals(vlanToProbe))
585 .filter(i -> i.connectPoint().deviceId().equals(pairDeviceId))
586 .filter(i -> i.connectPoint().port().equals(location.port()))
587 .forEach(i -> {
588 log.debug("Probing host {} on pair device {}", host.id(), i.connectPoint());
589 srManager.probingService.probeHost(host, i.connectPoint(), ProbeMode.DISCOVER);
590 });
591 } else {
592 srManager.interfaceService.getInterfaces().stream()
593 .filter(i -> i.vlanTagged().contains(vlanToProbe) ||
594 i.vlanUntagged().equals(vlanToProbe) ||
595 i.vlanNative().equals(vlanToProbe))
596 .filter(i -> i.connectPoint().deviceId().equals(pairDeviceId))
597 .filter(i -> !i.connectPoint().port().equals(pairRemotePort))
598 .forEach(i -> {
599 log.debug("Probing host {} on pair device {}", host.id(), i.connectPoint());
600 srManager.probingService.probeHost(host, i.connectPoint(), ProbeMode.DISCOVER);
601 });
602 }
Charles Chan93e71ba2016-04-29 14:38:22 -0700603 }
604
605 /**
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700606 * Populates or revokes a bridging rule on given deviceId that matches given mac,
607 * given vlan and output to given port.
Charles Chanb7b4c932017-06-15 19:25:25 -0700608 *
609 * @param deviceId device ID
610 * @param port port
611 * @param mac mac address
612 * @param vlanId VLAN ID
613 * @param revoke true to revoke the rule; false to populate
Charles Chan9797ebb2020-02-14 13:23:57 -0800614 * @return future that includes the flow objective if succeeded, null if otherwise
Charles Chanb7b4c932017-06-15 19:25:25 -0700615 */
Charles Chan9797ebb2020-02-14 13:23:57 -0800616 private CompletableFuture<Objective> processBridgingRule(DeviceId deviceId, PortNumber port, MacAddress mac,
Charles Chanb7b4c932017-06-15 19:25:25 -0700617 VlanId vlanId, boolean revoke) {
Charles Chan2ff1bac2018-03-29 16:03:41 -0700618 log.info("{} bridging entry for host {}/{} at {}:{}", revoke ? "Revoking" : "Populating",
Charles Chanb7b4c932017-06-15 19:25:25 -0700619 mac, vlanId, deviceId, port);
620
Charles Chan2ff1bac2018-03-29 16:03:41 -0700621 if (!revoke) {
Charles Chan9797ebb2020-02-14 13:23:57 -0800622 return srManager.defaultRoutingHandler.populateBridging(deviceId, port, mac, vlanId);
Charles Chan2ff1bac2018-03-29 16:03:41 -0700623 } else {
Charles Chan9797ebb2020-02-14 13:23:57 -0800624 return srManager.defaultRoutingHandler.revokeBridging(deviceId, port, mac, vlanId);
Charles Chanb7b4c932017-06-15 19:25:25 -0700625 }
Charles Chanb7b4c932017-06-15 19:25:25 -0700626 }
627
628 /**
629 * Populate or revoke a routing rule on given deviceId that matches given ip,
630 * set destination mac to given mac, set vlan to given vlan and output to given port.
631 *
632 * @param deviceId device ID
633 * @param port port
634 * @param mac mac address
635 * @param vlanId VLAN ID
636 * @param ip IP address
637 * @param revoke true to revoke the rule; false to populate
Charles Chan9797ebb2020-02-14 13:23:57 -0800638 * @return future that includes the flow objective if succeeded, null if otherwise
Charles Chanb7b4c932017-06-15 19:25:25 -0700639 */
Charles Chan9797ebb2020-02-14 13:23:57 -0800640 private CompletableFuture<Objective> processRoutingRule(DeviceId deviceId, PortNumber port, MacAddress mac,
Charles Chanb7b4c932017-06-15 19:25:25 -0700641 VlanId vlanId, IpAddress ip, boolean revoke) {
642 ConnectPoint location = new ConnectPoint(deviceId, port);
Charles Chanf9a52702017-06-16 15:19:24 -0700643 if (!srManager.deviceConfiguration.inSameSubnet(location, ip)) {
644 log.info("{} is not included in the subnet config of {}/{}. Ignored.", ip, deviceId, port);
Charles Chan9797ebb2020-02-14 13:23:57 -0800645 return CompletableFuture.completedFuture(null);
Charles Chanb7b4c932017-06-15 19:25:25 -0700646 }
Charles Chanb7b4c932017-06-15 19:25:25 -0700647
Charles Chanf9a52702017-06-16 15:19:24 -0700648 log.info("{} routing rule for {} at {}", revoke ? "Revoking" : "Populating", ip, location);
649 if (revoke) {
Charles Chan9797ebb2020-02-14 13:23:57 -0800650 return srManager.defaultRoutingHandler.revokeRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port, true);
Charles Chanf9a52702017-06-16 15:19:24 -0700651 } else {
Charles Chan9797ebb2020-02-14 13:23:57 -0800652 return srManager.defaultRoutingHandler.populateRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port, true);
Charles Chan6ea94fc2016-05-10 17:29:47 -0700653 }
Charles Chan6ea94fc2016-05-10 17:29:47 -0700654 }
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700655
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700656 /**
657 * Populate or revoke a routing rule and egress rules on given deviceId that matches given IP,
658 * to set destination mac to given mac, set inner vlan and outer vlan to given vlans,
659 * set outer TPID, and output to given port.
660 *
661 * @param deviceId device ID
662 * @param port port
663 * @param mac mac address
664 * @param innerVlan inner VLAN ID
665 * @param outerVlan outer VLAN ID
666 * @param outerTpid outer TPID
667 * @param ip IP address
668 * @param revoke true to revoke the rule; false to populate
669 */
670 private void processDoubleTaggedRoutingRule(DeviceId deviceId, PortNumber port,
671 MacAddress mac, VlanId innerVlan,
672 VlanId outerVlan, EthType outerTpid,
673 IpAddress ip, boolean revoke) {
Charles Chanb67dfdd2018-07-24 16:40:35 -0700674 if (!srManager.routeDoubleTaggedHosts) {
675 log.debug("Routing for double tagged host is disabled. Ignore {}/{}/{}", mac, outerVlan, innerVlan);
Charles Chan57465d32018-07-19 14:55:31 -0700676 return;
677 }
678
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700679 ConnectPoint location = new ConnectPoint(deviceId, port);
680 if (!srManager.deviceConfiguration.inSameSubnet(location, ip)) {
681 log.info("{} is not included in the subnet config of {}/{}. Ignored.", ip, deviceId, port);
682 return;
683 }
684 log.info("{} routing rule for double-tagged host {} at {}",
685 revoke ? "Revoking" : "Populating", ip, location);
686 if (revoke) {
687 srManager.defaultRoutingHandler.revokeDoubleTaggedRoute(
688 deviceId, ip.toIpPrefix(), mac, innerVlan, outerVlan, outerTpid, port);
689 } else {
690 srManager.defaultRoutingHandler.populateDoubleTaggedRoute(
691 deviceId, ip.toIpPrefix(), mac, innerVlan, outerVlan, outerTpid, port);
692 }
693 }
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700694
Charles Chan57465d32018-07-19 14:55:31 -0700695 void populateAllDoubleTaggedHost() {
Charles Chanb67dfdd2018-07-24 16:40:35 -0700696 log.info("Enabling routing for all double tagged hosts");
Charles Chan57465d32018-07-19 14:55:31 -0700697 Sets.newHashSet(srManager.hostService.getHosts()).stream().filter(this::isDoubleTaggedHost)
Charles Chand7e62142019-11-25 09:47:22 -0800698 .forEach(h -> effectiveLocations(h).forEach(l ->
Charles Chan57465d32018-07-19 14:55:31 -0700699 h.ipAddresses().forEach(i ->
700 processDoubleTaggedRoutingRule(l.deviceId(), l.port(), h.mac(), h.innerVlan(),
701 h.vlan(), h.tpid(), i, false)
702 )
703 )
704 );
705 }
706
707 void revokeAllDoubleTaggedHost() {
Charles Chanb67dfdd2018-07-24 16:40:35 -0700708 log.info("Disabling routing for all double tagged hosts");
Charles Chan57465d32018-07-19 14:55:31 -0700709 Sets.newHashSet(srManager.hostService.getHosts()).stream().filter(this::isDoubleTaggedHost)
Charles Chand7e62142019-11-25 09:47:22 -0800710 .forEach(h -> effectiveLocations(h).forEach(l ->
Charles Chan57465d32018-07-19 14:55:31 -0700711 h.ipAddresses().forEach(i ->
712 processDoubleTaggedRoutingRule(l.deviceId(), l.port(), h.mac(), h.innerVlan(),
713 h.vlan(), h.tpid(), i, true)
714 )
715 )
716 );
717 }
718
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700719 /**
Charles Chanf76de302018-06-15 18:54:18 -0700720 * Returns VLAN ID to be used to program redirection flow on pair port.
721 *
722 * @param hostVlanId host VLAN ID
723 * @param location host location
724 * @return VLAN ID to be used; Or null if host VLAN does not match the interface config
725 */
726 VlanId vlanForPairPort(VlanId hostVlanId, ConnectPoint location) {
727 VlanId internalVlan = srManager.getInternalVlanId(location);
728 Set<VlanId> taggedVlan = srManager.interfaceService.getTaggedVlanId(location);
729
730 if (!hostVlanId.equals(VlanId.NONE) && taggedVlan.contains(hostVlanId)) {
731 return hostVlanId;
732 } else if (hostVlanId.equals(VlanId.NONE) && internalVlan != null) {
733 return internalVlan;
734 } else {
735 log.warn("VLAN mismatch. hostVlan={}, location={}, internalVlan={}, taggedVlan={}",
736 hostVlanId, location, internalVlan, taggedVlan);
737 return null;
738 }
739 }
740
741 /**
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700742 * Update forwarding objective for unicast bridging and unicast routing.
743 * Also check the validity of updated interface configuration on VLAN.
744 *
745 * @param deviceId device ID that host attaches to
746 * @param portNum port number that host attaches to
747 * @param vlanId Vlan ID configured on the switch port
748 * @param popVlan true to pop Vlan tag at TrafficTreatment, false otherwise
749 * @param install true to populate the objective, false to revoke
750 */
Charles Chand7e62142019-11-25 09:47:22 -0800751 // TODO Current implementation does not handle dual-homed hosts with auxiliary locations.
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700752 void processIntfVlanUpdatedEvent(DeviceId deviceId, PortNumber portNum, VlanId vlanId,
pierb84e9192019-05-17 20:47:06 +0200753 boolean popVlan, boolean install) {
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700754 ConnectPoint connectPoint = new ConnectPoint(deviceId, portNum);
755 Set<Host> hosts = hostService.getConnectedHosts(connectPoint);
756
757 if (hosts == null || hosts.size() == 0) {
Charles Chan10b2fee2018-04-21 00:44:29 -0700758 log.debug("processIntfVlanUpdatedEvent: No hosts connected to {}", connectPoint);
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700759 return;
760 }
761
pierb84e9192019-05-17 20:47:06 +0200762 hosts.forEach(host -> hostWorkers.execute(() -> processIntfVlanUpdatedEventInternal(
763 host, deviceId, portNum, vlanId, popVlan, install), host.id().hashCode()));
764 }
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700765
pierb84e9192019-05-17 20:47:06 +0200766 private void processIntfVlanUpdatedEventInternal(Host host, DeviceId deviceId, PortNumber portNum,
767 VlanId vlanId, boolean popVlan, boolean install) {
768 MacAddress mac = host.mac();
769 VlanId hostVlanId = host.vlan();
770
Shibu Vijayakumar9ed929a2018-03-01 15:45:59 -0800771 if (!install) {
772 // Do not check the host validity. Just remove all rules corresponding to the vlan id
773 // Revoke forwarding objective for bridging to the host
774 srManager.defaultRoutingHandler.updateBridging(deviceId, portNum, mac, vlanId, popVlan, false);
775
776 // Revoke forwarding objective and corresponding simple Next objective
777 // for each Host and IP address connected to given port
778 host.ipAddresses().forEach(ipAddress ->
779 srManager.routingRulePopulator.updateFwdObj(deviceId, portNum, ipAddress.toIpPrefix(),
780 mac, vlanId, popVlan, false)
pierb84e9192019-05-17 20:47:06 +0200781 );
Shibu Vijayakumar9ed929a2018-03-01 15:45:59 -0800782 } else {
783 // Check whether the host vlan is valid for new interface configuration
784 if ((!popVlan && hostVlanId.equals(vlanId)) ||
785 (popVlan && hostVlanId.equals(VlanId.NONE))) {
786 srManager.defaultRoutingHandler.updateBridging(deviceId, portNum, mac, vlanId, popVlan, true);
787 // Update Forwarding objective and corresponding simple Next objective
788 // for each Host and IP address connected to given port
789 host.ipAddresses().forEach(ipAddress ->
790 srManager.routingRulePopulator.updateFwdObj(deviceId, portNum, ipAddress.toIpPrefix(),
791 mac, vlanId, popVlan, true)
792 );
793 }
pierb84e9192019-05-17 20:47:06 +0200794 }
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700795 }
796
797 /**
798 * Populate or revoke routing rule for each host, according to the updated
799 * subnet configuration on the interface.
800 * @param cp connect point of the updated interface
801 * @param ipPrefixSet IP Prefixes added or removed
802 * @param install true if IP Prefixes added, false otherwise
803 */
Charles Chand7e62142019-11-25 09:47:22 -0800804 // TODO Current implementation does not handle dual-homed hosts with auxiliary locations.
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700805 void processIntfIpUpdatedEvent(ConnectPoint cp, Set<IpPrefix> ipPrefixSet, boolean install) {
806 Set<Host> hosts = hostService.getConnectedHosts(cp);
807
808 if (hosts == null || hosts.size() == 0) {
Charles Chan10b2fee2018-04-21 00:44:29 -0700809 log.debug("processIntfIpUpdatedEvent: No hosts connected to {}", cp);
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700810 return;
811 }
812
813 // Check whether the host IP address is in the interface's subnet
pierb84e9192019-05-17 20:47:06 +0200814 hosts.forEach(host -> hostWorkers.execute(() -> processIntfIpUpdatedEventInternal(
815 host, cp, ipPrefixSet, install)));
816 }
817
818 private void processIntfIpUpdatedEventInternal(Host host, ConnectPoint cp, Set<IpPrefix> ipPrefixSet,
819 boolean install) {
820 host.ipAddresses().forEach(hostIpAddress -> {
821 ipPrefixSet.forEach(ipPrefix -> {
822 if (install && ipPrefix.contains(hostIpAddress)) {
823 srManager.defaultRoutingHandler.populateRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
824 host.mac(), host.vlan(), cp.port(), true);
825 } else if (!install && ipPrefix.contains(hostIpAddress)) {
826 srManager.defaultRoutingHandler.revokeRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
827 host.mac(), host.vlan(), cp.port(), true);
828 }
829 });
830 });
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700831 }
Saurav Das45f48152018-01-18 12:07:33 -0800832
833 /**
834 * Returns the set of portnumbers on the given device that are part of the
835 * locations for dual-homed hosts.
836 *
837 * @param deviceId the given deviceId
838 * @return set of port numbers on given device that are dual-homed host
839 * locations. May be empty if no dual homed hosts are connected to
840 * the given device
841 */
842 Set<PortNumber> getDualHomedHostPorts(DeviceId deviceId) {
843 Set<PortNumber> dualHomedLocations = new HashSet<>();
844 srManager.hostService.getConnectedHosts(deviceId).stream()
Charles Chand7e62142019-11-25 09:47:22 -0800845 .filter(host -> effectiveLocations(host).size() == 2)
846 .forEach(host -> effectiveLocations(host).stream()
Saurav Das45f48152018-01-18 12:07:33 -0800847 .filter(loc -> loc.deviceId().equals(deviceId))
848 .forEach(loc -> dualHomedLocations.add(loc.port())));
849 return dualHomedLocations;
850 }
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700851
852 /**
853 * Checks if the given host is double-tagged or not.
854 *
855 * @param host host to check
856 * @return true if it is double-tagged, false otherwise
857 */
858 private boolean isDoubleTaggedHost(Host host) {
859 return !host.innerVlan().equals(VlanId.NONE);
860 }
861
Charles Chand7e62142019-11-25 09:47:22 -0800862 /**
863 * Returns effective locations of given host.
864 *
865 * @param host host to check
866 * @return auxLocations of the host if exists, or locations of the host otherwise.
867 */
868 Set<HostLocation> effectiveLocations(Host host) {
869 return (host.auxLocations() != null) ? host.auxLocations() : host.locations();
870 }
871
Charles Chand2990362016-04-18 13:44:03 -0700872}