blob: 14df6227a3374d81542e8bd52880de0e91246f9e [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;
Charles Chan10b0fb72017-02-02 16:20:42 -080024import org.onosproject.net.ConnectPoint;
Charles Chan1eaf4802016-04-18 13:44:03 -070025import org.onosproject.net.DeviceId;
Charles Chan370a65b2016-05-10 17:29:47 -070026import org.onosproject.net.Host;
Charles Chanc22cef32016-04-29 14:38:22 -070027import org.onosproject.net.HostLocation;
Charles Chan1eaf4802016-04-18 13:44:03 -070028import org.onosproject.net.PortNumber;
Charles Chan1eaf4802016-04-18 13:44:03 -070029import org.onosproject.net.host.HostEvent;
30import org.onosproject.net.host.HostService;
Charles Chanc6bcdf92018-06-01 16:33:48 -070031import org.onosproject.net.host.ProbeMode;
Charles Chan1eaf4802016-04-18 13:44:03 -070032import org.slf4j.Logger;
33import org.slf4j.LoggerFactory;
34
Saurav Dasf9332192017-02-18 14:05:44 -080035import com.google.common.collect.Sets;
Charles Chan3ed34d82017-06-22 18:03:14 -070036
Saurav Dase6c448a2018-01-18 12:07:33 -080037import java.util.HashSet;
Charles Chan3ed34d82017-06-22 18:03:14 -070038import java.util.Optional;
Charles Chan1eaf4802016-04-18 13:44:03 -070039import java.util.Set;
Charles Chand9265a32017-06-16 15:19:24 -070040import java.util.stream.Collectors;
41
42import static com.google.common.base.Preconditions.checkArgument;
Charles Chan1eaf4802016-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 Chan873661e2017-11-30 15:37:50 -080049
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
Saurav Dasec683dc2018-04-27 18:42:30 -070079 if (host.locations().size() > 1 || srManager.singleHomedDown) {
Saurav Dase6c448a2018-01-18 12:07:33 -080080 host.locations().forEach(loc -> {
81 if (srManager.mastershipService.isLocalMaster(loc.deviceId())) {
Saurav Dasec683dc2018-04-27 18:42:30 -070082 srManager.linkHandler.checkUplinksForHost(loc);
Saurav Dase6c448a2018-01-18 12:07:33 -080083 }
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
Jonghwan Hyun9aaa34f2018-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 Chand66d6712018-03-29 16:03:41 -0700105 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, false)
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700106 );
107 }
Charles Chanceb2a2e2017-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 Chand66d6712018-03-29 16:03:41 -0700113 if (host.locations().stream().noneMatch(l -> l.deviceId().equals(pairDeviceId))) {
Saurav Dasec683dc2018-04-27 18:42:30 -0700114 srManager.getPairLocalPort(pairDeviceId).ifPresent(pairRemotePort -> {
Charles Chanceb2a2e2017-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
Charles Chan868c9572018-06-15 18:54:18 -0700117 VlanId vlanId = vlanForPairPort(hostVlanId, location);
118 if (vlanId == null) {
119 return;
120 }
Charles Chanceb2a2e2017-09-12 18:57:47 -0700121
122 processBridgingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, false);
123 ips.forEach(ip -> processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId,
124 ip, false));
Charles Chan873661e2017-11-30 15:37:50 -0800125
126 if (srManager.activeProbing) {
127 probe(host, location, pairDeviceId, pairRemotePort);
128 }
Charles Chanceb2a2e2017-09-12 18:57:47 -0700129 });
130 }
131 });
Ruchi Sahota07869322019-05-09 17:26:14 -0400132
133 int nextId = srManager.getMacVlanNextObjectiveId(location.deviceId(), hostMac, hostVlanId, null, false);
134 if (nextId != -1) {
135 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(hostVlanId);
136 log.debug(" Updating next objective for device {}, host {}/{}, port {}, nextid {}",
137 location.deviceId(), hostMac, vlanId, location.port(), nextId);
138 srManager.updateMacVlanTreatment(location.deviceId(), hostMac, vlanId,
139 location.port(), nextId);
140 }
Charles Chand9265a32017-06-16 15:19:24 -0700141 }
142
143 void processHostRemovedEvent(HostEvent event) {
Charles Chan41f5ec02016-06-13 18:54:31 -0700144 processHostRemoved(event.subject());
145 }
146
Charles Chand9265a32017-06-16 15:19:24 -0700147 private void processHostRemoved(Host host) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700148 MacAddress hostMac = host.mac();
149 VlanId hostVlanId = host.vlan();
Charles Chand9265a32017-06-16 15:19:24 -0700150 Set<HostLocation> locations = host.locations();
Charles Chan41f5ec02016-06-13 18:54:31 -0700151 Set<IpAddress> ips = host.ipAddresses();
Charles Chan3ed34d82017-06-22 18:03:14 -0700152 log.info("Host {}/{} is removed from {}", hostMac, hostVlanId, locations);
Charles Chanc22cef32016-04-29 14:38:22 -0700153
Charles Chan910be6a2017-08-23 14:46:43 -0700154 locations.forEach(location -> {
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700155 if (isDoubleTaggedHost(host)) {
156 ips.forEach(ip ->
157 processDoubleTaggedRoutingRule(location.deviceId(), location.port(), hostMac,
158 host.innerVlan(), hostVlanId, host.tpid(), ip, true)
159 );
160 } else {
161 processBridgingRule(location.deviceId(), location.port(), hostMac, hostVlanId, true);
162 ips.forEach(ip ->
Charles Chand66d6712018-03-29 16:03:41 -0700163 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, true)
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700164 );
165 }
Charles Chan3ed34d82017-06-22 18:03:14 -0700166
167 // Also remove redirection flows on the pair device if exists.
168 Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(location.deviceId());
Saurav Dasec683dc2018-04-27 18:42:30 -0700169 Optional<PortNumber> pairLocalPort = srManager.getPairLocalPort(location.deviceId());
Charles Chand66d6712018-03-29 16:03:41 -0700170 if (pairDeviceId.isPresent() && pairLocalPort.isPresent()) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700171 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
172 // when the host is untagged
Charles Chan868c9572018-06-15 18:54:18 -0700173 VlanId vlanId = vlanForPairPort(hostVlanId, location);
174 if (vlanId == null) {
175 return;
176 }
Charles Chan3ed34d82017-06-22 18:03:14 -0700177
178 processBridgingRule(pairDeviceId.get(), pairLocalPort.get(), hostMac, vlanId, true);
179 ips.forEach(ip ->
180 processRoutingRule(pairDeviceId.get(), pairLocalPort.get(), hostMac, vlanId,
181 ip, true));
182 }
Charles Chanbd84dd52018-06-21 19:07:12 -0700183
184 // Delete prefix from sr-device-subnet when the next hop host is removed
185 srManager.routeService.getRouteTables().forEach(tableId -> {
186 srManager.routeService.getRoutes(tableId).forEach(routeInfo -> {
187 if (routeInfo.allRoutes().stream().anyMatch(rr -> ips.contains(rr.nextHop()))) {
188 log.debug("HostRemoved. removeSubnet {}, {}", location, routeInfo.prefix());
189 srManager.deviceConfiguration.removeSubnet(location, routeInfo.prefix());
190 }
191 });
192 });
Ruchi Sahota07869322019-05-09 17:26:14 -0400193
Charles Chand9265a32017-06-16 15:19:24 -0700194 });
Charles Chanc22cef32016-04-29 14:38:22 -0700195 }
196
Charles Chand9265a32017-06-16 15:19:24 -0700197 void processHostMovedEvent(HostEvent event) {
Charles Chan4b5769a2018-04-25 18:51:46 -0400198 Host host = event.subject();
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700199 MacAddress hostMac = host.mac();
200 VlanId hostVlanId = host.vlan();
Charles Chan4b5769a2018-04-25 18:51:46 -0400201 Set<HostLocation> prevLocations = event.prevSubject().locations();
202 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
203 Set<HostLocation> newLocations = host.locations();
204 Set<IpAddress> newIps = host.ipAddresses();
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700205 EthType hostTpid = host.tpid();
206 boolean doubleTaggedHost = isDoubleTaggedHost(host);
Charles Chan4b5769a2018-04-25 18:51:46 -0400207
Charles Chan873661e2017-11-30 15:37:50 -0800208 log.info("Host {}/{} is moved from {} to {}", hostMac, hostVlanId, prevLocations, newLocations);
Charles Chand9265a32017-06-16 15:19:24 -0700209 Set<DeviceId> newDeviceIds = newLocations.stream().map(HostLocation::deviceId)
210 .collect(Collectors.toSet());
Charles Chanc22cef32016-04-29 14:38:22 -0700211
Charles Chand9265a32017-06-16 15:19:24 -0700212 // For each old location
Charles Chand66d6712018-03-29 16:03:41 -0700213 Sets.difference(prevLocations, newLocations).forEach(prevLocation -> {
Charles Chan3ed34d82017-06-22 18:03:14 -0700214 // Remove routing rules for old IPs
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700215 Sets.difference(prevIps, newIps).forEach(ip -> {
216 if (doubleTaggedHost) {
217 processDoubleTaggedRoutingRule(prevLocation.deviceId(), prevLocation.port(),
218 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, true);
219 } else {
Charles Chan3ed34d82017-06-22 18:03:14 -0700220 processRoutingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId,
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700221 ip, true);
222 }
223 });
Charles Chan3ed34d82017-06-22 18:03:14 -0700224
225 // Redirect the flows to pair link if configured
226 // Note: Do not continue removing any rule
227 Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(prevLocation.deviceId());
Saurav Dasec683dc2018-04-27 18:42:30 -0700228 Optional<PortNumber> pairLocalPort = srManager.getPairLocalPort(prevLocation.deviceId());
Charles Chan6d43c162018-10-11 12:35:36 -0700229 if (pairDeviceId.isPresent() && pairLocalPort.isPresent() &&
230 newLocations.stream().anyMatch(location -> location.deviceId().equals(pairDeviceId.get())) &&
231 newLocations.stream().noneMatch(location -> location.deviceId().equals(prevLocation.deviceId()))) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700232 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
233 // when the host is untagged
234 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(prevLocation)).orElse(hostVlanId);
235
236 processBridgingRule(prevLocation.deviceId(), pairLocalPort.get(), hostMac, vlanId, false);
237 newIps.forEach(ip ->
238 processRoutingRule(prevLocation.deviceId(), pairLocalPort.get(), hostMac, vlanId,
239 ip, false));
240 return;
241 }
Charles Chand9265a32017-06-16 15:19:24 -0700242
Charles Chanfbcb8812018-04-18 18:41:05 -0700243 // Remove flows for unchanged IPs only when the host moves from a switch to another.
Charles Chand9265a32017-06-16 15:19:24 -0700244 // Otherwise, do not remove and let the adding part update the old flow
245 if (!newDeviceIds.contains(prevLocation.deviceId())) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700246 processBridgingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId, true);
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700247 Sets.intersection(prevIps, newIps).forEach(ip -> {
248 if (doubleTaggedHost) {
249 processDoubleTaggedRoutingRule(prevLocation.deviceId(), prevLocation.port(),
250 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, true);
251 } else {
252 processRoutingRule(prevLocation.deviceId(), prevLocation.port(),
253 hostMac, hostVlanId, ip, true);
254 }
255 });
Charles Chand9265a32017-06-16 15:19:24 -0700256 }
257
258 // Remove bridging rules if new interface vlan is different from old interface vlan
259 // Otherwise, do not remove and let the adding part update the old flow
260 if (newLocations.stream().noneMatch(newLocation -> {
261 VlanId oldAssignedVlan = srManager.getInternalVlanId(prevLocation);
262 VlanId newAssignedVlan = srManager.getInternalVlanId(newLocation);
263 // Host is tagged and the new location has the host vlan in vlan-tagged
Charles Chan098ca202018-05-01 11:50:20 -0700264 return srManager.interfaceService.getTaggedVlanId(newLocation).contains(hostVlanId) ||
Charles Chand9265a32017-06-16 15:19:24 -0700265 (oldAssignedVlan != null && newAssignedVlan != null &&
266 // Host is untagged and the new location has the same assigned vlan
267 oldAssignedVlan.equals(newAssignedVlan));
268 })) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700269 processBridgingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId, true);
Charles Chand9265a32017-06-16 15:19:24 -0700270 }
271
272 // Remove routing rules for unchanged IPs if none of the subnet of new location contains
273 // the IP. Otherwise, do not remove and let the adding part update the old flow
274 Sets.intersection(prevIps, newIps).forEach(ip -> {
275 if (newLocations.stream().noneMatch(newLocation ->
276 srManager.deviceConfiguration.inSameSubnet(newLocation, ip))) {
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700277 if (doubleTaggedHost) {
278 processDoubleTaggedRoutingRule(prevLocation.deviceId(), prevLocation.port(),
279 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, true);
280 } else {
281 processRoutingRule(prevLocation.deviceId(), prevLocation.port(),
282 hostMac, hostVlanId, ip, true);
283 }
Charles Chand9265a32017-06-16 15:19:24 -0700284 }
Charles Chanc22cef32016-04-29 14:38:22 -0700285 });
Charles Chand9265a32017-06-16 15:19:24 -0700286 });
287
288 // For each new location, add all new IPs.
Charles Chanfbcb8812018-04-18 18:41:05 -0700289 Sets.difference(newLocations, prevLocations).forEach(newLocation -> {
Charles Chan3ed34d82017-06-22 18:03:14 -0700290 processBridgingRule(newLocation.deviceId(), newLocation.port(), hostMac, hostVlanId, false);
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700291 newIps.forEach(ip -> {
292 if (doubleTaggedHost) {
293 processDoubleTaggedRoutingRule(newLocation.deviceId(), newLocation.port(),
294 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, false);
295 } else {
Charles Chan3ed34d82017-06-22 18:03:14 -0700296 processRoutingRule(newLocation.deviceId(), newLocation.port(), hostMac, hostVlanId,
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700297 ip, false);
298 }
299 });
Charles Chan4b5769a2018-04-25 18:51:46 -0400300
301 // Probe on pair device when host move
302 // Majorly for the 2nd step of [1A/x, 1B/x] -> [1A/x, 1B/y] -> [1A/y, 1B/y]
303 // But will also cover [1A/x] -> [1A/y] -> [1A/y, 1B/y]
304 if (srManager.activeProbing) {
Charles Chan6e90fbb2018-07-23 15:27:34 -0700305
Charles Chan4b5769a2018-04-25 18:51:46 -0400306 srManager.getPairDeviceId(newLocation.deviceId()).ifPresent(pairDeviceId ->
307 srManager.getPairLocalPort(pairDeviceId).ifPresent(pairRemotePort ->
308 probe(host, newLocation, pairDeviceId, pairRemotePort)
309 )
310 );
311 }
Charles Chand9265a32017-06-16 15:19:24 -0700312 });
313
314 // For each unchanged location, add new IPs and remove old IPs.
Charles Chanfbcb8812018-04-18 18:41:05 -0700315 Sets.intersection(newLocations, prevLocations).forEach(unchangedLocation -> {
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700316 Sets.difference(prevIps, newIps).forEach(ip -> {
317 if (doubleTaggedHost) {
318 processDoubleTaggedRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(),
319 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, true);
320 } else {
321 processRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(),
322 hostMac, hostVlanId, ip, true);
323 }
324 });
Charles Chand9265a32017-06-16 15:19:24 -0700325
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700326 Sets.difference(newIps, prevIps).forEach(ip -> {
327 if (doubleTaggedHost) {
328 processDoubleTaggedRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(),
329 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, false);
330 } else {
331 processRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(),
332 hostMac, hostVlanId, ip, false);
333 }
334 });
Charles Chan6e90fbb2018-07-23 15:27:34 -0700335
336 // Verify existing location and see if it is still valid
337 srManager.probingService.probeHost(host, unchangedLocation, ProbeMode.VERIFY);
Charles Chand9265a32017-06-16 15:19:24 -0700338 });
Saurav Dase6c448a2018-01-18 12:07:33 -0800339
340 // ensure dual-homed host locations have viable uplinks
Saurav Dasec683dc2018-04-27 18:42:30 -0700341 if (newLocations.size() > prevLocations.size() || srManager.singleHomedDown) {
Saurav Dase6c448a2018-01-18 12:07:33 -0800342 newLocations.forEach(loc -> {
343 if (srManager.mastershipService.isLocalMaster(loc.deviceId())) {
Saurav Dasec683dc2018-04-27 18:42:30 -0700344 srManager.linkHandler.checkUplinksForHost(loc);
Saurav Dase6c448a2018-01-18 12:07:33 -0800345 }
346 });
347 }
Charles Chanc22cef32016-04-29 14:38:22 -0700348 }
349
Charles Chand9265a32017-06-16 15:19:24 -0700350 void processHostUpdatedEvent(HostEvent event) {
Charles Chan873661e2017-11-30 15:37:50 -0800351 Host host = event.subject();
352 MacAddress hostMac = host.mac();
353 VlanId hostVlanId = host.vlan();
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700354 EthType hostTpid = host.tpid();
Charles Chan873661e2017-11-30 15:37:50 -0800355 Set<HostLocation> locations = host.locations();
Charles Chanc22cef32016-04-29 14:38:22 -0700356 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
Charles Chan873661e2017-11-30 15:37:50 -0800357 Set<IpAddress> newIps = host.ipAddresses();
Charles Chanb3c1faf2017-11-20 08:46:24 -0800358 log.info("Host {}/{} is updated", hostMac, hostVlanId);
Charles Chanc22cef32016-04-29 14:38:22 -0700359
Charles Chand66d6712018-03-29 16:03:41 -0700360 locations.forEach(location -> {
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700361 Sets.difference(prevIps, newIps).forEach(ip -> {
362 if (isDoubleTaggedHost(host)) {
363 processDoubleTaggedRoutingRule(location.deviceId(), location.port(), hostMac,
364 host.innerVlan(), hostVlanId, hostTpid, ip, true);
365 } else {
366 processRoutingRule(location.deviceId(), location.port(), hostMac,
367 hostVlanId, ip, true);
368 }
369 });
370 Sets.difference(newIps, prevIps).forEach(ip -> {
371 if (isDoubleTaggedHost(host)) {
372 processDoubleTaggedRoutingRule(location.deviceId(), location.port(), hostMac,
373 host.innerVlan(), hostVlanId, hostTpid, ip, false);
374 } else {
375 processRoutingRule(location.deviceId(), location.port(), hostMac,
376 hostVlanId, ip, false);
377 }
378 });
Charles Chand9265a32017-06-16 15:19:24 -0700379 });
Charles Chanb3c1faf2017-11-20 08:46:24 -0800380
381 // Use the pair link temporarily before the second location of a dual-homed host shows up.
382 // This do not affect single-homed hosts since the flow will be blocked in
383 // processBridgingRule or processRoutingRule due to VLAN or IP mismatch respectively
Charles Chan873661e2017-11-30 15:37:50 -0800384 locations.forEach(location ->
Charles Chanb3c1faf2017-11-20 08:46:24 -0800385 srManager.getPairDeviceId(location.deviceId()).ifPresent(pairDeviceId -> {
Charles Chand66d6712018-03-29 16:03:41 -0700386 if (locations.stream().noneMatch(l -> l.deviceId().equals(pairDeviceId))) {
Charles Chan873661e2017-11-30 15:37:50 -0800387 Set<IpAddress> ipsToAdd = Sets.difference(newIps, prevIps);
388 Set<IpAddress> ipsToRemove = Sets.difference(prevIps, newIps);
389
Saurav Dasec683dc2018-04-27 18:42:30 -0700390 srManager.getPairLocalPort(pairDeviceId).ifPresent(pairRemotePort -> {
Charles Chanb3c1faf2017-11-20 08:46:24 -0800391 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
392 // when the host is untagged
Charles Chan868c9572018-06-15 18:54:18 -0700393 VlanId vlanId = vlanForPairPort(hostVlanId, location);
394 if (vlanId == null) {
395 return;
396 }
Charles Chanb3c1faf2017-11-20 08:46:24 -0800397
Charles Chan873661e2017-11-30 15:37:50 -0800398 ipsToRemove.forEach(ip ->
Charles Chanb3c1faf2017-11-20 08:46:24 -0800399 processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, true)
400 );
Charles Chan873661e2017-11-30 15:37:50 -0800401 ipsToAdd.forEach(ip ->
Charles Chanb3c1faf2017-11-20 08:46:24 -0800402 processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, false)
403 );
Charles Chan873661e2017-11-30 15:37:50 -0800404
405 if (srManager.activeProbing) {
406 probe(host, location, pairDeviceId, pairRemotePort);
407 }
Charles Chanb3c1faf2017-11-20 08:46:24 -0800408 });
409 }
Charles Chan873661e2017-11-30 15:37:50 -0800410 })
411 );
412 }
413
414 /**
415 * When a non-pair port comes up, probe each host on the pair device if
416 * (1) the host is tagged and the tagged vlan of current port contains host vlan; or
417 * (2) the host is untagged and the internal vlan is the same on the host port and current port.
418 *
419 * @param cp connect point
420 */
421 void processPortUp(ConnectPoint cp) {
Saurav Dasec683dc2018-04-27 18:42:30 -0700422 if (cp.port().equals(srManager.getPairLocalPort(cp.deviceId()).orElse(null))) {
Charles Chan873661e2017-11-30 15:37:50 -0800423 return;
424 }
425 if (srManager.activeProbing) {
426 srManager.getPairDeviceId(cp.deviceId())
427 .ifPresent(pairDeviceId -> srManager.hostService.getConnectedHosts(pairDeviceId).stream()
428 .filter(host -> isHostInVlanOfPort(host, pairDeviceId, cp))
Charles Chanc6bcdf92018-06-01 16:33:48 -0700429 .forEach(host -> srManager.probingService.probeHost(host, cp, ProbeMode.DISCOVER))
Charles Chan873661e2017-11-30 15:37:50 -0800430 );
431 }
432 }
433
434 /**
435 * Checks if given host located on given device id matches VLAN config of current port.
436 *
437 * @param host host to check
438 * @param deviceId device id to check
439 * @param cp current connect point
440 * @return true if the host located at deviceId matches the VLAN config on cp
441 */
442 private boolean isHostInVlanOfPort(Host host, DeviceId deviceId, ConnectPoint cp) {
443 VlanId internalVlan = srManager.getInternalVlanId(cp);
Charles Chan098ca202018-05-01 11:50:20 -0700444 Set<VlanId> taggedVlan = srManager.interfaceService.getTaggedVlanId(cp);
Charles Chan873661e2017-11-30 15:37:50 -0800445
446 return taggedVlan.contains(host.vlan()) ||
447 (internalVlan != null && host.locations().stream()
448 .filter(l -> l.deviceId().equals(deviceId))
449 .map(srManager::getInternalVlanId)
450 .anyMatch(internalVlan::equals));
451 }
452
453 /**
454 * Send a probe on all locations with the same VLAN on pair device, excluding pair port.
455 *
456 * @param host host to probe
457 * @param location newly discovered host location
458 * @param pairDeviceId pair device id
459 * @param pairRemotePort pair remote port
460 */
461 private void probe(Host host, ConnectPoint location, DeviceId pairDeviceId, PortNumber pairRemotePort) {
Mayank Tiwarif7942402018-10-29 18:27:35 -0400462 //Check if the host still exists in the host store
463 if (hostService.getHost(host.id()) == null) {
464 log.debug("Host entry for host {} no more present. Aborting hostprobe discover for this host", host.id());
465 return;
466 }
467
Charles Chan873661e2017-11-30 15:37:50 -0800468 VlanId vlanToProbe = host.vlan().equals(VlanId.NONE) ?
469 srManager.getInternalVlanId(location) : host.vlan();
Mayank Tiwarif7942402018-10-29 18:27:35 -0400470 if (srManager.symmetricProbing) {
471 srManager.interfaceService.getInterfaces().stream()
472 .filter(i -> i.vlanTagged().contains(vlanToProbe) ||
473 i.vlanUntagged().equals(vlanToProbe) ||
474 i.vlanNative().equals(vlanToProbe))
475 .filter(i -> i.connectPoint().deviceId().equals(pairDeviceId))
476 .filter(i -> i.connectPoint().port().equals(location.port()))
477 .forEach(i -> {
478 log.debug("Probing host {} on pair device {}", host.id(), i.connectPoint());
479 srManager.probingService.probeHost(host, i.connectPoint(), ProbeMode.DISCOVER);
480 });
481 } else {
482 srManager.interfaceService.getInterfaces().stream()
483 .filter(i -> i.vlanTagged().contains(vlanToProbe) ||
484 i.vlanUntagged().equals(vlanToProbe) ||
485 i.vlanNative().equals(vlanToProbe))
486 .filter(i -> i.connectPoint().deviceId().equals(pairDeviceId))
487 .filter(i -> !i.connectPoint().port().equals(pairRemotePort))
488 .forEach(i -> {
489 log.debug("Probing host {} on pair device {}", host.id(), i.connectPoint());
490 srManager.probingService.probeHost(host, i.connectPoint(), ProbeMode.DISCOVER);
491 });
492 }
Charles Chanc22cef32016-04-29 14:38:22 -0700493 }
494
495 /**
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700496 * Populates or revokes a bridging rule on given deviceId that matches given mac,
497 * given vlan and output to given port.
Charles Chan9595e6a2017-06-15 19:25:25 -0700498 *
499 * @param deviceId device ID
500 * @param port port
501 * @param mac mac address
502 * @param vlanId VLAN ID
503 * @param revoke true to revoke the rule; false to populate
504 */
505 private void processBridgingRule(DeviceId deviceId, PortNumber port, MacAddress mac,
506 VlanId vlanId, boolean revoke) {
Charles Chand66d6712018-03-29 16:03:41 -0700507 log.info("{} bridging entry for host {}/{} at {}:{}", revoke ? "Revoking" : "Populating",
Charles Chan9595e6a2017-06-15 19:25:25 -0700508 mac, vlanId, deviceId, port);
509
Charles Chand66d6712018-03-29 16:03:41 -0700510 if (!revoke) {
511 srManager.defaultRoutingHandler.populateBridging(deviceId, port, mac, vlanId);
512 } else {
513 srManager.defaultRoutingHandler.revokeBridging(deviceId, port, mac, vlanId);
Charles Chan9595e6a2017-06-15 19:25:25 -0700514 }
Charles Chan9595e6a2017-06-15 19:25:25 -0700515 }
516
517 /**
518 * Populate or revoke a routing rule on given deviceId that matches given ip,
519 * set destination mac to given mac, set vlan to given vlan and output to given port.
520 *
521 * @param deviceId device ID
522 * @param port port
523 * @param mac mac address
524 * @param vlanId VLAN ID
525 * @param ip IP address
526 * @param revoke true to revoke the rule; false to populate
527 */
528 private void processRoutingRule(DeviceId deviceId, PortNumber port, MacAddress mac,
529 VlanId vlanId, IpAddress ip, boolean revoke) {
530 ConnectPoint location = new ConnectPoint(deviceId, port);
Charles Chand9265a32017-06-16 15:19:24 -0700531 if (!srManager.deviceConfiguration.inSameSubnet(location, ip)) {
532 log.info("{} is not included in the subnet config of {}/{}. Ignored.", ip, deviceId, port);
533 return;
Charles Chan9595e6a2017-06-15 19:25:25 -0700534 }
Charles Chan9595e6a2017-06-15 19:25:25 -0700535
Charles Chand9265a32017-06-16 15:19:24 -0700536 log.info("{} routing rule for {} at {}", revoke ? "Revoking" : "Populating", ip, location);
537 if (revoke) {
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000538 srManager.defaultRoutingHandler.revokeRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port, true);
Charles Chand9265a32017-06-16 15:19:24 -0700539 } else {
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000540 srManager.defaultRoutingHandler.populateRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port, true);
Charles Chan370a65b2016-05-10 17:29:47 -0700541 }
Charles Chan370a65b2016-05-10 17:29:47 -0700542 }
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700543
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700544 /**
545 * Populate or revoke a routing rule and egress rules on given deviceId that matches given IP,
546 * to set destination mac to given mac, set inner vlan and outer vlan to given vlans,
547 * set outer TPID, and output to given port.
548 *
549 * @param deviceId device ID
550 * @param port port
551 * @param mac mac address
552 * @param innerVlan inner VLAN ID
553 * @param outerVlan outer VLAN ID
554 * @param outerTpid outer TPID
555 * @param ip IP address
556 * @param revoke true to revoke the rule; false to populate
557 */
558 private void processDoubleTaggedRoutingRule(DeviceId deviceId, PortNumber port,
559 MacAddress mac, VlanId innerVlan,
560 VlanId outerVlan, EthType outerTpid,
561 IpAddress ip, boolean revoke) {
Charles Chanfbca55e2018-07-24 16:40:35 -0700562 if (!srManager.routeDoubleTaggedHosts) {
563 log.debug("Routing for double tagged host is disabled. Ignore {}/{}/{}", mac, outerVlan, innerVlan);
Charles Chan4eb73bb2018-07-19 14:55:31 -0700564 return;
565 }
566
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700567 ConnectPoint location = new ConnectPoint(deviceId, port);
568 if (!srManager.deviceConfiguration.inSameSubnet(location, ip)) {
569 log.info("{} is not included in the subnet config of {}/{}. Ignored.", ip, deviceId, port);
570 return;
571 }
572 log.info("{} routing rule for double-tagged host {} at {}",
573 revoke ? "Revoking" : "Populating", ip, location);
574 if (revoke) {
575 srManager.defaultRoutingHandler.revokeDoubleTaggedRoute(
576 deviceId, ip.toIpPrefix(), mac, innerVlan, outerVlan, outerTpid, port);
577 } else {
578 srManager.defaultRoutingHandler.populateDoubleTaggedRoute(
579 deviceId, ip.toIpPrefix(), mac, innerVlan, outerVlan, outerTpid, port);
580 }
581 }
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700582
Charles Chan4eb73bb2018-07-19 14:55:31 -0700583 void populateAllDoubleTaggedHost() {
Charles Chanfbca55e2018-07-24 16:40:35 -0700584 log.info("Enabling routing for all double tagged hosts");
Charles Chan4eb73bb2018-07-19 14:55:31 -0700585 Sets.newHashSet(srManager.hostService.getHosts()).stream().filter(this::isDoubleTaggedHost)
586 .forEach(h -> h.locations().forEach(l ->
587 h.ipAddresses().forEach(i ->
588 processDoubleTaggedRoutingRule(l.deviceId(), l.port(), h.mac(), h.innerVlan(),
589 h.vlan(), h.tpid(), i, false)
590 )
591 )
592 );
593 }
594
595 void revokeAllDoubleTaggedHost() {
Charles Chanfbca55e2018-07-24 16:40:35 -0700596 log.info("Disabling routing for all double tagged hosts");
Charles Chan4eb73bb2018-07-19 14:55:31 -0700597 Sets.newHashSet(srManager.hostService.getHosts()).stream().filter(this::isDoubleTaggedHost)
598 .forEach(h -> h.locations().forEach(l ->
599 h.ipAddresses().forEach(i ->
600 processDoubleTaggedRoutingRule(l.deviceId(), l.port(), h.mac(), h.innerVlan(),
601 h.vlan(), h.tpid(), i, true)
602 )
603 )
604 );
605 }
606
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700607 /**
Charles Chan868c9572018-06-15 18:54:18 -0700608 * Returns VLAN ID to be used to program redirection flow on pair port.
609 *
610 * @param hostVlanId host VLAN ID
611 * @param location host location
612 * @return VLAN ID to be used; Or null if host VLAN does not match the interface config
613 */
614 VlanId vlanForPairPort(VlanId hostVlanId, ConnectPoint location) {
615 VlanId internalVlan = srManager.getInternalVlanId(location);
616 Set<VlanId> taggedVlan = srManager.interfaceService.getTaggedVlanId(location);
617
618 if (!hostVlanId.equals(VlanId.NONE) && taggedVlan.contains(hostVlanId)) {
619 return hostVlanId;
620 } else if (hostVlanId.equals(VlanId.NONE) && internalVlan != null) {
621 return internalVlan;
622 } else {
623 log.warn("VLAN mismatch. hostVlan={}, location={}, internalVlan={}, taggedVlan={}",
624 hostVlanId, location, internalVlan, taggedVlan);
625 return null;
626 }
627 }
628
629 /**
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700630 * Update forwarding objective for unicast bridging and unicast routing.
631 * Also check the validity of updated interface configuration on VLAN.
632 *
633 * @param deviceId device ID that host attaches to
634 * @param portNum port number that host attaches to
635 * @param vlanId Vlan ID configured on the switch port
636 * @param popVlan true to pop Vlan tag at TrafficTreatment, false otherwise
637 * @param install true to populate the objective, false to revoke
638 */
639 void processIntfVlanUpdatedEvent(DeviceId deviceId, PortNumber portNum, VlanId vlanId,
640 boolean popVlan, boolean install) {
641 ConnectPoint connectPoint = new ConnectPoint(deviceId, portNum);
642 Set<Host> hosts = hostService.getConnectedHosts(connectPoint);
643
644 if (hosts == null || hosts.size() == 0) {
Charles Chan39b75522018-04-21 00:44:29 -0700645 log.debug("processIntfVlanUpdatedEvent: No hosts connected to {}", connectPoint);
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700646 return;
647 }
648
649 hosts.forEach(host -> {
650 MacAddress mac = host.mac();
651 VlanId hostVlanId = host.vlan();
652
653 // Check whether the host vlan is valid for new interface configuration
654 if ((!popVlan && hostVlanId.equals(vlanId)) ||
655 (popVlan && hostVlanId.equals(VlanId.NONE))) {
Charles Chand66d6712018-03-29 16:03:41 -0700656 srManager.defaultRoutingHandler.updateBridging(deviceId, portNum, mac, vlanId, popVlan, install);
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700657 // Update Forwarding objective and corresponding simple Next objective
658 // for each host and IP address connected to given port
659 host.ipAddresses().forEach(ipAddress ->
Charles Chand66d6712018-03-29 16:03:41 -0700660 srManager.defaultRoutingHandler.updateFwdObj(deviceId, portNum, ipAddress.toIpPrefix(),
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700661 mac, vlanId, popVlan, install)
662 );
663 }
664 });
665 }
666
667 /**
668 * Populate or revoke routing rule for each host, according to the updated
669 * subnet configuration on the interface.
670 * @param cp connect point of the updated interface
671 * @param ipPrefixSet IP Prefixes added or removed
672 * @param install true if IP Prefixes added, false otherwise
673 */
674 void processIntfIpUpdatedEvent(ConnectPoint cp, Set<IpPrefix> ipPrefixSet, boolean install) {
675 Set<Host> hosts = hostService.getConnectedHosts(cp);
676
677 if (hosts == null || hosts.size() == 0) {
Charles Chan39b75522018-04-21 00:44:29 -0700678 log.debug("processIntfIpUpdatedEvent: No hosts connected to {}", cp);
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700679 return;
680 }
681
682 // Check whether the host IP address is in the interface's subnet
683 hosts.forEach(host ->
684 host.ipAddresses().forEach(hostIpAddress -> {
685 ipPrefixSet.forEach(ipPrefix -> {
686 if (install && ipPrefix.contains(hostIpAddress)) {
Charles Chand66d6712018-03-29 16:03:41 -0700687 srManager.defaultRoutingHandler.populateRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000688 host.mac(), host.vlan(), cp.port(), true);
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700689 } else if (!install && ipPrefix.contains(hostIpAddress)) {
Charles Chand66d6712018-03-29 16:03:41 -0700690 srManager.defaultRoutingHandler.revokeRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000691 host.mac(), host.vlan(), cp.port(), true);
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700692 }
693 });
694 }));
695 }
Saurav Dase6c448a2018-01-18 12:07:33 -0800696
697 /**
698 * Returns the set of portnumbers on the given device that are part of the
699 * locations for dual-homed hosts.
700 *
701 * @param deviceId the given deviceId
702 * @return set of port numbers on given device that are dual-homed host
703 * locations. May be empty if no dual homed hosts are connected to
704 * the given device
705 */
706 Set<PortNumber> getDualHomedHostPorts(DeviceId deviceId) {
707 Set<PortNumber> dualHomedLocations = new HashSet<>();
708 srManager.hostService.getConnectedHosts(deviceId).stream()
709 .filter(host -> host.locations().size() == 2)
710 .forEach(host -> host.locations().stream()
711 .filter(loc -> loc.deviceId().equals(deviceId))
712 .forEach(loc -> dualHomedLocations.add(loc.port())));
713 return dualHomedLocations;
714 }
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700715
716 /**
717 * Checks if the given host is double-tagged or not.
718 *
719 * @param host host to check
720 * @return true if it is double-tagged, false otherwise
721 */
722 private boolean isDoubleTaggedHost(Host host) {
723 return !host.innerVlan().equals(VlanId.NONE);
724 }
725
Charles Chan1eaf4802016-04-18 13:44:03 -0700726}