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