blob: 3a2d2435101256c0be294fd42528880f845ef3ba [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;
27import org.onosproject.net.flow.FlowRule;
28import org.slf4j.Logger;
29import org.slf4j.LoggerFactory;
30
31import java.util.ArrayList;
32import java.util.HashMap;
33import java.util.HashSet;
Srikanth Vavilapalli4db76e32015-04-07 15:12:32 -070034import java.util.List;
sanghob35a6192015-04-01 13:05:26 -070035import java.util.Set;
36
37import static com.google.common.base.Preconditions.checkNotNull;
38
39public class DefaultRoutingHandler {
40
41 private static Logger log = LoggerFactory.getLogger(DefaultRoutingHandler.class);
42
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
59 // population process was aborted due to errors, mostly for groups not found.
60 ABORTED,
61
62 // population process was finished successfully.
63 SUCCEEDED
64 }
65
66 /**
67 * Creates a DefaultRoutingHandler object.
68 *
69 * @param srManager SegmentRoutingManager object
70 */
71 public DefaultRoutingHandler(SegmentRoutingManager srManager) {
72 this.srManager = srManager;
73 this.rulePopulator = checkNotNull(srManager.routingRulePopulator);
sangho666cd6d2015-04-14 16:27:13 -070074 this.config = checkNotNull(srManager.deviceConfiguration);
sanghob35a6192015-04-01 13:05:26 -070075 this.populationStatus = Status.IDLE;
sangho20eff1d2015-04-13 15:15:58 -070076 this.currentEcmpSpgMap = Maps.newHashMap();
sanghob35a6192015-04-01 13:05:26 -070077 }
78
79 /**
80 * Populates all routing rules to all connected routers, including default
81 * routing rules, adjacency rules, and policy rules if any.
82 *
83 * @return true if it succeeds in populating all rules, otherwise false
84 */
85 public boolean populateAllRoutingRules() {
86
87 populationStatus = Status.STARTED;
sangho20eff1d2015-04-13 15:15:58 -070088 rulePopulator.resetCounter();
sanghob35a6192015-04-01 13:05:26 -070089 log.info("Starts to populate routing rules");
90
91 for (Device sw : srManager.deviceService.getDevices()) {
92 if (srManager.mastershipService.
93 getLocalRole(sw.id()) != MastershipRole.MASTER) {
94 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
326 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
327 ecmpSPG.getAllLearnedSwitchesAndVia();
328 for (Integer itrIdx : switchVia.keySet()) {
329 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
330 switchVia.get(itrIdx);
331 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
350 private boolean populateEcmpRoutingRulePartial(DeviceId targetSw, DeviceId destSw,
351 Set<DeviceId> nextHops) {
352 boolean result;
353
354 if (nextHops.isEmpty()) {
355 nextHops.add(destSw);
356 }
357
358 // If both target switch and dest switch are edge routers, then set IP rule
359 // for both subnet and router IP.
sangho666cd6d2015-04-14 16:27:13 -0700360 if (config.isEdgeDevice(targetSw) && config.isEdgeDevice(destSw)) {
361 List<Ip4Prefix> subnets = config.getSubnets(destSw);
Srikanth Vavilapalli4db76e32015-04-07 15:12:32 -0700362 result = rulePopulator.populateIpRuleForSubnet(targetSw,
363 subnets,
364 destSw,
365 nextHops);
sanghob35a6192015-04-01 13:05:26 -0700366 if (!result) {
367 return false;
368 }
369
sangho666cd6d2015-04-14 16:27:13 -0700370 Ip4Address routerIp = config.getRouterIp(destSw);
371 IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH);
372 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
sanghob35a6192015-04-01 13:05:26 -0700373 if (!result) {
374 return false;
375 }
376
377 // If the target switch is an edge router, then set IP rules for the router IP.
sangho666cd6d2015-04-14 16:27:13 -0700378 } else if (config.isEdgeDevice(targetSw)) {
379 Ip4Address routerIp = config.getRouterIp(destSw);
380 IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH);
381 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
sanghob35a6192015-04-01 13:05:26 -0700382 if (!result) {
383 return false;
384 }
385
386 // If the target switch is an transit router, then set MPLS rules only.
sangho666cd6d2015-04-14 16:27:13 -0700387 } else if (!config.isEdgeDevice(targetSw)) {
sanghob35a6192015-04-01 13:05:26 -0700388 result = rulePopulator.populateMplsRule(targetSw, destSw, nextHops);
389 if (!result) {
390 return false;
391 }
sanghob35a6192015-04-01 13:05:26 -0700392 }
393
394 return true;
395 }
396
397 /**
398 * Populates table miss entries for all tables, and pipeline rules for
399 * VLAN and TACM tables.
400 *
401 * @param deviceId Switch ID to set the rules
402 */
403 public void populateTtpRules(DeviceId deviceId) {
404
405 rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.VLAN,
406 true, false, false, FlowRule.Type.DEFAULT);
407 rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.ETHER,
408 true, false, false, FlowRule.Type.DEFAULT);
409 rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.IP,
410 false, true, true, FlowRule.Type.ACL);
411 rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.MPLS,
412 false, true, true, FlowRule.Type.ACL);
413 rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.ACL,
414 false, false, false, FlowRule.Type.DEFAULT);
415
416 rulePopulator.populateTableVlan(deviceId);
417 rulePopulator.populateTableTMac(deviceId);
418 }
419
420 /**
421 * Start the flow rule population process if it was never started.
422 * The process finishes successfully when all flow rules are set and
423 * stops with ABORTED status when any groups required for flows is not
424 * set yet.
425 */
426 public void startPopulationProcess() {
427 synchronized (populationStatus) {
428 if (populationStatus == Status.IDLE ||
429 populationStatus == Status.SUCCEEDED) {
430 populationStatus = Status.STARTED;
431 populateAllRoutingRules();
432 }
433 }
434 }
435
436 /**
437 * Resume the flow rule population process if it was aborted for any reason.
438 * Mostly the process is aborted when the groups required are not set yet.
439 */
440 public void resumePopulationProcess() {
441 synchronized (populationStatus) {
442 if (populationStatus == Status.ABORTED) {
443 populationStatus = Status.STARTED;
444 // TODO: we need to restart from the point aborted instead of restarting.
445 populateAllRoutingRules();
446 }
447 }
448 }
449}