blob: 308bdffef6ce9028ffbe3cedba7493950d2ff709 [file] [log] [blame]
Sean Condonfae8e662016-12-15 10:25:13 +00001/*
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 */
16package org.onosproject.drivers.microsemi;
17
18import static org.slf4j.LoggerFactory.getLogger;
19
20import java.util.ArrayList;
21import java.util.List;
22import java.util.Set;
23import java.util.concurrent.TimeUnit;
24
25import org.onlab.osgi.ServiceDirectory;
26import org.onosproject.drivers.microsemi.EA1000FlowRuleProgrammable.Ea1000Port;
27import org.onosproject.net.DeviceId;
28import org.onosproject.net.PortNumber;
29import org.onosproject.net.behaviour.NextGroup;
30import org.onosproject.net.behaviour.Pipeliner;
31import org.onosproject.net.behaviour.PipelinerContext;
32import org.onosproject.net.driver.AbstractHandlerBehaviour;
33import org.onosproject.net.flow.DefaultFlowRule;
34import org.onosproject.net.flow.DefaultTrafficSelector;
35import org.onosproject.net.flow.DefaultTrafficTreatment;
36import org.onosproject.net.flow.FlowRule;
37import org.onosproject.net.flow.FlowRuleOperations;
38import org.onosproject.net.flow.FlowRuleOperationsContext;
39import org.onosproject.net.flow.FlowRuleService;
40import org.onosproject.net.flow.TrafficSelector;
41import org.onosproject.net.flow.TrafficTreatment;
42import org.onosproject.net.flow.criteria.Criterion;
43import org.onosproject.net.flow.criteria.Criterion.Type;
44import org.onosproject.net.flow.criteria.PortCriterion;
45import org.onosproject.net.flow.criteria.VlanIdCriterion;
46import org.onosproject.net.flow.instructions.Instruction;
47import org.onosproject.net.flow.instructions.Instructions;
48import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
49import org.onosproject.net.flow.instructions.L2ModificationInstruction;
50import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
51import org.onosproject.net.flowobjective.FilteringObjective;
52import org.onosproject.net.flowobjective.ForwardingObjective;
53import org.onosproject.net.flowobjective.NextObjective;
54import org.onosproject.net.flowobjective.Objective;
55import org.onosproject.net.flowobjective.ObjectiveError;
56import org.slf4j.Logger;
57
58import com.google.common.cache.Cache;
59import com.google.common.cache.CacheBuilder;
60import com.google.common.cache.RemovalCause;
61import com.google.common.cache.RemovalNotification;
62
63/**
64 * Support for FlowObjectives in the EA1000.
65 *
66 * Used with the CarrierEthernet App
67 *
68 */
69public class EA1000Pipeliner extends AbstractHandlerBehaviour implements Pipeliner {
70
71 protected final Logger log = getLogger(getClass());
72 protected ServiceDirectory serviceDirectory;
73 protected FlowRuleService flowRuleService;
74 protected DeviceId deviceId;
75 protected Cache<Integer, NextObjective> pendingNext;
76 protected Integer evcIdBase = 1;
77
78 @Override
79 public void init(DeviceId deviceId, PipelinerContext context) {
80 this.serviceDirectory = context.directory();
81 this.deviceId = deviceId;
82
83 flowRuleService = serviceDirectory.get(FlowRuleService.class);
84
85 pendingNext = CacheBuilder.newBuilder()
86 .expireAfterWrite(20, TimeUnit.SECONDS)
87 .removalListener((RemovalNotification<Integer, NextObjective> notification) -> {
88 if (notification.getCause() == RemovalCause.EXPIRED) {
89 notification.getValue().context()
90 .ifPresent(c -> c.onError(notification.getValue(),
91 ObjectiveError.FLOWINSTALLATIONFAILED));
92 }
93 }).build();
94
95 log.debug("Loaded handler behaviour EA1000Pipeliner for " + handler().data().deviceId().uri());
96 }
97
98 @Override
99 public void filter(FilteringObjective filterObjective) {
100 TrafficTreatment.Builder actions;
101 boolean oppositePort = false;
102 int evcId = -1;
103 switch (filterObjective.type()) {
104 case PERMIT:
105 if (filterObjective.meta() == null) {
106 actions = DefaultTrafficTreatment.builder().add(Instructions.popVlan());
107 } else {
108 oppositePort = true; //Experimental - push happens on the opposite port
109 actions = DefaultTrafficTreatment.builder(filterObjective.meta());
110 if (filterObjective.meta().metered() != null) {
111 actions.meter(filterObjective.meta().metered().meterId());
112 }
113 actions.transition(0);
114 boolean isPush = false;
115 int vid = 0;
116 for (Instruction inst:filterObjective.meta().immediate()) {
117 if (inst.type() == Instruction.Type.L2MODIFICATION) {
118 L2ModificationInstruction l2mod = (L2ModificationInstruction) inst;
119 if (l2mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_PUSH) {
120 isPush = true;
121 } else if (l2mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_ID) {
122 vid = ((ModVlanIdInstruction) l2mod).vlanId().id();
123 }
124 }
125 }
126 if (isPush && vid > 0) {
127 evcId = vid;
128 }
129 }
130 break;
131 case DENY:
132 actions = (filterObjective.meta() == null) ?
133 DefaultTrafficTreatment.builder() :
134 DefaultTrafficTreatment.builder(filterObjective.meta());
135 actions.drop();
136 break;
137 default:
138 log.warn("Unknown filter type: {}", filterObjective.type());
139 actions = DefaultTrafficTreatment.builder().drop();
140 }
141
142 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
143
144 for (Criterion c:filterObjective.conditions()) {
145 if (c.type() == Type.VLAN_VID && evcId == -1) {
146 evcId = ((VlanIdCriterion) c).vlanId().id();
147 }
148 selector.add(c);
149 }
150
151 if (filterObjective.key() != null) {
152 if (oppositePort) {
153 //Experimental
154 Ea1000Port port = Ea1000Port.fromNum(((PortCriterion) filterObjective.key()).port().toLong());
155 selector.matchInPort(PortNumber.portNumber(port.opposite().portNum()));
156 } else {
157 selector.add(filterObjective.key());
158 }
159 }
160
161 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
162 .forDevice(deviceId)
163 .withSelector(selector.build())
164 .withTreatment(actions.build())
165 .fromApp(filterObjective.appId())
166 .forTable(evcId)
167 .withPriority(filterObjective.priority());
168
169 if (filterObjective.permanent()) {
170 ruleBuilder.makePermanent();
171 } else {
172 ruleBuilder.makeTemporary(filterObjective.timeout());
173 }
174
175 installObjective(ruleBuilder, filterObjective);
176
177 log.debug("filter() of EA1000Pipeliner called for "
178 + handler().data().deviceId().uri()
179 + ". Objective: " + filterObjective);
180 }
181
182 @Override
183 public void forward(ForwardingObjective forwardObjective) {
184 TrafficSelector selector = forwardObjective.selector();
185
186 if (forwardObjective.treatment() != null) {
187 List<Instruction> instructions = forwardObjective.treatment().immediate();
188 if (instructions != null && instructions.size() == 1
189 && instructions.get(0).type() == Instruction.Type.OUTPUT
190 && ((OutputInstruction) instructions.get(0)).port() == PortNumber.CONTROLLER) {
191 Set<Criterion> criteria = forwardObjective.selector().criteria();
192 log.info("EA1000 does not yet implement forwarding to CONTROLLER for flow objective for: "
193 + handler().data().deviceId().uri()
194 + ". "
195 + forwardObjective);
196 return;
197 } else {
198 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
199 .forDevice(deviceId)
200 .withSelector(selector)
201 .fromApp(forwardObjective.appId())
202 .withPriority(forwardObjective.priority())
203 .withTreatment(forwardObjective.treatment());
204
205 if (forwardObjective.permanent()) {
206 ruleBuilder.makePermanent();
207 } else {
208 ruleBuilder.makeTemporary(forwardObjective.timeout());
209 }
210 installObjective(ruleBuilder, forwardObjective);
211 }
212 } else {
213 NextObjective nextObjective = pendingNext.getIfPresent(forwardObjective.nextId());
214 if (nextObjective != null) {
215 pendingNext.invalidate(forwardObjective.nextId());
216 nextObjective.next().forEach(treat -> {
217 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
218 .forDevice(deviceId)
219 .withSelector(selector)
220 .fromApp(forwardObjective.appId())
221 .withPriority(forwardObjective.priority())
222 .withTreatment(treat);
223
224 if (forwardObjective.permanent()) {
225 ruleBuilder.makePermanent();
226 } else {
227 ruleBuilder.makeTemporary(forwardObjective.timeout());
228 }
229 installObjective(ruleBuilder, forwardObjective);
230 });
231 } else {
232 forwardObjective.context().ifPresent(c -> c.onError(forwardObjective,
233 ObjectiveError.GROUPMISSING));
234 }
235 }
236 log.debug("EA1000: Unhandled Forwarding Objective for: "
237 + handler().data().deviceId().uri()
238 + ". "
239 + forwardObjective);
240 }
241
242 @Override
243 public void next(NextObjective nextObjective) {
244 pendingNext.put(nextObjective.id(), nextObjective);
245 nextObjective.context().ifPresent(context -> context.onSuccess(nextObjective));
246
247 log.debug("next() of EA1000Pipeliner called for "
248 + handler().data().deviceId().uri()
249 + ". Objective: " + nextObjective);
250 }
251
252 @Override
253 public List<String> getNextMappings(NextGroup nextGroup) {
254 log.debug("getNextMappings() of EA1000Pipeliner called for "
255 + handler().data().deviceId().uri()
256 + ". Objective: " + nextGroup);
257 return new ArrayList<String>();
258 }
259
260 protected void installObjective(FlowRule.Builder ruleBuilder, Objective objective) {
261 FlowRuleOperations.Builder flowBuilder = FlowRuleOperations.builder();
262 switch (objective.op()) {
263
264 case ADD:
265 flowBuilder.add(ruleBuilder.build());
266 break;
267 case REMOVE:
268 flowBuilder.remove(ruleBuilder.build());
269 break;
270 default:
271 log.warn("Unknown operation {}", objective.op());
272 }
273
274 flowRuleService.apply(flowBuilder.build(new FlowRuleOperationsContext() {
275 @Override
276 public void onSuccess(FlowRuleOperations ops) {
277 objective.context().ifPresent(context -> context.onSuccess(objective));
278 }
279
280 @Override
281 public void onError(FlowRuleOperations ops) {
282 objective.context()
283 .ifPresent(context -> context.onError(objective, ObjectiveError.FLOWINSTALLATIONFAILED));
284 }
285 }));
286 }
287}