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