blob: 50466c666f6835c8ec814f1309fdabc9e0fae8c1 [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;
Srikanth Vavilapalli4db76e32015-04-07 15:12:32 -070020import org.onlab.packet.Ip4Prefix;
sanghob35a6192015-04-01 13:05:26 -070021import org.onlab.packet.IpPrefix;
22import org.onosproject.net.Device;
23import org.onosproject.net.DeviceId;
sangho20eff1d2015-04-13 15:15:58 -070024import org.onosproject.net.Link;
sanghob35a6192015-04-01 13:05:26 -070025import org.onosproject.net.MastershipRole;
26import org.onosproject.net.flow.FlowRule;
27import 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
40 private static Logger log = LoggerFactory.getLogger(DefaultRoutingHandler.class);
41
42 private SegmentRoutingManager srManager;
43 private RoutingRulePopulator rulePopulator;
44 private NetworkConfigHandler config;
sangho20eff1d2015-04-13 15:15:58 -070045 private HashMap<DeviceId, ECMPShortestPathGraph> currentEcmpSpgMap;
sanghob35a6192015-04-01 13:05:26 -070046 private Status populationStatus;
47
48 /**
49 * Represents the default routing population status.
50 */
51 public enum Status {
52 // population process is not started yet.
53 IDLE,
54
55 // population process started.
56 STARTED,
57
58 // population process was aborted due to errors, mostly for groups not found.
59 ABORTED,
60
61 // population process was finished successfully.
62 SUCCEEDED
63 }
64
65 /**
66 * Creates a DefaultRoutingHandler object.
67 *
68 * @param srManager SegmentRoutingManager object
69 */
70 public DefaultRoutingHandler(SegmentRoutingManager srManager) {
71 this.srManager = srManager;
72 this.rulePopulator = checkNotNull(srManager.routingRulePopulator);
73 this.config = checkNotNull(srManager.networkConfigHandler);
74 this.populationStatus = Status.IDLE;
sangho20eff1d2015-04-13 15:15:58 -070075 this.currentEcmpSpgMap = Maps.newHashMap();
sanghob35a6192015-04-01 13:05:26 -070076 }
77
78 /**
79 * Populates all routing rules to all connected routers, including default
80 * routing rules, adjacency rules, and policy rules if any.
81 *
82 * @return true if it succeeds in populating all rules, otherwise false
83 */
84 public boolean populateAllRoutingRules() {
85
86 populationStatus = Status.STARTED;
sangho20eff1d2015-04-13 15:15:58 -070087 rulePopulator.resetCounter();
sanghob35a6192015-04-01 13:05:26 -070088 log.info("Starts to populate routing rules");
89
90 for (Device sw : srManager.deviceService.getDevices()) {
91 if (srManager.mastershipService.
92 getLocalRole(sw.id()) != MastershipRole.MASTER) {
93 continue;
94 }
95
sangho20eff1d2015-04-13 15:15:58 -070096 ECMPShortestPathGraph ecmpSpg = new ECMPShortestPathGraph(sw.id(), srManager);
97 if (!populateEcmpRoutingRules(sw.id(), ecmpSpg)) {
sanghob35a6192015-04-01 13:05:26 -070098 populationStatus = Status.ABORTED;
99 log.debug("Abort routing rule population");
100 return false;
101 }
sangho20eff1d2015-04-13 15:15:58 -0700102 currentEcmpSpgMap.put(sw.id(), ecmpSpg);
sanghob35a6192015-04-01 13:05:26 -0700103
104 // TODO: Set adjacency routing rule for all switches
105 }
106
107 populationStatus = Status.SUCCEEDED;
sangho20eff1d2015-04-13 15:15:58 -0700108 log.info("Completes routing rule population. Total # of rules pushed : {}",
109 rulePopulator.getCounter());
sanghob35a6192015-04-01 13:05:26 -0700110 return true;
111 }
112
sangho20eff1d2015-04-13 15:15:58 -0700113 /**
114 * Populates the routing rules according to the route changes due to the link
115 * failure or link add. It computes the routes changed due to the link changes and
116 * repopulates the rules only for the routes.
117 *
118 * @param linkFail link failed, null for link added
119 * @return true if it succeeds to populate all rules, false otherwise
120 */
121 public boolean populateRoutingRulesForLinkStatusChange(Link linkFail) {
122
123 synchronized (populationStatus) {
124
125 if (populationStatus == Status.STARTED) {
126 return true;
127 }
128
129 Set<ArrayList<DeviceId>> routeChanges;
130 populationStatus = Status.STARTED;
131 if (linkFail == null) {
132 // Compare all routes of existing ECMP SPG with the new ones
133 routeChanges = computeRouteChange();
134 } else {
135 // Compare existing ECMP SPG only with the link removed
136 routeChanges = computeDamagedRoutes(linkFail);
137 }
138
139 if (routeChanges.isEmpty()) {
140 log.debug("No route changes for the link status change");
141 populationStatus = Status.SUCCEEDED;
142 return true;
143 }
144
145 if (repopulateRoutingRulesForRoutes(routeChanges)) {
146 populationStatus = Status.SUCCEEDED;
147 log.info("Complete to repopulate the rules. # of rules populated : {}",
148 rulePopulator.getCounter());
149 return true;
150 } else {
151 populationStatus = Status.ABORTED;
152 log.warn("Failed to repopulate the rules.");
153 return false;
154 }
155 }
156 }
157
158 private boolean repopulateRoutingRulesForRoutes(Set<ArrayList<DeviceId>> routes) {
159 rulePopulator.resetCounter();
160 for (ArrayList<DeviceId> link: routes) {
161 if (link.size() == 1) {
162 ECMPShortestPathGraph ecmpSpg = new ECMPShortestPathGraph(link.get(0), srManager);
163 if (populateEcmpRoutingRules(link.get(0), ecmpSpg)) {
164 currentEcmpSpgMap.put(link.get(0), ecmpSpg);
165 }
166 continue;
167 }
168 DeviceId src = link.get(0);
169 DeviceId dst = link.get(1);
170 ECMPShortestPathGraph ecmpSpg = new ECMPShortestPathGraph(dst, srManager);
171
172 currentEcmpSpgMap.put(dst, ecmpSpg);
173 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
174 ecmpSpg.getAllLearnedSwitchesAndVia();
175 for (Integer itrIdx : switchVia.keySet()) {
176 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
177 switchVia.get(itrIdx);
178 for (DeviceId targetSw : swViaMap.keySet()) {
179 if (!targetSw.equals(src)) {
180 continue;
181 }
182 Set<DeviceId> nextHops = new HashSet<>();
183 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
184 if (via.isEmpty()) {
185 nextHops.add(dst);
186 } else {
187 nextHops.add(via.get(0));
188 }
189 }
190 if (!populateEcmpRoutingRulePartial(targetSw, dst, nextHops)) {
191 return false;
192 }
193 }
194 }
195 }
196 return true;
197 }
198
199 private Set<ArrayList<DeviceId>> computeDamagedRoutes(Link linkFail) {
200
201 Set<ArrayList<DeviceId>> routes = new HashSet<>();
202
203 for (Device sw : srManager.deviceService.getDevices()) {
204 if (srManager.mastershipService.
205 getLocalRole(sw.id()) != MastershipRole.MASTER) {
206 continue;
207 }
208 ECMPShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(sw.id());
209 if (ecmpSpg == null) {
210 log.error("No existing ECMP path for switch {}", sw.id());
211 continue;
212 }
213 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
214 ecmpSpg.getAllLearnedSwitchesAndVia();
215 for (Integer itrIdx : switchVia.keySet()) {
216 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
217 switchVia.get(itrIdx);
218 for (DeviceId targetSw : swViaMap.keySet()) {
219 DeviceId destSw = sw.id();
220 Set<ArrayList<DeviceId>> subLinks =
221 computeLinks(targetSw, destSw, swViaMap);
222 for (ArrayList<DeviceId> alink: subLinks) {
223 if (alink.get(0).equals(linkFail.src().deviceId()) &&
224 alink.get(1).equals(linkFail.dst().deviceId())) {
225 ArrayList<DeviceId> aRoute = new ArrayList<>();
226 aRoute.add(targetSw);
227 aRoute.add(destSw);
228 routes.add(aRoute);
229 break;
230 }
231 }
232 }
233 }
234 }
235
236 return routes;
237 }
238
239 private Set<ArrayList<DeviceId>> computeRouteChange() {
240
241 Set<ArrayList<DeviceId>> routes = new HashSet<>();
242
243 for (Device sw : srManager.deviceService.getDevices()) {
244 if (srManager.mastershipService.
245 getLocalRole(sw.id()) != MastershipRole.MASTER) {
246 continue;
247 }
248 ECMPShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(sw.id());
249 if (ecmpSpg == null) {
250 log.debug("No existing ECMP path for Switch {}", sw.id());
251 ArrayList<DeviceId> route = new ArrayList<>();
252 route.add(sw.id());
253 routes.add(route);
254 continue;
255 }
256 ECMPShortestPathGraph newEcmpSpg =
257 new ECMPShortestPathGraph(sw.id(), srManager);
258 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
259 ecmpSpg.getAllLearnedSwitchesAndVia();
260 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchViaUpdated =
261 newEcmpSpg.getAllLearnedSwitchesAndVia();
262
263 for (Integer itrIdx : switchVia.keySet()) {
264 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
265 switchVia.get(itrIdx);
266 for (DeviceId srcSw : swViaMap.keySet()) {
267 ArrayList<ArrayList<DeviceId>> via1 = swViaMap.get(srcSw);
268 ArrayList<ArrayList<DeviceId>> via2 = getVia(switchViaUpdated, srcSw);
269 if (!via1.equals(via2)) {
270 ArrayList<DeviceId> route = new ArrayList<>();
271 route.add(srcSw);
272 route.add(sw.id());
273 routes.add(route);
274 }
275 }
276 }
277
278 }
279
280 return routes;
281 }
282
283 private ArrayList<ArrayList<DeviceId>> getVia(HashMap<Integer, HashMap<DeviceId,
284 ArrayList<ArrayList<DeviceId>>>> switchVia, DeviceId srcSw) {
285 for (Integer itrIdx : switchVia.keySet()) {
286 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
287 switchVia.get(itrIdx);
288 if (swViaMap.get(srcSw) == null) {
289 continue;
290 } else {
291 return swViaMap.get(srcSw);
292 }
293 }
294
295 return new ArrayList<>();
296 }
297
298 private Set<ArrayList<DeviceId>> computeLinks(DeviceId src,
299 DeviceId dst,
300 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> viaMap) {
301 Set<ArrayList<DeviceId>> subLinks = Sets.newHashSet();
302 for (ArrayList<DeviceId> via : viaMap.get(src)) {
303 DeviceId linkSrc = src;
304 DeviceId linkDst = dst;
305 for (DeviceId viaDevice: via) {
306 ArrayList<DeviceId> link = new ArrayList<>();
307 linkDst = viaDevice;
308 link.add(linkSrc);
309 link.add(linkDst);
310 subLinks.add(link);
311 linkSrc = viaDevice;
312 }
313 ArrayList<DeviceId> link = new ArrayList<>();
314 link.add(linkSrc);
315 link.add(dst);
316 subLinks.add(link);
317 }
318
319 return subLinks;
320 }
321
322 private boolean populateEcmpRoutingRules(DeviceId destSw,
sanghob35a6192015-04-01 13:05:26 -0700323 ECMPShortestPathGraph ecmpSPG) {
324
325 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
326 ecmpSPG.getAllLearnedSwitchesAndVia();
327 for (Integer itrIdx : switchVia.keySet()) {
328 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
329 switchVia.get(itrIdx);
330 for (DeviceId targetSw : swViaMap.keySet()) {
sanghob35a6192015-04-01 13:05:26 -0700331 Set<DeviceId> nextHops = new HashSet<>();
332
333 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
334 if (via.isEmpty()) {
335 nextHops.add(destSw);
336 } else {
337 nextHops.add(via.get(0));
338 }
339 }
340 if (!populateEcmpRoutingRulePartial(targetSw, destSw, nextHops)) {
341 return false;
342 }
343 }
344 }
345
346 return true;
347 }
348
349 private boolean populateEcmpRoutingRulePartial(DeviceId targetSw, DeviceId destSw,
350 Set<DeviceId> nextHops) {
351 boolean result;
352
353 if (nextHops.isEmpty()) {
354 nextHops.add(destSw);
355 }
356
357 // If both target switch and dest switch are edge routers, then set IP rule
358 // for both subnet and router IP.
359 if (config.isEdgeRouter(targetSw) && config.isEdgeRouter(destSw)) {
Srikanth Vavilapalli4db76e32015-04-07 15:12:32 -0700360 List<Ip4Prefix> subnets = config.getSubnetInfo(destSw);
361 result = rulePopulator.populateIpRuleForSubnet(targetSw,
362 subnets,
363 destSw,
364 nextHops);
sanghob35a6192015-04-01 13:05:26 -0700365 if (!result) {
366 return false;
367 }
368
369 IpPrefix routerIp = config.getRouterIpAddress(destSw);
370 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIp, destSw, nextHops);
371 if (!result) {
372 return false;
373 }
374
375 // If the target switch is an edge router, then set IP rules for the router IP.
376 } else if (config.isEdgeRouter(targetSw)) {
377 IpPrefix routerIp = config.getRouterIpAddress(destSw);
378 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIp, destSw, nextHops);
379 if (!result) {
380 return false;
381 }
382
383 // If the target switch is an transit router, then set MPLS rules only.
384 } else if (config.isTransitRouter(targetSw)) {
385 result = rulePopulator.populateMplsRule(targetSw, destSw, nextHops);
386 if (!result) {
387 return false;
388 }
389 } else {
390 log.warn("The switch {} is neither an edge router nor a transit router.", targetSw);
391 return false;
392 }
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}