Refactor 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 com.google.common.base.Preconditions;
15 import io.netty.buffer.ByteBuf;
16 import org.opendaylight.protocol.util.Ipv4Util;
17 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.RdAs;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.RdIpv4;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.RdTwoOctetAs;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.RouteDistinguisher;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.RouteDistinguisherBuilder;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
24 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
25 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
26 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 /**
31  * Utility class for of RouteDistinguisher serialization and parsing.
32  * https://tools.ietf.org/html/rfc4364#section-4.2
33  */
34 public final class RouteDistinguisherUtil {
35     public static final int RD_LENGTH = 8;
36
37     private static final Logger LOG = LoggerFactory.getLogger(RouteDistinguisherUtil.class);
38     @VisibleForTesting
39     static final char SEPARATOR = ':';
40
41     private RouteDistinguisherUtil() {
42         // Hidden on purpose
43     }
44
45     /**
46      * Serializes route distinguisher according to type and writes into ByteBuf.
47      */
48     public static void serializeRouteDistinquisher(final RouteDistinguisher distinguisher,
49             final ByteBuf byteAggregator) {
50         requireNonNull(distinguisher);
51         checkState(byteAggregator != null && byteAggregator.isWritable(RD_LENGTH),
52                 "Cannot write Route Distinguisher to provided buffer.");
53         if (distinguisher.getRdTwoOctetAs() != null) {
54             serialize(byteAggregator, distinguisher.getRdTwoOctetAs());
55         } else if (distinguisher.getRdAs() != null) {
56             serialize(byteAggregator, distinguisher.getRdAs());
57         } else if (distinguisher.getRdIpv4() != null) {
58             serialize(byteAggregator, distinguisher.getRdIpv4());
59         } else {
60             LOG.warn("Unable to serialize Route Distinguisher. Invalid RD value found. RD={}", distinguisher);
61         }
62     }
63
64     private static void serialize(final ByteBuf buf, final RdAs as) {
65         final String value = as.getValue();
66         final int first = findSeparator(value, 0);
67         checkNoColon(value, first);
68
69         buf.writeShort(RDType.AS_4BYTE.value);
70         buf.writeInt(Integer.parseUnsignedInt(value.substring(0, first)));
71         buf.writeShort(Integer.parseUnsignedInt(value.substring(first + 1)));
72     }
73
74     private static void serialize(final ByteBuf buf, final RdTwoOctetAs as) {
75         final String value = as.getValue();
76         final int first = findSeparator(value, 0) + 1;
77         final int second = findSeparator(value, first);
78         checkNoColon(value, second);
79
80         buf.writeShort(RDType.AS_2BYTE.value);
81         buf.writeShort(Integer.parseUnsignedInt(value.substring(first, second)));
82         buf.writeInt(Integer.parseUnsignedInt(value.substring(second + 1)));
83     }
84
85     private static void serialize(final ByteBuf buf, final RdIpv4 ipv4) {
86         final String value = ipv4.getValue();
87         final int first = findSeparator(value, 0);
88         checkNoColon(value, first);
89
90         buf.writeShort(RDType.IPV4.value);
91         buf.writeBytes(Ipv4Util.bytesForAddress(new Ipv4Address(value.substring(0, first))));
92         buf.writeShort(Integer.parseUnsignedInt(value.substring(first + 1)));
93     }
94
95     private static int findSeparator(final String str, final int fromIndex) {
96         final int found = str.indexOf(SEPARATOR, fromIndex);
97         checkState(found != -1, "Invalid route distinguiser %s", str);
98         return found;
99     }
100
101     private static void checkNoColon(final String str, final int lastIndex) {
102         checkState(str.indexOf(SEPARATOR, lastIndex + 1) == -1, "Invalid route distinguiser %s", str);
103     }
104
105     /**
106      * Parses three types of route distinguisher from given ByteBuf.
107      */
108     public static RouteDistinguisher parseRouteDistinguisher(final ByteBuf buffer) {
109         Preconditions.checkState(buffer != null && buffer.isReadable(RD_LENGTH),
110                 "Cannot read Route Distinguisher from provided buffer.");
111         final int type = buffer.readUnsignedShort();
112         final RDType rdType = RDType.valueOf(type);
113         switch (rdType) {
114             case AS_2BYTE:
115                 return new RouteDistinguisher(new RdTwoOctetAs(new StringBuilder()
116                     .append(type)
117                     .append(SEPARATOR)
118                     .append(buffer.readUnsignedShort())
119                     .append(SEPARATOR)
120                     .append(buffer.readUnsignedInt())
121                     .toString()));
122             case IPV4:
123                 return new RouteDistinguisher(new RdIpv4(new StringBuilder()
124                     .append(Ipv4Util.addressForByteBuf(buffer).getValue())
125                     .append(SEPARATOR)
126                     .append(buffer.readUnsignedShort())
127                     .toString()));
128             case AS_4BYTE:
129                 return new RouteDistinguisher(new RdAs(new StringBuilder()
130                     .append(buffer.readUnsignedInt())
131                     .append(SEPARATOR)
132                     .append(buffer.readUnsignedShort())
133                     .toString()));
134             default:
135                 // now that this RD type is not supported, we want to read the remain 6 bytes
136                 // in order to get the byte index correct
137                 final StringBuilder routeDistiguisher = new StringBuilder();
138                 for (int i = 0; i < 6; i++) {
139                     routeDistiguisher.append("0x").append(Integer.toHexString(buffer.readByte() & 0xFF)).append(' ');
140                 }
141                 LOG.debug("Invalid Route Distinguisher: type={}, rawRouteDistinguisherValue={}", type,
142                     routeDistiguisher);
143                 throw new IllegalArgumentException("Invalid Route Distinguisher type " + type);
144         }
145     }
146
147     public static RouteDistinguisher parseRouteDistinguisher(final String str) {
148         return str == null ? null : RouteDistinguisherBuilder.getDefaultInstance(str);
149     }
150
151     public static RouteDistinguisher parseRouteDistinguisher(final Object obj) {
152         if (obj instanceof String) {
153             return RouteDistinguisherBuilder.getDefaultInstance((String) obj);
154         } else if (obj instanceof RouteDistinguisher) {
155             return (RouteDistinguisher) obj;
156         } else {
157             return null;
158         }
159     }
160
161     public static RouteDistinguisher extractRouteDistinguisher(final DataContainerNode<?> route,
162             final NodeIdentifier rdNid) {
163         final NormalizedNode<?, ?> rdNode = NormalizedNodes.findNode(route, rdNid).orElse(null);
164         return rdNode == null ? null : parseRouteDistinguisher(rdNode.getValue());
165     }
166
167     private enum RDType {
168         AS_2BYTE(0),
169         IPV4(1),
170         AS_4BYTE(2),
171         INVALID(-1);
172
173         public final int value;
174
175         RDType(final int val) {
176             this.value = val;
177         }
178
179         static RDType valueOf(final int value) {
180             for (RDType type : values()) {
181                 if (type.value == value) {
182                     return type;
183                 }
184             }
185             return INVALID;
186         }
187     }
188 }