blob: dcc77f89b472c3e1a63e7896f7ae2a8bcc808478 [file] [log] [blame]
Charles Chan1eaf4802016-04-18 13:44:03 -07001/*
Brian O'Connor0947d7e2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Charles Chan1eaf4802016-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
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -070019import org.onlab.packet.EthType;
Charles Chan1eaf4802016-04-18 13:44:03 -070020import org.onlab.packet.IpAddress;
Jonghwan Hyune5ef7622017-08-25 17:48:36 -070021import org.onlab.packet.IpPrefix;
Charles Chan1eaf4802016-04-18 13:44:03 -070022import org.onlab.packet.MacAddress;
23import org.onlab.packet.VlanId;
piercf557922019-05-17 20:47:06 +020024import org.onlab.util.PredictableExecutor;
Charles Chan10b0fb72017-02-02 16:20:42 -080025import org.onosproject.net.ConnectPoint;
Charles Chan1eaf4802016-04-18 13:44:03 -070026import org.onosproject.net.DeviceId;
Charles Chan370a65b2016-05-10 17:29:47 -070027import org.onosproject.net.Host;
Charles Chanc22cef32016-04-29 14:38:22 -070028import org.onosproject.net.HostLocation;
Charles Chan1eaf4802016-04-18 13:44:03 -070029import org.onosproject.net.PortNumber;
Charles Chan1eaf4802016-04-18 13:44:03 -070030import org.onosproject.net.host.HostEvent;
31import org.onosproject.net.host.HostService;
Charles Chanc6bcdf92018-06-01 16:33:48 -070032import org.onosproject.net.host.ProbeMode;
Charles Chan1eaf4802016-04-18 13:44:03 -070033import org.slf4j.Logger;
34import org.slf4j.LoggerFactory;
35
Saurav Dasf9332192017-02-18 14:05:44 -080036import com.google.common.collect.Sets;
Charles Chan3ed34d82017-06-22 18:03:14 -070037
Saurav Dase6c448a2018-01-18 12:07:33 -080038import java.util.HashSet;
Charles Chan3ed34d82017-06-22 18:03:14 -070039import java.util.Optional;
Charles Chan1eaf4802016-04-18 13:44:03 -070040import java.util.Set;
Charles Chand9265a32017-06-16 15:19:24 -070041import java.util.stream.Collectors;
42
43import static com.google.common.base.Preconditions.checkArgument;
piercf557922019-05-17 20:47:06 +020044import static org.onlab.util.Tools.groupedThreads;
Charles Chan1eaf4802016-04-18 13:44:03 -070045
46/**
47 * Handles host-related events.
48 */
49public class HostHandler {
50 private static final Logger log = LoggerFactory.getLogger(HostHandler.class);
Charles Chan873661e2017-11-30 15:37:50 -080051
Charles Chan114aec72017-06-19 14:00:53 -070052 protected final SegmentRoutingManager srManager;
Charles Chan1eaf4802016-04-18 13:44:03 -070053 private HostService hostService;
piercf557922019-05-17 20:47:06 +020054 // Host workers - 0 will leverage available processors
55 private static final int DEFAULT_THREADS = 0;
56 protected PredictableExecutor hostWorkers;
Charles Chan1eaf4802016-04-18 13:44:03 -070057
58 /**
59 * Constructs the HostHandler.
60 *
61 * @param srManager Segment Routing manager
62 */
Charles Chand9265a32017-06-16 15:19:24 -070063 HostHandler(SegmentRoutingManager srManager) {
Charles Chan1eaf4802016-04-18 13:44:03 -070064 this.srManager = srManager;
Charles Chan1eaf4802016-04-18 13:44:03 -070065 hostService = srManager.hostService;
piercf557922019-05-17 20:47:06 +020066 this.hostWorkers = new PredictableExecutor(DEFAULT_THREADS,
67 groupedThreads("onos/sr", "h-worker-%d", log));
68 }
69
70 /**
71 * Shutdowns the workers.
72 */
73 void terminate() {
74 hostWorkers.shutdown();
Charles Chan1eaf4802016-04-18 13:44:03 -070075 }
76
Charles Chandebfea32016-10-24 14:52:01 -070077 protected void init(DeviceId devId) {
piercf557922019-05-17 20:47:06 +020078 // Init hosts in parallel using hostWorkers executor
79 hostService.getHosts().forEach(
80 host -> hostWorkers.execute(() -> initHost(host, devId), host.id().hashCode())
Charles Chand9265a32017-06-16 15:19:24 -070081 );
Charles Chanc22cef32016-04-29 14:38:22 -070082 }
Charles Chan1eaf4802016-04-18 13:44:03 -070083
piercf557922019-05-17 20:47:06 +020084 private void initHost(Host host, DeviceId deviceId) {
Charles Chana2ccb582019-11-25 09:47:22 -080085 effectiveLocations(host).forEach(location -> {
piercf557922019-05-17 20:47:06 +020086 if (location.deviceId().equals(deviceId) ||
87 location.deviceId().equals(srManager.getPairDeviceId(deviceId).orElse(null))) {
88 processHostAddedAtLocation(host, location);
89 }
90 });
91 }
92
Charles Chand9265a32017-06-16 15:19:24 -070093 void processHostAddedEvent(HostEvent event) {
piercf557922019-05-17 20:47:06 +020094 Host host = event.subject();
95 hostWorkers.execute(() -> processHostAdded(host), host.id().hashCode());
Charles Chanc22cef32016-04-29 14:38:22 -070096 }
97
Charles Chand9265a32017-06-16 15:19:24 -070098 private void processHostAdded(Host host) {
Charles Chana2ccb582019-11-25 09:47:22 -080099 effectiveLocations(host).forEach(location -> processHostAddedAtLocation(host, location));
Saurav Dase6c448a2018-01-18 12:07:33 -0800100 // ensure dual-homed host locations have viable uplinks
Charles Chana2ccb582019-11-25 09:47:22 -0800101 if (effectiveLocations(host).size() > 1 || srManager.singleHomedDown) {
102 effectiveLocations(host).forEach(loc -> {
Saurav Dase6c448a2018-01-18 12:07:33 -0800103 if (srManager.mastershipService.isLocalMaster(loc.deviceId())) {
Saurav Dasec683dc2018-04-27 18:42:30 -0700104 srManager.linkHandler.checkUplinksForHost(loc);
Saurav Dase6c448a2018-01-18 12:07:33 -0800105 }
106 });
107 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700108 }
109
Charles Chand9265a32017-06-16 15:19:24 -0700110 void processHostAddedAtLocation(Host host, HostLocation location) {
Charles Chana2ccb582019-11-25 09:47:22 -0800111 checkArgument(effectiveLocations(host).contains(location), "{} is not a location of {}", location, host);
Charles Chand9265a32017-06-16 15:19:24 -0700112
Charles Chanceb2a2e2017-09-12 18:57:47 -0700113 MacAddress hostMac = host.mac();
114 VlanId hostVlanId = host.vlan();
Charles Chana2ccb582019-11-25 09:47:22 -0800115 Set<HostLocation> locations = effectiveLocations(host);
Charles Chand9265a32017-06-16 15:19:24 -0700116 Set<IpAddress> ips = host.ipAddresses();
Charles Chanceb2a2e2017-09-12 18:57:47 -0700117 log.info("Host {}/{} is added at {}", hostMac, hostVlanId, locations);
Charles Chand9265a32017-06-16 15:19:24 -0700118
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700119 if (isDoubleTaggedHost(host)) {
120 ips.forEach(ip ->
121 processDoubleTaggedRoutingRule(location.deviceId(), location.port(), hostMac,
122 host.innerVlan(), hostVlanId, host.tpid(), ip, false)
123 );
124 } else {
125 processBridgingRule(location.deviceId(), location.port(), hostMac, hostVlanId, false);
126 ips.forEach(ip ->
Charles Chand66d6712018-03-29 16:03:41 -0700127 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, false)
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700128 );
129 }
Charles Chanceb2a2e2017-09-12 18:57:47 -0700130
131 // Use the pair link temporarily before the second location of a dual-homed host shows up.
132 // This do not affect single-homed hosts since the flow will be blocked in
133 // processBridgingRule or processRoutingRule due to VLAN or IP mismatch respectively
134 srManager.getPairDeviceId(location.deviceId()).ifPresent(pairDeviceId -> {
Charles Chana2ccb582019-11-25 09:47:22 -0800135 if (effectiveLocations(host).stream().noneMatch(l -> l.deviceId().equals(pairDeviceId))) {
Saurav Dasec683dc2018-04-27 18:42:30 -0700136 srManager.getPairLocalPort(pairDeviceId).ifPresent(pairRemotePort -> {
Charles Chanceb2a2e2017-09-12 18:57:47 -0700137 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
138 // when the host is untagged
Charles Chan868c9572018-06-15 18:54:18 -0700139 VlanId vlanId = vlanForPairPort(hostVlanId, location);
140 if (vlanId == null) {
141 return;
142 }
Charles Chanceb2a2e2017-09-12 18:57:47 -0700143
144 processBridgingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, false);
145 ips.forEach(ip -> processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId,
146 ip, false));
Charles Chan873661e2017-11-30 15:37:50 -0800147
148 if (srManager.activeProbing) {
149 probe(host, location, pairDeviceId, pairRemotePort);
150 }
Charles Chanceb2a2e2017-09-12 18:57:47 -0700151 });
152 }
153 });
Ruchi Sahota07869322019-05-09 17:26:14 -0400154
155 int nextId = srManager.getMacVlanNextObjectiveId(location.deviceId(), hostMac, hostVlanId, null, false);
156 if (nextId != -1) {
157 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(hostVlanId);
158 log.debug(" Updating next objective for device {}, host {}/{}, port {}, nextid {}",
159 location.deviceId(), hostMac, vlanId, location.port(), nextId);
160 srManager.updateMacVlanTreatment(location.deviceId(), hostMac, vlanId,
161 location.port(), nextId);
162 }
Charles Chand9265a32017-06-16 15:19:24 -0700163 }
164
165 void processHostRemovedEvent(HostEvent event) {
piercf557922019-05-17 20:47:06 +0200166 Host host = event.subject();
167 hostWorkers.execute(() -> processHostRemoved(host), host.id().hashCode());
Charles Chan41f5ec02016-06-13 18:54:31 -0700168 }
169
Charles Chand9265a32017-06-16 15:19:24 -0700170 private void processHostRemoved(Host host) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700171 MacAddress hostMac = host.mac();
172 VlanId hostVlanId = host.vlan();
Charles Chana2ccb582019-11-25 09:47:22 -0800173 Set<HostLocation> locations = effectiveLocations(host);
Charles Chan41f5ec02016-06-13 18:54:31 -0700174 Set<IpAddress> ips = host.ipAddresses();
Charles Chan3ed34d82017-06-22 18:03:14 -0700175 log.info("Host {}/{} is removed from {}", hostMac, hostVlanId, locations);
Charles Chanc22cef32016-04-29 14:38:22 -0700176
Charles Chan910be6a2017-08-23 14:46:43 -0700177 locations.forEach(location -> {
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700178 if (isDoubleTaggedHost(host)) {
179 ips.forEach(ip ->
180 processDoubleTaggedRoutingRule(location.deviceId(), location.port(), hostMac,
181 host.innerVlan(), hostVlanId, host.tpid(), ip, true)
182 );
183 } else {
184 processBridgingRule(location.deviceId(), location.port(), hostMac, hostVlanId, true);
185 ips.forEach(ip ->
Charles Chand66d6712018-03-29 16:03:41 -0700186 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, true)
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700187 );
188 }
Charles Chan3ed34d82017-06-22 18:03:14 -0700189
190 // Also remove redirection flows on the pair device if exists.
191 Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(location.deviceId());
Saurav Dasec683dc2018-04-27 18:42:30 -0700192 Optional<PortNumber> pairLocalPort = srManager.getPairLocalPort(location.deviceId());
Charles Chand66d6712018-03-29 16:03:41 -0700193 if (pairDeviceId.isPresent() && pairLocalPort.isPresent()) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700194 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
195 // when the host is untagged
Charles Chan868c9572018-06-15 18:54:18 -0700196 VlanId vlanId = vlanForPairPort(hostVlanId, location);
197 if (vlanId == null) {
198 return;
199 }
Charles Chan3ed34d82017-06-22 18:03:14 -0700200
201 processBridgingRule(pairDeviceId.get(), pairLocalPort.get(), hostMac, vlanId, true);
202 ips.forEach(ip ->
203 processRoutingRule(pairDeviceId.get(), pairLocalPort.get(), hostMac, vlanId,
204 ip, true));
205 }
Charles Chanbd84dd52018-06-21 19:07:12 -0700206
207 // Delete prefix from sr-device-subnet when the next hop host is removed
208 srManager.routeService.getRouteTables().forEach(tableId -> {
209 srManager.routeService.getRoutes(tableId).forEach(routeInfo -> {
210 if (routeInfo.allRoutes().stream().anyMatch(rr -> ips.contains(rr.nextHop()))) {
211 log.debug("HostRemoved. removeSubnet {}, {}", location, routeInfo.prefix());
212 srManager.deviceConfiguration.removeSubnet(location, routeInfo.prefix());
213 }
214 });
215 });
Ruchi Sahota07869322019-05-09 17:26:14 -0400216
Charles Chand9265a32017-06-16 15:19:24 -0700217 });
Charles Chanc22cef32016-04-29 14:38:22 -0700218 }
219
Charles Chand9265a32017-06-16 15:19:24 -0700220 void processHostMovedEvent(HostEvent event) {
Charles Chan4b5769a2018-04-25 18:51:46 -0400221 Host host = event.subject();
piercf557922019-05-17 20:47:06 +0200222 hostWorkers.execute(() -> processHostMovedEventInternal(event), host.id().hashCode());
223 }
224
225 private void processHostMovedEventInternal(HostEvent event) {
Charles Chana2ccb582019-11-25 09:47:22 -0800226 // This method will be called when one of the following value has changed:
227 // (1) locations (2) auxLocations or (3) both locations and auxLocations.
228 // We only need to proceed when effectiveLocation has changed.
229 Set<HostLocation> newLocations = effectiveLocations(event.subject());
230 Set<HostLocation> prevLocations = effectiveLocations(event.prevSubject());
231
232 if (newLocations.equals(prevLocations)) {
233 log.info("effectiveLocations of {} has not changed. Skipping {}", event.subject().id(), event);
234 return;
235 }
236
piercf557922019-05-17 20:47:06 +0200237 Host host = event.subject();
Charles Chana2ccb582019-11-25 09:47:22 -0800238 Host prevHost = event.prevSubject();
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700239 MacAddress hostMac = host.mac();
240 VlanId hostVlanId = host.vlan();
Charles Chana2ccb582019-11-25 09:47:22 -0800241 Set<IpAddress> prevIps = prevHost.ipAddresses();
Charles Chan4b5769a2018-04-25 18:51:46 -0400242 Set<IpAddress> newIps = host.ipAddresses();
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700243 EthType hostTpid = host.tpid();
244 boolean doubleTaggedHost = isDoubleTaggedHost(host);
Charles Chan4b5769a2018-04-25 18:51:46 -0400245
Charles Chan873661e2017-11-30 15:37:50 -0800246 log.info("Host {}/{} is moved from {} to {}", hostMac, hostVlanId, prevLocations, newLocations);
Charles Chand9265a32017-06-16 15:19:24 -0700247 Set<DeviceId> newDeviceIds = newLocations.stream().map(HostLocation::deviceId)
248 .collect(Collectors.toSet());
Charles Chanc22cef32016-04-29 14:38:22 -0700249
Charles Chand9265a32017-06-16 15:19:24 -0700250 // For each old location
Charles Chand66d6712018-03-29 16:03:41 -0700251 Sets.difference(prevLocations, newLocations).forEach(prevLocation -> {
Charles Chan3ed34d82017-06-22 18:03:14 -0700252 // Remove routing rules for old IPs
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700253 Sets.difference(prevIps, newIps).forEach(ip -> {
254 if (doubleTaggedHost) {
255 processDoubleTaggedRoutingRule(prevLocation.deviceId(), prevLocation.port(),
256 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, true);
257 } else {
Charles Chan3ed34d82017-06-22 18:03:14 -0700258 processRoutingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId,
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700259 ip, true);
260 }
261 });
Charles Chan3ed34d82017-06-22 18:03:14 -0700262
263 // Redirect the flows to pair link if configured
264 // Note: Do not continue removing any rule
265 Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(prevLocation.deviceId());
Saurav Dasec683dc2018-04-27 18:42:30 -0700266 Optional<PortNumber> pairLocalPort = srManager.getPairLocalPort(prevLocation.deviceId());
Charles Chan6d43c162018-10-11 12:35:36 -0700267 if (pairDeviceId.isPresent() && pairLocalPort.isPresent() &&
268 newLocations.stream().anyMatch(location -> location.deviceId().equals(pairDeviceId.get())) &&
269 newLocations.stream().noneMatch(location -> location.deviceId().equals(prevLocation.deviceId()))) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700270 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
271 // when the host is untagged
272 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(prevLocation)).orElse(hostVlanId);
273
274 processBridgingRule(prevLocation.deviceId(), pairLocalPort.get(), hostMac, vlanId, false);
275 newIps.forEach(ip ->
276 processRoutingRule(prevLocation.deviceId(), pairLocalPort.get(), hostMac, vlanId,
277 ip, false));
278 return;
279 }
Charles Chand9265a32017-06-16 15:19:24 -0700280
Charles Chanfbcb8812018-04-18 18:41:05 -0700281 // Remove flows for unchanged IPs only when the host moves from a switch to another.
Charles Chand9265a32017-06-16 15:19:24 -0700282 // Otherwise, do not remove and let the adding part update the old flow
283 if (!newDeviceIds.contains(prevLocation.deviceId())) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700284 processBridgingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId, true);
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700285 Sets.intersection(prevIps, newIps).forEach(ip -> {
286 if (doubleTaggedHost) {
287 processDoubleTaggedRoutingRule(prevLocation.deviceId(), prevLocation.port(),
288 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, true);
289 } else {
290 processRoutingRule(prevLocation.deviceId(), prevLocation.port(),
291 hostMac, hostVlanId, ip, true);
292 }
293 });
Charles Chand9265a32017-06-16 15:19:24 -0700294 }
295
296 // Remove bridging rules if new interface vlan is different from old interface vlan
297 // Otherwise, do not remove and let the adding part update the old flow
298 if (newLocations.stream().noneMatch(newLocation -> {
299 VlanId oldAssignedVlan = srManager.getInternalVlanId(prevLocation);
300 VlanId newAssignedVlan = srManager.getInternalVlanId(newLocation);
301 // Host is tagged and the new location has the host vlan in vlan-tagged
Charles Chan098ca202018-05-01 11:50:20 -0700302 return srManager.interfaceService.getTaggedVlanId(newLocation).contains(hostVlanId) ||
Charles Chand9265a32017-06-16 15:19:24 -0700303 (oldAssignedVlan != null && newAssignedVlan != null &&
304 // Host is untagged and the new location has the same assigned vlan
305 oldAssignedVlan.equals(newAssignedVlan));
306 })) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700307 processBridgingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId, true);
Charles Chand9265a32017-06-16 15:19:24 -0700308 }
309
310 // Remove routing rules for unchanged IPs if none of the subnet of new location contains
311 // the IP. Otherwise, do not remove and let the adding part update the old flow
312 Sets.intersection(prevIps, newIps).forEach(ip -> {
313 if (newLocations.stream().noneMatch(newLocation ->
314 srManager.deviceConfiguration.inSameSubnet(newLocation, ip))) {
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700315 if (doubleTaggedHost) {
316 processDoubleTaggedRoutingRule(prevLocation.deviceId(), prevLocation.port(),
317 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, true);
318 } else {
319 processRoutingRule(prevLocation.deviceId(), prevLocation.port(),
320 hostMac, hostVlanId, ip, true);
321 }
Charles Chand9265a32017-06-16 15:19:24 -0700322 }
Charles Chanc22cef32016-04-29 14:38:22 -0700323 });
Charles Chand9265a32017-06-16 15:19:24 -0700324 });
325
326 // For each new location, add all new IPs.
Charles Chanfbcb8812018-04-18 18:41:05 -0700327 Sets.difference(newLocations, prevLocations).forEach(newLocation -> {
Charles Chan3ed34d82017-06-22 18:03:14 -0700328 processBridgingRule(newLocation.deviceId(), newLocation.port(), hostMac, hostVlanId, false);
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700329 newIps.forEach(ip -> {
330 if (doubleTaggedHost) {
331 processDoubleTaggedRoutingRule(newLocation.deviceId(), newLocation.port(),
332 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, false);
333 } else {
Charles Chan3ed34d82017-06-22 18:03:14 -0700334 processRoutingRule(newLocation.deviceId(), newLocation.port(), hostMac, hostVlanId,
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700335 ip, false);
336 }
337 });
Charles Chan4b5769a2018-04-25 18:51:46 -0400338
339 // Probe on pair device when host move
340 // Majorly for the 2nd step of [1A/x, 1B/x] -> [1A/x, 1B/y] -> [1A/y, 1B/y]
341 // But will also cover [1A/x] -> [1A/y] -> [1A/y, 1B/y]
342 if (srManager.activeProbing) {
Charles Chan6e90fbb2018-07-23 15:27:34 -0700343
Charles Chan4b5769a2018-04-25 18:51:46 -0400344 srManager.getPairDeviceId(newLocation.deviceId()).ifPresent(pairDeviceId ->
345 srManager.getPairLocalPort(pairDeviceId).ifPresent(pairRemotePort ->
346 probe(host, newLocation, pairDeviceId, pairRemotePort)
347 )
348 );
349 }
Charles Chand9265a32017-06-16 15:19:24 -0700350 });
351
352 // For each unchanged location, add new IPs and remove old IPs.
Charles Chanfbcb8812018-04-18 18:41:05 -0700353 Sets.intersection(newLocations, prevLocations).forEach(unchangedLocation -> {
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700354 Sets.difference(prevIps, newIps).forEach(ip -> {
355 if (doubleTaggedHost) {
356 processDoubleTaggedRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(),
357 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, true);
358 } else {
359 processRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(),
360 hostMac, hostVlanId, ip, true);
361 }
362 });
Charles Chand9265a32017-06-16 15:19:24 -0700363
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700364 Sets.difference(newIps, prevIps).forEach(ip -> {
365 if (doubleTaggedHost) {
366 processDoubleTaggedRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(),
367 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, false);
368 } else {
369 processRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(),
370 hostMac, hostVlanId, ip, false);
371 }
372 });
Charles Chan6e90fbb2018-07-23 15:27:34 -0700373
374 // Verify existing location and see if it is still valid
375 srManager.probingService.probeHost(host, unchangedLocation, ProbeMode.VERIFY);
Charles Chand9265a32017-06-16 15:19:24 -0700376 });
Saurav Dase6c448a2018-01-18 12:07:33 -0800377
378 // ensure dual-homed host locations have viable uplinks
Saurav Dasec683dc2018-04-27 18:42:30 -0700379 if (newLocations.size() > prevLocations.size() || srManager.singleHomedDown) {
Saurav Dase6c448a2018-01-18 12:07:33 -0800380 newLocations.forEach(loc -> {
381 if (srManager.mastershipService.isLocalMaster(loc.deviceId())) {
Saurav Dasec683dc2018-04-27 18:42:30 -0700382 srManager.linkHandler.checkUplinksForHost(loc);
Saurav Dase6c448a2018-01-18 12:07:33 -0800383 }
384 });
385 }
Charles Chanc22cef32016-04-29 14:38:22 -0700386 }
387
Charles Chand9265a32017-06-16 15:19:24 -0700388 void processHostUpdatedEvent(HostEvent event) {
Charles Chan873661e2017-11-30 15:37:50 -0800389 Host host = event.subject();
piercf557922019-05-17 20:47:06 +0200390 hostWorkers.execute(() -> processHostUpdatedEventInternal(event), host.id().hashCode());
391 }
392
393 private void processHostUpdatedEventInternal(HostEvent event) {
394 Host host = event.subject();
Charles Chan873661e2017-11-30 15:37:50 -0800395 MacAddress hostMac = host.mac();
396 VlanId hostVlanId = host.vlan();
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700397 EthType hostTpid = host.tpid();
Charles Chana2ccb582019-11-25 09:47:22 -0800398 Set<HostLocation> locations = effectiveLocations(host);
Charles Chanc22cef32016-04-29 14:38:22 -0700399 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
Charles Chan873661e2017-11-30 15:37:50 -0800400 Set<IpAddress> newIps = host.ipAddresses();
Charles Chanb3c1faf2017-11-20 08:46:24 -0800401 log.info("Host {}/{} is updated", hostMac, hostVlanId);
Charles Chanc22cef32016-04-29 14:38:22 -0700402
Charles Chand66d6712018-03-29 16:03:41 -0700403 locations.forEach(location -> {
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700404 Sets.difference(prevIps, newIps).forEach(ip -> {
405 if (isDoubleTaggedHost(host)) {
406 processDoubleTaggedRoutingRule(location.deviceId(), location.port(), hostMac,
407 host.innerVlan(), hostVlanId, hostTpid, ip, true);
408 } else {
409 processRoutingRule(location.deviceId(), location.port(), hostMac,
410 hostVlanId, ip, true);
411 }
412 });
413 Sets.difference(newIps, prevIps).forEach(ip -> {
414 if (isDoubleTaggedHost(host)) {
415 processDoubleTaggedRoutingRule(location.deviceId(), location.port(), hostMac,
416 host.innerVlan(), hostVlanId, hostTpid, ip, false);
417 } else {
418 processRoutingRule(location.deviceId(), location.port(), hostMac,
419 hostVlanId, ip, false);
420 }
421 });
Charles Chand9265a32017-06-16 15:19:24 -0700422 });
Charles Chanb3c1faf2017-11-20 08:46:24 -0800423
424 // Use the pair link temporarily before the second location of a dual-homed host shows up.
425 // This do not affect single-homed hosts since the flow will be blocked in
426 // processBridgingRule or processRoutingRule due to VLAN or IP mismatch respectively
Charles Chan873661e2017-11-30 15:37:50 -0800427 locations.forEach(location ->
Charles Chanb3c1faf2017-11-20 08:46:24 -0800428 srManager.getPairDeviceId(location.deviceId()).ifPresent(pairDeviceId -> {
Charles Chand66d6712018-03-29 16:03:41 -0700429 if (locations.stream().noneMatch(l -> l.deviceId().equals(pairDeviceId))) {
Charles Chan873661e2017-11-30 15:37:50 -0800430 Set<IpAddress> ipsToAdd = Sets.difference(newIps, prevIps);
431 Set<IpAddress> ipsToRemove = Sets.difference(prevIps, newIps);
432
Saurav Dasec683dc2018-04-27 18:42:30 -0700433 srManager.getPairLocalPort(pairDeviceId).ifPresent(pairRemotePort -> {
Charles Chanb3c1faf2017-11-20 08:46:24 -0800434 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
435 // when the host is untagged
Charles Chan868c9572018-06-15 18:54:18 -0700436 VlanId vlanId = vlanForPairPort(hostVlanId, location);
437 if (vlanId == null) {
438 return;
439 }
Charles Chanb3c1faf2017-11-20 08:46:24 -0800440
Charles Chan873661e2017-11-30 15:37:50 -0800441 ipsToRemove.forEach(ip ->
Charles Chanb3c1faf2017-11-20 08:46:24 -0800442 processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, true)
443 );
Charles Chan873661e2017-11-30 15:37:50 -0800444 ipsToAdd.forEach(ip ->
Charles Chanb3c1faf2017-11-20 08:46:24 -0800445 processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, false)
446 );
Charles Chan873661e2017-11-30 15:37:50 -0800447
448 if (srManager.activeProbing) {
449 probe(host, location, pairDeviceId, pairRemotePort);
450 }
Charles Chanb3c1faf2017-11-20 08:46:24 -0800451 });
452 }
Charles Chan873661e2017-11-30 15:37:50 -0800453 })
454 );
455 }
456
457 /**
458 * When a non-pair port comes up, probe each host on the pair device if
459 * (1) the host is tagged and the tagged vlan of current port contains host vlan; or
460 * (2) the host is untagged and the internal vlan is the same on the host port and current port.
461 *
462 * @param cp connect point
463 */
Charles Chana2ccb582019-11-25 09:47:22 -0800464 // TODO Current implementation does not handle dual-homed hosts with auxiliary locations.
Charles Chan873661e2017-11-30 15:37:50 -0800465 void processPortUp(ConnectPoint cp) {
Saurav Dasec683dc2018-04-27 18:42:30 -0700466 if (cp.port().equals(srManager.getPairLocalPort(cp.deviceId()).orElse(null))) {
Charles Chan873661e2017-11-30 15:37:50 -0800467 return;
468 }
469 if (srManager.activeProbing) {
470 srManager.getPairDeviceId(cp.deviceId())
piercf557922019-05-17 20:47:06 +0200471 .ifPresent(pairDeviceId -> srManager.hostService.getConnectedHosts(pairDeviceId).forEach(
472 host -> hostWorkers.execute(() -> probingIfNecessary(host, pairDeviceId, cp),
473 host.id().hashCode())));
474 }
475 }
476
Charles Chana2ccb582019-11-25 09:47:22 -0800477 // TODO Current implementation does not handle dual-homed hosts with auxiliary locations.
piercf557922019-05-17 20:47:06 +0200478 private void probingIfNecessary(Host host, DeviceId pairDeviceId, ConnectPoint cp) {
479 if (isHostInVlanOfPort(host, pairDeviceId, cp)) {
480 srManager.probingService.probeHost(host, cp, ProbeMode.DISCOVER);
Charles Chan873661e2017-11-30 15:37:50 -0800481 }
482 }
483
484 /**
485 * Checks if given host located on given device id matches VLAN config of current port.
486 *
487 * @param host host to check
488 * @param deviceId device id to check
489 * @param cp current connect point
490 * @return true if the host located at deviceId matches the VLAN config on cp
491 */
492 private boolean isHostInVlanOfPort(Host host, DeviceId deviceId, ConnectPoint cp) {
493 VlanId internalVlan = srManager.getInternalVlanId(cp);
Charles Chan098ca202018-05-01 11:50:20 -0700494 Set<VlanId> taggedVlan = srManager.interfaceService.getTaggedVlanId(cp);
Charles Chan873661e2017-11-30 15:37:50 -0800495
496 return taggedVlan.contains(host.vlan()) ||
Charles Chana2ccb582019-11-25 09:47:22 -0800497 (internalVlan != null && effectiveLocations(host).stream()
Charles Chan873661e2017-11-30 15:37:50 -0800498 .filter(l -> l.deviceId().equals(deviceId))
499 .map(srManager::getInternalVlanId)
500 .anyMatch(internalVlan::equals));
501 }
502
503 /**
504 * Send a probe on all locations with the same VLAN on pair device, excluding pair port.
505 *
506 * @param host host to probe
507 * @param location newly discovered host location
508 * @param pairDeviceId pair device id
509 * @param pairRemotePort pair remote port
510 */
Charles Chana2ccb582019-11-25 09:47:22 -0800511 // TODO Current implementation does not handle dual-homed hosts with auxiliary locations.
Charles Chan873661e2017-11-30 15:37:50 -0800512 private void probe(Host host, ConnectPoint location, DeviceId pairDeviceId, PortNumber pairRemotePort) {
Mayank Tiwarif7942402018-10-29 18:27:35 -0400513 //Check if the host still exists in the host store
514 if (hostService.getHost(host.id()) == null) {
515 log.debug("Host entry for host {} no more present. Aborting hostprobe discover for this host", host.id());
516 return;
517 }
518
Charles Chan873661e2017-11-30 15:37:50 -0800519 VlanId vlanToProbe = host.vlan().equals(VlanId.NONE) ?
520 srManager.getInternalVlanId(location) : host.vlan();
Mayank Tiwarif7942402018-10-29 18:27:35 -0400521 if (srManager.symmetricProbing) {
522 srManager.interfaceService.getInterfaces().stream()
523 .filter(i -> i.vlanTagged().contains(vlanToProbe) ||
524 i.vlanUntagged().equals(vlanToProbe) ||
525 i.vlanNative().equals(vlanToProbe))
526 .filter(i -> i.connectPoint().deviceId().equals(pairDeviceId))
527 .filter(i -> i.connectPoint().port().equals(location.port()))
528 .forEach(i -> {
529 log.debug("Probing host {} on pair device {}", host.id(), i.connectPoint());
530 srManager.probingService.probeHost(host, i.connectPoint(), ProbeMode.DISCOVER);
531 });
532 } else {
533 srManager.interfaceService.getInterfaces().stream()
534 .filter(i -> i.vlanTagged().contains(vlanToProbe) ||
535 i.vlanUntagged().equals(vlanToProbe) ||
536 i.vlanNative().equals(vlanToProbe))
537 .filter(i -> i.connectPoint().deviceId().equals(pairDeviceId))
538 .filter(i -> !i.connectPoint().port().equals(pairRemotePort))
539 .forEach(i -> {
540 log.debug("Probing host {} on pair device {}", host.id(), i.connectPoint());
541 srManager.probingService.probeHost(host, i.connectPoint(), ProbeMode.DISCOVER);
542 });
543 }
Charles Chanc22cef32016-04-29 14:38:22 -0700544 }
545
546 /**
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700547 * Populates or revokes a bridging rule on given deviceId that matches given mac,
548 * given vlan and output to given port.
Charles Chan9595e6a2017-06-15 19:25:25 -0700549 *
550 * @param deviceId device ID
551 * @param port port
552 * @param mac mac address
553 * @param vlanId VLAN ID
554 * @param revoke true to revoke the rule; false to populate
555 */
556 private void processBridgingRule(DeviceId deviceId, PortNumber port, MacAddress mac,
557 VlanId vlanId, boolean revoke) {
Charles Chand66d6712018-03-29 16:03:41 -0700558 log.info("{} bridging entry for host {}/{} at {}:{}", revoke ? "Revoking" : "Populating",
Charles Chan9595e6a2017-06-15 19:25:25 -0700559 mac, vlanId, deviceId, port);
560
Charles Chand66d6712018-03-29 16:03:41 -0700561 if (!revoke) {
562 srManager.defaultRoutingHandler.populateBridging(deviceId, port, mac, vlanId);
563 } else {
564 srManager.defaultRoutingHandler.revokeBridging(deviceId, port, mac, vlanId);
Charles Chan9595e6a2017-06-15 19:25:25 -0700565 }
Charles Chan9595e6a2017-06-15 19:25:25 -0700566 }
567
568 /**
569 * Populate or revoke a routing rule on given deviceId that matches given ip,
570 * set destination mac to given mac, set vlan to given vlan and output to given port.
571 *
572 * @param deviceId device ID
573 * @param port port
574 * @param mac mac address
575 * @param vlanId VLAN ID
576 * @param ip IP address
577 * @param revoke true to revoke the rule; false to populate
578 */
579 private void processRoutingRule(DeviceId deviceId, PortNumber port, MacAddress mac,
580 VlanId vlanId, IpAddress ip, boolean revoke) {
581 ConnectPoint location = new ConnectPoint(deviceId, port);
Charles Chand9265a32017-06-16 15:19:24 -0700582 if (!srManager.deviceConfiguration.inSameSubnet(location, ip)) {
583 log.info("{} is not included in the subnet config of {}/{}. Ignored.", ip, deviceId, port);
584 return;
Charles Chan9595e6a2017-06-15 19:25:25 -0700585 }
Charles Chan9595e6a2017-06-15 19:25:25 -0700586
Charles Chand9265a32017-06-16 15:19:24 -0700587 log.info("{} routing rule for {} at {}", revoke ? "Revoking" : "Populating", ip, location);
588 if (revoke) {
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000589 srManager.defaultRoutingHandler.revokeRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port, true);
Charles Chand9265a32017-06-16 15:19:24 -0700590 } else {
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000591 srManager.defaultRoutingHandler.populateRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port, true);
Charles Chan370a65b2016-05-10 17:29:47 -0700592 }
Charles Chan370a65b2016-05-10 17:29:47 -0700593 }
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700594
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700595 /**
596 * Populate or revoke a routing rule and egress rules on given deviceId that matches given IP,
597 * to set destination mac to given mac, set inner vlan and outer vlan to given vlans,
598 * set outer TPID, and output to given port.
599 *
600 * @param deviceId device ID
601 * @param port port
602 * @param mac mac address
603 * @param innerVlan inner VLAN ID
604 * @param outerVlan outer VLAN ID
605 * @param outerTpid outer TPID
606 * @param ip IP address
607 * @param revoke true to revoke the rule; false to populate
608 */
609 private void processDoubleTaggedRoutingRule(DeviceId deviceId, PortNumber port,
610 MacAddress mac, VlanId innerVlan,
611 VlanId outerVlan, EthType outerTpid,
612 IpAddress ip, boolean revoke) {
Charles Chanfbca55e2018-07-24 16:40:35 -0700613 if (!srManager.routeDoubleTaggedHosts) {
614 log.debug("Routing for double tagged host is disabled. Ignore {}/{}/{}", mac, outerVlan, innerVlan);
Charles Chan4eb73bb2018-07-19 14:55:31 -0700615 return;
616 }
617
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700618 ConnectPoint location = new ConnectPoint(deviceId, port);
619 if (!srManager.deviceConfiguration.inSameSubnet(location, ip)) {
620 log.info("{} is not included in the subnet config of {}/{}. Ignored.", ip, deviceId, port);
621 return;
622 }
623 log.info("{} routing rule for double-tagged host {} at {}",
624 revoke ? "Revoking" : "Populating", ip, location);
625 if (revoke) {
626 srManager.defaultRoutingHandler.revokeDoubleTaggedRoute(
627 deviceId, ip.toIpPrefix(), mac, innerVlan, outerVlan, outerTpid, port);
628 } else {
629 srManager.defaultRoutingHandler.populateDoubleTaggedRoute(
630 deviceId, ip.toIpPrefix(), mac, innerVlan, outerVlan, outerTpid, port);
631 }
632 }
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700633
Charles Chan4eb73bb2018-07-19 14:55:31 -0700634 void populateAllDoubleTaggedHost() {
Charles Chanfbca55e2018-07-24 16:40:35 -0700635 log.info("Enabling routing for all double tagged hosts");
Charles Chan4eb73bb2018-07-19 14:55:31 -0700636 Sets.newHashSet(srManager.hostService.getHosts()).stream().filter(this::isDoubleTaggedHost)
Charles Chana2ccb582019-11-25 09:47:22 -0800637 .forEach(h -> effectiveLocations(h).forEach(l ->
Charles Chan4eb73bb2018-07-19 14:55:31 -0700638 h.ipAddresses().forEach(i ->
639 processDoubleTaggedRoutingRule(l.deviceId(), l.port(), h.mac(), h.innerVlan(),
640 h.vlan(), h.tpid(), i, false)
641 )
642 )
643 );
644 }
645
646 void revokeAllDoubleTaggedHost() {
Charles Chanfbca55e2018-07-24 16:40:35 -0700647 log.info("Disabling routing for all double tagged hosts");
Charles Chan4eb73bb2018-07-19 14:55:31 -0700648 Sets.newHashSet(srManager.hostService.getHosts()).stream().filter(this::isDoubleTaggedHost)
Charles Chana2ccb582019-11-25 09:47:22 -0800649 .forEach(h -> effectiveLocations(h).forEach(l ->
Charles Chan4eb73bb2018-07-19 14:55:31 -0700650 h.ipAddresses().forEach(i ->
651 processDoubleTaggedRoutingRule(l.deviceId(), l.port(), h.mac(), h.innerVlan(),
652 h.vlan(), h.tpid(), i, true)
653 )
654 )
655 );
656 }
657
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700658 /**
Charles Chan868c9572018-06-15 18:54:18 -0700659 * Returns VLAN ID to be used to program redirection flow on pair port.
660 *
661 * @param hostVlanId host VLAN ID
662 * @param location host location
663 * @return VLAN ID to be used; Or null if host VLAN does not match the interface config
664 */
665 VlanId vlanForPairPort(VlanId hostVlanId, ConnectPoint location) {
666 VlanId internalVlan = srManager.getInternalVlanId(location);
667 Set<VlanId> taggedVlan = srManager.interfaceService.getTaggedVlanId(location);
668
669 if (!hostVlanId.equals(VlanId.NONE) && taggedVlan.contains(hostVlanId)) {
670 return hostVlanId;
671 } else if (hostVlanId.equals(VlanId.NONE) && internalVlan != null) {
672 return internalVlan;
673 } else {
674 log.warn("VLAN mismatch. hostVlan={}, location={}, internalVlan={}, taggedVlan={}",
675 hostVlanId, location, internalVlan, taggedVlan);
676 return null;
677 }
678 }
679
680 /**
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700681 * Update forwarding objective for unicast bridging and unicast routing.
682 * Also check the validity of updated interface configuration on VLAN.
683 *
684 * @param deviceId device ID that host attaches to
685 * @param portNum port number that host attaches to
686 * @param vlanId Vlan ID configured on the switch port
687 * @param popVlan true to pop Vlan tag at TrafficTreatment, false otherwise
688 * @param install true to populate the objective, false to revoke
689 */
Charles Chana2ccb582019-11-25 09:47:22 -0800690 // TODO Current implementation does not handle dual-homed hosts with auxiliary locations.
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700691 void processIntfVlanUpdatedEvent(DeviceId deviceId, PortNumber portNum, VlanId vlanId,
piercf557922019-05-17 20:47:06 +0200692 boolean popVlan, boolean install) {
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700693 ConnectPoint connectPoint = new ConnectPoint(deviceId, portNum);
694 Set<Host> hosts = hostService.getConnectedHosts(connectPoint);
695
696 if (hosts == null || hosts.size() == 0) {
Charles Chan39b75522018-04-21 00:44:29 -0700697 log.debug("processIntfVlanUpdatedEvent: No hosts connected to {}", connectPoint);
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700698 return;
699 }
700
piercf557922019-05-17 20:47:06 +0200701 hosts.forEach(host -> hostWorkers.execute(() -> processIntfVlanUpdatedEventInternal(
702 host, deviceId, portNum, vlanId, popVlan, install), host.id().hashCode()));
703 }
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700704
piercf557922019-05-17 20:47:06 +0200705 private void processIntfVlanUpdatedEventInternal(Host host, DeviceId deviceId, PortNumber portNum,
706 VlanId vlanId, boolean popVlan, boolean install) {
707 MacAddress mac = host.mac();
708 VlanId hostVlanId = host.vlan();
709
710 // Check whether the host vlan is valid for new interface configuration
711 if ((!popVlan && hostVlanId.equals(vlanId)) ||
712 (popVlan && hostVlanId.equals(VlanId.NONE))) {
713 srManager.defaultRoutingHandler.updateBridging(deviceId, portNum, mac, vlanId, popVlan, install);
714 // Update Forwarding objective and corresponding simple Next objective
715 // for each host and IP address connected to given port
716 host.ipAddresses().forEach(ipAddress -> srManager.defaultRoutingHandler.updateFwdObj(
717 deviceId, portNum, ipAddress.toIpPrefix(), mac, vlanId, popVlan, install)
718 );
719 }
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700720 }
721
722 /**
723 * Populate or revoke routing rule for each host, according to the updated
724 * subnet configuration on the interface.
725 * @param cp connect point of the updated interface
726 * @param ipPrefixSet IP Prefixes added or removed
727 * @param install true if IP Prefixes added, false otherwise
728 */
Charles Chana2ccb582019-11-25 09:47:22 -0800729 // TODO Current implementation does not handle dual-homed hosts with auxiliary locations.
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700730 void processIntfIpUpdatedEvent(ConnectPoint cp, Set<IpPrefix> ipPrefixSet, boolean install) {
731 Set<Host> hosts = hostService.getConnectedHosts(cp);
732
733 if (hosts == null || hosts.size() == 0) {
Charles Chan39b75522018-04-21 00:44:29 -0700734 log.debug("processIntfIpUpdatedEvent: No hosts connected to {}", cp);
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700735 return;
736 }
737
738 // Check whether the host IP address is in the interface's subnet
piercf557922019-05-17 20:47:06 +0200739 hosts.forEach(host -> hostWorkers.execute(() -> processIntfIpUpdatedEventInternal(
740 host, cp, ipPrefixSet, install)));
741 }
742
743 private void processIntfIpUpdatedEventInternal(Host host, ConnectPoint cp, Set<IpPrefix> ipPrefixSet,
744 boolean install) {
745 host.ipAddresses().forEach(hostIpAddress -> {
746 ipPrefixSet.forEach(ipPrefix -> {
747 if (install && ipPrefix.contains(hostIpAddress)) {
748 srManager.defaultRoutingHandler.populateRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
749 host.mac(), host.vlan(), cp.port(), true);
750 } else if (!install && ipPrefix.contains(hostIpAddress)) {
751 srManager.defaultRoutingHandler.revokeRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
752 host.mac(), host.vlan(), cp.port(), true);
753 }
754 });
755 });
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700756 }
Saurav Dase6c448a2018-01-18 12:07:33 -0800757
758 /**
759 * Returns the set of portnumbers on the given device that are part of the
760 * locations for dual-homed hosts.
761 *
762 * @param deviceId the given deviceId
763 * @return set of port numbers on given device that are dual-homed host
764 * locations. May be empty if no dual homed hosts are connected to
765 * the given device
766 */
767 Set<PortNumber> getDualHomedHostPorts(DeviceId deviceId) {
768 Set<PortNumber> dualHomedLocations = new HashSet<>();
769 srManager.hostService.getConnectedHosts(deviceId).stream()
Charles Chana2ccb582019-11-25 09:47:22 -0800770 .filter(host -> effectiveLocations(host).size() == 2)
771 .forEach(host -> effectiveLocations(host).stream()
Saurav Dase6c448a2018-01-18 12:07:33 -0800772 .filter(loc -> loc.deviceId().equals(deviceId))
773 .forEach(loc -> dualHomedLocations.add(loc.port())));
774 return dualHomedLocations;
775 }
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700776
777 /**
778 * Checks if the given host is double-tagged or not.
779 *
780 * @param host host to check
781 * @return true if it is double-tagged, false otherwise
782 */
783 private boolean isDoubleTaggedHost(Host host) {
784 return !host.innerVlan().equals(VlanId.NONE);
785 }
786
Charles Chana2ccb582019-11-25 09:47:22 -0800787 /**
788 * Returns effective locations of given host.
789 *
790 * @param host host to check
791 * @return auxLocations of the host if exists, or locations of the host otherwise.
792 */
793 Set<HostLocation> effectiveLocations(Host host) {
794 return (host.auxLocations() != null) ? host.auxLocations() : host.locations();
795 }
796
Charles Chan1eaf4802016-04-18 13:44:03 -0700797}