Minor model refactor
[openflowjava.git] / openflow-protocol-impl / src / main / java / org / opendaylight / openflowjava / protocol / impl / util / MatchSerializer.java
1 /*
2  * Copyright (c) 2013 Pantheon Technologies s.r.o. 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
9 package org.opendaylight.openflowjava.protocol.impl.util;
10
11 import io.netty.buffer.ByteBuf;
12
13 import java.util.Arrays;
14 import java.util.HashMap;
15 import java.util.List;
16 import java.util.Map;
17
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev131002.BosMatchEntry;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev131002.DscpMatchEntry;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev131002.EcnMatchEntry;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev131002.EthTypeMatchEntry;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev131002.Icmpv4CodeMatchEntry;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev131002.Icmpv4TypeMatchEntry;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev131002.Icmpv6CodeMatchEntry;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev131002.Icmpv6TypeMatchEntry;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev131002.Ipv4AddressMatchEntry;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev131002.Ipv6AddressMatchEntry;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev131002.Ipv6FlabelMatchEntry;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev131002.IsidMatchEntry;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev131002.MacAddressMatchEntry;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev131002.MaskMatchEntry;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev131002.MetadataMatchEntry;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev131002.MplsLabelMatchEntry;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev131002.OpCodeMatchEntry;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev131002.PortMatchEntry;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev131002.PortNumberMatchEntry;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev131002.ProtocolNumberMatchEntry;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev131002.PseudoFieldMatchEntry;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev131002.TcMatchEntry;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev131002.VlanPcpMatchEntry;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev131002.VlanVidMatchEntry;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.Ipv6ExthdrFlags;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.StandardMatchType;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.ArpOp;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.ArpSha;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.ArpSpa;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.ArpTha;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.ArpTpa;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.Clazz;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.EthDst;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.EthSrc;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.EthType;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.ExperimenterClass;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.Icmpv4Code;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.Icmpv4Type;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.Icmpv6Code;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.Icmpv6Type;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.InPhyPort;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.InPort;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.IpDscp;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.IpEcn;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.IpProto;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.Ipv4Dst;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.Ipv4Src;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.Ipv6Dst;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.Ipv6Exthdr;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.Ipv6Flabel;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.Ipv6NdSll;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.Ipv6NdTarget;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.Ipv6NdTll;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.Ipv6Src;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.MatchField;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.Metadata;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.MplsBos;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.MplsLabel;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.MplsTc;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.Nxm0Class;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.Nxm1Class;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.OpenflowBasicClass;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.OxmMatchType;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.PbbIsid;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.SctpDst;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.SctpSrc;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.TcpDst;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.TcpSrc;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.TunnelId;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.UdpDst;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.UdpSrc;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.VlanPcp;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.VlanVid;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.match.grouping.Match;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.oxm.fields.grouping.MatchEntries;
93 import org.slf4j.Logger;
94 import org.slf4j.LoggerFactory;
95
96 /**
97  * Serializes ofp_match (OpenFlow v1.3) and its oxm_fields structures
98  * @author michal.polkorab
99  * @author timotej.kubas
100  */
101 public abstract class MatchSerializer {
102     private static final Logger LOGGER = LoggerFactory.getLogger(MatchSerializer.class);
103     private static final byte STANDARD_MATCH_TYPE_CODE = 0;
104     private static final byte OXM_MATCH_TYPE_CODE = 1;
105     private static final int NXM0_CLASS_CODE = 0x0000;
106     private static final int NXM1_CLASS_CODE = 0x0001;
107     private static final int OPENFLOW_BASIC_CLASS_CODE = 0x8000;
108     private static final int EXPERIMENTER_CLASS_CODE = 0xFFFF;
109     private static final byte MATCH_TYPE_AND_LENGTH_SIZE = 4;
110     private static final byte MATCH_ENTRY_HEADER_LENGTH = 4;
111
112     /**
113      * Encodes match (OpenFlow v1.3)
114      * @param match ofp_match object
115      * @param out output ByteBuf
116      */
117     public static void encodeMatch(Match match, ByteBuf out) {
118         if (match == null) {
119             LOGGER.debug("Match is null");
120             return;
121         }
122         encodeType(match, out);
123         // Length of ofp_match (excluding padding)
124         int length = computeMatchLengthInternal(match);
125         out.writeShort(length);
126         encodeMatchEntries(match.getMatchEntries(), out);
127         int paddingRemainder = length % EncodeConstants.PADDING;
128         if (paddingRemainder != 0) {
129             ByteBufUtils.padBuffer(EncodeConstants.PADDING - paddingRemainder, out);
130         }
131     }
132
133     private static void encodeType(Match match, ByteBuf out) {
134         if (match.getType().isAssignableFrom(StandardMatchType.class)) {
135             out.writeShort(STANDARD_MATCH_TYPE_CODE);
136         } else if (match.getType().isAssignableFrom(OxmMatchType.class)) {
137             out.writeShort(OXM_MATCH_TYPE_CODE);
138         }
139     }
140
141     /**
142      * Encodes MatchEntries
143      * @param matchEntries list of match entries (oxm_fields)
144      * @param out output ByteBuf
145      */
146     public static void encodeMatchEntries(List<MatchEntries> matchEntries, ByteBuf out) {
147         if (matchEntries == null) {
148             LOGGER.debug("Match entries are null");
149             return;
150         }
151         for (MatchEntries entry : matchEntries) {
152             encodeClass(entry.getOxmClass(), out);
153             encodeRest(entry, out);
154         }
155     }
156
157     /**
158      * Encodes OXM ids (for Multipart - TableFeatures messages)
159      * @param matchEntries list of match entries (oxm_fields)
160      * @param out output ByteBuf
161      */
162     public static void encodeMatchIds(List<MatchEntries> matchEntries, ByteBuf out) {
163         if (matchEntries == null) {
164             LOGGER.debug("Match entries are null");
165             return;
166         }
167         for (MatchEntries entry : matchEntries) {
168             encodeClass(entry.getOxmClass(), out);
169             MatchIdsSerializer.encodeIdsRest(entry, out);
170         }
171     }
172
173     private static void encodeClass(Class<? extends Clazz> clazz, ByteBuf out) {
174         if (clazz.isAssignableFrom(Nxm0Class.class)) {
175             out.writeShort(NXM0_CLASS_CODE);
176         } else if (clazz.isAssignableFrom(Nxm1Class.class)) {
177             out.writeShort(NXM1_CLASS_CODE);
178         } else if (clazz.isAssignableFrom(OpenflowBasicClass.class)) {
179             out.writeShort(OPENFLOW_BASIC_CLASS_CODE);
180         } else if (clazz.isAssignableFrom(ExperimenterClass.class)) {
181             out.writeShort(EXPERIMENTER_CLASS_CODE);
182         }
183     }
184
185     private static void encodeRest(MatchEntries entry, ByteBuf out) {
186         int fieldValue = 0;
187         Class<? extends MatchField> field = entry.getOxmMatchField();
188         if (field.isAssignableFrom(InPort.class)) {
189             fieldValue = 0;
190             writeOxmFieldAndLength(out, fieldValue, false, EncodeConstants.SIZE_OF_INT_IN_BYTES);
191             out.writeInt(entry.getAugmentation(PortNumberMatchEntry.class).getPortNumber().getValue().intValue());
192         } else if (field.isAssignableFrom(InPhyPort.class)) {
193             fieldValue = 1;
194             writeOxmFieldAndLength(out, fieldValue, false, EncodeConstants.SIZE_OF_INT_IN_BYTES);
195             out.writeInt(entry.getAugmentation(PortNumberMatchEntry.class).getPortNumber().getValue().intValue());
196         } else if (field.isAssignableFrom(Metadata.class)) {
197             fieldValue = 2;
198             writeMetadataRelatedEntry(entry, out, fieldValue);
199         } else if (field.isAssignableFrom(EthDst.class)) {
200             fieldValue = 3;
201             writeMacAddressRelatedEntry(entry, out, fieldValue);
202         } else if (field.isAssignableFrom(EthSrc.class)) {
203             fieldValue = 4;
204             writeMacAddressRelatedEntry(entry, out, fieldValue);
205         } else if (field.isAssignableFrom(EthType.class)) {
206             fieldValue = 5;
207             writeOxmFieldAndLength(out, fieldValue, false, EncodeConstants.SIZE_OF_SHORT_IN_BYTES);
208             out.writeShort(entry.getAugmentation(EthTypeMatchEntry.class).getEthType().getValue().shortValue());
209         } else if (field.isAssignableFrom(VlanVid.class)) {
210             fieldValue = 6;
211             VlanVidMatchEntry vlanVid = entry.getAugmentation(VlanVidMatchEntry.class);
212             int vlanVidValue = vlanVid.getVlanVid();
213             if (vlanVid.isCfiBit()) {
214                 short cfi = 1 << 12; // 13-th bit
215                 vlanVidValue = vlanVidValue | cfi;
216             }
217
218             writeOxmFieldAndLength(out, fieldValue, entry.isHasMask(),
219                     EncodeConstants.SIZE_OF_SHORT_IN_BYTES);
220             out.writeShort(vlanVidValue);
221             writeMask(entry, out, EncodeConstants.SIZE_OF_SHORT_IN_BYTES);
222         } else if (field.isAssignableFrom(VlanPcp.class)) {
223             fieldValue = 7;
224             writeOxmFieldAndLength(out, fieldValue, false, EncodeConstants.SIZE_OF_BYTE_IN_BYTES);
225             out.writeByte(entry.getAugmentation(VlanPcpMatchEntry.class).getVlanPcp().byteValue());
226         } else if (field.isAssignableFrom(IpDscp.class)) {
227             fieldValue = 8;
228             writeOxmFieldAndLength(out, fieldValue, false, EncodeConstants.SIZE_OF_BYTE_IN_BYTES);
229             out.writeByte(entry.getAugmentation(DscpMatchEntry.class).getDscp().getValue());
230         } else if (field.isAssignableFrom(IpEcn.class)) {
231             fieldValue = 9;
232             writeOxmFieldAndLength(out, fieldValue, false, EncodeConstants.SIZE_OF_BYTE_IN_BYTES);
233             out.writeByte(entry.getAugmentation(EcnMatchEntry.class).getEcn());
234         } else if (field.isAssignableFrom(IpProto.class)) {
235             fieldValue = 10;
236             writeOxmFieldAndLength(out, fieldValue, false, EncodeConstants.SIZE_OF_BYTE_IN_BYTES);
237             out.writeByte(entry.getAugmentation(ProtocolNumberMatchEntry.class).getProtocolNumber());
238         } else if (field.isAssignableFrom(Ipv4Src.class)) {
239             fieldValue = 11;
240             writeIpv4AddressRelatedEntry(entry, out, fieldValue);
241         } else if (field.isAssignableFrom(Ipv4Dst.class)) {
242             fieldValue = 12;
243             writeIpv4AddressRelatedEntry(entry, out, fieldValue);
244         } else if (field.isAssignableFrom(TcpSrc.class)) {
245             fieldValue = 13;
246             writeOxmFieldAndLength(out, fieldValue, false, EncodeConstants.SIZE_OF_SHORT_IN_BYTES);
247             out.writeShort(entry.getAugmentation(PortMatchEntry.class).getPort().getValue().intValue());
248         } else if (field.isAssignableFrom(TcpDst.class)) {
249             fieldValue = 14;
250             writeOxmFieldAndLength(out, fieldValue, false, EncodeConstants.SIZE_OF_SHORT_IN_BYTES);
251             out.writeShort(entry.getAugmentation(PortMatchEntry.class).getPort().getValue().intValue());
252         } else if (field.isAssignableFrom(UdpSrc.class)) {
253             fieldValue = 15;
254             writeOxmFieldAndLength(out, fieldValue, false, EncodeConstants.SIZE_OF_SHORT_IN_BYTES);
255             out.writeShort(entry.getAugmentation(PortMatchEntry.class).getPort().getValue().intValue());
256         } else if (field.isAssignableFrom(UdpDst.class)) {
257             fieldValue = 16;
258             writeOxmFieldAndLength(out, fieldValue, false, EncodeConstants.SIZE_OF_SHORT_IN_BYTES);
259             out.writeShort(entry.getAugmentation(PortMatchEntry.class).getPort().getValue().intValue());
260         } else if (field.isAssignableFrom(SctpSrc.class)) {
261             fieldValue = 17;
262             writeOxmFieldAndLength(out, fieldValue, false, EncodeConstants.SIZE_OF_SHORT_IN_BYTES);
263             out.writeShort(entry.getAugmentation(PortMatchEntry.class).getPort().getValue().intValue());
264         } else if (field.isAssignableFrom(SctpDst.class)) {
265             fieldValue = 18;
266             writeOxmFieldAndLength(out, fieldValue, false, EncodeConstants.SIZE_OF_SHORT_IN_BYTES);
267             out.writeShort(entry.getAugmentation(PortMatchEntry.class).getPort().getValue().intValue());
268         } else if (field.isAssignableFrom(Icmpv4Type.class)) {
269             fieldValue = 19;
270             writeOxmFieldAndLength(out, fieldValue, false, EncodeConstants.SIZE_OF_BYTE_IN_BYTES);
271             out.writeByte(entry.getAugmentation(Icmpv4TypeMatchEntry.class).getIcmpv4Type());
272         } else if (field.isAssignableFrom(Icmpv4Code.class)) {
273             fieldValue = 20;
274             writeOxmFieldAndLength(out, fieldValue, false, EncodeConstants.SIZE_OF_BYTE_IN_BYTES);
275             out.writeByte(entry.getAugmentation(Icmpv4CodeMatchEntry.class).getIcmpv4Code());
276         } else if (field.isAssignableFrom(ArpOp.class)) {
277             fieldValue = 21;
278             writeOxmFieldAndLength(out, fieldValue, false, EncodeConstants.SIZE_OF_SHORT_IN_BYTES);
279             out.writeShort(entry.getAugmentation(OpCodeMatchEntry.class).getOpCode());
280         } else if (field.isAssignableFrom(ArpSpa.class)) {
281             fieldValue = 22;
282             writeIpv4AddressRelatedEntry(entry, out, fieldValue);
283         } else if (field.isAssignableFrom(ArpTpa.class)) {
284             fieldValue = 23;
285             writeIpv4AddressRelatedEntry(entry, out, fieldValue);
286         } else if (field.isAssignableFrom(ArpSha.class)) {
287             fieldValue = 24;
288             writeMacAddressRelatedEntry(entry, out, fieldValue);
289         } else if (field.isAssignableFrom(ArpTha.class)) {
290             fieldValue = 25;
291             writeMacAddressRelatedEntry(entry, out, fieldValue);
292         } else if (field.isAssignableFrom(Ipv6Src.class)) {
293             fieldValue = 26;
294             writeIpv6AddressRelatedEntry(entry, out, fieldValue);
295         } else if (field.isAssignableFrom(Ipv6Dst.class)) {
296             fieldValue = 27;
297             writeIpv6AddressRelatedEntry(entry, out, fieldValue);
298         } else if (field.isAssignableFrom(Ipv6Flabel.class)) {
299             fieldValue = 28;
300             writeOxmFieldAndLength(out, fieldValue, entry.isHasMask(),
301                     EncodeConstants.SIZE_OF_INT_IN_BYTES);
302             out.writeInt(entry.getAugmentation(Ipv6FlabelMatchEntry.class).getIpv6Flabel().getValue().intValue());
303             writeMask(entry, out, EncodeConstants.SIZE_OF_INT_IN_BYTES);
304         } else if (field.isAssignableFrom(Icmpv6Type.class)) {
305             fieldValue = 29;
306             writeOxmFieldAndLength(out, fieldValue, false, EncodeConstants.SIZE_OF_BYTE_IN_BYTES);
307             out.writeByte(entry.getAugmentation(Icmpv6TypeMatchEntry.class).getIcmpv6Type());
308         } else if (field.isAssignableFrom(Icmpv6Code.class)) {
309             fieldValue = 30;
310             writeOxmFieldAndLength(out, fieldValue, false, EncodeConstants.SIZE_OF_BYTE_IN_BYTES);
311             out.writeByte(entry.getAugmentation(Icmpv6CodeMatchEntry.class).getIcmpv6Code());
312         } else if (field.isAssignableFrom(Ipv6NdTarget.class)) {
313             fieldValue = 31;
314             writeIpv6AddressRelatedEntry(entry, out, fieldValue);
315         } else if (field.isAssignableFrom(Ipv6NdSll.class)) {
316             fieldValue = 32;
317             writeMacAddressRelatedEntry(entry, out, fieldValue);
318         } else if (field.isAssignableFrom(Ipv6NdTll.class)) {
319             fieldValue = 33;
320             writeMacAddressRelatedEntry(entry, out, fieldValue);
321         } else if (field.isAssignableFrom(MplsLabel.class)) {
322             fieldValue = 34;
323             writeOxmFieldAndLength(out, fieldValue, false, EncodeConstants.SIZE_OF_INT_IN_BYTES);
324             out.writeInt(entry.getAugmentation(MplsLabelMatchEntry.class).getMplsLabel().intValue());
325         } else if (field.isAssignableFrom(MplsTc.class)) {
326             fieldValue = 35;
327             writeOxmFieldAndLength(out, fieldValue, false, EncodeConstants.SIZE_OF_BYTE_IN_BYTES);
328             out.writeByte(entry.getAugmentation(TcMatchEntry.class).getTc());
329         } else if (field.isAssignableFrom(MplsBos.class)) {
330             fieldValue = 36;
331             writeOxmFieldAndLength(out, fieldValue, false, EncodeConstants.SIZE_OF_BYTE_IN_BYTES);
332             out.writeBoolean(entry.getAugmentation(BosMatchEntry.class).isBos().booleanValue());
333         } else if (field.isAssignableFrom(PbbIsid.class)) {
334             fieldValue = 37;
335             writeOxmFieldAndLength(out, fieldValue, entry.isHasMask(), EncodeConstants.SIZE_OF_3_BYTES);
336             out.writeMedium(entry.getAugmentation(IsidMatchEntry.class).getIsid().intValue());
337             writeMask(entry, out, EncodeConstants.SIZE_OF_3_BYTES);
338         } else if (field.isAssignableFrom(TunnelId.class)) {
339             fieldValue = 38;
340             writeMetadataRelatedEntry(entry, out, fieldValue);
341         } else if (field.isAssignableFrom(Ipv6Exthdr.class)) {
342             fieldValue = 39;
343             Ipv6ExthdrFlags pseudoField = entry.getAugmentation(PseudoFieldMatchEntry.class).getPseudoField();
344             Map<Integer, Boolean> map = new HashMap<>();
345             map.put(0, pseudoField.isNonext());
346             map.put(1, pseudoField.isEsp());
347             map.put(2, pseudoField.isAuth());
348             map.put(3, pseudoField.isDest());
349             map.put(4, pseudoField.isFrag());
350             map.put(5, pseudoField.isRouter());
351             map.put(6, pseudoField.isHop());
352             map.put(7, pseudoField.isUnrep());
353             map.put(8, pseudoField.isUnseq());
354             int bitmap = ByteBufUtils.fillBitMaskFromMap(map);
355
356             writeOxmFieldAndLength(out, fieldValue, entry.isHasMask(),
357                     EncodeConstants.SIZE_OF_SHORT_IN_BYTES);
358             out.writeShort(bitmap);
359             writeMask(entry, out, EncodeConstants.SIZE_OF_SHORT_IN_BYTES);
360         }
361     }
362
363     private static void writeMask(MatchEntries entry, ByteBuf out, int length) {
364         if (entry.isHasMask()) {
365             byte[] mask = entry.getAugmentation(MaskMatchEntry.class).getMask();
366             if (mask != null && mask.length != length) {
367                 throw new IllegalArgumentException("incorrect length of mask: "+
368                         mask.length + ", expected: " + length);
369             }
370             out.writeBytes(mask);
371         }
372     }
373
374     protected static void writeOxmFieldAndLength(ByteBuf out, int fieldValue, boolean hasMask, int lengthArg) {
375         int fieldAndMask = fieldValue << 1;
376         int length = lengthArg;
377         if (hasMask) {
378             fieldAndMask |= 1;
379             length *= 2;
380         }
381
382         out.writeByte(fieldAndMask);
383         out.writeByte(length);
384
385     }
386
387     private static void writeMetadataRelatedEntry(MatchEntries entry, ByteBuf out, int value) {
388         int fieldValue = value;
389         writeOxmFieldAndLength(out, fieldValue, entry.isHasMask(),
390                 EncodeConstants.SIZE_OF_LONG_IN_BYTES);
391         out.writeBytes(entry.getAugmentation(MetadataMatchEntry.class).getMetadata());
392         writeMask(entry, out, EncodeConstants.SIZE_OF_LONG_IN_BYTES);
393     }
394
395     private static void writeMacAddressRelatedEntry(MatchEntries entry, ByteBuf out, int value) {
396         int fieldValue = value;
397
398         writeOxmFieldAndLength(out, fieldValue, entry.isHasMask(),
399                 EncodeConstants.MAC_ADDRESS_LENGTH);
400         String macAddress = entry.getAugmentation(MacAddressMatchEntry.class).getMacAddress().getValue();
401         out.writeBytes(ByteBufUtils.macAddressToBytes(macAddress)); // 48 b + mask [OF 1.3.2 spec]
402         writeMask(entry, out, EncodeConstants.MAC_ADDRESS_LENGTH);
403     }
404
405     private static void writeIpv4AddressRelatedEntry(MatchEntries entry, ByteBuf out, int value) {
406         int fieldValue = value;
407
408         writeOxmFieldAndLength(out, fieldValue, entry.isHasMask(),
409                 EncodeConstants.SIZE_OF_INT_IN_BYTES);
410         writeIpv4Address(entry, out);
411         writeMask(entry, out, EncodeConstants.SIZE_OF_INT_IN_BYTES);
412     }
413
414     private static void writeIpv4Address(MatchEntries entry, ByteBuf out) {
415         String[] addressGroups = entry.getAugmentation(Ipv4AddressMatchEntry.class).getIpv4Address().getValue().split("\\.");
416         for (int i = 0; i < addressGroups.length; i++) {
417             out.writeByte(Integer.parseInt(addressGroups[i]));
418         }
419     }
420
421     private static void writeIpv6AddressRelatedEntry(MatchEntries entry, ByteBuf out, int value) {
422         int fieldValue = value;
423         String textAddress = entry.getAugmentation(Ipv6AddressMatchEntry.class).getIpv6Address().getValue();
424         String[] address;
425         if (textAddress.equals("::")) {
426             address = new String[EncodeConstants.GROUPS_IN_IPV6_ADDRESS];
427             Arrays.fill(address, "0");
428         } else {
429             address = parseIpv6Address(textAddress.split(":"));
430         }
431
432         writeOxmFieldAndLength(out, fieldValue, entry.isHasMask(),
433                 EncodeConstants.SIZE_OF_IPV6_ADDRESS_IN_BYTES);
434         for (int i = 0; i < address.length; i++) {
435             out.writeShort(Integer.parseInt(address[i], 16));
436         }
437         writeMask(entry, out, EncodeConstants.SIZE_OF_IPV6_ADDRESS_IN_BYTES);
438     }
439
440     private static String[] parseIpv6Address(String[] addressGroups) {
441         int countEmpty = 0;
442         for (int i = 0; i < addressGroups.length; i++) {
443             if (addressGroups[i].equals("")){
444                 countEmpty++;
445             }
446         }
447         String[] ready = new String[EncodeConstants.GROUPS_IN_IPV6_ADDRESS];
448         switch (countEmpty) {
449         case 0:
450             ready = addressGroups;
451             break;
452         case 1:
453             int zerosToBePushed = EncodeConstants.GROUPS_IN_IPV6_ADDRESS - addressGroups.length + 1;
454             int index = 0;
455             for (int i = 0; i < addressGroups.length; i++) {
456                 if (addressGroups[i].equals("")) {
457                     for (int j = 0; j < zerosToBePushed; j++) {
458                         ready[index] = "0";
459                         index++;
460                     }
461                 } else {
462                     ready[index] = addressGroups[i];
463                     index++;
464                 }
465             }
466             break;
467         case 2:
468             Arrays.fill(ready, "0");
469             ready[ready.length - 1] = addressGroups[addressGroups.length - 1];
470             break;
471         default:
472             throw new IllegalStateException("Incorrect ipv6 address");
473         }
474         return ready;
475     }
476
477     /**
478      * Computes length of match (in bytes)
479      * @param match
480      * @return length of ofp_match (excluding padding)
481      */
482     public static int computeMatchLengthInternal(Match match) {
483         int length = 0;
484         if (match != null) {
485             length += MATCH_TYPE_AND_LENGTH_SIZE + computeMatchEntriesLength(match.getMatchEntries());
486         }
487         return length;
488     }
489
490     /**
491      * Computes length of match (in bytes)
492      * @param match
493      * @return length of ofp_match (excluding padding)
494      */
495     public static int computeMatchLength(Match match) {
496         int length = computeMatchLengthInternal(match);
497         int paddingRemainder = length % EncodeConstants.PADDING;
498         if (paddingRemainder != 0) {
499             length += EncodeConstants.PADDING - paddingRemainder;
500         }
501         return length;
502     }
503
504     /**
505      * Computes length of MatchEntries (in bytes)
506      * @param matchEntries list of match entries (oxm_fields)
507      * @return length of MatchEntries
508      */
509     public static int computeMatchEntriesLength(List<MatchEntries> matchEntries) {
510         int length = 0;
511         if (matchEntries != null) {
512             for (MatchEntries entry : matchEntries) {
513                 length += MATCH_ENTRY_HEADER_LENGTH;
514                 Class<? extends MatchField> field = entry.getOxmMatchField();
515                 if (field.isAssignableFrom(InPort.class)) {
516                     length += EncodeConstants.SIZE_OF_INT_IN_BYTES;
517                 } else if (field.isAssignableFrom(InPhyPort.class)) {
518                     length += EncodeConstants.SIZE_OF_INT_IN_BYTES;
519                 } else if (field.isAssignableFrom(Metadata.class)) {
520                     length += computePossibleMaskEntryLength(entry, EncodeConstants.SIZE_OF_LONG_IN_BYTES);
521                 } else if (field.isAssignableFrom(EthDst.class)) {
522                     length += computePossibleMaskEntryLength(entry, EncodeConstants.MAC_ADDRESS_LENGTH);
523                 } else if (field.isAssignableFrom(EthSrc.class)) {
524                     length += computePossibleMaskEntryLength(entry, EncodeConstants.MAC_ADDRESS_LENGTH);
525                 } else if (field.isAssignableFrom(EthType.class)) {
526                     length += EncodeConstants.SIZE_OF_SHORT_IN_BYTES;
527                 } else if (field.isAssignableFrom(VlanVid.class)) {
528                     length += computePossibleMaskEntryLength(entry, EncodeConstants.SIZE_OF_SHORT_IN_BYTES);
529                 } else if (field.isAssignableFrom(VlanPcp.class)) {
530                     length += EncodeConstants.SIZE_OF_BYTE_IN_BYTES;
531                 } else if (field.isAssignableFrom(IpDscp.class)) {
532                     length += EncodeConstants.SIZE_OF_BYTE_IN_BYTES;
533                 } else if (field.isAssignableFrom(IpEcn.class)) {
534                     length += EncodeConstants.SIZE_OF_BYTE_IN_BYTES;
535                 } else if (field.isAssignableFrom(IpProto.class)) {
536                     length += EncodeConstants.SIZE_OF_BYTE_IN_BYTES;
537                 } else if (field.isAssignableFrom(Ipv4Src.class)) {
538                     length += computePossibleMaskEntryLength(entry, EncodeConstants.SIZE_OF_INT_IN_BYTES);
539                 } else if (field.isAssignableFrom(Ipv4Dst.class)) {
540                     length += computePossibleMaskEntryLength(entry, EncodeConstants.SIZE_OF_INT_IN_BYTES);
541                 } else if (field.isAssignableFrom(TcpSrc.class)) {
542                     length += EncodeConstants.SIZE_OF_SHORT_IN_BYTES;
543                 } else if (field.isAssignableFrom(TcpDst.class)) {
544                     length += EncodeConstants.SIZE_OF_SHORT_IN_BYTES;
545                 } else if (field.isAssignableFrom(UdpSrc.class)) {
546                     length += EncodeConstants.SIZE_OF_SHORT_IN_BYTES;
547                 } else if (field.isAssignableFrom(UdpDst.class)) {
548                     length += EncodeConstants.SIZE_OF_SHORT_IN_BYTES;
549                 } else if (field.isAssignableFrom(SctpSrc.class)) {
550                     length += EncodeConstants.SIZE_OF_SHORT_IN_BYTES;
551                 } else if (field.isAssignableFrom(SctpDst.class)) {
552                     length += EncodeConstants.SIZE_OF_SHORT_IN_BYTES;
553                 } else if (field.isAssignableFrom(Icmpv4Type.class)) {
554                     length += EncodeConstants.SIZE_OF_BYTE_IN_BYTES;
555                 } else if (field.isAssignableFrom(Icmpv4Code.class)) {
556                     length += EncodeConstants.SIZE_OF_BYTE_IN_BYTES;
557                 } else if (field.isAssignableFrom(ArpOp.class)) {
558                     length += EncodeConstants.SIZE_OF_SHORT_IN_BYTES;
559                 } else if (field.isAssignableFrom(ArpSpa.class)) {
560                     length += computePossibleMaskEntryLength(entry, EncodeConstants.SIZE_OF_INT_IN_BYTES);
561                 } else if (field.isAssignableFrom(ArpTpa.class)) {
562                     length += computePossibleMaskEntryLength(entry, EncodeConstants.SIZE_OF_INT_IN_BYTES);
563                 } else if (field.isAssignableFrom(ArpSha.class)) {
564                     length += computePossibleMaskEntryLength(entry, EncodeConstants.MAC_ADDRESS_LENGTH);
565                 } else if (field.isAssignableFrom(ArpTha.class)) {
566                     length += computePossibleMaskEntryLength(entry, EncodeConstants.MAC_ADDRESS_LENGTH);
567                 } else if (field.isAssignableFrom(Ipv6Src.class)) {
568                     length += computePossibleMaskEntryLength(entry, EncodeConstants.SIZE_OF_IPV6_ADDRESS_IN_BYTES);
569                 } else if (field.isAssignableFrom(Ipv6Dst.class)) {
570                     length += computePossibleMaskEntryLength(entry, EncodeConstants.SIZE_OF_IPV6_ADDRESS_IN_BYTES);
571                 } else if (field.isAssignableFrom(Ipv6Flabel.class)) {
572                     length += computePossibleMaskEntryLength(entry, EncodeConstants.SIZE_OF_INT_IN_BYTES);
573                 } else if (field.isAssignableFrom(Icmpv6Type.class)) {
574                     length += EncodeConstants.SIZE_OF_BYTE_IN_BYTES;
575                 } else if (field.isAssignableFrom(Icmpv6Code.class)) {
576                     length += EncodeConstants.SIZE_OF_BYTE_IN_BYTES;
577                 } else if (field.isAssignableFrom(Ipv6NdTarget.class)) {
578                     length += computePossibleMaskEntryLength(entry, EncodeConstants.SIZE_OF_IPV6_ADDRESS_IN_BYTES);
579                 } else if (field.isAssignableFrom(Ipv6NdSll.class)) {
580                     length += computePossibleMaskEntryLength(entry, EncodeConstants.MAC_ADDRESS_LENGTH);
581                 } else if (field.isAssignableFrom(Ipv6NdTll.class)) {
582                     length += computePossibleMaskEntryLength(entry, EncodeConstants.MAC_ADDRESS_LENGTH);
583                 } else if (field.isAssignableFrom(MplsLabel.class)) {
584                     length += EncodeConstants.SIZE_OF_INT_IN_BYTES;
585                 } else if (field.isAssignableFrom(MplsTc.class)) {
586                     length += EncodeConstants.SIZE_OF_BYTE_IN_BYTES;
587                 } else if (field.isAssignableFrom(MplsBos.class)) {
588                     length += EncodeConstants.SIZE_OF_BYTE_IN_BYTES;
589                 } else if (field.isAssignableFrom(PbbIsid.class)) {
590                     length += computePossibleMaskEntryLength(entry, EncodeConstants.SIZE_OF_3_BYTES);
591                 } else if (field.isAssignableFrom(TunnelId.class)) {
592                     length += computePossibleMaskEntryLength(entry, EncodeConstants.SIZE_OF_LONG_IN_BYTES);
593                 } else if (field.isAssignableFrom(Ipv6Exthdr.class)) {
594                     length += computePossibleMaskEntryLength(entry, EncodeConstants.SIZE_OF_SHORT_IN_BYTES);
595                 }
596             }
597         }
598         return length;
599     }
600
601     private static int computePossibleMaskEntryLength(MatchEntries entry, int length) {
602         int entryLength = length;
603         if (entry.isHasMask()) {
604             entryLength *= 2;
605         }
606         return entryLength;
607     }
608
609 }