blob: 4b3272de33dc629f8c840302f0394a030d971637 [file] [log] [blame]
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001/*
2 * Copyright 2016-present Open Networking Foundation
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 */
16
17package org.onosproject.segmentrouting.pwaas;
18
19import com.google.common.collect.Iterables;
20import com.google.common.collect.Lists;
21import org.apache.commons.lang3.RandomUtils;
22import org.onlab.packet.Ethernet;
23import org.onlab.packet.MacAddress;
24import org.onlab.packet.MplsLabel;
25import org.onlab.packet.VlanId;
26import org.onlab.util.KryoNamespace;
27import org.onosproject.net.ConnectPoint;
28import org.onosproject.net.DefaultLink;
29import org.onosproject.net.DeviceId;
30import org.onosproject.net.Link;
31import org.onosproject.net.Path;
32import org.onosproject.net.PortNumber;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080033import org.onosproject.net.flow.DefaultTrafficSelector;
34import org.onosproject.net.flow.DefaultTrafficTreatment;
35import org.onosproject.net.flow.TrafficSelector;
36import org.onosproject.net.flow.TrafficTreatment;
37import org.onosproject.net.flow.criteria.Criteria;
38import org.onosproject.net.flowobjective.DefaultFilteringObjective;
39import org.onosproject.net.flowobjective.DefaultForwardingObjective;
40import org.onosproject.net.flowobjective.DefaultNextObjective;
41import org.onosproject.net.flowobjective.DefaultObjectiveContext;
42import org.onosproject.net.flowobjective.FilteringObjective;
43import org.onosproject.net.flowobjective.ForwardingObjective;
44import org.onosproject.net.flowobjective.NextObjective;
45import org.onosproject.net.flowobjective.Objective;
46import org.onosproject.net.flowobjective.ObjectiveContext;
47import org.onosproject.net.flowobjective.ObjectiveError;
48import org.onosproject.segmentrouting.SegmentRoutingManager;
49import org.onosproject.segmentrouting.SegmentRoutingService;
50import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080051import org.onosproject.store.serializers.KryoNamespaces;
52import org.onosproject.store.service.ConsistentMap;
53import org.onosproject.store.service.DistributedSet;
54import org.onosproject.store.service.Serializer;
55import org.onosproject.store.service.Versioned;
56import org.slf4j.Logger;
57import org.slf4j.LoggerFactory;
58
59import java.util.ArrayList;
60import java.util.List;
61import java.util.Set;
62import java.util.concurrent.CompletableFuture;
63import java.util.stream.Collectors;
64
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -080065import static org.onosproject.net.flowobjective.ForwardingObjective.Flag.VERSATILE;
66import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Pipeline.INITIATION;
67import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Pipeline.TERMINATION;
68import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Result.*;
69import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Direction.FWD;
70import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Direction.REV;
71
72/**
73 * Handles pwaas related events.
74 */
75public class DefaultL2TunnelHandler implements L2TunnelHandler {
76
77 private static final Logger log = LoggerFactory.getLogger(DefaultL2TunnelHandler.class);
78
79 private final SegmentRoutingManager srManager;
80 /**
81 * To store the next objectives related to the initiation.
82 */
83 private final ConsistentMap<String, NextObjective> l2InitiationNextObjStore;
84 /**
85 * To store the next objectives related to the termination.
86 */
87 private final ConsistentMap<String, NextObjective> l2TerminationNextObjStore;
88
89 /**
90 * To store policies.
91 */
92 private final ConsistentMap<String, L2TunnelPolicy> l2PolicyStore;
93
94 /**
95 * To store tunnels.
96 */
97 private final ConsistentMap<String, L2Tunnel> l2TunnelStore;
98
99 private final KryoNamespace.Builder l2TunnelKryo;
100
101 /**
102 * Contains transport vlans used for spine-leaf pseudowires.
103 */
104 private final DistributedSet<VlanId> vlanStore;
105
106 /**
107 * Used for determining transport vlans for leaf-spine.
108 */
109 private short transportVlanUpper = 4093, transportVlanLower = 3500;
110
111 private static final VlanId UNTAGGED_TRANSPORT_VLAN = VlanId.vlanId((short) 4094);
112
113 /**
114 * Create a l2 tunnel handler for the deploy and
115 * for the tear down of pseudo wires.
116 *
117 * @param segmentRoutingManager the segment routing manager
118 */
119 public DefaultL2TunnelHandler(SegmentRoutingManager segmentRoutingManager) {
120 srManager = segmentRoutingManager;
121 l2TunnelKryo = new KryoNamespace.Builder()
122 .register(KryoNamespaces.API)
123 .register(L2Tunnel.class,
124 L2TunnelPolicy.class,
125 DefaultL2Tunnel.class,
126 DefaultL2TunnelPolicy.class,
127 L2Mode.class,
128 MplsLabel.class,
129 VlanId.class,
130 ConnectPoint.class);
131
132 l2InitiationNextObjStore = srManager.
133 storageService.
134 <String, NextObjective>consistentMapBuilder().
135 withName("onos-l2initiation-nextobj-store").
136 withSerializer(Serializer.using(l2TunnelKryo.build())).
137 build();
138
139 l2TerminationNextObjStore = srManager.storageService.
140 <String, NextObjective>consistentMapBuilder()
141 .withName("onos-l2termination-nextobj-store")
142 .withSerializer(Serializer.using(l2TunnelKryo.build()))
143 .build();
144
145 l2PolicyStore = srManager.storageService
146 .<String, L2TunnelPolicy>consistentMapBuilder()
147 .withName("onos-l2-policy-store")
148 .withSerializer(Serializer.using(l2TunnelKryo.build()))
149 .build();
150
151 l2TunnelStore = srManager.storageService
152 .<String, L2Tunnel>consistentMapBuilder()
153 .withName("onos-l2-tunnel-store")
154 .withSerializer(Serializer.using(l2TunnelKryo.build()))
155 .build();
156
157 vlanStore = srManager.storageService.<VlanId>setBuilder()
158 .withName("onos-transport-vlan-store")
159 .withSerializer(Serializer.using(
160 new KryoNamespace.Builder()
161 .register(KryoNamespaces.API)
162 .build()))
163 .build()
164 .asDistributedSet();
165 }
166
167 /**
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800168 * Used by manager only in initialization.
169 */
170 @Override
171 public void init() {
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800172 // Since we have no pseudowires in netcfg there
173 // is nothing to do in initialization.
174 // I leave it here because potentially we might need to
175 // use it in the future.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800176 }
177
178 /**
179 * Returns all L2 Policies.
180 *
181 * @return List of policies
182 */
183 @Override
184 public List<L2TunnelPolicy> getL2Policies() {
185
186 return new ArrayList<>(l2PolicyStore
187 .values()
188 .stream()
189 .map(Versioned::value)
190 .collect(Collectors.toList()));
191
192 }
193
194 /**
195 * Returns all L2 Tunnels.
196 *
197 * @return List of tunnels.
198 */
199 @Override
200 public List<L2Tunnel> getL2Tunnels() {
201
202 return new ArrayList<>(l2TunnelStore
203 .values()
204 .stream()
205 .map(Versioned::value)
206 .collect(Collectors.toList()));
207
208 }
209
210 @Override
211 public void processLinkDown(Link link) {
212
213 List<L2Tunnel> tunnels = getL2Tunnels();
214 List<L2TunnelPolicy> policies = getL2Policies();
215
216 // determine affected pseudowires and update them at once
217 Set<L2TunnelDescription> pwToUpdate = tunnels
218 .stream()
219 .filter(tun -> tun.pathUsed().contains(link))
220 .map(l2Tunnel -> {
221 L2TunnelPolicy policy = null;
222 for (L2TunnelPolicy l2Policy : policies) {
223 if (l2Policy.tunnelId() == l2Tunnel.tunnelId()) {
224 policy = l2Policy;
225 break;
226 }
227 }
228
229 return new DefaultL2TunnelDescription(l2Tunnel, policy);
230 })
231 .collect(Collectors.toSet());
232
233
234 log.info("Pseudowires affected by link failure : {}, rerouting them...", pwToUpdate);
235
236 // update all pseudowires
237 pwToUpdate.forEach(tun -> updatePw(tun, tun));
238 }
239
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800240 /**
241 * Returns the new vlan id for an ingress point of a
242 * pseudowire. For double tagged, it is the outer,
243 * For single tagged it is the single tag, and for
244 * inner it is None.
245 *
246 * @param ingressOuter vlanid of ingress outer
247 * @param ingressInner vlanid of ingress inner
248 * @param egressOuter vlanid of egress outer
249 * @param egressInner vlanid of egress inner
250 * @return returns the vlan id which will be installed at vlan table 1.
251 */
252 private VlanId determineEgressVlan(VlanId ingressOuter, VlanId ingressInner,
253 VlanId egressOuter, VlanId egressInner) {
254
255 // validity of vlan combinations was checked at verifyPseudowire
256 if (!(ingressOuter.equals(VlanId.NONE))) {
257 return egressOuter;
258 } else if (!(ingressInner.equals(VlanId.NONE))) {
259 return egressInner;
260 } else {
261 return VlanId.vlanId("None");
262 }
263 }
264
265 /**
266 * Determines vlan used for transporting the pw traffic.
267 *
268 * Leaf-Leaf traffic is transferred untagged, thus we choose the UNTAGGED_TRANSPORT_VLAN
269 * and also make sure to add the popVlan instruction.
270 * For spine-leaf pws we choose the highest vlan value available from a certain range.
271 *
272 * @param spinePw if the pw is leaf-spine.
273 * @return The vlan id chossen to transport this pseudowire. If vlan is UNTAGGED_TRANSPORT_VLAN
274 * then the pw is transported untagged.
275 */
276 private VlanId determineTransportVlan(boolean spinePw) {
277
278 if (!spinePw) {
279
280 log.info("Untagged transport with internal vlan {} for pseudowire!", UNTAGGED_TRANSPORT_VLAN);
281 return UNTAGGED_TRANSPORT_VLAN;
282 } else {
283 for (short i = transportVlanUpper; i > transportVlanLower; i--) {
284
285 VlanId vlanToUse = VlanId.vlanId((short) i);
286 if (!vlanStore.contains(vlanToUse)) {
287
288 vlanStore.add(vlanToUse);
289 log.info("Transport vlan {} for pseudowire!", vlanToUse);
290 return vlanToUse;
291 }
292 }
293
294 log.info("No available transport vlan found, pseudowire traffic will be carried untagged " +
295 "with internal vlan {}!", UNTAGGED_TRANSPORT_VLAN);
296 return UNTAGGED_TRANSPORT_VLAN;
297 }
298 }
299
300 /**
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800301 * Adds a single pseudowire.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800302 *
303 * @param pw The pseudowire
304 * @param spinePw True if pseudowire is from leaf to spine
305 * @return result of pseudowire deployment
306 */
307 private Result deployPseudowire(L2TunnelDescription pw, boolean spinePw) {
308
309 Result result;
310 long l2TunnelId;
311
312 l2TunnelId = pw.l2Tunnel().tunnelId();
313
314 // The tunnel id cannot be 0.
315 if (l2TunnelId == 0) {
316 log.warn("Tunnel id id must be > 0");
317 return Result.ADDITION_ERROR;
318 }
319
320 // get path here, need to use the same for fwd and rev direction
321 List<Link> path = getPath(pw.l2TunnelPolicy().cP1(),
322 pw.l2TunnelPolicy().cP2());
323 if (path == null) {
324 log.info("Deploying process : No path between the connection points for pseudowire {}", l2TunnelId);
325 return WRONG_PARAMETERS;
326 }
327
328 Link fwdNextHop;
329 Link revNextHop;
330 if (!spinePw) {
331 if (path.size() != 2) {
332 log.info("Deploying process : Path between two leafs should have size of 2, for pseudowire {}",
333 l2TunnelId);
334 return INTERNAL_ERROR;
335 }
336
337 fwdNextHop = path.get(0);
338 revNextHop = reverseLink(path.get(1));
339 } else {
340 if (path.size() != 1) {
341 log.info("Deploying process : Path between leaf spine should equal to 1, for pseudowire {}",
342 l2TunnelId);
343 return INTERNAL_ERROR;
344 }
345
346 fwdNextHop = path.get(0);
347 revNextHop = reverseLink(path.get(0));
348 }
349
350 pw.l2Tunnel().setPath(path);
351 pw.l2Tunnel().setTransportVlan(determineTransportVlan(spinePw));
352
353 // next hops for next objectives
354
355 log.info("Deploying process : Establishing forward direction for pseudowire {}", l2TunnelId);
356
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800357 VlanId egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP1OuterTag(),
358 pw.l2TunnelPolicy().cP1InnerTag(),
359 pw.l2TunnelPolicy().cP2OuterTag(),
360 pw.l2TunnelPolicy().cP2InnerTag());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800361 // We establish the tunnel.
362 // result.nextId will be used in fwd
363 result = deployPseudoWireInit(pw.l2Tunnel(),
364 pw.l2TunnelPolicy().cP1(),
365 pw.l2TunnelPolicy().cP2(),
366 FWD,
367 fwdNextHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800368 spinePw,
369 egressVlan);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800370 if (result != SUCCESS) {
371 log.info("Deploying process : Error in deploying pseudowire initiation for CP1");
372 return Result.ADDITION_ERROR;
373 }
374
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800375 // We create the policy.
376 result = deployPolicy(l2TunnelId,
377 pw.l2TunnelPolicy().cP1(),
378 pw.l2TunnelPolicy().cP1InnerTag(),
379 pw.l2TunnelPolicy().cP1OuterTag(),
380 egressVlan,
381 result.nextId);
382 if (result != SUCCESS) {
383 log.info("Deploying process : Error in deploying pseudowire policy for CP1");
384 return Result.ADDITION_ERROR;
385 }
386
387 // We terminate the tunnel
388 result = deployPseudoWireTerm(pw.l2Tunnel(),
389 pw.l2TunnelPolicy().cP2(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800390 egressVlan,
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800391 FWD,
392 spinePw);
393
394 if (result != SUCCESS) {
395 log.info("Deploying process : Error in deploying pseudowire termination for CP1");
396 return Result.ADDITION_ERROR;
397
398 }
399
400 log.info("Deploying process : Establishing reverse direction for pseudowire {}", l2TunnelId);
401
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800402 egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP2OuterTag(),
403 pw.l2TunnelPolicy().cP2InnerTag(),
404 pw.l2TunnelPolicy().cP1OuterTag(),
405 pw.l2TunnelPolicy().cP1InnerTag());
406
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800407 // We establish the reverse tunnel.
408 result = deployPseudoWireInit(pw.l2Tunnel(),
409 pw.l2TunnelPolicy().cP2(),
410 pw.l2TunnelPolicy().cP1(),
411 REV,
412 revNextHop,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800413 spinePw,
414 egressVlan);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800415 if (result != SUCCESS) {
416 log.info("Deploying process : Error in deploying pseudowire initiation for CP2");
417 return Result.ADDITION_ERROR;
418 }
419
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800420
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800421 result = deployPolicy(l2TunnelId,
422 pw.l2TunnelPolicy().cP2(),
423 pw.l2TunnelPolicy().cP2InnerTag(),
424 pw.l2TunnelPolicy().cP2OuterTag(),
425 egressVlan,
426 result.nextId);
427 if (result != SUCCESS) {
428 log.info("Deploying process : Error in deploying policy for CP2");
429 return Result.ADDITION_ERROR;
430 }
431
432 result = deployPseudoWireTerm(pw.l2Tunnel(),
433 pw.l2TunnelPolicy().cP1(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800434 egressVlan,
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800435 REV,
436 spinePw);
437
438 if (result != SUCCESS) {
439 log.info("Deploying process : Error in deploying pseudowire termination for CP2");
440 return Result.ADDITION_ERROR;
441 }
442
443 log.info("Deploying process : Updating relevant information for pseudowire {}", l2TunnelId);
444
445 // Populate stores
446 l2TunnelStore.put(Long.toString(l2TunnelId), pw.l2Tunnel());
447 l2PolicyStore.put(Long.toString(l2TunnelId), pw.l2TunnelPolicy());
448
449 return Result.SUCCESS;
450 }
451
452 /**
453 * To deploy a number of pseudo wires.
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800454 *
455 * @param pwToAdd the set of pseudo wires to add
456 */
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800457 public void deploy(Set<L2TunnelDescription> pwToAdd) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800458
459 Result result;
460
461 for (L2TunnelDescription currentL2Tunnel : pwToAdd) {
462 ConnectPoint cp1 = currentL2Tunnel.l2TunnelPolicy().cP1();
463 ConnectPoint cp2 = currentL2Tunnel.l2TunnelPolicy().cP2();
464 long tunnelId = currentL2Tunnel.l2TunnelPolicy().tunnelId();
465
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800466
467 try {
468 // differentiate between leaf-leaf pseudowires and leaf-spine
469 // and pass the appropriate flag in them.
470 if (!srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
471 !srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
472 log.warn("Can not deploy pseudowire from spine to spine!");
473 result = Result.INTERNAL_ERROR;
474 } else if (srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
475 srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
476 log.info("Deploying a leaf-leaf pseudowire {}", tunnelId);
477 result = deployPseudowire(currentL2Tunnel, false);
478 } else {
479 log.info("Deploying a leaf-spine pseudowire {}", tunnelId);
480 result = deployPseudowire(currentL2Tunnel, true);
481 }
482 } catch (DeviceConfigNotFoundException e) {
483 log.error("Exception caught when deploying pseudowire", e.toString());
484 result = Result.INTERNAL_ERROR;
485 }
486
487 switch (result) {
488 case INTERNAL_ERROR:
489 log.warn("Could not deploy pseudowire {}, internal error!", tunnelId);
490 break;
491 case WRONG_PARAMETERS:
492 log.warn("Could not deploy pseudowire {}, wrong parameters!", tunnelId);
493 break;
494 case ADDITION_ERROR:
495 log.warn("Could not deploy pseudowire {}, error in populating rules!", tunnelId);
496 break;
497 default:
498 log.info("Pseudowire with {} succesfully deployed!", tunnelId);
499 break;
500 }
501 }
502 }
503
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800504 /**
505 * Helper function to update a pw.
506 * <p>
507 * Called upon configuration changes that update existing pseudowires and
508 * when links fail. Checking of mastership for CP1 is mandatory because it is
509 * called in multiple instances for both cases.
510 * <p>
511 * Meant to call asynchronously for various events, thus this call can not block and need
512 * to perform asynchronous operations.
513 * <p>
514 * For this reason error checking is omitted.
515 *
516 * @param oldPw the pseudo wire to remove
517 * @param newPw the pseudo wire to add
518 */
519 private void updatePw(L2TunnelDescription oldPw,
520 L2TunnelDescription newPw) {
521 ConnectPoint oldCp1 = oldPw.l2TunnelPolicy().cP1();
522 long tunnelId = oldPw.l2Tunnel().tunnelId();
523
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800524 // only determine if the new pseudowire is leaf-spine, because
525 // removal process is the same for both leaf-leaf and leaf-spine pws
526 boolean newPwSpine;
527 try {
528 newPwSpine = !srManager.deviceConfiguration().isEdgeDevice(newPw.l2TunnelPolicy().cP1().deviceId()) ||
529 !srManager.deviceConfiguration().isEdgeDevice(newPw.l2TunnelPolicy().cP2().deviceId());
530 } catch (DeviceConfigNotFoundException e) {
531 // if exception is caught treat the new pw as leaf-leaf
532 newPwSpine = false;
533 }
534
535 // copy the variable here because we need to use it in lambda thus it needs to be final
536 boolean finalNewPwSpine = newPwSpine;
537
538 log.info("Updating pseudowire {}", oldPw.l2Tunnel().tunnelId());
539
540 // The async tasks to orchestrate the next and forwarding update
541 CompletableFuture<ObjectiveError> fwdInitNextFuture = new CompletableFuture<>();
542 CompletableFuture<ObjectiveError> revInitNextFuture = new CompletableFuture<>();
543 CompletableFuture<ObjectiveError> fwdTermNextFuture = new CompletableFuture<>();
544 CompletableFuture<ObjectiveError> revTermNextFuture = new CompletableFuture<>();
545 CompletableFuture<ObjectiveError> fwdPwFuture = new CompletableFuture<>();
546 CompletableFuture<ObjectiveError> revPwFuture = new CompletableFuture<>();
547
548 // first delete all information from our stores, we can not do it asynchronously
549 l2PolicyStore.remove(Long.toString(tunnelId));
550
551 // grab the old l2 tunnel from the store, since it carries information which is not exposed
552 // to the user configuration and set it to oldPw.
553 oldPw.setL2Tunnel(l2TunnelStore.get(Long.toString(tunnelId)).value());
554 VlanId transportVlan = l2TunnelStore.get(Long.toString(tunnelId)).value().transportVlan();
555 l2TunnelStore.remove(Long.toString(tunnelId));
556
557 // remove the reserved transport vlan, if one is used
558 if (!transportVlan.equals(UNTAGGED_TRANSPORT_VLAN)) {
559 vlanStore.remove(transportVlan);
560 }
561
562 // First we remove both policy.
563 log.debug("Start deleting fwd policy for {}", tunnelId);
564 VlanId egressVlan = determineEgressVlan(oldPw.l2TunnelPolicy().cP1OuterTag(),
565 oldPw.l2TunnelPolicy().cP1InnerTag(),
566 oldPw.l2TunnelPolicy().cP2OuterTag(),
567 oldPw.l2TunnelPolicy().cP2InnerTag());
568 deletePolicy(tunnelId, oldPw.l2TunnelPolicy().cP1(),
569 oldPw.l2TunnelPolicy().cP1InnerTag(),
570 oldPw.l2TunnelPolicy().cP1OuterTag(),
571 egressVlan,
572 fwdInitNextFuture,
573 FWD);
574
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800575 deletePolicy(tunnelId, oldPw.l2TunnelPolicy().cP2(),
576 oldPw.l2TunnelPolicy().cP2InnerTag(),
577 oldPw.l2TunnelPolicy().cP2OuterTag(),
578 egressVlan, revInitNextFuture,
579 REV);
580
581 // Finally we remove both the tunnels.
582 fwdInitNextFuture.thenAcceptAsync(status -> {
583 if (status == null) {
584 log.debug("Update process : Fwd policy removed. " +
585 "Now remove fwd {} for {}", INITIATION, tunnelId);
586 tearDownPseudoWireInit(tunnelId, oldPw.l2TunnelPolicy().cP1(), fwdTermNextFuture, FWD);
587 }
588 });
589 revInitNextFuture.thenAcceptAsync(status -> {
590 if (status == null) {
591 log.debug("Update process : Rev policy removed. " +
592 "Now remove rev {} for {}", INITIATION, tunnelId);
593 tearDownPseudoWireInit(tunnelId, oldPw.l2TunnelPolicy().cP2(), revTermNextFuture, REV);
594 }
595 });
596 fwdTermNextFuture.thenAcceptAsync(status -> {
597 if (status == null) {
598 log.debug("Update process : Fwd {} removed. " +
599 "Now remove fwd {} for {}", INITIATION, TERMINATION, tunnelId);
600 tearDownPseudoWireTerm(oldPw.l2Tunnel(), oldPw.l2TunnelPolicy().cP2(), fwdPwFuture, FWD);
601 }
602 });
603 revTermNextFuture.thenAcceptAsync(status -> {
604 if (status == null) {
605 log.debug("Update process : Rev {} removed. " +
606 "Now remove rev {} for {}", INITIATION, TERMINATION, tunnelId);
607 tearDownPseudoWireTerm(oldPw.l2Tunnel(), oldPw.l2TunnelPolicy().cP1(), revPwFuture, REV);
608 }
609 });
610
611 // get path here, need to use the same for fwd and rev direction
612 List<Link> path = getPath(newPw.l2TunnelPolicy().cP1(),
613 newPw.l2TunnelPolicy().cP2());
614 if (path == null) {
615 log.error("Update process : " +
616 "No path between the connection points for pseudowire {}", newPw.l2Tunnel().tunnelId());
617 return;
618 }
619
620 Link fwdNextHop, revNextHop;
621 if (!finalNewPwSpine) {
622 if (path.size() != 2) {
623 log.error("Update process : Error, path between two leafs should have size of 2, for pseudowire {}",
624 newPw.l2Tunnel().tunnelId());
625 return;
626 }
627 fwdNextHop = path.get(0);
628 revNextHop = reverseLink(path.get(1));
629 } else {
630 if (path.size() != 1) {
631 log.error("Update process : Error, path between leaf spine should equal to 1, for pseudowire {}",
632 newPw.l2Tunnel().tunnelId());
633 return;
634 }
635 fwdNextHop = path.get(0);
636 revNextHop = reverseLink(path.get(0));
637 }
638
639 // set new path and transport vlan.
640 newPw.l2Tunnel().setPath(path);
641 newPw.l2Tunnel().setTransportVlan(determineTransportVlan(newPwSpine));
642
643 // At the end we install the updated PW.
644 fwdPwFuture.thenAcceptAsync(status -> {
645 if (status == null) {
646
647 // Upgrade stores and book keeping information, need to move this here
648 // cause this call is asynchronous.
649 l2PolicyStore.put(Long.toString(tunnelId), newPw.l2TunnelPolicy());
650 l2TunnelStore.put(Long.toString(tunnelId), newPw.l2Tunnel());
651
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800652 VlanId egressVlanId = determineEgressVlan(newPw.l2TunnelPolicy().cP1OuterTag(),
653 newPw.l2TunnelPolicy().cP1InnerTag(),
654 newPw.l2TunnelPolicy().cP2OuterTag(),
655 newPw.l2TunnelPolicy().cP2InnerTag());
656
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800657 log.debug("Update process : Deploying new fwd pw for {}", tunnelId);
658 Result lamdaResult = deployPseudoWireInit(newPw.l2Tunnel(), newPw.l2TunnelPolicy().cP1(),
659 newPw.l2TunnelPolicy().cP2(), FWD,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800660 fwdNextHop, finalNewPwSpine, egressVlanId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800661 if (lamdaResult != SUCCESS) {
662 return;
663 }
664
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800665 lamdaResult = deployPolicy(tunnelId, newPw.l2TunnelPolicy().cP1(),
666 newPw.l2TunnelPolicy().cP1InnerTag(),
667 newPw.l2TunnelPolicy().cP1OuterTag(),
668 egressVlanId, lamdaResult.nextId);
669 if (lamdaResult != SUCCESS) {
670 return;
671 }
672 deployPseudoWireTerm(newPw.l2Tunnel(), newPw.l2TunnelPolicy().cP2(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800673 egressVlanId, FWD, finalNewPwSpine);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800674
675 }
676 });
677 revPwFuture.thenAcceptAsync(status -> {
678 if (status == null) {
679
680 log.debug("Update process : Deploying new rev pw for {}", tunnelId);
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800681
682 VlanId egressVlanId = determineEgressVlan(newPw.l2TunnelPolicy().cP2OuterTag(),
683 newPw.l2TunnelPolicy().cP2InnerTag(),
684 newPw.l2TunnelPolicy().cP1OuterTag(),
685 newPw.l2TunnelPolicy().cP1InnerTag());
686
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800687 Result lamdaResult = deployPseudoWireInit(newPw.l2Tunnel(),
688 newPw.l2TunnelPolicy().cP2(),
689 newPw.l2TunnelPolicy().cP1(),
690 REV,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800691 revNextHop, finalNewPwSpine, egressVlanId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800692 if (lamdaResult != SUCCESS) {
693 return;
694 }
695
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800696 lamdaResult = deployPolicy(tunnelId,
697 newPw.l2TunnelPolicy().cP2(),
698 newPw.l2TunnelPolicy().cP2InnerTag(),
699 newPw.l2TunnelPolicy().cP2OuterTag(),
700 egressVlanId,
701 lamdaResult.nextId);
702 if (lamdaResult != SUCCESS) {
703 return;
704 }
705 deployPseudoWireTerm(newPw.l2Tunnel(),
706 newPw.l2TunnelPolicy().cP1(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800707 egressVlanId,
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800708 REV, finalNewPwSpine);
709 }
710 });
711 }
712
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800713 /**
714 * Helper function for removing a single pseudowire.
715 * <p>
716 * No mastership of CP1 is checked, because it can be called from
717 * the CLI for removal of pseudowires.
718 *
719 * @param l2TunnelId the id of the pseudowire to tear down
720 * @return Returns SUCCESS if no error is obeserved or an appropriate
721 * error on a failure
722 */
723 private Result tearDownPseudowire(long l2TunnelId) {
724
725 CompletableFuture<ObjectiveError> fwdInitNextFuture = new CompletableFuture<>();
726 CompletableFuture<ObjectiveError> fwdTermNextFuture = new CompletableFuture<>();
727
728 CompletableFuture<ObjectiveError> revInitNextFuture = new CompletableFuture<>();
729 CompletableFuture<ObjectiveError> revTermNextFuture = new CompletableFuture<>();
730
731 if (l2TunnelId == 0) {
732 log.warn("Removal process : Tunnel id cannot be 0");
733 return Result.WRONG_PARAMETERS;
734 }
735
736 // check existence of tunnels/policy in the store, if one is missing abort!
737 Versioned<L2Tunnel> l2TunnelVersioned = l2TunnelStore.get(Long.toString(l2TunnelId));
738 Versioned<L2TunnelPolicy> l2TunnelPolicyVersioned = l2PolicyStore.get(Long.toString(l2TunnelId));
739 if ((l2TunnelVersioned == null) || (l2TunnelPolicyVersioned == null)) {
740 log.warn("Removal process : Policy and/or tunnel missing for tunnel id {}", l2TunnelId);
741 return Result.REMOVAL_ERROR;
742 }
743
744 L2TunnelDescription pwToRemove = new DefaultL2TunnelDescription(l2TunnelVersioned.value(),
745 l2TunnelPolicyVersioned.value());
746
747 // remove the tunnels and the policies from the store
748 l2PolicyStore.remove(Long.toString(l2TunnelId));
749 l2TunnelStore.remove(Long.toString(l2TunnelId));
750
751 // remove the reserved transport vlan
752 if (!pwToRemove.l2Tunnel().transportVlan().equals(UNTAGGED_TRANSPORT_VLAN)) {
753 vlanStore.remove(pwToRemove.l2Tunnel().transportVlan());
754 }
755
756 log.info("Removal process : Tearing down forward direction of pseudowire {}", l2TunnelId);
757
758 VlanId egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP1OuterTag(),
759 pwToRemove.l2TunnelPolicy().cP1InnerTag(),
760 pwToRemove.l2TunnelPolicy().cP2OuterTag(),
761 pwToRemove.l2TunnelPolicy().cP2InnerTag());
762 deletePolicy(l2TunnelId,
763 pwToRemove.l2TunnelPolicy().cP1(),
764 pwToRemove.l2TunnelPolicy().cP1InnerTag(),
765 pwToRemove.l2TunnelPolicy().cP1OuterTag(),
766 egressVlan,
767 fwdInitNextFuture,
768 FWD);
769
770 fwdInitNextFuture.thenAcceptAsync(status -> {
771 if (status == null) {
772 // Finally we will tear down the pseudo wire.
773 tearDownPseudoWireInit(l2TunnelId,
774 pwToRemove.l2TunnelPolicy().cP1(),
775 fwdTermNextFuture,
776 FWD);
777 }
778 });
779
780 fwdTermNextFuture.thenAcceptAsync(status -> {
781 if (status == null) {
782 tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
783 pwToRemove.l2TunnelPolicy().cP2(),
784 null,
785 FWD);
786 }
787 });
788
789 log.info("Removal process : Tearing down reverse direction of pseudowire {}", l2TunnelId);
790
791 egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP2OuterTag(),
792 pwToRemove.l2TunnelPolicy().cP2InnerTag(),
793 pwToRemove.l2TunnelPolicy().cP1OuterTag(),
794 pwToRemove.l2TunnelPolicy().cP1InnerTag());
795
796 // We do the same operations on the reverse side.
797 deletePolicy(l2TunnelId,
798 pwToRemove.l2TunnelPolicy().cP2(),
799 pwToRemove.l2TunnelPolicy().cP2InnerTag(),
800 pwToRemove.l2TunnelPolicy().cP2OuterTag(),
801 egressVlan,
802 revInitNextFuture,
803 REV);
804
805 revInitNextFuture.thenAcceptAsync(status -> {
806 if (status == null) {
807 tearDownPseudoWireInit(l2TunnelId,
808 pwToRemove.l2TunnelPolicy().cP2(),
809 revTermNextFuture,
810 REV);
811 }
812 });
813
814 revTermNextFuture.thenAcceptAsync(status -> {
815 if (status == null) {
816 tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
817 pwToRemove.l2TunnelPolicy().cP1(),
818 null,
819 REV);
820 }
821 });
822
823 return Result.SUCCESS;
824 }
825
826 @Override
827 public void tearDown(Set<L2TunnelDescription> pwToRemove) {
828
829 Result result;
830
831 // We remove all the pw in the configuration file.
832 for (L2TunnelDescription currentL2Tunnel : pwToRemove) {
833 ConnectPoint cp1 = currentL2Tunnel.l2TunnelPolicy().cP1();
834 ConnectPoint cp2 = currentL2Tunnel.l2TunnelPolicy().cP2();
835 long tunnelId = currentL2Tunnel.l2TunnelPolicy().tunnelId();
836
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800837 // no need to differentiate here between leaf-leaf and leaf-spine, because
838 // the only change is in the groups, which we do not remove either way
839 log.info("Removing pseudowire {}", tunnelId);
840
841 result = tearDownPseudowire(tunnelId);
842 switch (result) {
843 case WRONG_PARAMETERS:
844 log.warn("Error in supplied parameters for the pseudowire removal with tunnel id {}!",
845 tunnelId);
846 break;
847 case REMOVAL_ERROR:
848 log.warn("Error in pseudowire removal with tunnel id {}!", tunnelId);
849 break;
850 default:
851 log.warn("Pseudowire with tunnel id {} was removed successfully", tunnelId);
852 }
853 }
854 }
855
856 /**
857 * Handles the policy establishment which consists in
858 * create the filtering and forwarding objectives related
859 * to the initiation and termination.
860 *
861 * @param tunnelId the tunnel id
862 * @param ingress the ingress point
863 * @param ingressInner the ingress inner tag
864 * @param ingressOuter the ingress outer tag
865 * @param nextId the next objective id
866 * @param egressVlan Vlan-id to set, depends on ingress vlan
867 * combinations. For example, if pw is double tagged
868 * then this is the value of the outer vlan, if single
869 * tagged then it is the new value of the single tag.
870 * Should be None for untagged traffic.
871 * @return the result of the operation
872 */
873 private Result deployPolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner,
874 VlanId ingressOuter, VlanId egressVlan, int nextId) {
875
876 List<Objective> objectives = Lists.newArrayList();
877 // We create the forwarding objective for supporting
878 // the l2 tunnel.
879 ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
880 // We create and add objective context.
881 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
882 log.debug("FwdObj for tunnel {} populated", tunnelId),
883 (objective, error) ->
884 log.warn("Failed to populate fwdrObj " +
885 "for tunnel {}", tunnelId, error));
886 objectives.add(fwdBuilder.add(context));
887
888 // We create the filtering objective to define the
889 // permit traffic in the switch
890 FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
891
892 // We add the metadata.
893 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
894 .builder()
895 .setTunnelId(tunnelId)
896 .setVlanId(egressVlan);
897 filtBuilder.withMeta(treatment.build());
898
899 // We create and add objective context.
900 context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for tunnel {} populated", tunnelId),
901 (objective, error) -> log.warn("Failed to populate filterObj for " +
902 "tunnel {}", tunnelId, error));
903 objectives.add(filtBuilder.add(context));
904
905 for (Objective objective : objectives) {
906 if (objective instanceof ForwardingObjective) {
907 srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
908 log.debug("Creating new FwdObj for initiation NextObj with id={} for tunnel {}", nextId, tunnelId);
909 } else {
910 srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
911 log.debug("Creating new FiltObj for tunnel {}", tunnelId);
912 }
913 }
914 return SUCCESS;
915 }
916
917 /**
918 * Handles the tunnel establishment which consists in
919 * create the next objectives related to the initiation.
920 *
921 * @param l2Tunnel the tunnel to deploy
922 * @param ingress the ingress connect point
923 * @param egress the egress connect point
924 * @param direction the direction of the pw
925 * @param spinePw if the pseudowire involves a spine switch
926 * @return the result of the operation
927 */
928 private Result deployPseudoWireInit(L2Tunnel l2Tunnel, ConnectPoint ingress,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800929 ConnectPoint egress, Direction direction,
930 Link nextHop, boolean spinePw, VlanId termVlanId) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800931
932 if (nextHop == null) {
933 log.warn("No path between ingress and egress cps for tunnel {}", l2Tunnel.tunnelId());
934 return WRONG_PARAMETERS;
935 }
936
937 // We create the next objective without the metadata
938 // context and id. We check if it already exists in the
939 // store. If not we store as it is in the store.
940 NextObjective.Builder nextObjectiveBuilder = createNextObjective(INITIATION,
941 nextHop.src(),
942 nextHop.dst(),
943 l2Tunnel,
944 egress.deviceId(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -0800945 spinePw,
946 termVlanId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -0800947
948 if (nextObjectiveBuilder == null) {
949 return INTERNAL_ERROR;
950 }
951 // We set the metadata. We will use this metadata
952 // to inform the driver we are doing a l2 tunnel.
953 TrafficSelector metadata = DefaultTrafficSelector
954 .builder()
955 .matchTunnelId(l2Tunnel.tunnelId())
956 .build();
957 nextObjectiveBuilder.withMeta(metadata);
958 int nextId = srManager.flowObjectiveService.allocateNextId();
959 if (nextId < 0) {
960 log.warn("Not able to allocate a next id for initiation");
961 return INTERNAL_ERROR;
962 }
963 nextObjectiveBuilder.withId(nextId);
964 String key = generateKey(l2Tunnel.tunnelId(), direction);
965 l2InitiationNextObjStore.put(key, nextObjectiveBuilder.add());
966 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
967 log.debug("Initiation l2 tunnel rule " +
968 "for {} populated",
969 l2Tunnel.tunnelId()),
970 (objective, error) ->
971 log.warn("Failed to populate Initiation " +
972 "l2 tunnel rule for {}: {}",
973 l2Tunnel.tunnelId(), error));
974 NextObjective nextObjective = nextObjectiveBuilder.add(context);
975 srManager.flowObjectiveService.next(ingress.deviceId(), nextObjective);
976 log.debug("Initiation next objective for {} not found. Creating new NextObj with id={}",
977 l2Tunnel.tunnelId(), nextObjective.id());
978 Result result = SUCCESS;
979 result.nextId = nextObjective.id();
980 return result;
981 }
982
983 /**
984 * Handles the tunnel termination, which consists in the creation
985 * of a forwarding objective and a next objective.
986 *
987 * @param l2Tunnel the tunnel to terminate
988 * @param egress the egress point
989 * @param egressVlan the expected vlan at egress
990 * @param direction the direction
991 * @param spinePw if the pseudowire involves a spine switch
992 * @return the result of the operation
993 */
994 private Result deployPseudoWireTerm(L2Tunnel l2Tunnel, ConnectPoint egress,
995 VlanId egressVlan, Direction direction, boolean spinePw) {
996
997 // We create the group relative to the termination.
998 NextObjective.Builder nextObjectiveBuilder = createNextObjective(TERMINATION, egress, null,
999 l2Tunnel, egress.deviceId(),
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001000 spinePw,
1001 egressVlan);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001002 if (nextObjectiveBuilder == null) {
1003 return INTERNAL_ERROR;
1004 }
1005 TrafficSelector metadata = DefaultTrafficSelector
1006 .builder()
1007 .matchVlanId(egressVlan)
1008 .build();
1009 nextObjectiveBuilder.withMeta(metadata);
1010 int nextId = srManager.flowObjectiveService.allocateNextId();
1011 if (nextId < 0) {
1012 log.warn("Not able to allocate a next id for initiation");
1013 return INTERNAL_ERROR;
1014 }
1015 nextObjectiveBuilder.withId(nextId);
1016 String key = generateKey(l2Tunnel.tunnelId(), direction);
1017 l2TerminationNextObjStore.put(key, nextObjectiveBuilder.add());
1018 ObjectiveContext context = new DefaultObjectiveContext((objective) -> log.debug("Termination l2 tunnel rule " +
1019 "for {} populated",
1020 l2Tunnel.tunnelId()),
1021 (objective, error) -> log.warn("Failed to populate " +
1022 "termination l2 tunnel " +
1023 "rule for {}: {}",
1024 l2Tunnel.tunnelId(),
1025 error));
1026 NextObjective nextObjective = nextObjectiveBuilder.add(context);
1027 srManager.flowObjectiveService.next(egress.deviceId(), nextObjective);
1028 log.debug("Termination next objective for {} not found. Creating new NextObj with id={}",
1029 l2Tunnel.tunnelId(), nextObjective.id());
1030
1031 // We create the flow relative to the termination.
1032 ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(), l2Tunnel.tunnelId(),
1033 egress.port(), nextObjective.id());
1034 context = new DefaultObjectiveContext((objective) -> log.debug("FwdObj for tunnel termination {} populated",
1035 l2Tunnel.tunnelId()),
1036 (objective, error) -> log.warn("Failed to populate fwdrObj" +
1037 " for tunnel termination {}",
1038 l2Tunnel.tunnelId(), error));
1039 srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.add(context));
1040 log.debug("Creating new FwdObj for termination NextObj with id={} for tunnel {}",
1041 nextId, l2Tunnel.tunnelId());
1042
1043 if (spinePw) {
1044
1045 // determine the input port at the
1046 PortNumber inPort;
1047
1048 if (egress.deviceId().
1049 equals(l2Tunnel.pathUsed().get(0).dst().deviceId())) {
1050 inPort = l2Tunnel.pathUsed().get(0).dst().port();
1051 } else {
1052 inPort = l2Tunnel.pathUsed().get(0).src().port();
1053 }
1054
1055 MacAddress dstMac;
1056 try {
1057 dstMac = srManager.deviceConfiguration().getDeviceMac(egress.deviceId());
1058 } catch (Exception e) {
1059 log.info("Device not found in configuration, no programming of MAC address");
1060 dstMac = null;
1061 }
1062
1063 log.info("Populating filtering objective for pseudowire transport" +
1064 " with vlan = {}, port = {}, mac = {}",
1065 l2Tunnel.transportVlan(),
1066 inPort,
1067 dstMac);
1068 FilteringObjective.Builder filteringObjectiveBuilder =
1069 createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
1070 context = new DefaultObjectiveContext(( objective ) ->
1071 log.debug("Special filtObj for " + "for {} populated",
1072 l2Tunnel.tunnelId()),
1073 ( objective, error ) ->
1074 log.warn("Failed to populate " +
1075 "special filtObj " +
1076 "rule for {}: {}",
1077 l2Tunnel.tunnelId(), error));
1078 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
1079 filteringObjectiveBuilder.withMeta(treatment.build());
1080 srManager.flowObjectiveService.filter(egress.deviceId(), filteringObjectiveBuilder.add(context));
1081 log.debug("Creating new special FiltObj for termination point with tunnel {} for port {}",
1082 l2Tunnel.tunnelId(),
1083 inPort);
1084 }
1085
1086 return SUCCESS;
1087 }
1088
1089
1090 /**
1091 * Creates the filtering objective according to a given port and vlanid.
1092 *
1093 * @param inPort the in port
1094 * @param vlanId the inner vlan tag
1095 * @return the filtering objective
1096 */
1097 private FilteringObjective.Builder createNormalPipelineFiltObjective(PortNumber inPort,
1098 VlanId vlanId,
1099 MacAddress dstMac) {
1100
1101 log.info("Creating filtering objective for pseudowire transport with vlan={}, port={}, mac={}",
1102 vlanId,
1103 inPort,
1104 dstMac);
1105 FilteringObjective.Builder fwdBuilder = DefaultFilteringObjective
1106 .builder()
1107 .withKey(Criteria.matchInPort(inPort))
1108 .addCondition(Criteria.matchVlanId(vlanId))
1109 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1110 .permit()
1111 .fromApp(srManager.appId());
1112
1113 if (dstMac != null) {
1114 fwdBuilder.addCondition(Criteria.matchEthDst(dstMac));
1115 }
1116
1117 return fwdBuilder;
1118 }
1119
1120 /**
1121 * Creates the filtering objective according to a given policy.
1122 *
1123 * @param inPort the in port
1124 * @param innerTag the inner vlan tag
1125 * @param outerTag the outer vlan tag
1126 * @return the filtering objective
1127 */
1128 private FilteringObjective.Builder createFiltObjective(PortNumber inPort, VlanId innerTag, VlanId outerTag) {
1129
1130 log.info("Creating filtering objective for vlans {} / {}", outerTag, innerTag);
1131 return DefaultFilteringObjective
1132 .builder()
1133 .withKey(Criteria.matchInPort(inPort))
1134 .addCondition(Criteria.matchInnerVlanId(innerTag))
1135 .addCondition(Criteria.matchVlanId(outerTag))
1136 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1137 .permit()
1138 .fromApp(srManager.appId());
1139 }
1140
1141 /**
1142 * Creates the forwarding objective for the termination.
1143 *
1144 * @param pwLabel the pseudo wire label
1145 * @param tunnelId the tunnel id
1146 * @param egressPort the egress port
1147 * @param nextId the next step
1148 * @return the forwarding objective to support the termination
1149 */
1150 private ForwardingObjective.Builder createTermFwdObjective(MplsLabel pwLabel, long tunnelId,
1151 PortNumber egressPort, int nextId) {
1152
1153 TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
1154 TrafficTreatment.Builder trafficTreatment = DefaultTrafficTreatment.builder();
1155 // The flow has to match on the pw label and bos
1156 trafficSelector.matchEthType(Ethernet.MPLS_UNICAST);
1157 trafficSelector.matchMplsLabel(pwLabel);
1158 trafficSelector.matchMplsBos(true);
1159 // The flow has to decrement ttl, restore ttl in
1160 // pop mpls, set tunnel id and port.
1161 trafficTreatment.decMplsTtl();
1162 trafficTreatment.copyTtlIn();
1163 trafficTreatment.popMpls();
1164 trafficTreatment.setTunnelId(tunnelId);
1165 trafficTreatment.setOutput(egressPort);
1166
1167 return DefaultForwardingObjective
1168 .builder()
1169 .fromApp(srManager.appId())
1170 .makePermanent()
1171 .nextStep(nextId)
1172 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1173 .withSelector(trafficSelector.build())
1174 .withTreatment(trafficTreatment.build())
1175 .withFlag(VERSATILE);
1176 }
1177
1178 /**
1179 * Creates the forwarding objective for the initiation.
1180 *
1181 * @param tunnelId the tunnel id
1182 * @param inPort the input port
1183 * @param nextId the next step
1184 * @return the forwarding objective to support the initiation.
1185 */
1186 private ForwardingObjective.Builder createInitFwdObjective(long tunnelId, PortNumber inPort, int nextId) {
1187
1188 TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
1189
1190 // The flow has to match on the mpls logical
1191 // port and the tunnel id.
1192 trafficSelector.matchTunnelId(tunnelId);
1193 trafficSelector.matchInPort(inPort);
1194
1195 return DefaultForwardingObjective
1196 .builder()
1197 .fromApp(srManager.appId())
1198 .makePermanent()
1199 .nextStep(nextId)
1200 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
1201 .withSelector(trafficSelector.build())
1202 .withFlag(VERSATILE);
1203
1204 }
1205
1206 /**
1207 * Creates the next objective according to a given
1208 * pipeline. We don't set the next id and we don't
1209 * create the final meta to check if we are re-using
1210 * the same next objective for different tunnels.
1211 *
1212 * @param pipeline the pipeline to support
1213 * @param srcCp the source port
1214 * @param dstCp the destination port
1215 * @param l2Tunnel the tunnel to support
1216 * @param egressId the egress device id
1217 * @param spinePw if the pw involves a spine switch
1218 * @return the next objective to support the pipeline
1219 */
1220 private NextObjective.Builder createNextObjective(Pipeline pipeline, ConnectPoint srcCp,
1221 ConnectPoint dstCp, L2Tunnel l2Tunnel,
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001222 DeviceId egressId, boolean spinePw, VlanId termVlanId) {
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001223 NextObjective.Builder nextObjBuilder;
1224 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1225 if (pipeline == INITIATION) {
1226 nextObjBuilder = DefaultNextObjective
1227 .builder()
1228 .withType(NextObjective.Type.SIMPLE)
1229 .fromApp(srManager.appId());
1230 // The pw label is the bottom of stack. It has to
1231 // be different -1.
1232 if (l2Tunnel.pwLabel().toInt() == MplsLabel.MAX_MPLS) {
1233 log.warn("Pw label not configured");
1234 return null;
1235 }
1236 treatmentBuilder.pushMpls();
1237 treatmentBuilder.setMpls(l2Tunnel.pwLabel());
1238 treatmentBuilder.setMplsBos(true);
1239 treatmentBuilder.copyTtlOut();
1240
1241 // If the inter-co label is present we have to set the label.
1242 if (l2Tunnel.interCoLabel().toInt() != MplsLabel.MAX_MPLS) {
1243 treatmentBuilder.pushMpls();
1244 treatmentBuilder.setMpls(l2Tunnel.interCoLabel());
1245 treatmentBuilder.setMplsBos(false);
1246 treatmentBuilder.copyTtlOut();
1247 }
1248
1249 // if pw is leaf-to-leaf we need to
1250 // add the routing label also
1251 if (!spinePw) {
1252 // We retrieve the sr label from the config
1253 // specific for pseudowire traffic
1254 // using the egress leaf device id.
1255 MplsLabel srLabel;
1256 try {
1257 srLabel = MplsLabel.mplsLabel(srManager.deviceConfiguration().getPWRoutingLabel(egressId));
1258
1259 } catch (DeviceConfigNotFoundException e) {
1260 log.warn("Sr label for pw traffic not configured");
1261 return null;
1262 }
1263
1264 treatmentBuilder.pushMpls();
1265 treatmentBuilder.setMpls(srLabel);
1266 treatmentBuilder.setMplsBos(false);
1267 treatmentBuilder.copyTtlOut();
1268 }
1269
1270 // We have to rewrite the src and dst mac address.
1271 MacAddress ingressMac;
1272 try {
1273 ingressMac = srManager.deviceConfiguration().getDeviceMac(srcCp.deviceId());
1274 } catch (DeviceConfigNotFoundException e) {
1275 log.warn("Was not able to find the ingress mac");
1276 return null;
1277 }
1278 treatmentBuilder.setEthSrc(ingressMac);
1279 MacAddress neighborMac;
1280 try {
1281 neighborMac = srManager.deviceConfiguration().getDeviceMac(dstCp.deviceId());
1282 } catch (DeviceConfigNotFoundException e) {
1283 log.warn("Was not able to find the neighbor mac");
1284 return null;
1285 }
1286 treatmentBuilder.setEthDst(neighborMac);
1287
1288 // if not a leaf-spine pw we need to POP the vlan at the output
1289 // since we carry this traffic untagged.
1290 if (!spinePw) {
1291 treatmentBuilder.popVlan();
1292 }
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001293
1294 // set the appropriate transport vlan
1295 treatmentBuilder.setVlanId(l2Tunnel.transportVlan());
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001296 } else {
1297 // We create the next objective which
1298 // will be a simple l2 group.
1299 nextObjBuilder = DefaultNextObjective
1300 .builder()
1301 .withType(NextObjective.Type.SIMPLE)
1302 .fromApp(srManager.appId());
Andreas Pantelopoulos1e61db52018-02-22 17:54:45 -08001303
1304 // for termination point we use the outer vlan of the
1305 // encapsulated packet
1306 treatmentBuilder.setVlanId(termVlanId);
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001307 }
1308
Andreas Pantelopoulosb21547d2018-02-22 12:32:42 -08001309 treatmentBuilder.setOutput(srcCp.port());
1310 nextObjBuilder.addTreatment(treatmentBuilder.build());
1311 return nextObjBuilder;
1312 }
1313
1314 /**
1315 * Reverses a link.
1316 *
1317 * @param link link to be reversed
1318 * @return the reversed link
1319 */
1320 private Link reverseLink(Link link) {
1321
1322 DefaultLink.Builder linkBuilder = DefaultLink.builder();
1323
1324 linkBuilder.src(link.dst());
1325 linkBuilder.dst(link.src());
1326 linkBuilder.type(link.type());
1327 linkBuilder.providerId(link.providerId());
1328
1329 return linkBuilder.build();
1330 }
1331
1332 /**
1333 * Returns the path betwwen two connect points.
1334 *
1335 * @param srcCp source connect point
1336 * @param dstCp destination connect point
1337 * @return the path
1338 */
1339 private List<Link> getPath(ConnectPoint srcCp, ConnectPoint dstCp) {
1340 /* TODO We retrieve a set of paths in case of a link failure, what happens
1341 * if the TopologyService gets the link notification AFTER us and has not updated the paths?
1342 *
1343 * TODO This has the potential to act on old topology.
1344 * Maybe we should make SRManager be a listener on topology events instead raw link events.
1345 */
1346 Set<Path> paths = srManager.topologyService.getPaths(
1347 srManager.topologyService.currentTopology(),
1348 srcCp.deviceId(), dstCp.deviceId());
1349
1350 log.debug("Paths obtained from topology service {}", paths);
1351
1352 // We randomly pick a path.
1353 if (paths.isEmpty()) {
1354 return null;
1355 }
1356 int size = paths.size();
1357 int index = RandomUtils.nextInt(0, size);
1358
1359 List<Link> result = Iterables.get(paths, index).links();
1360 log.debug("Randomly picked a path {}", result);
1361
1362 return result;
1363 }
1364
1365 /**
1366 * Deletes a given policy using the parameter supplied.
1367 *
1368 * @param tunnelId the tunnel id
1369 * @param ingress the ingress point
1370 * @param ingressInner the ingress inner vlan id
1371 * @param ingressOuter the ingress outer vlan id
1372 * @param future to perform the async operation
1373 * @param direction the direction: forward or reverse
1374 */
1375 private void deletePolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner, VlanId ingressOuter,
1376 VlanId egressVlan, CompletableFuture<ObjectiveError> future, Direction direction) {
1377
1378 String key = generateKey(tunnelId, direction);
1379 if (!l2InitiationNextObjStore.containsKey(key)) {
1380 log.warn("Abort delete of policy for tunnel {}: next does not exist in the store", tunnelId);
1381 if (future != null) {
1382 future.complete(null);
1383 }
1384 return;
1385 }
1386 NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
1387 int nextId = nextObjective.id();
1388 List<Objective> objectives = Lists.newArrayList();
1389 // We create the forwarding objective.
1390 ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
1391 ObjectiveContext context = new ObjectiveContext() {
1392 @Override
1393 public void onSuccess(Objective objective) {
1394 log.debug("Previous fwdObj for policy {} removed", tunnelId);
1395 if (future != null) {
1396 future.complete(null);
1397 }
1398 }
1399
1400 @Override
1401 public void onError(Objective objective, ObjectiveError error) {
1402 log.warn("Failed to remove previous fwdObj for policy {}: {}", tunnelId, error);
1403 if (future != null) {
1404 future.complete(error);
1405 }
1406 }
1407 };
1408 objectives.add(fwdBuilder.remove(context));
1409 // We create the filtering objective to define the
1410 // permit traffic in the switch
1411 FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
1412 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
1413 .builder()
1414 .setTunnelId(tunnelId)
1415 .setVlanId(egressVlan);
1416 filtBuilder.withMeta(treatment.build());
1417 context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for policy {} revoked", tunnelId),
1418 (objective, error) ->
1419 log.warn("Failed to revoke filterObj for policy {}",
1420 tunnelId, error));
1421 objectives.add(filtBuilder.remove(context));
1422
1423 for (Objective objective : objectives) {
1424 if (objective instanceof ForwardingObjective) {
1425 srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
1426 } else {
1427 srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
1428 }
1429 }
1430 }
1431
1432 /**
1433 * Deletes the pseudo wire initiation.
1434 *
1435 * @param l2TunnelId the tunnel id
1436 * @param ingress the ingress connect point
1437 * @param future to perform an async operation
1438 * @param direction the direction: reverse of forward
1439 */
1440 private void tearDownPseudoWireInit(long l2TunnelId, ConnectPoint ingress,
1441 CompletableFuture<ObjectiveError> future, Direction direction) {
1442
1443 String key = generateKey(l2TunnelId, direction);
1444 if (!l2InitiationNextObjStore.containsKey(key)) {
1445 log.info("Abort delete of {} for {}: next does not exist in the store", INITIATION, key);
1446 if (future != null) {
1447 future.complete(null);
1448 }
1449 return;
1450 }
1451 NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
1452
1453 // un-comment in case you want to delete groups used by the pw
1454 // however, this will break the update of pseudowires cause the L2 interface group can
1455 // not be deleted (it is referenced by other groups)
1456 /*
1457 ObjectiveContext context = new ObjectiveContext() {
1458 @Override
1459 public void onSuccess(Objective objective) {
1460 log.debug("Previous {} next for {} removed", INITIATION, key);
1461 if (future != null) {
1462 future.complete(null);
1463 }
1464 }
1465
1466 @Override
1467 public void onError(Objective objective, ObjectiveError error) {
1468 log.warn("Failed to remove previous {} next for {}: {}", INITIATION, key, error);
1469 if (future != null) {
1470 future.complete(error);
1471 }
1472 }
1473 };
1474 srManager.flowObjectiveService.next(ingress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
1475 */
1476
1477 future.complete(null);
1478 l2InitiationNextObjStore.remove(key);
1479 }
1480
1481 /**
1482 * Deletes the pseudo wire termination.
1483 *
1484 * @param l2Tunnel the tunnel
1485 * @param egress the egress connect point
1486 * @param future the async task
1487 * @param direction the direction of the tunnel
1488 */
1489 private void tearDownPseudoWireTerm(L2Tunnel l2Tunnel,
1490 ConnectPoint egress,
1491 CompletableFuture<ObjectiveError> future,
1492 Direction direction) {
1493
1494 String key = generateKey(l2Tunnel.tunnelId(), direction);
1495 if (!l2TerminationNextObjStore.containsKey(key)) {
1496 log.info("Abort delete of {} for {}: next does not exist in the store", TERMINATION, key);
1497 if (future != null) {
1498 future.complete(null);
1499 }
1500 return;
1501 }
1502 NextObjective nextObjective = l2TerminationNextObjStore.get(key).value();
1503 ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(),
1504 l2Tunnel.tunnelId(),
1505 egress.port(),
1506 nextObjective.id());
1507 ObjectiveContext context = new DefaultObjectiveContext((objective) ->
1508 log.debug("FwdObj for {} {}, " +
1509 "direction {} removed",
1510 TERMINATION,
1511 l2Tunnel.tunnelId(),
1512 direction),
1513 (objective, error) ->
1514 log.warn("Failed to remove fwdObj " +
1515 "for {} {}" +
1516 ", direction {}",
1517 TERMINATION,
1518 l2Tunnel.tunnelId(),
1519 error,
1520 direction));
1521 srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.remove(context));
1522
1523 // un-comment in case you want to delete groups used by the pw
1524 // however, this will break the update of pseudowires cause the L2 interface group can
1525 // not be deleted (it is referenced by other groups)
1526 /*
1527 context = new ObjectiveContext() {
1528 @Override
1529 public void onSuccess(Objective objective) {
1530 log.debug("Previous {} next for {} removed", TERMINATION, key);
1531 if (future != null) {
1532 future.complete(null);
1533 }
1534 }
1535
1536 @Override
1537 public void onError(Objective objective, ObjectiveError error) {
1538 log.warn("Failed to remove previous {} next for {}: {}", TERMINATION, key, error);
1539 if (future != null) {
1540 future.complete(error);
1541 }
1542 }
1543 };
1544 srManager.flowObjectiveService.next(egress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
1545 */
1546
1547 // delete the extra filtering objective for terminating
1548 // spine-spine pws
1549 if (!l2Tunnel.transportVlan().equals(UNTAGGED_TRANSPORT_VLAN)) {
1550
1551 // determine the input port at the
1552 PortNumber inPort;
1553
1554 if (egress.deviceId().
1555 equals(l2Tunnel.pathUsed().get(0).dst().deviceId())) {
1556 inPort = l2Tunnel.pathUsed().get(0).dst().port();
1557 } else {
1558 inPort = l2Tunnel.pathUsed().get(0).src().port();
1559 }
1560
1561 MacAddress dstMac;
1562 try {
1563 dstMac = srManager.deviceConfiguration().getDeviceMac(egress.deviceId());
1564 } catch (Exception e) {
1565 log.info("Device not found in configuration, no programming of MAC address");
1566 dstMac = null;
1567 }
1568
1569 log.info("Removing filtering objective for pseudowire transport" +
1570 " with vlan = {}, port = {}, mac = {}",
1571 l2Tunnel.transportVlan(),
1572 inPort,
1573 dstMac);
1574 FilteringObjective.Builder filteringObjectiveBuilder =
1575 createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
1576 context = new DefaultObjectiveContext(( objective ) ->
1577 log.debug("Special filtObj for " + "for {} removed",
1578 l2Tunnel.tunnelId()), ( objective, error ) ->
1579 log.warn("Failed to populate " + "special filtObj " +
1580 "rule for {}: {}", l2Tunnel.tunnelId(), error));
1581 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
1582 filteringObjectiveBuilder.withMeta(treatment.build());
1583 srManager.flowObjectiveService.filter(egress.deviceId(), filteringObjectiveBuilder.remove(context));
1584 log.debug("Removing special FiltObj for termination point with tunnel {} for port {}",
1585 l2Tunnel.tunnelId(),
1586 inPort);
1587 }
1588
1589 l2TerminationNextObjStore.remove(key);
1590 future.complete(null);
1591 }
1592
1593 /**
1594 * Utilities to generate pw key.
1595 *
1596 * @param tunnelId the tunnel id
1597 * @param direction the direction of the pw
1598 * @return the key of the store
1599 */
1600 private String generateKey(long tunnelId, Direction direction) {
1601 return String.format("%s-%s", tunnelId, direction);
1602 }
1603
1604}