BGPCEP-706: Fix BGP Flowspec NumbericOphrand 46/64746/8
authorKevin Wang <kevixw@gmail.com>
Mon, 23 Oct 2017 21:05:27 +0000 (14:05 -0700)
committerKevin Wang <kevixw@gmail.com>
Mon, 6 Nov 2017 22:16:41 +0000 (22:16 +0000)
Right now when serializing a NumbericOphrand, the "end-of-list"
bit is not properly set. The last entry in the ophrand list
should have this bit set according to RFC [1]. Otherwise the packet
will be in malformed format.
This patch fix BGP flowspec NumbericOphrand to set the "end-of-list"
field properly. When the serialization happens, the end-of-list
field will be set in runtime.

[1] https://tools.ietf.org/html/rfc5575

Change-Id: Ib8841360b47137dc0ec0f54c3ef156a298ba3c81
Signed-off-by: Kevin Wang <kevixw@gmail.com>
bgp/flowspec/src/main/java/org/opendaylight/protocol/bgp/flowspec/FSDscpHandler.java
bgp/flowspec/src/main/java/org/opendaylight/protocol/bgp/flowspec/FSIpv6FlowLabelHandler.java
bgp/flowspec/src/main/java/org/opendaylight/protocol/bgp/flowspec/FSTcpFlagsHandler.java
bgp/flowspec/src/main/java/org/opendaylight/protocol/bgp/flowspec/handlers/AbstractFSFragmentHandler.java
bgp/flowspec/src/main/java/org/opendaylight/protocol/bgp/flowspec/handlers/AbstractNumericOperandParser.java
bgp/flowspec/src/main/java/org/opendaylight/protocol/bgp/flowspec/handlers/AbstractOperandParser.java
bgp/flowspec/src/main/java/org/opendaylight/protocol/bgp/flowspec/handlers/BitmaskOperandParser.java
bgp/flowspec/src/main/java/org/opendaylight/protocol/bgp/flowspec/handlers/NumericOneByteOperandParser.java
bgp/flowspec/src/main/java/org/opendaylight/protocol/bgp/flowspec/handlers/NumericTwoByteOperandParser.java
bgp/flowspec/src/test/java/org/opendaylight/protocol/bgp/flowspec/handlers/NumericOperandParserTest.java [new file with mode: 0644]

index 0e4261cb6a4b113f59a89d2d24dc8d0e8b937493..7599c18c226beadf767f367002a9b67cfee118af 100644 (file)
@@ -12,11 +12,13 @@ import static java.util.Objects.requireNonNull;
 import com.google.common.base.Preconditions;
 import io.netty.buffer.ByteBuf;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 import org.opendaylight.protocol.bgp.flowspec.handlers.FlowspecTypeParser;
 import org.opendaylight.protocol.bgp.flowspec.handlers.FlowspecTypeSerializer;
 import org.opendaylight.protocol.bgp.flowspec.handlers.NumericOneByteOperandParser;
 import org.opendaylight.protocol.bgp.flowspec.handlers.Util;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.Dscp;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.NumericOperand;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.FlowspecType;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.DscpCase;
@@ -25,24 +27,12 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flow
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.dscp._case.DscpsBuilder;
 
 public final class FSDscpHandler implements FlowspecTypeParser, FlowspecTypeSerializer {
-    public static final int DSCP_VALUE = 11;
+    static final int DSCP_VALUE = 11;
 
-    @Override
-    public void serializeType(FlowspecType fsType, ByteBuf output) {
-        Preconditions.checkArgument(fsType instanceof DscpCase, "DscpCase class is mandatory!");
-        output.writeByte(DSCP_VALUE);
-        serializeDscps(((DscpCase) fsType).getDscps(), output);
-    }
-
-    @Override
-    public FlowspecType parseType(ByteBuf buffer) {
-        requireNonNull(buffer, "input buffer is null, missing data to parse.");
-        return new DscpCaseBuilder().setDscps(parseDscps(buffer)).build();
-    }
-
-    private static final void serializeDscps(final List<Dscps> dscps, final ByteBuf nlriByteBuf) {
-        for (final Dscps dscp : dscps) {
-            NumericOneByteOperandParser.INSTANCE.serialize(dscp.getOp(), 1, nlriByteBuf);
+    private static void serializeDscps(final List<Dscps> dscps, final ByteBuf nlriByteBuf) {
+        for (final Iterator<Dscps> it = dscps.iterator(); it.hasNext(); ) {
+            final Dscps dscp = it.next();
+            NumericOneByteOperandParser.INSTANCE.serialize(dscp.getOp(), 1, !it.hasNext(), nlriByteBuf);
             Util.writeShortest(dscp.getValue().getValue(), nlriByteBuf);
         }
     }
@@ -57,10 +47,23 @@ public final class FSDscpHandler implements FlowspecTypeParser, FlowspecTypeSeri
             // RFC does not specify operator
             final NumericOperand op = NumericOneByteOperandParser.INSTANCE.parse(b);
             builder.setOp(op);
-            builder.setValue(new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.Dscp(nlri.readUnsignedByte()));
+            builder.setValue(new Dscp(nlri.readUnsignedByte()));
             end = op.isEndOfList();
             dscps.add(builder.build());
         }
         return dscps;
     }
+
+    @Override
+    public void serializeType(final FlowspecType fsType, final ByteBuf output) {
+        Preconditions.checkArgument(fsType instanceof DscpCase, "DscpCase class is mandatory!");
+        output.writeByte(DSCP_VALUE);
+        serializeDscps(((DscpCase) fsType).getDscps(), output);
+    }
+
+    @Override
+    public FlowspecType parseType(final ByteBuf buffer) {
+        requireNonNull(buffer, "input buffer is null, missing data to parse.");
+        return new DscpCaseBuilder().setDscps(parseDscps(buffer)).build();
+    }
 }
index 8bacccbfb59737e437f91462803ddad429f9fcb3..9e9515d751577716389277a3b24df357891d56a4 100644 (file)
@@ -11,6 +11,7 @@ import com.google.common.base.Preconditions;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 import org.opendaylight.protocol.bgp.flowspec.handlers.AbstractOperandParser;
 import org.opendaylight.protocol.bgp.flowspec.handlers.FlowspecTypeParser;
@@ -26,29 +27,31 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flow
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.group.ipv6.flowspec.flowspec.type.flow.label._case.FlowLabelBuilder;
 
 public final class FSIpv6FlowLabelHandler implements FlowspecTypeParser, FlowspecTypeSerializer {
-    public static final int FLOW_LABEL_VALUE = 13;
+    static final int FLOW_LABEL_VALUE = 13;
+
+    private static void serializeNumericFourByteValue(final List<FlowLabel> list, final ByteBuf nlriByteBuf) {
+        for (final Iterator<FlowLabel> it = list.iterator(); it.hasNext(); ) {
+            final FlowLabel label = it.next();
+            final ByteBuf protoBuf = Unpooled.buffer();
+            Util.writeShortest(label.getValue().intValue(), protoBuf);
+            NumericOneByteOperandParser.INSTANCE.serialize(label.getOp(), protoBuf.readableBytes(),
+                    !it.hasNext(), nlriByteBuf);
+            nlriByteBuf.writeBytes(protoBuf);
+        }
+    }
 
     @Override
-    public void serializeType(FlowspecType fsType, ByteBuf output) {
+    public void serializeType(final FlowspecType fsType, final ByteBuf output) {
         Preconditions.checkArgument(fsType instanceof FlowLabelCase, "FlowLabelCase class is mandatory!");
         output.writeByte(FLOW_LABEL_VALUE);
         serializeNumericFourByteValue(((FlowLabelCase) fsType).getFlowLabel(), output);
     }
 
     @Override
-    public FlowspecType parseType(ByteBuf buffer) {
+    public FlowspecType parseType(final ByteBuf buffer) {
         return new FlowLabelCaseBuilder().setFlowLabel(parseFlowLabel(buffer)).build();
     }
 
-    private static void serializeNumericFourByteValue(final List<FlowLabel> list, final ByteBuf nlriByteBuf) {
-        for (final FlowLabel item : list) {
-            final ByteBuf protoBuf = Unpooled.buffer();
-            Util.writeShortest(item.getValue().intValue(), protoBuf);
-            NumericOneByteOperandParser.INSTANCE.serialize(item.getOp(), protoBuf.readableBytes(), nlriByteBuf);
-            nlriByteBuf.writeBytes(protoBuf);
-        }
-    }
-
     private static List<FlowLabel> parseFlowLabel(final ByteBuf nlri) {
         final List<FlowLabel> labels = new ArrayList<>();
         boolean end = false;
index 29d6cc52fac262b89ec1bc6e4b2ef278c8b739fc..6aa36cf24bc0ef652d98b351ae1692f24ab95087 100644 (file)
@@ -13,6 +13,7 @@ import com.google.common.base.Preconditions;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 import org.opendaylight.protocol.bgp.flowspec.handlers.AbstractOperandParser;
 import org.opendaylight.protocol.bgp.flowspec.handlers.BitmaskOperandParser;
@@ -28,30 +29,32 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flow
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.tcp.flags._case.TcpFlagsBuilder;
 
 public final class FSTcpFlagsHandler implements FlowspecTypeParser, FlowspecTypeSerializer {
-    public static final int TCP_FLAGS_VALUE = 9;
+    static final int TCP_FLAGS_VALUE = 9;
+
+    private static void serializeTcpFlags(final List<TcpFlags> flags, final ByteBuf nlriByteBuf) {
+        for (final Iterator<TcpFlags> it = flags.iterator(); it.hasNext(); ) {
+            final TcpFlags flag = it.next();
+            final ByteBuf flagsBuf = Unpooled.buffer();
+            Util.writeShortest(flag.getValue(), flagsBuf);
+            BitmaskOperandParser.INSTANCE.serialize(flag.getOp(), flagsBuf.readableBytes(),
+                    !it.hasNext(), nlriByteBuf);
+            nlriByteBuf.writeBytes(flagsBuf);
+        }
+    }
 
     @Override
-    public void serializeType(FlowspecType fsType, ByteBuf output) {
+    public void serializeType(final FlowspecType fsType, final ByteBuf output) {
         Preconditions.checkArgument(fsType instanceof TcpFlagsCase, "TcpFlagsCase class is mandatory!");
         output.writeByte(TCP_FLAGS_VALUE);
         serializeTcpFlags(((TcpFlagsCase) fsType).getTcpFlags(), output);
     }
 
     @Override
-    public FlowspecType parseType(ByteBuf buffer) {
+    public FlowspecType parseType(final ByteBuf buffer) {
         requireNonNull(buffer, "input buffer is null, missing data to parse.");
         return new TcpFlagsCaseBuilder().setTcpFlags(parseTcpFlags(buffer)).build();
     }
 
-    private static final void serializeTcpFlags(final List<TcpFlags> flags, final ByteBuf nlriByteBuf) {
-        for (final TcpFlags flag : flags) {
-            final ByteBuf flagsBuf = Unpooled.buffer();
-            Util.writeShortest(flag.getValue(), flagsBuf);
-            BitmaskOperandParser.INSTANCE.serialize(flag.getOp(), flagsBuf.readableBytes(), nlriByteBuf);
-            nlriByteBuf.writeBytes(flagsBuf);
-        }
-    }
-
     private static List<TcpFlags> parseTcpFlags(final ByteBuf nlri) {
         final List<TcpFlags> flags = new ArrayList<>();
         boolean end = false;
index 8721df693a92599e888d894d7df42b051e9a1626..debc25e8fbced703853c6ac09e06a0e0b44850a4 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.protocol.bgp.flowspec.handlers;
 import com.google.common.base.Preconditions;
 import io.netty.buffer.ByteBuf;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.BitmaskOperand;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.Fragment;
@@ -23,37 +24,38 @@ public abstract class AbstractFSFragmentHandler implements FlowspecTypeParser, F
 
     public static final int FRAGMENT_VALUE = 12;
 
-    protected static final int LAST_FRAGMENT = 4;
-    protected static final int FIRST_FRAGMENT = 5;
-    protected static final int IS_A_FRAGMENT = 6;
-    protected static final int DONT_FRAGMENT = 7;
+    static final int LAST_FRAGMENT = 4;
+    static final int FIRST_FRAGMENT = 5;
+    static final int IS_A_FRAGMENT = 6;
+    static final int DONT_FRAGMENT = 7;
 
     protected abstract Fragment parseFragment(final byte fragment);
     protected abstract byte serializeFragment(final Fragment fragment);
 
     @Override
-    public void serializeType(FlowspecType fsType, ByteBuf output) {
+    public void serializeType(final FlowspecType fsType, final ByteBuf output) {
         Preconditions.checkArgument(fsType instanceof FragmentCase, "FragmentCase class is mandatory!");
         output.writeByte(FRAGMENT_VALUE);
         serializeFragments(((FragmentCase) fsType).getFragments(), output);
     }
 
     @Override
-    public FlowspecType parseType(ByteBuf buffer) {
+    public FlowspecType parseType(final ByteBuf buffer) {
         if (buffer == null) {
             return null;
         }
         return new FragmentCaseBuilder().setFragments(parseFragments(buffer)).build();
     }
 
-    protected final void serializeFragments(final List<Fragments> fragments, final ByteBuf nlriByteBuf) {
-        for (final Fragments fragment : fragments) {
-            BitmaskOperandParser.INSTANCE.serialize(fragment.getOp(), 1, nlriByteBuf);
+    private void serializeFragments(final List<Fragments> fragments, final ByteBuf nlriByteBuf) {
+        for (final Iterator<Fragments> it = fragments.iterator(); it.hasNext(); ) {
+            final Fragments fragment = it.next();
+            BitmaskOperandParser.INSTANCE.serialize(fragment.getOp(), 1, !it.hasNext(), nlriByteBuf);
             nlriByteBuf.writeByte(serializeFragment(fragment.getValue()));
         }
     }
 
-    protected final List<Fragments> parseFragments(final ByteBuf nlri) {
+    private List<Fragments> parseFragments(final ByteBuf nlri) {
         final List<Fragments> fragments = new ArrayList<>();
         boolean end = false;
         // we can do this as all fields will be rewritten in the cycle
index 202841aa34deb2bca6ebf4974933d9fddd762653..9ba6d67b4c9b2307e5a609f7e3b4cbafd1135909 100644 (file)
@@ -11,7 +11,6 @@ import com.google.common.annotations.VisibleForTesting;
 import io.netty.buffer.ByteBuf;
 import java.util.List;
 import java.util.Set;
-
 import org.opendaylight.protocol.util.BitArray;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.NumericOperand;
 
@@ -29,9 +28,9 @@ public abstract class AbstractNumericOperandParser<N> extends AbstractOperandPar
     @VisibleForTesting
     public static final String LESS_THAN_VALUE = "less-than";
 
-    protected static final int LESS_THAN = 5;
-    protected static final int GREATER_THAN = 6;
-    protected static final int EQUAL = 7;
+    private static final int LESS_THAN = 5;
+    private static final int GREATER_THAN = 6;
+    private static final int EQUAL = 7;
 
     /**
      * Serializes specific numeric operand type depending on the length field value.
@@ -45,13 +44,20 @@ public abstract class AbstractNumericOperandParser<N> extends AbstractOperandPar
 
     @Override
     public final NumericOperand create(final Set<String> operandValues) {
-        return new NumericOperand(operandValues.contains(AND_BIT_VALUE), operandValues.contains(END_OF_LIST_VALUE), operandValues.contains(EQUALS_VALUE), operandValues.contains(GREATER_THAN_VALUE), operandValues.contains(LESS_THAN_VALUE));
+        return new NumericOperand(
+                operandValues.contains(AND_BIT_VALUE),
+                operandValues.contains(END_OF_LIST_VALUE),
+                operandValues.contains(EQUALS_VALUE),
+                operandValues.contains(GREATER_THAN_VALUE),
+                operandValues.contains(LESS_THAN_VALUE)
+        );
     }
 
     @Override
-    public final void serialize(final NumericOperand operand, final int length, final ByteBuf buffer) {
+    public final void serialize(final NumericOperand operand, final int length,
+            final boolean endOfList, final ByteBuf buffer) {
         final BitArray operandValues = new BitArray(OPERAND_LENGTH);
-        operandValues.set(END_OF_LIST, operand.isEndOfList());
+        operandValues.set(END_OF_LIST, endOfList);
         operandValues.set(AND_BIT, operand.isAndBit());
         operandValues.set(LESS_THAN, operand.isLessThan());
         operandValues.set(GREATER_THAN, operand.isGreaterThan());
@@ -63,7 +69,13 @@ public abstract class AbstractNumericOperandParser<N> extends AbstractOperandPar
     @Override
     public final NumericOperand parse(final byte operand) {
         final BitArray operandValues = BitArray.valueOf(operand);
-        return new NumericOperand(operandValues.get(AND_BIT), operandValues.get(END_OF_LIST), operandValues.get(EQUAL), operandValues.get(GREATER_THAN), operandValues.get(LESS_THAN));
+        return new NumericOperand(
+                operandValues.get(AND_BIT),
+                operandValues.get(END_OF_LIST),
+                operandValues.get(EQUAL),
+                operandValues.get(GREATER_THAN),
+                operandValues.get(LESS_THAN)
+        );
     }
 
     @Override
index 6ab0ba997e1f485d472c11d4b8375a1f9842d978..7bedbb193f1bf5595265d884fad958cb4a67de1b 100644 (file)
@@ -50,9 +50,11 @@ public abstract class AbstractOperandParser<T> {
      *
      * @param op operand to be serialized
      * @param length value of the 'length' field
+     * @param endOfList if this operand is at the end of the list
      * @param buffer where the operand will be serialized to
      */
-    protected abstract void serialize(final T op, final int length, final ByteBuf buffer);
+    protected abstract void serialize(final T op, final int length, final boolean endOfList,
+            final ByteBuf buffer);
 
     /**
      * Parses operand from byte value.
index b3071037a991534b3913ad0141ae761aecd6dd3b..5a85133847bd512592e0b58805c912d54f41c57a 100644 (file)
@@ -10,7 +10,6 @@ package org.opendaylight.protocol.bgp.flowspec.handlers;
 import com.google.common.annotations.VisibleForTesting;
 import io.netty.buffer.ByteBuf;
 import java.util.Set;
-
 import org.opendaylight.protocol.util.BitArray;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.BitmaskOperand;
 
@@ -33,19 +32,23 @@ public final class BitmaskOperandParser extends AbstractOperandParser<BitmaskOpe
     private static final int NOT = 6;
     private static final int MATCH = 7;
 
-    private BitmaskOperandParser() {
-
-    }
+    private BitmaskOperandParser() { }
 
     @Override
     public BitmaskOperand create(final Set<String> opValues) {
-        return new BitmaskOperand(opValues.contains(AND_BIT_VALUE), opValues.contains(END_OF_LIST_VALUE), opValues.contains(MATCH_VALUE), opValues.contains(NOT_VALUE));
+        return new BitmaskOperand(
+                opValues.contains(AND_BIT_VALUE),
+                opValues.contains(END_OF_LIST_VALUE),
+                opValues.contains(MATCH_VALUE),
+                opValues.contains(NOT_VALUE)
+        );
     }
 
     @Override
-    public void serialize(final BitmaskOperand op, final int length, final ByteBuf buffer) {
+    public void serialize(final BitmaskOperand op, final int length, final boolean endOfList,
+            final ByteBuf buffer) {
         final BitArray bs = new BitArray(OPERAND_LENGTH);
-        bs.set(END_OF_LIST, op.isEndOfList());
+        bs.set(END_OF_LIST, endOfList);
         bs.set(AND_BIT, op.isAndBit());
         bs.set(MATCH, op.isMatch());
         bs.set(NOT, op.isNot());
index abb2a0de3cbc93ad6b2583ce68ccf7a393318aa5..af69d56dc0a7096ebb0f8835e2ea8be8a3480378 100644 (file)
@@ -8,8 +8,8 @@
 package org.opendaylight.protocol.bgp.flowspec.handlers;
 
 import io.netty.buffer.ByteBuf;
+import java.util.Iterator;
 import java.util.List;
-
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.NumericOneByteValue;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.NumericOperand;
 
@@ -36,8 +36,9 @@ public final class NumericOneByteOperandParser extends AbstractNumericByteOperan
      */
     @Override
     public <T extends NumericOneByteValue> void serialize(final List<T> list, final ByteBuf nlriByteBuf) {
-        for (final T operand : list) {
-            super.serialize(operand.getOp(), 1, nlriByteBuf);
+        for (final Iterator<T> it = list.iterator(); it.hasNext(); ) {
+            final T operand = it.next();
+            super.serialize(operand.getOp(), 1, !it.hasNext(), nlriByteBuf);
             Util.writeShortest(operand.getValue(), nlriByteBuf);
         }
     }
index 324886a7894a9a5ca3a04d075def30f24b510d90..ca148b0164b52bcd6cfea42b5c38c1747f3d17d3 100644 (file)
@@ -9,6 +9,7 @@ package org.opendaylight.protocol.bgp.flowspec.handlers;
 
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
+import java.util.Iterator;
 import java.util.List;
 
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.NumericOperand;
@@ -37,10 +38,11 @@ public final class NumericTwoByteOperandParser extends AbstractNumericByteOperan
      */
     @Override
     public <T extends NumericTwoByteValue> void serialize(final List<T> list, final ByteBuf nlriByteBuf) {
-        for (final T operand : list) {
+        for (final Iterator<T> it = list.iterator(); it.hasNext(); ) {
+            final T operand = it.next();
             final ByteBuf protoBuf = Unpooled.buffer();
             Util.writeShortest(operand.getValue(), protoBuf);
-            super.serialize(operand.getOp(), protoBuf.readableBytes(), nlriByteBuf);
+            super.serialize(operand.getOp(), protoBuf.readableBytes(), !it.hasNext(), nlriByteBuf);
             nlriByteBuf.writeBytes(protoBuf);
         }
     }
diff --git a/bgp/flowspec/src/test/java/org/opendaylight/protocol/bgp/flowspec/handlers/NumericOperandParserTest.java b/bgp/flowspec/src/test/java/org/opendaylight/protocol/bgp/flowspec/handlers/NumericOperandParserTest.java
new file mode 100644 (file)
index 0000000..30601fa
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2017 Lumina Networks, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.protocol.bgp.flowspec.handlers;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+import org.opendaylight.protocol.util.ByteArray;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.NumericOperand;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.icmp.code._case.Codes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.icmp.code._case.CodesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.port._case.Ports;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.port._case.PortsBuilder;
+
+public class NumericOperandParserTest {
+    private static final byte[] ONE_BYTE_CODE_LIST = new byte[]{
+            0x01, 0x64,
+            0x01, 0x65,
+            (byte) 0x81, 0x66,    // last port in the list should have end-of-list set
+    };
+
+    @Test
+    public void testSerializeTwoByte() {
+        final ByteBuf nlriByteBuf = Unpooled.buffer();
+        final List<Ports> ports = new ArrayList<>();
+        // create 3 ports without end-of-list bit set
+        for (int i = 0; i < 3; i++) {
+            ports.add(
+                    new PortsBuilder()
+                            .setOp(new NumericOperand(false, false, true, false, false))
+                            .setValue(100 + i)
+                            .build()
+            );
+        }
+        NumericTwoByteOperandParser.INSTANCE.serialize(ports, nlriByteBuf);
+        assertArrayEquals(ONE_BYTE_CODE_LIST, ByteArray.readAllBytes(nlriByteBuf));
+    }
+
+    @Test
+    public void testSerializeOneByte() {
+        final ByteBuf nlriByteBuf = Unpooled.buffer();
+        final List<Codes> codes = new ArrayList<>();
+        // create 3 ports without end-of-list bit set
+        for (int i = 0; i < 3; i++) {
+            codes.add(
+                    new CodesBuilder()
+                            .setOp(new NumericOperand(false, false, true, false, false))
+                            .setValue((short) (100 + i))
+                            .build()
+            );
+        }
+        NumericOneByteOperandParser.INSTANCE.serialize(codes, nlriByteBuf);
+        assertArrayEquals(ONE_BYTE_CODE_LIST, ByteArray.readAllBytes(nlriByteBuf));
+    }
+
+    @Test
+    public void testSerializeVariableByte() {
+        final ByteBuf nlriByteBuf = Unpooled.buffer();
+        // test with a operand with endOfList set to true, but override with false
+        NumericOneByteOperandParser.INSTANCE.serialize(
+                new NumericOperand(false, true, true, false, false),
+                1,
+                false,
+                nlriByteBuf);
+        assertArrayEquals(new byte[]{(byte) 0x01}, ByteArray.readAllBytes(nlriByteBuf));
+
+        // test with a operand with endOfList set to false, but override with true
+        nlriByteBuf.clear();
+        NumericOneByteOperandParser.INSTANCE.serialize(
+                new NumericOperand(false, true, true, false, false),
+                1,
+                true,
+                nlriByteBuf);
+        assertArrayEquals(new byte[]{(byte) 0x81}, ByteArray.readAllBytes(nlriByteBuf));
+    }
+}
\ No newline at end of file