Eliminate RouteDistinguisherBuilder
[bgpcep.git] / bgp / concepts / src / main / java / org / opendaylight / bgp / concepts / RouteDistinguisherUtil.java
1 /*
2  * Copyright (c) 2016 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.bgp.concepts;
9
10 import static com.google.common.base.Preconditions.checkState;
11 import static java.util.Objects.requireNonNull;
12
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;
28
29 /**
30  * Utility class for of RouteDistinguisher serialization and parsing.
31  * https://tools.ietf.org/html/rfc4364#section-4.2
32  */
33 public final class RouteDistinguisherUtil {
34     public static final int RD_LENGTH = 8;
35
36     private static final Logger LOG = LoggerFactory.getLogger(RouteDistinguisherUtil.class);
37     @VisibleForTesting
38     static final char SEPARATOR = ':';
39
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;
44
45     // Patterns for parsing String representation
46     private static final Pattern AS_2BYTE_PATTERN =
47         Pattern.compile("0:"
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])"
51             + ":"
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])");
60
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]))"
64             + ":"
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])");
68
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])"
78             + ":"
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])");
82
83
84     private RouteDistinguisherUtil() {
85         // Hidden on purpose
86     }
87
88     /**
89      * Serializes route distinguisher according to type and writes into ByteBuf.
90      */
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());
102         } else {
103             LOG.warn("Unable to serialize Route Distinguisher. Invalid RD value found. RD={}", distinguisher);
104         }
105     }
106
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);
111
112         buf.writeShort(RD_AS_4BYTE);
113         buf.writeInt(Integer.parseUnsignedInt(value.substring(0, first)));
114         buf.writeShort(Integer.parseUnsignedInt(value.substring(first + 1)));
115     }
116
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);
122
123         buf.writeShort(RD_AS_2BYTE);
124         buf.writeShort(Integer.parseUnsignedInt(value.substring(first, second)));
125         buf.writeInt(Integer.parseUnsignedInt(value.substring(second + 1)));
126     }
127
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);
132
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)));
136     }
137
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);
141         return found;
142     }
143
144     private static void checkNoColon(final String str, final int lastIndex) {
145         checkState(str.indexOf(SEPARATOR, lastIndex + 1) == -1, "Invalid route distinguiser %s", str);
146     }
147
148     /**
149      * Parses three types of route distinguisher from given ByteBuf.
150      */
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();
155         switch (type) {
156             case RD_AS_2BYTE:
157                 return new RouteDistinguisher(new RdTwoOctetAs(new StringBuilder()
158                     .append(type)
159                     .append(SEPARATOR)
160                     .append(buffer.readUnsignedShort())
161                     .append(SEPARATOR)
162                     .append(buffer.readUnsignedInt())
163                     .toString()));
164             case RD_IPV4:
165                 return new RouteDistinguisher(new RdIpv4(new StringBuilder()
166                     .append(Ipv4Util.addressForByteBuf(buffer).getValue())
167                     .append(SEPARATOR)
168                     .append(buffer.readUnsignedShort())
169                     .toString()));
170             case RD_AS_4BYTE:
171                 return new RouteDistinguisher(new RdAs(new StringBuilder()
172                     .append(buffer.readUnsignedInt())
173                     .append(SEPARATOR)
174                     .append(buffer.readUnsignedShort())
175                     .toString()));
176             default:
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(' ');
182                 }
183                 LOG.debug("Invalid Route Distinguisher: type={}, rawRouteDistinguisherValue={}", type, sb);
184                 throw new IllegalArgumentException("Invalid Route Distinguisher type " + type);
185         }
186     }
187
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));
195         } else {
196             throw new IllegalArgumentException("Cannot create Route Distinguisher from " + defaultValue);
197         }
198     }
199
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());
204     }
205 }