blob: 5fdbf69f9ab126bbe8e0b7aaef5a6be438fbc388 [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
Charles Chan1eaf4802016-04-18 13:44:03 -070019import org.onlab.packet.IpAddress;
Jonghwan Hyune5ef7622017-08-25 17:48:36 -070020import org.onlab.packet.IpPrefix;
Charles Chan1eaf4802016-04-18 13:44:03 -070021import org.onlab.packet.MacAddress;
22import org.onlab.packet.VlanId;
Charles Chan10b0fb72017-02-02 16:20:42 -080023import org.onosproject.net.ConnectPoint;
Charles Chan1eaf4802016-04-18 13:44:03 -070024import org.onosproject.net.DeviceId;
Charles Chan370a65b2016-05-10 17:29:47 -070025import org.onosproject.net.Host;
Charles Chanc22cef32016-04-29 14:38:22 -070026import org.onosproject.net.HostLocation;
Charles Chan1eaf4802016-04-18 13:44:03 -070027import org.onosproject.net.PortNumber;
Charles Chan1eaf4802016-04-18 13:44:03 -070028import org.onosproject.net.host.HostEvent;
Charles Chan873661e2017-11-30 15:37:50 -080029import org.onosproject.net.host.HostLocationProbingService.ProbeMode;
Charles Chan1eaf4802016-04-18 13:44:03 -070030import org.onosproject.net.host.HostService;
31import org.slf4j.Logger;
32import org.slf4j.LoggerFactory;
33
Saurav Dasf9332192017-02-18 14:05:44 -080034import com.google.common.collect.Sets;
Charles Chan3ed34d82017-06-22 18:03:14 -070035
Saurav Dase6c448a2018-01-18 12:07:33 -080036import java.util.HashSet;
Charles Chan3ed34d82017-06-22 18:03:14 -070037import java.util.Optional;
Charles Chan1eaf4802016-04-18 13:44:03 -070038import java.util.Set;
Charles Chand9265a32017-06-16 15:19:24 -070039import java.util.stream.Collectors;
40
41import static com.google.common.base.Preconditions.checkArgument;
Charles Chan1eaf4802016-04-18 13:44:03 -070042
43/**
44 * Handles host-related events.
45 */
46public class HostHandler {
47 private static final Logger log = LoggerFactory.getLogger(HostHandler.class);
Charles Chan873661e2017-11-30 15:37:50 -080048 static final int HOST_MOVED_DELAY_MS = 1000;
49
Charles Chan114aec72017-06-19 14:00:53 -070050 protected final SegmentRoutingManager srManager;
Charles Chan1eaf4802016-04-18 13:44:03 -070051 private HostService hostService;
Charles Chan1eaf4802016-04-18 13:44:03 -070052
53 /**
54 * Constructs the HostHandler.
55 *
56 * @param srManager Segment Routing manager
57 */
Charles Chand9265a32017-06-16 15:19:24 -070058 HostHandler(SegmentRoutingManager srManager) {
Charles Chan1eaf4802016-04-18 13:44:03 -070059 this.srManager = srManager;
Charles Chan1eaf4802016-04-18 13:44:03 -070060 hostService = srManager.hostService;
Charles Chan1eaf4802016-04-18 13:44:03 -070061 }
62
Charles Chandebfea32016-10-24 14:52:01 -070063 protected void init(DeviceId devId) {
Charles Chand9265a32017-06-16 15:19:24 -070064 hostService.getHosts().forEach(host ->
65 host.locations().stream()
Charles Chan873661e2017-11-30 15:37:50 -080066 .filter(location -> location.deviceId().equals(devId) ||
67 location.deviceId().equals(srManager.getPairDeviceId(devId).orElse(null)))
Charles Chand9265a32017-06-16 15:19:24 -070068 .forEach(location -> processHostAddedAtLocation(host, location))
69 );
Charles Chanc22cef32016-04-29 14:38:22 -070070 }
Charles Chan1eaf4802016-04-18 13:44:03 -070071
Charles Chand9265a32017-06-16 15:19:24 -070072 void processHostAddedEvent(HostEvent event) {
Charles Chan41f5ec02016-06-13 18:54:31 -070073 processHostAdded(event.subject());
Charles Chanc22cef32016-04-29 14:38:22 -070074 }
75
Charles Chand9265a32017-06-16 15:19:24 -070076 private void processHostAdded(Host host) {
77 host.locations().forEach(location -> processHostAddedAtLocation(host, location));
Saurav Dase6c448a2018-01-18 12:07:33 -080078 // ensure dual-homed host locations have viable uplinks
79 if (host.locations().size() > 1) {
80 host.locations().forEach(loc -> {
81 if (srManager.mastershipService.isLocalMaster(loc.deviceId())) {
82 srManager.linkHandler.checkUplinksForDualHomedHosts(loc);
83 }
84 });
85 }
Charles Chan1eaf4802016-04-18 13:44:03 -070086 }
87
Charles Chand9265a32017-06-16 15:19:24 -070088 void processHostAddedAtLocation(Host host, HostLocation location) {
89 checkArgument(host.locations().contains(location), "{} is not a location of {}", location, host);
90
Charles Chanceb2a2e2017-09-12 18:57:47 -070091 MacAddress hostMac = host.mac();
92 VlanId hostVlanId = host.vlan();
Charles Chand9265a32017-06-16 15:19:24 -070093 Set<HostLocation> locations = host.locations();
94 Set<IpAddress> ips = host.ipAddresses();
Charles Chanceb2a2e2017-09-12 18:57:47 -070095 log.info("Host {}/{} is added at {}", hostMac, hostVlanId, locations);
Charles Chand9265a32017-06-16 15:19:24 -070096
Charles Chand66d6712018-03-29 16:03:41 -070097 processBridgingRule(location.deviceId(), location.port(), hostMac, hostVlanId, false);
98 ips.forEach(ip ->
99 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, false)
100 );
Charles Chanceb2a2e2017-09-12 18:57:47 -0700101
102 // Use the pair link temporarily before the second location of a dual-homed host shows up.
103 // This do not affect single-homed hosts since the flow will be blocked in
104 // processBridgingRule or processRoutingRule due to VLAN or IP mismatch respectively
105 srManager.getPairDeviceId(location.deviceId()).ifPresent(pairDeviceId -> {
Charles Chand66d6712018-03-29 16:03:41 -0700106 if (host.locations().stream().noneMatch(l -> l.deviceId().equals(pairDeviceId))) {
Charles Chanceb2a2e2017-09-12 18:57:47 -0700107 srManager.getPairLocalPorts(pairDeviceId).ifPresent(pairRemotePort -> {
108 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
109 // when the host is untagged
110 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(hostVlanId);
111
112 processBridgingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, false);
113 ips.forEach(ip -> processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId,
114 ip, false));
Charles Chan873661e2017-11-30 15:37:50 -0800115
116 if (srManager.activeProbing) {
117 probe(host, location, pairDeviceId, pairRemotePort);
118 }
Charles Chanceb2a2e2017-09-12 18:57:47 -0700119 });
120 }
121 });
Charles Chand9265a32017-06-16 15:19:24 -0700122 }
123
124 void processHostRemovedEvent(HostEvent event) {
Charles Chan41f5ec02016-06-13 18:54:31 -0700125 processHostRemoved(event.subject());
126 }
127
Charles Chand9265a32017-06-16 15:19:24 -0700128 private void processHostRemoved(Host host) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700129 MacAddress hostMac = host.mac();
130 VlanId hostVlanId = host.vlan();
Charles Chand9265a32017-06-16 15:19:24 -0700131 Set<HostLocation> locations = host.locations();
Charles Chan41f5ec02016-06-13 18:54:31 -0700132 Set<IpAddress> ips = host.ipAddresses();
Charles Chan3ed34d82017-06-22 18:03:14 -0700133 log.info("Host {}/{} is removed from {}", hostMac, hostVlanId, locations);
Charles Chanc22cef32016-04-29 14:38:22 -0700134
Charles Chan910be6a2017-08-23 14:46:43 -0700135 locations.forEach(location -> {
Charles Chand66d6712018-03-29 16:03:41 -0700136 processBridgingRule(location.deviceId(), location.port(), hostMac, hostVlanId, true);
137 ips.forEach(ip ->
138 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, true)
139 );
Charles Chan3ed34d82017-06-22 18:03:14 -0700140
141 // Also remove redirection flows on the pair device if exists.
142 Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(location.deviceId());
143 Optional<PortNumber> pairLocalPort = srManager.getPairLocalPorts(location.deviceId());
Charles Chand66d6712018-03-29 16:03:41 -0700144 if (pairDeviceId.isPresent() && pairLocalPort.isPresent()) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700145 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
146 // when the host is untagged
147 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(hostVlanId);
148
149 processBridgingRule(pairDeviceId.get(), pairLocalPort.get(), hostMac, vlanId, true);
150 ips.forEach(ip ->
151 processRoutingRule(pairDeviceId.get(), pairLocalPort.get(), hostMac, vlanId,
152 ip, true));
153 }
Charles Chand9265a32017-06-16 15:19:24 -0700154 });
Charles Chanc22cef32016-04-29 14:38:22 -0700155 }
156
Charles Chand9265a32017-06-16 15:19:24 -0700157 void processHostMovedEvent(HostEvent event) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700158 MacAddress hostMac = event.subject().mac();
159 VlanId hostVlanId = event.subject().vlan();
Charles Chand9265a32017-06-16 15:19:24 -0700160 Set<HostLocation> prevLocations = event.prevSubject().locations();
Charles Chanc22cef32016-04-29 14:38:22 -0700161 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
Charles Chand9265a32017-06-16 15:19:24 -0700162 Set<HostLocation> newLocations = event.subject().locations();
Charles Chanc22cef32016-04-29 14:38:22 -0700163 Set<IpAddress> newIps = event.subject().ipAddresses();
Charles Chanc22cef32016-04-29 14:38:22 -0700164
Charles Chanfbcb8812018-04-18 18:41:05 -0700165 processHostMoved(hostMac, hostVlanId, prevLocations, prevIps, newLocations, newIps);
Charles Chan873661e2017-11-30 15:37:50 -0800166 }
167
168 private void processHostMoved(MacAddress hostMac, VlanId hostVlanId, Set<HostLocation> prevLocations,
169 Set<IpAddress> prevIps, Set<HostLocation> newLocations, Set<IpAddress> newIps) {
170 log.info("Host {}/{} is moved from {} to {}", hostMac, hostVlanId, prevLocations, newLocations);
Charles Chand9265a32017-06-16 15:19:24 -0700171 Set<DeviceId> newDeviceIds = newLocations.stream().map(HostLocation::deviceId)
172 .collect(Collectors.toSet());
Charles Chanc22cef32016-04-29 14:38:22 -0700173
Charles Chand9265a32017-06-16 15:19:24 -0700174 // For each old location
Charles Chand66d6712018-03-29 16:03:41 -0700175 Sets.difference(prevLocations, newLocations).forEach(prevLocation -> {
Charles Chan3ed34d82017-06-22 18:03:14 -0700176 // Remove routing rules for old IPs
177 Sets.difference(prevIps, newIps).forEach(ip ->
178 processRoutingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId,
179 ip, true)
180 );
181
182 // Redirect the flows to pair link if configured
183 // Note: Do not continue removing any rule
184 Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(prevLocation.deviceId());
185 Optional<PortNumber> pairLocalPort = srManager.getPairLocalPorts(prevLocation.deviceId());
186 if (pairDeviceId.isPresent() && pairLocalPort.isPresent() && newLocations.stream()
187 .anyMatch(location -> location.deviceId().equals(pairDeviceId.get()))) {
188 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
189 // when the host is untagged
190 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(prevLocation)).orElse(hostVlanId);
191
192 processBridgingRule(prevLocation.deviceId(), pairLocalPort.get(), hostMac, vlanId, false);
193 newIps.forEach(ip ->
194 processRoutingRule(prevLocation.deviceId(), pairLocalPort.get(), hostMac, vlanId,
195 ip, false));
196 return;
197 }
Charles Chand9265a32017-06-16 15:19:24 -0700198
Charles Chanfbcb8812018-04-18 18:41:05 -0700199 // Remove flows for unchanged IPs only when the host moves from a switch to another.
Charles Chand9265a32017-06-16 15:19:24 -0700200 // Otherwise, do not remove and let the adding part update the old flow
201 if (!newDeviceIds.contains(prevLocation.deviceId())) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700202 processBridgingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId, true);
Charles Chand9265a32017-06-16 15:19:24 -0700203 Sets.intersection(prevIps, newIps).forEach(ip ->
Charles Chan3ed34d82017-06-22 18:03:14 -0700204 processRoutingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId,
Charles Chand9265a32017-06-16 15:19:24 -0700205 ip, true)
206 );
207 }
208
209 // Remove bridging rules if new interface vlan is different from old interface vlan
210 // Otherwise, do not remove and let the adding part update the old flow
211 if (newLocations.stream().noneMatch(newLocation -> {
212 VlanId oldAssignedVlan = srManager.getInternalVlanId(prevLocation);
213 VlanId newAssignedVlan = srManager.getInternalVlanId(newLocation);
214 // Host is tagged and the new location has the host vlan in vlan-tagged
Charles Chan3ed34d82017-06-22 18:03:14 -0700215 return srManager.getTaggedVlanId(newLocation).contains(hostVlanId) ||
Charles Chand9265a32017-06-16 15:19:24 -0700216 (oldAssignedVlan != null && newAssignedVlan != null &&
217 // Host is untagged and the new location has the same assigned vlan
218 oldAssignedVlan.equals(newAssignedVlan));
219 })) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700220 processBridgingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId, true);
Charles Chand9265a32017-06-16 15:19:24 -0700221 }
222
223 // Remove routing rules for unchanged IPs if none of the subnet of new location contains
224 // the IP. Otherwise, do not remove and let the adding part update the old flow
225 Sets.intersection(prevIps, newIps).forEach(ip -> {
226 if (newLocations.stream().noneMatch(newLocation ->
227 srManager.deviceConfiguration.inSameSubnet(newLocation, ip))) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700228 processRoutingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId,
Charles Chand9265a32017-06-16 15:19:24 -0700229 ip, true);
230 }
Charles Chanc22cef32016-04-29 14:38:22 -0700231 });
Charles Chand9265a32017-06-16 15:19:24 -0700232 });
233
234 // For each new location, add all new IPs.
Charles Chanfbcb8812018-04-18 18:41:05 -0700235 Sets.difference(newLocations, prevLocations).forEach(newLocation -> {
Charles Chan3ed34d82017-06-22 18:03:14 -0700236 processBridgingRule(newLocation.deviceId(), newLocation.port(), hostMac, hostVlanId, false);
Charles Chand9265a32017-06-16 15:19:24 -0700237 newIps.forEach(ip ->
Charles Chan3ed34d82017-06-22 18:03:14 -0700238 processRoutingRule(newLocation.deviceId(), newLocation.port(), hostMac, hostVlanId,
Charles Chand9265a32017-06-16 15:19:24 -0700239 ip, false)
240 );
241 });
242
243 // For each unchanged location, add new IPs and remove old IPs.
Charles Chanfbcb8812018-04-18 18:41:05 -0700244 Sets.intersection(newLocations, prevLocations).forEach(unchangedLocation -> {
Charles Chand9265a32017-06-16 15:19:24 -0700245 Sets.difference(prevIps, newIps).forEach(ip ->
Charles Chan3ed34d82017-06-22 18:03:14 -0700246 processRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(), hostMac,
247 hostVlanId, ip, true)
Charles Chand9265a32017-06-16 15:19:24 -0700248 );
249
250 Sets.difference(newIps, prevIps).forEach(ip ->
Charles Chan3ed34d82017-06-22 18:03:14 -0700251 processRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(), hostMac,
252 hostVlanId, ip, false)
Charles Chand9265a32017-06-16 15:19:24 -0700253 );
254 });
Saurav Dase6c448a2018-01-18 12:07:33 -0800255
256 // ensure dual-homed host locations have viable uplinks
257 if (newLocations.size() > prevLocations.size()) {
258 newLocations.forEach(loc -> {
259 if (srManager.mastershipService.isLocalMaster(loc.deviceId())) {
260 srManager.linkHandler.checkUplinksForDualHomedHosts(loc);
261 }
262 });
263 }
Charles Chanc22cef32016-04-29 14:38:22 -0700264 }
265
Charles Chand9265a32017-06-16 15:19:24 -0700266 void processHostUpdatedEvent(HostEvent event) {
Charles Chan873661e2017-11-30 15:37:50 -0800267 Host host = event.subject();
268 MacAddress hostMac = host.mac();
269 VlanId hostVlanId = host.vlan();
270 Set<HostLocation> locations = host.locations();
Charles Chanc22cef32016-04-29 14:38:22 -0700271 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
Charles Chan873661e2017-11-30 15:37:50 -0800272 Set<IpAddress> newIps = host.ipAddresses();
Charles Chanb3c1faf2017-11-20 08:46:24 -0800273 log.info("Host {}/{} is updated", hostMac, hostVlanId);
Charles Chanc22cef32016-04-29 14:38:22 -0700274
Charles Chand66d6712018-03-29 16:03:41 -0700275 locations.forEach(location -> {
Charles Chand9265a32017-06-16 15:19:24 -0700276 Sets.difference(prevIps, newIps).forEach(ip ->
Charles Chanb3c1faf2017-11-20 08:46:24 -0800277 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, true)
Charles Chand9265a32017-06-16 15:19:24 -0700278 );
279 Sets.difference(newIps, prevIps).forEach(ip ->
Charles Chanb3c1faf2017-11-20 08:46:24 -0800280 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, false)
Charles Chand9265a32017-06-16 15:19:24 -0700281 );
282 });
Charles Chanb3c1faf2017-11-20 08:46:24 -0800283
284 // Use the pair link temporarily before the second location of a dual-homed host shows up.
285 // This do not affect single-homed hosts since the flow will be blocked in
286 // processBridgingRule or processRoutingRule due to VLAN or IP mismatch respectively
Charles Chan873661e2017-11-30 15:37:50 -0800287 locations.forEach(location ->
Charles Chanb3c1faf2017-11-20 08:46:24 -0800288 srManager.getPairDeviceId(location.deviceId()).ifPresent(pairDeviceId -> {
Charles Chand66d6712018-03-29 16:03:41 -0700289 if (locations.stream().noneMatch(l -> l.deviceId().equals(pairDeviceId))) {
Charles Chan873661e2017-11-30 15:37:50 -0800290 Set<IpAddress> ipsToAdd = Sets.difference(newIps, prevIps);
291 Set<IpAddress> ipsToRemove = Sets.difference(prevIps, newIps);
292
Charles Chanb3c1faf2017-11-20 08:46:24 -0800293 srManager.getPairLocalPorts(pairDeviceId).ifPresent(pairRemotePort -> {
294 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
295 // when the host is untagged
296 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(hostVlanId);
297
Charles Chan873661e2017-11-30 15:37:50 -0800298 ipsToRemove.forEach(ip ->
Charles Chanb3c1faf2017-11-20 08:46:24 -0800299 processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, true)
300 );
Charles Chan873661e2017-11-30 15:37:50 -0800301 ipsToAdd.forEach(ip ->
Charles Chanb3c1faf2017-11-20 08:46:24 -0800302 processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, false)
303 );
Charles Chan873661e2017-11-30 15:37:50 -0800304
305 if (srManager.activeProbing) {
306 probe(host, location, pairDeviceId, pairRemotePort);
307 }
Charles Chanb3c1faf2017-11-20 08:46:24 -0800308 });
309 }
Charles Chan873661e2017-11-30 15:37:50 -0800310 })
311 );
312 }
313
314 /**
315 * When a non-pair port comes up, probe each host on the pair device if
316 * (1) the host is tagged and the tagged vlan of current port contains host vlan; or
317 * (2) the host is untagged and the internal vlan is the same on the host port and current port.
318 *
319 * @param cp connect point
320 */
321 void processPortUp(ConnectPoint cp) {
322 if (cp.port().equals(srManager.getPairLocalPorts(cp.deviceId()).orElse(null))) {
323 return;
324 }
325 if (srManager.activeProbing) {
326 srManager.getPairDeviceId(cp.deviceId())
327 .ifPresent(pairDeviceId -> srManager.hostService.getConnectedHosts(pairDeviceId).stream()
328 .filter(host -> isHostInVlanOfPort(host, pairDeviceId, cp))
329 .forEach(host -> srManager.probingService.probeHostLocation(host, cp, ProbeMode.DISCOVER))
330 );
331 }
332 }
333
334 /**
335 * Checks if given host located on given device id matches VLAN config of current port.
336 *
337 * @param host host to check
338 * @param deviceId device id to check
339 * @param cp current connect point
340 * @return true if the host located at deviceId matches the VLAN config on cp
341 */
342 private boolean isHostInVlanOfPort(Host host, DeviceId deviceId, ConnectPoint cp) {
343 VlanId internalVlan = srManager.getInternalVlanId(cp);
344 Set<VlanId> taggedVlan = srManager.getTaggedVlanId(cp);
345
346 return taggedVlan.contains(host.vlan()) ||
347 (internalVlan != null && host.locations().stream()
348 .filter(l -> l.deviceId().equals(deviceId))
349 .map(srManager::getInternalVlanId)
350 .anyMatch(internalVlan::equals));
351 }
352
353 /**
354 * Send a probe on all locations with the same VLAN on pair device, excluding pair port.
355 *
356 * @param host host to probe
357 * @param location newly discovered host location
358 * @param pairDeviceId pair device id
359 * @param pairRemotePort pair remote port
360 */
361 private void probe(Host host, ConnectPoint location, DeviceId pairDeviceId, PortNumber pairRemotePort) {
362 VlanId vlanToProbe = host.vlan().equals(VlanId.NONE) ?
363 srManager.getInternalVlanId(location) : host.vlan();
364 srManager.interfaceService.getInterfaces().stream()
365 .filter(i -> i.vlanTagged().contains(vlanToProbe) ||
366 i.vlanUntagged().equals(vlanToProbe) ||
367 i.vlanNative().equals(vlanToProbe))
368 .filter(i -> i.connectPoint().deviceId().equals(pairDeviceId))
369 .filter(i -> !i.connectPoint().port().equals(pairRemotePort))
370 .forEach(i -> {
371 log.debug("Probing host {} on pair device {}", host.id(), i.connectPoint());
372 srManager.probingService.probeHostLocation(host, i.connectPoint(), ProbeMode.DISCOVER);
373 });
Charles Chanc22cef32016-04-29 14:38:22 -0700374 }
375
376 /**
Charles Chan9595e6a2017-06-15 19:25:25 -0700377 * Populate or revoke a bridging rule on given deviceId that matches given mac, given vlan and
378 * output to given port.
379 *
380 * @param deviceId device ID
381 * @param port port
382 * @param mac mac address
383 * @param vlanId VLAN ID
384 * @param revoke true to revoke the rule; false to populate
385 */
386 private void processBridgingRule(DeviceId deviceId, PortNumber port, MacAddress mac,
387 VlanId vlanId, boolean revoke) {
Charles Chand66d6712018-03-29 16:03:41 -0700388 log.info("{} bridging entry for host {}/{} at {}:{}", revoke ? "Revoking" : "Populating",
Charles Chan9595e6a2017-06-15 19:25:25 -0700389 mac, vlanId, deviceId, port);
390
Charles Chand66d6712018-03-29 16:03:41 -0700391 if (!revoke) {
392 srManager.defaultRoutingHandler.populateBridging(deviceId, port, mac, vlanId);
393 } else {
394 srManager.defaultRoutingHandler.revokeBridging(deviceId, port, mac, vlanId);
Charles Chan9595e6a2017-06-15 19:25:25 -0700395 }
Charles Chan9595e6a2017-06-15 19:25:25 -0700396 }
397
398 /**
399 * Populate or revoke a routing rule on given deviceId that matches given ip,
400 * set destination mac to given mac, set vlan to given vlan and output to given port.
401 *
402 * @param deviceId device ID
403 * @param port port
404 * @param mac mac address
405 * @param vlanId VLAN ID
406 * @param ip IP address
407 * @param revoke true to revoke the rule; false to populate
408 */
409 private void processRoutingRule(DeviceId deviceId, PortNumber port, MacAddress mac,
410 VlanId vlanId, IpAddress ip, boolean revoke) {
411 ConnectPoint location = new ConnectPoint(deviceId, port);
Charles Chand9265a32017-06-16 15:19:24 -0700412 if (!srManager.deviceConfiguration.inSameSubnet(location, ip)) {
413 log.info("{} is not included in the subnet config of {}/{}. Ignored.", ip, deviceId, port);
414 return;
Charles Chan9595e6a2017-06-15 19:25:25 -0700415 }
Charles Chan9595e6a2017-06-15 19:25:25 -0700416
Charles Chand9265a32017-06-16 15:19:24 -0700417 log.info("{} routing rule for {} at {}", revoke ? "Revoking" : "Populating", ip, location);
418 if (revoke) {
Charles Chan910be6a2017-08-23 14:46:43 -0700419 srManager.defaultRoutingHandler.revokeRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port);
Charles Chand9265a32017-06-16 15:19:24 -0700420 } else {
Charles Chan910be6a2017-08-23 14:46:43 -0700421 srManager.defaultRoutingHandler.populateRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port);
Charles Chan370a65b2016-05-10 17:29:47 -0700422 }
Charles Chan370a65b2016-05-10 17:29:47 -0700423 }
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700424
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700425
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700426
427 /**
428 * Update forwarding objective for unicast bridging and unicast routing.
429 * Also check the validity of updated interface configuration on VLAN.
430 *
431 * @param deviceId device ID that host attaches to
432 * @param portNum port number that host attaches to
433 * @param vlanId Vlan ID configured on the switch port
434 * @param popVlan true to pop Vlan tag at TrafficTreatment, false otherwise
435 * @param install true to populate the objective, false to revoke
436 */
437 void processIntfVlanUpdatedEvent(DeviceId deviceId, PortNumber portNum, VlanId vlanId,
438 boolean popVlan, boolean install) {
439 ConnectPoint connectPoint = new ConnectPoint(deviceId, portNum);
440 Set<Host> hosts = hostService.getConnectedHosts(connectPoint);
441
442 if (hosts == null || hosts.size() == 0) {
Charles Chan39b75522018-04-21 00:44:29 -0700443 log.debug("processIntfVlanUpdatedEvent: No hosts connected to {}", connectPoint);
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700444 return;
445 }
446
447 hosts.forEach(host -> {
448 MacAddress mac = host.mac();
449 VlanId hostVlanId = host.vlan();
450
451 // Check whether the host vlan is valid for new interface configuration
452 if ((!popVlan && hostVlanId.equals(vlanId)) ||
453 (popVlan && hostVlanId.equals(VlanId.NONE))) {
Charles Chand66d6712018-03-29 16:03:41 -0700454 srManager.defaultRoutingHandler.updateBridging(deviceId, portNum, mac, vlanId, popVlan, install);
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700455 // Update Forwarding objective and corresponding simple Next objective
456 // for each host and IP address connected to given port
457 host.ipAddresses().forEach(ipAddress ->
Charles Chand66d6712018-03-29 16:03:41 -0700458 srManager.defaultRoutingHandler.updateFwdObj(deviceId, portNum, ipAddress.toIpPrefix(),
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700459 mac, vlanId, popVlan, install)
460 );
461 }
462 });
463 }
464
465 /**
466 * Populate or revoke routing rule for each host, according to the updated
467 * subnet configuration on the interface.
468 * @param cp connect point of the updated interface
469 * @param ipPrefixSet IP Prefixes added or removed
470 * @param install true if IP Prefixes added, false otherwise
471 */
472 void processIntfIpUpdatedEvent(ConnectPoint cp, Set<IpPrefix> ipPrefixSet, boolean install) {
473 Set<Host> hosts = hostService.getConnectedHosts(cp);
474
475 if (hosts == null || hosts.size() == 0) {
Charles Chan39b75522018-04-21 00:44:29 -0700476 log.debug("processIntfIpUpdatedEvent: No hosts connected to {}", cp);
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700477 return;
478 }
479
480 // Check whether the host IP address is in the interface's subnet
481 hosts.forEach(host ->
482 host.ipAddresses().forEach(hostIpAddress -> {
483 ipPrefixSet.forEach(ipPrefix -> {
484 if (install && ipPrefix.contains(hostIpAddress)) {
Charles Chand66d6712018-03-29 16:03:41 -0700485 srManager.defaultRoutingHandler.populateRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700486 host.mac(), host.vlan(), cp.port());
487 } else if (!install && ipPrefix.contains(hostIpAddress)) {
Charles Chand66d6712018-03-29 16:03:41 -0700488 srManager.defaultRoutingHandler.revokeRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700489 host.mac(), host.vlan(), cp.port());
490 }
491 });
492 }));
493 }
Saurav Dase6c448a2018-01-18 12:07:33 -0800494
495 /**
496 * Returns the set of portnumbers on the given device that are part of the
497 * locations for dual-homed hosts.
498 *
499 * @param deviceId the given deviceId
500 * @return set of port numbers on given device that are dual-homed host
501 * locations. May be empty if no dual homed hosts are connected to
502 * the given device
503 */
504 Set<PortNumber> getDualHomedHostPorts(DeviceId deviceId) {
505 Set<PortNumber> dualHomedLocations = new HashSet<>();
506 srManager.hostService.getConnectedHosts(deviceId).stream()
507 .filter(host -> host.locations().size() == 2)
508 .forEach(host -> host.locations().stream()
509 .filter(loc -> loc.deviceId().equals(deviceId))
510 .forEach(loc -> dualHomedLocations.add(loc.port())));
511 return dualHomedLocations;
512 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700513}