blob: 2176a2084b85063409f51b4ea4aa6b87b56d41d1 [file] [log] [blame]
Pier Ventred48320e2016-08-17 16:25:47 -07001/*
2 * Copyright 2016-present Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.net.intent.impl.compiler;
18
Pier Ventre81c47bf2016-11-04 07:26:22 -070019import com.google.common.collect.Lists;
Pier Ventre766995d2016-10-05 22:15:56 -070020import com.google.common.collect.Maps;
Pier Ventred48320e2016-08-17 16:25:47 -070021import com.google.common.collect.SetMultimap;
Yi Tseng2a81c9d2016-09-14 10:14:24 -070022import com.google.common.collect.Sets;
Pier Ventre766995d2016-10-05 22:15:56 -070023import org.onlab.packet.EthType;
24import org.onlab.packet.Ethernet;
Pier Ventred48320e2016-08-17 16:25:47 -070025import org.onlab.packet.Ip4Address;
26import org.onlab.packet.IpPrefix;
Pier Ventre766995d2016-10-05 22:15:56 -070027import org.onlab.packet.MplsLabel;
28import org.onlab.packet.VlanId;
29import org.onlab.util.Identifier;
Pier Ventred48320e2016-08-17 16:25:47 -070030import org.onosproject.net.ConnectPoint;
31import org.onosproject.net.DeviceId;
Pier Ventre766995d2016-10-05 22:15:56 -070032import org.onosproject.net.EncapsulationType;
Pier Ventre81c47bf2016-11-04 07:26:22 -070033import org.onosproject.net.FilteredConnectPoint;
Pier Ventred48320e2016-08-17 16:25:47 -070034import org.onosproject.net.Link;
35import org.onosproject.net.PortNumber;
36import org.onosproject.net.flow.DefaultTrafficSelector;
37import org.onosproject.net.flow.DefaultTrafficTreatment;
38import org.onosproject.net.flow.TrafficSelector;
39import org.onosproject.net.flow.TrafficTreatment;
Yi Tseng2a81c9d2016-09-14 10:14:24 -070040import org.onosproject.net.flow.criteria.Criteria;
41import org.onosproject.net.flow.criteria.Criterion;
Pier Ventre766995d2016-10-05 22:15:56 -070042import org.onosproject.net.flow.criteria.EthTypeCriterion;
Yi Tseng2a81c9d2016-09-14 10:14:24 -070043import org.onosproject.net.flow.criteria.MplsCriterion;
44import org.onosproject.net.flow.criteria.TunnelIdCriterion;
45import org.onosproject.net.flow.criteria.VlanIdCriterion;
Pier Ventre27d42572016-08-29 17:37:08 -070046import org.onosproject.net.flow.instructions.Instruction;
Pier Ventred48320e2016-08-17 16:25:47 -070047import org.onosproject.net.flow.instructions.L0ModificationInstruction;
48import org.onosproject.net.flow.instructions.L1ModificationInstruction;
49import org.onosproject.net.flow.instructions.L2ModificationInstruction;
50import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
Pier Ventre81c47bf2016-11-04 07:26:22 -070051import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsBosInstruction;
52import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction;
53import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModTunnelIdInstruction;
Pier Ventred48320e2016-08-17 16:25:47 -070054import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
55import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanPcpInstruction;
56import org.onosproject.net.flow.instructions.L3ModificationInstruction;
Pier Ventre81c47bf2016-11-04 07:26:22 -070057import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpEthInstruction;
58import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpIPInstruction;
59import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpOpInstruction;
Pier Ventred48320e2016-08-17 16:25:47 -070060import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPInstruction;
61import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPv6FlowLabelInstruction;
62import org.onosproject.net.flow.instructions.L4ModificationInstruction;
63import org.onosproject.net.flow.instructions.L4ModificationInstruction.ModTransportPortInstruction;
64import org.onosproject.net.intent.IntentCompilationException;
65import org.onosproject.net.intent.LinkCollectionIntent;
Pier Ventre766995d2016-10-05 22:15:56 -070066import org.onosproject.net.intent.constraint.EncapsulationConstraint;
67import org.onosproject.net.resource.impl.LabelAllocator;
Pier Ventred48320e2016-08-17 16:25:47 -070068
69import java.util.List;
Pier Ventre766995d2016-10-05 22:15:56 -070070import java.util.Map;
Pier Ventre647138f2016-08-26 17:32:44 -070071import java.util.Optional;
Pier Ventred48320e2016-08-17 16:25:47 -070072import java.util.Set;
Pier Ventre81c47bf2016-11-04 07:26:22 -070073import java.util.stream.Collectors;
Pier Ventred48320e2016-08-17 16:25:47 -070074
Pier Ventre81c47bf2016-11-04 07:26:22 -070075import static org.onosproject.net.flow.criteria.Criterion.Type.*;
Yi Tseng2a81c9d2016-09-14 10:14:24 -070076
Pier Ventred48320e2016-08-17 16:25:47 -070077/**
78 * Shared APIs and implementations for Link Collection compilers.
79 */
80public class LinkCollectionCompiler<T> {
81
Pier Ventre766995d2016-10-05 22:15:56 -070082 /**
83 * Reference to the label allocator.
84 */
85 static LabelAllocator labelAllocator;
86
87 /**
Pier Ventre81c47bf2016-11-04 07:26:22 -070088 * Influence compiler behavior. If true the compiler
89 * try to optimize the chain of the actions.
90 */
91 static boolean optimize;
92
93 /**
94 * Influence compiler behavior. If true the compiler
95 * try to optimize the copy ttl actions.
96 */
97 static boolean copyTtl;
98
99 /**
Pier Ventre766995d2016-10-05 22:15:56 -0700100 * The allowed tag criterions.
101 */
Yi Tseng2a81c9d2016-09-14 10:14:24 -0700102 private static final Set<Criterion.Type> TAG_CRITERION_TYPES =
103 Sets.immutableEnumSet(VLAN_VID, MPLS_LABEL, TUNNEL_ID);
104
Pier Ventred48320e2016-08-17 16:25:47 -0700105 /**
Pier Ventre766995d2016-10-05 22:15:56 -0700106 * Error message for wrong egress scenario.
107 */
108 private static final String WRONG_EGRESS = "Egress points not equal to 1 " +
109 "and apply treatment at ingress, " +
110 "which treatments should I apply ???";
111
112 /**
113 * Error message for wrong ingress scenario.
114 */
115 private static final String WRONG_INGRESS = "Ingress points not equal to 1 " +
116 "and apply treatment at egress, " +
117 "how can I match in the core ???";
118
119 /**
120 * Error message for wrong encapsulation scenario.
121 */
122 private static final String WRONG_ENCAPSULATION = "Wrong scenario - 1 hop with " +
123 "encapsualtion";
124
125 /**
126 * Error message for unavailable labels.
127 */
128 private static final String NO_LABELS = "No available label for %s";
129
130 /**
131 * Error message for wrong encapsulation.
132 */
133 private static final String UNKNOWN_ENCAPSULATION = "Unknown encapsulation type";
134
135 /**
136 * Error message for unsupported L0 instructions.
137 */
138 private static final String UNSUPPORTED_L0 = "L0 not supported";
139
140 /**
141 * Error message for unsupported L1 instructions.
142 */
143 private static final String UNSUPPORTED_L1 = "L1 not supported";
144
145 /**
146 * Error message for unsupported eth subtype.
147 */
148 private static final String UNSUPPORTED_ETH_SUBTYPE = "Bad eth subtype";
149
150 /**
151 * Error message for unsupported pop action.
152 */
153 private static final String UNSUPPORTED_POP_ACTION = "Can't handle pop label";
154
155 /**
156 * Error message for unsupported L2 instructions.
157 */
158 private static final String UNSUPPORTED_L2 = "Unknown L2 Modification instruction";
159
160 /**
161 * Error message for unsupported IP subtype.
162 */
163 private static final String UNSUPPORTED_IP_SUBTYPE = "Bad ip subtype";
164
165 /**
166 * Error message for unsupported ARP.
167 */
168 private static final String UNSUPPORTED_ARP = "IPv6 not supported for ARP";
169
170 /**
171 * Error message for unsupported L3 instructions.
172 */
173 private static final String UNSUPPORTED_L3 = "Unknown L3 Modification instruction";
174
175 /**
176 * Error message for unsupported L4 subtype.
177 */
178 private static final String UNSUPPORTED_L4_SUBTYPE = "Unknown L4 subtype";
179
180 /**
181 * Error message for unsupported L4 instructions.
182 */
183 private static final String UNSUPPORTED_L4 = "Unknown L4 Modification instruction";
184
185 /**
186 * Error message for unsupported instructions.
187 */
188 private static final String UNSUPPORTED_INSTRUCTION = "Unknown instruction type";
189
190 /**
191 * Creates the flows representations. This default implementation does
192 * nothing. Subclasses should override this method to create their
193 * specific flows representations (flow rule, flow objective).
194 *
195 * @param intent the intent to compile
196 * @param deviceId the affected device
197 * @param inPorts the input ports
198 * @param outPorts the output ports
199 * @param labels the labels for the label switching hop by hop
200 * @return the list of flows representations
201 */
202 protected List<T> createRules(LinkCollectionIntent intent,
203 DeviceId deviceId,
204 Set<PortNumber> inPorts,
205 Set<PortNumber> outPorts,
206 Map<ConnectPoint, Identifier<?>> labels) {
207 return null;
208 }
209
210 /**
211 * Helper method to handle the different scenario (not encap, single hop, encap).
212 *
213 * @param encapConstraint the encapsulation constraint if it is present
214 * @param intent the link collection intent
215 * @param inPort the in port
216 * @param outPorts the out ports
217 * @param deviceId the current device
218 * @param labels the labels used by the encapsulation
219 * @return the forwarding instruction
220 */
221 protected ForwardingInstructions createForwardingInstruction(Optional<EncapsulationConstraint> encapConstraint,
222 LinkCollectionIntent intent,
223 PortNumber inPort,
224 Set<PortNumber> outPorts,
225 DeviceId deviceId,
226 Map<ConnectPoint, Identifier<?>> labels) {
227 ForwardingInstructions instructions = null;
228 /*
229 * If not encapsulation or single hop.
230 */
231 if (!encapConstraint.isPresent() || intent.links().isEmpty()) {
232 instructions = this.createForwardingInstructions(
233 intent,
234 inPort,
235 deviceId,
236 outPorts
237 );
238 /*
239 * If encapsulation is present. We retrieve the labels
240 * for this iteration;
241 */
242 } else {
243 Identifier<?> inLabel = labels.get(new ConnectPoint(deviceId, inPort));
244 Map<ConnectPoint, Identifier<?>> outLabels = Maps.newHashMap();
245 outPorts.forEach(outPort -> {
246 ConnectPoint key = new ConnectPoint(deviceId, outPort);
247 outLabels.put(key, labels.get(key));
248 });
249 instructions = this.createForwardingInstructions(
250 intent,
251 inPort,
252 inLabel,
253 deviceId,
254 outPorts,
255 outLabels,
256 encapConstraint.get().encapType()
257 );
258 }
259 return instructions;
260 }
261
262 /**
Pier Ventre81c47bf2016-11-04 07:26:22 -0700263 * Helper method to generate the egress actions.
264 *
265 * @param treatmentBuilder the treatment builder to update
266 * @param egressPoints the egress points
267 * @param initialState the initial state of the transition
268 */
269 private void generateEgressActions(TrafficTreatment.Builder treatmentBuilder,
270 List<FilteredConnectPoint> egressPoints,
271 TrafficSelector initialState,
272 LinkCollectionIntent intent) {
273
274 TrafficSelector prevState = initialState;
275 for (FilteredConnectPoint egressPoint : egressPoints) {
276 /*
277 * If we are at the egress, we have to transit to the final
278 * state. First we add the Intent treatment.
279 */
280 intent.treatment().allInstructions().stream()
281 .filter(inst -> inst.type() != Instruction.Type.NOACTION)
282 .forEach(treatmentBuilder::add);
283 /*
284 * We generate the transition FIP->FEP.
285 */
286 TrafficTreatment forwardingTreatment =
287 forwardingTreatment(prevState,
288 egressPoint.trafficSelector(),
289 getEthType(intent.selector()));
290 /*
291 * We add the instruction necessary to the transition.
292 * Potentially we override the intent treatment.
293 */
294 forwardingTreatment.allInstructions().stream()
295 .filter(inst -> inst.type() != Instruction.Type.NOACTION)
296 .forEach(treatmentBuilder::add);
297 /*
298 * Finally we set the output action.
299 */
300 treatmentBuilder.setOutput(egressPoint.connectPoint().port());
301 if (optimize) {
302 /*
303 * We update the previous state. In this way instead of
304 * transiting from FIP->FEP we do FEP->FEP and so on.
305 */
306 prevState = egressPoint.trafficSelector();
307 }
308 }
309
310 }
311
312 /**
313 * Helper method to order the egress ports according to a
314 * specified criteria. The idea is to generate first the actions
315 * for the egress ports which are similar to the specified criteria
316 * then the others. In this way we can mitigate the problems related
317 * to the chain of actions and we can optimize also the number of
318 * actions.
319 *
320 * @param orderCriteria the ordering criteria
321 * @param pointsToOrder the egress points to order
322 * @return a list of port ordered
323 */
324 private List<FilteredConnectPoint> orderedEgressPoints(TrafficSelector orderCriteria,
325 List<FilteredConnectPoint> pointsToOrder) {
326 /*
327 * We are interested only to the labels. The idea is to order
328 * by the tags.
329 *
330 */
331 Criterion vlanIdCriterion = orderCriteria.getCriterion(VLAN_VID);
332 Criterion mplsLabelCriterion = orderCriteria.getCriterion(MPLS_LABEL);
333 /*
334 * We collect all the untagged points.
335 *
336 */
337 List<FilteredConnectPoint> untaggedEgressPoints = pointsToOrder
338 .stream()
339 .filter(pointToOrder -> {
340 TrafficSelector selector = pointToOrder.trafficSelector();
341 return selector.getCriterion(VLAN_VID) == null &&
342 selector.getCriterion(MPLS_LABEL) == null;
343 }).collect(Collectors.toList());
344 /*
345 * We collect all the vlan points.
346 */
347 List<FilteredConnectPoint> vlanEgressPoints = pointsToOrder
348 .stream()
349 .filter(pointToOrder -> {
350 TrafficSelector selector = pointToOrder.trafficSelector();
351 return selector.getCriterion(VLAN_VID) != null &&
352 selector.getCriterion(MPLS_LABEL) == null;
353 }).collect(Collectors.toList());
354 /*
355 * We collect all the mpls points.
356 */
357 List<FilteredConnectPoint> mplsEgressPoints = pointsToOrder
358 .stream()
359 .filter(pointToOrder -> {
360 TrafficSelector selector = pointToOrder.trafficSelector();
361 return selector.getCriterion(VLAN_VID) == null &&
362 selector.getCriterion(MPLS_LABEL) != null;
363 }).collect(Collectors.toList());
364 /*
365 * We create the final list of ports.
366 */
367 List<FilteredConnectPoint> orderedList = Lists.newArrayList();
368 /*
369 * The ordering criteria is vlan id. First we add the vlan
370 * ports. Then the others.
371 */
372 if (vlanIdCriterion != null && mplsLabelCriterion == null) {
373 orderedList.addAll(vlanEgressPoints);
374 orderedList.addAll(untaggedEgressPoints);
375 orderedList.addAll(mplsEgressPoints);
376 return orderedList;
377 }
378 /*
379 * The ordering criteria is mpls label. First we add the mpls
380 * ports. Then the others.
381 */
382 if (vlanIdCriterion == null && mplsLabelCriterion != null) {
383 orderedList.addAll(mplsEgressPoints);
384 orderedList.addAll(untaggedEgressPoints);
385 orderedList.addAll(vlanEgressPoints);
386 return orderedList;
387 }
388 /*
389 * The ordering criteria is untagged. First we add the untagged
390 * ports. Then the others.
391 */
392 if (vlanIdCriterion == null && mplsLabelCriterion == null) {
393 orderedList.addAll(untaggedEgressPoints);
394 orderedList.addAll(vlanEgressPoints);
395 orderedList.addAll(mplsEgressPoints);
396 return orderedList;
397 }
398 /*
399 * Unhandled scenario.
400 */
401 orderedList.addAll(vlanEgressPoints);
402 orderedList.addAll(mplsEgressPoints);
403 orderedList.addAll(untaggedEgressPoints);
404 return orderedList;
405 }
406
407 /**
Pier Ventre766995d2016-10-05 22:15:56 -0700408 * Manages the Intents with a single ingress point (p2p, sp2mp)
409 * creating properly the selector builder and the treatment builder.
410 *
411 * @param selectorBuilder the selector builder to update
412 * @param treatmentBuilder the treatment builder to update
413 * @param intent the intent to compile
414 * @param deviceId the current device
415 * @param outPorts the output ports of this device
416 */
417 private void manageSpIntent(TrafficSelector.Builder selectorBuilder,
418 TrafficTreatment.Builder treatmentBuilder,
419 LinkCollectionIntent intent,
420 DeviceId deviceId,
421 Set<PortNumber> outPorts) {
422 /*
423 * Sanity check.
424 */
425 if (intent.filteredIngressPoints().size() != 1) {
426 throw new IntentCompilationException(WRONG_INGRESS);
427 }
428 /*
429 * For the p2p and sp2mp the transition initial state
430 * to final state is performed at the egress.
431 */
432 Optional<FilteredConnectPoint> filteredIngressPoint =
433 intent.filteredIngressPoints().stream().findFirst();
434 /*
435 * We build the final selector, adding the selector
436 * of the FIP to the Intent selector and potentially
437 * overriding its matches.
438 */
439 filteredIngressPoint.get()
440 .trafficSelector()
441 .criteria()
442 .forEach(selectorBuilder::add);
443 /*
444 * In this scenario, potentially we can have several output
Pier Ventre81c47bf2016-11-04 07:26:22 -0700445 * ports. First we have to insert in the treatment the actions
446 * for the core.
Pier Ventre766995d2016-10-05 22:15:56 -0700447 */
Pier Ventre81c47bf2016-11-04 07:26:22 -0700448 List<FilteredConnectPoint> egressPoints = Lists.newArrayList();
Pier Ventre766995d2016-10-05 22:15:56 -0700449 for (PortNumber outPort : outPorts) {
450 Optional<FilteredConnectPoint> filteredEgressPoint =
451 getFilteredConnectPointFromIntent(deviceId, outPort, intent);
Pier Ventre81c47bf2016-11-04 07:26:22 -0700452 if (!filteredEgressPoint.isPresent()) {
453 treatmentBuilder.setOutput(outPort);
454 } else {
455 egressPoints.add(filteredEgressPoint.get());
Pier Ventre766995d2016-10-05 22:15:56 -0700456 }
Pier Ventre766995d2016-10-05 22:15:56 -0700457 }
Pier Ventre81c47bf2016-11-04 07:26:22 -0700458 /*
459 * The idea is to order the egress points. Before we deal
460 * with the egress points which looks like similar to the ingress
461 * point then the others.
462 */
463 TrafficSelector prevState = filteredIngressPoint.get().trafficSelector();
464 if (optimize) {
465 egressPoints = orderedEgressPoints(prevState, egressPoints);
466 }
467 /*
468 * Then we deal with the egress points.
469 */
470 generateEgressActions(treatmentBuilder, egressPoints, prevState, intent);
Pier Ventre766995d2016-10-05 22:15:56 -0700471 }
472
473 /**
474 * Manages the Intents with multiple ingress points creating properly
475 * the selector builder and the treatment builder.
476 *
477 * @param selectorBuilder the selector builder to update
478 * @param treatmentBuilder the treatment builder to update
479 * @param intent the intent to compile
480 * @param inPort the input port of the current device
481 * @param deviceId the current device
482 * @param outPorts the output ports of this device
483 */
484 private void manageMpIntent(TrafficSelector.Builder selectorBuilder,
485 TrafficTreatment.Builder treatmentBuilder,
486 LinkCollectionIntent intent,
487 PortNumber inPort,
488 DeviceId deviceId,
489 Set<PortNumber> outPorts) {
490 /*
491 * Sanity check
492 */
493 if (intent.filteredEgressPoints().size() != 1) {
494 throw new IntentCompilationException(WRONG_EGRESS);
495 }
496 /*
497 * We try to understand if the device is one of the ingress points.
498 */
499 Optional<FilteredConnectPoint> filteredIngressPoint =
500 getFilteredConnectPointFromIntent(deviceId, inPort, intent);
501 /*
502 * We retrieve from the Intent the unique egress points.
503 */
504 Optional<FilteredConnectPoint> filteredEgressPoint =
505 intent.filteredEgressPoints().stream().findFirst();
506 /*
507 * We check if the device is the ingress device
508 */
509 if (filteredIngressPoint.isPresent()) {
510 /*
511 * We are at ingress, so basically what we have to do is this:
512 * apply a set of operations (treatment, FEP) in order to have
513 * a transition from the initial state to the final state.
514 *
515 * We initialize the treatment with the Intent treatment
516 */
517 intent.treatment().allInstructions().stream()
518 .filter(inst -> inst.type() != Instruction.Type.NOACTION)
519 .forEach(treatmentBuilder::add);
520 /*
521 * We build the final selector, adding the selector
522 * of the FIP to the Intent selector and potentially
523 * overriding its matches.
524 */
525 filteredIngressPoint.get()
526 .trafficSelector()
527 .criteria()
528 .forEach(selectorBuilder::add);
529 /*
530 * We define the transition FIP->FEP, basically
531 * the set of the operations we need for reaching
532 * the final state.
533 */
534 TrafficTreatment forwardingTreatment =
535 forwardingTreatment(filteredIngressPoint.get().trafficSelector(),
536 filteredEgressPoint.get().trafficSelector(),
537 getEthType(intent.selector()));
538 /*
539 * We add to the treatment the actions necessary for the
540 * transition, potentially overriding the treatment of the
541 * Intent. The Intent treatment has always a low priority
542 * in respect of the FEP.
543 */
544 forwardingTreatment.allInstructions().stream()
545 .filter(inst -> inst.type() != Instruction.Type.NOACTION)
546 .forEach(treatmentBuilder::add);
547 } else {
548 /*
549 * We are in the core or in the egress switch.
550 * The packets are in their final state. We need
551 * to match against this final state.
552 *
553 * we derive the final state defined by the intent
554 * treatment.
555 */
556 updateBuilder(selectorBuilder, intent.treatment());
557 /*
558 * We derive the final state defined by the unique
559 * FEP. We merge the two states.
560 */
561 filteredEgressPoint.get()
562 .trafficSelector()
563 .criteria()
564 .forEach(selectorBuilder::add);
565 }
566 /*
567 * Finally we set the output action.
568 */
569 outPorts.forEach(treatmentBuilder::setOutput);
570 }
571
572 /**
573 * Computes treatment and selector which will be used
574 * in the flow representation (Rule, Objective).
575 *
576 * @param intent the intent to compile
577 * @param inPort the input port of this device
578 * @param deviceId the current device
579 * @param outPorts the output ports of this device
580 * @return the forwarding instruction object which encapsulates treatment and selector
581 */
582 protected ForwardingInstructions createForwardingInstructions(LinkCollectionIntent intent,
583 PortNumber inPort,
584 DeviceId deviceId,
585 Set<PortNumber> outPorts) {
586
587 /*
588 * We build an empty treatment and we initialize the selector with
589 * the intent selector.
590 */
591 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
592 .builder();
593 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector
594 .builder(intent.selector())
595 .matchInPort(inPort);
596
597 if (!intent.applyTreatmentOnEgress()) {
598 manageMpIntent(selectorBuilder,
599 treatmentBuilder,
600 intent,
601 inPort,
602 deviceId,
603 outPorts
604 );
605 } else {
606 manageSpIntent(selectorBuilder,
607 treatmentBuilder,
608 intent,
609 deviceId,
610 outPorts
611 );
612 }
613 /*
614 * We return selector and treatment necessary to build the flow rule
615 * or the flow objective.
616 */
617 return new ForwardingInstructions(treatmentBuilder.build(), selectorBuilder.build());
618 }
619
620 /**
621 * Manages the ingress of the Intents (p2p, sp2mp, mp2sp) with encapsulation.
622 *
623 * @param selectorBuilder the selector builder to update
624 * @param treatmentBuilder the treatment builder to update
625 * @param intent the intent to compile
626 * @param inPort the input port of this device
627 * @param deviceId the current device
628 * @param outPorts the output ports of this device
629 * @param outLabels the labels associated to the output port
630 * @param type the encapsulation type
631 */
632 private void manageEncapAtIngress(TrafficSelector.Builder selectorBuilder,
633 TrafficTreatment.Builder treatmentBuilder,
634 LinkCollectionIntent intent,
635 PortNumber inPort,
636 DeviceId deviceId,
637 Set<PortNumber> outPorts,
638 Map<ConnectPoint, Identifier<?>> outLabels,
639 EncapsulationType type) {
640
641 Optional<FilteredConnectPoint> filteredIngressPoint =
642 getFilteredConnectPointFromIntent(deviceId, inPort, intent);
643 /*
644 * We fill the selector builder with the intent selector.
645 */
646 intent.selector().criteria().forEach(selectorBuilder::add);
647 /*
648 * We build the final selector, adding the selector
649 * of the FIP to the Intent selector and potentially
650 * overriding its matches.
651 */
652 filteredIngressPoint.get()
653 .trafficSelector()
654 .criteria()
655 .forEach(selectorBuilder::add);
656 /*
657 * In this scenario, we can have several output ports.
658 */
659 outPorts.forEach(outPort -> {
660 Optional<FilteredConnectPoint> filteredEgressPoint =
661 getFilteredConnectPointFromIntent(deviceId, outPort, intent);
662 /*
663 * If we are at the egress, we don't handle
664 * with encapsulation. Error scenario
665 */
666 if (filteredEgressPoint.isPresent()) {
667 throw new IntentCompilationException(WRONG_ENCAPSULATION);
668 }
669 /*
670 * Transit/core, we have to transit to the intermediate
671 * state. We build a temporary selector for the encapsulation.
672 */
673 TrafficSelector.Builder encapBuilder = DefaultTrafficSelector.builder();
674 /*
675 * We retrieve the associated label to the output port.
676 */
677 ConnectPoint cp = new ConnectPoint(deviceId, outPort);
678 Identifier<?> outLabel = outLabels.get(cp);
679 /*
680 * If there aren't labels, we cannot handle.
681 */
682 if (outLabel == null) {
683 throw new IntentCompilationException(String.format(NO_LABELS, cp));
684 }
685 /*
686 * In the core we match using encapsulation.
687 */
688 updateSelectorFromEncapsulation(
689 encapBuilder,
690 type,
691 outLabel
692 );
693 /*
694 * We generate the transition.
695 */
696 TrafficTreatment forwardingTreatment =
697 forwardingTreatment(filteredIngressPoint.get().trafficSelector(),
698 encapBuilder.build(),
699 getEthType(intent.selector()));
700 /*
701 * We add the instruction necessary to the transition.
702 */
703 forwardingTreatment.allInstructions().stream()
704 .filter(inst -> inst.type() != Instruction.Type.NOACTION)
705 .forEach(treatmentBuilder::add);
706 /*
707 * Finally we set the output action.
708 */
709 treatmentBuilder.setOutput(outPort);
710 });
711
712 }
713
714 /**
715 * Manages the core and transit of the Intents (p2p, sp2mp, mp2sp)
716 * with encapsulation.
717 *
718 * @param selectorBuilder the selector builder to update
719 * @param treatmentBuilder the treatment builder to update
720 * @param intent the intent to compile
721 * @param inPort the input port of this device
722 * @param inLabel the label associated to the input port
723 * @param deviceId the current device
724 * @param outPorts the output ports of this device
725 * @param outLabels the labels associated to the output port
726 * @param type the encapsulation type
727 */
728 private void manageEncapAtCoreAndEgress(TrafficSelector.Builder selectorBuilder,
729 TrafficTreatment.Builder treatmentBuilder,
730 LinkCollectionIntent intent,
731 PortNumber inPort,
732 Identifier<?> inLabel,
733 DeviceId deviceId,
734 Set<PortNumber> outPorts,
735 Map<ConnectPoint, Identifier<?>> outLabels,
736 EncapsulationType type) {
737
738 /*
739 * If there are not labels, we cannot handle.
740 */
741 ConnectPoint inCp = new ConnectPoint(deviceId, inPort);
742 if (inLabel == null) {
743 throw new IntentCompilationException(String.format(NO_LABELS, inCp));
744 }
745 /*
746 * In the core and at egress we match using encapsulation.
747 */
748 updateSelectorFromEncapsulation(
749 selectorBuilder,
750 type,
751 inLabel
752 );
753 /*
754 * We need to order the actions. First the actions
Pier Ventre81c47bf2016-11-04 07:26:22 -0700755 * related to the not-egress points. At the same time we collect
756 * also the egress points.
Pier Ventre766995d2016-10-05 22:15:56 -0700757 */
Pier Ventre81c47bf2016-11-04 07:26:22 -0700758 List<FilteredConnectPoint> egressPoints = Lists.newArrayList();
759 for (PortNumber outPort : outPorts) {
Pier Ventre766995d2016-10-05 22:15:56 -0700760 Optional<FilteredConnectPoint> filteredEgressPoint =
761 getFilteredConnectPointFromIntent(deviceId, outPort, intent);
762 if (!filteredEgressPoint.isPresent()) {
763 /*
764 * We build a temporary selector for the encapsulation.
765 */
766 TrafficSelector.Builder encapBuilder = DefaultTrafficSelector.builder();
767 /*
768 * We retrieve the associated label to the output port.
769 */
770 ConnectPoint cp = new ConnectPoint(deviceId, outPort);
771 Identifier<?> outLabel = outLabels.get(cp);
772 /*
773 * If there are not labels, we cannot handle.
774 */
775 if (outLabel == null) {
776 throw new IntentCompilationException(String.format(NO_LABELS, cp));
777 }
778 /*
779 * In the core we match using encapsulation.
780 */
781 updateSelectorFromEncapsulation(
782 encapBuilder,
783 type,
784 outLabel
785 );
786 /*
787 * We generate the transition.
788 */
789 TrafficTreatment forwardingTreatment =
790 forwardingTreatment(selectorBuilder.build(),
791 encapBuilder.build(),
792 getEthType(intent.selector()));
793 /*
794 * We add the instruction necessary to the transition.
795 */
796 forwardingTreatment.allInstructions().stream()
797 .filter(inst -> inst.type() != Instruction.Type.NOACTION)
798 .forEach(treatmentBuilder::add);
799 /*
800 * Finally we set the output action.
801 */
802 treatmentBuilder.setOutput(outPort);
Pier Ventre81c47bf2016-11-04 07:26:22 -0700803 } else {
804 egressPoints.add(filteredEgressPoint.get());
Pier Ventre766995d2016-10-05 22:15:56 -0700805 }
Pier Ventre81c47bf2016-11-04 07:26:22 -0700806 }
807 /*
808 * The idea is to order the egress points. Before we deal
809 * with the egress points which looks like similar to the
810 * selector derived from the encpsulation constraint then
811 * the others.
812 */
813 TrafficSelector prevState = selectorBuilder.build();
814 if (optimize) {
815 egressPoints = orderedEgressPoints(prevState, egressPoints);
816 }
Pier Ventre766995d2016-10-05 22:15:56 -0700817 /*
818 * In this case, we have to transit to the final
819 * state.
820 */
Pier Ventre81c47bf2016-11-04 07:26:22 -0700821 generateEgressActions(treatmentBuilder, egressPoints, prevState, intent);
Pier Ventre766995d2016-10-05 22:15:56 -0700822 }
823
824 /**
825 * Computes treatment and selector which will be used
826 * in the flow representation (Rule, Objective).
827 *
828 * @param intent the intent to compile
829 * @param inPort the input port of this device
830 * @param inLabel the label associated to the input port
831 * @param deviceId the current device
832 * @param outPorts the output ports of this device
833 * @param outLabels the labels associated to the output port
834 * @param type the encapsulation type
835 * @return the forwarding instruction object which encapsulates treatment and selector
836 */
837 protected ForwardingInstructions createForwardingInstructions(LinkCollectionIntent intent,
838 PortNumber inPort,
839 Identifier<?> inLabel,
840 DeviceId deviceId,
841 Set<PortNumber> outPorts,
842 Map<ConnectPoint, Identifier<?>> outLabels,
843 EncapsulationType type) {
844 /*
845 * We build an empty treatment and an empty selector.
846 */
847 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
848 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
849 selectorBuilder.matchInPort(inPort);
850 Optional<FilteredConnectPoint> filteredIngressPoint =
851 getFilteredConnectPointFromIntent(deviceId, inPort, intent);
852
853 if (filteredIngressPoint.isPresent()) {
854 manageEncapAtIngress(selectorBuilder,
855 treatmentBuilder,
856 intent,
857 inPort,
858 deviceId,
859 outPorts,
860 outLabels,
861 type
862 );
863 } else {
864 manageEncapAtCoreAndEgress(selectorBuilder,
865 treatmentBuilder,
866 intent,
867 inPort,
868 inLabel,
869 deviceId,
870 outPorts,
871 outLabels,
872 type);
873 }
874 /*
875 * We return selector and treatment necessary to build the flow rule
876 * or the flow objective.
877 */
878 return new ForwardingInstructions(treatmentBuilder.build(), selectorBuilder.build());
879 }
880
881 /**
882 * Helper class to encapsulate treatment and selector
883 * in an unique abstraction.
Pier Ventred48320e2016-08-17 16:25:47 -0700884 */
885 protected class ForwardingInstructions {
886
887 private TrafficTreatment trafficTreatment;
888
889 private TrafficSelector trafficSelector;
890
891 public ForwardingInstructions(TrafficTreatment treatment, TrafficSelector selector) {
892
893 this.trafficTreatment = treatment;
894 this.trafficSelector = selector;
895
896 }
897
898 public TrafficTreatment treatment() {
899 return this.trafficTreatment;
900 }
901
902 public TrafficSelector selector() {
903 return this.trafficSelector;
904 }
905
906 }
907
908 /**
Pier Ventre766995d2016-10-05 22:15:56 -0700909 * Helper method to compute input and output ports
910 * for each device crossed in the path.
Pier Ventred48320e2016-08-17 16:25:47 -0700911 *
912 * @param intent the related intents
913 * @param inputPorts the input ports to compute
914 * @param outputPorts the output ports to compute
915 */
916 protected void computePorts(LinkCollectionIntent intent,
917 SetMultimap<DeviceId, PortNumber> inputPorts,
918 SetMultimap<DeviceId, PortNumber> outputPorts) {
919
920 for (Link link : intent.links()) {
921 inputPorts.put(link.dst().deviceId(), link.dst().port());
922 outputPorts.put(link.src().deviceId(), link.src().port());
923 }
924
925 for (ConnectPoint ingressPoint : intent.ingressPoints()) {
926 inputPorts.put(ingressPoint.deviceId(), ingressPoint.port());
927 }
928
929 for (ConnectPoint egressPoint : intent.egressPoints()) {
930 outputPorts.put(egressPoint.deviceId(), egressPoint.port());
931 }
Pier Ventred48320e2016-08-17 16:25:47 -0700932
933 }
934
935 /**
Pier Ventre766995d2016-10-05 22:15:56 -0700936 * Retrieves the encapsulation constraint from the link collection intent.
Pier Ventred48320e2016-08-17 16:25:47 -0700937 *
Pier Ventre766995d2016-10-05 22:15:56 -0700938 * @param intent the intent to analyze
939 * @return the encapsulation constraint
Pier Ventred48320e2016-08-17 16:25:47 -0700940 */
Pier Ventre766995d2016-10-05 22:15:56 -0700941 protected Optional<EncapsulationConstraint> getIntentEncapConstraint(LinkCollectionIntent intent) {
942 return intent.constraints().stream()
943 .filter(constraint -> constraint instanceof EncapsulationConstraint)
944 .map(x -> (EncapsulationConstraint) x).findAny();
Pier Ventred48320e2016-08-17 16:25:47 -0700945 }
946
947
948 /**
Yi Tseng2a81c9d2016-09-14 10:14:24 -0700949 * Get FilteredConnectPoint from LinkCollectionIntent.
Pier Ventre766995d2016-10-05 22:15:56 -0700950 *
Yi Tseng2a81c9d2016-09-14 10:14:24 -0700951 * @param deviceId device Id for connect point
952 * @param portNumber port number
953 * @param intent source intent
954 * @return filtered connetion point
955 */
956 private Optional<FilteredConnectPoint> getFilteredConnectPointFromIntent(DeviceId deviceId,
957 PortNumber portNumber,
958 LinkCollectionIntent intent) {
959 Set<FilteredConnectPoint> filteredConnectPoints =
960 Sets.union(intent.filteredIngressPoints(), intent.filteredEgressPoints());
961 return filteredConnectPoints.stream()
962 .filter(port -> port.connectPoint().deviceId().equals(deviceId))
963 .filter(port -> port.connectPoint().port().equals(portNumber))
964 .findFirst();
965 }
966
967 /**
968 * Get tag criterion from selector.
969 * The criterion should be one of type in tagCriterionTypes.
970 *
971 * @param selector selector
972 * @return Criterion that matched, if there is no tag criterion, return null
973 */
974 private Criterion getTagCriterion(TrafficSelector selector) {
975 return selector.criteria().stream()
976 .filter(criterion -> TAG_CRITERION_TYPES.contains(criterion.type()))
977 .findFirst()
978 .orElse(Criteria.dummy());
979
980 }
981
982 /**
983 * Compares tag type between ingress and egress point and generate
984 * treatment for egress point of intent.
985 *
Pier Ventre766995d2016-10-05 22:15:56 -0700986 * @param ingress ingress selector for the intent
987 * @param egress egress selector for the intent
988 * @param ethType the ethertype to use in mpls_pop
Yi Tseng2a81c9d2016-09-14 10:14:24 -0700989 * @return Builder of TrafficTreatment
990 */
Pier Ventre766995d2016-10-05 22:15:56 -0700991 private TrafficTreatment forwardingTreatment(TrafficSelector ingress,
992 TrafficSelector egress,
993 EthType ethType) {
Yi Tseng2a81c9d2016-09-14 10:14:24 -0700994
995
Pier Ventre766995d2016-10-05 22:15:56 -0700996 if (ingress.equals(egress)) {
Yi Tseng2a81c9d2016-09-14 10:14:24 -0700997 return DefaultTrafficTreatment.emptyTreatment();
998 }
999
1000 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
1001
1002 /*
1003 * "null" means there is no tag for the port
1004 * Tag criterion will be null if port is normal connection point
1005 */
Pier Ventre766995d2016-10-05 22:15:56 -07001006 Criterion ingressTagCriterion = getTagCriterion(ingress);
1007 Criterion egressTagCriterion = getTagCriterion(egress);
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001008
1009 if (ingressTagCriterion.type() != egressTagCriterion.type()) {
1010
1011 /*
1012 * Tag type of ingress port and egress port are different.
1013 * Need to remove tag from ingress, then add new tag for egress.
1014 * Remove nothing if ingress port use VXLAN or there is no tag
1015 * on ingress port.
1016 */
1017 switch (ingressTagCriterion.type()) {
1018 case VLAN_VID:
1019 builder.popVlan();
1020 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001021
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001022 case MPLS_LABEL:
Pier Ventre81c47bf2016-11-04 07:26:22 -07001023 if (copyTtl) {
1024 builder.copyTtlIn();
1025 }
Pier Ventre766995d2016-10-05 22:15:56 -07001026 builder.popMpls(ethType);
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001027 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001028
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001029 default:
1030 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001031
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001032 }
1033
1034 /*
1035 * Push new tag for egress port.
1036 */
1037 switch (egressTagCriterion.type()) {
1038 case VLAN_VID:
1039 builder.pushVlan();
1040 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001041
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001042 case MPLS_LABEL:
1043 builder.pushMpls();
Pier Ventre81c47bf2016-11-04 07:26:22 -07001044 if (copyTtl) {
1045 builder.copyTtlOut();
1046 }
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001047 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001048
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001049 default:
1050 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001051
Pier Ventred48320e2016-08-17 16:25:47 -07001052 }
1053 }
1054
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001055 switch (egressTagCriterion.type()) {
1056 case VLAN_VID:
1057 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) egressTagCriterion;
1058 builder.setVlanId(vlanIdCriterion.vlanId());
1059 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001060
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001061 case MPLS_LABEL:
1062 MplsCriterion mplsCriterion = (MplsCriterion) egressTagCriterion;
1063 builder.setMpls(mplsCriterion.label());
1064 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001065
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001066 case TUNNEL_ID:
1067 TunnelIdCriterion tunnelIdCriterion = (TunnelIdCriterion) egressTagCriterion;
1068 builder.setTunnelId(tunnelIdCriterion.tunnelId());
1069 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001070
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001071 default:
1072 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001073
Pier Ventre27d42572016-08-29 17:37:08 -07001074 }
Pier Ventre27d42572016-08-29 17:37:08 -07001075
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001076 return builder.build();
Pier Ventre647138f2016-08-26 17:32:44 -07001077 }
1078
1079 /**
Pier Ventred48320e2016-08-17 16:25:47 -07001080 * Update the selector builder using a L0 instruction.
1081 *
1082 * @param builder the builder to update
1083 * @param l0instruction the l0 instruction to use
1084 */
1085 private void updateBuilder(TrafficSelector.Builder builder, L0ModificationInstruction l0instruction) {
Pier Ventre766995d2016-10-05 22:15:56 -07001086 throw new IntentCompilationException(UNSUPPORTED_L0);
Pier Ventred48320e2016-08-17 16:25:47 -07001087 }
1088
1089 /**
1090 * Update the selector builder using a L1 instruction.
1091 *
1092 * @param builder the builder to update
1093 * @param l1instruction the l1 instruction to use
1094 */
1095 private void updateBuilder(TrafficSelector.Builder builder, L1ModificationInstruction l1instruction) {
Pier Ventre766995d2016-10-05 22:15:56 -07001096 throw new IntentCompilationException(UNSUPPORTED_L1);
Pier Ventred48320e2016-08-17 16:25:47 -07001097 }
1098
1099 /**
1100 * Update the selector builder using a L2 instruction.
1101 *
1102 * @param builder the builder to update
1103 * @param l2instruction the l2 instruction to use
1104 */
1105 private void updateBuilder(TrafficSelector.Builder builder, L2ModificationInstruction l2instruction) {
1106 switch (l2instruction.subtype()) {
1107 case ETH_SRC:
1108 case ETH_DST:
1109 ModEtherInstruction ethInstr = (ModEtherInstruction) l2instruction;
1110 switch (ethInstr.subtype()) {
1111 case ETH_SRC:
1112 builder.matchEthSrc(ethInstr.mac());
1113 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001114
Pier Ventred48320e2016-08-17 16:25:47 -07001115 case ETH_DST:
1116 builder.matchEthDst(ethInstr.mac());
1117 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001118
Pier Ventred48320e2016-08-17 16:25:47 -07001119 default:
Pier Ventre766995d2016-10-05 22:15:56 -07001120 throw new IntentCompilationException(UNSUPPORTED_ETH_SUBTYPE);
Pier Ventred48320e2016-08-17 16:25:47 -07001121 }
1122 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001123
Pier Ventred48320e2016-08-17 16:25:47 -07001124 case VLAN_ID:
1125 ModVlanIdInstruction vlanIdInstr = (ModVlanIdInstruction) l2instruction;
1126 builder.matchVlanId(vlanIdInstr.vlanId());
1127 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001128
Pier Ventred48320e2016-08-17 16:25:47 -07001129 case VLAN_PUSH:
1130 //FIXME
1131 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001132
Pier Ventred48320e2016-08-17 16:25:47 -07001133 case VLAN_POP:
1134 //TODO how do we handle dropped label? remove the selector?
Pier Ventre766995d2016-10-05 22:15:56 -07001135 throw new IntentCompilationException(UNSUPPORTED_POP_ACTION);
Pier Ventred48320e2016-08-17 16:25:47 -07001136 case VLAN_PCP:
1137 ModVlanPcpInstruction vlanPcpInstruction = (ModVlanPcpInstruction) l2instruction;
1138 builder.matchVlanPcp(vlanPcpInstruction.vlanPcp());
1139 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001140
Pier Ventred48320e2016-08-17 16:25:47 -07001141 case MPLS_LABEL:
1142 case MPLS_PUSH:
1143 //FIXME
1144 ModMplsLabelInstruction mplsInstr = (ModMplsLabelInstruction) l2instruction;
1145 builder.matchMplsLabel(mplsInstr.label());
1146 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001147
Pier Ventred48320e2016-08-17 16:25:47 -07001148 case MPLS_POP:
1149 //TODO how do we handle dropped label? remove the selector?
Pier Ventre766995d2016-10-05 22:15:56 -07001150 throw new IntentCompilationException(UNSUPPORTED_POP_ACTION);
Pier Ventred48320e2016-08-17 16:25:47 -07001151 case DEC_MPLS_TTL:
1152 // no-op
1153 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001154
Pier Ventred48320e2016-08-17 16:25:47 -07001155 case MPLS_BOS:
1156 ModMplsBosInstruction mplsBosInstr = (ModMplsBosInstruction) l2instruction;
1157 builder.matchMplsBos(mplsBosInstr.mplsBos());
1158 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001159
Pier Ventred48320e2016-08-17 16:25:47 -07001160 case TUNNEL_ID:
1161 ModTunnelIdInstruction tunInstr = (ModTunnelIdInstruction) l2instruction;
1162 builder.matchTunnelId(tunInstr.tunnelId());
1163 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001164
Pier Ventred48320e2016-08-17 16:25:47 -07001165 default:
Pier Ventre766995d2016-10-05 22:15:56 -07001166 throw new IntentCompilationException(UNSUPPORTED_L2);
Pier Ventred48320e2016-08-17 16:25:47 -07001167 }
1168
1169 }
1170
1171 /**
1172 * Update the selector builder using a L3 instruction.
1173 *
1174 * @param builder the builder to update
1175 * @param l3instruction the l3 instruction to use
1176 */
1177 private void updateBuilder(TrafficSelector.Builder builder, L3ModificationInstruction l3instruction) {
1178 // TODO check ethernet proto
1179 switch (l3instruction.subtype()) {
1180 case IPV4_SRC:
1181 case IPV4_DST:
1182 case IPV6_SRC:
1183 case IPV6_DST:
1184 ModIPInstruction ipInstr = (ModIPInstruction) l3instruction;
1185 // TODO check if ip falls in original prefix
1186 IpPrefix prefix = ipInstr.ip().toIpPrefix();
1187 switch (ipInstr.subtype()) {
1188 case IPV4_SRC:
1189 builder.matchIPSrc(prefix);
1190 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001191
Pier Ventred48320e2016-08-17 16:25:47 -07001192 case IPV4_DST:
1193 builder.matchIPSrc(prefix);
1194 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001195
Pier Ventred48320e2016-08-17 16:25:47 -07001196 case IPV6_SRC:
1197 builder.matchIPv6Src(prefix);
1198 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001199
Pier Ventred48320e2016-08-17 16:25:47 -07001200 case IPV6_DST:
1201 builder.matchIPv6Dst(prefix);
1202 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001203
Pier Ventred48320e2016-08-17 16:25:47 -07001204 default:
Pier Ventre766995d2016-10-05 22:15:56 -07001205 throw new IntentCompilationException(UNSUPPORTED_IP_SUBTYPE);
Pier Ventred48320e2016-08-17 16:25:47 -07001206 }
1207 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001208
Pier Ventred48320e2016-08-17 16:25:47 -07001209 case IPV6_FLABEL:
1210 ModIPv6FlowLabelInstruction ipFlowInstr = (ModIPv6FlowLabelInstruction) l3instruction;
1211 builder.matchIPv6FlowLabel(ipFlowInstr.flowLabel());
1212 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001213
Pier Ventred48320e2016-08-17 16:25:47 -07001214 case DEC_TTL:
1215 // no-op
1216 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001217
Pier Ventred48320e2016-08-17 16:25:47 -07001218 case TTL_OUT:
1219 // no-op
1220 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001221
Pier Ventred48320e2016-08-17 16:25:47 -07001222 case TTL_IN:
1223 // no-op
1224 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001225
Pier Ventred48320e2016-08-17 16:25:47 -07001226 case ARP_SPA:
1227 ModArpIPInstruction arpIpInstr = (ModArpIPInstruction) l3instruction;
1228 if (arpIpInstr.ip().isIp4()) {
1229 builder.matchArpSpa((Ip4Address) arpIpInstr.ip());
1230 } else {
Pier Ventre766995d2016-10-05 22:15:56 -07001231 throw new IntentCompilationException(UNSUPPORTED_ARP);
Pier Ventred48320e2016-08-17 16:25:47 -07001232 }
1233 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001234
Pier Ventred48320e2016-08-17 16:25:47 -07001235 case ARP_SHA:
1236 ModArpEthInstruction arpEthInstr = (ModArpEthInstruction) l3instruction;
1237 builder.matchArpSha(arpEthInstr.mac());
1238 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001239
Pier Ventred48320e2016-08-17 16:25:47 -07001240 case ARP_OP:
1241 ModArpOpInstruction arpOpInstr = (ModArpOpInstruction) l3instruction;
1242 //FIXME is the long to int cast safe?
1243 builder.matchArpOp((int) arpOpInstr.op());
1244 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001245
Pier Ventred48320e2016-08-17 16:25:47 -07001246 default:
Pier Ventre766995d2016-10-05 22:15:56 -07001247 throw new IntentCompilationException(UNSUPPORTED_L3);
Pier Ventred48320e2016-08-17 16:25:47 -07001248 }
1249 }
1250
1251 /**
1252 * Update the selector builder using a L4 instruction.
1253 *
1254 * @param builder the builder to update
1255 * @param l4instruction the l4 instruction to use
1256 */
1257 private void updateBuilder(TrafficSelector.Builder builder, L4ModificationInstruction l4instruction) {
1258 if (l4instruction instanceof ModTransportPortInstruction) {
1259 // TODO check IP proto
1260 ModTransportPortInstruction l4mod = (ModTransportPortInstruction) l4instruction;
1261 switch (l4mod.subtype()) {
1262 case TCP_SRC:
1263 builder.matchTcpSrc(l4mod.port());
1264 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001265
Pier Ventred48320e2016-08-17 16:25:47 -07001266 case TCP_DST:
1267 builder.matchTcpDst(l4mod.port());
1268 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001269
Pier Ventred48320e2016-08-17 16:25:47 -07001270 case UDP_SRC:
1271 builder.matchUdpSrc(l4mod.port());
1272 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001273
Pier Ventred48320e2016-08-17 16:25:47 -07001274 case UDP_DST:
1275 builder.matchUdpDst(l4mod.port());
1276 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001277
Pier Ventred48320e2016-08-17 16:25:47 -07001278 default:
Pier Ventre766995d2016-10-05 22:15:56 -07001279 throw new IntentCompilationException(UNSUPPORTED_L4_SUBTYPE);
Pier Ventred48320e2016-08-17 16:25:47 -07001280 }
1281 } else {
Pier Ventre766995d2016-10-05 22:15:56 -07001282 throw new IntentCompilationException(UNSUPPORTED_L4);
Pier Ventred48320e2016-08-17 16:25:47 -07001283 }
1284 }
1285
1286 /**
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001287 * Update selector builder by using treatment.
Pier Ventred48320e2016-08-17 16:25:47 -07001288 *
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001289 * @param builder builder to update
1290 * @param treatment traffic treatment
Pier Ventred48320e2016-08-17 16:25:47 -07001291 */
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001292 private void updateBuilder(TrafficSelector.Builder builder, TrafficTreatment treatment) {
1293
1294 treatment.allInstructions().forEach(instruction -> {
1295 switch (instruction.type()) {
1296 case L0MODIFICATION:
1297 updateBuilder(builder, (L0ModificationInstruction) instruction);
1298 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001299
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001300 case L1MODIFICATION:
1301 updateBuilder(builder, (L1ModificationInstruction) instruction);
1302 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001303
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001304 case L2MODIFICATION:
1305 updateBuilder(builder, (L2ModificationInstruction) instruction);
1306 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001307
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001308 case L3MODIFICATION:
1309 updateBuilder(builder, (L3ModificationInstruction) instruction);
1310 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001311
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001312 case L4MODIFICATION:
1313 updateBuilder(builder, (L4ModificationInstruction) instruction);
1314 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001315
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001316 case NOACTION:
1317 case OUTPUT:
1318 case GROUP:
1319 case QUEUE:
1320 case TABLE:
1321 case METER:
1322 case METADATA:
1323 case EXTENSION: // TODO is extension no-op or unsupported?
1324 // Nothing to do
1325 break;
Pier Ventre766995d2016-10-05 22:15:56 -07001326
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001327 default:
Pier Ventre766995d2016-10-05 22:15:56 -07001328 throw new IntentCompilationException(UNSUPPORTED_INSTRUCTION);
Yi Tseng2a81c9d2016-09-14 10:14:24 -07001329 }
1330 });
1331
Pier Ventred48320e2016-08-17 16:25:47 -07001332 }
1333
Pier Ventre766995d2016-10-05 22:15:56 -07001334 /**
1335 * The method generates a selector starting from
1336 * the encapsulation information (type and label to match).
1337 *
1338 * @param selectorBuilder the builder to update
1339 * @param type the type of encapsulation
1340 * @param identifier the label to match
1341 */
1342 private void updateSelectorFromEncapsulation(TrafficSelector.Builder selectorBuilder,
1343 EncapsulationType type,
1344 Identifier<?> identifier) {
1345 switch (type) {
1346 case MPLS:
1347 MplsLabel label = (MplsLabel) identifier;
1348 selectorBuilder.matchMplsLabel(label);
1349 selectorBuilder.matchEthType(Ethernet.MPLS_UNICAST);
1350 break;
1351
1352 case VLAN:
1353 VlanId id = (VlanId) identifier;
1354 selectorBuilder.matchVlanId(id);
1355 break;
1356
1357 default:
1358 throw new IntentCompilationException(UNKNOWN_ENCAPSULATION);
1359 }
1360 }
1361
1362 /**
1363 * Helper function to define the match on the ethertype.
1364 * If the selector define an ethertype we will use it,
1365 * otherwise IPv4 will be used by default.
1366 *
1367 * @param selector the traffic selector
1368 * @return the ethertype we should match
1369 */
1370 private EthType getEthType(TrafficSelector selector) {
1371 Criterion c = selector.getCriterion(Criterion.Type.ETH_TYPE);
1372 if (c != null && c instanceof EthTypeCriterion) {
1373 EthTypeCriterion ethertype = (EthTypeCriterion) c;
1374 return ethertype.ethType();
1375 }
1376 return EthType.EtherType.IPV4.ethType();
1377 }
1378
Pier Ventred48320e2016-08-17 16:25:47 -07001379}