blob: 7dbbbeb5807c9760dd4bd2aca44dac4450a6f9ea [file] [log] [blame]
Charles Chandebfea32016-10-24 14:52:01 -07001/*
Brian O'Connor0947d7e2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Charles Chandebfea32016-10-24 14:52:01 -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
Saurav Das261c3002017-06-13 15:35:54 -070019import com.google.common.collect.Sets;
20
Charles Chandebfea32016-10-24 14:52:01 -070021import org.onlab.packet.IpPrefix;
22import org.onlab.packet.MacAddress;
Charles Chan90772a72017-02-08 15:52:08 -080023import org.onlab.packet.VlanId;
Charles Chanf0ae41e2017-08-23 13:55:39 -070024import org.onosproject.net.ConnectPoint;
Charles Chan910be6a2017-08-23 14:46:43 -070025import org.onosproject.net.HostLocation;
26import org.onosproject.net.PortNumber;
27import org.onosproject.net.host.HostEvent;
Ray Milkeya8154312017-08-08 13:00:43 -070028import org.onosproject.routeservice.ResolvedRoute;
29import org.onosproject.routeservice.RouteEvent;
Charles Chandebfea32016-10-24 14:52:01 -070030import org.onosproject.net.DeviceId;
Charles Chan910be6a2017-08-23 14:46:43 -070031import org.onosproject.routeservice.RouteInfo;
Charles Chandebfea32016-10-24 14:52:01 -070032import org.slf4j.Logger;
33import org.slf4j.LoggerFactory;
34
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +000035import java.util.HashMap;
36import java.util.Map;
Charles Chan910be6a2017-08-23 14:46:43 -070037import java.util.Collection;
Charles Chanee8dbf82017-06-22 14:15:05 -070038import java.util.Objects;
Charles Chan910be6a2017-08-23 14:46:43 -070039import java.util.Optional;
40import java.util.Set;
Charles Chan910be6a2017-08-23 14:46:43 -070041import java.util.stream.Collectors;
Charles Chanee8dbf82017-06-22 14:15:05 -070042
Charles Chandebfea32016-10-24 14:52:01 -070043/**
44 * Handles RouteEvent and manages routing entries.
45 */
46public class RouteHandler {
47 private static final Logger log = LoggerFactory.getLogger(RouteHandler.class);
48 private final SegmentRoutingManager srManager;
49
Charles Chanf0ae41e2017-08-23 13:55:39 -070050 RouteHandler(SegmentRoutingManager srManager) {
Charles Chandebfea32016-10-24 14:52:01 -070051 this.srManager = srManager;
52 }
53
54 protected void init(DeviceId deviceId) {
Charles Chan7d20a4e2018-04-13 14:01:49 -040055 Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(deviceId);
56
57 srManager.routeService.getRouteTables().stream()
58 .map(srManager.routeService::getRoutes)
59 .flatMap(Collection::stream)
60 .map(RouteInfo::allRoutes)
61 .filter(allRoutes -> allRoutes.stream().allMatch(resolvedRoute ->
62 srManager.nextHopLocations(resolvedRoute).stream().allMatch(cp ->
63 deviceId.equals(cp.deviceId()) ||
64 (pairDeviceId.isPresent() && pairDeviceId.get().equals(cp.deviceId()))
65 )))
66 .forEach(this::processRouteAddedInternal);
Charles Chandebfea32016-10-24 14:52:01 -070067 }
68
Charles Chanf0ae41e2017-08-23 13:55:39 -070069 void processRouteAdded(RouteEvent event) {
Charles Chanfbcb8812018-04-18 18:41:05 -070070 processRouteAddedInternal(event.alternatives());
Charles Chandebfea32016-10-24 14:52:01 -070071 }
72
Charles Chan910be6a2017-08-23 14:46:43 -070073 private void processRouteAddedInternal(Collection<ResolvedRoute> routes) {
Charles Chanee8dbf82017-06-22 14:15:05 -070074 if (!isReady()) {
Charles Chan910be6a2017-08-23 14:46:43 -070075 log.info("System is not ready. Skip adding route for {}", routes);
Charles Chanee8dbf82017-06-22 14:15:05 -070076 return;
77 }
78
Charles Chan910be6a2017-08-23 14:46:43 -070079 log.info("processRouteAddedInternal. routes={}", routes);
Charles Chanf0ae41e2017-08-23 13:55:39 -070080
Charles Chan482b6422018-04-09 11:52:08 -040081 if (routes.size() > 2) {
82 log.info("Route {} has more than two next hops. Do not process route change", routes);
83 return;
84 }
85
Charles Chan910be6a2017-08-23 14:46:43 -070086 Set<ConnectPoint> allLocations = Sets.newHashSet();
87 Set<IpPrefix> allPrefixes = Sets.newHashSet();
88 routes.forEach(route -> {
89 allLocations.addAll(srManager.nextHopLocations(route));
90 allPrefixes.add(route.prefix());
91 });
92 log.debug("RouteAdded. populateSubnet {}, {}", allLocations, allPrefixes);
93 srManager.defaultRoutingHandler.populateSubnet(allLocations, allPrefixes);
Charles Chandebfea32016-10-24 14:52:01 -070094
Charles Chan910be6a2017-08-23 14:46:43 -070095 routes.forEach(route -> {
96 IpPrefix prefix = route.prefix();
97 MacAddress nextHopMac = route.nextHopMac();
98 VlanId nextHopVlan = route.nextHopVlan();
99 Set<ConnectPoint> locations = srManager.nextHopLocations(route);
100
101 locations.forEach(location -> {
102 log.debug("RouteAdded. addSubnet {}, {}", location, prefix);
103 srManager.deviceConfiguration.addSubnet(location, prefix);
104 log.debug("RouteAdded populateRoute {}, {}, {}, {}", location, prefix, nextHopMac, nextHopVlan);
105 srManager.defaultRoutingHandler.populateRoute(location.deviceId(), prefix,
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000106 nextHopMac, nextHopVlan, location.port(), false);
Charles Chan910be6a2017-08-23 14:46:43 -0700107 });
108 });
Charles Chandebfea32016-10-24 14:52:01 -0700109 }
110
Charles Chanf0ae41e2017-08-23 13:55:39 -0700111 void processRouteUpdated(RouteEvent event) {
Charles Chanfbcb8812018-04-18 18:41:05 -0700112 processRouteUpdatedInternal(Sets.newHashSet(event.alternatives()),
113 Sets.newHashSet(event.prevAlternatives()));
Charles Chan910be6a2017-08-23 14:46:43 -0700114 }
115
116 void processAlternativeRoutesChanged(RouteEvent event) {
Charles Chanfbcb8812018-04-18 18:41:05 -0700117 processRouteUpdatedInternal(Sets.newHashSet(event.alternatives()),
118 Sets.newHashSet(event.prevAlternatives()));
Charles Chan910be6a2017-08-23 14:46:43 -0700119 }
120
121 private void processRouteUpdatedInternal(Set<ResolvedRoute> routes, Set<ResolvedRoute> oldRoutes) {
122 if (!isReady()) {
123 log.info("System is not ready. Skip updating route for {} -> {}", oldRoutes, routes);
124 return;
125 }
126
127 log.info("processRouteUpdatedInternal. routes={}, oldRoutes={}", routes, oldRoutes);
128
Charles Chan482b6422018-04-09 11:52:08 -0400129 if (routes.size() > 2) {
130 log.info("Route {} has more than two next hops. Do not process route change", routes);
131 return;
132 }
133
Charles Chan910be6a2017-08-23 14:46:43 -0700134 Set<ConnectPoint> allLocations = Sets.newHashSet();
135 Set<IpPrefix> allPrefixes = Sets.newHashSet();
136 routes.forEach(route -> {
137 allLocations.addAll(srManager.nextHopLocations(route));
138 allPrefixes.add(route.prefix());
139 });
Charles Chan482b6422018-04-09 11:52:08 -0400140
141 // Just come back from an invalid next hop count
142 // Revoke subnet from all locations and reset oldRoutes such that system will be reprogrammed from scratch
143 if (oldRoutes.size() > 2) {
144 log.info("Revoke subnet {} and reset oldRoutes");
145 srManager.defaultRoutingHandler.revokeSubnet(allPrefixes);
146 oldRoutes = Sets.newHashSet();
147 }
148
Charles Chan910be6a2017-08-23 14:46:43 -0700149 log.debug("RouteUpdated. populateSubnet {}, {}", allLocations, allPrefixes);
150 srManager.defaultRoutingHandler.populateSubnet(allLocations, allPrefixes);
151
Charles Chan910be6a2017-08-23 14:46:43 -0700152 Set<ResolvedRoute> toBeRemoved = Sets.difference(oldRoutes, routes).immutableCopy();
153 Set<ResolvedRoute> toBeAdded = Sets.difference(routes, oldRoutes).immutableCopy();
154
155 toBeRemoved.forEach(route -> {
156 srManager.nextHopLocations(route).forEach(oldLocation -> {
Charles Chan06f626c2018-02-05 17:20:05 -0800157 if (toBeAdded.stream().map(srManager::nextHopLocations)
158 .flatMap(Set::stream).map(ConnectPoint::deviceId)
159 .noneMatch(deviceId -> deviceId.equals(oldLocation.deviceId()))) {
160 IpPrefix prefix = route.prefix();
161 log.debug("RouteUpdated. removeSubnet {}, {}", oldLocation, prefix);
162 srManager.deviceConfiguration.removeSubnet(oldLocation, prefix);
163 // We don't remove the flow on the old location in occasion of two next hops becoming one
164 // since the populateSubnet will point the old location to the new location via spine.
165 }
Charles Chan910be6a2017-08-23 14:46:43 -0700166 });
167 });
168
169 toBeAdded.forEach(route -> {
170 IpPrefix prefix = route.prefix();
171 MacAddress nextHopMac = route.nextHopMac();
172 VlanId nextHopVlan = route.nextHopVlan();
173 Set<ConnectPoint> locations = srManager.nextHopLocations(route);
174
175 locations.forEach(location -> {
176 log.debug("RouteUpdated. addSubnet {}, {}", location, prefix);
177 srManager.deviceConfiguration.addSubnet(location, prefix);
178 log.debug("RouteUpdated. populateRoute {}, {}, {}, {}", location, prefix, nextHopMac, nextHopVlan);
179 srManager.defaultRoutingHandler.populateRoute(location.deviceId(), prefix,
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000180 nextHopMac, nextHopVlan, location.port(), false);
Charles Chan910be6a2017-08-23 14:46:43 -0700181 });
182 });
Charles Chandebfea32016-10-24 14:52:01 -0700183 }
184
Charles Chanf0ae41e2017-08-23 13:55:39 -0700185 void processRouteRemoved(RouteEvent event) {
Charles Chanfbcb8812018-04-18 18:41:05 -0700186 processRouteRemovedInternal(event.alternatives());
Charles Chandebfea32016-10-24 14:52:01 -0700187 }
188
Charles Chan910be6a2017-08-23 14:46:43 -0700189 private void processRouteRemovedInternal(Collection<ResolvedRoute> routes) {
Charles Chanee8dbf82017-06-22 14:15:05 -0700190 if (!isReady()) {
Charles Chan910be6a2017-08-23 14:46:43 -0700191 log.info("System is not ready. Skip removing route for {}", routes);
Charles Chanee8dbf82017-06-22 14:15:05 -0700192 return;
193 }
194
Charles Chan910be6a2017-08-23 14:46:43 -0700195 log.info("processRouteRemovedInternal. routes={}", routes);
Charles Chanf0ae41e2017-08-23 13:55:39 -0700196
Charles Chan910be6a2017-08-23 14:46:43 -0700197 Set<IpPrefix> allPrefixes = Sets.newHashSet();
198 routes.forEach(route -> {
199 allPrefixes.add(route.prefix());
200 });
201 log.debug("RouteRemoved. revokeSubnet {}", allPrefixes);
202 srManager.defaultRoutingHandler.revokeSubnet(allPrefixes);
Charles Chandebfea32016-10-24 14:52:01 -0700203
Charles Chan910be6a2017-08-23 14:46:43 -0700204 routes.forEach(route -> {
205 IpPrefix prefix = route.prefix();
206 MacAddress nextHopMac = route.nextHopMac();
207 VlanId nextHopVlan = route.nextHopVlan();
208 Set<ConnectPoint> locations = srManager.nextHopLocations(route);
209
210 locations.forEach(location -> {
211 log.debug("RouteRemoved. removeSubnet {}, {}", location, prefix);
212 srManager.deviceConfiguration.removeSubnet(location, prefix);
Charles Chanc4d68882018-03-15 16:41:10 -0700213 // We don't need to call revokeRoute again since revokeSubnet will remove the prefix
214 // from all devices, including the ones that next hop attaches to.
Charles Chan910be6a2017-08-23 14:46:43 -0700215
216 // Also remove redirection flows on the pair device if exists.
217 Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(location.deviceId());
Saurav Dasec683dc2018-04-27 18:42:30 -0700218 Optional<PortNumber> pairLocalPort = srManager.getPairLocalPort(location.deviceId());
Charles Chan910be6a2017-08-23 14:46:43 -0700219 if (pairDeviceId.isPresent() && pairLocalPort.isPresent()) {
220 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
221 // when the host is untagged
222 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(nextHopVlan);
223
224 log.debug("RouteRemoved. revokeRoute {}, {}, {}, {}", location, prefix, nextHopMac, nextHopVlan);
225 srManager.defaultRoutingHandler.revokeRoute(pairDeviceId.get(), prefix,
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000226 nextHopMac, vlanId, pairLocalPort.get(), false);
Charles Chan910be6a2017-08-23 14:46:43 -0700227 }
228 });
229 });
230 }
231
232 void processHostMovedEvent(HostEvent event) {
233 log.info("processHostMovedEvent {}", event);
234 MacAddress hostMac = event.subject().mac();
235 VlanId hostVlanId = event.subject().vlan();
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000236 // map of nextId for prev port in each device
237 Map<DeviceId, Integer> nextIdMap = new HashMap<>();
Charles Chan910be6a2017-08-23 14:46:43 -0700238
239 affectedRoutes(hostMac, hostVlanId).forEach(affectedRoute -> {
240 IpPrefix prefix = affectedRoute.prefix();
241 Set<HostLocation> prevLocations = event.prevSubject().locations();
242 Set<HostLocation> newLocations = event.subject().locations();
243
Charles Chan07203792018-07-17 16:33:11 -0700244 Set<ConnectPoint> connectPoints = newLocations.stream()
245 .map(l -> (ConnectPoint) l).collect(Collectors.toSet());
246 log.debug("HostMoved. populateSubnet {}, {}", newLocations, prefix);
247 srManager.defaultRoutingHandler.populateSubnet(connectPoints, Sets.newHashSet(prefix));
248
Charles Chanfbcb8812018-04-18 18:41:05 -0700249 Set<DeviceId> newDeviceIds = newLocations.stream().map(HostLocation::deviceId)
250 .collect(Collectors.toSet());
251
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000252 // Set of deviceIDs of the previous locations where the host was connected
253 // Used to determine if host moved to different connect points
254 // on same device or moved to a different device altogether
255 Set<DeviceId> oldDeviceIds = prevLocations.stream().map(HostLocation::deviceId)
256 .collect(Collectors.toSet());
257
Charles Chan910be6a2017-08-23 14:46:43 -0700258 // For each old location
Charles Chand66d6712018-03-29 16:03:41 -0700259 Sets.difference(prevLocations, newLocations).forEach(prevLocation -> {
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000260 //find next Id for each old port, add to map
261 int nextId = srManager.getNextIdForHostPort(prevLocation.deviceId(), hostMac,
262 hostVlanId, prevLocation.port());
263 log.debug("HostMoved. NextId For Host {}, {}, {}, {} {}", prevLocation, prefix,
264 hostMac, hostVlanId, nextId);
265
266 nextIdMap.put(prevLocation.deviceId(), nextId);
267
Charles Chanfbcb8812018-04-18 18:41:05 -0700268 // Remove flows for unchanged IPs only when the host moves from a switch to another.
269 // Otherwise, do not remove and let the adding part update the old flow
Charles Chan4dcd5102019-02-28 15:40:57 -0800270 if (newDeviceIds.contains(prevLocation.deviceId())) {
271 return;
Charles Chanfbcb8812018-04-18 18:41:05 -0700272 }
Charles Chan4dcd5102019-02-28 15:40:57 -0800273
274 log.debug("HostMoved. removeSubnet {}, {}", prevLocation, prefix);
275 srManager.deviceConfiguration.removeSubnet(prevLocation, prefix);
276
277 // Do not remove flow from a device if the route is still reachable via its pair device.
278 // populateSubnet will update the flow to point to its pair device via spine.
279 DeviceId pairDeviceId = srManager.getPairDeviceId(prevLocation.deviceId()).orElse(null);
280 if (newLocations.stream().anyMatch(n -> n.deviceId().equals(pairDeviceId))) {
281 return;
282 }
283
284 log.debug("HostMoved. revokeRoute {}, {}, {}, {}", prevLocation, prefix, hostMac, hostVlanId);
285 srManager.defaultRoutingHandler.revokeRoute(prevLocation.deviceId(), prefix,
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000286 hostMac, hostVlanId, prevLocation.port(), false);
Charles Chan910be6a2017-08-23 14:46:43 -0700287 });
288
289 // For each new location, add all new IPs.
Charles Chand66d6712018-03-29 16:03:41 -0700290 Sets.difference(newLocations, prevLocations).forEach(newLocation -> {
Charles Chan07203792018-07-17 16:33:11 -0700291 log.debug("HostMoved. addSubnet {}, {}", newLocation, prefix);
292 srManager.deviceConfiguration.addSubnet(newLocation, prefix);
293
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000294 //its a new connect point, not a move from an existing device, populateRoute
295 if (!oldDeviceIds.contains(newLocation.deviceId())) {
296 log.debug("HostMoved. populateRoute {}, {}, {}, {}", newLocation, prefix, hostMac, hostVlanId);
297 srManager.defaultRoutingHandler.populateRoute(newLocation.deviceId(), prefix,
298 hostMac, hostVlanId, newLocation.port(), false);
299 } else {
300 // update same flow to point to new nextObj
301 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(newLocation)).orElse(hostVlanId);
302 //use nextIdMap to send new port details to update the nextId group bucket
303 log.debug("HostMoved. update L3 Ucast Group Bucket {}, {}, {} --> {}",
304 newLocation, hostMac, vlanId, nextIdMap.get(newLocation.deviceId()));
305 srManager.updateMacVlanTreatment(newLocation.deviceId(), hostMac, vlanId,
306 newLocation.port(), nextIdMap.get(newLocation.deviceId()));
307 }
Charles Chan910be6a2017-08-23 14:46:43 -0700308 });
309
310 });
311 }
312
313 private Set<ResolvedRoute> affectedRoutes(MacAddress mac, VlanId vlanId) {
314 return srManager.routeService.getRouteTables().stream()
315 .map(routeTableId -> srManager.routeService.getRoutes(routeTableId))
316 .flatMap(Collection::stream)
317 .map(RouteInfo::allRoutes)
318 .flatMap(Collection::stream)
319 .filter(resolvedRoute -> mac.equals(resolvedRoute.nextHopMac()) &&
320 vlanId.equals(resolvedRoute.nextHopVlan())).collect(Collectors.toSet());
Charles Chandebfea32016-10-24 14:52:01 -0700321 }
Charles Chanee8dbf82017-06-22 14:15:05 -0700322
323 private boolean isReady() {
324 return Objects.nonNull(srManager.deviceConfiguration) &&
Charles Chan910be6a2017-08-23 14:46:43 -0700325 Objects.nonNull(srManager.defaultRoutingHandler);
326 }
Charles Chandebfea32016-10-24 14:52:01 -0700327}