BUG-5904 Support 2-byte AS Route Distinguisher 97/39497/2
authorKevin Wang <kevixw@gmail.com>
Thu, 26 May 2016 20:00:28 +0000 (13:00 -0700)
committerMilos Fabian <milfabia@cisco.com>
Mon, 30 May 2016 09:48:52 +0000 (09:48 +0000)
 - Support type 0 RD
 - Add universal parse function for RD

Change-Id: I78d6a4b9cb0fac62ba796d6ffe54dfa9e712e628
Signed-off-by: Kevin Wang <kevixw@gmail.com>
bgp/concepts/src/main/java/org/opendaylight/bgp/concepts/RouteDistinguisherUtil.java
bgp/concepts/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/bgp/types/rev130919/RouteDistinguisherBuilder.java
bgp/concepts/src/main/yang/bgp-types.yang
bgp/concepts/src/test/java/org/opendaylight/bgp/concepts/RouteDistinguisherUtilTest.java

index 4eac844cd6557cbd95acbbc2e2e0ebc265b487d6..8636f4b656fce523471f07ee69026427e44d3dd0 100644 (file)
@@ -7,22 +7,48 @@
  */
 package org.opendaylight.bgp.concepts;
 
+import com.google.common.base.Preconditions;
 import io.netty.buffer.ByteBuf;
 import org.opendaylight.protocol.util.ByteBufWriteUtil;
 import org.opendaylight.protocol.util.Ipv4Util;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.RdAs;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.RdIpv4;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.RdTwoOctetAs;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.RouteDistinguisher;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.RouteDistinguisherBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Utility class for of RouteDistinguisher serialization and parsing.
  * https://tools.ietf.org/html/rfc4364#section-4.2
  */
 public final class RouteDistinguisherUtil {
+    private static final Logger LOG = LoggerFactory.getLogger(RouteDistinguisherUtil.class);
+
+    private enum RD_TYPE {
+        AS_2BYTE(0),
+        IPV4(1),
+        AS_4BYTE(2),
+        INVALID(-1);
+
+        public final int value;
+
+        RD_TYPE(int val) {
+            value = val;
+        }
+
+        public static RD_TYPE valueOf(final int value) {
+            for (RD_TYPE type : values()) {
+                if (type.value == value) {
+                    return type;
+                }
+            }
+            return INVALID;
+        }
+    }
 
-    private static final int IPV4_TYPE = 1;
-    private static final int AS_4BYTE_TYPE = 2;
     private static final String SEPARATOR = ":";
     public static final int RD_LENGTH = 8;
 
@@ -33,22 +59,32 @@ public final class RouteDistinguisherUtil {
     /**
      * Serializes route distinguisher according to type and writes into ByteBuf.
      *
-     * @param distinquisher
+     * @param distinguisher
      * @param byteAggregator
      */
-    public static void serializeRouteDistinquisher(final RouteDistinguisher distinquisher, final ByteBuf byteAggregator) {
-        if (distinquisher.getRdAs() != null) {
-            final String[] values = distinquisher.getRdAs().getValue().split(SEPARATOR);
-            byteAggregator.writeShort(AS_4BYTE_TYPE);
+    public static void serializeRouteDistinquisher(final RouteDistinguisher distinguisher, final ByteBuf byteAggregator) {
+        Preconditions.checkNotNull(distinguisher);
+        Preconditions.checkState(byteAggregator != null && byteAggregator.isWritable(RD_LENGTH), "Cannot write Route Distinguisher to provided buffer.");
+        if (distinguisher.getRdTwoOctetAs() != null) {
+            final String[] values = distinguisher.getRdTwoOctetAs().getValue().split(SEPARATOR);
+            byteAggregator.writeShort(RD_TYPE.AS_2BYTE.value);
+            ByteBufWriteUtil.writeUnsignedShort(Integer.parseInt(values[1]), byteAggregator);
+            final long assignedNumber = Integer.parseUnsignedInt(values[2]);
+            ByteBufWriteUtil.writeUnsignedInt(assignedNumber, byteAggregator);
+        } else if (distinguisher.getRdAs() != null) {
+            final String[] values = distinguisher.getRdAs().getValue().split(SEPARATOR);
+            byteAggregator.writeShort(RD_TYPE.AS_4BYTE.value);
             final long admin = Integer.parseUnsignedInt(values[0]);
             ByteBufWriteUtil.writeUnsignedInt(admin, byteAggregator);
             ByteBufWriteUtil.writeUnsignedShort(Integer.parseInt(values[1]), byteAggregator);
-        } else if (distinquisher.getRdIpv4() != null) {
-            final String[] values = distinquisher.getRdIpv4().getValue().split(SEPARATOR);
+        } else if (distinguisher.getRdIpv4() != null) {
+            final String[] values = distinguisher.getRdIpv4().getValue().split(SEPARATOR);
             final Ipv4Address ip = new Ipv4Address(values[0]);
-            byteAggregator.writeShort(IPV4_TYPE);
+            byteAggregator.writeShort(RD_TYPE.IPV4.value);
             ByteBufWriteUtil.writeIpv4Address(ip, byteAggregator);
             ByteBufWriteUtil.writeUnsignedShort(Integer.parseInt(values[1]), byteAggregator);
+        } else {
+            LOG.warn("Unable to serialize Route Distinguisher. Invalid RD value found. RD={}", distinguisher);
         }
     }
 
@@ -59,22 +95,50 @@ public final class RouteDistinguisherUtil {
      * @return RouteDistinguisher
      */
     public static RouteDistinguisher parseRouteDistinguisher(final ByteBuf buffer) {
+        Preconditions.checkState(buffer != null && buffer.isReadable(RD_LENGTH), "Cannot read Route Distinguisher from provided buffer.");
         final int type = buffer.readUnsignedShort();
+        final RD_TYPE rdType = RD_TYPE.valueOf(type);
         final StringBuilder routeDistiguisher = new StringBuilder();
-        switch (type) {
-        case IPV4_TYPE:
+        switch (rdType) {
+        case AS_2BYTE:
+            routeDistiguisher.append(type);
+            routeDistiguisher.append(SEPARATOR);
+            routeDistiguisher.append(buffer.readUnsignedShort());
+            routeDistiguisher.append(SEPARATOR);
+            routeDistiguisher.append(buffer.readUnsignedInt());
+            return new RouteDistinguisher(new RdTwoOctetAs(routeDistiguisher.toString()));
+        case IPV4:
             routeDistiguisher.append(Ipv4Util.addressForByteBuf(buffer).getValue());
             routeDistiguisher.append(SEPARATOR);
             routeDistiguisher.append(buffer.readUnsignedShort());
             return new RouteDistinguisher(new RdIpv4(routeDistiguisher.toString()));
-        case AS_4BYTE_TYPE:
+        case AS_4BYTE:
             routeDistiguisher.append(buffer.readUnsignedInt());
             routeDistiguisher.append(SEPARATOR);
             routeDistiguisher.append(buffer.readUnsignedShort());
             return new RouteDistinguisher(new RdAs(routeDistiguisher.toString()));
         default:
-            break;
+            // now that this RD type is not supported, we want to read the remain 6 bytes
+            // in order to get the byte index correct
+            for (int i = 0; i < 6; i++) {
+                routeDistiguisher.append("0x").append(Integer.toHexString(buffer.readByte() & 0xFF)).append(" ");
+            }
+            LOG.debug("Invalid Route Distinguisher: type={}, rawRouteDistinguisherValue={}", type, routeDistiguisher.toString());
+            throw new IllegalArgumentException("Invalid Route Distinguisher type " + type);
+        }
+    }
+
+    public static RouteDistinguisher parseRouteDistinguisher(final String str) {
+        return str == null ? null : RouteDistinguisherBuilder.getDefaultInstance(str);
+    }
+
+    public static RouteDistinguisher parseRouteDistinguisher(final Object obj) {
+        if (obj instanceof String) {
+            return RouteDistinguisherBuilder.getDefaultInstance((String) obj);
+        } else if (obj instanceof RouteDistinguisher) {
+            return (RouteDistinguisher) obj;
+        } else {
+            return null;
         }
-        return null;
     }
 }
index 9baeeef446001ec70213656052490355645e3063..79c96da1df2aec9c3ea72fbeef21be29e5852439 100644 (file)
@@ -21,6 +21,21 @@ import java.util.regex.Pattern;
  */
 public class RouteDistinguisherBuilder {
 
+    private static final Pattern RD_TWO_OCTET_AS =
+        Pattern.compile("0:"
+            + "([0-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|"
+            + "[1-9][0-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9][0-9]|"
+            + "[1-9][0-9][0-9][0-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]|"
+            + "[1-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]|[1-3][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]|"
+            + "4[0-1][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]|42[0-8][0-9][0-9][0-9][0-9][0-9][0-9][0-9]|"
+            + "429[0-3][0-9][0-9][0-9][0-9][0-9][0-9]|4294[0-8][0-9][0-9][0-9][0-9][0-9]|"
+            + "42949[0-5][0-9][0-9][0-9][0-9]|429496[0-6][0-9][0-9][0-9]|4294967[0-1][0-9][0-9]|"
+            + "42949672[0-8][0-9]|429496729[0-5])"
+            + ":"
+            + "([0-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|"
+            + "[1-5][0-9][0-9][0-9][0-9]|6[0-4][0-9][0-9][0-9]|"
+            + "65[0-4][0-9][0-9]|655[0-2][0-9]|6553[0-5])");
+
     private static final Pattern RD_IPV4 =
         Pattern.compile("((([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\.){3}"
             + "([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]))"
@@ -48,7 +63,9 @@ public class RouteDistinguisherBuilder {
     }
 
     public static RouteDistinguisher getDefaultInstance(final java.lang.String defaultValue) {
-        if (RD_IPV4.matcher(defaultValue).matches()) {
+        if (RD_TWO_OCTET_AS.matcher(defaultValue).matches()) {
+            return new RouteDistinguisher((new RdTwoOctetAs(defaultValue)));
+        } else if (RD_IPV4.matcher(defaultValue).matches()) {
             return new RouteDistinguisher(new RdIpv4(defaultValue));
         } else if (RD_AS.matcher(defaultValue).matches()) {
             return new RouteDistinguisher(new RdAs(defaultValue));
index 7435c8e71a3f7609846b9e58fdfa0e0164b193e5..6c51c4be395250169ccfaf5d88aa4152e283ad7b 100644 (file)
@@ -83,11 +83,31 @@ module bgp-types {
     typedef route-distinguisher {
         reference "https://tools.ietf.org/html/rfc4364#section-4.2";
         type union {
+            type rd-two-octet-as;
             type rd-ipv4;
             type rd-as;
         }
     }
 
+    typedef rd-two-octet-as {
+        type string {
+            /* 2B AS : 4B number */
+            pattern '0:'
+                + '([0-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|'
+                + '[1-5][0-9][0-9][0-9][0-9]|6[0-4][0-9][0-9][0-9]|'
+                + '65[0-4][0-9][0-9]|655[0-2][0-9]|6553[0-5])'
+                + ':'
+                + '([0-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|'
+                + '[1-9][0-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9][0-9]|'
+                + '[1-9][0-9][0-9][0-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]|'
+                + '[1-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]|[1-3][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]|'
+                + '4[0-1][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]|42[0-8][0-9][0-9][0-9][0-9][0-9][0-9][0-9]|'
+                + '429[0-3][0-9][0-9][0-9][0-9][0-9][0-9]|4294[0-8][0-9][0-9][0-9][0-9][0-9]|'
+                + '42949[0-5][0-9][0-9][0-9][0-9]|429496[0-6][0-9][0-9][0-9]|4294967[0-1][0-9][0-9]|'
+                + '42949672[0-8][0-9]|429496729[0-5])';
+        }
+    }
+
     typedef rd-ipv4 {
         type string {
             /* IPv4 : 2B number */
index 0bcdbdd26244d0e23898f50fd8986bddb5f99a44..378018bebaf22251dbaf3555dcbdc97a11d7de16 100644 (file)
@@ -20,29 +20,59 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.type
 public class RouteDistinguisherUtilTest {
 
     private static final String IP_ADDRESS = "1.2.3.4";
+    private static final String IP_PORT = "10";
     private static final String ADMIN = "55";
+    private static final String ASSIGNED_NUMBER = "65535";
+    private static final byte[] AS_2B_BYTES = { 0, 0, 0, 55, 0, 0, (byte)0xff, (byte)0xff};
     private static final byte[] IP_BYTES = { 0, 1, 1, 2, 3, 4, 0, 10 };
-    private static final byte[] AS_4B_BYTES = { 0, 2, 0,0, 0, 55, (byte)0xff, (byte)0xff};
+    private static final byte[] AS_4B_BYTES = { 0, 2, 0, 0, 0, 55, (byte)0xff, (byte)0xff};
+    private static final byte[] INVALID_RD_TYPE_BYTES = { 0, 3, 0, 0, 0, 55, (byte)0xff, (byte)0xff};
     private static final char SEPARATOR = ':';
 
+    @Test
+    public void testAs2BRouteDistinguisher() {
+        final RouteDistinguisher expected = createRouteDistinguisher(0, ADMIN, ASSIGNED_NUMBER);
+        final RouteDistinguisher parsed = RouteDistinguisherUtil.parseRouteDistinguisher(Unpooled.copiedBuffer(AS_2B_BYTES));
+        assertEquals(expected.getRdTwoOctetAs(), parsed.getRdTwoOctetAs());
+        final ByteBuf byteAggregator = Unpooled.buffer(AS_2B_BYTES.length);
+        RouteDistinguisherUtil.serializeRouteDistinquisher(expected, byteAggregator);
+        assertArrayEquals(AS_2B_BYTES, byteAggregator.array());
+        assertEquals("0" + SEPARATOR + ADMIN + SEPARATOR + ASSIGNED_NUMBER, parsed.getRdTwoOctetAs().getValue());
+    }
+
     @Test
     public void testIpv4RouteDistinguisher() {
-        final RouteDistinguisher expected = createRouteDistinguisher(1, 10L, IP_ADDRESS);
+        final RouteDistinguisher expected = createRouteDistinguisher(1, IP_ADDRESS, IP_PORT);
         final RouteDistinguisher parsed = RouteDistinguisherUtil.parseRouteDistinguisher(Unpooled.copiedBuffer(IP_BYTES));
         assertEquals(expected.getRdIpv4(), parsed.getRdIpv4());
         final ByteBuf byteAggregator = Unpooled.buffer(IP_BYTES.length);
         RouteDistinguisherUtil.serializeRouteDistinquisher(expected, byteAggregator);
         assertArrayEquals(IP_BYTES, byteAggregator.array());
+        assertEquals(IP_ADDRESS + SEPARATOR + IP_PORT, parsed.getRdIpv4().getValue());
     }
 
     @Test
     public void testAs4BRouteDistinguisher() {
-        final RouteDistinguisher expected = createRouteDistinguisher(2, 65535L, ADMIN);
+        final RouteDistinguisher expected = createRouteDistinguisher(2, ADMIN, ASSIGNED_NUMBER);
         final RouteDistinguisher parsed = RouteDistinguisherUtil.parseRouteDistinguisher(Unpooled.copiedBuffer(AS_4B_BYTES));
         assertEquals(expected.getRdAs(), parsed.getRdAs());
         final ByteBuf byteAggregator = Unpooled.buffer(AS_4B_BYTES.length);
         RouteDistinguisherUtil.serializeRouteDistinquisher(expected, byteAggregator);
         assertArrayEquals(AS_4B_BYTES, byteAggregator.array());
+        assertEquals(ADMIN + SEPARATOR + ASSIGNED_NUMBER, parsed.getRdAs().getValue());
+    }
+
+    @Test
+    public void testParseRouteDistinguisher() {
+        final RouteDistinguisher expected = RouteDistinguisherUtil.parseRouteDistinguisher(ADMIN + SEPARATOR + ASSIGNED_NUMBER);
+        final RouteDistinguisher parsed = RouteDistinguisherUtil.parseRouteDistinguisher(Unpooled.copiedBuffer(AS_4B_BYTES));
+        assertEquals(expected.getRdAs(), parsed.getRdAs());
+
+        final RouteDistinguisher expectedRD = RouteDistinguisherUtil.parseRouteDistinguisher(expected);
+        assertEquals(expectedRD.getRdAs(), parsed.getRdAs());
+
+        final RouteDistinguisher expectedObj = RouteDistinguisherUtil.parseRouteDistinguisher((Object) (ADMIN + SEPARATOR + ASSIGNED_NUMBER));
+        assertEquals(expectedObj.getRdAs(), parsed.getRdAs());
     }
 
     @Test(expected=UnsupportedOperationException.class)
@@ -56,12 +86,25 @@ public class RouteDistinguisherUtilTest {
         }
     }
 
-    private RouteDistinguisher createRouteDistinguisher(final int type, final long assignedNumberSubfield, final String administratorSubfield) {
+    @Test(expected=IllegalArgumentException.class)
+    public void testInvalidRDType() throws Throwable {
+        RouteDistinguisherUtil.parseRouteDistinguisher(Unpooled.copiedBuffer(INVALID_RD_TYPE_BYTES));
+    }
+
+    /**
+     * Create 4-octet AS RD or IPv4 RD, 2-octet AS RD cannot be created with this function
+     * @param administratorSubfield
+     * @param assignedNumberSubfield
+     * @return
+     */
+    private RouteDistinguisher createRouteDistinguisher(final int type, final String administratorSubfield, final String assignedNumberSubfield) {
         final StringBuffer routeDistiguisher = new StringBuffer();
+        if (type == 0) {
+            routeDistiguisher.append(type).append(SEPARATOR);
+        }
         routeDistiguisher.append(administratorSubfield);
         routeDistiguisher.append(SEPARATOR);
         routeDistiguisher.append(assignedNumberSubfield);
         return RouteDistinguisherBuilder.getDefaultInstance(routeDistiguisher.toString());
     }
-
 }