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