blob: 75b3eba115312eba71229577e92407a5c282cc89 [file] [log] [blame]
Pier Ventred48320e2016-08-17 16:25:47 -07001/*
Brian O'Connorce2a03d2017-08-03 19:21:03 -07002 * Copyright 2016-present Open Networking Foundation
Pier Ventred48320e2016-08-17 16:25:47 -07003 *
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.net.intent.impl.compiler;
18
Thomas Szyrkowiec770093f2017-05-05 13:06:53 +020019import com.google.common.collect.ImmutableList;
Pier Ventre81c47bf2016-11-04 07:26:22 -070020import com.google.common.collect.Lists;
Pier Ventre766995d2016-10-05 22:15:56 -070021import com.google.common.collect.Maps;
Pier Ventred48320e2016-08-17 16:25:47 -070022import com.google.common.collect.SetMultimap;
Yi Tseng2a81c9d2016-09-14 10:14:24 -070023import com.google.common.collect.Sets;
Pier Ventre766995d2016-10-05 22:15:56 -070024import org.onlab.packet.EthType;
25import org.onlab.packet.Ethernet;
Pier Ventred48320e2016-08-17 16:25:47 -070026import org.onlab.packet.Ip4Address;
27import org.onlab.packet.IpPrefix;
Pier Ventre766995d2016-10-05 22:15:56 -070028import org.onlab.packet.MplsLabel;
29import org.onlab.packet.VlanId;
30import org.onlab.util.Identifier;
Pier Ventred48320e2016-08-17 16:25:47 -070031import org.onosproject.net.ConnectPoint;
32import org.onosproject.net.DeviceId;
Pier Ventre766995d2016-10-05 22:15:56 -070033import org.onosproject.net.EncapsulationType;
Pier Ventre81c47bf2016-11-04 07:26:22 -070034import org.onosproject.net.FilteredConnectPoint;
Pier Ventred48320e2016-08-17 16:25:47 -070035import org.onosproject.net.Link;
36import org.onosproject.net.PortNumber;
Thomas Szyrkowiec770093f2017-05-05 13:06:53 +020037import org.onosproject.net.domain.DomainId;
38import org.onosproject.net.domain.DomainPointToPointIntent;
39import org.onosproject.net.domain.DomainService;
Pier Ventred48320e2016-08-17 16:25:47 -070040import org.onosproject.net.flow.DefaultTrafficSelector;
41import org.onosproject.net.flow.DefaultTrafficTreatment;
42import org.onosproject.net.flow.TrafficSelector;
43import org.onosproject.net.flow.TrafficTreatment;
Yi Tseng2a81c9d2016-09-14 10:14:24 -070044import org.onosproject.net.flow.criteria.Criteria;
45import org.onosproject.net.flow.criteria.Criterion;
Pier Ventre766995d2016-10-05 22:15:56 -070046import org.onosproject.net.flow.criteria.EthTypeCriterion;
Yi Tseng2a81c9d2016-09-14 10:14:24 -070047import org.onosproject.net.flow.criteria.MplsCriterion;
48import org.onosproject.net.flow.criteria.TunnelIdCriterion;
49import org.onosproject.net.flow.criteria.VlanIdCriterion;
Pier Ventre27d42572016-08-29 17:37:08 -070050import org.onosproject.net.flow.instructions.Instruction;
Pier Ventred48320e2016-08-17 16:25:47 -070051import org.onosproject.net.flow.instructions.L0ModificationInstruction;
52import org.onosproject.net.flow.instructions.L1ModificationInstruction;
53import org.onosproject.net.flow.instructions.L2ModificationInstruction;
54import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
Pier Ventre81c47bf2016-11-04 07:26:22 -070055import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsBosInstruction;
56import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction;
57import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModTunnelIdInstruction;
Pier Ventred48320e2016-08-17 16:25:47 -070058import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
59import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanPcpInstruction;
60import org.onosproject.net.flow.instructions.L3ModificationInstruction;
Pier Ventre81c47bf2016-11-04 07:26:22 -070061import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpEthInstruction;
62import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpIPInstruction;
63import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpOpInstruction;
Pier Ventred48320e2016-08-17 16:25:47 -070064import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPInstruction;
65import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPv6FlowLabelInstruction;
66import org.onosproject.net.flow.instructions.L4ModificationInstruction;
67import org.onosproject.net.flow.instructions.L4ModificationInstruction.ModTransportPortInstruction;
Thomas Szyrkowiec770093f2017-05-05 13:06:53 +020068import org.onosproject.net.intent.Intent;
Pier Ventred48320e2016-08-17 16:25:47 -070069import org.onosproject.net.intent.IntentCompilationException;
70import org.onosproject.net.intent.LinkCollectionIntent;
Thomas Szyrkowiec770093f2017-05-05 13:06:53 +020071import org.onosproject.net.intent.constraint.DomainConstraint;
Pier Ventre766995d2016-10-05 22:15:56 -070072import org.onosproject.net.intent.constraint.EncapsulationConstraint;
73import org.onosproject.net.resource.impl.LabelAllocator;
Thomas Szyrkowiec770093f2017-05-05 13:06:53 +020074import org.slf4j.Logger;
75import org.slf4j.LoggerFactory;
Pier Ventred48320e2016-08-17 16:25:47 -070076
77import java.util.List;
Pier Ventre766995d2016-10-05 22:15:56 -070078import java.util.Map;
Pier Ventre647138f2016-08-26 17:32:44 -070079import java.util.Optional;
Pier Ventred48320e2016-08-17 16:25:47 -070080import java.util.Set;
Pier Ventre81c47bf2016-11-04 07:26:22 -070081import java.util.stream.Collectors;
Pier Ventred48320e2016-08-17 16:25:47 -070082
Thomas Szyrkowiec770093f2017-05-05 13:06:53 +020083import static org.onosproject.net.domain.DomainId.LOCAL;
Pier Ventre81c47bf2016-11-04 07:26:22 -070084import static org.onosproject.net.flow.criteria.Criterion.Type.*;
Yi Tseng2a81c9d2016-09-14 10:14:24 -070085
Pier Ventred48320e2016-08-17 16:25:47 -070086/**
87 * Shared APIs and implementations for Link Collection compilers.
88 */
Yi Tseng84c5a3d2017-04-14 16:42:59 -070089public abstract class LinkCollectionCompiler<T> {
Pier Ventred48320e2016-08-17 16:25:47 -070090
Pier Ventre766995d2016-10-05 22:15:56 -070091 /**
92 * Reference to the label allocator.
93 */
94 static LabelAllocator labelAllocator;
95
96 /**
Pier Ventre81c47bf2016-11-04 07:26:22 -070097 * Influence compiler behavior. If true the compiler
98 * try to optimize the chain of the actions.
99 */
Yi Tseng84c5a3d2017-04-14 16:42:59 -0700100 static boolean optimizeInstructions;
Pier Ventre81c47bf2016-11-04 07:26:22 -0700101
102 /**
103 * Influence compiler behavior. If true the compiler
104 * try to optimize the copy ttl actions.
105 */
106 static boolean copyTtl;
107
108 /**
Pier Ventre766995d2016-10-05 22:15:56 -0700109 * The allowed tag criterions.
110 */
Yi Tseng2a81c9d2016-09-14 10:14:24 -0700111 private static final Set<Criterion.Type> TAG_CRITERION_TYPES =
112 Sets.immutableEnumSet(VLAN_VID, MPLS_LABEL, TUNNEL_ID);
113
Pier Ventred48320e2016-08-17 16:25:47 -0700114 /**
Pier Ventre766995d2016-10-05 22:15:56 -0700115 * Error message for wrong egress scenario.
116 */
117 private static final String WRONG_EGRESS = "Egress points not equal to 1 " +
118 "and apply treatment at ingress, " +
119 "which treatments should I apply ???";
120
121 /**
122 * Error message for wrong ingress scenario.
123 */
124 private static final String WRONG_INGRESS = "Ingress points not equal to 1 " +
125 "and apply treatment at egress, " +
126 "how can I match in the core ???";
127
128 /**
129 * Error message for wrong encapsulation scenario.
130 */
131 private static final String WRONG_ENCAPSULATION = "Wrong scenario - 1 hop with " +
132 "encapsualtion";
133
134 /**
135 * Error message for unavailable labels.
136 */
137 private static final String NO_LABELS = "No available label for %s";
138
139 /**
140 * Error message for wrong encapsulation.
141 */
142 private static final String UNKNOWN_ENCAPSULATION = "Unknown encapsulation type";
143
144 /**
145 * Error message for unsupported L0 instructions.
146 */
147 private static final String UNSUPPORTED_L0 = "L0 not supported";
148
149 /**
150 * Error message for unsupported L1 instructions.
151 */
152 private static final String UNSUPPORTED_L1 = "L1 not supported";
153
154 /**
155 * Error message for unsupported eth subtype.
156 */
157 private static final String UNSUPPORTED_ETH_SUBTYPE = "Bad eth subtype";
158
159 /**
160 * Error message for unsupported pop action.
161 */
162 private static final String UNSUPPORTED_POP_ACTION = "Can't handle pop label";
163
164 /**
165 * Error message for unsupported L2 instructions.
166 */
167 private static final String UNSUPPORTED_L2 = "Unknown L2 Modification instruction";
168
169 /**
170 * Error message for unsupported IP subtype.
171 */
172 private static final String UNSUPPORTED_IP_SUBTYPE = "Bad ip subtype";
173
174 /**
175 * Error message for unsupported ARP.
176 */
177 private static final String UNSUPPORTED_ARP = "IPv6 not supported for ARP";
178
179 /**
180 * Error message for unsupported L3 instructions.
181 */
182 private static final String UNSUPPORTED_L3 = "Unknown L3 Modification instruction";
183
184 /**
185 * Error message for unsupported L4 subtype.
186 */
187 private static final String UNSUPPORTED_L4_SUBTYPE = "Unknown L4 subtype";
188
189 /**
190 * Error message for unsupported L4 instructions.
191 */
192 private static final String UNSUPPORTED_L4 = "Unknown L4 Modification instruction";
193
194 /**
195 * Error message for unsupported instructions.
196 */
197 private static final String UNSUPPORTED_INSTRUCTION = "Unknown instruction type";
198
Thomas Szyrkowiec770093f2017-05-05 13:06:53 +0200199 private static Logger log = LoggerFactory.getLogger(LinkCollectionCompiler.class);
200
Pier Ventre766995d2016-10-05 22:15:56 -0700201 /**
Yi Tseng84c5a3d2017-04-14 16:42:59 -0700202 * Influence compiler behavior.
203 *
204 * @return true if we need the compiler optimizeTreatments the chain of the actions.
205 */
206 abstract boolean optimizeTreatments();
207
208 /**
Pier Ventre766995d2016-10-05 22:15:56 -0700209 * Creates the flows representations. This default implementation does
210 * nothing. Subclasses should override this method to create their
211 * specific flows representations (flow rule, flow objective).
212 *
213 * @param intent the intent to compile
214 * @param deviceId the affected device
215 * @param inPorts the input ports
216 * @param outPorts the output ports
217 * @param labels the labels for the label switching hop by hop
218 * @return the list of flows representations
219 */
220 protected List<T> createRules(LinkCollectionIntent intent,
221 DeviceId deviceId,
222 Set<PortNumber> inPorts,
223 Set<PortNumber> outPorts,
224 Map<ConnectPoint, Identifier<?>> labels) {
225 return null;
226 }
227
228 /**
229 * Helper method to handle the different scenario (not encap, single hop, encap).
230 *
231 * @param encapConstraint the encapsulation constraint if it is present
232 * @param intent the link collection intent
233 * @param inPort the in port
234 * @param outPorts the out ports
235 * @param deviceId the current device
236 * @param labels the labels used by the encapsulation
237 * @return the forwarding instruction
238 */
239 protected ForwardingInstructions createForwardingInstruction(Optional<EncapsulationConstraint> encapConstraint,
240 LinkCollectionIntent intent,
241 PortNumber inPort,
242 Set<PortNumber> outPorts,
243 DeviceId deviceId,
244 Map<ConnectPoint, Identifier<?>> labels) {
245 ForwardingInstructions instructions = null;
246 /*
247 * If not encapsulation or single hop.
248 */
249 if (!encapConstraint.isPresent() || intent.links().isEmpty()) {
250 instructions = this.createForwardingInstructions(
251 intent,
252 inPort,
253 deviceId,
254 outPorts
255 );
256 /*
257 * If encapsulation is present. We retrieve the labels
258 * for this iteration;
259 */
260 } else {
261 Identifier<?> inLabel = labels.get(new ConnectPoint(deviceId, inPort));
262 Map<ConnectPoint, Identifier<?>> outLabels = Maps.newHashMap();
263 outPorts.forEach(outPort -> {
264 ConnectPoint key = new ConnectPoint(deviceId, outPort);
265 outLabels.put(key, labels.get(key));
266 });
267 instructions = this.createForwardingInstructions(
268 intent,
269 inPort,
270 inLabel,
271 deviceId,
272 outPorts,
273 outLabels,
274 encapConstraint.get().encapType()
275 );
276 }
277 return instructions;
278 }
279
280 /**
Pier Ventre5c4a0762016-11-21 10:28:13 -0800281 * Helper method which handles the proper generation of the ouput actions.
282 *
283 * @param outPorts the output ports
284 * @param deviceId the current device
285 * @param intent the intent to compile
286 * @param outLabels the output labels
287 * @param type the encapsulation type
288 * @param preCondition the previous state
289 * @param treatmentBuilder the builder to update with the ouput actions
290 */
291 private void manageOutputPorts(Set<PortNumber> outPorts,
292 DeviceId deviceId,
293 LinkCollectionIntent intent,
294 Map<ConnectPoint, Identifier<?>> outLabels,
295 EncapsulationType type,
296 TrafficSelector.Builder preCondition,
297 TrafficTreatment.Builder treatmentBuilder) {
298 /*
299 * We need to order the actions. First the actions
300 * related to the not-egress points. At the same time we collect
301 * also the egress points.
302 */
303 List<FilteredConnectPoint> egressPoints = Lists.newArrayList();
304 for (PortNumber outPort : outPorts) {
305 Optional<FilteredConnectPoint> filteredEgressPoint =
306 getFilteredConnectPointFromIntent(deviceId, outPort, intent);
307 if (!filteredEgressPoint.isPresent()) {
308 /*
309 * We build a temporary selector for the encapsulation.
310 */
311 TrafficSelector.Builder encapBuilder = DefaultTrafficSelector.builder();
312 /*
313 * We retrieve the associated label to the output port.
314 */
315 ConnectPoint cp = new ConnectPoint(deviceId, outPort);
316 Identifier<?> outLabel = outLabels.get(cp);
317 /*
318 * If there are not labels, we cannot handle.
319 */
320 if (outLabel == null) {
321 throw new IntentCompilationException(String.format(NO_LABELS, cp));
322 }
323 /*
324 * In the core we match using encapsulation.
325 */
326 updateSelectorFromEncapsulation(
327 encapBuilder,
328 type,
329 outLabel
330 );
331 /*
332 * We generate the transition.
333 */
334 TrafficTreatment forwardingTreatment =
335 forwardingTreatment(preCondition.build(),
336 encapBuilder.build(),
337 getEthType(intent.selector()));
338 /*
339 * We add the instruction necessary to the transition.
340 */
341 forwardingTreatment.allInstructions().stream()
342 .filter(inst -> inst.type() != Instruction.Type.NOACTION)
343 .forEach(treatmentBuilder::add);
344 /*
345 * Finally we set the output action.
346 */
347 treatmentBuilder.setOutput(outPort);
348 /*
349 * The encapsulation modifies the packet. If we are optimizing
350 * we have to update the state.
351 */
Yi Tseng84c5a3d2017-04-14 16:42:59 -0700352 if (optimizeTreatments()) {
Pier Ventre5c4a0762016-11-21 10:28:13 -0800353 preCondition = encapBuilder;
354 }
355 } else {
356 egressPoints.add(filteredEgressPoint.get());
357 }
358 }
359 /*
360 * The idea is to order the egress points. Before we deal
361 * with the egress points which looks like similar to the
362 * selector derived from the previous state then the
363 * the others.
364 */
365 TrafficSelector prevState = preCondition.build();
Yi Tseng84c5a3d2017-04-14 16:42:59 -0700366 if (optimizeTreatments()) {
Pier Ventre5c4a0762016-11-21 10:28:13 -0800367 egressPoints = orderedEgressPoints(prevState, egressPoints);
368 }
369 /*
370 * In this case, we have to transit to the final
371 * state.
372 */
373 generateEgressActions(treatmentBuilder, egressPoints, prevState, intent);
374
375 }
376
377 /**
Pier Ventre81c47bf2016-11-04 07:26:22 -0700378 * Helper method to generate the egress actions.
379 *
380 * @param treatmentBuilder the treatment builder to update
381 * @param egressPoints the egress points
382 * @param initialState the initial state of the transition
383 */
384 private void generateEgressActions(TrafficTreatment.Builder treatmentBuilder,
385 List<FilteredConnectPoint> egressPoints,
386 TrafficSelector initialState,
387 LinkCollectionIntent intent) {
388
389 TrafficSelector prevState = initialState;
390 for (FilteredConnectPoint egressPoint : egressPoints) {
391 /*
392 * If we are at the egress, we have to transit to the final
393 * state. First we add the Intent treatment.
394 */
395 intent.treatment().allInstructions().stream()
396 .filter(inst -> inst.type() != Instruction.Type.NOACTION)
397 .forEach(treatmentBuilder::add);
398 /*
399 * We generate the transition FIP->FEP.
400 */
401 TrafficTreatment forwardingTreatment =
402 forwardingTreatment(prevState,
403 egressPoint.trafficSelector(),
404 getEthType(intent.selector()));
405 /*
406 * We add the instruction necessary to the transition.
407 * Potentially we override the intent treatment.
408 */
409 forwardingTreatment.allInstructions().stream()
410 .filter(inst -> inst.type() != Instruction.Type.NOACTION)
411 .forEach(treatmentBuilder::add);
412 /*
413 * Finally we set the output action.
414 */
415 treatmentBuilder.setOutput(egressPoint.connectPoint().port());
Yi Tseng84c5a3d2017-04-14 16:42:59 -0700416 if (optimizeTreatments()) {
Pier Ventre81c47bf2016-11-04 07:26:22 -0700417 /*
418 * We update the previous state. In this way instead of
419 * transiting from FIP->FEP we do FEP->FEP and so on.
420 */
421 prevState = egressPoint.trafficSelector();
422 }
423 }
424
425 }
426
427 /**
428 * Helper method to order the egress ports according to a
429 * specified criteria. The idea is to generate first the actions
430 * for the egress ports which are similar to the specified criteria
431 * then the others. In this way we can mitigate the problems related
432 * to the chain of actions and we can optimize also the number of
433 * actions.
434 *
435 * @param orderCriteria the ordering criteria
436 * @param pointsToOrder the egress points to order
437 * @return a list of port ordered
438 */
439 private List<FilteredConnectPoint> orderedEgressPoints(TrafficSelector orderCriteria,
440 List<FilteredConnectPoint> pointsToOrder) {
441 /*
442 * We are interested only to the labels. The idea is to order
443 * by the tags.
444 *
445 */
446 Criterion vlanIdCriterion = orderCriteria.getCriterion(VLAN_VID);
447 Criterion mplsLabelCriterion = orderCriteria.getCriterion(MPLS_LABEL);
448 /*
449 * We collect all the untagged points.
450 *
451 */
452 List<FilteredConnectPoint> untaggedEgressPoints = pointsToOrder
453 .stream()
454 .filter(pointToOrder -> {
455 TrafficSelector selector = pointToOrder.trafficSelector();
456 return selector.getCriterion(VLAN_VID) == null &&
457 selector.getCriterion(MPLS_LABEL) == null;
458 }).collect(Collectors.toList());
459 /*
460 * We collect all the vlan points.
461 */
462 List<FilteredConnectPoint> vlanEgressPoints = pointsToOrder
463 .stream()
464 .filter(pointToOrder -> {
465 TrafficSelector selector = pointToOrder.trafficSelector();
466 return selector.getCriterion(VLAN_VID) != null &&
467 selector.getCriterion(MPLS_LABEL) == null;
468 }).collect(Collectors.toList());
469 /*
470 * We collect all the mpls points.
471 */
472 List<FilteredConnectPoint> mplsEgressPoints = pointsToOrder
473 .stream()
474 .filter(pointToOrder -> {
475 TrafficSelector selector = pointToOrder.trafficSelector();
476 return selector.getCriterion(VLAN_VID) == null &&
477 selector.getCriterion(MPLS_LABEL) != null;
478 }).collect(Collectors.toList());
479 /*
480 * We create the final list of ports.
481 */
482 List<FilteredConnectPoint> orderedList = Lists.newArrayList();
483 /*
484 * The ordering criteria is vlan id. First we add the vlan
485 * ports. Then the others.
486 */
487 if (vlanIdCriterion != null && mplsLabelCriterion == null) {
488 orderedList.addAll(vlanEgressPoints);
489 orderedList.addAll(untaggedEgressPoints);
490 orderedList.addAll(mplsEgressPoints);
491 return orderedList;
492 }
493 /*
494 * The ordering criteria is mpls label. First we add the mpls
495 * ports. Then the others.
496 */
497 if (vlanIdCriterion == null && mplsLabelCriterion != null) {
498 orderedList.addAll(mplsEgressPoints);
499 orderedList.addAll(untaggedEgressPoints);
500 orderedList.addAll(vlanEgressPoints);
501 return orderedList;
502 }
503 /*
504 * The ordering criteria is untagged. First we add the untagged
505 * ports. Then the others.
506 */
507 if (vlanIdCriterion == null && mplsLabelCriterion == null) {
508 orderedList.addAll(untaggedEgressPoints);
509 orderedList.addAll(vlanEgressPoints);
510 orderedList.addAll(mplsEgressPoints);
511 return orderedList;
512 }
513 /*
514 * Unhandled scenario.
515 */
516 orderedList.addAll(vlanEgressPoints);
517 orderedList.addAll(mplsEgressPoints);
518 orderedList.addAll(untaggedEgressPoints);
519 return orderedList;
520 }
521
522 /**
Pier Ventre766995d2016-10-05 22:15:56 -0700523 * Manages the Intents with a single ingress point (p2p, sp2mp)
524 * creating properly the selector builder and the treatment builder.
525 *
526 * @param selectorBuilder the selector builder to update
527 * @param treatmentBuilder the treatment builder to update
528 * @param intent the intent to compile
529 * @param deviceId the current device
530 * @param outPorts the output ports of this device
531 */
532 private void manageSpIntent(TrafficSelector.Builder selectorBuilder,
533 TrafficTreatment.Builder treatmentBuilder,
534 LinkCollectionIntent intent,
535 DeviceId deviceId,
536 Set<PortNumber> outPorts) {
537 /*
538 * Sanity check.
539 */
540 if (intent.filteredIngressPoints().size() != 1) {
541 throw new IntentCompilationException(WRONG_INGRESS);
542 }
543 /*
544 * For the p2p and sp2mp the transition initial state
545 * to final state is performed at the egress.
546 */
547 Optional<FilteredConnectPoint> filteredIngressPoint =
548 intent.filteredIngressPoints().stream().findFirst();
549 /*
550 * We build the final selector, adding the selector
551 * of the FIP to the Intent selector and potentially
552 * overriding its matches.
553 */
554 filteredIngressPoint.get()
555 .trafficSelector()
556 .criteria()
557 .forEach(selectorBuilder::add);
558 /*
559 * In this scenario, potentially we can have several output
Pier Ventre81c47bf2016-11-04 07:26:22 -0700560 * ports. First we have to insert in the treatment the actions
561 * for the core.
Pier Ventre766995d2016-10-05 22:15:56 -0700562 */
Pier Ventre81c47bf2016-11-04 07:26:22 -0700563 List<FilteredConnectPoint> egressPoints = Lists.newArrayList();
Pier Ventre766995d2016-10-05 22:15:56 -0700564 for (PortNumber outPort : outPorts) {
565 Optional<FilteredConnectPoint> filteredEgressPoint =
566 getFilteredConnectPointFromIntent(deviceId, outPort, intent);
Pier Ventre81c47bf2016-11-04 07:26:22 -0700567 if (!filteredEgressPoint.isPresent()) {
568 treatmentBuilder.setOutput(outPort);
569 } else {
570 egressPoints.add(filteredEgressPoint.get());
Pier Ventre766995d2016-10-05 22:15:56 -0700571 }
Pier Ventre766995d2016-10-05 22:15:56 -0700572 }
Pier Ventre81c47bf2016-11-04 07:26:22 -0700573 /*
574 * The idea is to order the egress points. Before we deal
575 * with the egress points which looks like similar to the ingress
576 * point then the others.
577 */
578 TrafficSelector prevState = filteredIngressPoint.get().trafficSelector();
Yi Tseng84c5a3d2017-04-14 16:42:59 -0700579 if (optimizeTreatments()) {
Pier Ventre81c47bf2016-11-04 07:26:22 -0700580 egressPoints = orderedEgressPoints(prevState, egressPoints);
581 }
582 /*
583 * Then we deal with the egress points.
584 */
585 generateEgressActions(treatmentBuilder, egressPoints, prevState, intent);
Pier Ventre766995d2016-10-05 22:15:56 -0700586 }
587
588 /**
589 * Manages the Intents with multiple ingress points creating properly
590 * the selector builder and the treatment builder.
591 *
592 * @param selectorBuilder the selector builder to update
593 * @param treatmentBuilder the treatment builder to update
594 * @param intent the intent to compile
595 * @param inPort the input port of the current device
596 * @param deviceId the current device
597 * @param outPorts the output ports of this device
598 */
599 private void manageMpIntent(TrafficSelector.Builder selectorBuilder,
600 TrafficTreatment.Builder treatmentBuilder,
601 LinkCollectionIntent intent,
602 PortNumber inPort,
603 DeviceId deviceId,
604 Set<PortNumber> outPorts) {
605 /*
606 * Sanity check
607 */
608 if (intent.filteredEgressPoints().size() != 1) {
609 throw new IntentCompilationException(WRONG_EGRESS);
610 }
611 /*
612 * We try to understand if the device is one of the ingress points.
613 */
614 Optional<FilteredConnectPoint> filteredIngressPoint =
615 getFilteredConnectPointFromIntent(deviceId, inPort, intent);
616 /*
617 * We retrieve from the Intent the unique egress points.
618 */
619 Optional<FilteredConnectPoint> filteredEgressPoint =
620 intent.filteredEgressPoints().stream().findFirst();
621 /*
622 * We check if the device is the ingress device
623 */
624 if (filteredIngressPoint.isPresent()) {
625 /*
626 * We are at ingress, so basically what we have to do is this:
627 * apply a set of operations (treatment, FEP) in order to have
628 * a transition from the initial state to the final state.
629 *
630 * We initialize the treatment with the Intent treatment
631 */
632 intent.treatment().allInstructions().stream()
633 .filter(inst -> inst.type() != Instruction.Type.NOACTION)
634 .forEach(treatmentBuilder::add);
635 /*
636 * We build the final selector, adding the selector
637 * of the FIP to the Intent selector and potentially
638 * overriding its matches.
639 */
640 filteredIngressPoint.get()
641 .trafficSelector()
642 .criteria()
643 .forEach(selectorBuilder::add);
644 /*
645 * We define the transition FIP->FEP, basically
646 * the set of the operations we need for reaching
647 * the final state.
648 */
649 TrafficTreatment forwardingTreatment =
650 forwardingTreatment(filteredIngressPoint.get().trafficSelector(),
651 filteredEgressPoint.get().trafficSelector(),
652 getEthType(intent.selector()));
653 /*
654 * We add to the treatment the actions necessary for the
655 * transition, potentially overriding the treatment of the
656 * Intent. The Intent treatment has always a low priority
657 * in respect of the FEP.
658 */
659 forwardingTreatment.allInstructions().stream()
660 .filter(inst -> inst.type() != Instruction.Type.NOACTION)
661 .forEach(treatmentBuilder::add);
662 } else {
663 /*
664 * We are in the core or in the egress switch.
665 * The packets are in their final state. We need
666 * to match against this final state.
667 *
668 * we derive the final state defined by the intent
669 * treatment.
670 */
671 updateBuilder(selectorBuilder, intent.treatment());
672 /*
673 * We derive the final state defined by the unique
674 * FEP. We merge the two states.
675 */
676 filteredEgressPoint.get()
677 .trafficSelector()
678 .criteria()
679 .forEach(selectorBuilder::add);
680 }
681 /*
682 * Finally we set the output action.
683 */
684 outPorts.forEach(treatmentBuilder::setOutput);
685 }
686
687 /**
688 * Computes treatment and selector which will be used
689 * in the flow representation (Rule, Objective).
690 *
691 * @param intent the intent to compile
692 * @param inPort the input port of this device
693 * @param deviceId the current device
694 * @param outPorts the output ports of this device
695 * @return the forwarding instruction object which encapsulates treatment and selector
696 */
697 protected ForwardingInstructions createForwardingInstructions(LinkCollectionIntent intent,
698 PortNumber inPort,
699 DeviceId deviceId,
700 Set<PortNumber> outPorts) {
701
702 /*
703 * We build an empty treatment and we initialize the selector with
704 * the intent selector.
705 */
706 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
707 .builder();
708 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector
709 .builder(intent.selector())
710 .matchInPort(inPort);
711
712 if (!intent.applyTreatmentOnEgress()) {
713 manageMpIntent(selectorBuilder,
714 treatmentBuilder,
715 intent,
716 inPort,
717 deviceId,
718 outPorts
719 );
720 } else {
721 manageSpIntent(selectorBuilder,
722 treatmentBuilder,
723 intent,
724 deviceId,
725 outPorts
726 );
727 }
728 /*
729 * We return selector and treatment necessary to build the flow rule
730 * or the flow objective.
731 */
732 return new ForwardingInstructions(treatmentBuilder.build(), selectorBuilder.build());
733 }
734
735 /**
736 * Manages the ingress of the Intents (p2p, sp2mp, mp2sp) with encapsulation.
737 *
738 * @param selectorBuilder the selector builder to update
739 * @param treatmentBuilder the treatment builder to update
740 * @param intent the intent to compile
741 * @param inPort the input port of this device
742 * @param deviceId the current device
743 * @param outPorts the output ports of this device
744 * @param outLabels the labels associated to the output port
745 * @param type the encapsulation type
746 */
747 private void manageEncapAtIngress(TrafficSelector.Builder selectorBuilder,
748 TrafficTreatment.Builder treatmentBuilder,
749 LinkCollectionIntent intent,
750 PortNumber inPort,
751 DeviceId deviceId,
752 Set<PortNumber> outPorts,
753 Map<ConnectPoint, Identifier<?>> outLabels,
754 EncapsulationType type) {
755
756 Optional<FilteredConnectPoint> filteredIngressPoint =
757 getFilteredConnectPointFromIntent(deviceId, inPort, intent);
758 /*
759 * We fill the selector builder with the intent selector.
760 */
761 intent.selector().criteria().forEach(selectorBuilder::add);
762 /*
763 * We build the final selector, adding the selector
764 * of the FIP to the Intent selector and potentially
765 * overriding its matches.
766 */
767 filteredIngressPoint.get()
768 .trafficSelector()
769 .criteria()
770 .forEach(selectorBuilder::add);
771 /*
Pier Ventre5c4a0762016-11-21 10:28:13 -0800772 * In this case the precondition is the selector of the filtered
773 * ingress point.
Pier Ventre766995d2016-10-05 22:15:56 -0700774 */
Pier Ventre5c4a0762016-11-21 10:28:13 -0800775 TrafficSelector.Builder preCondition = DefaultTrafficSelector
776 .builder(filteredIngressPoint.get().trafficSelector());
777 /*
778 * Generate the output actions.
779 */
780 manageOutputPorts(
781 outPorts,
782 deviceId,
783 intent,
784 outLabels,
785 type,
786 preCondition,
787 treatmentBuilder
788 );
Pier Ventre766995d2016-10-05 22:15:56 -0700789
790 }
791
792 /**
793 * Manages the core and transit of the Intents (p2p, sp2mp, mp2sp)
794 * with encapsulation.
795 *
796 * @param selectorBuilder the selector builder to update
797 * @param treatmentBuilder the treatment builder to update
798 * @param intent the intent to compile
799 * @param inPort the input port of this device
800 * @param inLabel the label associated to the input port
801 * @param deviceId the current device
802 * @param outPorts the output ports of this device
803 * @param outLabels the labels associated to the output port
804 * @param type the encapsulation type
805 */
806 private void manageEncapAtCoreAndEgress(TrafficSelector.Builder selectorBuilder,
807 TrafficTreatment.Builder treatmentBuilder,
808 LinkCollectionIntent intent,
809 PortNumber inPort,
810 Identifier<?> inLabel,
811 DeviceId deviceId,
812 Set<PortNumber> outPorts,
813 Map<ConnectPoint, Identifier<?>> outLabels,
814 EncapsulationType type) {
815
816 /*
817 * If there are not labels, we cannot handle.
818 */
819 ConnectPoint inCp = new ConnectPoint(deviceId, inPort);
820 if (inLabel == null) {
821 throw new IntentCompilationException(String.format(NO_LABELS, inCp));
822 }
823 /*
824 * In the core and at egress we match using encapsulation.
825 */
826 updateSelectorFromEncapsulation(
827 selectorBuilder,
828 type,
829 inLabel
830 );
831 /*
Pier Ventre5c4a0762016-11-21 10:28:13 -0800832 * Generate the output actions.
Pier Ventre766995d2016-10-05 22:15:56 -0700833 */
Pier Ventre5c4a0762016-11-21 10:28:13 -0800834 manageOutputPorts(
835 outPorts,
836 deviceId,
837 intent,
838 outLabels,
839 type,
840 selectorBuilder,
841 treatmentBuilder
842 );
843
Pier Ventre766995d2016-10-05 22:15:56 -0700844 }
845
846 /**
847 * Computes treatment and selector which will be used
848 * in the flow representation (Rule, Objective).
849 *
850 * @param intent the intent to compile
851 * @param inPort the input port of this device
852 * @param inLabel the label associated to the input port
853 * @param deviceId the current device
854 * @param outPorts the output ports of this device
855 * @param outLabels the labels associated to the output port
856 * @param type the encapsulation type
857 * @return the forwarding instruction object which encapsulates treatment and selector
858 */
859 protected ForwardingInstructions createForwardingInstructions(LinkCollectionIntent intent,
860 PortNumber inPort,
861 Identifier<?> inLabel,
862 DeviceId deviceId,
863 Set<PortNumber> outPorts,
864 Map<ConnectPoint, Identifier<?>> outLabels,
865 EncapsulationType type) {
866 /*
867 * We build an empty treatment and an empty selector.
868 */
869 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
870 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
871 selectorBuilder.matchInPort(inPort);
872 Optional<FilteredConnectPoint> filteredIngressPoint =
873 getFilteredConnectPointFromIntent(deviceId, inPort, intent);
874
875 if (filteredIngressPoint.isPresent()) {
876 manageEncapAtIngress(selectorBuilder,
877 treatmentBuilder,
878 intent,
879 inPort,
880 deviceId,
881 outPorts,
882 outLabels,
883 type
884 );
885 } else {
886 manageEncapAtCoreAndEgress(selectorBuilder,
887 treatmentBuilder,
888 intent,
889 inPort,
890 inLabel,
891 deviceId,
892 outPorts,
893 outLabels,
894 type);
895 }
896 /*
897 * We return selector and treatment necessary to build the flow rule
898 * or the flow objective.
899 */
900 return new ForwardingInstructions(treatmentBuilder.build(), selectorBuilder.build());
901 }
902
903 /**
904 * Helper class to encapsulate treatment and selector
905 * in an unique abstraction.
Pier Ventred48320e2016-08-17 16:25:47 -0700906 */
907 protected class ForwardingInstructions {
908
909 private TrafficTreatment trafficTreatment;
910
911 private TrafficSelector trafficSelector;
912
913 public ForwardingInstructions(TrafficTreatment treatment, TrafficSelector selector) {
914
915 this.trafficTreatment = treatment;
916 this.trafficSelector = selector;
917
918 }
919
920 public TrafficTreatment treatment() {
921 return this.trafficTreatment;
922 }
923
924 public TrafficSelector selector() {
925 return this.trafficSelector;
926 }
927
928 }
929
930 /**
Pier Ventre766995d2016-10-05 22:15:56 -0700931 * Helper method to compute input and output ports
932 * for each device crossed in the path.
Pier Ventred48320e2016-08-17 16:25:47 -0700933 *
934 * @param intent the related intents
935 * @param inputPorts the input ports to compute
936 * @param outputPorts the output ports to compute
937 */
938 protected void computePorts(LinkCollectionIntent intent,
939 SetMultimap<DeviceId, PortNumber> inputPorts,
940 SetMultimap<DeviceId, PortNumber> outputPorts) {
941
942 for (Link link : intent.links()) {
943 inputPorts.put(link.dst().deviceId(), link.dst().port());
944 outputPorts.put(link.src().deviceId(), link.src().port());
945 }
946
947 for (ConnectPoint ingressPoint : intent.ingressPoints()) {
948 inputPorts.put(ingressPoint.deviceId(), ingressPoint.port());
949 }
950
951 for (ConnectPoint egressPoint : intent.egressPoints()) {
952 outputPorts.put(egressPoint.deviceId(), egressPoint.port());
953 }
Pier Ventred48320e2016-08-17 16:25:47 -0700954
955 }
956
957 /**
Pier Ventre766995d2016-10-05 22:15:56 -0700958 * Retrieves the encapsulation constraint from the link collection intent.
Pier Ventred48320e2016-08-17 16:25:47 -0700959 *
Pier Ventre766995d2016-10-05 22:15:56 -0700960 * @param intent the intent to analyze
961 * @return the encapsulation constraint
Pier Ventred48320e2016-08-17 16:25:47 -0700962 */
Pier Ventre766995d2016-10-05 22:15:56 -0700963 protected Optional<EncapsulationConstraint> getIntentEncapConstraint(LinkCollectionIntent intent) {
964 return intent.constraints().stream()
965 .filter(constraint -> constraint instanceof EncapsulationConstraint)
966 .map(x -> (EncapsulationConstraint) x).findAny();
Pier Ventred48320e2016-08-17 16:25:47 -0700967 }
968
Thomas Szyrkowiec770093f2017-05-05 13:06:53 +0200969 /**
970 * Checks if domain processing is enabled for this intent by looking for the {@link DomainConstraint}.
971 *
972 * @param intent the intent to be checked
973 * @return is the processing of domains enabled
974 */
975 protected boolean isDomainProcessingEnabled(LinkCollectionIntent intent) {
976 return intent.constraints().contains(DomainConstraint.domain());
977 }
978
979 /**
980 * Creates the domain intents that the {@link LinkCollectionIntent} contains.
981 *
982 * @param intent the link collection intent
983 * @param domainService the domain service
984 * @return the resulting list of domain intents
985 */
986 protected List<Intent> getDomainIntents(LinkCollectionIntent intent,
987 DomainService domainService) {
988 ImmutableList.Builder<Intent> intentList = ImmutableList.builder();
989 // domain handling is only applied for a single entry and exit point
990 // TODO: support multi point to multi point
991 if (intent.filteredIngressPoints().size() != 1 || intent
992 .filteredEgressPoints().size() != 1) {
993 log.warn("Multiple ingress or egress ports not supported!");
994 return intentList.build();
995 }
996 ImmutableList.Builder<Link> domainLinks = ImmutableList.builder();
997 // get the initial ingress connection point
998 FilteredConnectPoint ingress =
999 intent.filteredIngressPoints().iterator().next();
1000 FilteredConnectPoint egress;
1001 DeviceId currentDevice = ingress.connectPoint().deviceId();
1002 // the current domain (or LOCAL)
1003 DomainId currentDomain = domainService.getDomain(currentDevice);
1004 // if we entered a domain store the domain ingress
1005 FilteredConnectPoint domainIngress =
1006 LOCAL.equals(currentDomain) ? null : ingress;
1007 // loop until (hopefully) all links have been checked once
1008 // this is necessary because a set is not sorted by default
1009 for (int i = 0; i < intent.links().size(); i++) {
1010 // find the next link
1011 List<Link> nextLinks =
1012 getEgressLinks(intent.links(), currentDevice);
1013 // no matching link exists
1014 if (nextLinks.isEmpty()) {
1015 throw new IntentCompilationException(
1016 "No matching link starting at " + ingress
1017 .connectPoint().deviceId());
1018 }
1019 // get the first link
1020 Link nextLink = nextLinks.get(0);
1021 ingress = new FilteredConnectPoint(nextLink.src());
1022 egress = new FilteredConnectPoint(nextLink.dst());
1023 // query the domain for the domain of the link's destination
1024 DomainId dstDomain = domainService
1025 .getDomain(egress.connectPoint().deviceId());
1026 if (!currentDomain.equals(dstDomain)) {
1027 // we are leaving the current domain or LOCAL
1028 log.debug("Domain transition from {} to {}.", currentDomain,
1029 dstDomain);
1030 if (!LOCAL.equals(currentDomain)) {
1031 // add the domain intent to the intent list
1032 intentList.add(createDomainP2PIntent(intent, domainIngress,
1033 ingress,
1034 domainLinks.build()));
1035 // TODO: might end up with an unused builder
1036 // reset domain links builder
1037 domainLinks = ImmutableList.builder();
1038 }
1039 // update current domain (might be LOCAL)
1040 currentDomain = dstDomain;
1041 // update the domain's ingress
1042 domainIngress = LOCAL.equals(currentDomain) ? null : egress;
1043 } else {
1044 if (!LOCAL.equals(currentDomain)) {
1045 // we are staying in the same domain, store the traversed link
1046 domainLinks.add(nextLink);
1047 log.debug("{} belongs to the same domain.",
1048 egress.connectPoint().deviceId());
1049 }
1050 }
1051 currentDevice = egress.connectPoint().deviceId();
1052 }
1053 // get the egress point
1054 egress = intent.filteredEgressPoints().iterator().next();
1055 // still inside a domain?
1056 if (!LOCAL.equals(currentDomain) &&
1057 currentDomain.equals(domainService.getDomain(
1058 egress.connectPoint().deviceId()))) {
1059 // add intent
1060 intentList.add(createDomainP2PIntent(intent, domainIngress, egress,
1061 domainLinks.build()));
1062 }
1063
1064 return intentList.build();
1065 }
1066
1067 /**
1068 * Create a domain point to point intent from the parameters.
1069 *
1070 * @param originalIntent the original intent to extract the app ID and key
1071 * @param ingress the ingress connection point
1072 * @param egress the egress connection point
1073 * @param domainLinks the list of traversed links
1074 * @return the domain point to point intent
1075 */
1076 private static DomainPointToPointIntent createDomainP2PIntent(
1077 Intent originalIntent, FilteredConnectPoint ingress,
1078 FilteredConnectPoint egress, List<Link> domainLinks) {
1079 return DomainPointToPointIntent.builder()
1080 .appId(originalIntent.appId())
1081 .filteredIngressPoint(ingress)
1082 .filteredEgressPoint(egress)
1083 .key(originalIntent.key())
1084 .links(domainLinks)
1085 .build();
1086 }
1087
1088 /**
1089 * Get links originating from the source device ID.
1090 *
1091 * @param links list of available links
1092 * @param source the device ID of the source device
1093 * @return the list of links with the given source
1094 */
1095 private List<Link> getEgressLinks(Set<Link> links, final DeviceId source) {
1096 return links.stream()
1097 .filter(link -> link.src().deviceId().equals(source))
1098 .collect(Collectors.toList());
1099 }
1100
Pier Ventred48320e2016-08-17 16:25:47 -07001101
1102 /**
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001103 * Get FilteredConnectPoint from LinkCollectionIntent.
Pier Ventre766995d2016-10-05 22:15:56 -07001104 *
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001105 * @param deviceId device Id for connect point
1106 * @param portNumber port number
1107 * @param intent source intent
1108 * @return filtered connetion point
1109 */
1110 private Optional<FilteredConnectPoint> getFilteredConnectPointFromIntent(DeviceId deviceId,
1111 PortNumber portNumber,
1112 LinkCollectionIntent intent) {
1113 Set<FilteredConnectPoint> filteredConnectPoints =
1114 Sets.union(intent.filteredIngressPoints(), intent.filteredEgressPoints());
1115 return filteredConnectPoints.stream()
1116 .filter(port -> port.connectPoint().deviceId().equals(deviceId))
1117 .filter(port -> port.connectPoint().port().equals(portNumber))
1118 .findFirst();
1119 }
1120
1121 /**
1122 * Get tag criterion from selector.
1123 * The criterion should be one of type in tagCriterionTypes.
1124 *
1125 * @param selector selector
1126 * @return Criterion that matched, if there is no tag criterion, return null
1127 */
1128 private Criterion getTagCriterion(TrafficSelector selector) {
1129 return selector.criteria().stream()
1130 .filter(criterion -> TAG_CRITERION_TYPES.contains(criterion.type()))
1131 .findFirst()
1132 .orElse(Criteria.dummy());
1133
1134 }
1135
1136 /**
1137 * Compares tag type between ingress and egress point and generate
1138 * treatment for egress point of intent.
1139 *
Pier Ventre766995d2016-10-05 22:15:56 -07001140 * @param ingress ingress selector for the intent
1141 * @param egress egress selector for the intent
1142 * @param ethType the ethertype to use in mpls_pop
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001143 * @return Builder of TrafficTreatment
1144 */
Pier Ventre766995d2016-10-05 22:15:56 -07001145 private TrafficTreatment forwardingTreatment(TrafficSelector ingress,
1146 TrafficSelector egress,
1147 EthType ethType) {
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001148
1149
Pier Ventre766995d2016-10-05 22:15:56 -07001150 if (ingress.equals(egress)) {
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001151 return DefaultTrafficTreatment.emptyTreatment();
1152 }
1153
1154 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
1155
1156 /*
1157 * "null" means there is no tag for the port
1158 * Tag criterion will be null if port is normal connection point
1159 */
Pier Ventre766995d2016-10-05 22:15:56 -07001160 Criterion ingressTagCriterion = getTagCriterion(ingress);
1161 Criterion egressTagCriterion = getTagCriterion(egress);
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001162
1163 if (ingressTagCriterion.type() != egressTagCriterion.type()) {
1164
1165 /*
1166 * Tag type of ingress port and egress port are different.
1167 * Need to remove tag from ingress, then add new tag for egress.
1168 * Remove nothing if ingress port use VXLAN or there is no tag
1169 * on ingress port.
1170 */
1171 switch (ingressTagCriterion.type()) {
1172 case VLAN_VID:
1173 builder.popVlan();
1174 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001175
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001176 case MPLS_LABEL:
Pier Ventre81c47bf2016-11-04 07:26:22 -07001177 if (copyTtl) {
1178 builder.copyTtlIn();
1179 }
Pier Ventre766995d2016-10-05 22:15:56 -07001180 builder.popMpls(ethType);
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001181 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001182
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001183 default:
1184 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001185
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001186 }
1187
1188 /*
1189 * Push new tag for egress port.
1190 */
1191 switch (egressTagCriterion.type()) {
1192 case VLAN_VID:
1193 builder.pushVlan();
1194 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001195
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001196 case MPLS_LABEL:
1197 builder.pushMpls();
Pier Ventre81c47bf2016-11-04 07:26:22 -07001198 if (copyTtl) {
1199 builder.copyTtlOut();
1200 }
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001201 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001202
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001203 default:
1204 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001205
Pier Ventred48320e2016-08-17 16:25:47 -07001206 }
1207 }
1208
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001209 switch (egressTagCriterion.type()) {
1210 case VLAN_VID:
1211 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) egressTagCriterion;
1212 builder.setVlanId(vlanIdCriterion.vlanId());
1213 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001214
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001215 case MPLS_LABEL:
1216 MplsCriterion mplsCriterion = (MplsCriterion) egressTagCriterion;
1217 builder.setMpls(mplsCriterion.label());
1218 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001219
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001220 case TUNNEL_ID:
1221 TunnelIdCriterion tunnelIdCriterion = (TunnelIdCriterion) egressTagCriterion;
1222 builder.setTunnelId(tunnelIdCriterion.tunnelId());
1223 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001224
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001225 default:
1226 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001227
Pier Ventre27d42572016-08-29 17:37:08 -07001228 }
Pier Ventre27d42572016-08-29 17:37:08 -07001229
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001230 return builder.build();
Pier Ventre647138f2016-08-26 17:32:44 -07001231 }
1232
1233 /**
Pier Ventred48320e2016-08-17 16:25:47 -07001234 * Update the selector builder using a L0 instruction.
1235 *
1236 * @param builder the builder to update
1237 * @param l0instruction the l0 instruction to use
1238 */
1239 private void updateBuilder(TrafficSelector.Builder builder, L0ModificationInstruction l0instruction) {
Pier Ventre766995d2016-10-05 22:15:56 -07001240 throw new IntentCompilationException(UNSUPPORTED_L0);
Pier Ventred48320e2016-08-17 16:25:47 -07001241 }
1242
1243 /**
1244 * Update the selector builder using a L1 instruction.
1245 *
1246 * @param builder the builder to update
1247 * @param l1instruction the l1 instruction to use
1248 */
1249 private void updateBuilder(TrafficSelector.Builder builder, L1ModificationInstruction l1instruction) {
Pier Ventre766995d2016-10-05 22:15:56 -07001250 throw new IntentCompilationException(UNSUPPORTED_L1);
Pier Ventred48320e2016-08-17 16:25:47 -07001251 }
1252
1253 /**
1254 * Update the selector builder using a L2 instruction.
1255 *
1256 * @param builder the builder to update
1257 * @param l2instruction the l2 instruction to use
1258 */
1259 private void updateBuilder(TrafficSelector.Builder builder, L2ModificationInstruction l2instruction) {
1260 switch (l2instruction.subtype()) {
1261 case ETH_SRC:
1262 case ETH_DST:
1263 ModEtherInstruction ethInstr = (ModEtherInstruction) l2instruction;
1264 switch (ethInstr.subtype()) {
1265 case ETH_SRC:
1266 builder.matchEthSrc(ethInstr.mac());
1267 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001268
Pier Ventred48320e2016-08-17 16:25:47 -07001269 case ETH_DST:
1270 builder.matchEthDst(ethInstr.mac());
1271 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001272
Pier Ventred48320e2016-08-17 16:25:47 -07001273 default:
Pier Ventre766995d2016-10-05 22:15:56 -07001274 throw new IntentCompilationException(UNSUPPORTED_ETH_SUBTYPE);
Pier Ventred48320e2016-08-17 16:25:47 -07001275 }
1276 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001277
Pier Ventred48320e2016-08-17 16:25:47 -07001278 case VLAN_ID:
1279 ModVlanIdInstruction vlanIdInstr = (ModVlanIdInstruction) l2instruction;
1280 builder.matchVlanId(vlanIdInstr.vlanId());
1281 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001282
Pier Ventred48320e2016-08-17 16:25:47 -07001283 case VLAN_PUSH:
1284 //FIXME
1285 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001286
Pier Ventred48320e2016-08-17 16:25:47 -07001287 case VLAN_POP:
1288 //TODO how do we handle dropped label? remove the selector?
Pier Ventre766995d2016-10-05 22:15:56 -07001289 throw new IntentCompilationException(UNSUPPORTED_POP_ACTION);
Pier Ventred48320e2016-08-17 16:25:47 -07001290 case VLAN_PCP:
1291 ModVlanPcpInstruction vlanPcpInstruction = (ModVlanPcpInstruction) l2instruction;
1292 builder.matchVlanPcp(vlanPcpInstruction.vlanPcp());
1293 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001294
Pier Ventred48320e2016-08-17 16:25:47 -07001295 case MPLS_LABEL:
1296 case MPLS_PUSH:
1297 //FIXME
1298 ModMplsLabelInstruction mplsInstr = (ModMplsLabelInstruction) l2instruction;
1299 builder.matchMplsLabel(mplsInstr.label());
1300 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001301
Pier Ventred48320e2016-08-17 16:25:47 -07001302 case MPLS_POP:
1303 //TODO how do we handle dropped label? remove the selector?
Pier Ventre766995d2016-10-05 22:15:56 -07001304 throw new IntentCompilationException(UNSUPPORTED_POP_ACTION);
Pier Ventred48320e2016-08-17 16:25:47 -07001305 case DEC_MPLS_TTL:
1306 // no-op
1307 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001308
Pier Ventred48320e2016-08-17 16:25:47 -07001309 case MPLS_BOS:
1310 ModMplsBosInstruction mplsBosInstr = (ModMplsBosInstruction) l2instruction;
1311 builder.matchMplsBos(mplsBosInstr.mplsBos());
1312 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001313
Pier Ventred48320e2016-08-17 16:25:47 -07001314 case TUNNEL_ID:
1315 ModTunnelIdInstruction tunInstr = (ModTunnelIdInstruction) l2instruction;
1316 builder.matchTunnelId(tunInstr.tunnelId());
1317 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001318
Pier Ventred48320e2016-08-17 16:25:47 -07001319 default:
Pier Ventre766995d2016-10-05 22:15:56 -07001320 throw new IntentCompilationException(UNSUPPORTED_L2);
Pier Ventred48320e2016-08-17 16:25:47 -07001321 }
1322
1323 }
1324
1325 /**
1326 * Update the selector builder using a L3 instruction.
1327 *
1328 * @param builder the builder to update
1329 * @param l3instruction the l3 instruction to use
1330 */
1331 private void updateBuilder(TrafficSelector.Builder builder, L3ModificationInstruction l3instruction) {
1332 // TODO check ethernet proto
1333 switch (l3instruction.subtype()) {
1334 case IPV4_SRC:
1335 case IPV4_DST:
1336 case IPV6_SRC:
1337 case IPV6_DST:
1338 ModIPInstruction ipInstr = (ModIPInstruction) l3instruction;
1339 // TODO check if ip falls in original prefix
1340 IpPrefix prefix = ipInstr.ip().toIpPrefix();
1341 switch (ipInstr.subtype()) {
1342 case IPV4_SRC:
1343 builder.matchIPSrc(prefix);
1344 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001345
Pier Ventred48320e2016-08-17 16:25:47 -07001346 case IPV4_DST:
1347 builder.matchIPSrc(prefix);
1348 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001349
Pier Ventred48320e2016-08-17 16:25:47 -07001350 case IPV6_SRC:
1351 builder.matchIPv6Src(prefix);
1352 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001353
Pier Ventred48320e2016-08-17 16:25:47 -07001354 case IPV6_DST:
1355 builder.matchIPv6Dst(prefix);
1356 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001357
Pier Ventred48320e2016-08-17 16:25:47 -07001358 default:
Pier Ventre766995d2016-10-05 22:15:56 -07001359 throw new IntentCompilationException(UNSUPPORTED_IP_SUBTYPE);
Pier Ventred48320e2016-08-17 16:25:47 -07001360 }
1361 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001362
Pier Ventred48320e2016-08-17 16:25:47 -07001363 case IPV6_FLABEL:
1364 ModIPv6FlowLabelInstruction ipFlowInstr = (ModIPv6FlowLabelInstruction) l3instruction;
1365 builder.matchIPv6FlowLabel(ipFlowInstr.flowLabel());
1366 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001367
Pier Ventred48320e2016-08-17 16:25:47 -07001368 case DEC_TTL:
1369 // no-op
1370 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001371
Pier Ventred48320e2016-08-17 16:25:47 -07001372 case TTL_OUT:
1373 // no-op
1374 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001375
Pier Ventred48320e2016-08-17 16:25:47 -07001376 case TTL_IN:
1377 // no-op
1378 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001379
Pier Ventred48320e2016-08-17 16:25:47 -07001380 case ARP_SPA:
1381 ModArpIPInstruction arpIpInstr = (ModArpIPInstruction) l3instruction;
1382 if (arpIpInstr.ip().isIp4()) {
1383 builder.matchArpSpa((Ip4Address) arpIpInstr.ip());
1384 } else {
Pier Ventre766995d2016-10-05 22:15:56 -07001385 throw new IntentCompilationException(UNSUPPORTED_ARP);
Pier Ventred48320e2016-08-17 16:25:47 -07001386 }
1387 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001388
Pier Ventred48320e2016-08-17 16:25:47 -07001389 case ARP_SHA:
1390 ModArpEthInstruction arpEthInstr = (ModArpEthInstruction) l3instruction;
1391 builder.matchArpSha(arpEthInstr.mac());
1392 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001393
Pier Ventred48320e2016-08-17 16:25:47 -07001394 case ARP_OP:
1395 ModArpOpInstruction arpOpInstr = (ModArpOpInstruction) l3instruction;
1396 //FIXME is the long to int cast safe?
1397 builder.matchArpOp((int) arpOpInstr.op());
1398 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001399
Pier Ventred48320e2016-08-17 16:25:47 -07001400 default:
Pier Ventre766995d2016-10-05 22:15:56 -07001401 throw new IntentCompilationException(UNSUPPORTED_L3);
Pier Ventred48320e2016-08-17 16:25:47 -07001402 }
1403 }
1404
1405 /**
1406 * Update the selector builder using a L4 instruction.
1407 *
1408 * @param builder the builder to update
1409 * @param l4instruction the l4 instruction to use
1410 */
1411 private void updateBuilder(TrafficSelector.Builder builder, L4ModificationInstruction l4instruction) {
1412 if (l4instruction instanceof ModTransportPortInstruction) {
1413 // TODO check IP proto
1414 ModTransportPortInstruction l4mod = (ModTransportPortInstruction) l4instruction;
1415 switch (l4mod.subtype()) {
1416 case TCP_SRC:
1417 builder.matchTcpSrc(l4mod.port());
1418 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001419
Pier Ventred48320e2016-08-17 16:25:47 -07001420 case TCP_DST:
1421 builder.matchTcpDst(l4mod.port());
1422 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001423
Pier Ventred48320e2016-08-17 16:25:47 -07001424 case UDP_SRC:
1425 builder.matchUdpSrc(l4mod.port());
1426 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001427
Pier Ventred48320e2016-08-17 16:25:47 -07001428 case UDP_DST:
1429 builder.matchUdpDst(l4mod.port());
1430 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001431
Pier Ventred48320e2016-08-17 16:25:47 -07001432 default:
Pier Ventre766995d2016-10-05 22:15:56 -07001433 throw new IntentCompilationException(UNSUPPORTED_L4_SUBTYPE);
Pier Ventred48320e2016-08-17 16:25:47 -07001434 }
1435 } else {
Pier Ventre766995d2016-10-05 22:15:56 -07001436 throw new IntentCompilationException(UNSUPPORTED_L4);
Pier Ventred48320e2016-08-17 16:25:47 -07001437 }
1438 }
1439
1440 /**
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001441 * Update selector builder by using treatment.
Pier Ventred48320e2016-08-17 16:25:47 -07001442 *
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001443 * @param builder builder to update
1444 * @param treatment traffic treatment
Pier Ventred48320e2016-08-17 16:25:47 -07001445 */
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001446 private void updateBuilder(TrafficSelector.Builder builder, TrafficTreatment treatment) {
1447
1448 treatment.allInstructions().forEach(instruction -> {
1449 switch (instruction.type()) {
1450 case L0MODIFICATION:
1451 updateBuilder(builder, (L0ModificationInstruction) instruction);
1452 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001453
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001454 case L1MODIFICATION:
1455 updateBuilder(builder, (L1ModificationInstruction) instruction);
1456 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001457
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001458 case L2MODIFICATION:
1459 updateBuilder(builder, (L2ModificationInstruction) instruction);
1460 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001461
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001462 case L3MODIFICATION:
1463 updateBuilder(builder, (L3ModificationInstruction) instruction);
1464 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001465
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001466 case L4MODIFICATION:
1467 updateBuilder(builder, (L4ModificationInstruction) instruction);
1468 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001469
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001470 case NOACTION:
1471 case OUTPUT:
1472 case GROUP:
1473 case QUEUE:
1474 case TABLE:
1475 case METER:
1476 case METADATA:
1477 case EXTENSION: // TODO is extension no-op or unsupported?
1478 // Nothing to do
1479 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001480
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001481 default:
Pier Ventre766995d2016-10-05 22:15:56 -07001482 throw new IntentCompilationException(UNSUPPORTED_INSTRUCTION);
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001483 }
1484 });
1485
Pier Ventred48320e2016-08-17 16:25:47 -07001486 }
1487
Pier Ventre766995d2016-10-05 22:15:56 -07001488 /**
1489 * The method generates a selector starting from
1490 * the encapsulation information (type and label to match).
1491 *
1492 * @param selectorBuilder the builder to update
1493 * @param type the type of encapsulation
1494 * @param identifier the label to match
1495 */
1496 private void updateSelectorFromEncapsulation(TrafficSelector.Builder selectorBuilder,
1497 EncapsulationType type,
1498 Identifier<?> identifier) {
1499 switch (type) {
1500 case MPLS:
1501 MplsLabel label = (MplsLabel) identifier;
1502 selectorBuilder.matchMplsLabel(label);
1503 selectorBuilder.matchEthType(Ethernet.MPLS_UNICAST);
1504 break;
1505
1506 case VLAN:
1507 VlanId id = (VlanId) identifier;
1508 selectorBuilder.matchVlanId(id);
1509 break;
1510
1511 default:
1512 throw new IntentCompilationException(UNKNOWN_ENCAPSULATION);
1513 }
1514 }
1515
1516 /**
1517 * Helper function to define the match on the ethertype.
1518 * If the selector define an ethertype we will use it,
1519 * otherwise IPv4 will be used by default.
1520 *
1521 * @param selector the traffic selector
1522 * @return the ethertype we should match
1523 */
1524 private EthType getEthType(TrafficSelector selector) {
1525 Criterion c = selector.getCriterion(Criterion.Type.ETH_TYPE);
1526 if (c != null && c instanceof EthTypeCriterion) {
1527 EthTypeCriterion ethertype = (EthTypeCriterion) c;
1528 return ethertype.ethType();
1529 }
1530 return EthType.EtherType.IPV4.ethType();
1531 }
1532
Pier Ventred48320e2016-08-17 16:25:47 -07001533}