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