blob: e14fdc25d78b2ba90e2409ed61a01638ff09eb2b [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;
Charles Chan47933752017-11-30 15:37:50 -080030import org.onosproject.net.host.HostLocationProbingService.ProbeMode;
Charles Chand2990362016-04-18 13:44:03 -070031import org.onosproject.net.host.HostService;
32import 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 static final int HOST_MOVED_DELAY_MS = 1000;
50
Charles Chan2e2e3402017-06-19 14:00:53 -070051 protected final SegmentRoutingManager srManager;
Charles Chand2990362016-04-18 13:44:03 -070052 private HostService hostService;
Charles Chand2990362016-04-18 13:44:03 -070053
54 /**
55 * Constructs the HostHandler.
56 *
57 * @param srManager Segment Routing manager
58 */
Charles Chanf9a52702017-06-16 15:19:24 -070059 HostHandler(SegmentRoutingManager srManager) {
Charles Chand2990362016-04-18 13:44:03 -070060 this.srManager = srManager;
Charles Chand2990362016-04-18 13:44:03 -070061 hostService = srManager.hostService;
Charles Chand2990362016-04-18 13:44:03 -070062 }
63
Charles Chan03a73e02016-10-24 14:52:01 -070064 protected void init(DeviceId devId) {
Charles Chanf9a52702017-06-16 15:19:24 -070065 hostService.getHosts().forEach(host ->
66 host.locations().stream()
Charles Chan47933752017-11-30 15:37:50 -080067 .filter(location -> location.deviceId().equals(devId) ||
68 location.deviceId().equals(srManager.getPairDeviceId(devId).orElse(null)))
Charles Chanf9a52702017-06-16 15:19:24 -070069 .forEach(location -> processHostAddedAtLocation(host, location))
70 );
Charles Chan93e71ba2016-04-29 14:38:22 -070071 }
Charles Chand2990362016-04-18 13:44:03 -070072
Charles Chanf9a52702017-06-16 15:19:24 -070073 void processHostAddedEvent(HostEvent event) {
Charles Chan35fd1a72016-06-13 18:54:31 -070074 processHostAdded(event.subject());
Charles Chan93e71ba2016-04-29 14:38:22 -070075 }
76
Charles Chanf9a52702017-06-16 15:19:24 -070077 private void processHostAdded(Host host) {
78 host.locations().forEach(location -> processHostAddedAtLocation(host, location));
Saurav Das45f48152018-01-18 12:07:33 -080079 // ensure dual-homed host locations have viable uplinks
80 if (host.locations().size() > 1) {
81 host.locations().forEach(loc -> {
82 if (srManager.mastershipService.isLocalMaster(loc.deviceId())) {
83 srManager.linkHandler.checkUplinksForDualHomedHosts(loc);
84 }
85 });
86 }
Charles Chand2990362016-04-18 13:44:03 -070087 }
88
Charles Chanf9a52702017-06-16 15:19:24 -070089 void processHostAddedAtLocation(Host host, HostLocation location) {
90 checkArgument(host.locations().contains(location), "{} is not a location of {}", location, host);
91
Charles Chan8e786b52017-09-12 18:57:47 -070092 MacAddress hostMac = host.mac();
93 VlanId hostVlanId = host.vlan();
Charles Chanf9a52702017-06-16 15:19:24 -070094 Set<HostLocation> locations = host.locations();
95 Set<IpAddress> ips = host.ipAddresses();
Charles Chan8e786b52017-09-12 18:57:47 -070096 log.info("Host {}/{} is added at {}", hostMac, hostVlanId, locations);
Charles Chanf9a52702017-06-16 15:19:24 -070097
Jonghwan Hyun800d9d02018-04-09 09:40:50 -070098 if (isDoubleTaggedHost(host)) {
99 ips.forEach(ip ->
100 processDoubleTaggedRoutingRule(location.deviceId(), location.port(), hostMac,
101 host.innerVlan(), hostVlanId, host.tpid(), ip, false)
102 );
103 } else {
104 processBridgingRule(location.deviceId(), location.port(), hostMac, hostVlanId, false);
105 ips.forEach(ip ->
Charles Chan2ff1bac2018-03-29 16:03:41 -0700106 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, false)
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700107 );
108 }
Charles Chan8e786b52017-09-12 18:57:47 -0700109
110 // Use the pair link temporarily before the second location of a dual-homed host shows up.
111 // This do not affect single-homed hosts since the flow will be blocked in
112 // processBridgingRule or processRoutingRule due to VLAN or IP mismatch respectively
113 srManager.getPairDeviceId(location.deviceId()).ifPresent(pairDeviceId -> {
Charles Chan2ff1bac2018-03-29 16:03:41 -0700114 if (host.locations().stream().noneMatch(l -> l.deviceId().equals(pairDeviceId))) {
Charles Chan8e786b52017-09-12 18:57:47 -0700115 srManager.getPairLocalPorts(pairDeviceId).ifPresent(pairRemotePort -> {
116 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
117 // when the host is untagged
118 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(hostVlanId);
119
120 processBridgingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, false);
121 ips.forEach(ip -> processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId,
122 ip, false));
Charles Chan47933752017-11-30 15:37:50 -0800123
124 if (srManager.activeProbing) {
125 probe(host, location, pairDeviceId, pairRemotePort);
126 }
Charles Chan8e786b52017-09-12 18:57:47 -0700127 });
128 }
129 });
Charles Chanf9a52702017-06-16 15:19:24 -0700130 }
131
132 void processHostRemovedEvent(HostEvent event) {
Charles Chan35fd1a72016-06-13 18:54:31 -0700133 processHostRemoved(event.subject());
134 }
135
Charles Chanf9a52702017-06-16 15:19:24 -0700136 private void processHostRemoved(Host host) {
Charles Chan65238242017-06-22 18:03:14 -0700137 MacAddress hostMac = host.mac();
138 VlanId hostVlanId = host.vlan();
Charles Chanf9a52702017-06-16 15:19:24 -0700139 Set<HostLocation> locations = host.locations();
Charles Chan35fd1a72016-06-13 18:54:31 -0700140 Set<IpAddress> ips = host.ipAddresses();
Charles Chan65238242017-06-22 18:03:14 -0700141 log.info("Host {}/{} is removed from {}", hostMac, hostVlanId, locations);
Charles Chan93e71ba2016-04-29 14:38:22 -0700142
Charles Chan2fde6d42017-08-23 14:46:43 -0700143 locations.forEach(location -> {
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700144 if (isDoubleTaggedHost(host)) {
145 ips.forEach(ip ->
146 processDoubleTaggedRoutingRule(location.deviceId(), location.port(), hostMac,
147 host.innerVlan(), hostVlanId, host.tpid(), ip, true)
148 );
149 } else {
150 processBridgingRule(location.deviceId(), location.port(), hostMac, hostVlanId, true);
151 ips.forEach(ip ->
Charles Chan2ff1bac2018-03-29 16:03:41 -0700152 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, true)
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700153 );
154 }
Charles Chan65238242017-06-22 18:03:14 -0700155
156 // Also remove redirection flows on the pair device if exists.
157 Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(location.deviceId());
158 Optional<PortNumber> pairLocalPort = srManager.getPairLocalPorts(location.deviceId());
Charles Chan2ff1bac2018-03-29 16:03:41 -0700159 if (pairDeviceId.isPresent() && pairLocalPort.isPresent()) {
Charles Chan65238242017-06-22 18:03:14 -0700160 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
161 // when the host is untagged
162 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(hostVlanId);
163
164 processBridgingRule(pairDeviceId.get(), pairLocalPort.get(), hostMac, vlanId, true);
165 ips.forEach(ip ->
166 processRoutingRule(pairDeviceId.get(), pairLocalPort.get(), hostMac, vlanId,
167 ip, true));
168 }
Charles Chanf9a52702017-06-16 15:19:24 -0700169 });
Charles Chan93e71ba2016-04-29 14:38:22 -0700170 }
171
Charles Chanf9a52702017-06-16 15:19:24 -0700172 void processHostMovedEvent(HostEvent event) {
Charles Chanf9a52702017-06-16 15:19:24 -0700173 Set<HostLocation> prevLocations = event.prevSubject().locations();
Charles Chan93e71ba2016-04-29 14:38:22 -0700174 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
Charles Chanf9a52702017-06-16 15:19:24 -0700175 Set<HostLocation> newLocations = event.subject().locations();
Charles Chan93e71ba2016-04-29 14:38:22 -0700176 Set<IpAddress> newIps = event.subject().ipAddresses();
Charles Chan93e71ba2016-04-29 14:38:22 -0700177
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700178 processHostMoved(event.subject(), prevLocations, prevIps, newLocations, newIps);
Charles Chan47933752017-11-30 15:37:50 -0800179 }
180
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700181 private void processHostMoved(Host host, Set<HostLocation> prevLocations, Set<IpAddress> prevIps,
182 Set<HostLocation> newLocations, Set<IpAddress> newIps) {
183 MacAddress hostMac = host.mac();
184 VlanId hostVlanId = host.vlan();
185 EthType hostTpid = host.tpid();
186 boolean doubleTaggedHost = isDoubleTaggedHost(host);
Charles Chan47933752017-11-30 15:37:50 -0800187 log.info("Host {}/{} is moved from {} to {}", hostMac, hostVlanId, prevLocations, newLocations);
Charles Chanf9a52702017-06-16 15:19:24 -0700188 Set<DeviceId> newDeviceIds = newLocations.stream().map(HostLocation::deviceId)
189 .collect(Collectors.toSet());
Charles Chan93e71ba2016-04-29 14:38:22 -0700190
Charles Chanf9a52702017-06-16 15:19:24 -0700191 // For each old location
Charles Chan2ff1bac2018-03-29 16:03:41 -0700192 Sets.difference(prevLocations, newLocations).forEach(prevLocation -> {
Charles Chan65238242017-06-22 18:03:14 -0700193 // Remove routing rules for old IPs
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700194 Sets.difference(prevIps, newIps).forEach(ip -> {
195 if (doubleTaggedHost) {
196 processDoubleTaggedRoutingRule(prevLocation.deviceId(), prevLocation.port(),
197 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, true);
198 } else {
Charles Chan65238242017-06-22 18:03:14 -0700199 processRoutingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId,
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700200 ip, true);
201 }
202 });
Charles Chan65238242017-06-22 18:03:14 -0700203
204 // Redirect the flows to pair link if configured
205 // Note: Do not continue removing any rule
206 Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(prevLocation.deviceId());
207 Optional<PortNumber> pairLocalPort = srManager.getPairLocalPorts(prevLocation.deviceId());
208 if (pairDeviceId.isPresent() && pairLocalPort.isPresent() && newLocations.stream()
209 .anyMatch(location -> location.deviceId().equals(pairDeviceId.get()))) {
210 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
211 // when the host is untagged
212 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(prevLocation)).orElse(hostVlanId);
213
214 processBridgingRule(prevLocation.deviceId(), pairLocalPort.get(), hostMac, vlanId, false);
215 newIps.forEach(ip ->
216 processRoutingRule(prevLocation.deviceId(), pairLocalPort.get(), hostMac, vlanId,
217 ip, false));
218 return;
219 }
Charles Chanf9a52702017-06-16 15:19:24 -0700220
Charles Chan50bb6ef2018-04-18 18:41:05 -0700221 // Remove flows for unchanged IPs only when the host moves from a switch to another.
Charles Chanf9a52702017-06-16 15:19:24 -0700222 // Otherwise, do not remove and let the adding part update the old flow
223 if (!newDeviceIds.contains(prevLocation.deviceId())) {
Charles Chan65238242017-06-22 18:03:14 -0700224 processBridgingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId, true);
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700225 Sets.intersection(prevIps, newIps).forEach(ip -> {
226 if (doubleTaggedHost) {
227 processDoubleTaggedRoutingRule(prevLocation.deviceId(), prevLocation.port(),
228 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, true);
229 } else {
230 processRoutingRule(prevLocation.deviceId(), prevLocation.port(),
231 hostMac, hostVlanId, ip, true);
232 }
233 });
Charles Chanf9a52702017-06-16 15:19:24 -0700234 }
235
236 // Remove bridging rules if new interface vlan is different from old interface vlan
237 // Otherwise, do not remove and let the adding part update the old flow
238 if (newLocations.stream().noneMatch(newLocation -> {
239 VlanId oldAssignedVlan = srManager.getInternalVlanId(prevLocation);
240 VlanId newAssignedVlan = srManager.getInternalVlanId(newLocation);
241 // Host is tagged and the new location has the host vlan in vlan-tagged
Charles Chan65238242017-06-22 18:03:14 -0700242 return srManager.getTaggedVlanId(newLocation).contains(hostVlanId) ||
Charles Chanf9a52702017-06-16 15:19:24 -0700243 (oldAssignedVlan != null && newAssignedVlan != null &&
244 // Host is untagged and the new location has the same assigned vlan
245 oldAssignedVlan.equals(newAssignedVlan));
246 })) {
Charles Chan65238242017-06-22 18:03:14 -0700247 processBridgingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId, true);
Charles Chanf9a52702017-06-16 15:19:24 -0700248 }
249
250 // Remove routing rules for unchanged IPs if none of the subnet of new location contains
251 // the IP. Otherwise, do not remove and let the adding part update the old flow
252 Sets.intersection(prevIps, newIps).forEach(ip -> {
253 if (newLocations.stream().noneMatch(newLocation ->
254 srManager.deviceConfiguration.inSameSubnet(newLocation, ip))) {
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700255 if (doubleTaggedHost) {
256 processDoubleTaggedRoutingRule(prevLocation.deviceId(), prevLocation.port(),
257 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, true);
258 } else {
259 processRoutingRule(prevLocation.deviceId(), prevLocation.port(),
260 hostMac, hostVlanId, ip, true);
261 }
Charles Chanf9a52702017-06-16 15:19:24 -0700262 }
Charles Chan93e71ba2016-04-29 14:38:22 -0700263 });
Charles Chanf9a52702017-06-16 15:19:24 -0700264 });
265
266 // For each new location, add all new IPs.
Charles Chan50bb6ef2018-04-18 18:41:05 -0700267 Sets.difference(newLocations, prevLocations).forEach(newLocation -> {
Charles Chan65238242017-06-22 18:03:14 -0700268 processBridgingRule(newLocation.deviceId(), newLocation.port(), hostMac, hostVlanId, false);
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700269 newIps.forEach(ip -> {
270 if (doubleTaggedHost) {
271 processDoubleTaggedRoutingRule(newLocation.deviceId(), newLocation.port(),
272 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, false);
273 } else {
Charles Chan65238242017-06-22 18:03:14 -0700274 processRoutingRule(newLocation.deviceId(), newLocation.port(), hostMac, hostVlanId,
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700275 ip, false);
276 }
277 });
Charles Chanf9a52702017-06-16 15:19:24 -0700278 });
279
280 // For each unchanged location, add new IPs and remove old IPs.
Charles Chan50bb6ef2018-04-18 18:41:05 -0700281 Sets.intersection(newLocations, prevLocations).forEach(unchangedLocation -> {
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700282 Sets.difference(prevIps, newIps).forEach(ip -> {
283 if (doubleTaggedHost) {
284 processDoubleTaggedRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(),
285 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, true);
286 } else {
287 processRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(),
288 hostMac, hostVlanId, ip, true);
289 }
290 });
Charles Chanf9a52702017-06-16 15:19:24 -0700291
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700292 Sets.difference(newIps, prevIps).forEach(ip -> {
293 if (doubleTaggedHost) {
294 processDoubleTaggedRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(),
295 hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, false);
296 } else {
297 processRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(),
298 hostMac, hostVlanId, ip, false);
299 }
300 });
Charles Chanf9a52702017-06-16 15:19:24 -0700301 });
Saurav Das45f48152018-01-18 12:07:33 -0800302
303 // ensure dual-homed host locations have viable uplinks
304 if (newLocations.size() > prevLocations.size()) {
305 newLocations.forEach(loc -> {
306 if (srManager.mastershipService.isLocalMaster(loc.deviceId())) {
307 srManager.linkHandler.checkUplinksForDualHomedHosts(loc);
308 }
309 });
310 }
Charles Chan93e71ba2016-04-29 14:38:22 -0700311 }
312
Charles Chanf9a52702017-06-16 15:19:24 -0700313 void processHostUpdatedEvent(HostEvent event) {
Charles Chan47933752017-11-30 15:37:50 -0800314 Host host = event.subject();
315 MacAddress hostMac = host.mac();
316 VlanId hostVlanId = host.vlan();
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700317 EthType hostTpid = host.tpid();
Charles Chan47933752017-11-30 15:37:50 -0800318 Set<HostLocation> locations = host.locations();
Charles Chan93e71ba2016-04-29 14:38:22 -0700319 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
Charles Chan47933752017-11-30 15:37:50 -0800320 Set<IpAddress> newIps = host.ipAddresses();
Charles Chan3d650a62017-11-20 08:46:24 -0800321 log.info("Host {}/{} is updated", hostMac, hostVlanId);
Charles Chan93e71ba2016-04-29 14:38:22 -0700322
Charles Chan2ff1bac2018-03-29 16:03:41 -0700323 locations.forEach(location -> {
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700324 Sets.difference(prevIps, newIps).forEach(ip -> {
325 if (isDoubleTaggedHost(host)) {
326 processDoubleTaggedRoutingRule(location.deviceId(), location.port(), hostMac,
327 host.innerVlan(), hostVlanId, hostTpid, ip, true);
328 } else {
329 processRoutingRule(location.deviceId(), location.port(), hostMac,
330 hostVlanId, ip, true);
331 }
332 });
333 Sets.difference(newIps, prevIps).forEach(ip -> {
334 if (isDoubleTaggedHost(host)) {
335 processDoubleTaggedRoutingRule(location.deviceId(), location.port(), hostMac,
336 host.innerVlan(), hostVlanId, hostTpid, ip, false);
337 } else {
338 processRoutingRule(location.deviceId(), location.port(), hostMac,
339 hostVlanId, ip, false);
340 }
341 });
Charles Chanf9a52702017-06-16 15:19:24 -0700342 });
Charles Chan3d650a62017-11-20 08:46:24 -0800343
344 // Use the pair link temporarily before the second location of a dual-homed host shows up.
345 // This do not affect single-homed hosts since the flow will be blocked in
346 // processBridgingRule or processRoutingRule due to VLAN or IP mismatch respectively
Charles Chan47933752017-11-30 15:37:50 -0800347 locations.forEach(location ->
Charles Chan3d650a62017-11-20 08:46:24 -0800348 srManager.getPairDeviceId(location.deviceId()).ifPresent(pairDeviceId -> {
Charles Chan2ff1bac2018-03-29 16:03:41 -0700349 if (locations.stream().noneMatch(l -> l.deviceId().equals(pairDeviceId))) {
Charles Chan47933752017-11-30 15:37:50 -0800350 Set<IpAddress> ipsToAdd = Sets.difference(newIps, prevIps);
351 Set<IpAddress> ipsToRemove = Sets.difference(prevIps, newIps);
352
Charles Chan3d650a62017-11-20 08:46:24 -0800353 srManager.getPairLocalPorts(pairDeviceId).ifPresent(pairRemotePort -> {
354 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
355 // when the host is untagged
356 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(hostVlanId);
357
Charles Chan47933752017-11-30 15:37:50 -0800358 ipsToRemove.forEach(ip ->
Charles Chan3d650a62017-11-20 08:46:24 -0800359 processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, true)
360 );
Charles Chan47933752017-11-30 15:37:50 -0800361 ipsToAdd.forEach(ip ->
Charles Chan3d650a62017-11-20 08:46:24 -0800362 processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, false)
363 );
Charles Chan47933752017-11-30 15:37:50 -0800364
365 if (srManager.activeProbing) {
366 probe(host, location, pairDeviceId, pairRemotePort);
367 }
Charles Chan3d650a62017-11-20 08:46:24 -0800368 });
369 }
Charles Chan47933752017-11-30 15:37:50 -0800370 })
371 );
372 }
373
374 /**
375 * When a non-pair port comes up, probe each host on the pair device if
376 * (1) the host is tagged and the tagged vlan of current port contains host vlan; or
377 * (2) the host is untagged and the internal vlan is the same on the host port and current port.
378 *
379 * @param cp connect point
380 */
381 void processPortUp(ConnectPoint cp) {
382 if (cp.port().equals(srManager.getPairLocalPorts(cp.deviceId()).orElse(null))) {
383 return;
384 }
385 if (srManager.activeProbing) {
386 srManager.getPairDeviceId(cp.deviceId())
387 .ifPresent(pairDeviceId -> srManager.hostService.getConnectedHosts(pairDeviceId).stream()
388 .filter(host -> isHostInVlanOfPort(host, pairDeviceId, cp))
389 .forEach(host -> srManager.probingService.probeHostLocation(host, cp, ProbeMode.DISCOVER))
390 );
391 }
392 }
393
394 /**
395 * Checks if given host located on given device id matches VLAN config of current port.
396 *
397 * @param host host to check
398 * @param deviceId device id to check
399 * @param cp current connect point
400 * @return true if the host located at deviceId matches the VLAN config on cp
401 */
402 private boolean isHostInVlanOfPort(Host host, DeviceId deviceId, ConnectPoint cp) {
403 VlanId internalVlan = srManager.getInternalVlanId(cp);
404 Set<VlanId> taggedVlan = srManager.getTaggedVlanId(cp);
405
406 return taggedVlan.contains(host.vlan()) ||
407 (internalVlan != null && host.locations().stream()
408 .filter(l -> l.deviceId().equals(deviceId))
409 .map(srManager::getInternalVlanId)
410 .anyMatch(internalVlan::equals));
411 }
412
413 /**
414 * Send a probe on all locations with the same VLAN on pair device, excluding pair port.
415 *
416 * @param host host to probe
417 * @param location newly discovered host location
418 * @param pairDeviceId pair device id
419 * @param pairRemotePort pair remote port
420 */
421 private void probe(Host host, ConnectPoint location, DeviceId pairDeviceId, PortNumber pairRemotePort) {
422 VlanId vlanToProbe = host.vlan().equals(VlanId.NONE) ?
423 srManager.getInternalVlanId(location) : host.vlan();
424 srManager.interfaceService.getInterfaces().stream()
425 .filter(i -> i.vlanTagged().contains(vlanToProbe) ||
426 i.vlanUntagged().equals(vlanToProbe) ||
427 i.vlanNative().equals(vlanToProbe))
428 .filter(i -> i.connectPoint().deviceId().equals(pairDeviceId))
429 .filter(i -> !i.connectPoint().port().equals(pairRemotePort))
430 .forEach(i -> {
431 log.debug("Probing host {} on pair device {}", host.id(), i.connectPoint());
432 srManager.probingService.probeHostLocation(host, i.connectPoint(), ProbeMode.DISCOVER);
433 });
Charles Chan93e71ba2016-04-29 14:38:22 -0700434 }
435
436 /**
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700437 * Populates or revokes a bridging rule on given deviceId that matches given mac,
438 * given vlan and output to given port.
Charles Chanb7b4c932017-06-15 19:25:25 -0700439 *
440 * @param deviceId device ID
441 * @param port port
442 * @param mac mac address
443 * @param vlanId VLAN ID
444 * @param revoke true to revoke the rule; false to populate
445 */
446 private void processBridgingRule(DeviceId deviceId, PortNumber port, MacAddress mac,
447 VlanId vlanId, boolean revoke) {
Charles Chan2ff1bac2018-03-29 16:03:41 -0700448 log.info("{} bridging entry for host {}/{} at {}:{}", revoke ? "Revoking" : "Populating",
Charles Chanb7b4c932017-06-15 19:25:25 -0700449 mac, vlanId, deviceId, port);
450
Charles Chan2ff1bac2018-03-29 16:03:41 -0700451 if (!revoke) {
452 srManager.defaultRoutingHandler.populateBridging(deviceId, port, mac, vlanId);
453 } else {
454 srManager.defaultRoutingHandler.revokeBridging(deviceId, port, mac, vlanId);
Charles Chanb7b4c932017-06-15 19:25:25 -0700455 }
Charles Chanb7b4c932017-06-15 19:25:25 -0700456 }
457
458 /**
459 * Populate or revoke a routing rule on given deviceId that matches given ip,
460 * set destination mac to given mac, set vlan to given vlan and output to given port.
461 *
462 * @param deviceId device ID
463 * @param port port
464 * @param mac mac address
465 * @param vlanId VLAN ID
466 * @param ip IP address
467 * @param revoke true to revoke the rule; false to populate
468 */
469 private void processRoutingRule(DeviceId deviceId, PortNumber port, MacAddress mac,
470 VlanId vlanId, IpAddress ip, boolean revoke) {
471 ConnectPoint location = new ConnectPoint(deviceId, port);
Charles Chanf9a52702017-06-16 15:19:24 -0700472 if (!srManager.deviceConfiguration.inSameSubnet(location, ip)) {
473 log.info("{} is not included in the subnet config of {}/{}. Ignored.", ip, deviceId, port);
474 return;
Charles Chanb7b4c932017-06-15 19:25:25 -0700475 }
Charles Chanb7b4c932017-06-15 19:25:25 -0700476
Charles Chanf9a52702017-06-16 15:19:24 -0700477 log.info("{} routing rule for {} at {}", revoke ? "Revoking" : "Populating", ip, location);
478 if (revoke) {
Charles Chan2fde6d42017-08-23 14:46:43 -0700479 srManager.defaultRoutingHandler.revokeRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port);
Charles Chanf9a52702017-06-16 15:19:24 -0700480 } else {
Charles Chan2fde6d42017-08-23 14:46:43 -0700481 srManager.defaultRoutingHandler.populateRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port);
Charles Chan6ea94fc2016-05-10 17:29:47 -0700482 }
Charles Chan6ea94fc2016-05-10 17:29:47 -0700483 }
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700484
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700485 /**
486 * Populate or revoke a routing rule and egress rules on given deviceId that matches given IP,
487 * to set destination mac to given mac, set inner vlan and outer vlan to given vlans,
488 * set outer TPID, and output to given port.
489 *
490 * @param deviceId device ID
491 * @param port port
492 * @param mac mac address
493 * @param innerVlan inner VLAN ID
494 * @param outerVlan outer VLAN ID
495 * @param outerTpid outer TPID
496 * @param ip IP address
497 * @param revoke true to revoke the rule; false to populate
498 */
499 private void processDoubleTaggedRoutingRule(DeviceId deviceId, PortNumber port,
500 MacAddress mac, VlanId innerVlan,
501 VlanId outerVlan, EthType outerTpid,
502 IpAddress ip, boolean revoke) {
503 ConnectPoint location = new ConnectPoint(deviceId, port);
504 if (!srManager.deviceConfiguration.inSameSubnet(location, ip)) {
505 log.info("{} is not included in the subnet config of {}/{}. Ignored.", ip, deviceId, port);
506 return;
507 }
508 log.info("{} routing rule for double-tagged host {} at {}",
509 revoke ? "Revoking" : "Populating", ip, location);
510 if (revoke) {
511 srManager.defaultRoutingHandler.revokeDoubleTaggedRoute(
512 deviceId, ip.toIpPrefix(), mac, innerVlan, outerVlan, outerTpid, port);
513 } else {
514 srManager.defaultRoutingHandler.populateDoubleTaggedRoute(
515 deviceId, ip.toIpPrefix(), mac, innerVlan, outerVlan, outerTpid, port);
516 }
517 }
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700518
519 /**
520 * Update forwarding objective for unicast bridging and unicast routing.
521 * Also check the validity of updated interface configuration on VLAN.
522 *
523 * @param deviceId device ID that host attaches to
524 * @param portNum port number that host attaches to
525 * @param vlanId Vlan ID configured on the switch port
526 * @param popVlan true to pop Vlan tag at TrafficTreatment, false otherwise
527 * @param install true to populate the objective, false to revoke
528 */
529 void processIntfVlanUpdatedEvent(DeviceId deviceId, PortNumber portNum, VlanId vlanId,
530 boolean popVlan, boolean install) {
531 ConnectPoint connectPoint = new ConnectPoint(deviceId, portNum);
532 Set<Host> hosts = hostService.getConnectedHosts(connectPoint);
533
534 if (hosts == null || hosts.size() == 0) {
Charles Chan10b2fee2018-04-21 00:44:29 -0700535 log.debug("processIntfVlanUpdatedEvent: No hosts connected to {}", connectPoint);
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700536 return;
537 }
538
539 hosts.forEach(host -> {
540 MacAddress mac = host.mac();
541 VlanId hostVlanId = host.vlan();
542
543 // Check whether the host vlan is valid for new interface configuration
544 if ((!popVlan && hostVlanId.equals(vlanId)) ||
545 (popVlan && hostVlanId.equals(VlanId.NONE))) {
Charles Chan2ff1bac2018-03-29 16:03:41 -0700546 srManager.defaultRoutingHandler.updateBridging(deviceId, portNum, mac, vlanId, popVlan, install);
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700547 // Update Forwarding objective and corresponding simple Next objective
548 // for each host and IP address connected to given port
549 host.ipAddresses().forEach(ipAddress ->
Charles Chan2ff1bac2018-03-29 16:03:41 -0700550 srManager.defaultRoutingHandler.updateFwdObj(deviceId, portNum, ipAddress.toIpPrefix(),
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700551 mac, vlanId, popVlan, install)
552 );
553 }
554 });
555 }
556
557 /**
558 * Populate or revoke routing rule for each host, according to the updated
559 * subnet configuration on the interface.
560 * @param cp connect point of the updated interface
561 * @param ipPrefixSet IP Prefixes added or removed
562 * @param install true if IP Prefixes added, false otherwise
563 */
564 void processIntfIpUpdatedEvent(ConnectPoint cp, Set<IpPrefix> ipPrefixSet, boolean install) {
565 Set<Host> hosts = hostService.getConnectedHosts(cp);
566
567 if (hosts == null || hosts.size() == 0) {
Charles Chan10b2fee2018-04-21 00:44:29 -0700568 log.debug("processIntfIpUpdatedEvent: No hosts connected to {}", cp);
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700569 return;
570 }
571
572 // Check whether the host IP address is in the interface's subnet
573 hosts.forEach(host ->
574 host.ipAddresses().forEach(hostIpAddress -> {
575 ipPrefixSet.forEach(ipPrefix -> {
576 if (install && ipPrefix.contains(hostIpAddress)) {
Charles Chan2ff1bac2018-03-29 16:03:41 -0700577 srManager.defaultRoutingHandler.populateRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700578 host.mac(), host.vlan(), cp.port());
579 } else if (!install && ipPrefix.contains(hostIpAddress)) {
Charles Chan2ff1bac2018-03-29 16:03:41 -0700580 srManager.defaultRoutingHandler.revokeRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
Jonghwan Hyun42fe1052017-08-25 17:48:36 -0700581 host.mac(), host.vlan(), cp.port());
582 }
583 });
584 }));
585 }
Saurav Das45f48152018-01-18 12:07:33 -0800586
587 /**
588 * Returns the set of portnumbers on the given device that are part of the
589 * locations for dual-homed hosts.
590 *
591 * @param deviceId the given deviceId
592 * @return set of port numbers on given device that are dual-homed host
593 * locations. May be empty if no dual homed hosts are connected to
594 * the given device
595 */
596 Set<PortNumber> getDualHomedHostPorts(DeviceId deviceId) {
597 Set<PortNumber> dualHomedLocations = new HashSet<>();
598 srManager.hostService.getConnectedHosts(deviceId).stream()
599 .filter(host -> host.locations().size() == 2)
600 .forEach(host -> host.locations().stream()
601 .filter(loc -> loc.deviceId().equals(deviceId))
602 .forEach(loc -> dualHomedLocations.add(loc.port())));
603 return dualHomedLocations;
604 }
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700605
606 /**
607 * Checks if the given host is double-tagged or not.
608 *
609 * @param host host to check
610 * @return true if it is double-tagged, false otherwise
611 */
612 private boolean isDoubleTaggedHost(Host host) {
613 return !host.innerVlan().equals(VlanId.NONE);
614 }
615
Charles Chand2990362016-04-18 13:44:03 -0700616}