blob: 0fa2acacf77bf7e106e26886e281d91c2d2a6cff [file] [log] [blame]
sanghob35a6192015-04-01 13:05:26 -07001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
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 */
16package org.onosproject.segmentrouting;
17
sangho20eff1d2015-04-13 15:15:58 -070018import com.google.common.collect.Maps;
19import com.google.common.collect.Sets;
sangho666cd6d2015-04-14 16:27:13 -070020import org.onlab.packet.Ip4Address;
Srikanth Vavilapalli4db76e32015-04-07 15:12:32 -070021import org.onlab.packet.Ip4Prefix;
sanghob35a6192015-04-01 13:05:26 -070022import org.onlab.packet.IpPrefix;
23import org.onosproject.net.Device;
24import org.onosproject.net.DeviceId;
sangho20eff1d2015-04-13 15:15:58 -070025import org.onosproject.net.Link;
sanghob35a6192015-04-01 13:05:26 -070026import org.onosproject.net.MastershipRole;
sanghob35a6192015-04-01 13:05:26 -070027import org.slf4j.Logger;
28import org.slf4j.LoggerFactory;
29
30import java.util.ArrayList;
31import java.util.HashMap;
32import java.util.HashSet;
Srikanth Vavilapalli4db76e32015-04-07 15:12:32 -070033import java.util.List;
sanghob35a6192015-04-01 13:05:26 -070034import java.util.Set;
35
36import static com.google.common.base.Preconditions.checkNotNull;
37
38public class DefaultRoutingHandler {
39
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070040 private static Logger log = LoggerFactory
41 .getLogger(DefaultRoutingHandler.class);
sanghob35a6192015-04-01 13:05:26 -070042
43 private SegmentRoutingManager srManager;
44 private RoutingRulePopulator rulePopulator;
sangho20eff1d2015-04-13 15:15:58 -070045 private HashMap<DeviceId, ECMPShortestPathGraph> currentEcmpSpgMap;
sangho45b009c2015-05-07 13:30:57 -070046 private HashMap<DeviceId, ECMPShortestPathGraph> updatedEcmpSpgMap;
sangho666cd6d2015-04-14 16:27:13 -070047 private DeviceConfiguration config;
sanghob35a6192015-04-01 13:05:26 -070048 private Status populationStatus;
49
50 /**
51 * Represents the default routing population status.
52 */
53 public enum Status {
54 // population process is not started yet.
55 IDLE,
56
57 // population process started.
58 STARTED,
59
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070060 // population process was aborted due to errors, mostly for groups not
61 // found.
sanghob35a6192015-04-01 13:05:26 -070062 ABORTED,
63
64 // population process was finished successfully.
65 SUCCEEDED
66 }
67
68 /**
69 * Creates a DefaultRoutingHandler object.
70 *
71 * @param srManager SegmentRoutingManager object
72 */
73 public DefaultRoutingHandler(SegmentRoutingManager srManager) {
74 this.srManager = srManager;
75 this.rulePopulator = checkNotNull(srManager.routingRulePopulator);
sangho666cd6d2015-04-14 16:27:13 -070076 this.config = checkNotNull(srManager.deviceConfiguration);
sanghob35a6192015-04-01 13:05:26 -070077 this.populationStatus = Status.IDLE;
sangho20eff1d2015-04-13 15:15:58 -070078 this.currentEcmpSpgMap = Maps.newHashMap();
sanghob35a6192015-04-01 13:05:26 -070079 }
80
81 /**
82 * Populates all routing rules to all connected routers, including default
83 * routing rules, adjacency rules, and policy rules if any.
84 *
85 * @return true if it succeeds in populating all rules, otherwise false
86 */
87 public boolean populateAllRoutingRules() {
88
89 populationStatus = Status.STARTED;
sangho20eff1d2015-04-13 15:15:58 -070090 rulePopulator.resetCounter();
sanghob35a6192015-04-01 13:05:26 -070091 log.info("Starts to populate routing rules");
92
93 for (Device sw : srManager.deviceService.getDevices()) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070094 if (srManager.mastershipService.getLocalRole(sw.id()) != MastershipRole.MASTER) {
sanghob35a6192015-04-01 13:05:26 -070095 continue;
96 }
97
sangho20eff1d2015-04-13 15:15:58 -070098 ECMPShortestPathGraph ecmpSpg = new ECMPShortestPathGraph(sw.id(), srManager);
99 if (!populateEcmpRoutingRules(sw.id(), ecmpSpg)) {
sanghob35a6192015-04-01 13:05:26 -0700100 populationStatus = Status.ABORTED;
101 log.debug("Abort routing rule population");
102 return false;
103 }
sangho20eff1d2015-04-13 15:15:58 -0700104 currentEcmpSpgMap.put(sw.id(), ecmpSpg);
sanghob35a6192015-04-01 13:05:26 -0700105
106 // TODO: Set adjacency routing rule for all switches
107 }
108
109 populationStatus = Status.SUCCEEDED;
sangho20eff1d2015-04-13 15:15:58 -0700110 log.info("Completes routing rule population. Total # of rules pushed : {}",
111 rulePopulator.getCounter());
sanghob35a6192015-04-01 13:05:26 -0700112 return true;
113 }
114
sangho20eff1d2015-04-13 15:15:58 -0700115 /**
116 * Populates the routing rules according to the route changes due to the link
117 * failure or link add. It computes the routes changed due to the link changes and
118 * repopulates the rules only for the routes.
119 *
120 * @param linkFail link failed, null for link added
121 * @return true if it succeeds to populate all rules, false otherwise
122 */
123 public boolean populateRoutingRulesForLinkStatusChange(Link linkFail) {
124
125 synchronized (populationStatus) {
126
127 if (populationStatus == Status.STARTED) {
sangho52abe3a2015-05-05 14:13:34 -0700128 log.warn("Previous rule population is not finished.");
sangho20eff1d2015-04-13 15:15:58 -0700129 return true;
130 }
131
sangho45b009c2015-05-07 13:30:57 -0700132 // Take the snapshots of the links
133 updatedEcmpSpgMap = new HashMap<>();
134 for (Device sw : srManager.deviceService.getDevices()) {
135 if (srManager.mastershipService.
136 getLocalRole(sw.id()) != MastershipRole.MASTER) {
137 continue;
138 }
139 ECMPShortestPathGraph ecmpSpgUpdated =
140 new ECMPShortestPathGraph(sw.id(), srManager);
141 updatedEcmpSpgMap.put(sw.id(), ecmpSpgUpdated);
142 }
143
sangho52abe3a2015-05-05 14:13:34 -0700144 log.info("Starts rule population from link change");
145
sangho20eff1d2015-04-13 15:15:58 -0700146 Set<ArrayList<DeviceId>> routeChanges;
147 populationStatus = Status.STARTED;
148 if (linkFail == null) {
149 // Compare all routes of existing ECMP SPG with the new ones
150 routeChanges = computeRouteChange();
151 } else {
152 // Compare existing ECMP SPG only with the link removed
153 routeChanges = computeDamagedRoutes(linkFail);
154 }
155
156 if (routeChanges.isEmpty()) {
sangho52abe3a2015-05-05 14:13:34 -0700157 log.info("No route changes for the link status change");
sangho20eff1d2015-04-13 15:15:58 -0700158 populationStatus = Status.SUCCEEDED;
159 return true;
160 }
161
162 if (repopulateRoutingRulesForRoutes(routeChanges)) {
163 populationStatus = Status.SUCCEEDED;
164 log.info("Complete to repopulate the rules. # of rules populated : {}",
165 rulePopulator.getCounter());
166 return true;
167 } else {
168 populationStatus = Status.ABORTED;
169 log.warn("Failed to repopulate the rules.");
170 return false;
171 }
172 }
173 }
174
175 private boolean repopulateRoutingRulesForRoutes(Set<ArrayList<DeviceId>> routes) {
176 rulePopulator.resetCounter();
177 for (ArrayList<DeviceId> link: routes) {
sangho834e4b02015-05-01 09:38:25 -0700178 // When only the source device is defined, reinstall routes to all other devices
sangho20eff1d2015-04-13 15:15:58 -0700179 if (link.size() == 1) {
180 ECMPShortestPathGraph ecmpSpg = new ECMPShortestPathGraph(link.get(0), srManager);
181 if (populateEcmpRoutingRules(link.get(0), ecmpSpg)) {
182 currentEcmpSpgMap.put(link.get(0), ecmpSpg);
sangho52abe3a2015-05-05 14:13:34 -0700183 } else {
sangho45b009c2015-05-07 13:30:57 -0700184 log.warn("Failed to populate the flow rules from {} to all", link.get(0));
sangho52abe3a2015-05-05 14:13:34 -0700185 return false;
sangho20eff1d2015-04-13 15:15:58 -0700186 }
sangho45b009c2015-05-07 13:30:57 -0700187 } else {
188 DeviceId src = link.get(0);
189 DeviceId dst = link.get(1);
190 log.trace("repopulateRoutingRulesForRoutes: running ECMP graph "
191 + "for device {}", dst);
192 ECMPShortestPathGraph ecmpSpg = updatedEcmpSpgMap.get(dst);
193 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
194 ecmpSpg.getAllLearnedSwitchesAndVia();
195 for (Integer itrIdx : switchVia.keySet()) {
196 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
197 switchVia.get(itrIdx);
198 for (DeviceId targetSw : swViaMap.keySet()) {
199 if (!targetSw.equals(src)) {
200 continue;
201 }
202 Set<DeviceId> nextHops = new HashSet<>();
203 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
204 if (via.isEmpty()) {
205 nextHops.add(dst);
206 } else {
207 nextHops.add(via.get(0));
208 }
209 }
210 if (!populateEcmpRoutingRulePartial(targetSw, dst, nextHops)) {
211 return false;
sangho20eff1d2015-04-13 15:15:58 -0700212 }
213 }
sangho20eff1d2015-04-13 15:15:58 -0700214 }
sangho45b009c2015-05-07 13:30:57 -0700215 currentEcmpSpgMap.put(dst, ecmpSpg);
sangho20eff1d2015-04-13 15:15:58 -0700216 }
217 }
218 return true;
219 }
220
221 private Set<ArrayList<DeviceId>> computeDamagedRoutes(Link linkFail) {
222
223 Set<ArrayList<DeviceId>> routes = new HashSet<>();
224
225 for (Device sw : srManager.deviceService.getDevices()) {
226 if (srManager.mastershipService.
227 getLocalRole(sw.id()) != MastershipRole.MASTER) {
228 continue;
229 }
230 ECMPShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(sw.id());
231 if (ecmpSpg == null) {
232 log.error("No existing ECMP path for switch {}", sw.id());
233 continue;
234 }
235 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
236 ecmpSpg.getAllLearnedSwitchesAndVia();
237 for (Integer itrIdx : switchVia.keySet()) {
238 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
239 switchVia.get(itrIdx);
240 for (DeviceId targetSw : swViaMap.keySet()) {
241 DeviceId destSw = sw.id();
242 Set<ArrayList<DeviceId>> subLinks =
243 computeLinks(targetSw, destSw, swViaMap);
244 for (ArrayList<DeviceId> alink: subLinks) {
245 if (alink.get(0).equals(linkFail.src().deviceId()) &&
246 alink.get(1).equals(linkFail.dst().deviceId())) {
247 ArrayList<DeviceId> aRoute = new ArrayList<>();
248 aRoute.add(targetSw);
249 aRoute.add(destSw);
250 routes.add(aRoute);
251 break;
252 }
253 }
254 }
255 }
sangho45b009c2015-05-07 13:30:57 -0700256
sangho20eff1d2015-04-13 15:15:58 -0700257 }
258
259 return routes;
260 }
261
262 private Set<ArrayList<DeviceId>> computeRouteChange() {
263
264 Set<ArrayList<DeviceId>> routes = new HashSet<>();
265
266 for (Device sw : srManager.deviceService.getDevices()) {
267 if (srManager.mastershipService.
268 getLocalRole(sw.id()) != MastershipRole.MASTER) {
sangho45b009c2015-05-07 13:30:57 -0700269 log.warn("No mastership for {} and skip route optimization");
sangho20eff1d2015-04-13 15:15:58 -0700270 continue;
271 }
sangho45b009c2015-05-07 13:30:57 -0700272
273 log.trace("link of {} - ", sw.id());
274 for (Link link: srManager.linkService.getDeviceLinks(sw.id())) {
275 log.trace("{} -> {} ", link.src().deviceId(), link.dst().deviceId());
276 }
277
278 log.debug("Checking route change for switch {}", sw.id());
sangho20eff1d2015-04-13 15:15:58 -0700279 ECMPShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(sw.id());
280 if (ecmpSpg == null) {
281 log.debug("No existing ECMP path for Switch {}", sw.id());
282 ArrayList<DeviceId> route = new ArrayList<>();
283 route.add(sw.id());
284 routes.add(route);
285 continue;
286 }
sangho45b009c2015-05-07 13:30:57 -0700287 log.debug("computeRouteChange: running ECMP graph "
288 + "for device {}", sw.id());
289 ECMPShortestPathGraph newEcmpSpg = updatedEcmpSpgMap.get(sw.id());
290 currentEcmpSpgMap.put(sw.id(), newEcmpSpg);
sangho20eff1d2015-04-13 15:15:58 -0700291 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
292 ecmpSpg.getAllLearnedSwitchesAndVia();
293 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchViaUpdated =
294 newEcmpSpg.getAllLearnedSwitchesAndVia();
295
sangho45b009c2015-05-07 13:30:57 -0700296 for (Integer itrIdx : switchViaUpdated.keySet()) {
297 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMapUpdated =
298 switchViaUpdated.get(itrIdx);
299 for (DeviceId srcSw : swViaMapUpdated.keySet()) {
300 ArrayList<ArrayList<DeviceId>> viaUpdated = swViaMapUpdated.get(srcSw);
301 ArrayList<ArrayList<DeviceId>> via = getVia(switchVia, srcSw);
302 if (via.isEmpty() || !viaUpdated.equals(via)) {
sangho20eff1d2015-04-13 15:15:58 -0700303 ArrayList<DeviceId> route = new ArrayList<>();
304 route.add(srcSw);
305 route.add(sw.id());
306 routes.add(route);
307 }
308 }
309 }
sangho45b009c2015-05-07 13:30:57 -0700310 }
sangho20eff1d2015-04-13 15:15:58 -0700311
sangho45b009c2015-05-07 13:30:57 -0700312 for (ArrayList<DeviceId> link: routes) {
313 log.trace("Link changes - ");
314 if (link.size() == 1) {
315 log.trace(" : {} - all", link.get(0));
316 } else {
317 log.trace(" : {} - {}", link.get(0), link.get(1));
318 }
sangho20eff1d2015-04-13 15:15:58 -0700319 }
320
321 return routes;
322 }
323
324 private ArrayList<ArrayList<DeviceId>> getVia(HashMap<Integer, HashMap<DeviceId,
325 ArrayList<ArrayList<DeviceId>>>> switchVia, DeviceId srcSw) {
326 for (Integer itrIdx : switchVia.keySet()) {
327 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
328 switchVia.get(itrIdx);
329 if (swViaMap.get(srcSw) == null) {
330 continue;
331 } else {
332 return swViaMap.get(srcSw);
333 }
334 }
335
336 return new ArrayList<>();
337 }
338
339 private Set<ArrayList<DeviceId>> computeLinks(DeviceId src,
340 DeviceId dst,
341 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> viaMap) {
342 Set<ArrayList<DeviceId>> subLinks = Sets.newHashSet();
343 for (ArrayList<DeviceId> via : viaMap.get(src)) {
344 DeviceId linkSrc = src;
345 DeviceId linkDst = dst;
346 for (DeviceId viaDevice: via) {
347 ArrayList<DeviceId> link = new ArrayList<>();
348 linkDst = viaDevice;
349 link.add(linkSrc);
350 link.add(linkDst);
351 subLinks.add(link);
352 linkSrc = viaDevice;
353 }
354 ArrayList<DeviceId> link = new ArrayList<>();
355 link.add(linkSrc);
356 link.add(dst);
357 subLinks.add(link);
358 }
359
360 return subLinks;
361 }
362
363 private boolean populateEcmpRoutingRules(DeviceId destSw,
sanghob35a6192015-04-01 13:05:26 -0700364 ECMPShortestPathGraph ecmpSPG) {
365
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700366 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia = ecmpSPG
367 .getAllLearnedSwitchesAndVia();
sanghob35a6192015-04-01 13:05:26 -0700368 for (Integer itrIdx : switchVia.keySet()) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700369 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap = switchVia
370 .get(itrIdx);
sanghob35a6192015-04-01 13:05:26 -0700371 for (DeviceId targetSw : swViaMap.keySet()) {
sanghob35a6192015-04-01 13:05:26 -0700372 Set<DeviceId> nextHops = new HashSet<>();
373
374 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
375 if (via.isEmpty()) {
376 nextHops.add(destSw);
377 } else {
378 nextHops.add(via.get(0));
379 }
380 }
381 if (!populateEcmpRoutingRulePartial(targetSw, destSw, nextHops)) {
382 return false;
383 }
384 }
385 }
386
387 return true;
388 }
389
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700390 private boolean populateEcmpRoutingRulePartial(DeviceId targetSw,
391 DeviceId destSw,
392 Set<DeviceId> nextHops) {
sanghob35a6192015-04-01 13:05:26 -0700393 boolean result;
394
395 if (nextHops.isEmpty()) {
396 nextHops.add(destSw);
397 }
398
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700399 // If both target switch and dest switch are edge routers, then set IP
sangho52abe3a2015-05-05 14:13:34 -0700400 // rule for both subnet and router IP.
sangho666cd6d2015-04-14 16:27:13 -0700401 if (config.isEdgeDevice(targetSw) && config.isEdgeDevice(destSw)) {
402 List<Ip4Prefix> subnets = config.getSubnets(destSw);
Srikanth Vavilapalli4db76e32015-04-07 15:12:32 -0700403 result = rulePopulator.populateIpRuleForSubnet(targetSw,
404 subnets,
405 destSw,
406 nextHops);
sanghob35a6192015-04-01 13:05:26 -0700407 if (!result) {
408 return false;
409 }
410
sangho666cd6d2015-04-14 16:27:13 -0700411 Ip4Address routerIp = config.getRouterIp(destSw);
412 IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH);
413 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
sanghob35a6192015-04-01 13:05:26 -0700414 if (!result) {
415 return false;
416 }
417
sangho52abe3a2015-05-05 14:13:34 -0700418 // If the target switch is an edge router, then set IP rules for the router IP.
sangho666cd6d2015-04-14 16:27:13 -0700419 } else if (config.isEdgeDevice(targetSw)) {
420 Ip4Address routerIp = config.getRouterIp(destSw);
421 IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH);
422 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
sanghob35a6192015-04-01 13:05:26 -0700423 if (!result) {
424 return false;
425 }
sangho52abe3a2015-05-05 14:13:34 -0700426 }
sanghob35a6192015-04-01 13:05:26 -0700427
sangho52abe3a2015-05-05 14:13:34 -0700428 // Populates MPLS rules to all routers
429 result = rulePopulator.populateMplsRule(targetSw, destSw, nextHops);
430 if (!result) {
431 return false;
sanghob35a6192015-04-01 13:05:26 -0700432 }
433
434 return true;
435 }
436
437 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700438 * Populates table miss entries for all tables, and pipeline rules for VLAN
439 * and TACM tables.
sanghob35a6192015-04-01 13:05:26 -0700440 *
441 * @param deviceId Switch ID to set the rules
442 */
443 public void populateTtpRules(DeviceId deviceId) {
sanghob35a6192015-04-01 13:05:26 -0700444 rulePopulator.populateTableVlan(deviceId);
445 rulePopulator.populateTableTMac(deviceId);
446 }
447
448 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700449 * Start the flow rule population process if it was never started. The
450 * process finishes successfully when all flow rules are set and stops with
451 * ABORTED status when any groups required for flows is not set yet.
sanghob35a6192015-04-01 13:05:26 -0700452 */
453 public void startPopulationProcess() {
454 synchronized (populationStatus) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700455 if (populationStatus == Status.IDLE
456 || populationStatus == Status.SUCCEEDED) {
sanghob35a6192015-04-01 13:05:26 -0700457 populationStatus = Status.STARTED;
458 populateAllRoutingRules();
459 }
460 }
461 }
462
463 /**
464 * Resume the flow rule population process if it was aborted for any reason.
465 * Mostly the process is aborted when the groups required are not set yet.
466 */
467 public void resumePopulationProcess() {
468 synchronized (populationStatus) {
469 if (populationStatus == Status.ABORTED) {
470 populationStatus = Status.STARTED;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700471 // TODO: we need to restart from the point aborted instead of
472 // restarting.
sanghob35a6192015-04-01 13:05:26 -0700473 populateAllRoutingRules();
474 }
475 }
476 }
477}