718c7bbf1cdaebbb54eb38bf8e06a7a992b4044b
[onosfw.git] /
1 /*
2  * Copyright 2015 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 package org.onosproject.net.intent.impl.compiler;
17
18 import com.google.common.collect.Sets;
19
20 import org.apache.felix.scr.annotations.Activate;
21 import org.apache.felix.scr.annotations.Component;
22 import org.apache.felix.scr.annotations.Deactivate;
23 import org.apache.felix.scr.annotations.Reference;
24 import org.apache.felix.scr.annotations.ReferenceCardinality;
25 import org.onlab.packet.EthType;
26 import org.onlab.packet.Ethernet;
27 import org.onlab.packet.MplsLabel;
28 import org.onlab.packet.VlanId;
29 import org.onosproject.core.ApplicationId;
30 import org.onosproject.core.CoreService;
31 import org.onosproject.net.ConnectPoint;
32 import org.onosproject.net.DeviceId;
33 import org.onosproject.net.Link;
34 import org.onosproject.net.LinkKey;
35 import org.onosproject.net.PortNumber;
36 import org.onosproject.net.flow.DefaultFlowRule;
37 import org.onosproject.net.flow.DefaultTrafficSelector;
38 import org.onosproject.net.flow.DefaultTrafficTreatment;
39 import org.onosproject.net.flow.FlowRule;
40 import org.onosproject.net.flow.TrafficSelector;
41 import org.onosproject.net.flow.TrafficTreatment;
42 import org.onosproject.net.flow.criteria.Criterion;
43 import org.onosproject.net.flow.criteria.EthTypeCriterion;
44 import org.onosproject.net.flow.instructions.Instruction;
45 import org.onosproject.net.flow.instructions.L2ModificationInstruction;
46 import org.onosproject.net.intent.FlowRuleIntent;
47 import org.onosproject.net.intent.Intent;
48 import org.onosproject.net.intent.IntentCompiler;
49 import org.onosproject.net.intent.IntentExtensionService;
50 import org.onosproject.net.intent.MplsPathIntent;
51 import org.onosproject.net.newresource.ResourcePath;
52 import org.onosproject.net.newresource.ResourceService;
53 import org.onosproject.net.resource.link.LinkResourceAllocations;
54 import org.slf4j.Logger;
55
56 import java.util.Collections;
57 import java.util.HashMap;
58 import java.util.Iterator;
59 import java.util.LinkedList;
60 import java.util.List;
61 import java.util.Map;
62 import java.util.Optional;
63 import java.util.Set;
64 import java.util.stream.Collectors;
65
66 import static com.google.common.base.Preconditions.checkNotNull;
67 import static org.onosproject.net.LinkKey.linkKey;
68 import static org.slf4j.LoggerFactory.getLogger;
69
70 @Component(immediate = true)
71 public class MplsPathIntentCompiler implements IntentCompiler<MplsPathIntent> {
72
73     private final Logger log = getLogger(getClass());
74
75     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
76     protected IntentExtensionService intentExtensionService;
77
78     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79     protected CoreService coreService;
80
81     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82     protected ResourceService resourceService;
83
84     protected ApplicationId appId;
85
86     @Override
87     public List<Intent> compile(MplsPathIntent intent, List<Intent> installable,
88                                 Set<LinkResourceAllocations> resources) {
89         Map<LinkKey, MplsLabel> labels = assignMplsLabel(intent);
90         List<FlowRule> rules = generateRules(intent, labels);
91
92         return Collections.singletonList(new FlowRuleIntent(appId, rules, intent.resources()));
93     }
94
95     @Activate
96     public void activate() {
97         appId = coreService.registerApplication("org.onosproject.net.intent");
98         intentExtensionService.registerCompiler(MplsPathIntent.class, this);
99     }
100
101     @Deactivate
102     public void deactivate() {
103         intentExtensionService.unregisterCompiler(MplsPathIntent.class);
104     }
105
106     private Map<LinkKey, MplsLabel> assignMplsLabel(MplsPathIntent intent) {
107         // TODO: do it better... Suggestions?
108         Set<LinkKey> linkRequest = Sets.newHashSetWithExpectedSize(intent.path()
109                 .links().size() - 2);
110         for (int i = 1; i <= intent.path().links().size() - 2; i++) {
111             LinkKey link = linkKey(intent.path().links().get(i));
112             linkRequest.add(link);
113             // add the inverse link. I want that the label is reserved both for
114             // the direct and inverse link
115             linkRequest.add(linkKey(link.dst(), link.src()));
116         }
117
118         Map<LinkKey, MplsLabel> labels = findMplsLabels(linkRequest);
119         if (labels.isEmpty()) {
120             return Collections.emptyMap();
121         }
122
123         List<ResourcePath> resources = labels.entrySet().stream()
124                 .map(x -> ResourcePath.discrete(linkKey(x.getKey().src(), x.getKey().src()), x.getValue()))
125                 .collect(Collectors.toList());
126         List<org.onosproject.net.newresource.ResourceAllocation> allocations =
127                 resourceService.allocate(intent.id(), resources);
128         if (allocations.isEmpty()) {
129             Collections.emptyMap();
130         }
131
132         return labels;
133     }
134
135     private Map<LinkKey, MplsLabel> findMplsLabels(Set<LinkKey> links) {
136         Map<LinkKey, MplsLabel> labels = new HashMap<>();
137         for (LinkKey link : links) {
138             Optional<MplsLabel> label = findMplsLabel(link);
139             if (label.isPresent()) {
140                 labels.put(link, label.get());
141             }
142         }
143
144         return labels;
145     }
146
147     private Optional<MplsLabel> findMplsLabel(LinkKey link) {
148         return resourceService.getAvailableResources(ResourcePath.discrete(link)).stream()
149                 .filter(x -> x.last() instanceof MplsLabel)
150                 .map(x -> (MplsLabel) x.last())
151                 .findFirst();
152     }
153
154     private MplsLabel getMplsLabel(Map<LinkKey, MplsLabel> labels, LinkKey link) {
155         return labels.get(link);
156     }
157
158     private List<FlowRule> generateRules(MplsPathIntent intent,
159                                          Map<LinkKey, MplsLabel> labels) {
160
161         Iterator<Link> links = intent.path().links().iterator();
162         Link srcLink = links.next();
163         ConnectPoint prev = srcLink.dst();
164
165         Link link = links.next();
166         // List of flow rules to be installed
167         List<FlowRule> rules = new LinkedList<>();
168
169         // Ingress traffic
170         // Get the new MPLS label
171         MplsLabel mpls = getMplsLabel(labels, linkKey(link));
172         checkNotNull(mpls);
173         MplsLabel prevLabel = mpls;
174         rules.add(ingressFlow(prev.port(), link, intent, mpls));
175
176         prev = link.dst();
177
178         while (links.hasNext()) {
179
180             link = links.next();
181
182             if (links.hasNext()) {
183                 // Transit traffic
184                 // Get the new MPLS label
185                 mpls = getMplsLabel(labels, linkKey(link));
186                 checkNotNull(mpls);
187                 rules.add(transitFlow(prev.port(), link, intent,
188                         prevLabel, mpls));
189                 prevLabel = mpls;
190
191             } else {
192                 // Egress traffic
193                 rules.add(egressFlow(prev.port(), link, intent,
194                         prevLabel));
195             }
196
197             prev = link.dst();
198         }
199         return rules;
200     }
201
202     private FlowRule ingressFlow(PortNumber inPort, Link link,
203                                  MplsPathIntent intent,
204                                  MplsLabel label) {
205
206         TrafficSelector.Builder ingressSelector = DefaultTrafficSelector
207                 .builder(intent.selector());
208         TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
209         ingressSelector.matchInPort(inPort);
210
211         if (intent.ingressLabel().isPresent()) {
212             ingressSelector.matchEthType(Ethernet.MPLS_UNICAST)
213                     .matchMplsLabel(intent.ingressLabel().get());
214
215             // Swap the MPLS label
216             treat.setMpls(label);
217         } else {
218             // Push and set the MPLS label
219             treat.pushMpls().setMpls(label);
220         }
221         // Add the output action
222         treat.setOutput(link.src().port());
223
224         return createFlowRule(intent, link.src().deviceId(), ingressSelector.build(), treat.build());
225     }
226
227     private FlowRule transitFlow(PortNumber inPort, Link link,
228                                  MplsPathIntent intent,
229                                  MplsLabel prevLabel,
230                                  MplsLabel outLabel) {
231
232         // Ignore the ingress Traffic Selector and use only the MPLS label
233         // assigned in the previous link
234         TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
235         selector.matchInPort(inPort).matchEthType(Ethernet.MPLS_UNICAST)
236                 .matchMplsLabel(prevLabel);
237         TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
238
239         // Set the new label only if the label on the packet is
240         // different
241         if (!prevLabel.equals(outLabel)) {
242             treat.setMpls(outLabel);
243         }
244
245         treat.setOutput(link.src().port());
246         return createFlowRule(intent, link.src().deviceId(), selector.build(), treat.build());
247     }
248
249     private FlowRule egressFlow(PortNumber inPort, Link link,
250                                 MplsPathIntent intent,
251                                 MplsLabel prevLabel) {
252         // egress point: either set the egress MPLS label or pop the
253         // MPLS label based on the intent annotations
254
255         TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
256         selector.matchInPort(inPort).matchEthType(Ethernet.MPLS_UNICAST)
257                 .matchMplsLabel(prevLabel);
258
259         // apply the intent's treatments
260         TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder(intent
261                 .treatment());
262
263         // check if the treatement is popVlan or setVlan (rewrite),
264         // than selector needs to match any VlanId
265         for (Instruction instruct : intent.treatment().allInstructions()) {
266             if (instruct instanceof L2ModificationInstruction) {
267                 L2ModificationInstruction l2Mod = (L2ModificationInstruction) instruct;
268                 if (l2Mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_PUSH) {
269                     break;
270                 }
271                 if (l2Mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_POP ||
272                         l2Mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_ID) {
273                     selector.matchVlanId(VlanId.ANY);
274                 }
275             }
276         }
277
278         if (intent.egressLabel().isPresent()) {
279             treat.setMpls(intent.egressLabel().get());
280         } else {
281                 treat.popMpls(outputEthType(intent.selector()));
282         }
283         treat.setOutput(link.src().port());
284         return createFlowRule(intent, link.src().deviceId(),
285                 selector.build(), treat.build());
286     }
287
288     protected FlowRule createFlowRule(MplsPathIntent intent, DeviceId deviceId,
289                                       TrafficSelector selector, TrafficTreatment treat) {
290         return DefaultFlowRule.builder()
291                 .forDevice(deviceId)
292                 .withSelector(selector)
293                 .withTreatment(treat)
294                 .withPriority(intent.priority())
295                 .fromApp(appId)
296                 .makePermanent()
297                 .build();
298     }
299
300     // if the ingress ethertype is defined, the egress traffic
301     // will be use that value, otherwise the IPv4 ethertype is used.
302     private EthType outputEthType(TrafficSelector selector) {
303         Criterion c = selector.getCriterion(Criterion.Type.ETH_TYPE);
304         if (c != null && c instanceof EthTypeCriterion) {
305             EthTypeCriterion ethertype = (EthTypeCriterion) c;
306             return ethertype.ethType();
307         } else {
308             return EthType.EtherType.IPV4.ethType();
309         }
310     }
311 }