Bug 5503 - remove package cyclic dependency in BGP-FS
[bgpcep.git] / bgp / flowspec / src / main / java / org / opendaylight / protocol / bgp / flowspec / AbstractFlowspecNlriParser.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.Function;
12 import com.google.common.base.Joiner;
13 import com.google.common.base.Optional;
14 import com.google.common.base.Preconditions;
15 import com.google.common.collect.Iterables;
16 import io.netty.buffer.ByteBuf;
17 import io.netty.buffer.Unpooled;
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.Set;
21 import org.opendaylight.protocol.bgp.flowspec.handlers.BitmaskOperandParser;
22 import org.opendaylight.protocol.bgp.flowspec.handlers.NumericOneByteOperandParser;
23 import org.opendaylight.protocol.bgp.flowspec.handlers.NumericTwoByteOperandParser;
24 import org.opendaylight.protocol.bgp.parser.BGPParsingException;
25 import org.opendaylight.protocol.bgp.parser.spi.NlriParser;
26 import org.opendaylight.protocol.bgp.parser.spi.NlriSerializer;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.Dscp;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.Fragment;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.Flowspec;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.FlowspecBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.FlowspecType;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.DestinationPortCase;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.DestinationPortCaseBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.DscpCase;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.DscpCaseBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.FragmentCase;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.FragmentCaseBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.IcmpCodeCase;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.IcmpCodeCaseBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.IcmpTypeCase;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.IcmpTypeCaseBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.PacketLengthCase;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.PacketLengthCaseBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.PortCase;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.PortCaseBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.SourcePortCase;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.SourcePortCaseBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.TcpFlagsCase;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.TcpFlagsCaseBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.destination.port._case.DestinationPorts;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.destination.port._case.DestinationPortsBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.dscp._case.Dscps;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.dscp._case.DscpsBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.fragment._case.Fragments;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.fragment._case.FragmentsBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.icmp.code._case.Codes;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.icmp.code._case.CodesBuilder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.icmp.type._case.Types;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.icmp.type._case.TypesBuilder;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.packet.length._case.PacketLengths;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.packet.length._case.PacketLengthsBuilder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.port._case.Ports;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.port._case.PortsBuilder;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.source.port._case.SourcePorts;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.source.port._case.SourcePortsBuilder;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.tcp.flags._case.TcpFlags;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.tcp.flags._case.TcpFlagsBuilder;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.ipv4.flowspec.flowspec.type.DestinationPrefixCase;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.ipv4.flowspec.flowspec.type.SourcePrefixCase;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.Attributes;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.Attributes1;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.Attributes2;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.destination.DestinationType;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.MpReachNlriBuilder;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.MpUnreachNlriBuilder;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.mp.reach.nlri.AdvertizedRoutesBuilder;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
78 import org.opendaylight.yangtools.yang.binding.DataObject;
79 import org.opendaylight.yangtools.yang.common.QName;
80 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
81 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
82 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
83 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
84 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
85 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
86 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
87
88 public abstract class AbstractFlowspecNlriParser implements NlriParser, NlriSerializer {
89
90     @VisibleForTesting
91     static final NodeIdentifier FLOWSPEC_NID = new NodeIdentifier(Flowspec.QNAME);
92     @VisibleForTesting
93     protected static final NodeIdentifier FLOWSPEC_TYPE_NID = new NodeIdentifier(FlowspecType.QNAME);
94     @VisibleForTesting
95     static final NodeIdentifier DEST_PREFIX_NID = new NodeIdentifier(QName.create(DestinationPrefixCase.QNAME, "destination-prefix").intern());
96     @VisibleForTesting
97     static final NodeIdentifier SOURCE_PREFIX_NID = new NodeIdentifier(QName.create(SourcePrefixCase.QNAME, "source-prefix").intern());
98     @VisibleForTesting
99     static final NodeIdentifier PORTS_NID = new NodeIdentifier(Ports.QNAME);
100     @VisibleForTesting
101     static final NodeIdentifier DEST_PORT_NID = new NodeIdentifier(DestinationPorts.QNAME);
102     @VisibleForTesting
103     static final NodeIdentifier SOURCE_PORT_NID = new NodeIdentifier(SourcePorts.QNAME);
104     @VisibleForTesting
105     static final NodeIdentifier ICMP_TYPE_NID = new NodeIdentifier(Types.QNAME);
106     @VisibleForTesting
107     static final NodeIdentifier ICMP_CODE_NID = new NodeIdentifier(Codes.QNAME);
108     @VisibleForTesting
109     static final NodeIdentifier TCP_FLAGS_NID = new NodeIdentifier(TcpFlags.QNAME);
110     @VisibleForTesting
111     static final NodeIdentifier PACKET_LENGTHS_NID = new NodeIdentifier(PacketLengths.QNAME);
112     @VisibleForTesting
113     static final NodeIdentifier DSCP_NID = new NodeIdentifier(Dscps.QNAME);
114     @VisibleForTesting
115     static final NodeIdentifier FRAGMENT_NID = new NodeIdentifier(Fragments.QNAME);
116     @VisibleForTesting
117     static final NodeIdentifier OP_NID = new NodeIdentifier(QName.create("urn:opendaylight:params:xml:ns:yang:bgp-flowspec","2015-08-07","op"));
118     @VisibleForTesting
119     static final NodeIdentifier VALUE_NID = new NodeIdentifier(QName.create("urn:opendaylight:params:xml:ns:yang:bgp-flowspec","2015-08-07","value"));
120
121     protected static final int NLRI_LENGTH = 1;
122     protected static final int NLRI_LENGTH_EXTENDED = 2;
123
124     protected SimpleFlowspecTypeRegistry flowspecTypeRegistry;
125
126     /**
127      * Add this constant to length value to achieve all ones in the leftmost nibble.
128      */
129     private static final int LENGTH_MAGIC = 61440;
130     private static final int MAX_NLRI_LENGTH = 4095;
131     private static final int MAX_NLRI_LENGTH_ONE_BYTE = 240;
132
133     @VisibleForTesting
134     static final String DO_NOT_VALUE = "do-not";
135     @VisibleForTesting
136     static final String FIRST_VALUE = "first";
137     @VisibleForTesting
138     static final String LAST_VALUE = "last";
139     @VisibleForTesting
140     static final String IS_A_VALUE = "is-a";
141
142     private static final String FLOW_SEPARATOR = " AND ";
143
144     protected abstract void serializeMpReachNlri(final Attributes1 pathAttributes, final ByteBuf byteAggregator);
145
146     protected abstract void serializeMpUnreachNlri(final Attributes2 pathAttributes, final ByteBuf byteAggregator);
147
148     public abstract void extractSpecificFlowspec(final ChoiceNode fsType, final FlowspecBuilder fsBuilder);
149
150     protected abstract void stringSpecificFSNlriType(final FlowspecType value, final StringBuilder buffer);
151
152     abstract DestinationType createWithdrawnDestinationType(final List<Flowspec> dst);
153
154     abstract DestinationType createAdvertizedRoutesDestinationType(final List<Flowspec> dst);
155
156     @Override
157     public final void serializeAttribute(final DataObject attribute, final ByteBuf byteAggregator) {
158         Preconditions.checkArgument(attribute instanceof Attributes, "Attribute parameter is not a PathAttribute object.");
159         final Attributes pathAttributes = (Attributes) attribute;
160         final Attributes1 pathAttributes1 = pathAttributes.getAugmentation(Attributes1.class);
161         final Attributes2 pathAttributes2 = pathAttributes.getAugmentation(Attributes2.class);
162         serializeMpReachNlri(pathAttributes1, byteAggregator);
163         serializeMpUnreachNlri(pathAttributes2, byteAggregator);
164     }
165
166     @Override
167     public final void parseNlri(final ByteBuf nlri, final MpUnreachNlriBuilder builder) throws BGPParsingException {
168         if (!nlri.isReadable()) {
169             return;
170         }
171         final List<Flowspec> dst = parseNlri(nlri);
172
173         builder.setWithdrawnRoutes(new WithdrawnRoutesBuilder().setDestinationType(createWithdrawnDestinationType(dst)).build());
174     }
175
176     @Override
177     public final void parseNlri(final ByteBuf nlri, final MpReachNlriBuilder builder) throws BGPParsingException {
178         if (!nlri.isReadable()) {
179             return;
180         }
181         final List<Flowspec> dst = parseNlri(nlri);
182         builder.setAdvertizedRoutes(new AdvertizedRoutesBuilder().setDestinationType(createAdvertizedRoutesDestinationType(dst)).build());
183     }
184
185     /**
186      * Serializes Flowspec NLRI to ByteBuf.
187      *
188      * @param flows flowspec NLRI to be serialized
189      * @param buffer where flowspec NLRI will be serialized
190      */
191     public final void serializeNlri(final List<Flowspec> flows, final ByteBuf buffer) {
192         final ByteBuf nlriByteBuf = Unpooled.buffer();
193         for (final Flowspec flow : flows) {
194             this.flowspecTypeRegistry.serializeFlowspecType(flow.getFlowspecType(), nlriByteBuf);
195         }
196         Preconditions.checkState(nlriByteBuf.readableBytes() <= MAX_NLRI_LENGTH, "Maximum length of Flowspec NLRI reached.");
197         if (nlriByteBuf.readableBytes() <= MAX_NLRI_LENGTH_ONE_BYTE) {
198             buffer.writeByte(nlriByteBuf.readableBytes());
199         } else {
200             buffer.writeShort(nlriByteBuf.readableBytes() + LENGTH_MAGIC);
201         }
202         buffer.writeBytes(nlriByteBuf);
203     }
204
205     public final String stringNlri(final DataContainerNode<?> flowspec) {
206         return stringNlri(extractFlowspec(flowspec));
207     }
208
209     public final List<Flowspec> extractFlowspec(final DataContainerNode<?> route) {
210         final List<Flowspec> fsList = new ArrayList<>();
211         final Optional<DataContainerChild<? extends PathArgument, ?>> flowspecs = route.getChild(FLOWSPEC_NID);
212         if (flowspecs.isPresent()) {
213             for (final UnkeyedListEntryNode flowspec : ((UnkeyedListNode)flowspecs.get()).getValue()) {
214                 final FlowspecBuilder fsBuilder = new FlowspecBuilder();
215                 final Optional<DataContainerChild<?, ?>> flowspecType = flowspec.getChild(FLOWSPEC_TYPE_NID);
216                 if (flowspecType.isPresent()) {
217                     final ChoiceNode fsType = (ChoiceNode) flowspecType.get();
218                     processFlowspecType(fsType, fsBuilder);
219                 }
220                 fsList.add(fsBuilder.build());
221             }
222         }
223         return fsList;
224     }
225
226     private void processFlowspecType(final ChoiceNode fsType, final FlowspecBuilder fsBuilder) {
227         if (fsType.getChild(PORTS_NID).isPresent()) {
228             fsBuilder.setFlowspecType(new PortCaseBuilder().setPorts(createPorts((UnkeyedListNode) fsType.getChild(PORTS_NID).get())).build());
229         } else if (fsType.getChild(DEST_PORT_NID).isPresent()) {
230             fsBuilder.setFlowspecType(new DestinationPortCaseBuilder().setDestinationPorts(createDestinationPorts((UnkeyedListNode) fsType.getChild(DEST_PORT_NID).get())).build());
231         } else if (fsType.getChild(SOURCE_PORT_NID).isPresent()) {
232             fsBuilder.setFlowspecType(new SourcePortCaseBuilder().setSourcePorts(createSourcePorts((UnkeyedListNode) fsType.getChild(SOURCE_PORT_NID).get())).build());
233         } else if (fsType.getChild(ICMP_TYPE_NID).isPresent()) {
234             fsBuilder.setFlowspecType(new IcmpTypeCaseBuilder().setTypes(createTypes((UnkeyedListNode) fsType.getChild(ICMP_TYPE_NID).get())).build());
235         } else if (fsType.getChild(ICMP_CODE_NID).isPresent()) {
236             fsBuilder.setFlowspecType(new IcmpCodeCaseBuilder().setCodes(createCodes((UnkeyedListNode) fsType.getChild(ICMP_CODE_NID).get())).build());
237         } else if (fsType.getChild(TCP_FLAGS_NID).isPresent()) {
238             fsBuilder.setFlowspecType(new TcpFlagsCaseBuilder().setTcpFlags(createTcpFlags((UnkeyedListNode) fsType.getChild(TCP_FLAGS_NID).get())).build());
239         } else if (fsType.getChild(PACKET_LENGTHS_NID).isPresent()) {
240             fsBuilder.setFlowspecType(new PacketLengthCaseBuilder().setPacketLengths(createPacketLengths((UnkeyedListNode) fsType.getChild(PACKET_LENGTHS_NID).get())).build());
241         } else if (fsType.getChild(DSCP_NID).isPresent()) {
242             fsBuilder.setFlowspecType(new DscpCaseBuilder().setDscps(createDscpsLengths((UnkeyedListNode) fsType.getChild(DSCP_NID).get())).build());
243         } else if (fsType.getChild(FRAGMENT_NID).isPresent()) {
244             fsBuilder.setFlowspecType(new FragmentCaseBuilder().setFragments(createFragments((UnkeyedListNode) fsType.getChild(FRAGMENT_NID).get())).build());
245         } else {
246             extractSpecificFlowspec(fsType, fsBuilder);
247         }
248     }
249
250     private static List<Ports> createPorts(final UnkeyedListNode portsData) {
251         final List<Ports> ports = new ArrayList<>();
252
253         for (final UnkeyedListEntryNode node : portsData.getValue()) {
254             final PortsBuilder portsBuilder = new PortsBuilder();
255             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
256             if (opValue.isPresent()) {
257                 portsBuilder.setOp(NumericTwoByteOperandParser.INSTANCE.create((Set<String>) opValue.get().getValue()));
258             }
259             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
260             if (valueNode.isPresent()) {
261                 portsBuilder.setValue((Integer) valueNode.get().getValue());
262             }
263             ports.add(portsBuilder.build());
264         }
265
266         return ports;
267     }
268
269     private static List<DestinationPorts> createDestinationPorts(final UnkeyedListNode destinationPortsData) {
270         final List<DestinationPorts> destinationPorts = new ArrayList<>();
271
272         for (final UnkeyedListEntryNode node : destinationPortsData.getValue()) {
273             final DestinationPortsBuilder destPortsBuilder = new DestinationPortsBuilder();
274             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
275             if (opValue.isPresent()) {
276                 destPortsBuilder.setOp(NumericTwoByteOperandParser.INSTANCE.create((Set<String>) opValue.get().getValue()));
277             }
278             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
279             if (valueNode.isPresent()) {
280                 destPortsBuilder.setValue((Integer) valueNode.get().getValue());
281             }
282             destinationPorts.add(destPortsBuilder.build());
283         }
284
285         return destinationPorts;
286     }
287
288     private static List<SourcePorts> createSourcePorts(final UnkeyedListNode sourcePortsData) {
289         final List<SourcePorts> sourcePorts = new ArrayList<>();
290
291         for (final UnkeyedListEntryNode node : sourcePortsData.getValue()) {
292             final SourcePortsBuilder sourcePortsBuilder = new SourcePortsBuilder();
293             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
294             if (opValue.isPresent()) {
295                 sourcePortsBuilder.setOp(NumericTwoByteOperandParser.INSTANCE.create((Set<String>) opValue.get().getValue()));
296             }
297             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
298             if (valueNode.isPresent()) {
299                 sourcePortsBuilder.setValue((Integer) valueNode.get().getValue());
300             }
301             sourcePorts.add(sourcePortsBuilder.build());
302         }
303
304         return sourcePorts;
305     }
306
307     private static List<Types> createTypes(final UnkeyedListNode typesData) {
308         final List<Types> types = new ArrayList<>();
309
310         for (final UnkeyedListEntryNode node : typesData.getValue()) {
311             final TypesBuilder typesBuilder = new TypesBuilder();
312             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
313             if (opValue.isPresent()) {
314                 typesBuilder.setOp(NumericOneByteOperandParser.INSTANCE.create((Set<String>) opValue.get().getValue()));
315             }
316             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
317             if (valueNode.isPresent()) {
318                 typesBuilder.setValue((Short) valueNode.get().getValue());
319             }
320             types.add(typesBuilder.build());
321         }
322
323         return types;
324     }
325
326     private static List<Codes> createCodes(final UnkeyedListNode codesData) {
327         final List<Codes> codes = new ArrayList<>();
328
329         for (final UnkeyedListEntryNode node : codesData.getValue()) {
330             final CodesBuilder codesBuilder = new CodesBuilder();
331             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
332             if (opValue.isPresent()) {
333                 codesBuilder.setOp(NumericOneByteOperandParser.INSTANCE.create((Set<String>) opValue.get().getValue()));
334             }
335             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
336             if (valueNode.isPresent()) {
337                 codesBuilder.setValue((Short) valueNode.get().getValue());
338             }
339             codes.add(codesBuilder.build());
340         }
341
342         return codes;
343     }
344
345     private static List<TcpFlags> createTcpFlags(final UnkeyedListNode tcpFlagsData) {
346         final List<TcpFlags> tcpFlags = new ArrayList<>();
347
348         for (final UnkeyedListEntryNode node : tcpFlagsData.getValue()) {
349             final TcpFlagsBuilder tcpFlagsBuilder = new TcpFlagsBuilder();
350             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
351             if (opValue.isPresent()) {
352                 tcpFlagsBuilder.setOp(BitmaskOperandParser.INSTANCE.create((Set<String>) opValue.get().getValue()));
353             }
354             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
355             if (valueNode.isPresent()) {
356                 tcpFlagsBuilder.setValue((Integer) valueNode.get().getValue());
357             }
358             tcpFlags.add(tcpFlagsBuilder.build());
359         }
360
361         return tcpFlags;
362     }
363
364     private static List<PacketLengths> createPacketLengths(final UnkeyedListNode packetLengthsData) {
365         final List<PacketLengths> packetLengths = new ArrayList<>();
366
367         for (final UnkeyedListEntryNode node : packetLengthsData.getValue()) {
368             final PacketLengthsBuilder packetLengthsBuilder = new PacketLengthsBuilder();
369             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
370             if (opValue.isPresent()) {
371                 packetLengthsBuilder.setOp(NumericTwoByteOperandParser.INSTANCE.create((Set<String>) opValue.get().getValue()));
372             }
373             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
374             if (valueNode.isPresent()) {
375                 packetLengthsBuilder.setValue((Integer) valueNode.get().getValue());
376             }
377             packetLengths.add(packetLengthsBuilder.build());
378         }
379
380         return packetLengths;
381     }
382
383     private static List<Dscps> createDscpsLengths(final UnkeyedListNode dscpLengthsData) {
384         final List<Dscps> dscpsLengths = new ArrayList<>();
385
386         for (final UnkeyedListEntryNode node : dscpLengthsData.getValue()) {
387             final DscpsBuilder dscpsLengthsBuilder = new DscpsBuilder();
388             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
389             if (opValue.isPresent()) {
390                 dscpsLengthsBuilder.setOp(NumericOneByteOperandParser.INSTANCE.create((Set<String>) opValue.get().getValue()));
391             }
392             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
393             if (valueNode.isPresent()) {
394                 dscpsLengthsBuilder.setValue(new Dscp((Short) valueNode.get().getValue()));
395             }
396             dscpsLengths.add(dscpsLengthsBuilder.build());
397         }
398
399         return dscpsLengths;
400     }
401
402     private static List<Fragments> createFragments(final UnkeyedListNode fragmentsData) {
403         final List<Fragments> fragments = new ArrayList<>();
404
405         for (final UnkeyedListEntryNode node : fragmentsData.getValue()) {
406             final FragmentsBuilder fragmentsBuilder = new FragmentsBuilder();
407             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
408             if (opValue.isPresent()) {
409                 fragmentsBuilder.setOp(BitmaskOperandParser.INSTANCE.create((Set<String>) opValue.get().getValue()));
410             }
411             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
412             if (valueNode.isPresent()) {
413                 fragmentsBuilder.setValue(createFragment((Set<String>) valueNode.get().getValue()));
414             }
415             fragments.add(fragmentsBuilder.build());
416         }
417
418         return fragments;
419     }
420
421     private static Fragment createFragment(final Set<String> data) {
422         return new Fragment(data.contains(DO_NOT_VALUE), data.contains(FIRST_VALUE), data.contains(IS_A_VALUE), data.contains(LAST_VALUE));
423     }
424
425     final String stringNlri(final List<Flowspec> flows) {
426         final StringBuilder buffer = new StringBuilder("all packets ");
427         final Joiner joiner = Joiner.on(FLOW_SEPARATOR);
428         joiner.appendTo(buffer, Iterables.transform(flows, new Function<Flowspec, String>() {
429             @Override
430             public String apply(final Flowspec input) {
431                 return encodeFlow(input);
432             }
433         }));
434         return buffer.toString().replace("  ", " ");
435     }
436
437     @VisibleForTesting
438     final String encodeFlow(final Flowspec flow) {
439         final StringBuilder buffer = new StringBuilder();
440         final FlowspecType value = flow.getFlowspecType();
441         if (value instanceof PortCase) {
442             buffer.append("where port ");
443             buffer.append(NumericTwoByteOperandParser.INSTANCE.toString(((PortCase) value).getPorts()));
444         } else if (value instanceof DestinationPortCase) {
445             buffer.append("where destination port ");
446             buffer.append(NumericTwoByteOperandParser.INSTANCE.toString(((DestinationPortCase) value).getDestinationPorts()));
447         } else if (value instanceof SourcePortCase) {
448             buffer.append("where source port ");
449             buffer.append(NumericTwoByteOperandParser.INSTANCE.toString(((SourcePortCase) value).getSourcePorts()));
450         } else if (value instanceof IcmpTypeCase) {
451             buffer.append("where ICMP type ");
452             buffer.append(NumericOneByteOperandParser.INSTANCE.toString(((IcmpTypeCase) value).getTypes()));
453         } else if (value instanceof IcmpCodeCase) {
454             buffer.append("where ICMP code ");
455             buffer.append(NumericOneByteOperandParser.INSTANCE.toString(((IcmpCodeCase) value).getCodes()));
456         } else if (value instanceof TcpFlagsCase) {
457             buffer.append(stringTcpFlags(((TcpFlagsCase) value).getTcpFlags()));
458         } else if (value instanceof PacketLengthCase) {
459             buffer.append("where packet length ");
460             buffer.append(NumericTwoByteOperandParser.INSTANCE.toString(((PacketLengthCase) value).getPacketLengths()));
461         } else if (value instanceof DscpCase) {
462             buffer.append(stringDscp(((DscpCase) value).getDscps()));
463         } else if (value instanceof FragmentCase) {
464             buffer.append(stringFragment(((FragmentCase) value).getFragments()));
465         } else {
466             stringSpecificFSNlriType(value, buffer);
467         }
468         return buffer.toString();
469     }
470
471     private static String stringTcpFlags(final List<TcpFlags> flags) {
472         final StringBuilder buffer = new StringBuilder("where TCP flags ");
473         boolean isFirst = true;
474         for (final TcpFlags item : flags) {
475             buffer.append(BitmaskOperandParser.INSTANCE.toString(item.getOp(), isFirst));
476             buffer.append(item.getValue());
477             buffer.append(' ');
478             if (isFirst) {
479                 isFirst = false;
480             }
481         }
482         return buffer.toString();
483     }
484
485     private static String stringDscp(final List<Dscps> dscps) {
486         final StringBuilder buffer = new StringBuilder("where DSCP ");
487         boolean isFirst = true;
488         for (final Dscps item : dscps) {
489             buffer.append(NumericOneByteOperandParser.INSTANCE.toString(item.getOp(), isFirst));
490             buffer.append(item.getValue().getValue());
491             buffer.append(' ');
492             if (isFirst) {
493                 isFirst = false;
494             }
495         }
496         return buffer.toString();
497     }
498
499     private static String stringFragment(final List<Fragments> fragments) {
500         final StringBuilder buffer = new StringBuilder("where fragment ");
501         boolean isFirst = true;
502         for (final Fragments item : fragments) {
503             buffer.append(BitmaskOperandParser.INSTANCE.toString(item.getOp(), isFirst));
504             buffer.append(stringFragment(item.getValue()));
505             if (isFirst) {
506                 isFirst = false;
507             }
508         }
509         return buffer.toString();
510     }
511
512     private static String stringFragment(final Fragment fragment) {
513         final StringBuilder buffer = new StringBuilder();
514         if (fragment.isDoNot()) {
515             buffer.append("'DO NOT' ");
516         }
517         if (fragment.isFirst()) {
518             buffer.append("'IS FIRST' ");
519         }
520         if (fragment.isLast()) {
521             buffer.append("'IS LAST' ");
522         }
523         if (fragment.isIsA()) {
524             buffer.append("'IS A' ");
525         }
526         return buffer.toString();
527     }
528
529     /**
530      * Parses Flowspec NLRI into list of Flowspec.
531      *
532      * @param nlri byte representation of NLRI which will be parsed
533      * @return list of Flowspec
534      */
535     public final List<Flowspec> parseNlri(final ByteBuf nlri) throws BGPParsingException {
536         if (!nlri.isReadable()) {
537             return null;
538         }
539         final List<Flowspec> fss = new ArrayList<>();
540
541         // length field can be one or two bytes (if needed)
542         // check the length of nlri to see how many bytes we can skip
543         final int length = nlri.readableBytes();
544         nlri.skipBytes(length > MAX_NLRI_LENGTH_ONE_BYTE ? NLRI_LENGTH_EXTENDED : NLRI_LENGTH);
545
546         while (nlri.isReadable()) {
547             final FlowspecBuilder builder = new FlowspecBuilder();
548             builder.setFlowspecType(this.flowspecTypeRegistry.parseFlowspecType(nlri));
549             fss.add(builder.build());
550         }
551         return fss;
552     }
553
554 }