From bdf3587cfb9cfc4b3161ccc2ea9a42a80a698d5f Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Thu, 26 May 2016 13:00:28 -0700 Subject: [PATCH] BUG-5904 Support 2-byte AS Route Distinguisher - Support type 0 RD - Add universal parse function for RD Change-Id: I78d6a4b9cb0fac62ba796d6ffe54dfa9e712e628 Signed-off-by: Kevin Wang --- .../bgp/concepts/RouteDistinguisherUtil.java | 94 ++++++++++++++++--- .../rev130919/RouteDistinguisherBuilder.java | 19 +++- bgp/concepts/src/main/yang/bgp-types.yang | 20 ++++ .../concepts/RouteDistinguisherUtilTest.java | 53 ++++++++++- 4 files changed, 165 insertions(+), 21 deletions(-) diff --git a/bgp/concepts/src/main/java/org/opendaylight/bgp/concepts/RouteDistinguisherUtil.java b/bgp/concepts/src/main/java/org/opendaylight/bgp/concepts/RouteDistinguisherUtil.java index 4eac844cd6..8636f4b656 100644 --- a/bgp/concepts/src/main/java/org/opendaylight/bgp/concepts/RouteDistinguisherUtil.java +++ b/bgp/concepts/src/main/java/org/opendaylight/bgp/concepts/RouteDistinguisherUtil.java @@ -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; } } diff --git a/bgp/concepts/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/bgp/types/rev130919/RouteDistinguisherBuilder.java b/bgp/concepts/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/bgp/types/rev130919/RouteDistinguisherBuilder.java index 9baeeef446..79c96da1df 100644 --- a/bgp/concepts/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/bgp/types/rev130919/RouteDistinguisherBuilder.java +++ b/bgp/concepts/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/bgp/types/rev130919/RouteDistinguisherBuilder.java @@ -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)); diff --git a/bgp/concepts/src/main/yang/bgp-types.yang b/bgp/concepts/src/main/yang/bgp-types.yang index 7435c8e71a..6c51c4be39 100644 --- a/bgp/concepts/src/main/yang/bgp-types.yang +++ b/bgp/concepts/src/main/yang/bgp-types.yang @@ -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 */ diff --git a/bgp/concepts/src/test/java/org/opendaylight/bgp/concepts/RouteDistinguisherUtilTest.java b/bgp/concepts/src/test/java/org/opendaylight/bgp/concepts/RouteDistinguisherUtilTest.java index 0bcdbdd262..378018beba 100644 --- a/bgp/concepts/src/test/java/org/opendaylight/bgp/concepts/RouteDistinguisherUtilTest.java +++ b/bgp/concepts/src/test/java/org/opendaylight/bgp/concepts/RouteDistinguisherUtilTest.java @@ -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()); } - } -- 2.36.6