Use ByteBuf.readRetainedSlice()
[openflowplugin.git] / extension / openflowplugin-extension-nicira / src / main / java / org / opendaylight / openflowplugin / extension / vendor / nicira / convertor / action / RegLoad2Convertor.java
1 /*
2  * Copyright (c) 2018 SUSE LINUX GmbH.  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
9 package org.opendaylight.openflowplugin.extension.vendor.nicira.convertor.action;
10
11 import com.google.common.base.Preconditions;
12 import com.google.common.math.LongMath;
13 import java.math.RoundingMode;
14 import java.util.Collections;
15 import org.eclipse.jdt.annotation.Nullable;
16 import org.opendaylight.openflowjava.nx.codec.match.NiciraMatchCodecs;
17 import org.opendaylight.openflowplugin.extension.api.ConvertorActionFromOFJava;
18 import org.opendaylight.openflowplugin.extension.api.ConvertorActionToOFJava;
19 import org.opendaylight.openflowplugin.extension.api.path.ActionPath;
20 import org.opendaylight.openflowplugin.extension.vendor.nicira.convertor.CodecPreconditionException;
21 import org.opendaylight.openflowplugin.extension.vendor.nicira.convertor.match.NshFlagsConvertor;
22 import org.opendaylight.openflowplugin.extension.vendor.nicira.convertor.match.NshTtlConvertor;
23 import org.opendaylight.openflowplugin.extension.vendor.nicira.convertor.match.Nshc1Convertor;
24 import org.opendaylight.openflowplugin.extension.vendor.nicira.convertor.match.Nshc2Convertor;
25 import org.opendaylight.openflowplugin.extension.vendor.nicira.convertor.match.Nshc3Convertor;
26 import org.opendaylight.openflowplugin.extension.vendor.nicira.convertor.match.Nshc4Convertor;
27 import org.opendaylight.openflowplugin.extension.vendor.nicira.convertor.match.NsiConvertor;
28 import org.opendaylight.openflowplugin.extension.vendor.nicira.convertor.match.NspConvertor;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev150225.experimenter.id.match.entry.ExperimenterIdCase;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.action.rev150203.actions.grouping.Action;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev150225.MatchField;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev150225.match.entries.grouping.MatchEntry;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.ofj.aug.nx.action.ActionRegLoad2;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.ofj.aug.nx.action.ActionRegLoad2Builder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.ofj.nx.action.reg.load2.grouping.NxActionRegLoad2;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.ofj.nx.action.reg.load2.grouping.NxActionRegLoad2Builder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxNshFlags;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxNshTtl;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxNshc1;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxNshc2;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxNshc3;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxNshc4;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxNsi;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxNsp;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.OfjAugNxExpMatch;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.ofj.aug.nx.exp.match.NxExpMatchEntryValue;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.ofj.aug.nx.exp.match.nx.exp.match.entry.value.NshFlagsCaseValue;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.ofj.aug.nx.exp.match.nx.exp.match.entry.value.NshTtlCaseValue;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.ofj.aug.nx.exp.match.nx.exp.match.entry.value.NshcCaseValue;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.ofj.aug.nx.exp.match.nx.exp.match.entry.value.NsiCaseValue;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.ofj.aug.nx.exp.match.nx.exp.match.entry.value.NspCaseValue;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.ofj.nxm.nx.match.nsh.flags.grouping.NshFlagsValues;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.ofj.nxm.nx.match.nsh.ttl.grouping.NshTtlValues;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.ofj.nxm.nx.match.nsi.grouping.NsiValues;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.ofj.nxm.nx.match.nsp.grouping.NspValues;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.NxActionRegLoadGrouping;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.DstChoice;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.dst.choice.DstNxNshFlagsCase;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.dst.choice.DstNxNshFlagsCaseBuilder;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.dst.choice.DstNxNshTtlCase;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.dst.choice.DstNxNshTtlCaseBuilder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.dst.choice.DstNxNshc1Case;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.dst.choice.DstNxNshc1CaseBuilder;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.dst.choice.DstNxNshc2Case;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.dst.choice.DstNxNshc2CaseBuilder;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.dst.choice.DstNxNshc3Case;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.dst.choice.DstNxNshc3CaseBuilder;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.dst.choice.DstNxNshc4Case;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.dst.choice.DstNxNshc4CaseBuilder;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.dst.choice.DstNxNsiCase;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.dst.choice.DstNxNsiCaseBuilder;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.dst.choice.DstNxNspCase;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.dst.choice.DstNxNspCaseBuilder;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.reg.load.grouping.NxRegLoad;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.reg.load.grouping.NxRegLoadBuilder;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.reg.load.grouping.nx.reg.load.Dst;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.reg.load.grouping.nx.reg.load.DstBuilder;
78 import org.opendaylight.yangtools.yang.common.Empty;
79 import org.opendaylight.yangtools.yang.common.Uint16;
80 import org.opendaylight.yangtools.yang.common.Uint32;
81 import org.opendaylight.yangtools.yang.common.Uint64;
82 import org.opendaylight.yangtools.yang.common.Uint8;
83
84 /**
85  * Convert between RegLoad SAL action and RegLoad2 nicira action.
86  */
87 public class RegLoad2Convertor implements
88         ConvertorActionToOFJava<org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action,
89                 Action>,
90         ConvertorActionFromOFJava<Action, ActionPath> {
91
92     @Override
93     public org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action convert(
94             final Action input, final ActionPath path) {
95         NxActionRegLoad2 actionRegLoad2 = ((ActionRegLoad2) input.getActionChoice()).getNxActionRegLoad2();
96         MatchEntry matchEntry = actionRegLoad2.getMatchEntry().get(0);
97         NxRegLoad nxRegLoad = resolveRegLoad(matchEntry);
98         return RegLoadConvertor.resolveAction(nxRegLoad, path);
99     }
100
101     @Override
102     public Action convert(
103             final org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action actionCase) {
104         Preconditions.checkArgument(actionCase instanceof NxActionRegLoadGrouping);
105
106         NxActionRegLoadGrouping nxAction = (NxActionRegLoadGrouping) actionCase;
107         MatchEntry matchEntry = resolveMatchEntry(nxAction.getNxRegLoad());
108         NxActionRegLoad2 nxActionRegLoad2 = new NxActionRegLoad2Builder()
109                 .setMatchEntry(Collections.singletonList(matchEntry))
110                 .build();
111         ActionRegLoad2 actionRegLoad2 = new ActionRegLoad2Builder().setNxActionRegLoad2(nxActionRegLoad2).build();
112         return ActionUtil.createAction(actionRegLoad2);
113     }
114
115     private static MatchEntry resolveMatchEntry(final NxRegLoad nxRegLoad) {
116         Dst dst = nxRegLoad.getDst();
117         Uint64 value = nxRegLoad.getValue();
118         Uint16 start = dst.getStart();
119         Uint16 end = dst.getEnd();
120         Uint64[] valueMask = resolveValueMask(value, start, end);
121         value = valueMask[0];
122         Uint64 mask = valueMask[1];
123         DstChoice dstChoice = dst.getDstChoice();
124         return resolveMatchEntry(dstChoice, value, mask);
125     }
126
127     private static MatchEntry resolveMatchEntry(final DstChoice dstChoice, final Uint64 value, final Uint64 mask) {
128         try {
129             if (dstChoice instanceof DstNxNshFlagsCase) {
130                 return NshFlagsConvertor.buildMatchEntry(Uint8.valueOf(value), Uint8.valueOf(mask));
131             }
132             if (dstChoice instanceof DstNxNspCase) {
133                 return NspConvertor.buildMatchEntry(Uint32.valueOf(value), Uint32.valueOf(mask));
134             }
135             if (dstChoice instanceof DstNxNsiCase) {
136                 return NsiConvertor.buildMatchEntry(Uint8.valueOf(value), Uint8.valueOf(mask));
137             }
138             if (dstChoice instanceof DstNxNshc1Case) {
139                 return Nshc1Convertor.buildMatchEntry(Uint32.valueOf(value), Uint32.valueOf(mask));
140             }
141             if (dstChoice instanceof DstNxNshc2Case) {
142                 return Nshc2Convertor.buildMatchEntry(Uint32.valueOf(value), Uint32.valueOf(mask));
143             }
144             if (dstChoice instanceof DstNxNshc3Case) {
145                 return Nshc3Convertor.buildMatchEntry(Uint32.valueOf(value), Uint32.valueOf(mask));
146             }
147             if (dstChoice instanceof DstNxNshc4Case) {
148                 return Nshc4Convertor.buildMatchEntry(Uint32.valueOf(value), Uint32.valueOf(mask));
149             }
150             if (dstChoice instanceof DstNxNshTtlCase) {
151                 return NshTtlConvertor.buildMatchEntry(Uint8.valueOf(value), Uint8.valueOf(mask));
152             }
153         } catch (ArithmeticException e) {
154             throw new IllegalArgumentException("Value or bit range too big for destination choice", e);
155         }
156
157         throw new CodecPreconditionException("Missing implementation of a case in dst-choice? " + dstChoice.getClass());
158     }
159
160     private static NxRegLoad resolveRegLoad(final MatchEntry matchEntry) {
161         MatchField oxmMatchField = matchEntry.getOxmMatchField();
162         ExperimenterIdCase experimenterIdCase = (ExperimenterIdCase) matchEntry.getMatchEntryValue();
163         OfjAugNxExpMatch ofjAugNxExpMatch = experimenterIdCase.augmentation(OfjAugNxExpMatch.class);
164         NxExpMatchEntryValue nxExpMatchEntryValue = ofjAugNxExpMatch.getNxExpMatchEntryValue();
165         DstBuilder dstBuilder = new DstBuilder();
166         return resolveRegLoad(oxmMatchField, nxExpMatchEntryValue, dstBuilder);
167     }
168
169     private static NxRegLoad resolveRegLoad(
170             final MatchField oxmMatchField,
171             final NxExpMatchEntryValue value,
172             final DstBuilder dstBuilder) {
173
174         if (NxmNxNshFlags.VALUE.equals(oxmMatchField)) {
175             int valueLength = NiciraMatchCodecs.NSH_FLAGS_CODEC.getValueLength();
176             dstBuilder.setDstChoice(new DstNxNshFlagsCaseBuilder().setNxNshFlags(Empty.value()).build());
177             NshFlagsValues nshFlagsValues = ((NshFlagsCaseValue) value).getNshFlagsValues();
178             return resolveRegLoad(nshFlagsValues.getNshFlags(), nshFlagsValues.getMask(), valueLength, dstBuilder);
179         } else if (NxmNxNsp.VALUE.equals(oxmMatchField)) {
180             int valueLength = NiciraMatchCodecs.NSP_CODEC.getValueLength();
181             dstBuilder.setDstChoice(new DstNxNspCaseBuilder().setNxNspDst(Empty.value()).build());
182             NspValues nspValues = ((NspCaseValue) value).getNspValues();
183             return resolveRegLoad(nspValues.getNsp(), nspValues.getMask(), valueLength, dstBuilder);
184         } else if (NxmNxNsi.VALUE.equals(oxmMatchField)) {
185             int valueLength = NiciraMatchCodecs.NSI_CODEC.getValueLength();
186             dstBuilder.setDstChoice(new DstNxNsiCaseBuilder().setNxNsiDst(Empty.value()).build());
187             NsiValues nsiValues = ((NsiCaseValue) value).getNsiValues();
188             return resolveRegLoad(nsiValues.getNsi(), nsiValues.getMask(), valueLength, dstBuilder);
189         } else if (NxmNxNshc1.VALUE.equals(oxmMatchField)) {
190             int valueLength = NiciraMatchCodecs.NSC1_CODEC.getValueLength();
191             dstBuilder.setDstChoice(new DstNxNshc1CaseBuilder().setNxNshc1Dst(Empty.value()).build());
192             NshcCaseValue nshcCaseValue = (NshcCaseValue) value;
193             return resolveRegLoad(nshcCaseValue.getNshc(), nshcCaseValue.getMask(), valueLength, dstBuilder);
194         } else if (NxmNxNshc2.VALUE.equals(oxmMatchField)) {
195             int valueLength = NiciraMatchCodecs.NSC2_CODEC.getValueLength();
196             dstBuilder.setDstChoice(new DstNxNshc2CaseBuilder().setNxNshc2Dst(Empty.value()).build());
197             NshcCaseValue nshcCaseValue = (NshcCaseValue) value;
198             return resolveRegLoad(nshcCaseValue.getNshc(), nshcCaseValue.getMask(), valueLength, dstBuilder);
199         } else if (NxmNxNshc3.VALUE.equals(oxmMatchField)) {
200             int valueLength = NiciraMatchCodecs.NSC3_CODEC.getValueLength();
201             dstBuilder.setDstChoice(new DstNxNshc3CaseBuilder().setNxNshc3Dst(Empty.value()).build());
202             NshcCaseValue nshcCaseValue = (NshcCaseValue) value;
203             return resolveRegLoad(nshcCaseValue.getNshc(), nshcCaseValue.getMask(), valueLength, dstBuilder);
204         } else if (NxmNxNshc4.VALUE.equals(oxmMatchField)) {
205             int valueLength = NiciraMatchCodecs.NSC4_CODEC.getValueLength();
206             dstBuilder.setDstChoice(new DstNxNshc4CaseBuilder().setNxNshc4Dst(Empty.value()).build());
207             NshcCaseValue nshcCaseValue = (NshcCaseValue) value;
208             return resolveRegLoad(nshcCaseValue.getNshc(), nshcCaseValue.getMask(), valueLength, dstBuilder);
209         } else if (NxmNxNshTtl.VALUE.equals(oxmMatchField)) {
210             int valueLength = NiciraMatchCodecs.NSH_TTL_CODEC.getValueLength();
211             dstBuilder.setDstChoice(new DstNxNshTtlCaseBuilder().setNxNshTtl(Empty.value()).build());
212             NshTtlValues nshTtlValues = ((NshTtlCaseValue) value).getNshTtlValues();
213             return resolveRegLoad(nshTtlValues.getNshTtl(), nshTtlValues.getMask(), valueLength, dstBuilder);
214         }
215
216         throw new CodecPreconditionException("Missing codec for " + value.implementedInterface());
217     }
218
219     private static NxRegLoad resolveRegLoad(final Uint8 value, final @Nullable Uint8 mask, final int valueLength,
220             final DstBuilder dstBuilder) {
221         return resolveRegLoad(value.toUint64(), mask == null ? null : mask.toUint64(), valueLength, dstBuilder);
222     }
223
224     private static NxRegLoad resolveRegLoad(final Uint32 value, final @Nullable Uint32 mask, final int valueLength,
225             final DstBuilder dstBuilder) {
226         return resolveRegLoad(value.toUint64(), mask == null ? null : mask.toUint64(), valueLength, dstBuilder);
227     }
228
229     // Convert the value/mask pair of the openflowjava reg_load2 action to the
230     // value/bit range pair of the openfloplugin reg_load action.
231     private static NxRegLoad resolveRegLoad(Uint64 value, final @Nullable Uint64 mask, final int length,
232             final DstBuilder dstBuilder) {
233         final int start;
234         final int end;
235         if (mask == null) {
236             start = 0;
237             end = length * 8;
238         } else {
239             final long maskBits = mask.longValue();
240             start = lowestSetBit(maskBits);
241             end = start + lowestSetBit(~(maskBits >> start));
242
243             final long valueBits = value.longValue();
244             final long newValueBits = (valueBits & maskBits) >> start;
245             final int bitLength = bitLength(newValueBits);
246             if (bitLength > end - start) {
247                 // We cannot map a REG_LOAD2 to a single REG_LOAD if the mask
248                 // has multiple 1-bit segments (i.e. 0xFF00FF)
249                 throw new IllegalArgumentException("Value does not fit in the first 1-bit segment of the mask");
250             }
251
252             value = Uint64.fromLongBits(newValueBits);
253         }
254
255         return new NxRegLoadBuilder()
256             .setDst(dstBuilder.setStart(Uint16.valueOf(start)).setEnd(Uint16.valueOf(end - 1)).build())
257             .setValue(value)
258             .build();
259     }
260
261     private static int lowestSetBit(final long value) {
262         return LongMath.log2(Long.lowestOneBit(value), RoundingMode.UNNECESSARY);
263     }
264
265     private static int bitLength(final long value) {
266         return 64 - Long.numberOfLeadingZeros(value);
267     }
268
269     // Convert value/bit range pair of the openfloplugin reg_load action to the
270     // value/mask pair of the openflowjava reg_load2 action.
271     private static Uint64[] resolveValueMask(final Uint64 value, final Uint16 start, final Uint16 end) {
272         final int startInt = start.toJava();
273         final int bits = end.toJava() - startInt + 1;
274         final long valueBits = value.longValue();
275         if (bitLength(valueBits) > bits) {
276             throw new IllegalArgumentException("Value does not fit the bit range");
277         }
278
279         final long maskBits = (1L << bits) - 1 << startInt;
280         return new Uint64[] {Uint64.fromLongBits(valueBits << startInt), Uint64.fromLongBits(maskBits)};
281     }
282 }