blob: b236d59477086f8e4141bcc7b8ac99fb72d14b9f [file] [log] [blame]
Ray Milkey661c38c2016-02-26 17:12:17 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Ray Milkey661c38c2016-02-26 17:12:17 -08003 *
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 */
16package org.onosproject.net.intent.impl.compiler;
17
Jian Li11260a02016-05-19 13:07:22 -070018import com.google.common.collect.Sets;
Michele Santuari6096acd2016-02-09 17:00:37 +010019import org.onlab.packet.EthType;
20import org.onlab.packet.Ethernet;
21import org.onlab.packet.MplsLabel;
Ray Milkey661c38c2016-02-26 17:12:17 -080022import org.onlab.packet.VlanId;
Pier Ventref8543d82016-09-28 19:49:33 -070023import org.onlab.util.Identifier;
Ray Milkey661c38c2016-02-26 17:12:17 -080024import org.onosproject.net.ConnectPoint;
25import org.onosproject.net.DeviceId;
Pier Ventref8543d82016-09-28 19:49:33 -070026import org.onosproject.net.EncapsulationType;
Ray Milkey661c38c2016-02-26 17:12:17 -080027import org.onosproject.net.Link;
28import org.onosproject.net.LinkKey;
29import org.onosproject.net.flow.DefaultTrafficSelector;
30import org.onosproject.net.flow.DefaultTrafficTreatment;
31import org.onosproject.net.flow.TrafficSelector;
32import org.onosproject.net.flow.TrafficTreatment;
33import org.onosproject.net.flow.criteria.Criterion;
Michele Santuari6096acd2016-02-09 17:00:37 +010034import org.onosproject.net.flow.criteria.EthTypeCriterion;
35import org.onosproject.net.flow.criteria.MplsCriterion;
Ray Milkey661c38c2016-02-26 17:12:17 -080036import org.onosproject.net.flow.criteria.VlanIdCriterion;
Michele Santuari6096acd2016-02-09 17:00:37 +010037import org.onosproject.net.flow.instructions.Instruction;
Ray Milkey661c38c2016-02-26 17:12:17 -080038import org.onosproject.net.flow.instructions.L2ModificationInstruction;
Yuta HIGUCHId95d5902016-06-27 00:18:45 -070039import org.onosproject.net.intent.IntentCompilationException;
Ray Milkey661c38c2016-02-26 17:12:17 -080040import org.onosproject.net.intent.PathIntent;
41import org.onosproject.net.intent.constraint.EncapsulationConstraint;
Sho SHIMIZUe18cb122016-02-22 21:04:56 -080042import org.onosproject.net.resource.ResourceService;
Pier Ventref8543d82016-09-28 19:49:33 -070043import org.onosproject.net.resource.impl.LabelAllocator;
Ray Milkey661c38c2016-02-26 17:12:17 -080044import org.slf4j.Logger;
45
Jian Li11260a02016-05-19 13:07:22 -070046import java.util.Iterator;
47import java.util.List;
48import java.util.Map;
49import java.util.Optional;
50import java.util.Set;
Ray Milkey661c38c2016-02-26 17:12:17 -080051
52import static org.onosproject.net.LinkKey.linkKey;
53
54/**
55 * Shared APIs and implementations for path compilers.
56 */
57
58public class PathCompiler<T> {
59
Pier Ventref8543d82016-09-28 19:49:33 -070060 private static final String ERROR_VLAN = "No VLAN Ids available for ";
61 private static final String ERROR_MPLS = "No available MPLS labels for ";
62
63 static LabelAllocator labelAllocator;
Pier Luigi Ventre51313bd2016-06-02 10:50:30 +020064
Ray Milkey661c38c2016-02-26 17:12:17 -080065 /**
66 * Defines methods used to create objects representing flows.
67 */
68 public interface PathCompilerCreateFlow<T> {
69
70 void createFlow(TrafficSelector originalSelector,
71 TrafficTreatment originalTreatment,
72 ConnectPoint ingress, ConnectPoint egress,
73 int priority,
74 boolean applyTreatment,
75 List<T> flows,
76 List<DeviceId> devices);
77
78 Logger log();
79
80 ResourceService resourceService();
81 }
82
83 private boolean isLast(List<Link> links, int i) {
84 return i == links.size() - 2;
85 }
86
Pier Luigi Ventre51313bd2016-06-02 10:50:30 +020087 /**
Pier Ventref8543d82016-09-28 19:49:33 -070088 * Returns the ethertype match needed. If the selector provides
89 * an ethertype, it will be used. IPv4 will be used otherwise.
Pier Luigi Ventre51313bd2016-06-02 10:50:30 +020090 *
Pier Ventref8543d82016-09-28 19:49:33 -070091 * @param selector the traffic selector.
92 * @return the ethertype we should match against
Pier Luigi Ventre51313bd2016-06-02 10:50:30 +020093 */
Pier Ventref8543d82016-09-28 19:49:33 -070094 private EthType getEthType(TrafficSelector selector) {
95 Criterion c = selector.getCriterion(Criterion.Type.ETH_TYPE);
96 if (c != null && c instanceof EthTypeCriterion) {
97 EthTypeCriterion ethertype = (EthTypeCriterion) c;
98 return ethertype.ethType();
99 } else {
100 return EthType.EtherType.IPV4.ethType();
Pier Luigi Ventre51313bd2016-06-02 10:50:30 +0200101 }
Pier Luigi Ventre51313bd2016-06-02 10:50:30 +0200102 }
103
104 /**
Pier Ventref8543d82016-09-28 19:49:33 -0700105 * Creates the flow rules for the path intent using VLAN
106 * encapsulation.
Pier Luigi Ventre51313bd2016-06-02 10:50:30 +0200107 *
Pier Ventref8543d82016-09-28 19:49:33 -0700108 * @param creator the flowrules creator
109 * @param flows the list of flows to fill
110 * @param devices the devices on the path
111 * @param intent the PathIntent to compile
Pier Luigi Ventre51313bd2016-06-02 10:50:30 +0200112 */
Ray Milkey661c38c2016-02-26 17:12:17 -0800113 private void manageVlanEncap(PathCompilerCreateFlow<T> creator, List<T> flows,
114 List<DeviceId> devices,
115 PathIntent intent) {
Pier Ventref8543d82016-09-28 19:49:33 -0700116
117 Set<Link> linksSet = Sets.newConcurrentHashSet();
118 for (int i = 1; i <= intent.path().links().size() - 2; i++) {
119 linksSet.add(intent.path().links().get(i));
120 }
121
122 Map<LinkKey, Identifier<?>> vlanIds = labelAllocator.assignLabelToLinks(
123 linksSet,
124 intent.id(),
125 EncapsulationType.VLAN
126 );
Ray Milkey661c38c2016-02-26 17:12:17 -0800127
128 Iterator<Link> links = intent.path().links().iterator();
129 Link srcLink = links.next();
130
131 Link link = links.next();
132
133 // Ingress traffic
Pier Ventref8543d82016-09-28 19:49:33 -0700134 VlanId vlanId = (VlanId) vlanIds.get(linkKey(link));
Ray Milkey661c38c2016-02-26 17:12:17 -0800135 if (vlanId == null) {
Pier Ventref8543d82016-09-28 19:49:33 -0700136 throw new IntentCompilationException(ERROR_VLAN + link);
Ray Milkey661c38c2016-02-26 17:12:17 -0800137 }
138 VlanId prevVlanId = vlanId;
139
140 Optional<VlanIdCriterion> vlanCriterion = intent.selector().criteria()
141 .stream().filter(criterion -> criterion.type() == Criterion.Type.VLAN_VID)
142 .map(criterion -> (VlanIdCriterion) criterion)
143 .findAny();
144
145 //Push VLAN if selector does not include VLAN
146 TrafficTreatment.Builder treatBuilder = DefaultTrafficTreatment.builder();
147 if (!vlanCriterion.isPresent()) {
148 treatBuilder.pushVlan();
149 }
150 //Tag the traffic with the new encapsulation VLAN
151 treatBuilder.setVlanId(vlanId);
152 creator.createFlow(intent.selector(), treatBuilder.build(),
153 srcLink.dst(), link.src(), intent.priority(), true,
154 flows, devices);
155
156 ConnectPoint prev = link.dst();
157
158 while (links.hasNext()) {
159
160 link = links.next();
161
162 if (links.hasNext()) {
163 // Transit traffic
Pier Ventref8543d82016-09-28 19:49:33 -0700164 VlanId egressVlanId = (VlanId) vlanIds.get(linkKey(link));
Ray Milkey661c38c2016-02-26 17:12:17 -0800165 if (egressVlanId == null) {
Pier Ventref8543d82016-09-28 19:49:33 -0700166 throw new IntentCompilationException(ERROR_VLAN + link);
Ray Milkey661c38c2016-02-26 17:12:17 -0800167 }
Ray Milkey661c38c2016-02-26 17:12:17 -0800168
169 TrafficSelector transitSelector = DefaultTrafficSelector.builder()
170 .matchInPort(prev.port())
171 .matchVlanId(prevVlanId).build();
172
173 TrafficTreatment.Builder transitTreat = DefaultTrafficTreatment.builder();
174
175 // Set the new vlanId only if the previous one is different
176 if (!prevVlanId.equals(egressVlanId)) {
177 transitTreat.setVlanId(egressVlanId);
178 }
179 creator.createFlow(transitSelector,
180 transitTreat.build(), prev, link.src(),
181 intent.priority(), true, flows, devices);
Pier Luigi Ventre51313bd2016-06-02 10:50:30 +0200182 /* For the next hop we have to remember
183 * the previous egress VLAN id and the egress
184 * node
185 */
186 prevVlanId = egressVlanId;
Ray Milkey661c38c2016-02-26 17:12:17 -0800187 prev = link.dst();
188 } else {
189 // Egress traffic
190 TrafficSelector egressSelector = DefaultTrafficSelector.builder()
191 .matchInPort(prev.port())
192 .matchVlanId(prevVlanId).build();
193 TrafficTreatment.Builder egressTreat = DefaultTrafficTreatment.builder(intent.treatment());
194
195 Optional<L2ModificationInstruction.ModVlanIdInstruction> modVlanIdInstruction = intent.treatment()
196 .allInstructions().stream().filter(
197 instruction -> instruction instanceof L2ModificationInstruction.ModVlanIdInstruction)
198 .map(x -> (L2ModificationInstruction.ModVlanIdInstruction) x).findAny();
199
Jian Li11260a02016-05-19 13:07:22 -0700200 Optional<L2ModificationInstruction.ModVlanHeaderInstruction> popVlanInstruction = intent.treatment()
Ray Milkey661c38c2016-02-26 17:12:17 -0800201 .allInstructions().stream().filter(
Jian Li11260a02016-05-19 13:07:22 -0700202 instruction -> instruction instanceof
203 L2ModificationInstruction.ModVlanHeaderInstruction)
204 .map(x -> (L2ModificationInstruction.ModVlanHeaderInstruction) x).findAny();
Ray Milkey661c38c2016-02-26 17:12:17 -0800205
206 if (!modVlanIdInstruction.isPresent() && !popVlanInstruction.isPresent()) {
207 if (vlanCriterion.isPresent()) {
208 egressTreat.setVlanId(vlanCriterion.get().vlanId());
209 } else {
210 egressTreat.popVlan();
211 }
212 }
213
214 creator.createFlow(egressSelector,
215 egressTreat.build(), prev, link.src(),
216 intent.priority(), true, flows, devices);
217 }
218 }
219 }
220
Pier Ventref8543d82016-09-28 19:49:33 -0700221 /**
222 * Creates the flow rules for the path intent using MPLS
223 * encapsulation.
224 *
225 * @param creator the flowrules creator
226 * @param flows the list of flows to fill
227 * @param devices the devices on the path
228 * @param intent the PathIntent to compile
229 */
Michele Santuari6096acd2016-02-09 17:00:37 +0100230 private void manageMplsEncap(PathCompilerCreateFlow<T> creator, List<T> flows,
231 List<DeviceId> devices,
232 PathIntent intent) {
Michele Santuari6096acd2016-02-09 17:00:37 +0100233
Pier Ventref8543d82016-09-28 19:49:33 -0700234 Set<Link> linksSet = Sets.newConcurrentHashSet();
235 for (int i = 1; i <= intent.path().links().size() - 2; i++) {
236 linksSet.add(intent.path().links().get(i));
237 }
238
239 Map<LinkKey, Identifier<?>> mplsLabels = labelAllocator.assignLabelToLinks(
240 linksSet,
241 intent.id(),
242 EncapsulationType.MPLS
243 );
Michele Santuari6096acd2016-02-09 17:00:37 +0100244 Iterator<Link> links = intent.path().links().iterator();
245 Link srcLink = links.next();
246
247 Link link = links.next();
248 // List of flow rules to be installed
249
250 // Ingress traffic
Pier Ventref8543d82016-09-28 19:49:33 -0700251 MplsLabel mplsLabel = (MplsLabel) mplsLabels.get(linkKey(link));
Michele Santuari6096acd2016-02-09 17:00:37 +0100252 if (mplsLabel == null) {
Pier Ventref8543d82016-09-28 19:49:33 -0700253 throw new IntentCompilationException(ERROR_MPLS + link);
Michele Santuari6096acd2016-02-09 17:00:37 +0100254 }
255 MplsLabel prevMplsLabel = mplsLabel;
256
257 Optional<MplsCriterion> mplsCriterion = intent.selector().criteria()
258 .stream().filter(criterion -> criterion.type() == Criterion.Type.MPLS_LABEL)
259 .map(criterion -> (MplsCriterion) criterion)
260 .findAny();
261
262 //Push MPLS if selector does not include MPLS
263 TrafficTreatment.Builder treatBuilder = DefaultTrafficTreatment.builder();
264 if (!mplsCriterion.isPresent()) {
265 treatBuilder.pushMpls();
266 }
267 //Tag the traffic with the new encapsulation MPLS label
268 treatBuilder.setMpls(mplsLabel);
269 creator.createFlow(intent.selector(), treatBuilder.build(),
270 srcLink.dst(), link.src(), intent.priority(), true, flows, devices);
271
272 ConnectPoint prev = link.dst();
273
274 while (links.hasNext()) {
275
276 link = links.next();
277
278 if (links.hasNext()) {
279 // Transit traffic
Pier Ventref8543d82016-09-28 19:49:33 -0700280 MplsLabel transitMplsLabel = (MplsLabel) mplsLabels.get(linkKey(link));
Michele Santuari6096acd2016-02-09 17:00:37 +0100281 if (transitMplsLabel == null) {
Pier Ventref8543d82016-09-28 19:49:33 -0700282 throw new IntentCompilationException(ERROR_MPLS + link);
Michele Santuari6096acd2016-02-09 17:00:37 +0100283 }
Michele Santuari6096acd2016-02-09 17:00:37 +0100284 TrafficSelector transitSelector = DefaultTrafficSelector.builder()
285 .matchInPort(prev.port())
286 .matchEthType(Ethernet.MPLS_UNICAST)
287 .matchMplsLabel(prevMplsLabel).build();
288
289 TrafficTreatment.Builder transitTreat = DefaultTrafficTreatment.builder();
290
Pier Ventref8543d82016-09-28 19:49:33 -0700291 // Set the new MPLS label only if the previous one is different
Michele Santuari6096acd2016-02-09 17:00:37 +0100292 if (!prevMplsLabel.equals(transitMplsLabel)) {
293 transitTreat.setMpls(transitMplsLabel);
294 }
295 creator.createFlow(transitSelector,
296 transitTreat.build(), prev, link.src(), intent.priority(), true, flows, devices);
Pier Ventref8543d82016-09-28 19:49:33 -0700297 prevMplsLabel = transitMplsLabel;
Michele Santuari6096acd2016-02-09 17:00:37 +0100298 prev = link.dst();
299 } else {
300 TrafficSelector.Builder egressSelector = DefaultTrafficSelector.builder()
301 .matchInPort(prev.port())
302 .matchEthType(Ethernet.MPLS_UNICAST)
303 .matchMplsLabel(prevMplsLabel);
304 TrafficTreatment.Builder egressTreat = DefaultTrafficTreatment.builder(intent.treatment());
305
306 // Egress traffic
307 // check if the treatement is popVlan or setVlan (rewrite),
308 // than selector needs to match any VlanId
309 for (Instruction instruct : intent.treatment().allInstructions()) {
310 if (instruct instanceof L2ModificationInstruction) {
311 L2ModificationInstruction l2Mod = (L2ModificationInstruction) instruct;
312 if (l2Mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_PUSH) {
313 break;
314 }
315 if (l2Mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_POP ||
316 l2Mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_ID) {
317 egressSelector.matchVlanId(VlanId.ANY);
318 }
319 }
320 }
321
322 if (mplsCriterion.isPresent()) {
323 egressTreat.setMpls(mplsCriterion.get().label());
324 } else {
Pier Ventref8543d82016-09-28 19:49:33 -0700325 egressTreat.popMpls(getEthType(intent.selector()));
Michele Santuari6096acd2016-02-09 17:00:37 +0100326 }
327
328 creator.createFlow(egressSelector.build(),
329 egressTreat.build(), prev, link.src(), intent.priority(), true, flows, devices);
330 }
331
332 }
333
334 }
335
Ray Milkey661c38c2016-02-26 17:12:17 -0800336 /**
337 * Compiles an intent down to flows.
338 *
339 * @param creator how to create the flows
340 * @param intent intent to process
341 * @param flows list of generated flows
342 * @param devices list of devices that correspond to the flows
343 */
344 public void compile(PathCompilerCreateFlow<T> creator,
345 PathIntent intent,
346 List<T> flows,
347 List<DeviceId> devices) {
348 // Note: right now recompile is not considered
349 // TODO: implement recompile behavior
350
351 List<Link> links = intent.path().links();
352
353 Optional<EncapsulationConstraint> encapConstraint = intent.constraints().stream()
354 .filter(constraint -> constraint instanceof EncapsulationConstraint)
355 .map(x -> (EncapsulationConstraint) x).findAny();
356 //if no encapsulation or is involved only a single switch use the default behaviour
Pier1677f9f2016-07-06 15:42:17 +0200357 if (!encapConstraint.isPresent() || links.size() == 2) {
Ray Milkey661c38c2016-02-26 17:12:17 -0800358 for (int i = 0; i < links.size() - 1; i++) {
359 ConnectPoint ingress = links.get(i).dst();
360 ConnectPoint egress = links.get(i + 1).src();
361 creator.createFlow(intent.selector(), intent.treatment(),
362 ingress, egress, intent.priority(),
363 isLast(links, i), flows, devices);
364 }
Pier1677f9f2016-07-06 15:42:17 +0200365 return;
Ray Milkey661c38c2016-02-26 17:12:17 -0800366 }
367
368 encapConstraint.map(EncapsulationConstraint::encapType)
369 .map(type -> {
370 switch (type) {
371 case VLAN:
372 manageVlanEncap(creator, flows, devices, intent);
Michele Santuari6096acd2016-02-09 17:00:37 +0100373 break;
374 case MPLS:
375 manageMplsEncap(creator, flows, devices, intent);
376 break;
Ray Milkey661c38c2016-02-26 17:12:17 -0800377 default:
378 // Nothing to do
379 }
380 return 0;
381 });
382 }
383
384}