2 * Copyright (c) 2013, 2015 Ericsson. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.flow;
10 import static java.util.Objects.requireNonNull;
11 import static java.util.Objects.requireNonNullElse;
13 import com.google.common.collect.ImmutableList;
14 import com.google.common.collect.Ordering;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.Collection;
18 import java.util.Collections;
19 import java.util.List;
21 import java.util.Optional;
22 import org.eclipse.jdt.annotation.NonNull;
23 import org.opendaylight.openflowplugin.api.OFConstants;
24 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.ConvertorExecutor;
25 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.action.data.ActionConvertorData;
26 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.common.Convertor;
27 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.common.ConvertorProcessor;
28 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.common.OrderComparator;
29 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.data.VersionDatapathIdConvertorData;
30 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.flow.cases.ApplyActionsCase;
31 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.flow.cases.ClearActionsCase;
32 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.flow.cases.GoToTableCase;
33 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.flow.cases.MeterCase;
34 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.flow.cases.WriteActionsCase;
35 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.flow.cases.WriteMetadataCase;
36 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.match.MatchInjector;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.VlanCfi;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCaseBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanIdActionCase;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.push.vlan.action._case.PushVlanActionBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInput;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInputBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.UpdatedFlow;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.UpdatedFlowBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.Flow;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Instructions;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActions;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.VlanMatch;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.VlanMatchBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.vlan.match.fields.VlanIdBuilder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.action.rev150203.actions.grouping.Action;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.instruction.rev130731.instructions.grouping.Instruction;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.FlowModCommand;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.MatchTypeBase;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.PortNumber;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.TableId;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev150225.OxmMatchType;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev150225.match.entries.grouping.MatchEntry;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FlowModInputBuilder;
71 import org.opendaylight.yangtools.yang.binding.util.BindingMap;
72 import org.opendaylight.yangtools.yang.common.Uint16;
73 import org.opendaylight.yangtools.yang.common.Uint32;
74 import org.opendaylight.yangtools.yang.common.Uint64;
77 * Converts the SAL Flow to OF Flow. It checks if there is a set-vlan-id (1.0) action made on OF1.3.
78 * If yes its handled separately.
84 * VersionDatapathIdConvertorData data = new VersionDatapathIdConvertorData(version);
85 * data.setDatapathId(datapathId);
86 * Optional<List<FlowModInputBuilder>> ofFlow = convertorManager.convert(salFlow, data);
90 public class FlowConvertor extends Convertor<Flow, List<FlowModInputBuilder>, VersionDatapathIdConvertorData> {
92 * Default idle timeout.
94 public static final Uint16 DEFAULT_IDLE_TIMEOUT = Uint16.ZERO;
97 * Default hard timeout.
99 public static final Uint16 DEFAULT_HARD_TIMEOUT = Uint16.ZERO;
104 public static final Uint16 DEFAULT_PRIORITY = Uint16.valueOf(0x8000);
109 public static final boolean DEFAULT_OFPFF_FLOW_REM = false;
112 * flow flag: check overlap.
114 public static final boolean DEFAULT_OFPFF_CHECK_OVERLAP = false;
117 * flow flag: reset counts.
119 public static final boolean DEFAULT_OFPFF_RESET_COUNTS = false;
122 * flow flag: don't keep track of packet counts.
124 public static final boolean DEFAULT_OFPFF_NO_PKT_COUNTS = false;
127 * flow flag: don't keep track of byte counts.
129 public static final boolean DEFAULT_OFPFF_NO_BYT_COUNTS = false;
132 * flow flag: emergency [OFP-1.0].
134 public static final boolean DEFAULT_OFPFF_EMERGENCY = false;
139 public static final Class<? extends MatchTypeBase> DEFAULT_MATCH_TYPE = OxmMatchType.class;
142 * default match entries - empty.
144 public static final List<MatchEntry> DEFAULT_MATCH_ENTRIES = ImmutableList.of();
146 // Default values for when things are null
147 private static final TableId DEFAULT_TABLE_ID = new TableId(Uint32.ZERO);
148 private static final Uint32 DEFAULT_BUFFER_ID = OFConstants.OFP_NO_BUFFER;
149 private static final Uint32 OFPP_ANY = Uint32.MAX_VALUE;
150 private static final PortNumber DEFAULT_OUT_PORT = new PortNumber(OFPP_ANY);
151 private static final Uint32 OFPG_ANY = Uint32.MAX_VALUE;
152 private static final Uint32 DEFAULT_OUT_GROUP = OFPG_ANY;
153 private static final Uint16 PUSH_VLAN = Uint16.valueOf(0x8100);
154 private static final Integer PUSH_TAG = PUSH_VLAN.toJava();
155 private static final VlanCfi PUSH_CFI = new VlanCfi(1);
156 private static final Ordering<org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction
157 .list.Instruction> INSTRUCTION_ORDERING = Ordering.from(OrderComparator.build());
158 private static final VlanMatch VLAN_MATCH_FALSE;
159 private static final VlanMatch VLAN_MATCH_TRUE;
160 private static final ConvertorProcessor<org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026
161 .instruction.Instruction, Instruction, ActionConvertorData> PROCESSOR =
162 new ConvertorProcessor<org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026
163 .instruction.Instruction, Instruction, ActionConvertorData>()
164 .addCase(new ApplyActionsCase())
165 .addCase(new ClearActionsCase())
166 .addCase(new GoToTableCase())
167 .addCase(new MeterCase())
168 .addCase(new WriteActionsCase())
169 .addCase(new WriteMetadataCase());
171 private static final List<Class<?>> TYPES = Arrays.asList(Flow.class, AddFlowInput.class,
172 RemoveFlowInput.class, UpdatedFlow.class);
175 final VlanId zeroVlan = new VlanId(Uint16.ZERO);
176 VLAN_MATCH_FALSE = new VlanMatchBuilder()
177 .setVlanId(new VlanIdBuilder().setVlanIdPresent(false).setVlanId(zeroVlan).build())
179 VLAN_MATCH_TRUE = new VlanMatchBuilder()
180 .setVlanId(new VlanIdBuilder().setVlanIdPresent(true).setVlanId(zeroVlan).build())
184 private FlowModInputBuilder toFlowModInput(final Flow flow,
185 final VersionDatapathIdConvertorData versionConverterData) {
187 FlowModInputBuilder flowMod = new FlowModInputBuilder()
188 .setIdleTimeout(requireNonNullElse(flow.getIdleTimeout(), DEFAULT_IDLE_TIMEOUT))
189 .setHardTimeout(requireNonNullElse(flow.getHardTimeout(), DEFAULT_HARD_TIMEOUT))
190 .setPriority(requireNonNullElse(flow.getPriority(), DEFAULT_PRIORITY))
191 .setBufferId(requireNonNullElse(flow.getBufferId(), DEFAULT_BUFFER_ID))
192 .setOutGroup(requireNonNullElse(flow.getOutGroup(), DEFAULT_OUT_GROUP));
193 salToOFFlowCookie(flow, flowMod);
194 salToOFFlowCookieMask(flow, flowMod);
195 salToOFFlowTableId(flow, flowMod);
196 salToOFFlowCommand(flow, flowMod);
197 salToOFFlowOutPort(flow, flowMod);
199 // convert and inject flowFlags
200 final Optional<Object> conversion = getConvertorExecutor().convert(flow.getFlags(), versionConverterData);
201 FlowFlagsInjector.inject(conversion, flowMod, versionConverterData.getVersion());
203 // convert and inject match
204 final Optional<Object> conversionMatch = getConvertorExecutor().convert(flow.getMatch(), versionConverterData);
205 MatchInjector.inject(conversionMatch, flowMod, versionConverterData.getVersion());
207 if (flow.getInstructions() != null) {
209 .setInstruction(toInstructions(flow, versionConverterData.getVersion(),
210 versionConverterData.getDatapathId()))
211 .setAction(getActions(versionConverterData.getVersion(), versionConverterData.getDatapathId(), flow));
215 .setVersion(versionConverterData.getVersion());
218 private static void salToOFFlowOutPort(final Flow flow, final FlowModInputBuilder flowMod) {
219 final var outPort = flow.getOutPort();
220 flowMod.setOutPort(outPort != null ? new PortNumber(outPort.longValue()) : DEFAULT_OUT_PORT);
223 private static void salToOFFlowCommand(final Flow flow, final FlowModInputBuilder flowMod) {
224 if (flow instanceof AddFlowInput || flow instanceof UpdatedFlow) {
225 flowMod.setCommand(FlowModCommand.OFPFCADD);
226 } else if (flow instanceof RemoveFlowInput) {
227 flowMod.setCommand(Boolean.TRUE.equals(flow.getStrict())
228 ? FlowModCommand.OFPFCDELETESTRICT : FlowModCommand.OFPFCDELETE);
232 private static void salToOFFlowTableId(final Flow flow, final FlowModInputBuilder flowMod) {
233 final var tableId = flow.getTableId();
234 flowMod.setTableId(tableId != null ? new TableId(flow.getTableId().toUint32()) : DEFAULT_TABLE_ID);
237 private static void salToOFFlowCookieMask(final Flow flow, final FlowModInputBuilder flowMod) {
238 final var mask = flow.getCookieMask();
239 flowMod.setCookieMask(mask != null ? mask.getValue() : OFConstants.DEFAULT_COOKIE_MASK);
242 private static void salToOFFlowCookie(final Flow flow, final FlowModInputBuilder flowMod) {
243 final var omNomNom = flow.getCookie();
244 flowMod.setCookie(omNomNom != null ? omNomNom.getValue() : OFConstants.DEFAULT_COOKIE);
247 private List<Instruction> toInstructions(final Flow flow, final short version, final Uint64 datapathid) {
248 final List<Instruction> instructionsList = new ArrayList<>();
249 final ActionConvertorData data = new ActionConvertorData(version);
250 data.setDatapathId(datapathid);
251 data.setIpProtocol(FlowConvertorUtil.getIpProtocolFromFlow(flow));
253 final ConvertorExecutor convertor = getConvertorExecutor();
254 for (var instruction : flow.getInstructions().nonnullInstruction().values()) {
255 Optional<Instruction> result = PROCESSOR.process(instruction.getInstruction(), data, convertor);
256 result.ifPresent(instructionsList::add);
259 return instructionsList;
262 private List<Action> getActions(final short version, final Uint64 datapathid, final Flow flow) {
263 Instructions instructions = flow.getInstructions();
264 List<org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction>
265 sortedInstructions = INSTRUCTION_ORDERING.sortedCopy(instructions.nonnullInstruction().values());
267 for (var instruction : sortedInstructions) {
268 final var curInstruction = instruction.getInstruction();
269 if (curInstruction instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026
270 .instruction.instruction.ApplyActionsCase) {
271 final var applyActions = ((org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026
272 .instruction.instruction.ApplyActionsCase) curInstruction).getApplyActions();
274 final ActionConvertorData data = new ActionConvertorData(version);
275 data.setDatapathId(datapathid);
276 data.setIpProtocol(FlowConvertorUtil.getIpProtocolFromFlow(flow));
277 Optional<List<Action>> result = getConvertorExecutor().convert(applyActions.getAction(), data);
278 return result.orElse(List.of());
285 // check if set vlanid action is present in the flow
286 private static boolean isSetVlanIdActionCasePresent(final Flow flow) {
287 // we are trying to find if there is a set-vlan-id action (OF1.0) action present in the flow.
288 // If yes,then we would need to two flows
289 final var insns = flow.getInstructions();
291 for (var instruction : insns.nonnullInstruction().values()) {
292 final var curInstruction = instruction.getInstruction();
294 if (curInstruction instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026
295 .instruction.instruction.ApplyActionsCase) {
296 ApplyActions applyActions = ((org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types
297 .rev131026.instruction.instruction.ApplyActionsCase) curInstruction).getApplyActions();
298 for (var action : applyActions.nonnullAction().values()) {
299 if (action.getAction() instanceof SetVlanIdActionCase) {
311 * A) If user provided flow's match includes vlan match  and action has set_vlan_field
312 * Install following rules.
313 * Â Â 1) match on (OFPVID_PRESENT |value) without mask + action [set_field]
315 * B) if user provided flow's match doesn't include vlan match but action has set_vlan field
316 * Â Â 1) Match on (OFPVID_NONEÂ ) without mask + action [push vlan tag + set_field]
317 * Â Â 2) Match on (OFPVID_PRESENT) with mask (OFPVID_PRESENT ) + action [ set_field]
319 private List<FlowModInputBuilder> handleSetVlanIdForOF13(final Flow srcFlow,
320 final VersionDatapathIdConvertorData versionDatapathIdConverterData) {
321 List<FlowModInputBuilder> list = new ArrayList<>(2);
323 final Match srcMatch = requireNonNull(srcFlow.getMatch());
324 final VlanMatch srcVlanMatch = srcMatch.getVlanMatch();
325 if (srcVlanMatch != null) {
326 //create flow with setfield and match
327 // match on vlan tag or vlanid with no mask
328 Optional<? extends Flow> optional = injectMatchToFlow(srcFlow, new MatchBuilder(srcMatch)
329 .setVlanMatch(new VlanMatchBuilder(srcVlanMatch)
330 .setVlanId(new VlanIdBuilder()
331 .setVlanIdPresent(srcVlanMatch.getVlanId().getVlanIdPresent())
332 .setVlanId(srcVlanMatch.getVlanId().getVlanId())
336 if (optional.isPresent()) {
337 list.add(toFlowModInput(optional.get(), versionDatapathIdConverterData));
342 // match on no vlan tag with no mask
343 Optional<? extends Flow> optional1 = injectMatchAndAction(srcFlow,
344 new MatchBuilder(srcMatch).setVlanMatch(VLAN_MATCH_FALSE).build());
345 if (optional1.isPresent()) {
346 list.add(toFlowModInput(optional1.get(), versionDatapathIdConverterData));
350 // match on vlan tag with mask
351 Optional<? extends Flow> optional2 = injectMatchToFlow(srcFlow,
352 new MatchBuilder(srcMatch).setVlanMatch(VLAN_MATCH_TRUE).build());
353 if (optional2.isPresent()) {
354 list.add(toFlowModInput(optional2.get(), versionDatapathIdConverterData));
361 private static Optional<? extends Flow> injectMatchToFlow(final Flow sourceFlow, final Match match) {
362 if (sourceFlow instanceof AddFlowInput) {
363 return Optional.of(new AddFlowInputBuilder(sourceFlow).setMatch(match).build());
364 } else if (sourceFlow instanceof RemoveFlowInput) {
365 return Optional.of(new RemoveFlowInputBuilder(sourceFlow).setMatch(match).build());
366 } else if (sourceFlow instanceof UpdatedFlow) {
367 return Optional.of(new UpdatedFlowBuilder(sourceFlow).setMatch(match).build());
369 return Optional.empty();
373 private static Optional<? extends Flow> injectMatchAndAction(final Flow sourceFlow, final Match match) {
375 Instructions instructions = new InstructionsBuilder()
376 .setInstruction(injectPushActionToInstruction(sourceFlow))
379 if (sourceFlow instanceof AddFlowInput) {
380 return Optional.of(new AddFlowInputBuilder(sourceFlow)
381 .setMatch(match).setInstructions(instructions).build());
382 } else if (sourceFlow instanceof RemoveFlowInput) {
383 return Optional.of(new RemoveFlowInputBuilder(sourceFlow)
384 .setMatch(match).setInstructions(instructions).build());
385 } else if (sourceFlow instanceof UpdatedFlow) {
386 return Optional.of(new UpdatedFlowBuilder(sourceFlow)
387 .setMatch(match).setInstructions(instructions).build());
389 return Optional.empty();
393 private static @NonNull Map<InstructionKey,
394 org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction>
395 injectPushActionToInstruction(final Flow sourceFlow) {
396 final var srcInstructionList = sourceFlow.getInstructions().nonnullInstruction();
397 final var targetInstructionList = BindingMap.<
398 org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey,
399 org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction>
400 orderedBuilder(srcInstructionList.size());
401 final var targetActionList = BindingMap.<
402 org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey,
403 org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action>orderedBuilder();
404 final var instructionBuilder = new org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026
405 .instruction.list.InstructionBuilder();
407 for (var srcInstruction : srcInstructionList.values()) {
408 final var curSrcInstruction = srcInstruction.getInstruction();
410 if (curSrcInstruction instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026
411 .instruction.instruction.ApplyActionsCase) {
412 final var applyActionscase = (org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types
413 .rev131026.instruction.instruction.ApplyActionsCase) curSrcInstruction;
414 final var srcActionList = applyActionscase.getApplyActions().nonnullAction();
417 for (var actionItem : srcActionList.values()) {
418 // check if its a set-vlan-action. If yes, then add the injected-action
420 if (actionItem.getAction() instanceof SetVlanIdActionCase) {
421 final var setVlanIdActionCase = (SetVlanIdActionCase) actionItem.getAction();
423 targetActionList.add(new ActionBuilder()
424 .setAction(new PushVlanActionCaseBuilder()
425 .setPushVlanAction(new PushVlanActionBuilder()
427 .setVlanId(setVlanIdActionCase.getSetVlanIdAction().getVlanId())
428 .setEthernetType(PUSH_VLAN)
432 // FIXME: this looks like a mismatch
433 .withKey(actionItem.key())
434 .setOrder(actionItem.getOrder() + offset)
440 // we need to increment the order for all the actions added after injection
441 final Integer newOrder = actionItem.getOrder() + offset;
442 actionItem = new ActionBuilder(actionItem)
444 .withKey(new ActionKey(newOrder))
448 targetActionList.add(actionItem);
451 instructionBuilder.setInstruction(new ApplyActionsCaseBuilder()
452 .setApplyActions(new ApplyActionsBuilder().setAction(targetActionList.build()).build())
455 instructionBuilder.setInstruction(curSrcInstruction);
458 targetInstructionList.add(instructionBuilder
459 .withKey(srcInstruction.key())
460 .setOrder(srcInstruction.getOrder())
464 return targetInstructionList.build();
468 public Collection<Class<?>> getTypes() {
473 public List<FlowModInputBuilder> convert(final Flow source, final VersionDatapathIdConvertorData data) {
474 if (data.getVersion() >= OFConstants.OFP_VERSION_1_3 && isSetVlanIdActionCasePresent(source)) {
475 return handleSetVlanIdForOF13(source, data);
477 return Collections.singletonList(toFlowModInput(source, data));