2 * Copyright (c) 2016 Pantheon Technologies s.r.o. 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.mdsal.model.ietf.util;
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Verify.verify;
13 import java.util.Arrays;
14 import java.util.HexFormat;
15 import org.eclipse.jdt.annotation.NonNull;
18 * IPv6 address parsing for {@code ietf-inet-types} ipv6-address and ipv6-prefix. This is an internal implementation
19 * class, not meant to be used directly in any shape or form to the outside world, as the code relies on the fact that
20 * the strings presented to it have been previously validated to conform to the regular expressions defined in the YANG
24 * IPv6 routines added by Anton Ivanov on 14.6.2015, revised by Robert Varga
27 * <b>BIG FAT WARNING!!!</b>
28 * Read all of the following before you touch any v6 code or decide to optimize it by invoking a "simple" Guava call.
31 * Java IPv6 is fundamentally broken and Google libraries do not fix it.
33 * <li>Java will always implicitly rewrite v4 mapped into v6 as a v4 address and there is absolutely no way to
34 * override this behaviour</li>
35 * <li>Guava libraries cannot parse non-canonical IPv6. They will throw an exception. Even if they did, they re-use
36 * the same broken java code underneath</li>
38 * This is why we have to parse v6 by ourselves.
41 * The following conversion code is based on inet_cidr_pton_ipv6 in NetBSD.
44 * The original BSD code is licensed under standard BSD license. While we are not obliged to provide an attribution,
45 * credit where credit is due. As far as why it is similar to Sun's sun.net.util please ask Sun why their code has the
46 * same variable names, comments and code flow.
48 public final class Ipv6Utils {
49 public static final int INET6_LENGTH = 16;
56 * Convert Ipv6Address object to a valid Canonical v6 address in byte format.
58 * @param bytes Byte array for output
59 * @param str String representation
60 * @param strLimit String offset which should not be processed
61 * @throws NullPointerException if ipv6address is null
63 @SuppressWarnings("checkstyle:localVariableName")
64 public static void fillIpv6Bytes(final byte @NonNull[] bytes, final String str, final int strLimit) {
65 // Leading :: requires some special handling.
67 if (str.charAt(i) == ':') {
68 // Note ++i side-effect in check
69 checkArgument(str.charAt(++i) == ':', "Invalid v6 address '%s'", str);
72 boolean haveVal = false;
77 while (i < strLimit) {
78 final char ch = str.charAt(i++);
84 // removed overrun check - the regexp checks for valid data
85 bytes[j++] = (byte) (val >>> 8 & 0xff);
86 bytes[j++] = (byte) (val & 0xff);
90 // no need to check separator position validity - regexp does that
97 // frankenstein - v4 attached to v6, mixed notation
98 if (ch == '.' && j + Ipv4Utils.INET4_LENGTH <= INET6_LENGTH) {
100 * This has passed the regexp so it is fairly safe to parse it
101 * straight away. Use the Ipv4Utils for that.
103 Ipv4Utils.fillIpv4Bytes(bytes, j, str, curtok, strLimit);
104 j += Ipv4Utils.INET4_LENGTH;
110 * Business as usual - ipv6 address digit.
111 * We can remove all checks from the original BSD code because
112 * the regexp has already verified that we are not being fed
113 * anything bigger than 0xffff between the separators.
115 val = (val << 4) + HexFormat.fromHexDigit(ch);
120 verifySize(j + Short.BYTES <= INET6_LENGTH, str);
121 bytes[j++] = (byte) (val >> 8 & 0xff);
122 bytes[j++] = (byte) (val & 0xff);
126 verifySize(j != INET6_LENGTH, str);
127 expandZeros(bytes, colonp, j);
129 verifySize(j == INET6_LENGTH, str);
133 private static void verifySize(final boolean expression, final String str) {
134 verify(expression, "Overrun in parsing of '%s', should not occur", str);
137 private static void expandZeros(final byte[] bytes, final int where, final int filledBytes) {
138 final int tailLength = filledBytes - where;
139 final int tailOffset = INET6_LENGTH - tailLength;
140 System.arraycopy(bytes, where, bytes, tailOffset, tailLength);
141 Arrays.fill(bytes, where, tailOffset, (byte)0);