Code clean up
[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 static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.VisibleForTesting;
13 import com.google.common.base.Joiner;
14 import com.google.common.base.Preconditions;
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.Optional;
20 import java.util.Set;
21 import java.util.stream.Collectors;
22 import javax.annotation.Nonnull;
23 import javax.annotation.Nullable;
24 import org.opendaylight.protocol.bgp.flowspec.handlers.BitmaskOperandParser;
25 import org.opendaylight.protocol.bgp.flowspec.handlers.NumericOneByteOperandParser;
26 import org.opendaylight.protocol.bgp.flowspec.handlers.NumericTwoByteOperandParser;
27 import org.opendaylight.protocol.bgp.parser.BGPParsingException;
28 import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
29 import org.opendaylight.protocol.bgp.parser.spi.MultiPathSupportUtil;
30 import org.opendaylight.protocol.bgp.parser.spi.NlriParser;
31 import org.opendaylight.protocol.bgp.parser.spi.NlriSerializer;
32 import org.opendaylight.protocol.bgp.parser.spi.PathIdUtil;
33 import org.opendaylight.protocol.bgp.parser.spi.PeerSpecificParserConstraint;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.Dscp;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.Fragment;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.Flowspec;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.FlowspecBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.FlowspecType;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.DestinationPortCase;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.DestinationPortCaseBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.DscpCase;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.DscpCaseBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.FragmentCase;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.FragmentCaseBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.IcmpCodeCase;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.IcmpCodeCaseBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.IcmpTypeCase;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.IcmpTypeCaseBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.PacketLengthCase;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.PacketLengthCaseBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.PortCase;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.PortCaseBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.SourcePortCase;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.SourcePortCaseBuilder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.TcpFlagsCase;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.TcpFlagsCaseBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.destination.port._case.DestinationPorts;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.destination.port._case.DestinationPortsBuilder;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.dscp._case.Dscps;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.dscp._case.DscpsBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.fragment._case.Fragments;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.fragment._case.FragmentsBuilder;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.icmp.code._case.Codes;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.icmp.code._case.CodesBuilder;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.icmp.type._case.Types;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.icmp.type._case.TypesBuilder;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.packet.length._case.PacketLengths;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.packet.length._case.PacketLengthsBuilder;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.port._case.Ports;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.port._case.PortsBuilder;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.source.port._case.SourcePorts;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.source.port._case.SourcePortsBuilder;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.tcp.flags._case.TcpFlags;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.flowspec.flowspec.type.tcp.flags._case.TcpFlagsBuilder;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.group.ipv4.flowspec.flowspec.type.DestinationPrefixCase;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev180329.flowspec.destination.group.ipv4.flowspec.flowspec.type.SourcePrefixCase;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.PathId;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.Attributes;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.Attributes1;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.Attributes2;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.destination.DestinationType;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlriBuilder;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpUnreachNlriBuilder;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.reach.nlri.AdvertizedRoutes;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.reach.nlri.AdvertizedRoutesBuilder;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.unreach.nlri.WithdrawnRoutes;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.AddressFamily;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.SubsequentAddressFamily;
90 import org.opendaylight.yangtools.yang.binding.DataObject;
91 import org.opendaylight.yangtools.yang.common.QName;
92 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
93 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
94 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
95 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
96 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
97 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
98 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
99 import org.slf4j.Logger;
100 import org.slf4j.LoggerFactory;
101
102 public abstract class AbstractFlowspecNlriParser implements NlriParser, NlriSerializer {
103     private static final Logger LOG = LoggerFactory.getLogger(AbstractFlowspecNlriParser.class);
104
105     @VisibleForTesting
106     static final NodeIdentifier FLOWSPEC_NID = new NodeIdentifier(Flowspec.QNAME);
107     @VisibleForTesting
108     static final NodeIdentifier FLOWSPEC_TYPE_NID = new NodeIdentifier(FlowspecType.QNAME);
109     public static final NodeIdentifier DEST_PREFIX_NID
110             = new NodeIdentifier(QName.create(DestinationPrefixCase.QNAME, "destination-prefix").intern());
111     public static final NodeIdentifier SOURCE_PREFIX_NID
112             = new NodeIdentifier(QName.create(SourcePrefixCase.QNAME, "source-prefix").intern());
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     public static final NodeIdentifier OP_NID
133             = new NodeIdentifier(QName.create(Flowspec.QNAME.getModule(), "op"));
134     @VisibleForTesting
135     public static final NodeIdentifier VALUE_NID
136             = new NodeIdentifier(QName.create(Flowspec.QNAME.getModule(), "value"));
137
138     protected final SimpleFlowspecTypeRegistry flowspecTypeRegistry;
139
140     /**
141      * Add this constant to length value to achieve all ones in the leftmost nibble.
142      */
143     private static final int LENGTH_MAGIC = 61440;
144     private static final int MAX_NLRI_LENGTH = 0xFFF;
145     private static final int MAX_NLRI_LENGTH_ONE_BYTE = 0xF0;
146
147     @VisibleForTesting
148     static final String DO_NOT_VALUE = "do-not";
149     @VisibleForTesting
150     static final String FIRST_VALUE = "first";
151     @VisibleForTesting
152     static final String LAST_VALUE = "last";
153     @VisibleForTesting
154     static final String IS_A_VALUE = "is-a";
155
156     private static final String FLOW_SEPARATOR = " AND ";
157
158     protected AbstractFlowspecNlriParser(final SimpleFlowspecTypeRegistry flowspecTypeRegistry) {
159         this.flowspecTypeRegistry = requireNonNull(flowspecTypeRegistry);
160     }
161
162     protected abstract void serializeMpReachNlri(final DestinationType dstType, final ByteBuf byteAggregator);
163
164     protected abstract void serializeMpUnreachNlri(final DestinationType dstType, final ByteBuf byteAggregator);
165
166     public abstract void extractSpecificFlowspec(final ChoiceNode fsType, final FlowspecBuilder fsBuilder);
167
168     protected abstract void stringSpecificFSNlriType(final FlowspecType value, final StringBuilder buffer);
169
170     /**
171      * Create withdrawn destination type
172      *
173      * @param nlriFields a list of NLRI fields to be included in the destination type
174      * @param pathId     associated path id with given NLRI
175      * @return created destination type
176      */
177     public abstract DestinationType createWithdrawnDestinationType(@Nonnull Object[] nlriFields,
178             @Nullable PathId pathId);
179
180     /**
181      * Create advertized destination type
182      *
183      * @param nlriFields a list of NLRI fields to be included in the destination type
184      * @param pathId     associated path id with given NLRI
185      * @return created destination type
186      */
187     public abstract DestinationType createAdvertizedRoutesDestinationType(@Nonnull Object[] nlriFields, @Nullable PathId pathId);
188
189     @Override
190     public final void serializeAttribute(final DataObject attribute, final ByteBuf byteAggregator) {
191         Preconditions.checkArgument(attribute instanceof Attributes,
192                 "Attribute parameter is not a PathAttribute object.");
193         final Attributes pathAttributes = (Attributes) attribute;
194         final Attributes1 pathAttributes1 = pathAttributes.getAugmentation(Attributes1.class);
195         final Attributes2 pathAttributes2 = pathAttributes.getAugmentation(Attributes2.class);
196
197         if (pathAttributes1 != null) {
198             final AdvertizedRoutes routes = pathAttributes1.getMpReachNlri().getAdvertizedRoutes();
199             if (routes != null) {
200                 serializeMpReachNlri(routes.getDestinationType(), byteAggregator);
201             }
202         }
203
204         if (pathAttributes2 != null) {
205             final WithdrawnRoutes routes = pathAttributes2.getMpUnreachNlri().getWithdrawnRoutes();
206             if (routes != null) {
207                 serializeMpUnreachNlri(routes.getDestinationType(), byteAggregator);
208             }
209         }
210     }
211
212     protected void serializeNlri(@Nonnull final Object[] nlriFields, @Nonnull final ByteBuf buffer) {
213         final List<Flowspec> flowspecList = (List<Flowspec>) nlriFields[0];
214         serializeNlri(flowspecList, buffer);
215     }
216
217     protected final void serializeNlri(final List<Flowspec> flowspecList, @Nonnull final ByteBuf buffer) {
218         if (flowspecList != null) {
219             for (final Flowspec flow : flowspecList) {
220                 this.flowspecTypeRegistry.serializeFlowspecType(flow.getFlowspecType(), buffer);
221             }
222         }
223     }
224
225     /**
226      * Serializes Flowspec NLRI to ByteBuf.
227      *
228      * @param nlriFields NLRI fields to be serialized
229      * @param pathId
230      * @param buffer     where flowspec NLRI will be serialized
231      */
232     protected final void serializeNlri(@Nonnull final Object[] nlriFields, @Nullable final PathId pathId,
233             @Nonnull final ByteBuf buffer) {
234         final ByteBuf nlriByteBuf = Unpooled.buffer();
235         PathIdUtil.writePathId(pathId, buffer);
236
237         serializeNlri(nlriFields, nlriByteBuf);
238
239         Preconditions.checkState(nlriByteBuf.readableBytes() <= MAX_NLRI_LENGTH,
240                 "Maximum length of Flowspec NLRI reached.");
241         if (nlriByteBuf.readableBytes() <= MAX_NLRI_LENGTH_ONE_BYTE) {
242             buffer.writeByte(nlriByteBuf.readableBytes());
243         } else {
244             buffer.writeShort(nlriByteBuf.readableBytes() + LENGTH_MAGIC);
245         }
246         buffer.writeBytes(nlriByteBuf);
247     }
248
249     public String stringNlri(final DataContainerNode<?> flowspec) {
250         return stringNlri(extractFlowspec(flowspec));
251     }
252
253     public final List<Flowspec> extractFlowspec(final DataContainerNode<?> route) {
254         requireNonNull(route, "Cannot extract flowspec from null route.");
255         final List<Flowspec> fsList = new ArrayList<>();
256         final Optional<DataContainerChild<? extends PathArgument, ?>> flowspecs = route.getChild(FLOWSPEC_NID);
257         if (flowspecs.isPresent()) {
258             for (final UnkeyedListEntryNode flowspec : ((UnkeyedListNode) flowspecs.get()).getValue()) {
259                 final FlowspecBuilder fsBuilder = new FlowspecBuilder();
260                 final Optional<DataContainerChild<?, ?>> flowspecType = flowspec.getChild(FLOWSPEC_TYPE_NID);
261                 if (flowspecType.isPresent()) {
262                     final ChoiceNode fsType = (ChoiceNode) flowspecType.get();
263                     processFlowspecType(fsType, fsBuilder);
264                 }
265                 fsList.add(fsBuilder.build());
266             }
267         }
268         return fsList;
269     }
270
271     private void processFlowspecType(final ChoiceNode fsType, final FlowspecBuilder fsBuilder) {
272         if (fsType.getChild(PORTS_NID).isPresent()) {
273             fsBuilder.setFlowspecType(new PortCaseBuilder()
274                     .setPorts(createPorts((UnkeyedListNode) fsType.getChild(PORTS_NID).get())).build());
275         } else if (fsType.getChild(DEST_PORT_NID).isPresent()) {
276             fsBuilder.setFlowspecType(new DestinationPortCaseBuilder()
277                     .setDestinationPorts(createDestinationPorts((UnkeyedListNode) fsType.getChild(DEST_PORT_NID).get()))
278                     .build());
279         } else if (fsType.getChild(SOURCE_PORT_NID).isPresent()) {
280             fsBuilder.setFlowspecType(new SourcePortCaseBuilder()
281                     .setSourcePorts(createSourcePorts((UnkeyedListNode) fsType.getChild(SOURCE_PORT_NID).get()))
282                     .build());
283         } else if (fsType.getChild(ICMP_TYPE_NID).isPresent()) {
284             fsBuilder.setFlowspecType(new IcmpTypeCaseBuilder()
285                     .setTypes(createTypes((UnkeyedListNode) fsType.getChild(ICMP_TYPE_NID).get())).build());
286         } else if (fsType.getChild(ICMP_CODE_NID).isPresent()) {
287             fsBuilder.setFlowspecType(new IcmpCodeCaseBuilder()
288                     .setCodes(createCodes((UnkeyedListNode) fsType.getChild(ICMP_CODE_NID).get())).build());
289         } else if (fsType.getChild(TCP_FLAGS_NID).isPresent()) {
290             fsBuilder.setFlowspecType(new TcpFlagsCaseBuilder()
291                     .setTcpFlags(createTcpFlags((UnkeyedListNode) fsType.getChild(TCP_FLAGS_NID).get())).build());
292         } else if (fsType.getChild(PACKET_LENGTHS_NID).isPresent()) {
293             fsBuilder.setFlowspecType(new PacketLengthCaseBuilder()
294                     .setPacketLengths(createPacketLengths((UnkeyedListNode) fsType.getChild(PACKET_LENGTHS_NID).get()))
295                     .build());
296         } else if (fsType.getChild(DSCP_NID).isPresent()) {
297             fsBuilder.setFlowspecType(new DscpCaseBuilder()
298                     .setDscps(createDscpsLengths((UnkeyedListNode) fsType.getChild(DSCP_NID).get())).build());
299         } else if (fsType.getChild(FRAGMENT_NID).isPresent()) {
300             fsBuilder.setFlowspecType(new FragmentCaseBuilder()
301                     .setFragments(createFragments((UnkeyedListNode) fsType.getChild(FRAGMENT_NID).get())).build());
302         } else {
303             extractSpecificFlowspec(fsType, fsBuilder);
304         }
305     }
306
307     private static List<Ports> createPorts(final UnkeyedListNode portsData) {
308         final List<Ports> ports = new ArrayList<>();
309
310         for (final UnkeyedListEntryNode node : portsData.getValue()) {
311             final PortsBuilder portsBuilder = new PortsBuilder();
312             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
313             opValue.ifPresent(dataContainerChild -> portsBuilder
314                     .setOp(NumericTwoByteOperandParser.INSTANCE.create((Set<String>) dataContainerChild.getValue())));
315             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
316             valueNode.ifPresent(dataContainerChild -> portsBuilder.setValue((Integer) dataContainerChild.getValue()));
317             ports.add(portsBuilder.build());
318         }
319
320         return ports;
321     }
322
323     private static List<DestinationPorts> createDestinationPorts(final UnkeyedListNode destinationPortsData) {
324         final List<DestinationPorts> destinationPorts = new ArrayList<>();
325
326         for (final UnkeyedListEntryNode node : destinationPortsData.getValue()) {
327             final DestinationPortsBuilder destPortsBuilder = new DestinationPortsBuilder();
328             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
329             opValue.ifPresent(dataContainerChild -> destPortsBuilder.setOp(NumericTwoByteOperandParser
330                     .INSTANCE.create((Set<String>) dataContainerChild.getValue())));
331             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
332             valueNode.ifPresent(dataContainerChild
333                     -> destPortsBuilder.setValue((Integer) dataContainerChild.getValue()));
334             destinationPorts.add(destPortsBuilder.build());
335         }
336
337         return destinationPorts;
338     }
339
340     private static List<SourcePorts> createSourcePorts(final UnkeyedListNode sourcePortsData) {
341         final List<SourcePorts> sourcePorts = new ArrayList<>();
342
343         for (final UnkeyedListEntryNode node : sourcePortsData.getValue()) {
344             final SourcePortsBuilder sourcePortsBuilder = new SourcePortsBuilder();
345             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
346             opValue.ifPresent(dataContainerChild -> sourcePortsBuilder.setOp(NumericTwoByteOperandParser
347                     .INSTANCE.create((Set<String>) dataContainerChild.getValue())));
348             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
349             valueNode.ifPresent(dataContainerChild
350                     -> sourcePortsBuilder.setValue((Integer) dataContainerChild.getValue()));
351             sourcePorts.add(sourcePortsBuilder.build());
352         }
353
354         return sourcePorts;
355     }
356
357     private static List<Types> createTypes(final UnkeyedListNode typesData) {
358         final List<Types> types = new ArrayList<>();
359
360         for (final UnkeyedListEntryNode node : typesData.getValue()) {
361             final TypesBuilder typesBuilder = new TypesBuilder();
362             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
363             opValue.ifPresent(dataContainerChild -> typesBuilder.setOp(NumericOneByteOperandParser
364                     .INSTANCE.create((Set<String>) dataContainerChild.getValue())));
365             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
366             valueNode.ifPresent(dataContainerChild -> typesBuilder.setValue((Short) dataContainerChild.getValue()));
367             types.add(typesBuilder.build());
368         }
369
370         return types;
371     }
372
373     private static List<Codes> createCodes(final UnkeyedListNode codesData) {
374         final List<Codes> codes = new ArrayList<>();
375
376         for (final UnkeyedListEntryNode node : codesData.getValue()) {
377             final CodesBuilder codesBuilder = new CodesBuilder();
378             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
379             opValue.ifPresent(dataContainerChild -> codesBuilder.setOp(NumericOneByteOperandParser
380                     .INSTANCE.create((Set<String>) dataContainerChild.getValue())));
381             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
382             valueNode.ifPresent(dataContainerChild -> codesBuilder.setValue((Short) dataContainerChild.getValue()));
383             codes.add(codesBuilder.build());
384         }
385
386         return codes;
387     }
388
389     private static List<TcpFlags> createTcpFlags(final UnkeyedListNode tcpFlagsData) {
390         final List<TcpFlags> tcpFlags = new ArrayList<>();
391
392         for (final UnkeyedListEntryNode node : tcpFlagsData.getValue()) {
393             final TcpFlagsBuilder tcpFlagsBuilder = new TcpFlagsBuilder();
394             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
395             opValue.ifPresent(dataContainerChild -> tcpFlagsBuilder
396                     .setOp(BitmaskOperandParser.INSTANCE.create((Set<String>) dataContainerChild.getValue())));
397             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
398             valueNode.ifPresent(dataContainerChild
399                     -> tcpFlagsBuilder.setValue((Integer) dataContainerChild.getValue()));
400             tcpFlags.add(tcpFlagsBuilder.build());
401         }
402
403         return tcpFlags;
404     }
405
406     private static List<PacketLengths> createPacketLengths(final UnkeyedListNode packetLengthsData) {
407         final List<PacketLengths> packetLengths = new ArrayList<>();
408
409         for (final UnkeyedListEntryNode node : packetLengthsData.getValue()) {
410             final PacketLengthsBuilder packetLengthsBuilder = new PacketLengthsBuilder();
411             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
412             opValue.ifPresent(dataContainerChild -> packetLengthsBuilder.setOp(NumericTwoByteOperandParser
413                     .INSTANCE.create((Set<String>) dataContainerChild.getValue())));
414             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
415             valueNode.ifPresent(dataContainerChild
416                     -> packetLengthsBuilder.setValue((Integer) dataContainerChild.getValue()));
417             packetLengths.add(packetLengthsBuilder.build());
418         }
419
420         return packetLengths;
421     }
422
423     private static List<Dscps> createDscpsLengths(final UnkeyedListNode dscpLengthsData) {
424         final List<Dscps> dscpsLengths = new ArrayList<>();
425
426         for (final UnkeyedListEntryNode node : dscpLengthsData.getValue()) {
427             final DscpsBuilder dscpsLengthsBuilder = new DscpsBuilder();
428             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
429             opValue.ifPresent(dataContainerChild -> dscpsLengthsBuilder.setOp(NumericOneByteOperandParser
430                     .INSTANCE.create((Set<String>) dataContainerChild.getValue())));
431             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
432             valueNode.ifPresent(dataContainerChild
433                     -> dscpsLengthsBuilder.setValue(new Dscp((Short) dataContainerChild.getValue())));
434             dscpsLengths.add(dscpsLengthsBuilder.build());
435         }
436
437         return dscpsLengths;
438     }
439
440     private static List<Fragments> createFragments(final UnkeyedListNode fragmentsData) {
441         final List<Fragments> fragments = new ArrayList<>();
442
443         for (final UnkeyedListEntryNode node : fragmentsData.getValue()) {
444             final FragmentsBuilder fragmentsBuilder = new FragmentsBuilder();
445             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
446             opValue.ifPresent(dataContainerChild -> fragmentsBuilder.setOp(BitmaskOperandParser
447                     .INSTANCE.create((Set<String>) dataContainerChild.getValue())));
448             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
449             valueNode.ifPresent(dataContainerChild
450                     -> fragmentsBuilder.setValue(createFragment((Set<String>) dataContainerChild.getValue())));
451             fragments.add(fragmentsBuilder.build());
452         }
453
454         return fragments;
455     }
456
457     private static Fragment createFragment(final Set<String> data) {
458         return new Fragment(data.contains(DO_NOT_VALUE), data.contains(FIRST_VALUE), data.contains(IS_A_VALUE),
459                 data.contains(LAST_VALUE));
460     }
461
462     protected final String stringNlri(final List<Flowspec> flows) {
463         final StringBuilder buffer = new StringBuilder("all packets ");
464         final Joiner joiner = Joiner.on(FLOW_SEPARATOR);
465         joiner.appendTo(buffer, flows.stream().map(this::encodeFlow).collect(Collectors.toList()));
466         return buffer.toString().replace("  ", " ");
467     }
468
469     @VisibleForTesting
470     final String encodeFlow(final Flowspec flow) {
471         final StringBuilder buffer = new StringBuilder();
472         final FlowspecType value = flow.getFlowspecType();
473         if (value instanceof PortCase) {
474             buffer.append("where port ");
475             buffer.append(NumericTwoByteOperandParser.INSTANCE.toString(((PortCase) value).getPorts()));
476         } else if (value instanceof DestinationPortCase) {
477             buffer.append("where destination port ");
478             buffer.append(NumericTwoByteOperandParser
479                     .INSTANCE.toString(((DestinationPortCase) value).getDestinationPorts()));
480         } else if (value instanceof SourcePortCase) {
481             buffer.append("where source port ");
482             buffer.append(NumericTwoByteOperandParser.INSTANCE.toString(((SourcePortCase) value).getSourcePorts()));
483         } else if (value instanceof IcmpTypeCase) {
484             buffer.append("where ICMP type ");
485             buffer.append(NumericOneByteOperandParser.INSTANCE.toString(((IcmpTypeCase) value).getTypes()));
486         } else if (value instanceof IcmpCodeCase) {
487             buffer.append("where ICMP code ");
488             buffer.append(NumericOneByteOperandParser.INSTANCE.toString(((IcmpCodeCase) value).getCodes()));
489         } else if (value instanceof TcpFlagsCase) {
490             buffer.append(stringTcpFlags(((TcpFlagsCase) value).getTcpFlags()));
491         } else if (value instanceof PacketLengthCase) {
492             buffer.append("where packet length ");
493             buffer.append(NumericTwoByteOperandParser.INSTANCE.toString(((PacketLengthCase) value).getPacketLengths()));
494         } else if (value instanceof DscpCase) {
495             buffer.append(stringDscp(((DscpCase) value).getDscps()));
496         } else if (value instanceof FragmentCase) {
497             buffer.append(stringFragment(((FragmentCase) value).getFragments()));
498         } else {
499             stringSpecificFSNlriType(value, buffer);
500         }
501         return buffer.toString();
502     }
503
504     private static String stringTcpFlags(final List<TcpFlags> flags) {
505         final StringBuilder buffer = new StringBuilder("where TCP flags ");
506         boolean isFirst = true;
507         for (final TcpFlags item : flags) {
508             buffer.append(BitmaskOperandParser.INSTANCE.toString(item.getOp(), isFirst));
509             buffer.append(item.getValue());
510             buffer.append(' ');
511             if (isFirst) {
512                 isFirst = false;
513             }
514         }
515         return buffer.toString();
516     }
517
518     private static String stringDscp(final List<Dscps> dscps) {
519         final StringBuilder buffer = new StringBuilder("where DSCP ");
520         boolean isFirst = true;
521         for (final Dscps item : dscps) {
522             buffer.append(NumericOneByteOperandParser.INSTANCE.toString(item.getOp(), isFirst));
523             buffer.append(item.getValue().getValue());
524             buffer.append(' ');
525             if (isFirst) {
526                 isFirst = false;
527             }
528         }
529         return buffer.toString();
530     }
531
532     private static String stringFragment(final List<Fragments> fragments) {
533         final StringBuilder buffer = new StringBuilder("where fragment ");
534         boolean isFirst = true;
535         for (final Fragments item : fragments) {
536             buffer.append(BitmaskOperandParser.INSTANCE.toString(item.getOp(), isFirst));
537             buffer.append(stringFragment(item.getValue()));
538             if (isFirst) {
539                 isFirst = false;
540             }
541         }
542         return buffer.toString();
543     }
544
545     private static String stringFragment(final Fragment fragment) {
546         final StringBuilder buffer = new StringBuilder();
547         if (fragment.isDoNot()) {
548             buffer.append("'DO NOT' ");
549         }
550         if (fragment.isFirst()) {
551             buffer.append("'IS FIRST' ");
552         }
553         if (fragment.isLast()) {
554             buffer.append("'IS LAST' ");
555         }
556         if (fragment.isIsA()) {
557             buffer.append("'IS A' ");
558         }
559         return buffer.toString();
560     }
561
562     public static int readNlriLength(@Nonnull final ByteBuf nlri) {
563         requireNonNull(nlri, "NLRI information cannot be null");
564         Preconditions.checkState(nlri.isReadable(), "NLRI Byte buffer is not readable.");
565         int length = nlri.readUnsignedByte();
566         if (length >= MAX_NLRI_LENGTH_ONE_BYTE) {
567             length = (length << Byte.SIZE | nlri.readUnsignedByte()) & MAX_NLRI_LENGTH;
568         }
569         Preconditions.checkState(length > 0 && length <= nlri.readableBytes(),
570                 "Invalid flowspec NLRI length %s", length);
571         return length;
572     }
573
574     /**
575      * Parses Flowspec NLRI into list of Flowspec.
576      *
577      * @param nlri byte representation of NLRI which will be parsed
578      * @return list of Flowspec
579      */
580     protected final List<Flowspec> parseNlriFlowspecList(@Nonnull final ByteBuf nlri) throws BGPParsingException {
581         if (!nlri.isReadable()) {
582             return null;
583         }
584         final List<Flowspec> fss = new ArrayList<>();
585
586         while (nlri.isReadable()) {
587             int nlriLength = readNlriLength(nlri);
588             Preconditions.checkState(nlriLength > 0 && nlriLength <= nlri.readableBytes(),
589                     "Invalid flowspec NLRI length %s", nlriLength);
590             LOG.trace("Flowspec NLRI length is {}", nlriLength);
591
592             while (nlriLength > 0) {
593                 final int readableLength = nlri.readableBytes();
594                 final FlowspecBuilder builder = new FlowspecBuilder();
595                 builder.setFlowspecType(this.flowspecTypeRegistry.parseFlowspecType(nlri));
596                 fss.add(builder.build());
597                 final int flowspecTypeLength = readableLength - nlri.readableBytes();
598                 nlriLength -= flowspecTypeLength;
599             }
600             Preconditions.checkState(nlriLength == 0,
601                     "Remain NLRI length should be 0 instead of %s", nlriLength);
602         }
603
604         return fss;
605     }
606
607     /**
608      * Override this function to parse additional NLRI fields
609      *
610      * @param nlri NLRI buffer
611      * @return Parsed additional fields
612      */
613     @Nonnull
614     protected Object[] parseNlri(@Nonnull final ByteBuf nlri) throws BGPParsingException {
615         return new Object[] {parseNlriFlowspecList(nlri)};
616     }
617
618     @Override
619     public final void parseNlri(@Nonnull final ByteBuf nlri, @Nonnull final MpReachNlriBuilder builder,
620             @Nullable final PeerSpecificParserConstraint constraint)
621         throws BGPParsingException {
622         if (!nlri.isReadable()) {
623             return;
624         }
625         final PathId pathId = readPathId(nlri, builder.getAfi(), builder.getSafi(), constraint);
626         final Object[] nlriFields = parseNlri(nlri);
627         builder.setAdvertizedRoutes(
628             new AdvertizedRoutesBuilder()
629                 .setDestinationType(
630                     createAdvertizedRoutesDestinationType(nlriFields, pathId)
631                 ).build()
632         );
633     }
634
635     @Nullable
636     protected static PathId readPathId(@Nonnull final ByteBuf nlri, final Class<? extends AddressFamily> afi,
637             final Class<? extends SubsequentAddressFamily> safi, final PeerSpecificParserConstraint constraint) {
638         if (MultiPathSupportUtil.isTableTypeSupported(constraint, new BgpTableTypeImpl(afi, safi))) {
639             return PathIdUtil.readPathId(nlri);
640         }
641         return null;
642     }
643
644     @Override
645     public final void parseNlri(@Nonnull final ByteBuf nlri, @Nonnull final MpUnreachNlriBuilder builder,
646             @Nullable final PeerSpecificParserConstraint constraint)
647         throws BGPParsingException {
648         if (!nlri.isReadable()) {
649             return;
650         }
651         final PathId pathId = readPathId(nlri, builder.getAfi(), builder.getSafi(), constraint);
652         final Object[] nlriFields = parseNlri(nlri);
653         builder.setWithdrawnRoutes(
654             new WithdrawnRoutesBuilder()
655                 .setDestinationType(
656                     createWithdrawnDestinationType(nlriFields, pathId)
657                 ).build()
658         );
659     }
660 }
661