Improve RouteDistinguisherUtil
[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 org.eclipse.jdt.annotation.NonNull;
16 import org.eclipse.jdt.annotation.Nullable;
17 import org.opendaylight.protocol.util.Ipv4Util;
18 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.RdAs;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.RdIpv4;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.RdTwoOctetAs;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.RouteDistinguisher;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.RouteDistinguisherBuilder;
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     private RouteDistinguisherUtil() {
46         // Hidden on purpose
47     }
48
49     /**
50      * Serializes route distinguisher according to type and writes into ByteBuf.
51      */
52     public static void serializeRouteDistinquisher(final RouteDistinguisher distinguisher,
53             final ByteBuf byteAggregator) {
54         requireNonNull(distinguisher);
55         checkState(byteAggregator != null && byteAggregator.isWritable(RD_LENGTH),
56                 "Cannot write Route Distinguisher to provided buffer.");
57         if (distinguisher.getRdTwoOctetAs() != null) {
58             serialize(byteAggregator, distinguisher.getRdTwoOctetAs());
59         } else if (distinguisher.getRdAs() != null) {
60             serialize(byteAggregator, distinguisher.getRdAs());
61         } else if (distinguisher.getRdIpv4() != null) {
62             serialize(byteAggregator, distinguisher.getRdIpv4());
63         } else {
64             LOG.warn("Unable to serialize Route Distinguisher. Invalid RD value found. RD={}", distinguisher);
65         }
66     }
67
68     private static void serialize(final ByteBuf buf, final RdAs as) {
69         final String value = as.getValue();
70         final int first = findSeparator(value, 0);
71         checkNoColon(value, first);
72
73         buf.writeShort(RD_AS_4BYTE);
74         buf.writeInt(Integer.parseUnsignedInt(value.substring(0, first)));
75         buf.writeShort(Integer.parseUnsignedInt(value.substring(first + 1)));
76     }
77
78     private static void serialize(final ByteBuf buf, final RdTwoOctetAs as) {
79         final String value = as.getValue();
80         final int first = findSeparator(value, 0) + 1;
81         final int second = findSeparator(value, first);
82         checkNoColon(value, second);
83
84         buf.writeShort(RD_AS_2BYTE);
85         buf.writeShort(Integer.parseUnsignedInt(value.substring(first, second)));
86         buf.writeInt(Integer.parseUnsignedInt(value.substring(second + 1)));
87     }
88
89     private static void serialize(final ByteBuf buf, final RdIpv4 ipv4) {
90         final String value = ipv4.getValue();
91         final int first = findSeparator(value, 0);
92         checkNoColon(value, first);
93
94         buf.writeShort(RD_IPV4);
95         buf.writeBytes(Ipv4Util.bytesForAddress(new Ipv4AddressNoZone(value.substring(0, first))));
96         buf.writeShort(Integer.parseUnsignedInt(value.substring(first + 1)));
97     }
98
99     private static int findSeparator(final String str, final int fromIndex) {
100         final int found = str.indexOf(SEPARATOR, fromIndex);
101         checkState(found != -1, "Invalid route distinguiser %s", str);
102         return found;
103     }
104
105     private static void checkNoColon(final String str, final int lastIndex) {
106         checkState(str.indexOf(SEPARATOR, lastIndex + 1) == -1, "Invalid route distinguiser %s", str);
107     }
108
109     /**
110      * Parses three types of route distinguisher from given ByteBuf.
111      */
112     public static @NonNull RouteDistinguisher parseRouteDistinguisher(final ByteBuf buffer) {
113         checkState(buffer != null && buffer.isReadable(RD_LENGTH),
114                 "Cannot read Route Distinguisher from provided buffer.");
115         final int type = buffer.readUnsignedShort();
116         switch (type) {
117             case RD_AS_2BYTE:
118                 return new RouteDistinguisher(new RdTwoOctetAs(new StringBuilder()
119                     .append(type)
120                     .append(SEPARATOR)
121                     .append(buffer.readUnsignedShort())
122                     .append(SEPARATOR)
123                     .append(buffer.readUnsignedInt())
124                     .toString()));
125             case RD_IPV4:
126                 return new RouteDistinguisher(new RdIpv4(new StringBuilder()
127                     .append(Ipv4Util.addressForByteBuf(buffer).getValue())
128                     .append(SEPARATOR)
129                     .append(buffer.readUnsignedShort())
130                     .toString()));
131             case RD_AS_4BYTE:
132                 return new RouteDistinguisher(new RdAs(new StringBuilder()
133                     .append(buffer.readUnsignedInt())
134                     .append(SEPARATOR)
135                     .append(buffer.readUnsignedShort())
136                     .toString()));
137             default:
138                 // now that this RD type is not supported, we want to read the remain 6 bytes
139                 // in order to get the byte index correct
140                 final var sb = new StringBuilder();
141                 for (int i = 0; i < 6; i++) {
142                     sb.append("0x").append(Integer.toHexString(buffer.readByte() & 0xFF)).append(' ');
143                 }
144                 LOG.debug("Invalid Route Distinguisher: type={}, rawRouteDistinguisherValue={}", type, sb);
145                 throw new IllegalArgumentException("Invalid Route Distinguisher type " + type);
146         }
147     }
148
149     public static @Nullable RouteDistinguisher parseRouteDistinguisher(final String str) {
150         return str == null ? null : RouteDistinguisherBuilder.getDefaultInstance(str);
151     }
152
153     public static @Nullable RouteDistinguisher parseRouteDistinguisher(final Object obj) {
154         if (obj instanceof String str) {
155             return RouteDistinguisherBuilder.getDefaultInstance(str);
156         } else if (obj instanceof RouteDistinguisher rd) {
157             return rd;
158         } else {
159             return null;
160         }
161     }
162
163     public static @Nullable RouteDistinguisher extractRouteDistinguisher(final DataContainerNode route,
164             final NodeIdentifier rdNid) {
165         final var rdNode = route.childByArg(rdNid);
166         return rdNode == null ? null : parseRouteDistinguisher(rdNode.body());
167     }
168
169 }