Bump upstreams for Silicon
[genius.git] / ipv6util / api / src / main / java / org / opendaylight / genius / ipv6util / api / Ipv6Util.java
1 /*
2  * Copyright (c) 2018 Alten Calsoft Labs India Pvt Ltd. 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.genius.ipv6util.api;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.net.InetAddresses;
13 import java.net.InetAddress;
14 import java.net.UnknownHostException;
15 import java.nio.ByteBuffer;
16 import java.util.Arrays;
17 import org.apache.commons.lang3.StringUtils;
18 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
19 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
20 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.packet.rev160620.EthernetHeader;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.packet.rev160620.Ipv6Header;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26 public final class Ipv6Util {
27
28     private static final Logger LOG = LoggerFactory.getLogger(Ipv6Util.class);
29
30     private Ipv6Util() {
31
32     }
33
34     public static String bytesToHexString(byte[] bytes) {
35         if (bytes == null) {
36             return "null";
37         }
38         StringBuilder buf = new StringBuilder();
39         for (int i = 0; i < bytes.length; i++) {
40             if (i > 0) {
41                 buf.append(":");
42             }
43             short u8byte = (short) (bytes[i] & 0xff);
44             String tmp = Integer.toHexString(u8byte);
45             if (tmp.length() == 1) {
46                 buf.append("0");
47             }
48             buf.append(tmp);
49         }
50         return buf.toString();
51     }
52
53     public static byte[] bytesFromHexString(String values) {
54         String target = "";
55         if (values != null) {
56             target = values;
57         }
58         String[] octets = target.split(":");
59
60         byte[] ret = new byte[octets.length];
61         for (int i = 0; i < octets.length; i++) {
62             ret[i] = Integer.valueOf(octets[i], 16).byteValue();
63         }
64         return ret;
65     }
66
67     public static int calculateIcmpv6Checksum(byte[] packet, Ipv6Header ip6Hdr) {
68         long checksum = getSummation(ip6Hdr.getSourceIpv6());
69         checksum += getSummation(ip6Hdr.getDestinationIpv6());
70         checksum = normalizeChecksum(checksum);
71
72         checksum += ip6Hdr.getIpv6Length().toJava();
73         checksum += ip6Hdr.getNextHeader().toJava();
74
75         int icmp6Offset = Ipv6Constants.ICMPV6_OFFSET;
76         long value = (packet[icmp6Offset] & 0xff) << 8 | packet[icmp6Offset + 1] & 0xff;
77         checksum += value;
78         checksum = normalizeChecksum(checksum);
79         icmp6Offset += 2;
80
81         // move to icmp6 payload skipping the checksum field
82         icmp6Offset += 2;
83         int length = packet.length - icmp6Offset;
84         while (length > 1) {
85             value = (packet[icmp6Offset] & 0xff) << 8 | packet[icmp6Offset + 1] & 0xff;
86             checksum += value;
87             checksum = normalizeChecksum(checksum);
88             icmp6Offset += 2;
89             length -= 2;
90         }
91
92         if (length > 0) {
93             checksum += packet[icmp6Offset];
94             checksum = normalizeChecksum(checksum);
95         }
96
97         int finalChecksum = (int) (~checksum & 0xffff);
98         return finalChecksum;
99     }
100
101     public static boolean validateChecksum(byte[] packet, Ipv6Header ip6Hdr, int recvChecksum) {
102         return calculateIcmpv6Checksum(packet, ip6Hdr) == recvChecksum;
103     }
104
105     private static long getSummation(Ipv6Address addr) {
106         byte[] baddr = null;
107         try {
108             baddr = InetAddress.getByName(addr.getValue()).getAddress();
109         } catch (UnknownHostException e) {
110             LOG.error("getSummation: Failed to deserialize address {}", addr.getValue(), e);
111             return 0;
112         }
113
114         long sum = 0;
115         int len = 0;
116         long value = 0;
117         while (len < baddr.length) {
118             value = (baddr[len] & 0xff) << 8 | baddr[len + 1] & 0xff;
119             sum += value;
120             sum = normalizeChecksum(sum);
121             len += 2;
122         }
123         return sum;
124     }
125
126     private static long normalizeChecksum(long value) {
127         if ((value & 0xffff0000) != 0) {
128             value = value & 0xffff;
129             value += 1;
130         }
131         return value;
132     }
133
134     public static byte[] convertEthernetHeaderToByte(EthernetHeader ethPdu) {
135         byte[] data = new byte[16];
136         Arrays.fill(data, (byte) 0);
137
138         ByteBuffer buf = ByteBuffer.wrap(data);
139         buf.put(bytesFromHexString(ethPdu.getDestinationMac().getValue()));
140         buf.put(bytesFromHexString(ethPdu.getSourceMac().getValue()));
141         buf.putShort((short) ethPdu.getEthertype().intValue());
142         return data;
143     }
144
145     public static byte[] convertIpv6HeaderToByte(Ipv6Header ip6Pdu) {
146         byte[] data = new byte[128];
147         Arrays.fill(data, (byte) 0);
148
149         ByteBuffer buf = ByteBuffer.wrap(data);
150         long flowLabel = (long) (ip6Pdu.getVersion().toJava() & 0x0f) << 28
151                                     | ip6Pdu.getFlowLabel().toJava() & 0x0fffffff;
152         buf.putInt((int) flowLabel);
153         buf.putShort((short) ip6Pdu.getIpv6Length().intValue());
154         buf.put((byte) ip6Pdu.getNextHeader().shortValue());
155         buf.put((byte) ip6Pdu.getHopLimit().shortValue());
156         try {
157             byte[] baddr = InetAddress.getByName(ip6Pdu.getSourceIpv6().getValue()).getAddress();
158             buf.put(baddr);
159             baddr = InetAddress.getByName(ip6Pdu.getDestinationIpv6().getValue()).getAddress();
160             buf.put(baddr);
161         } catch (UnknownHostException e) {
162             LOG.error("convertIpv6HeaderToByte: Failed to serialize src, dest address", e);
163         }
164         return data;
165     }
166
167     public static Ipv6Address getIpv6LinkLocalAddressFromMac(MacAddress mac) {
168         byte[] octets = bytesFromHexString(mac.getValue());
169
170         /*
171          * As per the RFC2373, steps involved to generate a LLA include 1. Convert the 48 bit MAC address to
172          * 64 bit value by inserting 0xFFFE between OUI and NIC Specific part. 2. Invert the Universal/Local
173          * flag in the OUI portion of the address. 3. Use the prefix "FE80::/10" along with the above 64 bit
174          * Interface identifier to generate the IPv6 LLA.
175          */
176
177         StringBuilder interfaceID = new StringBuilder();
178         short u8byte = (short) (octets[0] & 0xff);
179         u8byte ^= 1 << 1;
180         interfaceID.append(Integer.toHexString(0xFF & u8byte));
181         interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[1]), 2, "0"));
182         interfaceID.append(":");
183         interfaceID.append(Integer.toHexString(0xFF & octets[2]));
184         interfaceID.append("ff:fe");
185         interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[3]), 2, "0"));
186         interfaceID.append(":");
187         interfaceID.append(Integer.toHexString(0xFF & octets[4]));
188         interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[5]), 2, "0"));
189
190         // Return the address in its fully expanded format.
191         Ipv6Address ipv6LLA =
192                 new Ipv6Address(InetAddresses.forString("fe80:0:0:0:" + interfaceID.toString()).getHostAddress());
193         return ipv6LLA;
194     }
195
196     public static Ipv6Address getIpv6SolicitedNodeMcastAddress(Ipv6Address ipv6Address) {
197
198         /*
199          * According to RFC 4291, a Solicited Node Multicast Address is derived by adding the 24 lower order
200          * bits with the Solicited Node multicast prefix (i.e., FF02::1:FF00:0/104). Example: For
201          * IPv6Address of FE80::2AA:FF:FE28:9C5A, the corresponding solicited node multicast address would
202          * be FF02::1:FF28:9C5A
203          */
204
205         byte[] octets;
206         try {
207             octets = InetAddress.getByName(ipv6Address.getValue()).getAddress();
208         } catch (UnknownHostException e) {
209             LOG.error("getIpv6SolicitedNodeMcastAddress: Failed to serialize ipv6Address ", e);
210             return null;
211         }
212
213         // Return the address in its fully expanded format.
214         Ipv6Address solictedV6Address =
215                 new Ipv6Address(InetAddresses
216                         .forString("ff02::1:ff" + StringUtils.leftPad(Integer.toHexString(0xFF & octets[13]), 2, "0")
217                                 + ":" + StringUtils.leftPad(Integer.toHexString(0xFF & octets[14]), 2, "0")
218                                 + StringUtils.leftPad(Integer.toHexString(0xFF & octets[15]), 2, "0"))
219                         .getHostAddress());
220
221         return solictedV6Address;
222     }
223
224     public static MacAddress getIpv6MulticastMacAddress(Ipv6Address ipv6Address) {
225
226         /*
227          * According to RFC 2464, a Multicast MAC address is derived by concatenating 32 lower order bits of
228          * IPv6 Multicast Address with the multicast prefix (i.e., 33:33). Example: For Multicast
229          * IPv6Address of FF02::1:FF28:9C5A, the corresponding L2 multicast address would be 33:33:28:9C:5A
230          */
231         byte[] octets;
232         try {
233             octets = InetAddress.getByName(ipv6Address.getValue()).getAddress();
234         } catch (UnknownHostException e) {
235             LOG.error("getIpv6MulticastMacAddress: Failed to serialize ipv6Address ", e);
236             return null;
237         }
238
239         String macAddress = "33:33:" + StringUtils.leftPad(Integer.toHexString(0xFF & octets[12]), 2, "0") + ":"
240                 + StringUtils.leftPad(Integer.toHexString(0xFF & octets[13]), 2, "0") + ":"
241                 + StringUtils.leftPad(Integer.toHexString(0xFF & octets[14]), 2, "0") + ":"
242                 + StringUtils.leftPad(Integer.toHexString(0xFF & octets[15]), 2, "0");
243
244         return new MacAddress(macAddress);
245     }
246
247     /**
248      * Gets the formatted IP address. <br>
249      * e.g., <br>
250      * 1. input = "1001:db8:0:2::1", return = "1001:db8:0:2:0:0:0:1" <br>
251      * 2. input = "2607:f0d0:1002:51::4", return = "2607:f0d0:1002:51:0:0:0:4" <br>
252      * 3. input = "1001:db8:0:2:0:0:0:1", return = "1001:db8:0:2:0:0:0:1" <br>
253      * 4. input = "10.0.0.10", return = "10.0.0.10"
254      *
255      * @param ipAddress the IP address
256      * @return the formatted IP address
257      */
258     public static String getFormattedIpAddress(IpAddress ipAddress) {
259         requireNonNull(ipAddress, "ipAddress is null");
260         if (ipAddress.getIpv4Address() != null) {
261             // No formatting required for IPv4 address.
262             return ipAddress.getIpv4Address().getValue();
263         } else {
264             // IPv6 case
265             return getFormattedIpv6Address(ipAddress.getIpv6Address());
266         }
267     }
268
269     /**
270      * Gets the formatted IPv6 address. <br>
271      * e.g., <br>
272      * 1. input = "1001:db8:0:2::1", return = "1001:db8:0:2:0:0:0:1" <br>
273      * 2. input = "2607:f0d0:1002:51::4", return = "2607:f0d0:1002:51:0:0:0:4" <br>
274      * 3. input = "1001:db8:0:2:0:0:0:1", return = "1001:db8:0:2:0:0:0:1" <br>
275      *
276      * @param ipv6Address the IPv6 address
277      * @return the formatted IPv6 address
278      */
279     public static String getFormattedIpv6Address(Ipv6Address ipv6Address) {
280         try {
281             return InetAddress.getByName(ipv6Address.getValue()).getHostAddress();
282         } catch (UnknownHostException e) {
283             throw new IllegalArgumentException("Invalid ipv6Address=" + ipv6Address, e);
284         }
285     }
286 }