blob: 9590632c5fe388edc89db9ea29fe85bf9e8716b9 [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 Chan873661e2017-11-30 15:37:50 -080039import java.util.concurrent.Executors;
40import java.util.concurrent.ScheduledExecutorService;
41import java.util.concurrent.TimeUnit;
Charles Chand9265a32017-06-16 15:19:24 -070042import java.util.stream.Collectors;
43
44import static com.google.common.base.Preconditions.checkArgument;
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 static final int HOST_MOVED_DELAY_MS = 1000;
52
Charles Chan114aec72017-06-19 14:00:53 -070053 protected final SegmentRoutingManager srManager;
Charles Chan1eaf4802016-04-18 13:44:03 -070054 private HostService hostService;
Charles Chan1eaf4802016-04-18 13:44:03 -070055
56 /**
57 * Constructs the HostHandler.
58 *
59 * @param srManager Segment Routing manager
60 */
Charles Chand9265a32017-06-16 15:19:24 -070061 HostHandler(SegmentRoutingManager srManager) {
Charles Chan1eaf4802016-04-18 13:44:03 -070062 this.srManager = srManager;
Charles Chan1eaf4802016-04-18 13:44:03 -070063 hostService = srManager.hostService;
Charles Chan1eaf4802016-04-18 13:44:03 -070064 }
65
Charles Chandebfea32016-10-24 14:52:01 -070066 protected void init(DeviceId devId) {
Charles Chand9265a32017-06-16 15:19:24 -070067 hostService.getHosts().forEach(host ->
68 host.locations().stream()
Charles Chan873661e2017-11-30 15:37:50 -080069 .filter(location -> location.deviceId().equals(devId) ||
70 location.deviceId().equals(srManager.getPairDeviceId(devId).orElse(null)))
Charles Chand9265a32017-06-16 15:19:24 -070071 .forEach(location -> processHostAddedAtLocation(host, location))
72 );
Charles Chanc22cef32016-04-29 14:38:22 -070073 }
Charles Chan1eaf4802016-04-18 13:44:03 -070074
Charles Chand9265a32017-06-16 15:19:24 -070075 void processHostAddedEvent(HostEvent event) {
Charles Chan41f5ec02016-06-13 18:54:31 -070076 processHostAdded(event.subject());
Charles Chanc22cef32016-04-29 14:38:22 -070077 }
78
Charles Chand9265a32017-06-16 15:19:24 -070079 private void processHostAdded(Host host) {
80 host.locations().forEach(location -> processHostAddedAtLocation(host, location));
Saurav Dase6c448a2018-01-18 12:07:33 -080081 // ensure dual-homed host locations have viable uplinks
82 if (host.locations().size() > 1) {
83 host.locations().forEach(loc -> {
84 if (srManager.mastershipService.isLocalMaster(loc.deviceId())) {
85 srManager.linkHandler.checkUplinksForDualHomedHosts(loc);
86 }
87 });
88 }
Charles Chan1eaf4802016-04-18 13:44:03 -070089 }
90
Charles Chand9265a32017-06-16 15:19:24 -070091 void processHostAddedAtLocation(Host host, HostLocation location) {
92 checkArgument(host.locations().contains(location), "{} is not a location of {}", location, host);
93
Charles Chanceb2a2e2017-09-12 18:57:47 -070094 MacAddress hostMac = host.mac();
95 VlanId hostVlanId = host.vlan();
Charles Chand9265a32017-06-16 15:19:24 -070096 Set<HostLocation> locations = host.locations();
97 Set<IpAddress> ips = host.ipAddresses();
Charles Chanceb2a2e2017-09-12 18:57:47 -070098 log.info("Host {}/{} is added at {}", hostMac, hostVlanId, locations);
Charles Chand9265a32017-06-16 15:19:24 -070099
Charles Chand66d6712018-03-29 16:03:41 -0700100 processBridgingRule(location.deviceId(), location.port(), hostMac, hostVlanId, false);
101 ips.forEach(ip ->
102 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, false)
103 );
Charles Chanceb2a2e2017-09-12 18:57:47 -0700104
105 // Use the pair link temporarily before the second location of a dual-homed host shows up.
106 // This do not affect single-homed hosts since the flow will be blocked in
107 // processBridgingRule or processRoutingRule due to VLAN or IP mismatch respectively
108 srManager.getPairDeviceId(location.deviceId()).ifPresent(pairDeviceId -> {
Charles Chand66d6712018-03-29 16:03:41 -0700109 if (host.locations().stream().noneMatch(l -> l.deviceId().equals(pairDeviceId))) {
Charles Chanceb2a2e2017-09-12 18:57:47 -0700110 srManager.getPairLocalPorts(pairDeviceId).ifPresent(pairRemotePort -> {
111 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
112 // when the host is untagged
113 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(hostVlanId);
114
115 processBridgingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, false);
116 ips.forEach(ip -> processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId,
117 ip, false));
Charles Chan873661e2017-11-30 15:37:50 -0800118
119 if (srManager.activeProbing) {
120 probe(host, location, pairDeviceId, pairRemotePort);
121 }
Charles Chanceb2a2e2017-09-12 18:57:47 -0700122 });
123 }
124 });
Charles Chand9265a32017-06-16 15:19:24 -0700125 }
126
127 void processHostRemovedEvent(HostEvent event) {
Charles Chan41f5ec02016-06-13 18:54:31 -0700128 processHostRemoved(event.subject());
129 }
130
Charles Chand9265a32017-06-16 15:19:24 -0700131 private void processHostRemoved(Host host) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700132 MacAddress hostMac = host.mac();
133 VlanId hostVlanId = host.vlan();
Charles Chand9265a32017-06-16 15:19:24 -0700134 Set<HostLocation> locations = host.locations();
Charles Chan41f5ec02016-06-13 18:54:31 -0700135 Set<IpAddress> ips = host.ipAddresses();
Charles Chan3ed34d82017-06-22 18:03:14 -0700136 log.info("Host {}/{} is removed from {}", hostMac, hostVlanId, locations);
Charles Chanc22cef32016-04-29 14:38:22 -0700137
Charles Chan910be6a2017-08-23 14:46:43 -0700138 locations.forEach(location -> {
Charles Chand66d6712018-03-29 16:03:41 -0700139 processBridgingRule(location.deviceId(), location.port(), hostMac, hostVlanId, true);
140 ips.forEach(ip ->
141 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, true)
142 );
Charles Chan3ed34d82017-06-22 18:03:14 -0700143
144 // Also remove redirection flows on the pair device if exists.
145 Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(location.deviceId());
146 Optional<PortNumber> pairLocalPort = srManager.getPairLocalPorts(location.deviceId());
Charles Chand66d6712018-03-29 16:03:41 -0700147 if (pairDeviceId.isPresent() && pairLocalPort.isPresent()) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700148 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
149 // when the host is untagged
150 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(hostVlanId);
151
152 processBridgingRule(pairDeviceId.get(), pairLocalPort.get(), hostMac, vlanId, true);
153 ips.forEach(ip ->
154 processRoutingRule(pairDeviceId.get(), pairLocalPort.get(), hostMac, vlanId,
155 ip, true));
156 }
Charles Chand9265a32017-06-16 15:19:24 -0700157 });
Charles Chanc22cef32016-04-29 14:38:22 -0700158 }
159
Charles Chand9265a32017-06-16 15:19:24 -0700160 void processHostMovedEvent(HostEvent event) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700161 MacAddress hostMac = event.subject().mac();
162 VlanId hostVlanId = event.subject().vlan();
Charles Chand9265a32017-06-16 15:19:24 -0700163 Set<HostLocation> prevLocations = event.prevSubject().locations();
Charles Chanc22cef32016-04-29 14:38:22 -0700164 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
Charles Chand9265a32017-06-16 15:19:24 -0700165 Set<HostLocation> newLocations = event.subject().locations();
Charles Chanc22cef32016-04-29 14:38:22 -0700166 Set<IpAddress> newIps = event.subject().ipAddresses();
Charles Chanc22cef32016-04-29 14:38:22 -0700167
Charles Chan873661e2017-11-30 15:37:50 -0800168 // FIXME: Delay event handling a little bit to wait for the previous redirection flows to be completed
169 // The permanent solution would be introducing CompletableFuture and wait for it
170 if (prevLocations.size() == 1 && newLocations.size() == 2) {
171 log.debug("Delay event handling when host {}/{} moves from 1 to 2 locations", hostMac, hostVlanId);
172 ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
173 executorService.schedule(() ->
174 processHostMoved(hostMac, hostVlanId, prevLocations, prevIps, newLocations, newIps),
175 HOST_MOVED_DELAY_MS, TimeUnit.MILLISECONDS);
176 } else {
177 processHostMoved(hostMac, hostVlanId, prevLocations, prevIps, newLocations, newIps);
178 }
179 }
180
181 private void processHostMoved(MacAddress hostMac, VlanId hostVlanId, Set<HostLocation> prevLocations,
182 Set<IpAddress> prevIps, Set<HostLocation> newLocations, Set<IpAddress> newIps) {
183 log.info("Host {}/{} is moved from {} to {}", hostMac, hostVlanId, prevLocations, newLocations);
Charles Chand9265a32017-06-16 15:19:24 -0700184 Set<DeviceId> newDeviceIds = newLocations.stream().map(HostLocation::deviceId)
185 .collect(Collectors.toSet());
Charles Chanc22cef32016-04-29 14:38:22 -0700186
Charles Chand9265a32017-06-16 15:19:24 -0700187 // For each old location
Charles Chand66d6712018-03-29 16:03:41 -0700188 Sets.difference(prevLocations, newLocations).forEach(prevLocation -> {
Charles Chan3ed34d82017-06-22 18:03:14 -0700189 // Remove routing rules for old IPs
190 Sets.difference(prevIps, newIps).forEach(ip ->
191 processRoutingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId,
192 ip, true)
193 );
194
195 // Redirect the flows to pair link if configured
196 // Note: Do not continue removing any rule
197 Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(prevLocation.deviceId());
198 Optional<PortNumber> pairLocalPort = srManager.getPairLocalPorts(prevLocation.deviceId());
199 if (pairDeviceId.isPresent() && pairLocalPort.isPresent() && newLocations.stream()
200 .anyMatch(location -> location.deviceId().equals(pairDeviceId.get()))) {
201 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
202 // when the host is untagged
203 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(prevLocation)).orElse(hostVlanId);
204
205 processBridgingRule(prevLocation.deviceId(), pairLocalPort.get(), hostMac, vlanId, false);
206 newIps.forEach(ip ->
207 processRoutingRule(prevLocation.deviceId(), pairLocalPort.get(), hostMac, vlanId,
208 ip, false));
209 return;
210 }
Charles Chand9265a32017-06-16 15:19:24 -0700211
212 // Remove bridging rule and routing rules for unchanged IPs if the host moves from a switch to another.
213 // Otherwise, do not remove and let the adding part update the old flow
214 if (!newDeviceIds.contains(prevLocation.deviceId())) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700215 processBridgingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId, true);
Charles Chand9265a32017-06-16 15:19:24 -0700216 Sets.intersection(prevIps, newIps).forEach(ip ->
Charles Chan3ed34d82017-06-22 18:03:14 -0700217 processRoutingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId,
Charles Chand9265a32017-06-16 15:19:24 -0700218 ip, true)
219 );
220 }
221
222 // Remove bridging rules if new interface vlan is different from old interface vlan
223 // Otherwise, do not remove and let the adding part update the old flow
224 if (newLocations.stream().noneMatch(newLocation -> {
225 VlanId oldAssignedVlan = srManager.getInternalVlanId(prevLocation);
226 VlanId newAssignedVlan = srManager.getInternalVlanId(newLocation);
227 // Host is tagged and the new location has the host vlan in vlan-tagged
Charles Chan3ed34d82017-06-22 18:03:14 -0700228 return srManager.getTaggedVlanId(newLocation).contains(hostVlanId) ||
Charles Chand9265a32017-06-16 15:19:24 -0700229 (oldAssignedVlan != null && newAssignedVlan != null &&
230 // Host is untagged and the new location has the same assigned vlan
231 oldAssignedVlan.equals(newAssignedVlan));
232 })) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700233 processBridgingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId, true);
Charles Chand9265a32017-06-16 15:19:24 -0700234 }
235
236 // Remove routing rules for unchanged IPs if none of the subnet of new location contains
237 // the IP. Otherwise, do not remove and let the adding part update the old flow
238 Sets.intersection(prevIps, newIps).forEach(ip -> {
239 if (newLocations.stream().noneMatch(newLocation ->
240 srManager.deviceConfiguration.inSameSubnet(newLocation, ip))) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700241 processRoutingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId,
Charles Chand9265a32017-06-16 15:19:24 -0700242 ip, true);
243 }
Charles Chanc22cef32016-04-29 14:38:22 -0700244 });
Charles Chand9265a32017-06-16 15:19:24 -0700245 });
246
247 // For each new location, add all new IPs.
Charles Chand66d6712018-03-29 16:03:41 -0700248 Sets.difference(newLocations, prevLocations).stream()
Charles Chanabfe7e02017-08-09 16:50:15 -0700249 .forEach(newLocation -> {
Charles Chan3ed34d82017-06-22 18:03:14 -0700250 processBridgingRule(newLocation.deviceId(), newLocation.port(), hostMac, hostVlanId, false);
Charles Chand9265a32017-06-16 15:19:24 -0700251 newIps.forEach(ip ->
Charles Chan3ed34d82017-06-22 18:03:14 -0700252 processRoutingRule(newLocation.deviceId(), newLocation.port(), hostMac, hostVlanId,
Charles Chand9265a32017-06-16 15:19:24 -0700253 ip, false)
254 );
255 });
256
257 // For each unchanged location, add new IPs and remove old IPs.
Charles Chand66d6712018-03-29 16:03:41 -0700258 Sets.intersection(newLocations, prevLocations).stream()
Charles Chanabfe7e02017-08-09 16:50:15 -0700259 .forEach(unchangedLocation -> {
Charles Chand9265a32017-06-16 15:19:24 -0700260 Sets.difference(prevIps, newIps).forEach(ip ->
Charles Chan3ed34d82017-06-22 18:03:14 -0700261 processRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(), hostMac,
262 hostVlanId, ip, true)
Charles Chand9265a32017-06-16 15:19:24 -0700263 );
264
265 Sets.difference(newIps, prevIps).forEach(ip ->
Charles Chan3ed34d82017-06-22 18:03:14 -0700266 processRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(), hostMac,
267 hostVlanId, ip, false)
Charles Chand9265a32017-06-16 15:19:24 -0700268 );
269 });
Saurav Dase6c448a2018-01-18 12:07:33 -0800270
271 // ensure dual-homed host locations have viable uplinks
272 if (newLocations.size() > prevLocations.size()) {
273 newLocations.forEach(loc -> {
274 if (srManager.mastershipService.isLocalMaster(loc.deviceId())) {
275 srManager.linkHandler.checkUplinksForDualHomedHosts(loc);
276 }
277 });
278 }
Charles Chanc22cef32016-04-29 14:38:22 -0700279 }
280
Charles Chand9265a32017-06-16 15:19:24 -0700281 void processHostUpdatedEvent(HostEvent event) {
Charles Chan873661e2017-11-30 15:37:50 -0800282 Host host = event.subject();
283 MacAddress hostMac = host.mac();
284 VlanId hostVlanId = host.vlan();
285 Set<HostLocation> locations = host.locations();
Charles Chanc22cef32016-04-29 14:38:22 -0700286 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
Charles Chan873661e2017-11-30 15:37:50 -0800287 Set<IpAddress> newIps = host.ipAddresses();
Charles Chanb3c1faf2017-11-20 08:46:24 -0800288 log.info("Host {}/{} is updated", hostMac, hostVlanId);
Charles Chanc22cef32016-04-29 14:38:22 -0700289
Charles Chand66d6712018-03-29 16:03:41 -0700290 locations.forEach(location -> {
Charles Chand9265a32017-06-16 15:19:24 -0700291 Sets.difference(prevIps, newIps).forEach(ip ->
Charles Chanb3c1faf2017-11-20 08:46:24 -0800292 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, true)
Charles Chand9265a32017-06-16 15:19:24 -0700293 );
294 Sets.difference(newIps, prevIps).forEach(ip ->
Charles Chanb3c1faf2017-11-20 08:46:24 -0800295 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, false)
Charles Chand9265a32017-06-16 15:19:24 -0700296 );
297 });
Charles Chanb3c1faf2017-11-20 08:46:24 -0800298
299 // Use the pair link temporarily before the second location of a dual-homed host shows up.
300 // This do not affect single-homed hosts since the flow will be blocked in
301 // processBridgingRule or processRoutingRule due to VLAN or IP mismatch respectively
Charles Chan873661e2017-11-30 15:37:50 -0800302 locations.forEach(location ->
Charles Chanb3c1faf2017-11-20 08:46:24 -0800303 srManager.getPairDeviceId(location.deviceId()).ifPresent(pairDeviceId -> {
Charles Chand66d6712018-03-29 16:03:41 -0700304 if (locations.stream().noneMatch(l -> l.deviceId().equals(pairDeviceId))) {
Charles Chan873661e2017-11-30 15:37:50 -0800305 Set<IpAddress> ipsToAdd = Sets.difference(newIps, prevIps);
306 Set<IpAddress> ipsToRemove = Sets.difference(prevIps, newIps);
307
Charles Chanb3c1faf2017-11-20 08:46:24 -0800308 srManager.getPairLocalPorts(pairDeviceId).ifPresent(pairRemotePort -> {
309 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
310 // when the host is untagged
311 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(hostVlanId);
312
Charles Chan873661e2017-11-30 15:37:50 -0800313 ipsToRemove.forEach(ip ->
Charles Chanb3c1faf2017-11-20 08:46:24 -0800314 processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, true)
315 );
Charles Chan873661e2017-11-30 15:37:50 -0800316 ipsToAdd.forEach(ip ->
Charles Chanb3c1faf2017-11-20 08:46:24 -0800317 processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, false)
318 );
Charles Chan873661e2017-11-30 15:37:50 -0800319
320 if (srManager.activeProbing) {
321 probe(host, location, pairDeviceId, pairRemotePort);
322 }
Charles Chanb3c1faf2017-11-20 08:46:24 -0800323 });
324 }
Charles Chan873661e2017-11-30 15:37:50 -0800325 })
326 );
327 }
328
329 /**
330 * When a non-pair port comes up, probe each host on the pair device if
331 * (1) the host is tagged and the tagged vlan of current port contains host vlan; or
332 * (2) the host is untagged and the internal vlan is the same on the host port and current port.
333 *
334 * @param cp connect point
335 */
336 void processPortUp(ConnectPoint cp) {
337 if (cp.port().equals(srManager.getPairLocalPorts(cp.deviceId()).orElse(null))) {
338 return;
339 }
340 if (srManager.activeProbing) {
341 srManager.getPairDeviceId(cp.deviceId())
342 .ifPresent(pairDeviceId -> srManager.hostService.getConnectedHosts(pairDeviceId).stream()
343 .filter(host -> isHostInVlanOfPort(host, pairDeviceId, cp))
344 .forEach(host -> srManager.probingService.probeHostLocation(host, cp, ProbeMode.DISCOVER))
345 );
346 }
347 }
348
349 /**
350 * Checks if given host located on given device id matches VLAN config of current port.
351 *
352 * @param host host to check
353 * @param deviceId device id to check
354 * @param cp current connect point
355 * @return true if the host located at deviceId matches the VLAN config on cp
356 */
357 private boolean isHostInVlanOfPort(Host host, DeviceId deviceId, ConnectPoint cp) {
358 VlanId internalVlan = srManager.getInternalVlanId(cp);
359 Set<VlanId> taggedVlan = srManager.getTaggedVlanId(cp);
360
361 return taggedVlan.contains(host.vlan()) ||
362 (internalVlan != null && host.locations().stream()
363 .filter(l -> l.deviceId().equals(deviceId))
364 .map(srManager::getInternalVlanId)
365 .anyMatch(internalVlan::equals));
366 }
367
368 /**
369 * Send a probe on all locations with the same VLAN on pair device, excluding pair port.
370 *
371 * @param host host to probe
372 * @param location newly discovered host location
373 * @param pairDeviceId pair device id
374 * @param pairRemotePort pair remote port
375 */
376 private void probe(Host host, ConnectPoint location, DeviceId pairDeviceId, PortNumber pairRemotePort) {
377 VlanId vlanToProbe = host.vlan().equals(VlanId.NONE) ?
378 srManager.getInternalVlanId(location) : host.vlan();
379 srManager.interfaceService.getInterfaces().stream()
380 .filter(i -> i.vlanTagged().contains(vlanToProbe) ||
381 i.vlanUntagged().equals(vlanToProbe) ||
382 i.vlanNative().equals(vlanToProbe))
383 .filter(i -> i.connectPoint().deviceId().equals(pairDeviceId))
384 .filter(i -> !i.connectPoint().port().equals(pairRemotePort))
385 .forEach(i -> {
386 log.debug("Probing host {} on pair device {}", host.id(), i.connectPoint());
387 srManager.probingService.probeHostLocation(host, i.connectPoint(), ProbeMode.DISCOVER);
388 });
Charles Chanc22cef32016-04-29 14:38:22 -0700389 }
390
391 /**
Charles Chan9595e6a2017-06-15 19:25:25 -0700392 * Populate or revoke a bridging rule on given deviceId that matches given mac, given vlan and
393 * output to given port.
394 *
395 * @param deviceId device ID
396 * @param port port
397 * @param mac mac address
398 * @param vlanId VLAN ID
399 * @param revoke true to revoke the rule; false to populate
400 */
401 private void processBridgingRule(DeviceId deviceId, PortNumber port, MacAddress mac,
402 VlanId vlanId, boolean revoke) {
Charles Chand66d6712018-03-29 16:03:41 -0700403 log.info("{} bridging entry for host {}/{} at {}:{}", revoke ? "Revoking" : "Populating",
Charles Chan9595e6a2017-06-15 19:25:25 -0700404 mac, vlanId, deviceId, port);
405
Charles Chand66d6712018-03-29 16:03:41 -0700406 if (!revoke) {
407 srManager.defaultRoutingHandler.populateBridging(deviceId, port, mac, vlanId);
408 } else {
409 srManager.defaultRoutingHandler.revokeBridging(deviceId, port, mac, vlanId);
Charles Chan9595e6a2017-06-15 19:25:25 -0700410 }
Charles Chan9595e6a2017-06-15 19:25:25 -0700411 }
412
413 /**
414 * Populate or revoke a routing rule on given deviceId that matches given ip,
415 * set destination mac to given mac, set vlan to given vlan and output to given port.
416 *
417 * @param deviceId device ID
418 * @param port port
419 * @param mac mac address
420 * @param vlanId VLAN ID
421 * @param ip IP address
422 * @param revoke true to revoke the rule; false to populate
423 */
424 private void processRoutingRule(DeviceId deviceId, PortNumber port, MacAddress mac,
425 VlanId vlanId, IpAddress ip, boolean revoke) {
426 ConnectPoint location = new ConnectPoint(deviceId, port);
Charles Chand9265a32017-06-16 15:19:24 -0700427 if (!srManager.deviceConfiguration.inSameSubnet(location, ip)) {
428 log.info("{} is not included in the subnet config of {}/{}. Ignored.", ip, deviceId, port);
429 return;
Charles Chan9595e6a2017-06-15 19:25:25 -0700430 }
Charles Chan9595e6a2017-06-15 19:25:25 -0700431
Charles Chand9265a32017-06-16 15:19:24 -0700432 log.info("{} routing rule for {} at {}", revoke ? "Revoking" : "Populating", ip, location);
433 if (revoke) {
Charles Chan910be6a2017-08-23 14:46:43 -0700434 srManager.defaultRoutingHandler.revokeRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port);
Charles Chand9265a32017-06-16 15:19:24 -0700435 } else {
Charles Chan910be6a2017-08-23 14:46:43 -0700436 srManager.defaultRoutingHandler.populateRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port);
Charles Chan370a65b2016-05-10 17:29:47 -0700437 }
Charles Chan370a65b2016-05-10 17:29:47 -0700438 }
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700439
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700440
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700441
442 /**
443 * Update forwarding objective for unicast bridging and unicast routing.
444 * Also check the validity of updated interface configuration on VLAN.
445 *
446 * @param deviceId device ID that host attaches to
447 * @param portNum port number that host attaches to
448 * @param vlanId Vlan ID configured on the switch port
449 * @param popVlan true to pop Vlan tag at TrafficTreatment, false otherwise
450 * @param install true to populate the objective, false to revoke
451 */
452 void processIntfVlanUpdatedEvent(DeviceId deviceId, PortNumber portNum, VlanId vlanId,
453 boolean popVlan, boolean install) {
454 ConnectPoint connectPoint = new ConnectPoint(deviceId, portNum);
455 Set<Host> hosts = hostService.getConnectedHosts(connectPoint);
456
457 if (hosts == null || hosts.size() == 0) {
458 return;
459 }
460
461 hosts.forEach(host -> {
462 MacAddress mac = host.mac();
463 VlanId hostVlanId = host.vlan();
464
465 // Check whether the host vlan is valid for new interface configuration
466 if ((!popVlan && hostVlanId.equals(vlanId)) ||
467 (popVlan && hostVlanId.equals(VlanId.NONE))) {
Charles Chand66d6712018-03-29 16:03:41 -0700468 srManager.defaultRoutingHandler.updateBridging(deviceId, portNum, mac, vlanId, popVlan, install);
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700469 // Update Forwarding objective and corresponding simple Next objective
470 // for each host and IP address connected to given port
471 host.ipAddresses().forEach(ipAddress ->
Charles Chand66d6712018-03-29 16:03:41 -0700472 srManager.defaultRoutingHandler.updateFwdObj(deviceId, portNum, ipAddress.toIpPrefix(),
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700473 mac, vlanId, popVlan, install)
474 );
475 }
476 });
477 }
478
479 /**
480 * Populate or revoke routing rule for each host, according to the updated
481 * subnet configuration on the interface.
482 * @param cp connect point of the updated interface
483 * @param ipPrefixSet IP Prefixes added or removed
484 * @param install true if IP Prefixes added, false otherwise
485 */
486 void processIntfIpUpdatedEvent(ConnectPoint cp, Set<IpPrefix> ipPrefixSet, boolean install) {
487 Set<Host> hosts = hostService.getConnectedHosts(cp);
488
489 if (hosts == null || hosts.size() == 0) {
490 log.warn("processIntfIpUpdatedEvent: No hosts connected to {}", cp);
491 return;
492 }
493
494 // Check whether the host IP address is in the interface's subnet
495 hosts.forEach(host ->
496 host.ipAddresses().forEach(hostIpAddress -> {
497 ipPrefixSet.forEach(ipPrefix -> {
498 if (install && ipPrefix.contains(hostIpAddress)) {
Charles Chand66d6712018-03-29 16:03:41 -0700499 srManager.defaultRoutingHandler.populateRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700500 host.mac(), host.vlan(), cp.port());
501 } else if (!install && ipPrefix.contains(hostIpAddress)) {
Charles Chand66d6712018-03-29 16:03:41 -0700502 srManager.defaultRoutingHandler.revokeRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700503 host.mac(), host.vlan(), cp.port());
504 }
505 });
506 }));
507 }
Saurav Dase6c448a2018-01-18 12:07:33 -0800508
509 /**
510 * Returns the set of portnumbers on the given device that are part of the
511 * locations for dual-homed hosts.
512 *
513 * @param deviceId the given deviceId
514 * @return set of port numbers on given device that are dual-homed host
515 * locations. May be empty if no dual homed hosts are connected to
516 * the given device
517 */
518 Set<PortNumber> getDualHomedHostPorts(DeviceId deviceId) {
519 Set<PortNumber> dualHomedLocations = new HashSet<>();
520 srManager.hostService.getConnectedHosts(deviceId).stream()
521 .filter(host -> host.locations().size() == 2)
522 .forEach(host -> host.locations().stream()
523 .filter(loc -> loc.deviceId().equals(deviceId))
524 .forEach(loc -> dualHomedLocations.add(loc.port())));
525 return dualHomedLocations;
526 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700527}