2 * Copyright (c) 2018 Alten Calsoft Labs India Pvt Ltd. 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.genius.ipv6util.api;
10 import static java.util.Objects.requireNonNull;
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;
26 public final class Ipv6Util {
28 private static final Logger LOG = LoggerFactory.getLogger(Ipv6Util.class);
34 public static String bytesToHexString(byte[] bytes) {
38 StringBuilder buf = new StringBuilder();
39 for (int i = 0; i < bytes.length; i++) {
43 short u8byte = (short) (bytes[i] & 0xff);
44 String tmp = Integer.toHexString(u8byte);
45 if (tmp.length() == 1) {
50 return buf.toString();
53 public static byte[] bytesFromHexString(String values) {
58 String[] octets = target.split(":");
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();
67 public static int calculateIcmpv6Checksum(byte[] packet, Ipv6Header ip6Hdr) {
68 long checksum = getSummation(ip6Hdr.getSourceIpv6());
69 checksum += getSummation(ip6Hdr.getDestinationIpv6());
70 checksum = normalizeChecksum(checksum);
72 checksum += ip6Hdr.getIpv6Length().toJava();
73 checksum += ip6Hdr.getNextHeader().toJava();
75 int icmp6Offset = Ipv6Constants.ICMPV6_OFFSET;
76 long value = (packet[icmp6Offset] & 0xff) << 8 | packet[icmp6Offset + 1] & 0xff;
78 checksum = normalizeChecksum(checksum);
81 // move to icmp6 payload skipping the checksum field
83 int length = packet.length - icmp6Offset;
85 value = (packet[icmp6Offset] & 0xff) << 8 | packet[icmp6Offset + 1] & 0xff;
87 checksum = normalizeChecksum(checksum);
93 checksum += packet[icmp6Offset];
94 checksum = normalizeChecksum(checksum);
97 int finalChecksum = (int) (~checksum & 0xffff);
101 public static boolean validateChecksum(byte[] packet, Ipv6Header ip6Hdr, int recvChecksum) {
102 return calculateIcmpv6Checksum(packet, ip6Hdr) == recvChecksum;
105 private static long getSummation(Ipv6Address addr) {
108 baddr = InetAddress.getByName(addr.getValue()).getAddress();
109 } catch (UnknownHostException e) {
110 LOG.error("getSummation: Failed to deserialize address {}", addr.getValue(), e);
117 while (len < baddr.length) {
118 value = (baddr[len] & 0xff) << 8 | baddr[len + 1] & 0xff;
120 sum = normalizeChecksum(sum);
126 private static long normalizeChecksum(long value) {
127 if ((value & 0xffff0000) != 0) {
128 value = value & 0xffff;
134 public static byte[] convertEthernetHeaderToByte(EthernetHeader ethPdu) {
135 byte[] data = new byte[16];
136 Arrays.fill(data, (byte) 0);
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());
145 public static byte[] convertIpv6HeaderToByte(Ipv6Header ip6Pdu) {
146 byte[] data = new byte[128];
147 Arrays.fill(data, (byte) 0);
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());
157 byte[] baddr = InetAddress.getByName(ip6Pdu.getSourceIpv6().getValue()).getAddress();
159 baddr = InetAddress.getByName(ip6Pdu.getDestinationIpv6().getValue()).getAddress();
161 } catch (UnknownHostException e) {
162 LOG.error("convertIpv6HeaderToByte: Failed to serialize src, dest address", e);
167 public static Ipv6Address getIpv6LinkLocalAddressFromMac(MacAddress mac) {
168 byte[] octets = bytesFromHexString(mac.getValue());
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.
177 StringBuilder interfaceID = new StringBuilder();
178 short u8byte = (short) (octets[0] & 0xff);
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"));
190 // Return the address in its fully expanded format.
191 Ipv6Address ipv6LLA =
192 new Ipv6Address(InetAddresses.forString("fe80:0:0:0:" + interfaceID.toString()).getHostAddress());
196 public static Ipv6Address getIpv6SolicitedNodeMcastAddress(Ipv6Address ipv6Address) {
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
207 octets = InetAddress.getByName(ipv6Address.getValue()).getAddress();
208 } catch (UnknownHostException e) {
209 LOG.error("getIpv6SolicitedNodeMcastAddress: Failed to serialize ipv6Address ", e);
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"))
221 return solictedV6Address;
224 public static MacAddress getIpv6MulticastMacAddress(Ipv6Address ipv6Address) {
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
233 octets = InetAddress.getByName(ipv6Address.getValue()).getAddress();
234 } catch (UnknownHostException e) {
235 LOG.error("getIpv6MulticastMacAddress: Failed to serialize ipv6Address ", e);
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");
244 return new MacAddress(macAddress);
248 * Gets the formatted IP address. <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"
255 * @param ipAddress the IP address
256 * @return the formatted IP address
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();
265 return getFormattedIpv6Address(ipAddress.getIpv6Address());
270 * Gets the formatted IPv6 address. <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>
276 * @param ipv6Address the IPv6 address
277 * @return the formatted IPv6 address
279 public static String getFormattedIpv6Address(Ipv6Address ipv6Address) {
281 return InetAddress.getByName(ipv6Address.getValue()).getHostAddress();
282 } catch (UnknownHostException e) {
283 throw new IllegalArgumentException("Invalid ipv6Address=" + ipv6Address, e);