blob: cbad91072d43d528b7cdac1c216f8727e257b6d6 [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) {
sangho834e4b02015-05-01 09:38:25 -0700162 // When only the source device is defined, reinstall routes to all other devices
sangho20eff1d2015-04-13 15:15:58 -0700163 if (link.size() == 1) {
164 ECMPShortestPathGraph ecmpSpg = new ECMPShortestPathGraph(link.get(0), srManager);
165 if (populateEcmpRoutingRules(link.get(0), ecmpSpg)) {
166 currentEcmpSpgMap.put(link.get(0), ecmpSpg);
167 }
168 continue;
169 }
170 DeviceId src = link.get(0);
171 DeviceId dst = link.get(1);
172 ECMPShortestPathGraph ecmpSpg = new ECMPShortestPathGraph(dst, srManager);
173
174 currentEcmpSpgMap.put(dst, ecmpSpg);
175 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
176 ecmpSpg.getAllLearnedSwitchesAndVia();
177 for (Integer itrIdx : switchVia.keySet()) {
178 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
179 switchVia.get(itrIdx);
180 for (DeviceId targetSw : swViaMap.keySet()) {
181 if (!targetSw.equals(src)) {
182 continue;
183 }
184 Set<DeviceId> nextHops = new HashSet<>();
185 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
186 if (via.isEmpty()) {
187 nextHops.add(dst);
188 } else {
189 nextHops.add(via.get(0));
190 }
191 }
192 if (!populateEcmpRoutingRulePartial(targetSw, dst, nextHops)) {
193 return false;
194 }
195 }
196 }
197 }
198 return true;
199 }
200
201 private Set<ArrayList<DeviceId>> computeDamagedRoutes(Link linkFail) {
202
203 Set<ArrayList<DeviceId>> routes = new HashSet<>();
204
205 for (Device sw : srManager.deviceService.getDevices()) {
206 if (srManager.mastershipService.
207 getLocalRole(sw.id()) != MastershipRole.MASTER) {
208 continue;
209 }
210 ECMPShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(sw.id());
211 if (ecmpSpg == null) {
212 log.error("No existing ECMP path for switch {}", sw.id());
213 continue;
214 }
215 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
216 ecmpSpg.getAllLearnedSwitchesAndVia();
217 for (Integer itrIdx : switchVia.keySet()) {
218 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
219 switchVia.get(itrIdx);
220 for (DeviceId targetSw : swViaMap.keySet()) {
221 DeviceId destSw = sw.id();
222 Set<ArrayList<DeviceId>> subLinks =
223 computeLinks(targetSw, destSw, swViaMap);
224 for (ArrayList<DeviceId> alink: subLinks) {
225 if (alink.get(0).equals(linkFail.src().deviceId()) &&
226 alink.get(1).equals(linkFail.dst().deviceId())) {
227 ArrayList<DeviceId> aRoute = new ArrayList<>();
228 aRoute.add(targetSw);
229 aRoute.add(destSw);
230 routes.add(aRoute);
231 break;
232 }
233 }
234 }
235 }
236 }
237
238 return routes;
239 }
240
241 private Set<ArrayList<DeviceId>> computeRouteChange() {
242
243 Set<ArrayList<DeviceId>> routes = new HashSet<>();
244
245 for (Device sw : srManager.deviceService.getDevices()) {
246 if (srManager.mastershipService.
247 getLocalRole(sw.id()) != MastershipRole.MASTER) {
248 continue;
249 }
250 ECMPShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(sw.id());
251 if (ecmpSpg == null) {
252 log.debug("No existing ECMP path for Switch {}", sw.id());
253 ArrayList<DeviceId> route = new ArrayList<>();
254 route.add(sw.id());
255 routes.add(route);
256 continue;
257 }
258 ECMPShortestPathGraph newEcmpSpg =
259 new ECMPShortestPathGraph(sw.id(), srManager);
260 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
261 ecmpSpg.getAllLearnedSwitchesAndVia();
262 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchViaUpdated =
263 newEcmpSpg.getAllLearnedSwitchesAndVia();
264
265 for (Integer itrIdx : switchVia.keySet()) {
266 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
267 switchVia.get(itrIdx);
268 for (DeviceId srcSw : swViaMap.keySet()) {
269 ArrayList<ArrayList<DeviceId>> via1 = swViaMap.get(srcSw);
270 ArrayList<ArrayList<DeviceId>> via2 = getVia(switchViaUpdated, srcSw);
271 if (!via1.equals(via2)) {
272 ArrayList<DeviceId> route = new ArrayList<>();
273 route.add(srcSw);
274 route.add(sw.id());
275 routes.add(route);
276 }
277 }
278 }
279
280 }
281
282 return routes;
283 }
284
285 private ArrayList<ArrayList<DeviceId>> getVia(HashMap<Integer, HashMap<DeviceId,
286 ArrayList<ArrayList<DeviceId>>>> switchVia, DeviceId srcSw) {
287 for (Integer itrIdx : switchVia.keySet()) {
288 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
289 switchVia.get(itrIdx);
290 if (swViaMap.get(srcSw) == null) {
291 continue;
292 } else {
293 return swViaMap.get(srcSw);
294 }
295 }
296
297 return new ArrayList<>();
298 }
299
300 private Set<ArrayList<DeviceId>> computeLinks(DeviceId src,
301 DeviceId dst,
302 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> viaMap) {
303 Set<ArrayList<DeviceId>> subLinks = Sets.newHashSet();
304 for (ArrayList<DeviceId> via : viaMap.get(src)) {
305 DeviceId linkSrc = src;
306 DeviceId linkDst = dst;
307 for (DeviceId viaDevice: via) {
308 ArrayList<DeviceId> link = new ArrayList<>();
309 linkDst = viaDevice;
310 link.add(linkSrc);
311 link.add(linkDst);
312 subLinks.add(link);
313 linkSrc = viaDevice;
314 }
315 ArrayList<DeviceId> link = new ArrayList<>();
316 link.add(linkSrc);
317 link.add(dst);
318 subLinks.add(link);
319 }
320
321 return subLinks;
322 }
323
324 private boolean populateEcmpRoutingRules(DeviceId destSw,
sanghob35a6192015-04-01 13:05:26 -0700325 ECMPShortestPathGraph ecmpSPG) {
326
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700327 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia = ecmpSPG
328 .getAllLearnedSwitchesAndVia();
sanghob35a6192015-04-01 13:05:26 -0700329 for (Integer itrIdx : switchVia.keySet()) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700330 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap = switchVia
331 .get(itrIdx);
sanghob35a6192015-04-01 13:05:26 -0700332 for (DeviceId targetSw : swViaMap.keySet()) {
sanghob35a6192015-04-01 13:05:26 -0700333 Set<DeviceId> nextHops = new HashSet<>();
334
335 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
336 if (via.isEmpty()) {
337 nextHops.add(destSw);
338 } else {
339 nextHops.add(via.get(0));
340 }
341 }
342 if (!populateEcmpRoutingRulePartial(targetSw, destSw, nextHops)) {
343 return false;
344 }
345 }
346 }
347
348 return true;
349 }
350
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700351 private boolean populateEcmpRoutingRulePartial(DeviceId targetSw,
352 DeviceId destSw,
353 Set<DeviceId> nextHops) {
sanghob35a6192015-04-01 13:05:26 -0700354 boolean result;
355
356 if (nextHops.isEmpty()) {
357 nextHops.add(destSw);
358 }
359
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700360 // If both target switch and dest switch are edge routers, then set IP
361 // rule
sanghob35a6192015-04-01 13:05:26 -0700362 // for both subnet and router IP.
sangho666cd6d2015-04-14 16:27:13 -0700363 if (config.isEdgeDevice(targetSw) && config.isEdgeDevice(destSw)) {
364 List<Ip4Prefix> subnets = config.getSubnets(destSw);
Srikanth Vavilapalli4db76e32015-04-07 15:12:32 -0700365 result = rulePopulator.populateIpRuleForSubnet(targetSw,
366 subnets,
367 destSw,
368 nextHops);
sanghob35a6192015-04-01 13:05:26 -0700369 if (!result) {
370 return false;
371 }
372
sangho666cd6d2015-04-14 16:27:13 -0700373 Ip4Address routerIp = config.getRouterIp(destSw);
374 IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH);
375 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
sanghob35a6192015-04-01 13:05:26 -0700376 if (!result) {
377 return false;
378 }
379
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700380 // TODO: If the target switch is an edge router, then set IP rules for the router IP.
sangho666cd6d2015-04-14 16:27:13 -0700381 } else if (config.isEdgeDevice(targetSw)) {
382 Ip4Address routerIp = config.getRouterIp(destSw);
383 IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH);
384 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
sanghob35a6192015-04-01 13:05:26 -0700385 if (!result) {
386 return false;
387 }
388
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700389 // TODO: If the target switch is an transit router, then set MPLS rules only.
sangho666cd6d2015-04-14 16:27:13 -0700390 } else if (!config.isEdgeDevice(targetSw)) {
sanghob35a6192015-04-01 13:05:26 -0700391 result = rulePopulator.populateMplsRule(targetSw, destSw, nextHops);
392 if (!result) {
393 return false;
394 }
sanghob35a6192015-04-01 13:05:26 -0700395 }
396
397 return true;
398 }
399
400 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700401 * Populates table miss entries for all tables, and pipeline rules for VLAN
402 * and TACM tables.
sanghob35a6192015-04-01 13:05:26 -0700403 *
404 * @param deviceId Switch ID to set the rules
405 */
406 public void populateTtpRules(DeviceId deviceId) {
sanghob35a6192015-04-01 13:05:26 -0700407 rulePopulator.populateTableVlan(deviceId);
408 rulePopulator.populateTableTMac(deviceId);
409 }
410
411 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700412 * Start the flow rule population process if it was never started. The
413 * process finishes successfully when all flow rules are set and stops with
414 * ABORTED status when any groups required for flows is not set yet.
sanghob35a6192015-04-01 13:05:26 -0700415 */
416 public void startPopulationProcess() {
417 synchronized (populationStatus) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700418 if (populationStatus == Status.IDLE
419 || populationStatus == Status.SUCCEEDED) {
sanghob35a6192015-04-01 13:05:26 -0700420 populationStatus = Status.STARTED;
421 populateAllRoutingRules();
422 }
423 }
424 }
425
426 /**
427 * Resume the flow rule population process if it was aborted for any reason.
428 * Mostly the process is aborted when the groups required are not set yet.
429 */
430 public void resumePopulationProcess() {
431 synchronized (populationStatus) {
432 if (populationStatus == Status.ABORTED) {
433 populationStatus = Status.STARTED;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700434 // TODO: we need to restart from the point aborted instead of
435 // restarting.
sanghob35a6192015-04-01 13:05:26 -0700436 populateAllRoutingRules();
437 }
438 }
439 }
440}