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