blob: 7bb88995ee11507bd3b4fb0fbca5a211ba37ea41 [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;
sangho666cd6d2015-04-14 16:27:13 -070046 private DeviceConfiguration config;
sanghob35a6192015-04-01 13:05:26 -070047 private Status populationStatus;
48
49 /**
50 * Represents the default routing population status.
51 */
52 public enum Status {
53 // population process is not started yet.
54 IDLE,
55
56 // population process started.
57 STARTED,
58
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070059 // population process was aborted due to errors, mostly for groups not
60 // found.
sanghob35a6192015-04-01 13:05:26 -070061 ABORTED,
62
63 // population process was finished successfully.
64 SUCCEEDED
65 }
66
67 /**
68 * Creates a DefaultRoutingHandler object.
69 *
70 * @param srManager SegmentRoutingManager object
71 */
72 public DefaultRoutingHandler(SegmentRoutingManager srManager) {
73 this.srManager = srManager;
74 this.rulePopulator = checkNotNull(srManager.routingRulePopulator);
sangho666cd6d2015-04-14 16:27:13 -070075 this.config = checkNotNull(srManager.deviceConfiguration);
sanghob35a6192015-04-01 13:05:26 -070076 this.populationStatus = Status.IDLE;
sangho20eff1d2015-04-13 15:15:58 -070077 this.currentEcmpSpgMap = Maps.newHashMap();
sanghob35a6192015-04-01 13:05:26 -070078 }
79
80 /**
81 * Populates all routing rules to all connected routers, including default
82 * routing rules, adjacency rules, and policy rules if any.
83 *
84 * @return true if it succeeds in populating all rules, otherwise false
85 */
86 public boolean populateAllRoutingRules() {
87
88 populationStatus = Status.STARTED;
sangho20eff1d2015-04-13 15:15:58 -070089 rulePopulator.resetCounter();
sanghob35a6192015-04-01 13:05:26 -070090 log.info("Starts to populate routing rules");
91
92 for (Device sw : srManager.deviceService.getDevices()) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070093 if (srManager.mastershipService.getLocalRole(sw.id()) != MastershipRole.MASTER) {
sanghob35a6192015-04-01 13:05:26 -070094 continue;
95 }
96
sangho20eff1d2015-04-13 15:15:58 -070097 ECMPShortestPathGraph ecmpSpg = new ECMPShortestPathGraph(sw.id(), srManager);
98 if (!populateEcmpRoutingRules(sw.id(), ecmpSpg)) {
sanghob35a6192015-04-01 13:05:26 -070099 populationStatus = Status.ABORTED;
100 log.debug("Abort routing rule population");
101 return false;
102 }
sangho20eff1d2015-04-13 15:15:58 -0700103 currentEcmpSpgMap.put(sw.id(), ecmpSpg);
sanghob35a6192015-04-01 13:05:26 -0700104
105 // TODO: Set adjacency routing rule for all switches
106 }
107
108 populationStatus = Status.SUCCEEDED;
sangho20eff1d2015-04-13 15:15:58 -0700109 log.info("Completes routing rule population. Total # of rules pushed : {}",
110 rulePopulator.getCounter());
sanghob35a6192015-04-01 13:05:26 -0700111 return true;
112 }
113
sangho20eff1d2015-04-13 15:15:58 -0700114 /**
115 * Populates the routing rules according to the route changes due to the link
116 * failure or link add. It computes the routes changed due to the link changes and
117 * repopulates the rules only for the routes.
118 *
119 * @param linkFail link failed, null for link added
120 * @return true if it succeeds to populate all rules, false otherwise
121 */
122 public boolean populateRoutingRulesForLinkStatusChange(Link linkFail) {
123
124 synchronized (populationStatus) {
125
126 if (populationStatus == Status.STARTED) {
sangho52abe3a2015-05-05 14:13:34 -0700127 log.warn("Previous rule population is not finished.");
sangho20eff1d2015-04-13 15:15:58 -0700128 return true;
129 }
130
sangho52abe3a2015-05-05 14:13:34 -0700131 log.info("Starts rule population from link change");
132
sangho20eff1d2015-04-13 15:15:58 -0700133 Set<ArrayList<DeviceId>> routeChanges;
134 populationStatus = Status.STARTED;
135 if (linkFail == null) {
136 // Compare all routes of existing ECMP SPG with the new ones
137 routeChanges = computeRouteChange();
138 } else {
139 // Compare existing ECMP SPG only with the link removed
140 routeChanges = computeDamagedRoutes(linkFail);
141 }
142
143 if (routeChanges.isEmpty()) {
sangho52abe3a2015-05-05 14:13:34 -0700144 log.info("No route changes for the link status change");
sangho20eff1d2015-04-13 15:15:58 -0700145 populationStatus = Status.SUCCEEDED;
146 return true;
147 }
148
149 if (repopulateRoutingRulesForRoutes(routeChanges)) {
150 populationStatus = Status.SUCCEEDED;
151 log.info("Complete to repopulate the rules. # of rules populated : {}",
152 rulePopulator.getCounter());
153 return true;
154 } else {
155 populationStatus = Status.ABORTED;
156 log.warn("Failed to repopulate the rules.");
157 return false;
158 }
159 }
160 }
161
162 private boolean repopulateRoutingRulesForRoutes(Set<ArrayList<DeviceId>> routes) {
163 rulePopulator.resetCounter();
164 for (ArrayList<DeviceId> link: routes) {
sangho834e4b02015-05-01 09:38:25 -0700165 // When only the source device is defined, reinstall routes to all other devices
sangho20eff1d2015-04-13 15:15:58 -0700166 if (link.size() == 1) {
167 ECMPShortestPathGraph ecmpSpg = new ECMPShortestPathGraph(link.get(0), srManager);
168 if (populateEcmpRoutingRules(link.get(0), ecmpSpg)) {
169 currentEcmpSpgMap.put(link.get(0), ecmpSpg);
sangho52abe3a2015-05-05 14:13:34 -0700170 } else {
171 log.warn("Failed to populate the flow ruls from {} to all", link.get(0));
172 return false;
sangho20eff1d2015-04-13 15:15:58 -0700173 }
174 continue;
175 }
176 DeviceId src = link.get(0);
177 DeviceId dst = link.get(1);
178 ECMPShortestPathGraph ecmpSpg = new ECMPShortestPathGraph(dst, srManager);
179
180 currentEcmpSpgMap.put(dst, ecmpSpg);
181 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
182 ecmpSpg.getAllLearnedSwitchesAndVia();
183 for (Integer itrIdx : switchVia.keySet()) {
184 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
185 switchVia.get(itrIdx);
186 for (DeviceId targetSw : swViaMap.keySet()) {
187 if (!targetSw.equals(src)) {
188 continue;
189 }
190 Set<DeviceId> nextHops = new HashSet<>();
191 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
192 if (via.isEmpty()) {
193 nextHops.add(dst);
194 } else {
195 nextHops.add(via.get(0));
196 }
197 }
198 if (!populateEcmpRoutingRulePartial(targetSw, dst, nextHops)) {
199 return false;
200 }
201 }
202 }
203 }
204 return true;
205 }
206
207 private Set<ArrayList<DeviceId>> computeDamagedRoutes(Link linkFail) {
208
209 Set<ArrayList<DeviceId>> routes = new HashSet<>();
210
211 for (Device sw : srManager.deviceService.getDevices()) {
212 if (srManager.mastershipService.
213 getLocalRole(sw.id()) != MastershipRole.MASTER) {
214 continue;
215 }
216 ECMPShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(sw.id());
217 if (ecmpSpg == null) {
218 log.error("No existing ECMP path for switch {}", sw.id());
219 continue;
220 }
221 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
222 ecmpSpg.getAllLearnedSwitchesAndVia();
223 for (Integer itrIdx : switchVia.keySet()) {
224 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
225 switchVia.get(itrIdx);
226 for (DeviceId targetSw : swViaMap.keySet()) {
227 DeviceId destSw = sw.id();
228 Set<ArrayList<DeviceId>> subLinks =
229 computeLinks(targetSw, destSw, swViaMap);
230 for (ArrayList<DeviceId> alink: subLinks) {
231 if (alink.get(0).equals(linkFail.src().deviceId()) &&
232 alink.get(1).equals(linkFail.dst().deviceId())) {
233 ArrayList<DeviceId> aRoute = new ArrayList<>();
234 aRoute.add(targetSw);
235 aRoute.add(destSw);
236 routes.add(aRoute);
237 break;
238 }
239 }
240 }
241 }
242 }
243
244 return routes;
245 }
246
247 private Set<ArrayList<DeviceId>> computeRouteChange() {
248
249 Set<ArrayList<DeviceId>> routes = new HashSet<>();
250
251 for (Device sw : srManager.deviceService.getDevices()) {
252 if (srManager.mastershipService.
253 getLocalRole(sw.id()) != MastershipRole.MASTER) {
254 continue;
255 }
256 ECMPShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(sw.id());
257 if (ecmpSpg == null) {
258 log.debug("No existing ECMP path for Switch {}", sw.id());
259 ArrayList<DeviceId> route = new ArrayList<>();
260 route.add(sw.id());
261 routes.add(route);
262 continue;
263 }
264 ECMPShortestPathGraph newEcmpSpg =
265 new ECMPShortestPathGraph(sw.id(), srManager);
266 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
267 ecmpSpg.getAllLearnedSwitchesAndVia();
268 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchViaUpdated =
269 newEcmpSpg.getAllLearnedSwitchesAndVia();
270
271 for (Integer itrIdx : switchVia.keySet()) {
272 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
273 switchVia.get(itrIdx);
274 for (DeviceId srcSw : swViaMap.keySet()) {
275 ArrayList<ArrayList<DeviceId>> via1 = swViaMap.get(srcSw);
276 ArrayList<ArrayList<DeviceId>> via2 = getVia(switchViaUpdated, srcSw);
277 if (!via1.equals(via2)) {
278 ArrayList<DeviceId> route = new ArrayList<>();
279 route.add(srcSw);
280 route.add(sw.id());
281 routes.add(route);
282 }
283 }
284 }
285
286 }
287
288 return routes;
289 }
290
291 private ArrayList<ArrayList<DeviceId>> getVia(HashMap<Integer, HashMap<DeviceId,
292 ArrayList<ArrayList<DeviceId>>>> switchVia, DeviceId srcSw) {
293 for (Integer itrIdx : switchVia.keySet()) {
294 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
295 switchVia.get(itrIdx);
296 if (swViaMap.get(srcSw) == null) {
297 continue;
298 } else {
299 return swViaMap.get(srcSw);
300 }
301 }
302
303 return new ArrayList<>();
304 }
305
306 private Set<ArrayList<DeviceId>> computeLinks(DeviceId src,
307 DeviceId dst,
308 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> viaMap) {
309 Set<ArrayList<DeviceId>> subLinks = Sets.newHashSet();
310 for (ArrayList<DeviceId> via : viaMap.get(src)) {
311 DeviceId linkSrc = src;
312 DeviceId linkDst = dst;
313 for (DeviceId viaDevice: via) {
314 ArrayList<DeviceId> link = new ArrayList<>();
315 linkDst = viaDevice;
316 link.add(linkSrc);
317 link.add(linkDst);
318 subLinks.add(link);
319 linkSrc = viaDevice;
320 }
321 ArrayList<DeviceId> link = new ArrayList<>();
322 link.add(linkSrc);
323 link.add(dst);
324 subLinks.add(link);
325 }
326
327 return subLinks;
328 }
329
330 private boolean populateEcmpRoutingRules(DeviceId destSw,
sanghob35a6192015-04-01 13:05:26 -0700331 ECMPShortestPathGraph ecmpSPG) {
332
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700333 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia = ecmpSPG
334 .getAllLearnedSwitchesAndVia();
sanghob35a6192015-04-01 13:05:26 -0700335 for (Integer itrIdx : switchVia.keySet()) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700336 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap = switchVia
337 .get(itrIdx);
sanghob35a6192015-04-01 13:05:26 -0700338 for (DeviceId targetSw : swViaMap.keySet()) {
sanghob35a6192015-04-01 13:05:26 -0700339 Set<DeviceId> nextHops = new HashSet<>();
340
341 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
342 if (via.isEmpty()) {
343 nextHops.add(destSw);
344 } else {
345 nextHops.add(via.get(0));
346 }
347 }
348 if (!populateEcmpRoutingRulePartial(targetSw, destSw, nextHops)) {
349 return false;
350 }
351 }
352 }
353
354 return true;
355 }
356
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700357 private boolean populateEcmpRoutingRulePartial(DeviceId targetSw,
358 DeviceId destSw,
359 Set<DeviceId> nextHops) {
sanghob35a6192015-04-01 13:05:26 -0700360 boolean result;
361
362 if (nextHops.isEmpty()) {
363 nextHops.add(destSw);
364 }
365
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700366 // If both target switch and dest switch are edge routers, then set IP
sangho52abe3a2015-05-05 14:13:34 -0700367 // rule for both subnet and router IP.
sangho666cd6d2015-04-14 16:27:13 -0700368 if (config.isEdgeDevice(targetSw) && config.isEdgeDevice(destSw)) {
369 List<Ip4Prefix> subnets = config.getSubnets(destSw);
Srikanth Vavilapalli4db76e32015-04-07 15:12:32 -0700370 result = rulePopulator.populateIpRuleForSubnet(targetSw,
371 subnets,
372 destSw,
373 nextHops);
sanghob35a6192015-04-01 13:05:26 -0700374 if (!result) {
375 return false;
376 }
377
sangho666cd6d2015-04-14 16:27:13 -0700378 Ip4Address routerIp = config.getRouterIp(destSw);
379 IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH);
380 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
sanghob35a6192015-04-01 13:05:26 -0700381 if (!result) {
382 return false;
383 }
384
sangho52abe3a2015-05-05 14:13:34 -0700385 // If the target switch is an edge router, then set IP rules for the router IP.
sangho666cd6d2015-04-14 16:27:13 -0700386 } else if (config.isEdgeDevice(targetSw)) {
387 Ip4Address routerIp = config.getRouterIp(destSw);
388 IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH);
389 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
sanghob35a6192015-04-01 13:05:26 -0700390 if (!result) {
391 return false;
392 }
sangho52abe3a2015-05-05 14:13:34 -0700393 }
sanghob35a6192015-04-01 13:05:26 -0700394
sangho52abe3a2015-05-05 14:13:34 -0700395 // Populates MPLS rules to all routers
396 result = rulePopulator.populateMplsRule(targetSw, destSw, nextHops);
397 if (!result) {
398 return false;
sanghob35a6192015-04-01 13:05:26 -0700399 }
400
401 return true;
402 }
403
404 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700405 * Populates table miss entries for all tables, and pipeline rules for VLAN
406 * and TACM tables.
sanghob35a6192015-04-01 13:05:26 -0700407 *
408 * @param deviceId Switch ID to set the rules
409 */
410 public void populateTtpRules(DeviceId deviceId) {
sanghob35a6192015-04-01 13:05:26 -0700411 rulePopulator.populateTableVlan(deviceId);
412 rulePopulator.populateTableTMac(deviceId);
413 }
414
415 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700416 * Start the flow rule population process if it was never started. The
417 * process finishes successfully when all flow rules are set and stops with
418 * ABORTED status when any groups required for flows is not set yet.
sanghob35a6192015-04-01 13:05:26 -0700419 */
420 public void startPopulationProcess() {
421 synchronized (populationStatus) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700422 if (populationStatus == Status.IDLE
423 || populationStatus == Status.SUCCEEDED) {
sanghob35a6192015-04-01 13:05:26 -0700424 populationStatus = Status.STARTED;
425 populateAllRoutingRules();
426 }
427 }
428 }
429
430 /**
431 * Resume the flow rule population process if it was aborted for any reason.
432 * Mostly the process is aborted when the groups required are not set yet.
433 */
434 public void resumePopulationProcess() {
435 synchronized (populationStatus) {
436 if (populationStatus == Status.ABORTED) {
437 populationStatus = Status.STARTED;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700438 // TODO: we need to restart from the point aborted instead of
439 // restarting.
sanghob35a6192015-04-01 13:05:26 -0700440 populateAllRoutingRules();
441 }
442 }
443 }
444}