blob: c5bd6baeb5caf28a0592a278269e34c2fecff2a1 [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) {
127 return true;
128 }
129
130 Set<ArrayList<DeviceId>> routeChanges;
131 populationStatus = Status.STARTED;
132 if (linkFail == null) {
133 // Compare all routes of existing ECMP SPG with the new ones
134 routeChanges = computeRouteChange();
135 } else {
136 // Compare existing ECMP SPG only with the link removed
137 routeChanges = computeDamagedRoutes(linkFail);
138 }
139
140 if (routeChanges.isEmpty()) {
141 log.debug("No route changes for the link status change");
142 populationStatus = Status.SUCCEEDED;
143 return true;
144 }
145
146 if (repopulateRoutingRulesForRoutes(routeChanges)) {
147 populationStatus = Status.SUCCEEDED;
148 log.info("Complete to repopulate the rules. # of rules populated : {}",
149 rulePopulator.getCounter());
150 return true;
151 } else {
152 populationStatus = Status.ABORTED;
153 log.warn("Failed to repopulate the rules.");
154 return false;
155 }
156 }
157 }
158
159 private boolean repopulateRoutingRulesForRoutes(Set<ArrayList<DeviceId>> routes) {
160 rulePopulator.resetCounter();
161 for (ArrayList<DeviceId> link: routes) {
162 if (link.size() == 1) {
163 ECMPShortestPathGraph ecmpSpg = new ECMPShortestPathGraph(link.get(0), srManager);
164 if (populateEcmpRoutingRules(link.get(0), ecmpSpg)) {
165 currentEcmpSpgMap.put(link.get(0), ecmpSpg);
166 }
167 continue;
168 }
169 DeviceId src = link.get(0);
170 DeviceId dst = link.get(1);
171 ECMPShortestPathGraph ecmpSpg = new ECMPShortestPathGraph(dst, srManager);
172
173 currentEcmpSpgMap.put(dst, ecmpSpg);
174 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
175 ecmpSpg.getAllLearnedSwitchesAndVia();
176 for (Integer itrIdx : switchVia.keySet()) {
177 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
178 switchVia.get(itrIdx);
179 for (DeviceId targetSw : swViaMap.keySet()) {
180 if (!targetSw.equals(src)) {
181 continue;
182 }
183 Set<DeviceId> nextHops = new HashSet<>();
184 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
185 if (via.isEmpty()) {
186 nextHops.add(dst);
187 } else {
188 nextHops.add(via.get(0));
189 }
190 }
191 if (!populateEcmpRoutingRulePartial(targetSw, dst, nextHops)) {
192 return false;
193 }
194 }
195 }
196 }
197 return true;
198 }
199
200 private Set<ArrayList<DeviceId>> computeDamagedRoutes(Link linkFail) {
201
202 Set<ArrayList<DeviceId>> routes = new HashSet<>();
203
204 for (Device sw : srManager.deviceService.getDevices()) {
205 if (srManager.mastershipService.
206 getLocalRole(sw.id()) != MastershipRole.MASTER) {
207 continue;
208 }
209 ECMPShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(sw.id());
210 if (ecmpSpg == null) {
211 log.error("No existing ECMP path for switch {}", sw.id());
212 continue;
213 }
214 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
215 ecmpSpg.getAllLearnedSwitchesAndVia();
216 for (Integer itrIdx : switchVia.keySet()) {
217 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
218 switchVia.get(itrIdx);
219 for (DeviceId targetSw : swViaMap.keySet()) {
220 DeviceId destSw = sw.id();
221 Set<ArrayList<DeviceId>> subLinks =
222 computeLinks(targetSw, destSw, swViaMap);
223 for (ArrayList<DeviceId> alink: subLinks) {
224 if (alink.get(0).equals(linkFail.src().deviceId()) &&
225 alink.get(1).equals(linkFail.dst().deviceId())) {
226 ArrayList<DeviceId> aRoute = new ArrayList<>();
227 aRoute.add(targetSw);
228 aRoute.add(destSw);
229 routes.add(aRoute);
230 break;
231 }
232 }
233 }
234 }
235 }
236
237 return routes;
238 }
239
240 private Set<ArrayList<DeviceId>> computeRouteChange() {
241
242 Set<ArrayList<DeviceId>> routes = new HashSet<>();
243
244 for (Device sw : srManager.deviceService.getDevices()) {
245 if (srManager.mastershipService.
246 getLocalRole(sw.id()) != MastershipRole.MASTER) {
247 continue;
248 }
249 ECMPShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(sw.id());
250 if (ecmpSpg == null) {
251 log.debug("No existing ECMP path for Switch {}", sw.id());
252 ArrayList<DeviceId> route = new ArrayList<>();
253 route.add(sw.id());
254 routes.add(route);
255 continue;
256 }
257 ECMPShortestPathGraph newEcmpSpg =
258 new ECMPShortestPathGraph(sw.id(), srManager);
259 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
260 ecmpSpg.getAllLearnedSwitchesAndVia();
261 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchViaUpdated =
262 newEcmpSpg.getAllLearnedSwitchesAndVia();
263
264 for (Integer itrIdx : switchVia.keySet()) {
265 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
266 switchVia.get(itrIdx);
267 for (DeviceId srcSw : swViaMap.keySet()) {
268 ArrayList<ArrayList<DeviceId>> via1 = swViaMap.get(srcSw);
269 ArrayList<ArrayList<DeviceId>> via2 = getVia(switchViaUpdated, srcSw);
270 if (!via1.equals(via2)) {
271 ArrayList<DeviceId> route = new ArrayList<>();
272 route.add(srcSw);
273 route.add(sw.id());
274 routes.add(route);
275 }
276 }
277 }
278
279 }
280
281 return routes;
282 }
283
284 private ArrayList<ArrayList<DeviceId>> getVia(HashMap<Integer, HashMap<DeviceId,
285 ArrayList<ArrayList<DeviceId>>>> switchVia, DeviceId srcSw) {
286 for (Integer itrIdx : switchVia.keySet()) {
287 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
288 switchVia.get(itrIdx);
289 if (swViaMap.get(srcSw) == null) {
290 continue;
291 } else {
292 return swViaMap.get(srcSw);
293 }
294 }
295
296 return new ArrayList<>();
297 }
298
299 private Set<ArrayList<DeviceId>> computeLinks(DeviceId src,
300 DeviceId dst,
301 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> viaMap) {
302 Set<ArrayList<DeviceId>> subLinks = Sets.newHashSet();
303 for (ArrayList<DeviceId> via : viaMap.get(src)) {
304 DeviceId linkSrc = src;
305 DeviceId linkDst = dst;
306 for (DeviceId viaDevice: via) {
307 ArrayList<DeviceId> link = new ArrayList<>();
308 linkDst = viaDevice;
309 link.add(linkSrc);
310 link.add(linkDst);
311 subLinks.add(link);
312 linkSrc = viaDevice;
313 }
314 ArrayList<DeviceId> link = new ArrayList<>();
315 link.add(linkSrc);
316 link.add(dst);
317 subLinks.add(link);
318 }
319
320 return subLinks;
321 }
322
323 private boolean populateEcmpRoutingRules(DeviceId destSw,
sanghob35a6192015-04-01 13:05:26 -0700324 ECMPShortestPathGraph ecmpSPG) {
325
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700326 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia = ecmpSPG
327 .getAllLearnedSwitchesAndVia();
sanghob35a6192015-04-01 13:05:26 -0700328 for (Integer itrIdx : switchVia.keySet()) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700329 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap = switchVia
330 .get(itrIdx);
sanghob35a6192015-04-01 13:05:26 -0700331 for (DeviceId targetSw : swViaMap.keySet()) {
sanghob35a6192015-04-01 13:05:26 -0700332 Set<DeviceId> nextHops = new HashSet<>();
333
334 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
335 if (via.isEmpty()) {
336 nextHops.add(destSw);
337 } else {
338 nextHops.add(via.get(0));
339 }
340 }
341 if (!populateEcmpRoutingRulePartial(targetSw, destSw, nextHops)) {
342 return false;
343 }
344 }
345 }
346
347 return true;
348 }
349
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700350 private boolean populateEcmpRoutingRulePartial(DeviceId targetSw,
351 DeviceId destSw,
352 Set<DeviceId> nextHops) {
sanghob35a6192015-04-01 13:05:26 -0700353 boolean result;
354
355 if (nextHops.isEmpty()) {
356 nextHops.add(destSw);
357 }
358
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700359 // If both target switch and dest switch are edge routers, then set IP
360 // rule
sanghob35a6192015-04-01 13:05:26 -0700361 // for both subnet and router IP.
sangho666cd6d2015-04-14 16:27:13 -0700362 if (config.isEdgeDevice(targetSw) && config.isEdgeDevice(destSw)) {
363 List<Ip4Prefix> subnets = config.getSubnets(destSw);
Srikanth Vavilapalli4db76e32015-04-07 15:12:32 -0700364 result = rulePopulator.populateIpRuleForSubnet(targetSw,
365 subnets,
366 destSw,
367 nextHops);
sanghob35a6192015-04-01 13:05:26 -0700368 if (!result) {
369 return false;
370 }
371
sangho666cd6d2015-04-14 16:27:13 -0700372 Ip4Address routerIp = config.getRouterIp(destSw);
373 IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH);
374 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
sanghob35a6192015-04-01 13:05:26 -0700375 if (!result) {
376 return false;
377 }
378
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700379 // TODO: If the target switch is an edge router, then set IP rules for the router IP.
sangho666cd6d2015-04-14 16:27:13 -0700380 } else if (config.isEdgeDevice(targetSw)) {
381 Ip4Address routerIp = config.getRouterIp(destSw);
382 IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH);
383 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
sanghob35a6192015-04-01 13:05:26 -0700384 if (!result) {
385 return false;
386 }
387
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700388 // TODO: If the target switch is an transit router, then set MPLS rules only.
sangho666cd6d2015-04-14 16:27:13 -0700389 } else if (!config.isEdgeDevice(targetSw)) {
sanghob35a6192015-04-01 13:05:26 -0700390 result = rulePopulator.populateMplsRule(targetSw, destSw, nextHops);
391 if (!result) {
392 return false;
393 }
sanghob35a6192015-04-01 13:05:26 -0700394 }
395
396 return true;
397 }
398
399 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700400 * Populates table miss entries for all tables, and pipeline rules for VLAN
401 * and TACM tables.
sanghob35a6192015-04-01 13:05:26 -0700402 *
403 * @param deviceId Switch ID to set the rules
404 */
405 public void populateTtpRules(DeviceId deviceId) {
sanghob35a6192015-04-01 13:05:26 -0700406 rulePopulator.populateTableVlan(deviceId);
407 rulePopulator.populateTableTMac(deviceId);
408 }
409
410 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700411 * Start the flow rule population process if it was never started. The
412 * process finishes successfully when all flow rules are set and stops with
413 * ABORTED status when any groups required for flows is not set yet.
sanghob35a6192015-04-01 13:05:26 -0700414 */
415 public void startPopulationProcess() {
416 synchronized (populationStatus) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700417 if (populationStatus == Status.IDLE
418 || populationStatus == Status.SUCCEEDED) {
sanghob35a6192015-04-01 13:05:26 -0700419 populationStatus = Status.STARTED;
420 populateAllRoutingRules();
421 }
422 }
423 }
424
425 /**
426 * Resume the flow rule population process if it was aborted for any reason.
427 * Mostly the process is aborted when the groups required are not set yet.
428 */
429 public void resumePopulationProcess() {
430 synchronized (populationStatus) {
431 if (populationStatus == Status.ABORTED) {
432 populationStatus = Status.STARTED;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700433 // TODO: we need to restart from the point aborted instead of
434 // restarting.
sanghob35a6192015-04-01 13:05:26 -0700435 populateAllRoutingRules();
436 }
437 }
438 }
439}