blob: 3718e0185bde6e6fd3f634a7aed1486866c36265 [file] [log] [blame]
Jovana Vuleta1de61262017-06-14 11:10:29 +02001/*
2 * Copyright 2017-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.drivers.hp;
18
19import com.google.common.cache.Cache;
20import com.google.common.cache.CacheBuilder;
21import com.google.common.cache.RemovalCause;
22import com.google.common.cache.RemovalNotification;
23import com.google.common.collect.ImmutableList;
24import org.onlab.osgi.ServiceDirectory;
25import org.onlab.packet.Ethernet;
26import org.onlab.util.KryoNamespace;
27import org.onosproject.core.ApplicationId;
28import org.onosproject.core.CoreService;
29import org.onosproject.net.DeviceId;
30import org.onosproject.net.behaviour.NextGroup;
31import org.onosproject.net.behaviour.Pipeliner;
32import org.onosproject.net.behaviour.PipelinerContext;
33import org.onosproject.net.device.DeviceService;
34import org.onosproject.net.driver.AbstractHandlerBehaviour;
35import org.onosproject.net.flow.DefaultFlowRule;
36import org.onosproject.net.flow.DefaultTrafficSelector;
37import org.onosproject.net.flow.DefaultTrafficTreatment;
38import org.onosproject.net.flow.FlowRule;
39import org.onosproject.net.flow.FlowRuleOperations;
40import org.onosproject.net.flow.FlowRuleOperationsContext;
41import org.onosproject.net.flow.FlowRuleService;
42import org.onosproject.net.flow.TrafficSelector;
43import org.onosproject.net.flow.TrafficTreatment;
44import org.onosproject.net.flow.criteria.Criteria;
45import org.onosproject.net.flow.criteria.Criterion;
46import org.onosproject.net.flow.criteria.EthCriterion;
47import org.onosproject.net.flow.criteria.EthTypeCriterion;
48import org.onosproject.net.flow.criteria.IPCriterion;
49import org.onosproject.net.flow.criteria.PortCriterion;
50import org.onosproject.net.flow.criteria.VlanIdCriterion;
51import org.onosproject.net.flow.instructions.Instruction;
52import org.onosproject.net.flowobjective.FilteringObjective;
53import org.onosproject.net.flowobjective.FlowObjectiveStore;
54import org.onosproject.net.flowobjective.ForwardingObjective;
55import org.onosproject.net.flowobjective.NextObjective;
56import org.onosproject.net.flowobjective.Objective;
57import org.onosproject.net.flowobjective.ObjectiveError;
58import org.onosproject.net.group.DefaultGroupKey;
59import org.onosproject.net.group.GroupKey;
60import org.onosproject.net.group.GroupService;
61import org.onosproject.net.meter.MeterService;
62import org.slf4j.Logger;
63
64import java.util.List;
65import java.util.concurrent.TimeUnit;
66
67import static org.onosproject.net.flow.FlowRule.Builder;
68import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
69import static org.slf4j.LoggerFactory.getLogger;
70
71
72/**
73 * Abstraction of the HP pipeline handler.
74 * Possibly compliant with all HP OF switches but tested only with HP3800.
75 */
76public abstract class AbstractHPPipeline extends AbstractHandlerBehaviour implements Pipeliner {
77
78
79 protected static final String APPLICATION_ID = "org.onosproject.drivers.hp.HPPipeline";
80 public static final int CACHE_ENTRY_EXPIRATION_PERIOD = 20;
81 private final Logger log = getLogger(getClass());
82 protected FlowRuleService flowRuleService;
83 protected GroupService groupService;
84 protected MeterService meterService;
85 protected FlowObjectiveStore flowObjectiveStore;
86 protected DeviceId deviceId;
87 protected ApplicationId appId;
88 protected DeviceService deviceService;
89 protected KryoNamespace appKryo = new KryoNamespace.Builder()
90 .register(GroupKey.class)
91 .register(DefaultGroupKey.class)
92 .register(byte[].class)
93 .build("AbstractHPPipeline");
94 private ServiceDirectory serviceDirectory;
95 private CoreService coreService;
96 private Cache<Integer, NextObjective> pendingAddNext = CacheBuilder.newBuilder()
97 .expireAfterWrite(CACHE_ENTRY_EXPIRATION_PERIOD, TimeUnit.SECONDS)
98 .removalListener((RemovalNotification<Integer, NextObjective> notification) -> {
99 if (notification.getCause() == RemovalCause.EXPIRED) {
100 notification.getValue().context()
101 .ifPresent(c -> c.onError(notification.getValue(),
102 ObjectiveError.FLOWINSTALLATIONFAILED));
103 }
104 }).build();
105
106 /**
107 * Sets default table id.
108 * HP3800 switches have 3 tables, so one of them has to be default.
109 *
110 * @param ruleBuilder flow rule builder to be set table id
111 * @return flow rule builder with set table id for flow
112 */
113 protected abstract FlowRule.Builder setDefaultTableIdForFlowObjective(Builder ruleBuilder);
114
115 @Override
116 public void init(DeviceId deviceId, PipelinerContext context) {
117 this.serviceDirectory = context.directory();
118 this.deviceId = deviceId;
119
120 coreService = serviceDirectory.get(CoreService.class);
121 flowRuleService = serviceDirectory.get(FlowRuleService.class);
122 groupService = serviceDirectory.get(GroupService.class);
123 meterService = serviceDirectory.get(MeterService.class);
124 deviceService = serviceDirectory.get(DeviceService.class);
125 flowObjectiveStore = context.store();
126
127 appId = coreService.registerApplication(APPLICATION_ID);
128
129 initializePipeline();
130 }
131
132 /**
133 * Initializes pipeline.
134 */
135 protected abstract void initializePipeline();
136
137 protected void pass(Objective obj) {
138 obj.context().ifPresent(context -> context.onSuccess(obj));
139 }
140
141 protected void fail(Objective obj, ObjectiveError error) {
142 obj.context().ifPresent(context -> context.onError(obj, error));
143 }
144
145 @Override
146 public void forward(ForwardingObjective fwd) {
147
148 if (fwd.treatment() != null) {
149 // Deal with SPECIFIC and VERSATILE in the same manner.
150
151 TrafficTreatment.Builder noClearTreatment = DefaultTrafficTreatment.builder();
152 fwd.treatment().allInstructions().stream()
153 .filter(i -> i.type() != Instruction.Type.QUEUE).forEach(noClearTreatment::add);
154 if (fwd.treatment().metered() != null) {
155 noClearTreatment.meter(fwd.treatment().metered().meterId());
156 }
157
158 TrafficSelector.Builder noVlanSelector = DefaultTrafficSelector.builder();
159 fwd.selector().criteria().stream()
160 .filter(c -> c.type() != Criterion.Type.ETH_TYPE || (c.type() == Criterion.Type.ETH_TYPE
161 && ((EthTypeCriterion) c).ethType().toShort() != Ethernet.TYPE_VLAN))
162 .forEach(noVlanSelector::add);
163
164 // Then we create a new forwarding rule without the unsupported actions
165 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
166 .forDevice(deviceId)
167 .withSelector(noVlanSelector.build())
168 .withTreatment(noClearTreatment.build())
169 .withPriority(fwd.priority())
170 .withPriority(fwd.priority())
171 .fromApp(fwd.appId());
172
173 //TODO: check whether ForwardingObjective can specify table
174 setDefaultTableIdForFlowObjective(ruleBuilder);
175
176 if (fwd.permanent()) {
177 ruleBuilder.makePermanent();
178 } else {
179 ruleBuilder.makeTemporary(fwd.timeout());
180 }
181
182 installObjective(ruleBuilder, fwd);
183
184 } else {
185 NextObjective nextObjective;
186 NextGroup next;
187 TrafficTreatment treatment;
188 if (fwd.op() == ADD) {
189 // Give a try to the cache. Doing an operation
190 // on the store seems to be very expensive.
191 nextObjective = pendingAddNext.getIfPresent(fwd.nextId());
192 // If the next objective is not present
193 // We will try with the store
194 if (nextObjective == null) {
195 next = flowObjectiveStore.getNextGroup(fwd.nextId());
196 // We verify that next was in the store and then de-serialize
197 // the treatment in order to re-build the flow rule.
198 if (next == null) {
199 fwd.context().ifPresent(c -> c.onError(fwd, ObjectiveError.GROUPMISSING));
200 return;
201 }
202 treatment = appKryo.deserialize(next.data());
203 } else {
204 pendingAddNext.invalidate(fwd.nextId());
205 treatment = nextObjective.next().iterator().next();
206 }
207 } else {
208 // We get the NextGroup from the remove operation.
209 // Doing an operation on the store seems to be very expensive.
210 next = flowObjectiveStore.removeNextGroup(fwd.nextId());
211 if (next == null) {
212 fwd.context().ifPresent(c -> c.onError(fwd, ObjectiveError.GROUPMISSING));
213 return;
214 }
215 treatment = appKryo.deserialize(next.data());
216 }
217 // If the treatment is null we cannot re-build the original flow
218 if (treatment == null) {
219 fwd.context().ifPresent(c -> c.onError(fwd, ObjectiveError.GROUPMISSING));
220 return;
221 }
222 // Finally we build the flow rule and push to the flowrule subsystem.
223 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
224 .forDevice(deviceId)
225 .withSelector(fwd.selector())
226 .fromApp(fwd.appId())
227 .withPriority(fwd.priority())
228 .withTreatment(treatment);
229 if (fwd.permanent()) {
230 ruleBuilder.makePermanent();
231 } else {
232 ruleBuilder.makeTemporary(fwd.timeout());
233 }
234 installObjective(ruleBuilder, fwd);
235 }
236 }
237
238 /**
239 * Installs objective.
240 *
241 * @param ruleBuilder flow rule builder used to build rule from objective
242 * @param objective objective to be installed
243 */
244 protected void installObjective(FlowRule.Builder ruleBuilder, Objective objective) {
245 FlowRuleOperations.Builder flowBuilder = FlowRuleOperations.builder();
246
247 switch (objective.op()) {
248 case ADD:
249 log.trace("Requested installation of objective " + objective.toString());
250 FlowRule addRule = ruleBuilder.build();
251 log.trace("built rule is " + addRule.toString());
252 flowBuilder.add(addRule);
253 break;
254 case REMOVE:
255 log.trace("Requested installation of objective " + objective.toString());
256 FlowRule removeRule = ruleBuilder.build();
257 log.trace("built rule is " + removeRule.toString());
258 flowBuilder.remove(removeRule);
259 break;
260 default:
261 log.warn("Unknown operation {}", objective.op());
262 }
263
264 flowRuleService.apply(flowBuilder.build(new FlowRuleOperationsContext() {
265 @Override
266 public void onSuccess(FlowRuleOperations ops) {
267 objective.context().ifPresent(context -> context.onSuccess(objective));
268 log.trace("Installed objective " + objective.toString());
269 }
270
271 @Override
272 public void onError(FlowRuleOperations ops) {
273 objective.context()
274 .ifPresent(context -> context.onError(objective, ObjectiveError.FLOWINSTALLATIONFAILED));
275 log.trace("Objective installation failed" + objective.toString());
276 }
277 }));
278 }
279
280 @Override
281 public void next(NextObjective nextObjective) {
282 switch (nextObjective.op()) {
283 case ADD:
284 // We insert the value in the cache
285 pendingAddNext.put(nextObjective.id(), nextObjective);
286 // Then in the store, this will unblock the queued fwd obj
287 flowObjectiveStore.putNextGroup(
288 nextObjective.id(),
289 new SingleGroup(nextObjective.next().iterator().next())
290 );
291 break;
292 case REMOVE:
293 break;
294 default:
295 log.warn("Unsupported operation {}", nextObjective.op());
296 }
297 nextObjective.context().ifPresent(context -> context.onSuccess(nextObjective));
298 }
299
300 @Override
301 public List<String> getNextMappings(NextGroup nextGroup) {
302 //TODO: to be implemented
303 return ImmutableList.of();
304 }
305
306 @Override
307 public void filter(FilteringObjective filteringObjective) {
308 if (filteringObjective.type() == FilteringObjective.Type.PERMIT) {
309 processFilter(filteringObjective,
310 filteringObjective.op() == Objective.Operation.ADD,
311 filteringObjective.appId());
312 } else {
313 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
314 }
315 }
316
317 /**
318 * Filter processing and installation.
319 * Processes and installs filtering rules.
320 *
321 * @param filt
322 * @param install
323 * @param applicationId
324 */
325 private void processFilter(FilteringObjective filt, boolean install,
326 ApplicationId applicationId) {
327 // This driver only processes filtering criteria defined with switch
328 // ports as the key
329 PortCriterion port;
330 if (!filt.key().equals(Criteria.dummy()) &&
331 filt.key().type() == Criterion.Type.IN_PORT) {
332 port = (PortCriterion) filt.key();
333 } else {
334 log.warn("No key defined in filtering objective from app: {}. Not"
335 + "processing filtering objective", applicationId);
336 fail(filt, ObjectiveError.UNKNOWN);
337 return;
338 }
339 // convert filtering conditions for switch-intfs into flowrules
340 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
341 for (Criterion c : filt.conditions()) {
342 if (c.type() == Criterion.Type.ETH_DST) {
343 EthCriterion eth = (EthCriterion) c;
344 FlowRule.Builder rule = processEthFiler(filt, eth, port);
345 rule.forDevice(deviceId)
346 .fromApp(applicationId);
347 ops = install ? ops.add(rule.build()) : ops.remove(rule.build());
348
349 } else if (c.type() == Criterion.Type.VLAN_VID) {
350 VlanIdCriterion vlan = (VlanIdCriterion) c;
351 FlowRule.Builder rule = processVlanFiler(filt, vlan, port);
352 rule.forDevice(deviceId)
353 .fromApp(applicationId);
354 ops = install ? ops.add(rule.build()) : ops.remove(rule.build());
355
356 } else if (c.type() == Criterion.Type.IPV4_DST) {
357 IPCriterion ip = (IPCriterion) c;
358 FlowRule.Builder rule = processIpFilter(filt, ip, port);
359 rule.forDevice(deviceId)
360 .fromApp(applicationId);
361 ops = install ? ops.add(rule.build()) : ops.remove(rule.build());
362
363 } else {
364 log.warn("Driver does not currently process filtering condition"
365 + " of type: {}", c.type());
366 fail(filt, ObjectiveError.UNSUPPORTED);
367 }
368 }
369 // apply filtering flow rules
370 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
371 @Override
372 public void onSuccess(FlowRuleOperations ops) {
373 pass(filt);
374 log.trace("Applied filtering rules");
375 }
376
377 @Override
378 public void onError(FlowRuleOperations ops) {
379 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
380 log.info("Failed to apply filtering rules");
381 }
382 }));
383 }
384
385 protected abstract Builder processEthFiler(FilteringObjective filt,
386 EthCriterion eth, PortCriterion port);
387
388 protected abstract Builder processVlanFiler(FilteringObjective filt,
389 VlanIdCriterion vlan, PortCriterion port);
390
391 protected abstract Builder processIpFilter(FilteringObjective filt,
392 IPCriterion ip, PortCriterion port);
393
394 private class SingleGroup implements NextGroup {
395
396 private TrafficTreatment nextActions;
397
398 SingleGroup(TrafficTreatment next) {
399 this.nextActions = next;
400 }
401
402 @Override
403 public byte[] data() {
404 return appKryo.serialize(nextActions);
405 }
406
407 public TrafficTreatment treatment() {
408 return nextActions;
409 }
410
411 }
412
413
414}