Fix DOM MD-SAL dependencies
[bgpcep.git] / bgp / flowspec / src / main / java / org / opendaylight / protocol / bgp / flowspec / AbstractFSNlriParser.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. 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.protocol.bgp.flowspec;
9
10 import com.google.common.annotations.VisibleForTesting;
11 import com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import com.google.common.primitives.UnsignedBytes;
14 import com.google.common.primitives.UnsignedInts;
15 import io.netty.buffer.ByteBuf;
16 import io.netty.buffer.Unpooled;
17 import java.util.ArrayList;
18 import java.util.List;
19 import java.util.Set;
20 import org.opendaylight.protocol.bgp.parser.BGPParsingException;
21 import org.opendaylight.protocol.bgp.parser.spi.NlriParser;
22 import org.opendaylight.protocol.bgp.parser.spi.NlriSerializer;
23 import org.opendaylight.protocol.util.BitArray;
24 import org.opendaylight.protocol.util.ByteArray;
25 import org.opendaylight.protocol.util.ByteBufWriteUtil;
26 import org.opendaylight.protocol.util.Values;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.BitmaskOperand;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.Dscp;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.Fragment;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.NumericOneByteValue;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.NumericOperand;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.NumericTwoByteValue;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.Flowspec;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.FlowspecBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.FlowspecType;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.DestinationPortCase;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.DestinationPortCaseBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.DscpCase;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.DscpCaseBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.FragmentCase;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.FragmentCaseBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.IcmpCodeCase;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.IcmpCodeCaseBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.IcmpTypeCase;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.IcmpTypeCaseBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.PacketLengthCase;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.PacketLengthCaseBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.PortCase;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.PortCaseBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.SourcePortCase;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.SourcePortCaseBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.TcpFlagsCase;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.TcpFlagsCaseBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.destination.port._case.DestinationPorts;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.destination.port._case.DestinationPortsBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.dscp._case.Dscps;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.dscp._case.DscpsBuilder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.fragment._case.Fragments;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.fragment._case.FragmentsBuilder;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.icmp.code._case.Codes;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.icmp.code._case.CodesBuilder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.icmp.type._case.Types;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.icmp.type._case.TypesBuilder;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.packet.length._case.PacketLengths;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.packet.length._case.PacketLengthsBuilder;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.port._case.Ports;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.port._case.PortsBuilder;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.source.port._case.SourcePorts;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.source.port._case.SourcePortsBuilder;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.tcp.flags._case.TcpFlags;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.tcp.flags._case.TcpFlagsBuilder;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.ipv4.flowspec.flowspec.type.DestinationPrefixCase;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.ipv4.flowspec.flowspec.type.SourcePrefixCase;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.Attributes;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.Attributes1;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.Attributes2;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.destination.DestinationType;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.MpReachNlriBuilder;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.MpUnreachNlriBuilder;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.mp.reach.nlri.AdvertizedRoutesBuilder;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
82 import org.opendaylight.yangtools.yang.binding.DataObject;
83 import org.opendaylight.yangtools.yang.common.QName;
84 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
85 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
86 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
87 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
88 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
89 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
90 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
91 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
92
93 public abstract class AbstractFSNlriParser implements NlriParser, NlriSerializer {
94
95     // component types values
96     protected static final int DESTINATION_PREFIX_VALUE = 1;
97     protected static final int SOURCE_PREFIX_VALUE = 2;
98     protected static final int PORT_VALUE = 4;
99     protected static final int DESTINATION_PORT_VALUE = 5;
100     protected static final int SOURCE_PORT_VALUE = 6;
101     protected static final int ICMP_TYPE_VALUE = 7;
102     protected static final int ICMP_CODE_VALUE = 8;
103     protected static final int TCP_FLAGS_VALUE = 9;
104     protected static final int PACKET_LENGTH_VALUE = 10;
105     protected static final int DSCP_VALUE = 11;
106     protected static final int FRAGMENT_VALUE = 12;
107
108     protected static final NodeIdentifier FLOWSPEC_TYPE_NID = new NodeIdentifier(FlowspecType.QNAME);
109     @VisibleForTesting
110     static final NodeIdentifier DEST_PREFIX_NID = new NodeIdentifier(QName.cachedReference(QName.create(DestinationPrefixCase.QNAME, "destination-prefix")));
111     @VisibleForTesting
112     static final NodeIdentifier SOURCE_PREFIX_NID = new NodeIdentifier(QName.cachedReference(QName.create(SourcePrefixCase.QNAME, "source-prefix")));
113     @VisibleForTesting
114     static final NodeIdentifier PORTS_NID = new NodeIdentifier(Ports.QNAME);
115     @VisibleForTesting
116     static final NodeIdentifier DEST_PORT_NID = new NodeIdentifier(DestinationPorts.QNAME);
117     @VisibleForTesting
118     static final NodeIdentifier SOURCE_PORT_NID = new NodeIdentifier(SourcePorts.QNAME);
119     @VisibleForTesting
120     static final NodeIdentifier ICMP_TYPE_NID = new NodeIdentifier(Types.QNAME);
121     @VisibleForTesting
122     static final NodeIdentifier ICMP_CODE_NID = new NodeIdentifier(Codes.QNAME);
123     @VisibleForTesting
124     static final NodeIdentifier TCP_FLAGS_NID = new NodeIdentifier(TcpFlags.QNAME);
125     @VisibleForTesting
126     static final NodeIdentifier PACKET_LENGTHS_NID = new NodeIdentifier(PacketLengths.QNAME);
127     @VisibleForTesting
128     static final NodeIdentifier DSCP_NID = new NodeIdentifier(Dscps.QNAME);
129     @VisibleForTesting
130     static final NodeIdentifier FRAGMENT_NID = new NodeIdentifier(Fragments.QNAME);
131     @VisibleForTesting
132     static final NodeIdentifier OP_NID = new NodeIdentifier(QName.create("urn:opendaylight:params:xml:ns:yang:bgp-flowspec","2015-08-07","op"));
133     @VisibleForTesting
134     static final NodeIdentifier VALUE_NID = new NodeIdentifier(QName.create("urn:opendaylight:params:xml:ns:yang:bgp-flowspec","2015-08-07","value"));
135
136     protected static final int NLRI_LENGTH = 1;
137     protected static final int NLRI_LENGTH_EXTENDED = 2;
138
139     private static final int OPERAND_LENGTH = 8;
140
141     private static final int END_OF_LIST = 0;
142     private static final int AND_BIT = 1;
143     private static final int LENGTH_BITMASK = 48;
144     private static final int LENGTH_SHIFT = 4;
145     private static final int LESS_THAN = 5;
146     private static final int GREATER_THAN = 6;
147     private static final int EQUAL = 7;
148     private static final int NOT = 6;
149     private static final int MATCH = 7;
150
151     /**
152      * Add this constant to length value to achieve all ones in the leftmost nibble.
153      */
154     private static final int LENGTH_MAGIC = 61440;
155     private static final int MAX_NLRI_LENGTH = 4095;
156     private static final int MAX_NLRI_LENGTH_ONE_BYTE = 240;
157
158     @VisibleForTesting
159     static final String AND_BIT_VALUE = "and-bit";
160     @VisibleForTesting
161     static final String END_OF_LIST_VALUE = "end-of-list";
162     @VisibleForTesting
163     static final String EQUALS_VALUE = "equals";
164     @VisibleForTesting
165     static final String GREATER_THAN_VALUE = "greater-than";
166     @VisibleForTesting
167     static final String LESS_THAN_VALUE = "less-than";
168     @VisibleForTesting
169     static final String MATCH_VALUE = "match";
170     @VisibleForTesting
171     static final String NOT_VALUE = "not";
172     @VisibleForTesting
173     static final String DO_NOT_VALUE = "do-not";
174     @VisibleForTesting
175     static final String FIRST_VALUE = "first";
176     @VisibleForTesting
177     static final String LAST_VALUE = "last";
178     @VisibleForTesting
179     static final String IS_A_VALUE = "is-a";
180
181     protected static final int LAST_FRAGMENT = 4;
182     protected static final int FIRST_FRAGMENT = 5;
183     protected static final int IS_A_FRAGMENT = 6;
184     protected static final int DONT_FRAGMENT = 7;
185
186     protected abstract void serializeMpReachNlri(final Attributes1 pathAttributes, final ByteBuf byteAggregator);
187
188     protected abstract void serializeMpUnreachNlri(final Attributes2 pathAttributes, final ByteBuf byteAggregator);
189
190     protected abstract void serializeSpecificFSType(final FlowspecType fsType, final ByteBuf nlriByteBuf);
191
192     protected abstract byte serializeFragment(final Fragment fragment);
193
194     protected abstract Fragment parseFragment(final byte fragment);
195
196     protected abstract void setSpecificFlowspecType(final FlowspecBuilder builder, final short type, final ByteBuf nlri);
197
198     public abstract void extractSpecificFlowspec(final ChoiceNode fsType, final FlowspecBuilder fsBuilder);
199
200     abstract DestinationType createWidthdrawnDestinationType(final List<Flowspec> dst);
201
202     abstract DestinationType createAdvertizedRoutesDestinationType(final List<Flowspec> dst);
203
204     protected abstract void stringSpecificFSNlriType(final FlowspecType value, final StringBuilder buffer);
205
206     @Override
207     public final void serializeAttribute(final DataObject attribute, final ByteBuf byteAggregator) {
208         Preconditions.checkArgument(attribute instanceof Attributes, "Attribute parameter is not a PathAttribute object.");
209         final Attributes pathAttributes = (Attributes) attribute;
210         final Attributes1 pathAttributes1 = pathAttributes.getAugmentation(Attributes1.class);
211         final Attributes2 pathAttributes2 = pathAttributes.getAugmentation(Attributes2.class);
212         serializeMpReachNlri(pathAttributes1, byteAggregator);
213         serializeMpUnreachNlri(pathAttributes2, byteAggregator);
214     }
215
216     @Override
217     public final void parseNlri(final ByteBuf nlri, final MpUnreachNlriBuilder builder) throws BGPParsingException {
218         if (!nlri.isReadable()) {
219             return;
220         }
221         final List<Flowspec> dst = parseNlri(nlri);
222
223         builder.setWithdrawnRoutes(new WithdrawnRoutesBuilder().setDestinationType(createWidthdrawnDestinationType(dst)).build());
224     }
225
226     @Override
227     public final void parseNlri(final ByteBuf nlri, final MpReachNlriBuilder builder) throws BGPParsingException {
228         if (!nlri.isReadable()) {
229             return;
230         }
231         final List<Flowspec> dst = parseNlri(nlri);
232         builder.setAdvertizedRoutes(new AdvertizedRoutesBuilder().setDestinationType(createAdvertizedRoutesDestinationType(dst)).build());
233     }
234
235     /**
236      * Serializes Flowspec NLRI to ByteBuf.
237      *
238      * @param flows flowspec NLRI to be serialized
239      * @param buffer where flowspec NLRI will be serialized
240      */
241     public final void serializeNlri(final List<Flowspec> flows, final ByteBuf buffer) {
242         final ByteBuf nlriByteBuf = Unpooled.buffer();
243         for (final Flowspec flow : flows) {
244             serializeFlowspec(flow, nlriByteBuf);
245         }
246         Preconditions.checkState(nlriByteBuf.readableBytes() <= MAX_NLRI_LENGTH, "Maximum length of Flowspec NLRI reached.");
247         if (nlriByteBuf.readableBytes() <= MAX_NLRI_LENGTH_ONE_BYTE) {
248             buffer.writeByte(nlriByteBuf.readableBytes());
249         } else {
250             buffer.writeShort(nlriByteBuf.readableBytes() + LENGTH_MAGIC);
251         }
252         buffer.writeBytes(nlriByteBuf);
253     }
254
255     /**
256      * Serializes Flowspec component type that has maximum of 2B sized value field and numeric operand.
257      *
258      * @param list of items to be serialized
259      * @param nlriByteBuf where the items will be serialized
260      */
261     protected static final <T extends NumericTwoByteValue> void serializeNumericTwoByteValue(final List<T> list, final ByteBuf nlriByteBuf) {
262         for (final T item : list) {
263             final ByteBuf protoBuf = Unpooled.buffer();
264             writeShortest(item.getValue(), protoBuf);
265             serializeNumericOperand(item.getOp(), protoBuf.readableBytes(), nlriByteBuf);
266             nlriByteBuf.writeBytes(protoBuf);
267         }
268     }
269
270     /**
271      * Serializes Flowspec component type that has maximum of 1B sized value field and numeric operand.
272      *
273      * @param list of items to be serialized
274      * @param nlriByteBuf where the items will be serialized
275      */
276     protected static final <T extends NumericOneByteValue> void serializeNumericOneByteValue(final List<T> list, final ByteBuf nlriByteBuf) {
277         for (final T type : list) {
278             serializeNumericOperand(type.getOp(), 1, nlriByteBuf);
279             writeShortest(type.getValue(), nlriByteBuf);
280         }
281     }
282
283     /**
284      * Given the integer values, this method instead of writing the value
285      * in 4B field, compresses the value to lowest required byte field
286      * depending on the value.
287      *
288      * @param value integer to be written
289      * @param buffer ByteBuf where the value will be written
290      */
291     protected static final void writeShortest(final int value, final ByteBuf buffer) {
292         if (value <= Values.UNSIGNED_BYTE_MAX_VALUE) {
293             buffer.writeByte(UnsignedBytes.checkedCast(value));
294         } else if (value <= Values.UNSIGNED_SHORT_MAX_VALUE) {
295             ByteBufWriteUtil.writeUnsignedShort(value, buffer);
296         } else if (value <= Values.UNSIGNED_INT_MAX_VALUE) {
297             ByteBufWriteUtil.writeUnsignedInt(UnsignedInts.toLong(value), buffer);
298         } else {
299             buffer.writeLong(value);
300         }
301     }
302
303     protected static final void serializeNumericOperand(final NumericOperand op, final int length, final ByteBuf buffer) {
304         final BitArray bs = new BitArray(OPERAND_LENGTH);
305         bs.set(END_OF_LIST, op.isEndOfList());
306         bs.set(AND_BIT, op.isAndBit());
307         bs.set(LESS_THAN, op.isLessThan());
308         bs.set(GREATER_THAN, op.isGreaterThan());
309         bs.set(EQUAL, op.isEquals());
310         final byte len = (byte) (Integer.numberOfTrailingZeros(length) << LENGTH_SHIFT);
311         buffer.writeByte(bs.toByte() | len);
312     }
313
314     protected static final void serializeBitmaskOperand(final BitmaskOperand op, final int length, final ByteBuf buffer) {
315         final BitArray bs = new BitArray(OPERAND_LENGTH);
316         bs.set(END_OF_LIST, op.isEndOfList());
317         bs.set(AND_BIT, op.isAndBit());
318         bs.set(MATCH, op.isMatch());
319         bs.set(NOT, op.isNot());
320         final byte len = (byte) (Integer.numberOfTrailingZeros(length) << LENGTH_SHIFT);
321         buffer.writeByte(bs.toByte() | len);
322     }
323
324     protected static final void serializeTcpFlags(final List<TcpFlags> flags, final ByteBuf nlriByteBuf) {
325         for (final TcpFlags flag : flags) {
326             final ByteBuf flagsBuf = Unpooled.buffer();
327             writeShortest(flag.getValue(), flagsBuf);
328             serializeBitmaskOperand(flag.getOp(), flagsBuf.readableBytes(), nlriByteBuf);
329             nlriByteBuf.writeBytes(flagsBuf);
330         }
331     }
332
333     protected static final void serializeDscps(final List<Dscps> dscps, final ByteBuf nlriByteBuf) {
334         for (final Dscps dscp : dscps) {
335             serializeNumericOperand(dscp.getOp(), 1, nlriByteBuf);
336             writeShortest(dscp.getValue().getValue(), nlriByteBuf);
337         }
338     }
339
340     protected final void serializeFragments(final List<Fragments> fragments, final ByteBuf nlriByteBuf) {
341         for (final Fragments fragment : fragments) {
342             serializeBitmaskOperand(fragment.getOp(), 1, nlriByteBuf);
343             nlriByteBuf.writeByte(serializeFragment(fragment.getValue()));
344         }
345     }
346
347     protected final void serializeFlowspec(final Flowspec flow, final ByteBuf nlriByteBuf) {
348         final FlowspecType fsType = flow.getFlowspecType();
349         if (fsType instanceof PortCase) {
350             nlriByteBuf.writeByte(PORT_VALUE);
351             serializeNumericTwoByteValue(((PortCase) fsType).getPorts(), nlriByteBuf);
352         } else if (fsType instanceof DestinationPortCase) {
353             nlriByteBuf.writeByte(DESTINATION_PORT_VALUE);
354             serializeNumericTwoByteValue(((DestinationPortCase) fsType).getDestinationPorts(), nlriByteBuf);
355         } else if (fsType instanceof SourcePortCase) {
356             nlriByteBuf.writeByte(SOURCE_PORT_VALUE);
357             serializeNumericTwoByteValue(((SourcePortCase) fsType).getSourcePorts(), nlriByteBuf);
358         } else if (fsType instanceof IcmpTypeCase) {
359             nlriByteBuf.writeByte(ICMP_TYPE_VALUE);
360             serializeNumericOneByteValue(((IcmpTypeCase) fsType).getTypes(), nlriByteBuf);
361         } else if (fsType instanceof IcmpCodeCase) {
362             nlriByteBuf.writeByte(ICMP_CODE_VALUE);
363             serializeNumericOneByteValue(((IcmpCodeCase) fsType).getCodes(), nlriByteBuf);
364         } else if (fsType instanceof TcpFlagsCase) {
365             nlriByteBuf.writeByte(TCP_FLAGS_VALUE);
366             serializeTcpFlags(((TcpFlagsCase) fsType).getTcpFlags(), nlriByteBuf);
367         } else if (fsType instanceof PacketLengthCase) {
368             nlriByteBuf.writeByte(PACKET_LENGTH_VALUE);
369             serializeNumericTwoByteValue(((PacketLengthCase) fsType).getPacketLengths(), nlriByteBuf);
370         } else if (fsType instanceof DscpCase) {
371             nlriByteBuf.writeByte(DSCP_VALUE);
372             serializeDscps(((DscpCase) fsType).getDscps(), nlriByteBuf);
373         } else {
374             serializeSpecificFSType(fsType, nlriByteBuf);
375         }
376     }
377
378     /**
379      * Parses Flowspec NLRI into list of Flowspec.
380      *
381      * @param nlri byte representation of NLRI which will be parsed
382      * @return list of Flowspec
383      */
384     public final List<Flowspec> parseNlri(final ByteBuf nlri) throws BGPParsingException {
385         if (!nlri.isReadable()) {
386             return null;
387         }
388         final List<Flowspec> fss = new ArrayList<>();
389
390         // length field can be one or two bytes (if needed)
391         // check the length of nlri to see how many bytes we can skip
392         final int length = nlri.readableBytes();
393         nlri.skipBytes(length > MAX_NLRI_LENGTH_ONE_BYTE ? NLRI_LENGTH_EXTENDED : NLRI_LENGTH);
394
395         while (nlri.isReadable()) {
396             final FlowspecBuilder builder = new FlowspecBuilder();
397             final short type = nlri.readUnsignedByte();
398             setFlowspecType(builder, type, nlri);
399             fss.add(builder.build());
400         }
401         return fss;
402     }
403
404     protected final void setFlowspecType(final FlowspecBuilder builder, final short type, final ByteBuf nlri) {
405         switch (type) {
406         case PORT_VALUE:
407             builder.setFlowspecType(new PortCaseBuilder().setPorts(parsePort(nlri)).build());
408             break;
409         case DESTINATION_PORT_VALUE:
410             builder.setFlowspecType(new DestinationPortCaseBuilder().setDestinationPorts(parseDestinationPort(nlri)).build());
411             break;
412         case SOURCE_PORT_VALUE:
413             builder.setFlowspecType(new SourcePortCaseBuilder().setSourcePorts(parseSourcePort(nlri)).build());
414             break;
415         case ICMP_TYPE_VALUE:
416             builder.setFlowspecType(new IcmpTypeCaseBuilder().setTypes(parseIcmpType(nlri)).build());
417             break;
418         case ICMP_CODE_VALUE:
419             builder.setFlowspecType(new IcmpCodeCaseBuilder().setCodes(parseIcmpCode(nlri)).build());
420             break;
421         case TCP_FLAGS_VALUE:
422             builder.setFlowspecType(new TcpFlagsCaseBuilder().setTcpFlags(parseTcpFlags(nlri)).build());
423             break;
424         case PACKET_LENGTH_VALUE:
425             builder.setFlowspecType(new PacketLengthCaseBuilder().setPacketLengths(parsePacketLength(nlri)).build());
426             break;
427         case DSCP_VALUE:
428             builder.setFlowspecType(new DscpCaseBuilder().setDscps(parseDscp(nlri)).build());
429             break;
430         default:
431             setSpecificFlowspecType(builder, type, nlri);
432             break;
433         }
434     }
435
436     private static List<Ports> parsePort(final ByteBuf nlri) {
437         final List<Ports> ports = new ArrayList<>();
438         boolean end = false;
439         // we can do this as all fields will be rewritten in the cycle
440         final PortsBuilder builder = new PortsBuilder();
441         while (!end) {
442             final byte b = nlri.readByte();
443             final NumericOperand op = parseNumeric(b);
444             builder.setOp(op);
445             final short length = parseLength(b);
446             builder.setValue(ByteArray.bytesToInt(ByteArray.readBytes(nlri, length)));
447             end = op.isEndOfList();
448             ports.add(builder.build());
449         }
450         return ports;
451     }
452
453     private static List<DestinationPorts> parseDestinationPort(final ByteBuf nlri) {
454         final List<DestinationPorts> ports = new ArrayList<>();
455         boolean end = false;
456         // we can do this as all fields will be rewritten in the cycle
457         final DestinationPortsBuilder builder = new DestinationPortsBuilder();
458         while (!end) {
459             final byte b = nlri.readByte();
460             final NumericOperand op = parseNumeric(b);
461             builder.setOp(op);
462             final short length = parseLength(b);
463             builder.setValue(ByteArray.bytesToInt(ByteArray.readBytes(nlri, length)));
464             end = op.isEndOfList();
465             ports.add(builder.build());
466         }
467         return ports;
468     }
469
470     private static List<SourcePorts> parseSourcePort(final ByteBuf nlri) {
471         final List<SourcePorts> ports = new ArrayList<>();
472         boolean end = false;
473         // we can do this as all fields will be rewritten in the cycle
474         final SourcePortsBuilder builder = new SourcePortsBuilder();
475         while (!end) {
476             final byte b = nlri.readByte();
477             final NumericOperand op = parseNumeric(b);
478             builder.setOp(op);
479             final short length = parseLength(b);
480             builder.setValue(ByteArray.bytesToInt(ByteArray.readBytes(nlri, length)));
481             end = op.isEndOfList();
482             ports.add(builder.build());
483         }
484         return ports;
485     }
486
487     private static List<Types> parseIcmpType(final ByteBuf nlri) {
488         final List<Types> types = new ArrayList<>();
489         boolean end = false;
490         // we can do this as all fields will be rewritten in the cycle
491         final TypesBuilder builder = new TypesBuilder();
492         while (!end) {
493             final byte b = nlri.readByte();
494             final NumericOperand op = parseNumeric(b);
495             builder.setOp(op);
496             builder.setValue(nlri.readUnsignedByte());
497             end = op.isEndOfList();
498             types.add(builder.build());
499         }
500         return types;
501     }
502
503     private static List<Codes> parseIcmpCode(final ByteBuf nlri) {
504         final List<Codes> codes = new ArrayList<>();
505         boolean end = false;
506         // we can do this as all fields will be rewritten in the cycle
507         final CodesBuilder builder = new CodesBuilder();
508         while (!end) {
509             final byte b = nlri.readByte();
510             final NumericOperand op = parseNumeric(b);
511             builder.setOp(op);
512             builder.setValue(nlri.readUnsignedByte());
513             end = op.isEndOfList();
514             codes.add(builder.build());
515         }
516         return codes;
517     }
518
519     private static List<TcpFlags> parseTcpFlags(final ByteBuf nlri) {
520         final List<TcpFlags> flags = new ArrayList<>();
521         boolean end = false;
522         // we can do this as all fields will be rewritten in the cycle
523         final TcpFlagsBuilder builder = new TcpFlagsBuilder();
524         while (!end) {
525             final byte b = nlri.readByte();
526             final BitmaskOperand op = parseBitmask(b);
527             builder.setOp(op);
528             final short length = parseLength(b);
529             builder.setValue(ByteArray.bytesToInt(ByteArray.readBytes(nlri, length)));
530             end = op.isEndOfList();
531             flags.add(builder.build());
532         }
533         return flags;
534     }
535
536     private static List<PacketLengths> parsePacketLength(final ByteBuf nlri) {
537         final List<PacketLengths> plengths = new ArrayList<>();
538         boolean end = false;
539         // we can do this as all fields will be rewritten in the cycle
540         final PacketLengthsBuilder builder = new PacketLengthsBuilder();
541         while (!end) {
542             final byte b = nlri.readByte();
543             // RFC does not specify which operand to use
544             final NumericOperand op = parseNumeric(b);
545             builder.setOp(op);
546             final short length = parseLength(b);
547             builder.setValue(ByteArray.bytesToInt(ByteArray.readBytes(nlri, length)));
548             end = op.isEndOfList();
549             plengths.add(builder.build());
550         }
551         return plengths;
552     }
553
554     private static List<Dscps> parseDscp(final ByteBuf nlri) {
555         final List<Dscps> dscps = new ArrayList<>();
556         boolean end = false;
557         // we can do this as all fields will be rewritten in the cycle
558         final DscpsBuilder builder = new DscpsBuilder();
559         while (!end) {
560             final byte b = nlri.readByte();
561             // RFC does not specify operator
562             final NumericOperand op = parseNumeric(b);
563             builder.setOp(op);
564             builder.setValue(new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.Dscp(nlri.readUnsignedByte()));
565             end = op.isEndOfList();
566             dscps.add(builder.build());
567         }
568         return dscps;
569     }
570
571     protected final List<Fragments> parseFragment(final ByteBuf nlri) {
572         final List<Fragments> fragments = new ArrayList<>();
573         boolean end = false;
574         // we can do this as all fields will be rewritten in the cycle
575         final FragmentsBuilder builder = new FragmentsBuilder();
576         while (!end) {
577             final byte b = nlri.readByte();
578             final BitmaskOperand op = parseBitmask(b);
579             builder.setOp(op);
580             builder.setValue(parseFragment(nlri.readByte()));
581             end = op.isEndOfList();
582             fragments.add(builder.build());
583         }
584         return fragments;
585     }
586
587     protected static final NumericOperand parseNumeric(final byte op) {
588         final BitArray bs = BitArray.valueOf(op);
589         return new NumericOperand(bs.get(AND_BIT), bs.get(END_OF_LIST), bs.get(EQUAL), bs.get(GREATER_THAN), bs.get(LESS_THAN));
590     }
591
592     protected static final BitmaskOperand parseBitmask(final byte op) {
593         final BitArray bs = BitArray.valueOf(op);
594         return new BitmaskOperand(bs.get(AND_BIT), bs.get(END_OF_LIST), bs.get(MATCH), bs.get(NOT));
595     }
596
597     @VisibleForTesting
598     public static final short parseLength(final byte op) {
599         return (short) (1 << ((op & LENGTH_BITMASK) >> LENGTH_SHIFT));
600     }
601
602     public final String stringNlri(final DataContainerNode<?> flowspec) {
603         return stringNlri(extractFlowspec((MapEntryNode) flowspec));
604     }
605
606     public final Flowspec extractFlowspec(final MapEntryNode route) {
607         final FlowspecBuilder fsBuilder = new FlowspecBuilder();
608         final Optional<DataContainerChild<?, ?>> flowspecType = route.getChild(FLOWSPEC_TYPE_NID);
609         if (flowspecType.isPresent()) {
610             final ChoiceNode fsType = (ChoiceNode) flowspecType.get();
611             if (fsType.getChild(PORTS_NID).isPresent()) {
612                 fsBuilder.setFlowspecType(new PortCaseBuilder().setPorts(createPorts((UnkeyedListNode) fsType.getChild(PORTS_NID).get())).build());
613             } else if (fsType.getChild(DEST_PORT_NID).isPresent()) {
614                 fsBuilder.setFlowspecType(new DestinationPortCaseBuilder().setDestinationPorts(createDestinationPorts((UnkeyedListNode) fsType.getChild(DEST_PORT_NID).get())).build());
615             } else if (fsType.getChild(SOURCE_PORT_NID).isPresent()) {
616                 fsBuilder.setFlowspecType(new SourcePortCaseBuilder().setSourcePorts(createSourcePorts((UnkeyedListNode) fsType.getChild(SOURCE_PORT_NID).get())).build());
617             } else if (fsType.getChild(ICMP_TYPE_NID).isPresent()) {
618                 fsBuilder.setFlowspecType(new IcmpTypeCaseBuilder().setTypes(createTypes((UnkeyedListNode) fsType.getChild(ICMP_TYPE_NID).get())).build());
619             } else if (fsType.getChild(ICMP_CODE_NID).isPresent()) {
620                 fsBuilder.setFlowspecType(new IcmpCodeCaseBuilder().setCodes(createCodes((UnkeyedListNode) fsType.getChild(ICMP_CODE_NID).get())).build());
621             } else if (fsType.getChild(TCP_FLAGS_NID).isPresent()) {
622                 fsBuilder.setFlowspecType(new TcpFlagsCaseBuilder().setTcpFlags(createTcpFlags((UnkeyedListNode) fsType.getChild(TCP_FLAGS_NID).get())).build());
623             } else if (fsType.getChild(PACKET_LENGTHS_NID).isPresent()) {
624                 fsBuilder.setFlowspecType(new PacketLengthCaseBuilder().setPacketLengths(createPacketLengths((UnkeyedListNode) fsType.getChild(PACKET_LENGTHS_NID).get())).build());
625             } else if (fsType.getChild(DSCP_NID).isPresent()) {
626                 fsBuilder.setFlowspecType(new DscpCaseBuilder().setDscps(createDscpsLengths((UnkeyedListNode) fsType.getChild(DSCP_NID).get())).build());
627             } else if (fsType.getChild(FRAGMENT_NID).isPresent()) {
628                 fsBuilder.setFlowspecType(new FragmentCaseBuilder().setFragments(createFragments((UnkeyedListNode) fsType.getChild(FRAGMENT_NID).get())).build());
629             } else {
630                 extractSpecificFlowspec(fsType, fsBuilder);
631             }
632         }
633         return fsBuilder.build();
634     }
635
636     private static List<Ports> createPorts(final UnkeyedListNode portsData) {
637         final List<Ports> ports = new ArrayList<>();
638
639         for (final UnkeyedListEntryNode node : portsData.getValue()) {
640             final PortsBuilder portsBuilder = new PortsBuilder();
641             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
642             if (opValue.isPresent()) {
643                 portsBuilder.setOp(createNumericOperand((Set<String>) opValue.get().getValue()));
644             }
645             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
646             if (valueNode.isPresent()) {
647                 portsBuilder.setValue((Integer) valueNode.get().getValue());
648             }
649             ports.add(portsBuilder.build());
650         }
651
652         return ports;
653     }
654
655     private static List<DestinationPorts> createDestinationPorts(final UnkeyedListNode destinationPortsData) {
656         final List<DestinationPorts> destinationPorts = new ArrayList<>();
657
658         for (final UnkeyedListEntryNode node : destinationPortsData.getValue()) {
659             final DestinationPortsBuilder destPortsBuilder = new DestinationPortsBuilder();
660             if (node.getNodeType().getLocalName().equals("op")) {
661                 final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
662                 if (opValue.isPresent()) {
663                     destPortsBuilder.setOp(createNumericOperand((Set<String>) opValue.get().getValue()));
664                 }
665             }
666             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
667             if (valueNode.isPresent()) {
668                 destPortsBuilder.setValue((Integer) valueNode.get().getValue());
669             }
670             destinationPorts.add(destPortsBuilder.build());
671         }
672
673         return destinationPorts;
674     }
675
676     private static List<SourcePorts> createSourcePorts(final UnkeyedListNode sourcePortsData) {
677         final List<SourcePorts> sourcePorts = new ArrayList<>();
678
679         for (final UnkeyedListEntryNode node : sourcePortsData.getValue()) {
680             final SourcePortsBuilder sourcePortsBuilder = new SourcePortsBuilder();
681             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
682             if (opValue.isPresent()) {
683                 sourcePortsBuilder.setOp(createNumericOperand((Set<String>) opValue.get().getValue()));
684             }
685             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
686             if (valueNode.isPresent()) {
687                 sourcePortsBuilder.setValue((Integer) valueNode.get().getValue());
688             }
689             sourcePorts.add(sourcePortsBuilder.build());
690         }
691
692         return sourcePorts;
693     }
694
695     private static List<Types> createTypes(final UnkeyedListNode typesData) {
696         final List<Types> types = new ArrayList<>();
697
698         for (final UnkeyedListEntryNode node : typesData.getValue()) {
699             final TypesBuilder typesBuilder = new TypesBuilder();
700             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
701             if (opValue.isPresent()) {
702                 typesBuilder.setOp(createNumericOperand((Set<String>) opValue.get().getValue()));
703             }
704             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
705             if (valueNode.isPresent()) {
706                 typesBuilder.setValue((Short) valueNode.get().getValue());
707             }
708             types.add(typesBuilder.build());
709         }
710
711         return types;
712     }
713
714     private static List<Codes> createCodes(final UnkeyedListNode codesData) {
715         final List<Codes> codes = new ArrayList<>();
716
717         for (final UnkeyedListEntryNode node : codesData.getValue()) {
718             final CodesBuilder codesBuilder = new CodesBuilder();
719             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
720             if (opValue.isPresent()) {
721                 codesBuilder.setOp(createNumericOperand((Set<String>) opValue.get().getValue()));
722             }
723             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
724             if (valueNode.isPresent()) {
725                 codesBuilder.setValue((Short) valueNode.get().getValue());
726             }
727             codes.add(codesBuilder.build());
728         }
729
730         return codes;
731     }
732
733     private static List<TcpFlags> createTcpFlags(final UnkeyedListNode tcpFlagsData) {
734         final List<TcpFlags> tcpFlags = new ArrayList<>();
735
736         for (final UnkeyedListEntryNode node : tcpFlagsData.getValue()) {
737             final TcpFlagsBuilder tcpFlagsBuilder = new TcpFlagsBuilder();
738             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
739             if (opValue.isPresent()) {
740                 tcpFlagsBuilder.setOp(createBitmaskOperand((Set<String>) opValue.get().getValue()));
741             }
742             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
743             if (valueNode.isPresent()) {
744                 tcpFlagsBuilder.setValue((Integer) valueNode.get().getValue());
745             }
746             tcpFlags.add(tcpFlagsBuilder.build());
747         }
748
749         return tcpFlags;
750     }
751
752     private static List<PacketLengths> createPacketLengths(final UnkeyedListNode packetLengthsData) {
753         final List<PacketLengths> packetLengths = new ArrayList<>();
754
755         for (final UnkeyedListEntryNode node : packetLengthsData.getValue()) {
756             final PacketLengthsBuilder packetLengthsBuilder = new PacketLengthsBuilder();
757             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
758             if (opValue.isPresent()) {
759                 packetLengthsBuilder.setOp(createNumericOperand((Set<String>) opValue.get().getValue()));
760             }
761             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
762             if (valueNode.isPresent()) {
763                 packetLengthsBuilder.setValue((Integer) valueNode.get().getValue());
764             }
765             packetLengths.add(packetLengthsBuilder.build());
766         }
767
768         return packetLengths;
769     }
770
771     private static List<Dscps> createDscpsLengths(final UnkeyedListNode dscpLengthsData) {
772         final List<Dscps> dscpsLengths = new ArrayList<>();
773
774         for (final UnkeyedListEntryNode node : dscpLengthsData.getValue()) {
775             final DscpsBuilder dscpsLengthsBuilder = new DscpsBuilder();
776             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
777             if (opValue.isPresent()) {
778                 dscpsLengthsBuilder.setOp(createNumericOperand((Set<String>) opValue.get().getValue()));
779             }
780             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
781             if (valueNode.isPresent()) {
782                 dscpsLengthsBuilder.setValue(new Dscp((Short) valueNode.get().getValue()));
783             }
784             dscpsLengths.add(dscpsLengthsBuilder.build());
785         }
786
787         return dscpsLengths;
788     }
789
790     private static List<Fragments> createFragments(final UnkeyedListNode fragmentsData) {
791         final List<Fragments> fragments = new ArrayList<>();
792
793         for (final UnkeyedListEntryNode node : fragmentsData.getValue()) {
794             final FragmentsBuilder fragmentsBuilder = new FragmentsBuilder();
795             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
796             if (opValue.isPresent()) {
797                 fragmentsBuilder.setOp(createBitmaskOperand((Set<String>) opValue.get().getValue()));
798             }
799             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
800             if (valueNode.isPresent()) {
801                 fragmentsBuilder.setValue(createFragment((Set<String>) valueNode.get().getValue()));
802             }
803             fragments.add(fragmentsBuilder.build());
804         }
805
806         return fragments;
807     }
808
809     private static Fragment createFragment(final Set<String> data) {
810         return new Fragment(data.contains(DO_NOT_VALUE), data.contains(FIRST_VALUE), data.contains(IS_A_VALUE), data.contains(LAST_VALUE));
811     }
812
813     protected static final NumericOperand createNumericOperand(final Set<String> opValues) {
814         return new NumericOperand(opValues.contains(AND_BIT_VALUE), opValues.contains(END_OF_LIST_VALUE), opValues.contains(EQUALS_VALUE), opValues.contains(GREATER_THAN_VALUE), opValues.contains(LESS_THAN_VALUE));
815     }
816
817     private static BitmaskOperand createBitmaskOperand(final Set<String> opValues) {
818         return new BitmaskOperand(opValues.contains(AND_BIT_VALUE), opValues.contains(END_OF_LIST_VALUE), opValues.contains(MATCH_VALUE), opValues.contains(NOT_VALUE));
819     }
820
821     @VisibleForTesting
822     final String stringNlri(final Flowspec flow) {
823         final StringBuilder buffer = new StringBuilder("all packets ");
824         final FlowspecType value = flow.getFlowspecType();
825         if (value instanceof PortCase) {
826             buffer.append("where port ");
827             buffer.append(stringNumericTwo(((PortCase) value).getPorts()));
828         } else if (value instanceof DestinationPortCase) {
829             buffer.append("where destination port ");
830             buffer.append(stringNumericTwo(((DestinationPortCase) value).getDestinationPorts()));
831         } else if (value instanceof SourcePortCase) {
832             buffer.append("where source port ");
833             buffer.append(stringNumericTwo(((SourcePortCase) value).getSourcePorts()));
834         } else if (value instanceof IcmpTypeCase) {
835             buffer.append("where ICMP type ");
836             buffer.append(stringNumericOne(((IcmpTypeCase) value).getTypes()));
837         } else if (value instanceof IcmpCodeCase) {
838             buffer.append("where ICMP code ");
839             buffer.append(stringNumericOne(((IcmpCodeCase) value).getCodes()));
840         } else if (value instanceof TcpFlagsCase) {
841             buffer.append(stringTcpFlags(((TcpFlagsCase) value).getTcpFlags()));
842         } else if (value instanceof PacketLengthCase) {
843             buffer.append("where packet length ");
844             buffer.append(stringNumericTwo(((PacketLengthCase) value).getPacketLengths()));
845         } else if (value instanceof DscpCase) {
846             buffer.append(stringDscp(((DscpCase) value).getDscps()));
847         } else if (value instanceof FragmentCase) {
848             buffer.append(stringFragment(((FragmentCase) value).getFragments()));
849         } else {
850             stringSpecificFSNlriType(value, buffer);
851         }
852         return buffer.toString();
853     }
854
855     private static <T extends NumericTwoByteValue> String stringNumericTwo(final List<T> list) {
856         final StringBuilder buffer = new StringBuilder();
857         boolean isFirst = true;
858         for (final T item : list) {
859             buffer.append(stringNumericOperand(item.getOp(), isFirst));
860             buffer.append(item.getValue());
861             buffer.append(' ');
862             if (isFirst) {
863                 isFirst = false;
864             }
865         }
866         return buffer.toString();
867     }
868
869     protected static final <T extends NumericOneByteValue> String stringNumericOne(final List<T> list) {
870         final StringBuilder buffer = new StringBuilder();
871         boolean isFirst = true;
872         for (final T item : list) {
873             buffer.append(stringNumericOperand(item.getOp(), isFirst));
874             buffer.append(item.getValue());
875             buffer.append(' ');
876             if (isFirst) {
877                 isFirst = false;
878             }
879         }
880         return buffer.toString();
881     }
882
883     protected static final String stringNumericOperand(final NumericOperand op, final boolean isFirst) {
884         final StringBuilder buffer = new StringBuilder();
885         if (!op.isAndBit() && !isFirst) {
886             buffer.append("or ");
887         }
888         if (op.isAndBit()) {
889             buffer.append("and ");
890         }
891         if (op.isLessThan() && op.isEquals()) {
892             buffer.append("is less than or equal to ");
893             return buffer.toString();
894         } else if (op.isGreaterThan() && op.isEquals()) {
895             buffer.append("is greater than or equal to ");
896             return buffer.toString();
897         }
898         if (op.isEquals()) {
899             buffer.append("equals to ");
900         }
901         if (op.isLessThan()) {
902             buffer.append("is less than ");
903         }
904         if (op.isGreaterThan()) {
905             buffer.append("is greater than ");
906         }
907         return buffer.toString();
908     }
909
910     private static String stringTcpFlags(final List<TcpFlags> flags) {
911         final StringBuilder buffer = new StringBuilder("where TCP flags ");
912         boolean isFirst = true;
913         for (final TcpFlags item : flags) {
914             buffer.append(stringBitmaskOperand(item.getOp(), isFirst));
915             buffer.append(item.getValue());
916             buffer.append(' ');
917             if (isFirst) {
918                 isFirst = false;
919             }
920         }
921         return buffer.toString();
922     }
923
924     private static String stringBitmaskOperand(final BitmaskOperand op, final boolean isFirst) {
925         final StringBuilder buffer = new StringBuilder();
926         if (!op.isAndBit() && !isFirst) {
927             buffer.append("or ");
928         }
929         if (op.isAndBit()) {
930             buffer.append("and ");
931         }
932         if (op.isMatch()) {
933             buffer.append("does ");
934             if (op.isNot()) {
935                 buffer.append("not ");
936             }
937             buffer.append("match ");
938         } else if (op.isNot()) {
939             buffer.append("is not ");
940         }
941         return buffer.toString();
942     }
943
944     private static String stringDscp(final List<Dscps> dscps) {
945         final StringBuilder buffer = new StringBuilder("where DSCP ");
946         boolean isFirst = true;
947         for (final Dscps item : dscps) {
948             buffer.append(stringNumericOperand(item.getOp(), isFirst));
949             buffer.append(item.getValue().getValue());
950             buffer.append(' ');
951             if (isFirst) {
952                 isFirst = false;
953             }
954         }
955         return buffer.toString();
956     }
957
958     private static String stringFragment(final List<Fragments> fragments) {
959         final StringBuilder buffer = new StringBuilder("where fragment ");
960         boolean isFirst = true;
961         for (final Fragments item : fragments) {
962             buffer.append(stringBitmaskOperand(item.getOp(), isFirst));
963             buffer.append(stringFragment(item.getValue()));
964             if (isFirst) {
965                 isFirst = false;
966             }
967         }
968         return buffer.toString();
969     }
970
971     private static String stringFragment(final Fragment fragment) {
972         final StringBuilder buffer = new StringBuilder();
973         if (fragment.isDoNot()) {
974             buffer.append("'DO NOT' ");
975         }
976         if (fragment.isFirst()) {
977             buffer.append("'IS FIRST' ");
978         }
979         if (fragment.isLast()) {
980             buffer.append("'IS LAST' ");
981         }
982         if (fragment.isIsA()) {
983             buffer.append("'IS A' ");
984         }
985         return buffer.toString();
986     }
987 }