7f0cbe2c7147925509fb548acf4cd5c0a7b77015
[openflowplugin.git] / openflowplugin / src / main / java / org / opendaylight / openflowplugin / openflow / md / core / sal / convertor / flow / FlowConvertor.java
1 /*
2  * Copyright (c) 2013, 2015 Ericsson. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.flow;
9
10 import static java.util.Objects.requireNonNull;
11 import static java.util.Objects.requireNonNullElse;
12
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;
20 import java.util.Map;
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;
75 import org.opendaylight.yangtools.yang.common.Uint8;
76
77 /**
78  * Converts the SAL Flow to OF Flow. It checks if there is a set-vlan-id (1.0) action made on OF1.3.
79  * If yes its handled separately.
80  *
81  * <p>
82  * Example usage:
83  * <pre>
84  * {@code
85  * VersionDatapathIdConvertorData data = new VersionDatapathIdConvertorData(version);
86  * data.setDatapathId(datapathId);
87  * Optional<List<FlowModInputBuilder>> ofFlow = convertorManager.convert(salFlow, data);
88  * }
89  * </pre>
90  */
91 public class FlowConvertor extends Convertor<Flow, List<FlowModInputBuilder>, VersionDatapathIdConvertorData> {
92     /**
93      * Default idle timeout.
94      */
95     public static final Uint16 DEFAULT_IDLE_TIMEOUT = Uint16.ZERO;
96
97     /**
98      * Default hard timeout.
99      */
100     public static final Uint16 DEFAULT_HARD_TIMEOUT = Uint16.ZERO;
101
102     /**
103      * Default priority.
104      */
105     public static final Uint16 DEFAULT_PRIORITY = Uint16.valueOf(0x8000);
106
107     /**
108      * flow flag: remove.
109      */
110     public static final boolean DEFAULT_OFPFF_FLOW_REM = false;
111
112     /**
113      * flow flag: check overlap.
114      */
115     public static final boolean DEFAULT_OFPFF_CHECK_OVERLAP = false;
116
117     /**
118      * flow flag: reset counts.
119      */
120     public static final boolean DEFAULT_OFPFF_RESET_COUNTS = false;
121
122     /**
123      * flow flag: don't keep track of packet counts.
124      */
125     public static final boolean DEFAULT_OFPFF_NO_PKT_COUNTS = false;
126
127     /**
128      * flow flag: don't keep track of byte counts.
129      */
130     public static final boolean DEFAULT_OFPFF_NO_BYT_COUNTS = false;
131
132     /**
133      * flow flag: emergency [OFP-1.0].
134      */
135     public static final boolean DEFAULT_OFPFF_EMERGENCY = false;
136
137     /**
138      * OxmMatch type.
139      */
140     public static final MatchTypeBase DEFAULT_MATCH_TYPE = OxmMatchType.VALUE;
141
142     /**
143      * default match entries - empty.
144      */
145     public static final List<MatchEntry> DEFAULT_MATCH_ENTRIES = ImmutableList.of();
146
147     // Default values for when things are null
148     private static final TableId DEFAULT_TABLE_ID = new TableId(Uint32.ZERO);
149     private static final Uint32 DEFAULT_BUFFER_ID = OFConstants.OFP_NO_BUFFER;
150     private static final Uint32 OFPP_ANY = Uint32.MAX_VALUE;
151     private static final PortNumber DEFAULT_OUT_PORT = new PortNumber(OFPP_ANY);
152     private static final Uint32 OFPG_ANY = Uint32.MAX_VALUE;
153     private static final Uint32 DEFAULT_OUT_GROUP = OFPG_ANY;
154     private static final Uint16 PUSH_VLAN = Uint16.valueOf(0x8100);
155     private static final Integer PUSH_TAG = PUSH_VLAN.toJava();
156     private static final VlanCfi PUSH_CFI = new VlanCfi(1);
157     private static final Ordering<org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction
158         .list.Instruction> INSTRUCTION_ORDERING = Ordering.from(OrderComparator.build());
159     private static final VlanMatch VLAN_MATCH_FALSE;
160     private static final VlanMatch VLAN_MATCH_TRUE;
161     private static final ConvertorProcessor<org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026
162         .instruction.Instruction, Instruction, ActionConvertorData> PROCESSOR =
163             new ConvertorProcessor<org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026
164                 .instruction.Instruction, Instruction, ActionConvertorData>()
165             .addCase(new ApplyActionsCase())
166             .addCase(new ClearActionsCase())
167             .addCase(new GoToTableCase())
168             .addCase(new MeterCase())
169             .addCase(new WriteActionsCase())
170             .addCase(new WriteMetadataCase());
171
172     private static final List<Class<?>> TYPES = Arrays.asList(Flow.class, AddFlowInput.class,
173             RemoveFlowInput.class, UpdatedFlow.class);
174
175     static {
176         final VlanId zeroVlan = new VlanId(Uint16.ZERO);
177         VLAN_MATCH_FALSE = new VlanMatchBuilder()
178             .setVlanId(new VlanIdBuilder().setVlanIdPresent(false).setVlanId(zeroVlan).build())
179             .build();
180         VLAN_MATCH_TRUE = new VlanMatchBuilder()
181             .setVlanId(new VlanIdBuilder().setVlanIdPresent(true).setVlanId(zeroVlan).build())
182             .build();
183     }
184
185     private FlowModInputBuilder toFlowModInput(final Flow flow,
186             final VersionDatapathIdConvertorData versionConverterData) {
187
188         FlowModInputBuilder flowMod = new FlowModInputBuilder()
189             .setIdleTimeout(requireNonNullElse(flow.getIdleTimeout(), DEFAULT_IDLE_TIMEOUT))
190             .setHardTimeout(requireNonNullElse(flow.getHardTimeout(), DEFAULT_HARD_TIMEOUT))
191             .setPriority(requireNonNullElse(flow.getPriority(), DEFAULT_PRIORITY))
192             .setBufferId(requireNonNullElse(flow.getBufferId(), DEFAULT_BUFFER_ID))
193             .setOutGroup(requireNonNullElse(flow.getOutGroup(), DEFAULT_OUT_GROUP));
194         salToOFFlowCookie(flow, flowMod);
195         salToOFFlowCookieMask(flow, flowMod);
196         salToOFFlowTableId(flow, flowMod);
197         salToOFFlowCommand(flow, flowMod);
198         salToOFFlowOutPort(flow, flowMod);
199
200         // convert and inject flowFlags
201         final Optional<Object> conversion = getConvertorExecutor().convert(flow.getFlags(), versionConverterData);
202         FlowFlagsInjector.inject(conversion, flowMod, versionConverterData.getVersion());
203
204         // convert and inject match
205         final Optional<Object> conversionMatch = getConvertorExecutor().convert(flow.getMatch(), versionConverterData);
206         MatchInjector.inject(conversionMatch, flowMod, versionConverterData.getVersion());
207
208         if (flow.getInstructions() != null) {
209             flowMod
210                 .setInstruction(toInstructions(flow, versionConverterData.getVersion(),
211                     versionConverterData.getDatapathId()))
212                 .setAction(getActions(versionConverterData.getVersion(), versionConverterData.getDatapathId(), flow));
213         }
214
215         return flowMod
216             .setVersion(versionConverterData.getVersion());
217     }
218
219     private static void salToOFFlowOutPort(final Flow flow, final FlowModInputBuilder flowMod) {
220         final var outPort = flow.getOutPort();
221         flowMod.setOutPort(outPort != null ? new PortNumber(outPort.toUint32()) : DEFAULT_OUT_PORT);
222     }
223
224     private static void salToOFFlowCommand(final Flow flow, final FlowModInputBuilder flowMod) {
225         if (flow instanceof AddFlowInput || flow instanceof UpdatedFlow) {
226             flowMod.setCommand(FlowModCommand.OFPFCADD);
227         } else if (flow instanceof RemoveFlowInput) {
228             flowMod.setCommand(Boolean.TRUE.equals(flow.getStrict())
229                 ? FlowModCommand.OFPFCDELETESTRICT : FlowModCommand.OFPFCDELETE);
230         }
231     }
232
233     private static void salToOFFlowTableId(final Flow flow, final FlowModInputBuilder flowMod) {
234         final var tableId = flow.getTableId();
235         flowMod.setTableId(tableId != null ? new TableId(flow.getTableId().toUint32()) : DEFAULT_TABLE_ID);
236     }
237
238     private static void salToOFFlowCookieMask(final Flow flow, final FlowModInputBuilder flowMod) {
239         final var mask = flow.getCookieMask();
240         flowMod.setCookieMask(mask != null ? mask.getValue() : OFConstants.DEFAULT_COOKIE_MASK);
241     }
242
243     private static void salToOFFlowCookie(final Flow flow, final FlowModInputBuilder flowMod) {
244         final var omNomNom = flow.getCookie();
245         flowMod.setCookie(omNomNom != null ? omNomNom.getValue() : OFConstants.DEFAULT_COOKIE);
246     }
247
248     private List<Instruction> toInstructions(final Flow flow, final Uint8 version, final Uint64 datapathid) {
249         final List<Instruction> instructionsList = new ArrayList<>();
250         final ActionConvertorData data = new ActionConvertorData(version);
251         data.setDatapathId(datapathid);
252         data.setIpProtocol(FlowConvertorUtil.getIpProtocolFromFlow(flow));
253
254         final ConvertorExecutor convertor = getConvertorExecutor();
255         for (var instruction : flow.getInstructions().nonnullInstruction().values()) {
256             Optional<Instruction> result = PROCESSOR.process(instruction.getInstruction(), data, convertor);
257             result.ifPresent(instructionsList::add);
258         }
259
260         return instructionsList;
261     }
262
263     private List<Action> getActions(final Uint8 version, final Uint64 datapathid, final Flow flow) {
264         Instructions instructions = flow.getInstructions();
265         List<org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction>
266             sortedInstructions = INSTRUCTION_ORDERING.sortedCopy(instructions.nonnullInstruction().values());
267
268         for (var instruction : sortedInstructions) {
269             final var curInstruction = instruction.getInstruction();
270             if (curInstruction instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026
271                     .instruction.instruction.ApplyActionsCase) {
272                 final var applyActions = ((org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026
273                     .instruction.instruction.ApplyActionsCase) curInstruction).getApplyActions();
274
275                 final ActionConvertorData data = new ActionConvertorData(version);
276                 data.setDatapathId(datapathid);
277                 data.setIpProtocol(FlowConvertorUtil.getIpProtocolFromFlow(flow));
278                 Optional<List<Action>> result = getConvertorExecutor().convert(applyActions.getAction(), data);
279                 return result.orElse(List.of());
280             }
281         }
282
283         return null;
284     }
285
286     // check if set vlanid action is present in the flow
287     private static boolean isSetVlanIdActionCasePresent(final Flow flow) {
288         // we are trying to find if there is a set-vlan-id action (OF1.0) action present in the flow.
289         // If yes,then we would need to two flows
290         final var insns = flow.getInstructions();
291         if (insns != null) {
292             for (var instruction : insns.nonnullInstruction().values()) {
293                 final var curInstruction = instruction.getInstruction();
294
295                 if (curInstruction instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026
296                         .instruction.instruction.ApplyActionsCase) {
297                     ApplyActions applyActions = ((org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types
298                         .rev131026.instruction.instruction.ApplyActionsCase) curInstruction).getApplyActions();
299                     for (var action : applyActions.nonnullAction().values()) {
300                         if (action.getAction() instanceof SetVlanIdActionCase) {
301                             return true;
302                         }
303                     }
304                 }
305             }
306         }
307         return false;
308     }
309
310
311     /**
312      * A) If user provided flow's match includes vlan match Â and action has set_vlan_field
313      * Install following rules.
314      * Â  Â 1) match on (OFPVID_PRESENT |value) without mask + action [set_field]
315      * <p/>
316      * B) if user provided flow's match doesn't include vlan match but action has set_vlan field
317      * Â  Â  1) Match on (OFPVID_NONE ) without mask + action [push vlan tag + set_field]
318      * Â  Â  2) Match on (OFPVID_PRESENT) with mask (OFPVID_PRESENT ) + action [ set_field]
319      */
320     private List<FlowModInputBuilder> handleSetVlanIdForOF13(final Flow srcFlow,
321             final VersionDatapathIdConvertorData versionDatapathIdConverterData) {
322         List<FlowModInputBuilder> list = new ArrayList<>(2);
323
324         final Match srcMatch = requireNonNull(srcFlow.getMatch());
325         final VlanMatch srcVlanMatch = srcMatch.getVlanMatch();
326         if (srcVlanMatch != null) {
327             //create flow with setfield and match
328             // match on vlan tag or vlanid with no mask
329             Optional<? extends Flow> optional = injectMatchToFlow(srcFlow, new MatchBuilder(srcMatch)
330                 .setVlanMatch(new VlanMatchBuilder(srcVlanMatch)
331                     .setVlanId(new VlanIdBuilder()
332                         .setVlanIdPresent(srcVlanMatch.getVlanId().getVlanIdPresent())
333                         .setVlanId(srcVlanMatch.getVlanId().getVlanId())
334                         .build())
335                     .build())
336                 .build());
337             if (optional.isPresent()) {
338                 list.add(toFlowModInput(optional.get(), versionDatapathIdConverterData));
339             }
340         } else {
341             // create 2 flows
342             //flow 1
343             // match on no vlan tag with no mask
344             Optional<? extends Flow> optional1 = injectMatchAndAction(srcFlow,
345                 new MatchBuilder(srcMatch).setVlanMatch(VLAN_MATCH_FALSE).build());
346             if (optional1.isPresent()) {
347                 list.add(toFlowModInput(optional1.get(), versionDatapathIdConverterData));
348             }
349
350             //flow2
351             // match on vlan tag with mask
352             Optional<? extends Flow> optional2 = injectMatchToFlow(srcFlow,
353                 new MatchBuilder(srcMatch).setVlanMatch(VLAN_MATCH_TRUE).build());
354             if (optional2.isPresent()) {
355                 list.add(toFlowModInput(optional2.get(), versionDatapathIdConverterData));
356             }
357         }
358         return list;
359     }
360
361
362     private static Optional<? extends Flow> injectMatchToFlow(final Flow sourceFlow, final Match match) {
363         if (sourceFlow instanceof AddFlowInput) {
364             return Optional.of(new AddFlowInputBuilder(sourceFlow).setMatch(match).build());
365         } else if (sourceFlow instanceof RemoveFlowInput) {
366             return Optional.of(new RemoveFlowInputBuilder(sourceFlow).setMatch(match).build());
367         } else if (sourceFlow instanceof UpdatedFlow) {
368             return Optional.of(new UpdatedFlowBuilder(sourceFlow).setMatch(match).build());
369         } else {
370             return Optional.empty();
371         }
372     }
373
374     private static Optional<? extends Flow> injectMatchAndAction(final Flow sourceFlow, final Match match) {
375
376         Instructions instructions = new InstructionsBuilder()
377                 .setInstruction(injectPushActionToInstruction(sourceFlow))
378                 .build();
379
380         if (sourceFlow instanceof AddFlowInput) {
381             return Optional.of(new AddFlowInputBuilder(sourceFlow)
382                     .setMatch(match).setInstructions(instructions).build());
383         } else if (sourceFlow instanceof RemoveFlowInput) {
384             return Optional.of(new RemoveFlowInputBuilder(sourceFlow)
385                     .setMatch(match).setInstructions(instructions).build());
386         } else if (sourceFlow instanceof UpdatedFlow) {
387             return Optional.of(new UpdatedFlowBuilder(sourceFlow)
388                     .setMatch(match).setInstructions(instructions).build());
389         } else {
390             return Optional.empty();
391         }
392     }
393
394     private static @NonNull Map<InstructionKey,
395             org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction>
396                 injectPushActionToInstruction(final Flow sourceFlow) {
397         final var srcInstructionList = sourceFlow.getInstructions().nonnullInstruction();
398         final var targetInstructionList = BindingMap.<
399             org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey,
400             org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction>
401                 orderedBuilder(srcInstructionList.size());
402         final var targetActionList = BindingMap.<
403             org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey,
404             org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action>orderedBuilder();
405         final var instructionBuilder = new org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026
406             .instruction.list.InstructionBuilder();
407
408         for (var srcInstruction : srcInstructionList.values()) {
409             final var curSrcInstruction = srcInstruction.getInstruction();
410
411             if (curSrcInstruction instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026
412                     .instruction.instruction.ApplyActionsCase) {
413                 final var applyActionscase = (org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types
414                             .rev131026.instruction.instruction.ApplyActionsCase) curSrcInstruction;
415                 final var srcActionList = applyActionscase.getApplyActions().nonnullAction();
416
417                 int offset = 0;
418                 for (var actionItem : srcActionList.values()) {
419                     // check if its a set-vlan-action. If yes, then add the injected-action
420
421                     if (actionItem.getAction() instanceof SetVlanIdActionCase) {
422                         final var setVlanIdActionCase = (SetVlanIdActionCase) actionItem.getAction();
423
424                         targetActionList.add(new ActionBuilder()
425                             .setAction(new PushVlanActionCaseBuilder()
426                                 .setPushVlanAction(new PushVlanActionBuilder()
427                                     .setCfi(PUSH_CFI)
428                                     .setVlanId(setVlanIdActionCase.getSetVlanIdAction().getVlanId())
429                                     .setEthernetType(PUSH_VLAN)
430                                     .setTag(PUSH_TAG)
431                                     .build())
432                                 .build())
433                             // FIXME: this looks like a mismatch
434                             .withKey(actionItem.key())
435                             .setOrder(actionItem.getOrder() + offset)
436                             .build());
437                         offset++;
438                     }
439
440                     if (offset > 0) {
441                         // we need to increment the order for all the actions added after injection
442                         final Integer newOrder = actionItem.getOrder() + offset;
443                         actionItem = new ActionBuilder(actionItem)
444                             .setOrder(newOrder)
445                             .withKey(new ActionKey(newOrder))
446                             .build();
447                     }
448
449                     targetActionList.add(actionItem);
450                 }
451
452                 instructionBuilder.setInstruction(new ApplyActionsCaseBuilder()
453                     .setApplyActions(new ApplyActionsBuilder().setAction(targetActionList.build()).build())
454                     .build());
455             } else {
456                 instructionBuilder.setInstruction(curSrcInstruction);
457             }
458
459             targetInstructionList.add(instructionBuilder
460                 .withKey(srcInstruction.key())
461                 .setOrder(srcInstruction.getOrder())
462                 .build());
463         }
464
465         return targetInstructionList.build();
466     }
467
468     @Override
469     public Collection<Class<?>> getTypes() {
470         return  TYPES;
471     }
472
473     @Override
474     public List<FlowModInputBuilder> convert(final Flow source, final VersionDatapathIdConvertorData data) {
475         if (OFConstants.OFP_VERSION_1_3.compareTo(data.getVersion()) <= 0 && isSetVlanIdActionCasePresent(source)) {
476             return handleSetVlanIdForOF13(source, data);
477         } else {
478             return Collections.singletonList(toFlowModInput(source, data));
479         }
480     }
481 }