blob: d6db8c8dd4586f9330e54129e5587f98f14d3e8b [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
19import com.google.common.collect.SetMultimap;
20import org.onlab.packet.Ip4Address;
21import org.onlab.packet.IpPrefix;
22import org.onosproject.net.ConnectPoint;
23import org.onosproject.net.DeviceId;
24import org.onosproject.net.Link;
25import org.onosproject.net.PortNumber;
26import org.onosproject.net.flow.DefaultTrafficSelector;
27import org.onosproject.net.flow.DefaultTrafficTreatment;
28import org.onosproject.net.flow.TrafficSelector;
29import org.onosproject.net.flow.TrafficTreatment;
Pier Ventre27d42572016-08-29 17:37:08 -070030import org.onosproject.net.flow.instructions.Instruction;
Pier Ventred48320e2016-08-17 16:25:47 -070031import org.onosproject.net.flow.instructions.L0ModificationInstruction;
32import org.onosproject.net.flow.instructions.L1ModificationInstruction;
33import org.onosproject.net.flow.instructions.L2ModificationInstruction;
34import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
35import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsBosInstruction;
36import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction;
37import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModTunnelIdInstruction;
38import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
39import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanPcpInstruction;
40import org.onosproject.net.flow.instructions.L3ModificationInstruction;
41import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpEthInstruction;
42import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpIPInstruction;
43import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpOpInstruction;
44import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPInstruction;
45import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPv6FlowLabelInstruction;
46import org.onosproject.net.flow.instructions.L4ModificationInstruction;
47import org.onosproject.net.flow.instructions.L4ModificationInstruction.ModTransportPortInstruction;
48import org.onosproject.net.intent.IntentCompilationException;
49import org.onosproject.net.intent.LinkCollectionIntent;
50
51import java.util.List;
Pier Ventre647138f2016-08-26 17:32:44 -070052import java.util.Optional;
Pier Ventred48320e2016-08-17 16:25:47 -070053import java.util.Set;
54import java.util.stream.Collectors;
55
56/**
57 * Shared APIs and implementations for Link Collection compilers.
58 */
59public class LinkCollectionCompiler<T> {
60
61 /**
62 * Helper class to encapsulate treatment and selector.
63 */
64 protected class ForwardingInstructions {
65
66 private TrafficTreatment trafficTreatment;
67
68 private TrafficSelector trafficSelector;
69
70 public ForwardingInstructions(TrafficTreatment treatment, TrafficSelector selector) {
71
72 this.trafficTreatment = treatment;
73 this.trafficSelector = selector;
74
75 }
76
77 public TrafficTreatment treatment() {
78 return this.trafficTreatment;
79 }
80
81 public TrafficSelector selector() {
82 return this.trafficSelector;
83 }
84
85 }
86
87 /**
Brian O'Connora67f8f22016-09-22 11:13:37 -070088 * Helper method to compute input and output ports.
Pier Ventred48320e2016-08-17 16:25:47 -070089 *
90 * @param intent the related intents
91 * @param inputPorts the input ports to compute
92 * @param outputPorts the output ports to compute
93 */
94 protected void computePorts(LinkCollectionIntent intent,
95 SetMultimap<DeviceId, PortNumber> inputPorts,
96 SetMultimap<DeviceId, PortNumber> outputPorts) {
97
98 for (Link link : intent.links()) {
99 inputPorts.put(link.dst().deviceId(), link.dst().port());
100 outputPorts.put(link.src().deviceId(), link.src().port());
101 }
102
103 for (ConnectPoint ingressPoint : intent.ingressPoints()) {
104 inputPorts.put(ingressPoint.deviceId(), ingressPoint.port());
105 }
106
107 for (ConnectPoint egressPoint : intent.egressPoints()) {
108 outputPorts.put(egressPoint.deviceId(), egressPoint.port());
109 }
110
111 }
112
113 /**
114 * Helper method to compute ingress and egress ports.
115 *
116 * @param intent the related intents
117 * @param ingressPorts the ingress ports to compute
118 * @param egressPorts the egress ports to compute
119 */
120 protected void computePorts(LinkCollectionIntent intent,
121 DeviceId deviceId,
122 Set<PortNumber> ingressPorts,
123 Set<PortNumber> egressPorts) {
124
125 if (!intent.applyTreatmentOnEgress()) {
126 ingressPorts.addAll(intent.ingressPoints().stream()
127 .filter(point -> point.deviceId().equals(deviceId))
128 .map(ConnectPoint::port)
129 .collect(Collectors.toSet()));
130 } else {
131 egressPorts.addAll(intent.egressPoints().stream()
132 .filter(point -> point.deviceId().equals(deviceId))
133 .map(ConnectPoint::port)
134 .collect(Collectors.toSet()));
135 }
136
137 }
138
139 /**
140 * Creates the flows representations.
141 *
142 * @param intent the intent to compile
143 * @param deviceId the affected device
144 * @param inPorts the input ports
145 * @param outPorts the output ports
146 * @return the list of flows representations
147 */
148 protected List<T> createRules(LinkCollectionIntent intent, DeviceId deviceId,
149 Set<PortNumber> inPorts, Set<PortNumber> outPorts) {
150 return null;
151 }
152
153
154 /**
155 * Computes treatment and selector which will be used
156 * in the flow representation (Rule, Objective).
157 *
158 * @param intent the intent to compile
159 * @param inPort the input port
Pier Ventre647138f2016-08-26 17:32:44 -0700160 * @param deviceId the current device
Pier Ventred48320e2016-08-17 16:25:47 -0700161 * @param outPorts the output ports
162 * @param ingressPorts the ingress ports
163 * @param egressPorts the egress ports
164 * @return the forwarding instruction object which encapsulates treatment and selector
165 */
Pier Ventre647138f2016-08-26 17:32:44 -0700166 protected ForwardingInstructions createForwardingInstructions(LinkCollectionIntent intent,
167 PortNumber inPort,
168 DeviceId deviceId,
Pier Ventred48320e2016-08-17 16:25:47 -0700169 Set<PortNumber> outPorts,
170 Set<PortNumber> ingressPorts,
171 Set<PortNumber> egressPorts) {
172
173 TrafficTreatment.Builder defaultTreatmentBuilder = DefaultTrafficTreatment.builder();
174 outPorts.forEach(defaultTreatmentBuilder::setOutput);
175 TrafficTreatment outputOnlyTreatment = defaultTreatmentBuilder.build();
176 TrafficSelector.Builder selectorBuilder;
177 TrafficTreatment treatment;
178 TrafficTreatment intentTreatment;
179
180 if (!intent.applyTreatmentOnEgress()) {
181 TrafficTreatment.Builder ingressTreatmentBuilder = DefaultTrafficTreatment.builder(intent.treatment());
182 outPorts.forEach(ingressTreatmentBuilder::setOutput);
183 intentTreatment = ingressTreatmentBuilder.build();
184
185 if (ingressPorts.contains(inPort)) {
Pier Ventre647138f2016-08-26 17:32:44 -0700186 if (intent.ingressSelectors() != null && !intent.ingressSelectors().isEmpty()) {
187 /**
188 * We iterate on the ingress points looking for the connect point
189 * associated to inPort.
190 */
191 Optional<ConnectPoint> connectPoint = intent.ingressPoints()
192 .stream()
193 .filter(ingressPoint -> ingressPoint.port().equals(inPort)
194 && ingressPoint.deviceId().equals(deviceId))
195 .findFirst();
196 if (connectPoint.isPresent()) {
197 selectorBuilder = DefaultTrafficSelector
198 .builder(intent.ingressSelectors().get(connectPoint.get()));
199 } else {
200 throw new IntentCompilationException("Looking for connect point associated to the selector." +
201 "inPort not in IngressPoints");
202 }
203 } else {
204 selectorBuilder = DefaultTrafficSelector.builder(intent.selector());
205 }
206 treatment = this.updateBuilder(ingressTreatmentBuilder, selectorBuilder.build()).build();
Pier Ventred48320e2016-08-17 16:25:47 -0700207 } else {
208 selectorBuilder = this.createSelectorFromFwdInstructions(
209 new ForwardingInstructions(intentTreatment, intent.selector())
210 );
211 treatment = outputOnlyTreatment;
212 }
213 } else {
214 if (outPorts.stream().allMatch(egressPorts::contains)) {
Pier Ventre27d42572016-08-29 17:37:08 -0700215 TrafficTreatment.Builder egressTreatmentBuilder = DefaultTrafficTreatment.builder();
216 if (intent.egressTreatments() != null && !intent.egressTreatments().isEmpty()) {
217 for (PortNumber outPort : outPorts) {
218 Optional<ConnectPoint> connectPoint = intent.egressPoints()
219 .stream()
220 .filter(egressPoint -> egressPoint.port().equals(outPort)
221 && egressPoint.deviceId().equals(deviceId))
222 .findFirst();
223 if (connectPoint.isPresent()) {
224 TrafficTreatment egressTreatment = intent.egressTreatments().get(connectPoint.get());
225 this.addTreatment(egressTreatmentBuilder, egressTreatment);
226 egressTreatmentBuilder = this.updateBuilder(egressTreatmentBuilder, intent.selector());
227 egressTreatmentBuilder.setOutput(outPort);
228 } else {
229 throw new IntentCompilationException("Looking for connect point associated to " +
230 "the treatment. outPort not in egressPoints");
231 }
232 }
233 } else {
234 egressTreatmentBuilder = this
235 .updateBuilder(DefaultTrafficTreatment.builder(intent.treatment()), intent.selector());
236 outPorts.forEach(egressTreatmentBuilder::setOutput);
237 }
Pier Ventred48320e2016-08-17 16:25:47 -0700238 selectorBuilder = DefaultTrafficSelector.builder(intent.selector());
239 treatment = egressTreatmentBuilder.build();
240 } else {
241 selectorBuilder = DefaultTrafficSelector.builder(intent.selector());
242 treatment = outputOnlyTreatment;
243 }
244 }
245
246 TrafficSelector selector = selectorBuilder.matchInPort(inPort).build();
247
248 return new ForwardingInstructions(treatment, selector);
249
250 }
251
252 /**
Pier Ventre27d42572016-08-29 17:37:08 -0700253 * Update a builder using a treatment.
254 * @param builder the builder to update
255 * @param treatment the treatment to add
256 * @return the new builder
257 */
258 private TrafficTreatment.Builder addTreatment(TrafficTreatment.Builder builder, TrafficTreatment treatment) {
259 builder.deferred();
260 for (Instruction instruction : treatment.deferred()) {
261 builder.add(instruction);
262 }
263 builder.immediate();
264 for (Instruction instruction : treatment.immediate()) {
265 builder.add(instruction);
266 }
267 return builder;
268 }
269
270 /**
Pier Ventre647138f2016-08-26 17:32:44 -0700271 * Update the original builder with the necessary operations
272 * to have a correct forwarding given an ingress selector.
Pier Ventre27d42572016-08-29 17:37:08 -0700273 * TODO
274 * This means that if the ingress selectors match on different vlanids and
275 * the egress treatment rewrite the vlanid the forwarding works
276 * but if we need to push for example an mpls label at the egress
277 * we need to implement properly this method.
Pier Ventre647138f2016-08-26 17:32:44 -0700278 *
279 * @param treatmentBuilder the builder to modify
Pier Ventre27d42572016-08-29 17:37:08 -0700280 * @param intentSelector the intent selector to use as input
Pier Ventre647138f2016-08-26 17:32:44 -0700281 * @return the new treatment created
282 */
283 private TrafficTreatment.Builder updateBuilder(TrafficTreatment.Builder treatmentBuilder,
284 TrafficSelector intentSelector) {
285 return treatmentBuilder;
286 }
287
288 /**
Pier Ventred48320e2016-08-17 16:25:47 -0700289 * Update the selector builder using a L0 instruction.
290 *
291 * @param builder the builder to update
292 * @param l0instruction the l0 instruction to use
293 */
294 private void updateBuilder(TrafficSelector.Builder builder, L0ModificationInstruction l0instruction) {
295 throw new IntentCompilationException("L0 not supported");
296 }
297
298 /**
299 * Update the selector builder using a L1 instruction.
300 *
301 * @param builder the builder to update
302 * @param l1instruction the l1 instruction to use
303 */
304 private void updateBuilder(TrafficSelector.Builder builder, L1ModificationInstruction l1instruction) {
305 throw new IntentCompilationException("L1 not supported");
306 }
307
308 /**
309 * Update the selector builder using a L2 instruction.
310 *
311 * @param builder the builder to update
312 * @param l2instruction the l2 instruction to use
313 */
314 private void updateBuilder(TrafficSelector.Builder builder, L2ModificationInstruction l2instruction) {
315 switch (l2instruction.subtype()) {
316 case ETH_SRC:
317 case ETH_DST:
318 ModEtherInstruction ethInstr = (ModEtherInstruction) l2instruction;
319 switch (ethInstr.subtype()) {
320 case ETH_SRC:
321 builder.matchEthSrc(ethInstr.mac());
322 break;
323 case ETH_DST:
324 builder.matchEthDst(ethInstr.mac());
325 break;
326 default:
327 throw new IntentCompilationException("Bad eth subtype");
328 }
329 break;
330 case VLAN_ID:
331 ModVlanIdInstruction vlanIdInstr = (ModVlanIdInstruction) l2instruction;
332 builder.matchVlanId(vlanIdInstr.vlanId());
333 break;
334 case VLAN_PUSH:
335 //FIXME
336 break;
337 case VLAN_POP:
338 //TODO how do we handle dropped label? remove the selector?
339 throw new IntentCompilationException("Can't handle pop label");
340 case VLAN_PCP:
341 ModVlanPcpInstruction vlanPcpInstruction = (ModVlanPcpInstruction) l2instruction;
342 builder.matchVlanPcp(vlanPcpInstruction.vlanPcp());
343 break;
344 case MPLS_LABEL:
345 case MPLS_PUSH:
346 //FIXME
347 ModMplsLabelInstruction mplsInstr = (ModMplsLabelInstruction) l2instruction;
348 builder.matchMplsLabel(mplsInstr.label());
349 break;
350 case MPLS_POP:
351 //TODO how do we handle dropped label? remove the selector?
352 throw new IntentCompilationException("Can't handle pop label");
353 case DEC_MPLS_TTL:
354 // no-op
355 break;
356 case MPLS_BOS:
357 ModMplsBosInstruction mplsBosInstr = (ModMplsBosInstruction) l2instruction;
358 builder.matchMplsBos(mplsBosInstr.mplsBos());
359 break;
360 case TUNNEL_ID:
361 ModTunnelIdInstruction tunInstr = (ModTunnelIdInstruction) l2instruction;
362 builder.matchTunnelId(tunInstr.tunnelId());
363 break;
364 default:
365 throw new IntentCompilationException("Unknown L2 Modification instruction");
366 }
367
368 }
369
370 /**
371 * Update the selector builder using a L3 instruction.
372 *
373 * @param builder the builder to update
374 * @param l3instruction the l3 instruction to use
375 */
376 private void updateBuilder(TrafficSelector.Builder builder, L3ModificationInstruction l3instruction) {
377 // TODO check ethernet proto
378 switch (l3instruction.subtype()) {
379 case IPV4_SRC:
380 case IPV4_DST:
381 case IPV6_SRC:
382 case IPV6_DST:
383 ModIPInstruction ipInstr = (ModIPInstruction) l3instruction;
384 // TODO check if ip falls in original prefix
385 IpPrefix prefix = ipInstr.ip().toIpPrefix();
386 switch (ipInstr.subtype()) {
387 case IPV4_SRC:
388 builder.matchIPSrc(prefix);
389 break;
390 case IPV4_DST:
391 builder.matchIPSrc(prefix);
392 break;
393 case IPV6_SRC:
394 builder.matchIPv6Src(prefix);
395 break;
396 case IPV6_DST:
397 builder.matchIPv6Dst(prefix);
398 break;
399 default:
400 throw new IntentCompilationException("Bad type for IP instruction");
401 }
402 break;
403 case IPV6_FLABEL:
404 ModIPv6FlowLabelInstruction ipFlowInstr = (ModIPv6FlowLabelInstruction) l3instruction;
405 builder.matchIPv6FlowLabel(ipFlowInstr.flowLabel());
406 break;
407 case DEC_TTL:
408 // no-op
409 break;
410 case TTL_OUT:
411 // no-op
412 break;
413 case TTL_IN:
414 // no-op
415 break;
416 case ARP_SPA:
417 ModArpIPInstruction arpIpInstr = (ModArpIPInstruction) l3instruction;
418 if (arpIpInstr.ip().isIp4()) {
419 builder.matchArpSpa((Ip4Address) arpIpInstr.ip());
420 } else {
421 throw new IntentCompilationException("IPv6 not supported for ARP");
422 }
423 break;
424 case ARP_SHA:
425 ModArpEthInstruction arpEthInstr = (ModArpEthInstruction) l3instruction;
426 builder.matchArpSha(arpEthInstr.mac());
427 break;
428 case ARP_OP:
429 ModArpOpInstruction arpOpInstr = (ModArpOpInstruction) l3instruction;
430 //FIXME is the long to int cast safe?
431 builder.matchArpOp((int) arpOpInstr.op());
432 break;
433 default:
434 throw new IntentCompilationException("Unknown L3 Modification instruction");
435 }
436 }
437
438 /**
439 * Update the selector builder using a L4 instruction.
440 *
441 * @param builder the builder to update
442 * @param l4instruction the l4 instruction to use
443 */
444 private void updateBuilder(TrafficSelector.Builder builder, L4ModificationInstruction l4instruction) {
445 if (l4instruction instanceof ModTransportPortInstruction) {
446 // TODO check IP proto
447 ModTransportPortInstruction l4mod = (ModTransportPortInstruction) l4instruction;
448 switch (l4mod.subtype()) {
449 case TCP_SRC:
450 builder.matchTcpSrc(l4mod.port());
451 break;
452 case TCP_DST:
453 builder.matchTcpDst(l4mod.port());
454 break;
455 case UDP_SRC:
456 builder.matchUdpSrc(l4mod.port());
457 break;
458 case UDP_DST:
459 builder.matchUdpDst(l4mod.port());
460 break;
461 default:
462 throw new IntentCompilationException("Unknown L4 Modification instruction");
463 }
464 } else {
465 throw new IntentCompilationException("Unknown L4 Modification instruction");
466 }
467 }
468
469 /**
470 * Computes the new traffic selector using the
471 * forwarding instructions.
472 *
473 * @param fwInstructions it encapsulates the instructions to compute the new selector
474 * @return the traffic selector builder
475 */
476 private TrafficSelector.Builder createSelectorFromFwdInstructions(ForwardingInstructions fwInstructions) {
477 TrafficSelector.Builder defaultSelectorBuilder = DefaultTrafficSelector.builder(fwInstructions.selector());
478 fwInstructions.treatment().allInstructions().forEach(instruction -> {
479 switch (instruction.type()) {
480 case L0MODIFICATION:
481 updateBuilder(defaultSelectorBuilder, (L0ModificationInstruction) instruction);
482 break;
483 case L1MODIFICATION:
484 updateBuilder(defaultSelectorBuilder, (L1ModificationInstruction) instruction);
485 break;
486 case L2MODIFICATION:
487 updateBuilder(defaultSelectorBuilder, (L2ModificationInstruction) instruction);
488 break;
489 case L3MODIFICATION:
490 updateBuilder(defaultSelectorBuilder, (L3ModificationInstruction) instruction);
491 break;
492 case L4MODIFICATION:
493 updateBuilder(defaultSelectorBuilder, (L4ModificationInstruction) instruction);
494 break;
495 case NOACTION:
496 case OUTPUT:
497 case GROUP:
498 case QUEUE:
499 case TABLE:
500 case METER:
501 case METADATA:
502 case EXTENSION: // TODO is extension no-op or unsupported?
503 // Nothing to do
504 break;
505 default:
506 throw new IntentCompilationException("Unknown instruction type");
507 }
508 });
509 return defaultSelectorBuilder;
510 }
511
512}