2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.bgp.concepts;
10 import static com.google.common.base.Preconditions.checkState;
11 import static java.util.Objects.requireNonNull;
13 import com.google.common.annotations.VisibleForTesting;
14 import io.netty.buffer.ByteBuf;
15 import java.util.regex.Pattern;
16 import org.eclipse.jdt.annotation.NonNull;
17 import org.eclipse.jdt.annotation.Nullable;
18 import org.opendaylight.protocol.util.Ipv4Util;
19 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.RdAs;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.RdIpv4;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.RdTwoOctetAs;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.RouteDistinguisher;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
25 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
30 * Utility class for of RouteDistinguisher serialization and parsing.
31 * https://tools.ietf.org/html/rfc4364#section-4.2
33 public final class RouteDistinguisherUtil {
34 public static final int RD_LENGTH = 8;
36 private static final Logger LOG = LoggerFactory.getLogger(RouteDistinguisherUtil.class);
38 static final char SEPARATOR = ':';
40 // Route descriptor types
41 private static final int RD_AS_2BYTE = 0;
42 private static final int RD_IPV4 = 1;
43 private static final int RD_AS_4BYTE = 2;
45 // Patterns for parsing String representation
46 private static final Pattern AS_2BYTE_PATTERN =
48 + "([0-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|"
49 + "[1-5][0-9][0-9][0-9][0-9]|6[0-4][0-9][0-9][0-9]|"
50 + "65[0-4][0-9][0-9]|655[0-2][0-9]|6553[0-5])"
52 + "([0-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|"
53 + "[1-9][0-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9][0-9]|"
54 + "[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]|"
55 + "[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]|"
56 + "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]|"
57 + "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]|"
58 + "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]|"
59 + "42949672[0-8][0-9]|429496729[0-5])");
61 private static final Pattern IPV4_PATTERN =
62 Pattern.compile("((([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\.){3}"
63 + "([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]))"
65 + "([0-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|"
66 + "[1-5][0-9][0-9][0-9][0-9]|6[0-4][0-9][0-9][0-9]|"
67 + "65[0-4][0-9][0-9]|655[0-2][0-9]|6553[0-5])");
69 private static final Pattern AS_4BYTE_PATTERN =
70 Pattern.compile("([0-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|"
71 + "[1-9][0-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9][0-9]|"
72 + "[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]|"
73 + "[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]|"
74 + "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]|"
75 + "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]|"
76 + "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]|"
77 + "42949672[0-8][0-9]|429496729[0-5])"
79 + "([0-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|"
80 + "[1-5][0-9][0-9][0-9][0-9]|6[0-4][0-9][0-9][0-9]|"
81 + "65[0-4][0-9][0-9]|655[0-2][0-9]|6553[0-5])");
84 private RouteDistinguisherUtil() {
89 * Serializes route distinguisher according to type and writes into ByteBuf.
91 public static void serializeRouteDistinquisher(final RouteDistinguisher distinguisher,
92 final ByteBuf byteAggregator) {
93 requireNonNull(distinguisher);
94 checkState(byteAggregator != null && byteAggregator.isWritable(RD_LENGTH),
95 "Cannot write Route Distinguisher to provided buffer.");
96 if (distinguisher.getRdTwoOctetAs() != null) {
97 serialize(byteAggregator, distinguisher.getRdTwoOctetAs());
98 } else if (distinguisher.getRdAs() != null) {
99 serialize(byteAggregator, distinguisher.getRdAs());
100 } else if (distinguisher.getRdIpv4() != null) {
101 serialize(byteAggregator, distinguisher.getRdIpv4());
103 LOG.warn("Unable to serialize Route Distinguisher. Invalid RD value found. RD={}", distinguisher);
107 private static void serialize(final ByteBuf buf, final RdAs as) {
108 final String value = as.getValue();
109 final int first = findSeparator(value, 0);
110 checkNoColon(value, first);
112 buf.writeShort(RD_AS_4BYTE);
113 buf.writeInt(Integer.parseUnsignedInt(value.substring(0, first)));
114 buf.writeShort(Integer.parseUnsignedInt(value.substring(first + 1)));
117 private static void serialize(final ByteBuf buf, final RdTwoOctetAs as) {
118 final String value = as.getValue();
119 final int first = findSeparator(value, 0) + 1;
120 final int second = findSeparator(value, first);
121 checkNoColon(value, second);
123 buf.writeShort(RD_AS_2BYTE);
124 buf.writeShort(Integer.parseUnsignedInt(value.substring(first, second)));
125 buf.writeInt(Integer.parseUnsignedInt(value.substring(second + 1)));
128 private static void serialize(final ByteBuf buf, final RdIpv4 ipv4) {
129 final String value = ipv4.getValue();
130 final int first = findSeparator(value, 0);
131 checkNoColon(value, first);
133 buf.writeShort(RD_IPV4);
134 buf.writeBytes(Ipv4Util.bytesForAddress(new Ipv4AddressNoZone(value.substring(0, first))));
135 buf.writeShort(Integer.parseUnsignedInt(value.substring(first + 1)));
138 private static int findSeparator(final String str, final int fromIndex) {
139 final int found = str.indexOf(SEPARATOR, fromIndex);
140 checkState(found != -1, "Invalid route distinguiser %s", str);
144 private static void checkNoColon(final String str, final int lastIndex) {
145 checkState(str.indexOf(SEPARATOR, lastIndex + 1) == -1, "Invalid route distinguiser %s", str);
149 * Parses three types of route distinguisher from given ByteBuf.
151 public static @NonNull RouteDistinguisher parseRouteDistinguisher(final ByteBuf buffer) {
152 checkState(buffer != null && buffer.isReadable(RD_LENGTH),
153 "Cannot read Route Distinguisher from provided buffer.");
154 final int type = buffer.readUnsignedShort();
157 return new RouteDistinguisher(new RdTwoOctetAs(new StringBuilder()
160 .append(buffer.readUnsignedShort())
162 .append(buffer.readUnsignedInt())
165 return new RouteDistinguisher(new RdIpv4(new StringBuilder()
166 .append(Ipv4Util.addressForByteBuf(buffer).getValue())
168 .append(buffer.readUnsignedShort())
171 return new RouteDistinguisher(new RdAs(new StringBuilder()
172 .append(buffer.readUnsignedInt())
174 .append(buffer.readUnsignedShort())
177 // now that this RD type is not supported, we want to read the remain 6 bytes
178 // in order to get the byte index correct
179 final var sb = new StringBuilder();
180 for (int i = 0; i < 6; i++) {
181 sb.append("0x").append(Integer.toHexString(buffer.readByte() & 0xFF)).append(' ');
183 LOG.debug("Invalid Route Distinguisher: type={}, rawRouteDistinguisherValue={}", type, sb);
184 throw new IllegalArgumentException("Invalid Route Distinguisher type " + type);
188 public static @NonNull RouteDistinguisher parseRouteDistinguisher(final String defaultValue) {
189 if (AS_2BYTE_PATTERN.matcher(defaultValue).matches()) {
190 return new RouteDistinguisher(new RdTwoOctetAs(defaultValue));
191 } else if (IPV4_PATTERN.matcher(defaultValue).matches()) {
192 return new RouteDistinguisher(new RdIpv4(defaultValue));
193 } else if (AS_4BYTE_PATTERN.matcher(defaultValue).matches()) {
194 return new RouteDistinguisher(new RdAs(defaultValue));
196 throw new IllegalArgumentException("Cannot create Route Distinguisher from " + defaultValue);
200 public static @Nullable RouteDistinguisher extractRouteDistinguisher(final DataContainerNode route,
201 final NodeIdentifier rdNid) {
202 final var rdNode = route.childByArg(rdNid);
203 return rdNode == null ? null : parseRouteDistinguisher((String) rdNode.body());