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