blob: 3982731e4a81146563cf920198cedd42cf91f5b6 [file] [log] [blame]
Charles Chand2990362016-04-18 13:44:03 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Charles Chand2990362016-04-18 13:44:03 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.segmentrouting;
18
Jonghwan Hyun800d9d02018-04-09 09:40:50 -070019import org.onlab.packet.EthType;
Charles Chand2990362016-04-18 13:44:03 -070020import org.onlab.packet.IpAddress;
Jonghwan Hyun42fe1052017-08-25 17:48:36 -070021import org.onlab.packet.IpPrefix;
Charles Chand2990362016-04-18 13:44:03 -070022import org.onlab.packet.MacAddress;
23import org.onlab.packet.VlanId;
Charles Chan59cc16d2017-02-02 16:20:42 -080024import org.onosproject.net.ConnectPoint;
Charles Chand2990362016-04-18 13:44:03 -070025import org.onosproject.net.DeviceId;
Charles Chan6ea94fc2016-05-10 17:29:47 -070026import org.onosproject.net.Host;
Charles Chan93e71ba2016-04-29 14:38:22 -070027import org.onosproject.net.HostLocation;
Charles Chand2990362016-04-18 13:44:03 -070028import org.onosproject.net.PortNumber;
Charles Chand2990362016-04-18 13:44:03 -070029import org.onosproject.net.host.HostEvent;
30import org.onosproject.net.host.HostService;
Charles Chanff79dd92018-06-01 16:33:48 -070031import org.onosproject.net.host.ProbeMode;
Charles Chand2990362016-04-18 13:44:03 -070032import org.slf4j.Logger;
33import org.slf4j.LoggerFactory;
34
Saurav Das018605f2017-02-18 14:05:44 -080035import com.google.common.collect.Sets;
Charles Chan65238242017-06-22 18:03:14 -070036
Saurav Das45f48152018-01-18 12:07:33 -080037import java.util.HashSet;
Charles Chan65238242017-06-22 18:03:14 -070038import java.util.Optional;
Charles Chand2990362016-04-18 13:44:03 -070039import java.util.Set;
Charles Chanf9a52702017-06-16 15:19:24 -070040import java.util.stream.Collectors;
41
42import static com.google.common.base.Preconditions.checkArgument;
Charles Chand2990362016-04-18 13:44:03 -070043
44/**
45 * Handles host-related events.
46 */
47public class HostHandler {
48 private static final Logger log = LoggerFactory.getLogger(HostHandler.class);
Charles Chan47933752017-11-30 15:37:50 -080049
Charles Chan2e2e3402017-06-19 14:00:53 -070050 protected final SegmentRoutingManager srManager;
Charles Chand2990362016-04-18 13:44:03 -070051 private HostService hostService;
Charles Chand2990362016-04-18 13:44:03 -070052
53 /**
54 * Constructs the HostHandler.
55 *
56 * @param srManager Segment Routing manager
57 */
Charles Chanf9a52702017-06-16 15:19:24 -070058 HostHandler(SegmentRoutingManager srManager) {
Charles Chand2990362016-04-18 13:44:03 -070059 this.srManager = srManager;
Charles Chand2990362016-04-18 13:44:03 -070060 hostService = srManager.hostService;
Charles Chand2990362016-04-18 13:44:03 -070061 }
62
Charles Chan03a73e02016-10-24 14:52:01 -070063 protected void init(DeviceId devId) {
Charles Chanf9a52702017-06-16 15:19:24 -070064 hostService.getHosts().forEach(host ->
65 host.locations().stream()
Charles Chan47933752017-11-30 15:37:50 -080066 .filter(location -> location.deviceId().equals(devId) ||
67 location.deviceId().equals(srManager.getPairDeviceId(devId).orElse(null)))
Charles Chanf9a52702017-06-16 15:19:24 -070068 .forEach(location -> processHostAddedAtLocation(host, location))
69 );
Charles Chan93e71ba2016-04-29 14:38:22 -070070 }
Charles Chand2990362016-04-18 13:44:03 -070071
Charles Chanf9a52702017-06-16 15:19:24 -070072 void processHostAddedEvent(HostEvent event) {
Charles Chan35fd1a72016-06-13 18:54:31 -070073 processHostAdded(event.subject());
Charles Chan93e71ba2016-04-29 14:38:22 -070074 }
75
Charles Chanf9a52702017-06-16 15:19:24 -070076 private void processHostAdded(Host host) {
77 host.locations().forEach(location -> processHostAddedAtLocation(host, location));
Saurav Das45f48152018-01-18 12:07:33 -080078 // ensure dual-homed host locations have viable uplinks
Saurav Das9a554292018-04-27 18:42:30 -070079 if (host.locations().size() > 1 || srManager.singleHomedDown) {
Saurav Das45f48152018-01-18 12:07:33 -080080 host.locations().forEach(loc -> {
81 if (srManager.mastershipService.isLocalMaster(loc.deviceId())) {
Saurav Das9a554292018-04-27 18:42:30 -070082 srManager.linkHandler.checkUplinksForHost(loc);
Saurav Das45f48152018-01-18 12:07:33 -080083 }
84 });
85 }
Charles Chand2990362016-04-18 13:44:03 -070086 }
87
Charles Chanf9a52702017-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 Chan8e786b52017-09-12 18:57:47 -070091 MacAddress hostMac = host.mac();
92 VlanId hostVlanId = host.vlan();
Charles Chanf9a52702017-06-16 15:19:24 -070093 Set<HostLocation> locations = host.locations();
94 Set<IpAddress> ips = host.ipAddresses();
Charles Chan8e786b52017-09-12 18:57:47 -070095 log.info("Host {}/{} is added at {}", hostMac, hostVlanId, locations);
Charles Chanf9a52702017-06-16 15:19:24 -070096
Jonghwan Hyun800d9d02018-04-09 09:40:50 -070097 if (isDoubleTaggedHost(host)) {
98 ips.forEach(ip ->
99 processDoubleTaggedRoutingRule(location.deviceId(), location.port(), hostMac,
100 host.innerVlan(), hostVlanId, host.tpid(), ip, false)
101 );
102 } else {
103 processBridgingRule(location.deviceId(), location.port(), hostMac, hostVlanId, false);
104 ips.forEach(ip ->
Charles Chan2ff1bac2018-03-29 16:03:41 -0700105 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, false)
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700106 );
107 }
Charles Chan8e786b52017-09-12 18:57:47 -0700108
109 // Use the pair link temporarily before the second location of a dual-homed host shows up.
110 // This do not affect single-homed hosts since the flow will be blocked in
111 // processBridgingRule or processRoutingRule due to VLAN or IP mismatch respectively
112 srManager.getPairDeviceId(location.deviceId()).ifPresent(pairDeviceId -> {
Charles Chan2ff1bac2018-03-29 16:03:41 -0700113 if (host.locations().stream().noneMatch(l -> l.deviceId().equals(pairDeviceId))) {
Saurav Das9a554292018-04-27 18:42:30 -0700114 srManager.getPairLocalPort(pairDeviceId).ifPresent(pairRemotePort -> {
Charles Chan8e786b52017-09-12 18:57:47 -0700115 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
116 // when the host is untagged
117 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(hostVlanId);
118
119 processBridgingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, false);
120 ips.forEach(ip -> processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId,
121 ip, false));
Charles Chan47933752017-11-30 15:37:50 -0800122
123 if (srManager.activeProbing) {
124 probe(host, location, pairDeviceId, pairRemotePort);
125 }
Charles Chan8e786b52017-09-12 18:57:47 -0700126 });
127 }
128 });
Charles Chanf9a52702017-06-16 15:19:24 -0700129 }
130
131 void processHostRemovedEvent(HostEvent event) {
Charles Chan35fd1a72016-06-13 18:54:31 -0700132 processHostRemoved(event.subject());
133 }
134
Charles Chanf9a52702017-06-16 15:19:24 -0700135 private void processHostRemoved(Host host) {
Charles Chan65238242017-06-22 18:03:14 -0700136 MacAddress hostMac = host.mac();
137 VlanId hostVlanId = host.vlan();
Charles Chanf9a52702017-06-16 15:19:24 -0700138 Set<HostLocation> locations = host.locations();
Charles Chan35fd1a72016-06-13 18:54:31 -0700139 Set<IpAddress> ips = host.ipAddresses();
Charles Chan65238242017-06-22 18:03:14 -0700140 log.info("Host {}/{} is removed from {}", hostMac, hostVlanId, locations);
Charles Chan93e71ba2016-04-29 14:38:22 -0700141
Charles Chan2fde6d42017-08-23 14:46:43 -0700142 locations.forEach(location -> {
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700143 if (isDoubleTaggedHost(host)) {
144 ips.forEach(ip ->
145 processDoubleTaggedRoutingRule(location.deviceId(), location.port(), hostMac,
146 host.innerVlan(), hostVlanId, host.tpid(), ip, true)
147 );
148 } else {
149 processBridgingRule(location.deviceId(), location.port(), hostMac, hostVlanId, true);
150 ips.forEach(ip ->
Charles Chan2ff1bac2018-03-29 16:03:41 -0700151 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, true)
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700152 );
153 }
Charles Chan65238242017-06-22 18:03:14 -0700154
155 // Also remove redirection flows on the pair device if exists.
156 Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(location.deviceId());
Saurav Das9a554292018-04-27 18:42:30 -0700157 Optional<PortNumber> pairLocalPort = srManager.getPairLocalPort(location.deviceId());
Charles Chan2ff1bac2018-03-29 16:03:41 -0700158 if (pairDeviceId.isPresent() && pairLocalPort.isPresent()) {
Charles Chan65238242017-06-22 18:03:14 -0700159 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
160 // when the host is untagged
161 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(hostVlanId);
162
163 processBridgingRule(pairDeviceId.get(), pairLocalPort.get(), hostMac, vlanId, true);
164 ips.forEach(ip ->
165 processRoutingRule(pairDeviceId.get(), pairLocalPort.get(), hostMac, vlanId,
166 ip, true));
167 }
Charles Chanf9a52702017-06-16 15:19:24 -0700168 });
Charles Chan93e71ba2016-04-29 14:38:22 -0700169 }
170
Charles Chanf9a52702017-06-16 15:19:24 -0700171 void processHostMovedEvent(HostEvent event) {
Charles Chan9bd0e5a2018-04-25 18:51:46 -0400172 Host host = event.subject();
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700173 MacAddress hostMac = host.mac();
174 VlanId hostVlanId = host.vlan();
Charles Chan9bd0e5a2018-04-25 18:51:46 -0400175 Set<HostLocation> prevLocations = event.prevSubject().locations();
176 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
177 Set<HostLocation> newLocations = host.locations();
178 Set<IpAddress> newIps = host.ipAddresses();
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700179 EthType hostTpid = host.tpid();
180 boolean doubleTaggedHost = isDoubleTaggedHost(host);
Charles Chan9bd0e5a2018-04-25 18:51:46 -0400181
Charles Chan47933752017-11-30 15:37:50 -0800182 log.info("Host {}/{} is moved from {} to {}", hostMac, hostVlanId, prevLocations, newLocations);
Charles Chanf9a52702017-06-16 15:19:24 -0700183 Set<DeviceId> newDeviceIds = newLocations.stream().map(HostLocation::deviceId)
184 .collect(Collectors.toSet());
Charles Chan93e71ba2016-04-29 14:38:22 -0700185
Charles Chanf9a52702017-06-16 15:19:24 -0700186 // For each old location
Charles Chan2ff1bac2018-03-29 16:03:41 -0700187 Sets.difference(prevLocations, newLocations).forEach(prevLocation -> {
Charles Chanff79dd92018-06-01 16:33:48 -0700188 // First of all, verify each old location
189 srManager.probingService.probeHost(host, prevLocation, ProbeMode.VERIFY);
190
Charles Chan65238242017-06-22 18:03:14 -0700191 // Remove routing rules for old IPs
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700192 Sets.difference(prevIps, newIps).forEach(ip -> {
193 if (doubleTaggedHost) {
194 processDoubleTaggedRoutingRule(prevLocation.deviceId(), prevLocation.port(),
195 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, true);
196 } else {
Charles Chan65238242017-06-22 18:03:14 -0700197 processRoutingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId,
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700198 ip, true);
199 }
200 });
Charles Chan65238242017-06-22 18:03:14 -0700201
202 // Redirect the flows to pair link if configured
203 // Note: Do not continue removing any rule
204 Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(prevLocation.deviceId());
Saurav Das9a554292018-04-27 18:42:30 -0700205 Optional<PortNumber> pairLocalPort = srManager.getPairLocalPort(prevLocation.deviceId());
Charles Chan65238242017-06-22 18:03:14 -0700206 if (pairDeviceId.isPresent() && pairLocalPort.isPresent() && newLocations.stream()
207 .anyMatch(location -> location.deviceId().equals(pairDeviceId.get()))) {
208 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
209 // when the host is untagged
210 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(prevLocation)).orElse(hostVlanId);
211
212 processBridgingRule(prevLocation.deviceId(), pairLocalPort.get(), hostMac, vlanId, false);
213 newIps.forEach(ip ->
214 processRoutingRule(prevLocation.deviceId(), pairLocalPort.get(), hostMac, vlanId,
215 ip, false));
216 return;
217 }
Charles Chanf9a52702017-06-16 15:19:24 -0700218
Charles Chan50bb6ef2018-04-18 18:41:05 -0700219 // Remove flows for unchanged IPs only when the host moves from a switch to another.
Charles Chanf9a52702017-06-16 15:19:24 -0700220 // Otherwise, do not remove and let the adding part update the old flow
221 if (!newDeviceIds.contains(prevLocation.deviceId())) {
Charles Chan65238242017-06-22 18:03:14 -0700222 processBridgingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId, true);
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700223 Sets.intersection(prevIps, newIps).forEach(ip -> {
224 if (doubleTaggedHost) {
225 processDoubleTaggedRoutingRule(prevLocation.deviceId(), prevLocation.port(),
226 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, true);
227 } else {
228 processRoutingRule(prevLocation.deviceId(), prevLocation.port(),
229 hostMac, hostVlanId, ip, true);
230 }
231 });
Charles Chanf9a52702017-06-16 15:19:24 -0700232 }
233
234 // Remove bridging rules if new interface vlan is different from old interface vlan
235 // Otherwise, do not remove and let the adding part update the old flow
236 if (newLocations.stream().noneMatch(newLocation -> {
237 VlanId oldAssignedVlan = srManager.getInternalVlanId(prevLocation);
238 VlanId newAssignedVlan = srManager.getInternalVlanId(newLocation);
239 // Host is tagged and the new location has the host vlan in vlan-tagged
Charles Chan971d7ba2018-05-01 11:50:20 -0700240 return srManager.interfaceService.getTaggedVlanId(newLocation).contains(hostVlanId) ||
Charles Chanf9a52702017-06-16 15:19:24 -0700241 (oldAssignedVlan != null && newAssignedVlan != null &&
242 // Host is untagged and the new location has the same assigned vlan
243 oldAssignedVlan.equals(newAssignedVlan));
244 })) {
Charles Chan65238242017-06-22 18:03:14 -0700245 processBridgingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId, true);
Charles Chanf9a52702017-06-16 15:19:24 -0700246 }
247
248 // Remove routing rules for unchanged IPs if none of the subnet of new location contains
249 // the IP. Otherwise, do not remove and let the adding part update the old flow
250 Sets.intersection(prevIps, newIps).forEach(ip -> {
251 if (newLocations.stream().noneMatch(newLocation ->
252 srManager.deviceConfiguration.inSameSubnet(newLocation, ip))) {
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700253 if (doubleTaggedHost) {
254 processDoubleTaggedRoutingRule(prevLocation.deviceId(), prevLocation.port(),
255 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, true);
256 } else {
257 processRoutingRule(prevLocation.deviceId(), prevLocation.port(),
258 hostMac, hostVlanId, ip, true);
259 }
Charles Chanf9a52702017-06-16 15:19:24 -0700260 }
Charles Chan93e71ba2016-04-29 14:38:22 -0700261 });
Charles Chanf9a52702017-06-16 15:19:24 -0700262 });
263
264 // For each new location, add all new IPs.
Charles Chan50bb6ef2018-04-18 18:41:05 -0700265 Sets.difference(newLocations, prevLocations).forEach(newLocation -> {
Charles Chan65238242017-06-22 18:03:14 -0700266 processBridgingRule(newLocation.deviceId(), newLocation.port(), hostMac, hostVlanId, false);
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700267 newIps.forEach(ip -> {
268 if (doubleTaggedHost) {
269 processDoubleTaggedRoutingRule(newLocation.deviceId(), newLocation.port(),
270 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, false);
271 } else {
Charles Chan65238242017-06-22 18:03:14 -0700272 processRoutingRule(newLocation.deviceId(), newLocation.port(), hostMac, hostVlanId,
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700273 ip, false);
274 }
275 });
Charles Chan9bd0e5a2018-04-25 18:51:46 -0400276
277 // Probe on pair device when host move
278 // Majorly for the 2nd step of [1A/x, 1B/x] -> [1A/x, 1B/y] -> [1A/y, 1B/y]
279 // But will also cover [1A/x] -> [1A/y] -> [1A/y, 1B/y]
280 if (srManager.activeProbing) {
281 srManager.getPairDeviceId(newLocation.deviceId()).ifPresent(pairDeviceId ->
282 srManager.getPairLocalPort(pairDeviceId).ifPresent(pairRemotePort ->
283 probe(host, newLocation, pairDeviceId, pairRemotePort)
284 )
285 );
286 }
Charles Chanf9a52702017-06-16 15:19:24 -0700287 });
288
289 // For each unchanged location, add new IPs and remove old IPs.
Charles Chan50bb6ef2018-04-18 18:41:05 -0700290 Sets.intersection(newLocations, prevLocations).forEach(unchangedLocation -> {
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700291 Sets.difference(prevIps, newIps).forEach(ip -> {
292 if (doubleTaggedHost) {
293 processDoubleTaggedRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(),
294 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, true);
295 } else {
296 processRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(),
297 hostMac, hostVlanId, ip, true);
298 }
299 });
Charles Chanf9a52702017-06-16 15:19:24 -0700300
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700301 Sets.difference(newIps, prevIps).forEach(ip -> {
302 if (doubleTaggedHost) {
303 processDoubleTaggedRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(),
304 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, false);
305 } else {
306 processRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(),
307 hostMac, hostVlanId, ip, false);
308 }
309 });
Charles Chanf9a52702017-06-16 15:19:24 -0700310 });
Saurav Das45f48152018-01-18 12:07:33 -0800311
312 // ensure dual-homed host locations have viable uplinks
Saurav Das9a554292018-04-27 18:42:30 -0700313 if (newLocations.size() > prevLocations.size() || srManager.singleHomedDown) {
Saurav Das45f48152018-01-18 12:07:33 -0800314 newLocations.forEach(loc -> {
315 if (srManager.mastershipService.isLocalMaster(loc.deviceId())) {
Saurav Das9a554292018-04-27 18:42:30 -0700316 srManager.linkHandler.checkUplinksForHost(loc);
Saurav Das45f48152018-01-18 12:07:33 -0800317 }
318 });
319 }
Charles Chan93e71ba2016-04-29 14:38:22 -0700320 }
321
Charles Chanf9a52702017-06-16 15:19:24 -0700322 void processHostUpdatedEvent(HostEvent event) {
Charles Chan47933752017-11-30 15:37:50 -0800323 Host host = event.subject();
324 MacAddress hostMac = host.mac();
325 VlanId hostVlanId = host.vlan();
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700326 EthType hostTpid = host.tpid();
Charles Chan47933752017-11-30 15:37:50 -0800327 Set<HostLocation> locations = host.locations();
Charles Chan93e71ba2016-04-29 14:38:22 -0700328 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
Charles Chan47933752017-11-30 15:37:50 -0800329 Set<IpAddress> newIps = host.ipAddresses();
Charles Chan3d650a62017-11-20 08:46:24 -0800330 log.info("Host {}/{} is updated", hostMac, hostVlanId);
Charles Chan93e71ba2016-04-29 14:38:22 -0700331
Charles Chan2ff1bac2018-03-29 16:03:41 -0700332 locations.forEach(location -> {
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700333 Sets.difference(prevIps, newIps).forEach(ip -> {
334 if (isDoubleTaggedHost(host)) {
335 processDoubleTaggedRoutingRule(location.deviceId(), location.port(), hostMac,
336 host.innerVlan(), hostVlanId, hostTpid, ip, true);
337 } else {
338 processRoutingRule(location.deviceId(), location.port(), hostMac,
339 hostVlanId, ip, true);
340 }
341 });
342 Sets.difference(newIps, prevIps).forEach(ip -> {
343 if (isDoubleTaggedHost(host)) {
344 processDoubleTaggedRoutingRule(location.deviceId(), location.port(), hostMac,
345 host.innerVlan(), hostVlanId, hostTpid, ip, false);
346 } else {
347 processRoutingRule(location.deviceId(), location.port(), hostMac,
348 hostVlanId, ip, false);
349 }
350 });
Charles Chanf9a52702017-06-16 15:19:24 -0700351 });
Charles Chan3d650a62017-11-20 08:46:24 -0800352
353 // Use the pair link temporarily before the second location of a dual-homed host shows up.
354 // This do not affect single-homed hosts since the flow will be blocked in
355 // processBridgingRule or processRoutingRule due to VLAN or IP mismatch respectively
Charles Chan47933752017-11-30 15:37:50 -0800356 locations.forEach(location ->
Charles Chan3d650a62017-11-20 08:46:24 -0800357 srManager.getPairDeviceId(location.deviceId()).ifPresent(pairDeviceId -> {
Charles Chan2ff1bac2018-03-29 16:03:41 -0700358 if (locations.stream().noneMatch(l -> l.deviceId().equals(pairDeviceId))) {
Charles Chan47933752017-11-30 15:37:50 -0800359 Set<IpAddress> ipsToAdd = Sets.difference(newIps, prevIps);
360 Set<IpAddress> ipsToRemove = Sets.difference(prevIps, newIps);
361
Saurav Das9a554292018-04-27 18:42:30 -0700362 srManager.getPairLocalPort(pairDeviceId).ifPresent(pairRemotePort -> {
Charles Chan3d650a62017-11-20 08:46:24 -0800363 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
364 // when the host is untagged
365 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(hostVlanId);
366
Charles Chan47933752017-11-30 15:37:50 -0800367 ipsToRemove.forEach(ip ->
Charles Chan3d650a62017-11-20 08:46:24 -0800368 processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, true)
369 );
Charles Chan47933752017-11-30 15:37:50 -0800370 ipsToAdd.forEach(ip ->
Charles Chan3d650a62017-11-20 08:46:24 -0800371 processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, false)
372 );
Charles Chan47933752017-11-30 15:37:50 -0800373
374 if (srManager.activeProbing) {
375 probe(host, location, pairDeviceId, pairRemotePort);
376 }
Charles Chan3d650a62017-11-20 08:46:24 -0800377 });
378 }
Charles Chan47933752017-11-30 15:37:50 -0800379 })
380 );
381 }
382
383 /**
384 * When a non-pair port comes up, probe each host on the pair device if
385 * (1) the host is tagged and the tagged vlan of current port contains host vlan; or
386 * (2) the host is untagged and the internal vlan is the same on the host port and current port.
387 *
388 * @param cp connect point
389 */
390 void processPortUp(ConnectPoint cp) {
Saurav Das9a554292018-04-27 18:42:30 -0700391 if (cp.port().equals(srManager.getPairLocalPort(cp.deviceId()).orElse(null))) {
Charles Chan47933752017-11-30 15:37:50 -0800392 return;
393 }
394 if (srManager.activeProbing) {
395 srManager.getPairDeviceId(cp.deviceId())
396 .ifPresent(pairDeviceId -> srManager.hostService.getConnectedHosts(pairDeviceId).stream()
397 .filter(host -> isHostInVlanOfPort(host, pairDeviceId, cp))
Charles Chanff79dd92018-06-01 16:33:48 -0700398 .forEach(host -> srManager.probingService.probeHost(host, cp, ProbeMode.DISCOVER))
Charles Chan47933752017-11-30 15:37:50 -0800399 );
400 }
401 }
402
403 /**
404 * Checks if given host located on given device id matches VLAN config of current port.
405 *
406 * @param host host to check
407 * @param deviceId device id to check
408 * @param cp current connect point
409 * @return true if the host located at deviceId matches the VLAN config on cp
410 */
411 private boolean isHostInVlanOfPort(Host host, DeviceId deviceId, ConnectPoint cp) {
412 VlanId internalVlan = srManager.getInternalVlanId(cp);
Charles Chan971d7ba2018-05-01 11:50:20 -0700413 Set<VlanId> taggedVlan = srManager.interfaceService.getTaggedVlanId(cp);
Charles Chan47933752017-11-30 15:37:50 -0800414
415 return taggedVlan.contains(host.vlan()) ||
416 (internalVlan != null && host.locations().stream()
417 .filter(l -> l.deviceId().equals(deviceId))
418 .map(srManager::getInternalVlanId)
419 .anyMatch(internalVlan::equals));
420 }
421
422 /**
423 * Send a probe on all locations with the same VLAN on pair device, excluding pair port.
424 *
425 * @param host host to probe
426 * @param location newly discovered host location
427 * @param pairDeviceId pair device id
428 * @param pairRemotePort pair remote port
429 */
430 private void probe(Host host, ConnectPoint location, DeviceId pairDeviceId, PortNumber pairRemotePort) {
431 VlanId vlanToProbe = host.vlan().equals(VlanId.NONE) ?
432 srManager.getInternalVlanId(location) : host.vlan();
433 srManager.interfaceService.getInterfaces().stream()
434 .filter(i -> i.vlanTagged().contains(vlanToProbe) ||
435 i.vlanUntagged().equals(vlanToProbe) ||
436 i.vlanNative().equals(vlanToProbe))
437 .filter(i -> i.connectPoint().deviceId().equals(pairDeviceId))
438 .filter(i -> !i.connectPoint().port().equals(pairRemotePort))
439 .forEach(i -> {
440 log.debug("Probing host {} on pair device {}", host.id(), i.connectPoint());
Charles Chanff79dd92018-06-01 16:33:48 -0700441 srManager.probingService.probeHost(host, i.connectPoint(), ProbeMode.DISCOVER);
Charles Chan47933752017-11-30 15:37:50 -0800442 });
Charles Chan93e71ba2016-04-29 14:38:22 -0700443 }
444
445 /**
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700446 * Populates or revokes a bridging rule on given deviceId that matches given mac,
447 * given vlan and output to given port.
Charles Chanb7b4c932017-06-15 19:25:25 -0700448 *
449 * @param deviceId device ID
450 * @param port port
451 * @param mac mac address
452 * @param vlanId VLAN ID
453 * @param revoke true to revoke the rule; false to populate
454 */
455 private void processBridgingRule(DeviceId deviceId, PortNumber port, MacAddress mac,
456 VlanId vlanId, boolean revoke) {
Charles Chan2ff1bac2018-03-29 16:03:41 -0700457 log.info("{} bridging entry for host {}/{} at {}:{}", revoke ? "Revoking" : "Populating",
Charles Chanb7b4c932017-06-15 19:25:25 -0700458 mac, vlanId, deviceId, port);
459
Charles Chan2ff1bac2018-03-29 16:03:41 -0700460 if (!revoke) {
461 srManager.defaultRoutingHandler.populateBridging(deviceId, port, mac, vlanId);
462 } else {
463 srManager.defaultRoutingHandler.revokeBridging(deviceId, port, mac, vlanId);
Charles Chanb7b4c932017-06-15 19:25:25 -0700464 }
Charles Chanb7b4c932017-06-15 19:25:25 -0700465 }
466
467 /**
468 * Populate or revoke a routing rule on given deviceId that matches given ip,
469 * set destination mac to given mac, set vlan to given vlan and output to given port.
470 *
471 * @param deviceId device ID
472 * @param port port
473 * @param mac mac address
474 * @param vlanId VLAN ID
475 * @param ip IP address
476 * @param revoke true to revoke the rule; false to populate
477 */
478 private void processRoutingRule(DeviceId deviceId, PortNumber port, MacAddress mac,
479 VlanId vlanId, IpAddress ip, boolean revoke) {
480 ConnectPoint location = new ConnectPoint(deviceId, port);
Charles Chanf9a52702017-06-16 15:19:24 -0700481 if (!srManager.deviceConfiguration.inSameSubnet(location, ip)) {
482 log.info("{} is not included in the subnet config of {}/{}. Ignored.", ip, deviceId, port);
483 return;
Charles Chanb7b4c932017-06-15 19:25:25 -0700484 }
Charles Chanb7b4c932017-06-15 19:25:25 -0700485
Charles Chanf9a52702017-06-16 15:19:24 -0700486 log.info("{} routing rule for {} at {}", revoke ? "Revoking" : "Populating", ip, location);
487 if (revoke) {
Charles Chan2fde6d42017-08-23 14:46:43 -0700488 srManager.defaultRoutingHandler.revokeRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port);
Charles Chanf9a52702017-06-16 15:19:24 -0700489 } else {
Charles Chan2fde6d42017-08-23 14:46:43 -0700490 srManager.defaultRoutingHandler.populateRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port);
Charles Chan6ea94fc2016-05-10 17:29:47 -0700491 }
Charles Chan6ea94fc2016-05-10 17:29:47 -0700492 }
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700493
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700494 /**
495 * Populate or revoke a routing rule and egress rules on given deviceId that matches given IP,
496 * to set destination mac to given mac, set inner vlan and outer vlan to given vlans,
497 * set outer TPID, and output to given port.
498 *
499 * @param deviceId device ID
500 * @param port port
501 * @param mac mac address
502 * @param innerVlan inner VLAN ID
503 * @param outerVlan outer VLAN ID
504 * @param outerTpid outer TPID
505 * @param ip IP address
506 * @param revoke true to revoke the rule; false to populate
507 */
508 private void processDoubleTaggedRoutingRule(DeviceId deviceId, PortNumber port,
509 MacAddress mac, VlanId innerVlan,
510 VlanId outerVlan, EthType outerTpid,
511 IpAddress ip, boolean revoke) {
512 ConnectPoint location = new ConnectPoint(deviceId, port);
513 if (!srManager.deviceConfiguration.inSameSubnet(location, ip)) {
514 log.info("{} is not included in the subnet config of {}/{}. Ignored.", ip, deviceId, port);
515 return;
516 }
517 log.info("{} routing rule for double-tagged host {} at {}",
518 revoke ? "Revoking" : "Populating", ip, location);
519 if (revoke) {
520 srManager.defaultRoutingHandler.revokeDoubleTaggedRoute(
521 deviceId, ip.toIpPrefix(), mac, innerVlan, outerVlan, outerTpid, port);
522 } else {
523 srManager.defaultRoutingHandler.populateDoubleTaggedRoute(
524 deviceId, ip.toIpPrefix(), mac, innerVlan, outerVlan, outerTpid, port);
525 }
526 }
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700527
528 /**
529 * Update forwarding objective for unicast bridging and unicast routing.
530 * Also check the validity of updated interface configuration on VLAN.
531 *
532 * @param deviceId device ID that host attaches to
533 * @param portNum port number that host attaches to
534 * @param vlanId Vlan ID configured on the switch port
535 * @param popVlan true to pop Vlan tag at TrafficTreatment, false otherwise
536 * @param install true to populate the objective, false to revoke
537 */
538 void processIntfVlanUpdatedEvent(DeviceId deviceId, PortNumber portNum, VlanId vlanId,
539 boolean popVlan, boolean install) {
540 ConnectPoint connectPoint = new ConnectPoint(deviceId, portNum);
541 Set<Host> hosts = hostService.getConnectedHosts(connectPoint);
542
543 if (hosts == null || hosts.size() == 0) {
Charles Chan10b2fee2018-04-21 00:44:29 -0700544 log.debug("processIntfVlanUpdatedEvent: No hosts connected to {}", connectPoint);
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700545 return;
546 }
547
548 hosts.forEach(host -> {
549 MacAddress mac = host.mac();
550 VlanId hostVlanId = host.vlan();
551
552 // Check whether the host vlan is valid for new interface configuration
553 if ((!popVlan && hostVlanId.equals(vlanId)) ||
554 (popVlan && hostVlanId.equals(VlanId.NONE))) {
Charles Chan2ff1bac2018-03-29 16:03:41 -0700555 srManager.defaultRoutingHandler.updateBridging(deviceId, portNum, mac, vlanId, popVlan, install);
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700556 // Update Forwarding objective and corresponding simple Next objective
557 // for each host and IP address connected to given port
558 host.ipAddresses().forEach(ipAddress ->
Charles Chan2ff1bac2018-03-29 16:03:41 -0700559 srManager.defaultRoutingHandler.updateFwdObj(deviceId, portNum, ipAddress.toIpPrefix(),
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700560 mac, vlanId, popVlan, install)
561 );
562 }
563 });
564 }
565
566 /**
567 * Populate or revoke routing rule for each host, according to the updated
568 * subnet configuration on the interface.
569 * @param cp connect point of the updated interface
570 * @param ipPrefixSet IP Prefixes added or removed
571 * @param install true if IP Prefixes added, false otherwise
572 */
573 void processIntfIpUpdatedEvent(ConnectPoint cp, Set<IpPrefix> ipPrefixSet, boolean install) {
574 Set<Host> hosts = hostService.getConnectedHosts(cp);
575
576 if (hosts == null || hosts.size() == 0) {
Charles Chan10b2fee2018-04-21 00:44:29 -0700577 log.debug("processIntfIpUpdatedEvent: No hosts connected to {}", cp);
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700578 return;
579 }
580
581 // Check whether the host IP address is in the interface's subnet
582 hosts.forEach(host ->
583 host.ipAddresses().forEach(hostIpAddress -> {
584 ipPrefixSet.forEach(ipPrefix -> {
585 if (install && ipPrefix.contains(hostIpAddress)) {
Charles Chan2ff1bac2018-03-29 16:03:41 -0700586 srManager.defaultRoutingHandler.populateRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700587 host.mac(), host.vlan(), cp.port());
588 } else if (!install && ipPrefix.contains(hostIpAddress)) {
Charles Chan2ff1bac2018-03-29 16:03:41 -0700589 srManager.defaultRoutingHandler.revokeRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700590 host.mac(), host.vlan(), cp.port());
591 }
592 });
593 }));
594 }
Saurav Das45f48152018-01-18 12:07:33 -0800595
596 /**
597 * Returns the set of portnumbers on the given device that are part of the
598 * locations for dual-homed hosts.
599 *
600 * @param deviceId the given deviceId
601 * @return set of port numbers on given device that are dual-homed host
602 * locations. May be empty if no dual homed hosts are connected to
603 * the given device
604 */
605 Set<PortNumber> getDualHomedHostPorts(DeviceId deviceId) {
606 Set<PortNumber> dualHomedLocations = new HashSet<>();
607 srManager.hostService.getConnectedHosts(deviceId).stream()
608 .filter(host -> host.locations().size() == 2)
609 .forEach(host -> host.locations().stream()
610 .filter(loc -> loc.deviceId().equals(deviceId))
611 .forEach(loc -> dualHomedLocations.add(loc.port())));
612 return dualHomedLocations;
613 }
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700614
615 /**
616 * Checks if the given host is double-tagged or not.
617 *
618 * @param host host to check
619 * @return true if it is double-tagged, false otherwise
620 */
621 private boolean isDoubleTaggedHost(Host host) {
622 return !host.innerVlan().equals(VlanId.NONE);
623 }
624
Charles Chand2990362016-04-18 13:44:03 -0700625}