blob: 9eea76287d8357e26abe226d76f7d366748e6154 [file] [log] [blame]
Sangho Shin177e64f2014-10-28 17:49:23 -07001package net.onrc.onos.apps.segmentrouting;
2
3import java.util.ArrayList;
4import java.util.List;
5
6import net.floodlightcontroller.core.IOF13Switch;
7import net.floodlightcontroller.core.IOF13Switch.NeighborSet;
8import net.onrc.onos.core.drivermanager.OFSwitchImplDellOSR;
9import net.onrc.onos.core.topology.Link;
10import net.onrc.onos.core.topology.Switch;
11import net.onrc.onos.core.util.Dpid;
12import net.onrc.onos.core.util.PortNumber;
13
14import org.slf4j.Logger;
15import org.slf4j.LoggerFactory;
16
17public class SegmentRoutingTunnel {
18
19 private static final Logger log = LoggerFactory
20 .getLogger(SegmentRoutingTunnel.class);
21
22 private String tunnelId;
23 private List<Integer> labelIds;
24 private List<TunnelRouteInfo> routes;
25 private SegmentRoutingManager srManager;
26
27 private final int MAX_NUM_LABELS = 3;
28
29 public SegmentRoutingTunnel(SegmentRoutingManager srm, String tid,
30 List<Integer> labelIds) {
31 this.srManager = srm;
32 this.tunnelId = tid;
33 this.labelIds = labelIds;
34 this.routes = new ArrayList<TunnelRouteInfo>();
35 }
36
37 public String getTunnelId(){
38 return this.tunnelId;
39 }
40
41 public List<Integer> getLabelids() {
42 return this.labelIds;
43 }
44
45 public List<TunnelRouteInfo> getRoutes(){
46 return this.routes;
47 }
48
49 public boolean createTunnel() {
50
51 if (labelIds.isEmpty() || labelIds.size() < 2) {
52 log.debug("Wrong tunnel information");
53 return false;
54 }
55
56 List<String> Ids = new ArrayList<String>();
57 for (Integer label : labelIds) {
58 Ids.add(label.toString());
59 }
60
61 List<TunnelRouteInfo> stitchingRule = getStitchingRule(Ids);
62 if (stitchingRule == null) {
63 log.debug("Failed to get a tunnel rule.");
64 return false;
65 }
66
67 for (TunnelRouteInfo route: stitchingRule) {
68 NeighborSet ns = new NeighborSet();
69 for (Dpid dpid: route.getFwdSwDpid())
70 ns.addDpid(dpid);
71
72 printTunnelInfo(route.srcSwDpid, tunnelId, route.getRoute(), ns);
73 int groupId = -1;
74 if ((groupId =createGroupsForTunnel(tunnelId, route, ns)) < 0) {
75 log.debug("Failed to create a tunnel at driver.");
76 return false;
77 }
78 route.setGroupId(groupId);
79 }
80
81 this.routes = stitchingRule;
82
83 return true;
84 }
85
86 public boolean removeTunnel() {
87
88 for (TunnelRouteInfo route: routes) {
89 IOF13Switch sw13 = srManager.getIOF13Switch(route.srcSwDpid);
90 if (sw13 == null) {
91 log.warn("Cannot find the switch", route.srcSwDpid);
92 return false;
93 }
94 else {
95 if (!sw13.removeGroup(route.getGroupId())) {
96 log.warn("Faied to remove the tunnel {} at driver",
97 tunnelId);
98 return false;
99 }
100 }
101 }
102
103 return true;
104 }
105
106
107 /**
108 * Create groups for the tunnel
109 *
110 * @param tunnelId tunnel ID
111 * @param routeInfo label stacks for the tunnel
112 * @param ns NeighborSet to forward packets
113 * @return group ID, return -1 if it fails
114 */
115 private int createGroupsForTunnel(String tunnelId, TunnelRouteInfo routeInfo,
116 NeighborSet ns) {
117
118 IOF13Switch targetSw = srManager.getIOF13Switch(routeInfo.srcSwDpid);
119
120 if (targetSw == null) {
121 log.debug("Switch {} is gone.", routeInfo.srcSwDpid);
122 return -1;
123 }
124
125 List<Integer> Ids = new ArrayList<Integer>();
126 for (String IdStr: routeInfo.route)
127 Ids.add(Integer.parseInt(IdStr));
128
129 List<PortNumber> ports = getPortsFromNeighborSet(routeInfo.srcSwDpid, ns);
130 int groupId = targetSw.createGroup(Ids, ports);
131
132 return groupId;
133 }
134
135
136 /**
137 * Split the nodes IDs into multiple tunnel if Segment Stitching is required.
138 * We assume that the first node ID is the one of source router, and the last
139 * node ID is that of the destination router.
140 *
141 * @param route list of node IDs
142 * @return List of the TunnelRoutInfo
143 */
144 private List<TunnelRouteInfo> getStitchingRule(List<String> route) {
145
146 if (route.isEmpty() || route.size() < 3)
147 return null;
148
149 List<TunnelRouteInfo> rules = new ArrayList<TunnelRouteInfo>();
150
151 Switch srcSw = srManager.getSwitchFromNodeId(route.get(0));
152 if (srcSw == null) {
153 log.warn("Switch is not found for Node SID {}", route.get(0));
154 return null;
155 }
156 String srcDpid = srcSw.getDpid().toString();
157
158 int i = 0;
159 TunnelRouteInfo routeInfo = new TunnelRouteInfo();
160 boolean checkNeighbor = false;
161 String prevAdjacencySid = null;
162 String prevNodeId = null;
163
164 for (String nodeId: route) {
165 // The first node ID is always the source router.
166 // We assume that the first ID cannot be an Adjacency SID.
167 if (i == 0) {
168 srcSw = srManager.getSwitchFromNodeId(nodeId);
169 if (srcDpid == null)
170 srcDpid = srcSw.getDpid().toString();
171 routeInfo.setSrcDpid(srcDpid);
172 checkNeighbor = true;
173 i++;
174 }
175 // if this is the first node ID to put the label stack..
176 else if (i == 1) {
177 if (checkNeighbor) {
178 List<Dpid> fwdSws = getDpidIfNeighborOf(nodeId, srcSw);
179 // if nodeId is NOT the neighbor of srcSw..
180 if (fwdSws.isEmpty()) {
181 fwdSws = srManager.getForwardingSwitchForNodeId(srcSw,nodeId);
182 if (fwdSws == null || fwdSws.isEmpty()) {
183 log.warn("There is no route from node {} to node {}",
184 srcSw.getDpid(), nodeId);
185 return null;
186 }
187 routeInfo.addRoute(nodeId);
188 i++;
189 }
190 routeInfo.setFwdSwDpid(fwdSws);
191 // we check only the next node ID of the source router
192 checkNeighbor = false;
193 }
194 // if neighbor check is already done, then just add it
195 else {
196 routeInfo.addRoute(nodeId);
197 i++;
198 }
199 }
200 // if i > 1
201 else {
202 // If the adjacency SID is pushed and the next SID is the destination
203 // of the adjacency SID, then do not add the SID.
204 if (prevAdjacencySid != null) {
205 if (isAdjacencySidNeighborOf(prevNodeId, prevAdjacencySid, nodeId)) {
206 prevAdjacencySid = null;
207 prevNodeId = nodeId;
208 continue;
209 }
210 prevAdjacencySid = null;
211 }
212 routeInfo.addRoute(nodeId);
213 i++;
214 }
215
216 // If the adjacency ID is added the label stack,
217 // then we need to check if the next node is the destination of the adjacency SID
218 if (srManager.isAdjacencySid(nodeId))
219 prevAdjacencySid = nodeId;
220
221 // If the number of labels reaches the limit, start over the procedure
222 if (i == MAX_NUM_LABELS +1) {
223
224 rules.add(routeInfo);
225 routeInfo = new TunnelRouteInfo();
226
227 if (srManager.isAdjacencySid(nodeId)) {
228 // If the previous sub tunnel finishes with adjacency SID,
229 // then we need to start the procedure from the adjacency
230 // destination ID.
231 List<Switch> destNodeList =
232 getAdjacencyDestinationNode(prevNodeId, nodeId);
233 if (destNodeList == null || destNodeList.isEmpty()) {
234 log.warn("Cannot find destination node for adjacencySID {}",
235 nodeId);
236 return null;
237 }
238 // If the previous sub tunnel finishes with adjacency SID with
239 // multiple ports, then we need to remove the adjacency Sid
240 // from the previous sub tunnel and start the new sub tunnel
241 // with the adjacency Sid. Technically, the new subtunnel
242 // forward packets to the port assigned to the adjacency Sid
243 // and the label stack starts with the next ID.
244 // This is to avoid to install new policy rule to multiple nodes for stitching when the
245 // adjacency Sid that has more than one port.
246 if (destNodeList.size() > 1) {
247 rules.get(rules.size()-1).route.remove(nodeId);
248 srcSw = srManager.getSwitchFromNodeId(prevNodeId);
249 List<Dpid> fwdSws = getDpidIfNeighborOf(nodeId, srcSw);
250 routeInfo.setFwdSwDpid(fwdSws);
251 routeInfo.setSrcDpid(srcSw.getDpid().toString());
252 i = 1;
253 checkNeighbor = false;
254 continue;
255 }
256 else {
257 srcSw = destNodeList.get(0);
258 }
259 }
260 else {
261 srcSw = srManager.getSwitchFromNodeId(nodeId);
262 }
263 srcDpid = srcSw.getDpid().toString();
264 routeInfo.setSrcDpid(srcDpid);
265 i = 1;
266 checkNeighbor = true;
267 }
268
269 if (prevAdjacencySid == null)
270 prevNodeId = nodeId;
271 }
272
273
274 if (i < MAX_NUM_LABELS+1 && (routeInfo.getFwdSwDpid() != null &&
275 !routeInfo.getFwdSwDpid().isEmpty())) {
276 rules.add(routeInfo);
277 // NOTE: empty label stack can happen, but forwarding destination should be set
278 }
279
280 return rules;
281 }
282
283
284 /**
285 * Get port numbers of the neighbor set.
286 * If ECMP in transit router is not supported, then only one port should be returned
287 * regardless of number of nodes in neighbor set.
288 *
289 * @param srcSwDpid source switch
290 * @param ns Neighbor set of the switch
291 * @return List of PortNumber, null if not found
292 */
293 private List<PortNumber> getPortsFromNeighborSet(String srcSwDpid, NeighborSet ns) {
294
295 List<PortNumber> portList = new ArrayList<PortNumber>();
296 Switch srcSwitch = srManager.getSwitch(srcSwDpid);
297 if (srcSwitch == null)
298 return null;
299 IOF13Switch srcSwitch13 =
300 srManager.getIOF13Switch(srcSwitch.getDpid().toString());
301
302 for (Dpid neighborDpid: ns.getDpids()) {
303 if (srcSwitch13 instanceof OFSwitchImplDellOSR &&
304 ns.getDpids().size() == 1) {
305 Switch dstSwitch = srManager.getSwitch(neighborDpid.toString());
306 if (srManager.isTransitRouter(srcSwitch) &&
307 srManager.isTransitRouter(dstSwitch)) {
308 Link link = srcSwitch.getLinkToNeighbor(neighborDpid);
309 portList.add(link.getSrcPort().getNumber());
310 break;
311 }
312 }
313 else {
314 for (Link link: srcSwitch.getOutgoingLinks()) {
315 if (link.getDstSwitch().getDpid().equals(neighborDpid)) {
316 portList.add(link.getSrcPort().getNumber());
317 }
318 }
319 }
320 }
321
322 return portList;
323 }
324
325
326 /**
327 * Get the DPID of the router with node ID IF the node ID is the neighbor of the
328 * Switch srcSW.
329 * If the nodeId is the adjacency Sid, then it returns the destination router DPIDs.
330 *
331 * @param nodeId Node ID to check
332 * @param srcSw target Switch
333 * @return List of DPID of nodeId, empty list if the nodeId is not the neighbor of srcSW
334 */
335 private List<Dpid> getDpidIfNeighborOf(String nodeId, Switch srcSw) {
336 List<Dpid> fwdSws = new ArrayList<Dpid>();
337 // if the nodeID is the adjacency ID, then we need to regard it as the
338 // neighbor node ID and need to return the destination router DPID(s)
339 if (srManager.isAdjacencySid(nodeId)) {
340 String srcNodeId = srManager.getMplsLabel(srcSw.getDpid().toString());
341 List<Integer> ports =
342 srManager.getAdacencyPorts(Integer.parseInt(srcNodeId),
343 Integer.parseInt(nodeId));
344
345 for (Integer port: ports) {
346 for (Link link: srcSw.getOutgoingLinks()) {
347 if (link.getSrcPort().getPortNumber().value() == port) {
348 fwdSws.add(link.getDstSwitch().getDpid());
349 }
350 }
351 }
352 }
353 else {
354 List<Dpid> fwdSwDpids =
355 srManager.getForwardingSwitchForNodeId(srcSw,nodeId);
356 if (fwdSwDpids == null || fwdSwDpids.isEmpty()) {
357 log.warn("There is no route from node {} to node {}",
358 srcSw.getDpid(), nodeId);
359 return fwdSws;
360 }
361
362 for (Dpid dpid: fwdSwDpids) {
363 String id = srManager.getMplsLabel(dpid.toString()).toString();
364 if (id.equals(nodeId)) {
365 fwdSws.add(dpid);
366 break;
367 }
368 }
369 }
370
371 return fwdSws;
372 }
373
374
375
376 /**
377 * Check whether the router with preNodeid is connected to the router
378 * with nodeId via adjacencySid or not
379 *
380 * @param prevNodeId the router node ID of the adjacencySid
381 * @param adjacencySid adjacency SID
382 * @param nodeId the router node ID to check
383 * @return
384 */
385 private boolean isAdjacencySidNeighborOf(String prevNodeId, String adjacencySid, String nodeId) {
386
387 List<Integer> ports =
388 srManager.getAdacencyPorts(Integer.valueOf(prevNodeId),
389 Integer.valueOf(adjacencySid));
390
391 if (ports == null) {
392 log.warn("Cannot find ports for node ID {} and adjacencySID ",
393 prevNodeId, adjacencySid);
394 return false;
395 }
396
397 for (Integer port: ports) {
398 Switch sw = srManager.getSwitchFromNodeId(prevNodeId);
399 for (Link link: sw.getOutgoingLinks()) {
400 if (link.getSrcPort().getPortNumber().value() == port) {
401 String linkDstDpid = link.getDstPort().getDpid().toString();
402 String linkDstId = srManager.getMplsLabel(linkDstDpid);
403 if (linkDstId.equals(nodeId)) {
404 return true;
405 }
406 }
407 }
408 }
409
410 return false;
411 }
412
413
414 /**
415 * Get the destination Nodes of the adjacency Sid
416 *
417 * @param nodeId node ID of the adjacency Sid
418 * @param adjacencySid adjacency Sid
419 * @return List of Switch, empty list if not found
420 */
421 private List<Switch> getAdjacencyDestinationNode(String nodeId, String adjacencySid) {
422 List<Switch> dstSwList = new ArrayList<Switch>();
423
424 List<Integer> ports = srManager.getAdacencyPorts(Integer.valueOf(nodeId),
425 Integer.valueOf(adjacencySid));
426
427 Switch srcSw = srManager.getSwitchFromNodeId(nodeId);
428 for (Integer port: ports) {
429 for (Link link: srcSw.getOutgoingLinks()) {
430 if (link.getSrcPort().getPortNumber().value() == port) {
431 dstSwList.add(link.getDstSwitch());
432 }
433 }
434 }
435
436 return dstSwList;
437
438 }
439
440
441
442
443 /**
444 * print tunnel info - used only for debugging.
445 * @param targetSw
446 *
447 * @param fwdSwDpids
448 * @param ids
449 * @param tunnelId
450 */
451 private void printTunnelInfo(String targetSw, String tunnelId,
452 List<String> ids, NeighborSet ns) {
453 StringBuilder logStr = new StringBuilder("In switch " +
454 targetSw + ", create a tunnel " + tunnelId + " " + " of push ");
455 for (String id: ids)
456 logStr.append(id + "-");
457 logStr.append(" output to ");
458 for (Dpid dpid: ns.getDpids())
459 logStr.append(dpid + " - ");
460
461 log.debug(logStr.toString());
462
463 }
464
465 public class TunnelRouteInfo {
466
467 private String srcSwDpid;
468 private List<Dpid> fwdSwDpids;
469 private List<String> route;
470 private int gropuId;
471
472 public TunnelRouteInfo() {
473 fwdSwDpids = new ArrayList<Dpid>();
474 route = new ArrayList<String>();
475 }
476
477 private void setSrcDpid(String dpid) {
478 this.srcSwDpid = dpid;
479 }
480
481 private void setFwdSwDpid(List<Dpid> dpid) {
482 this.fwdSwDpids = dpid;
483 }
484
485 private void addRoute(String id) {
486 route.add(id);
487 }
488
489 private void setGroupId(int groupId) {
490 this.gropuId = groupId;
491 }
492
493 public String getSrcSwDpid() {
494 return this.srcSwDpid;
495 }
496
497 public List<Dpid> getFwdSwDpid() {
498 return this.fwdSwDpids;
499 }
500
501 public List<String> getRoute() {
502 return this.route;
503 }
504
505 public int getGroupId() {
506 return this.gropuId;
507 }
508 }
509
510}