/* * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.common; import com.google.common.base.Preconditions; import com.google.common.base.Splitter; import com.google.common.base.Strings; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; import com.google.common.net.InetAddresses; import com.google.common.primitives.UnsignedBytes; import java.math.BigInteger; import java.net.Inet4Address; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.Iterator; import java.util.List; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DottedQuad; import org.opendaylight.yang.gen.v1.urn.opendaylight.opendaylight.ipv6.arbitrary.bitmask.fields.rev160224.Ipv6ArbitraryMask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class IpConversionUtil { private static final Logger LOG = LoggerFactory.getLogger(IpConversionUtil.class); public static final String PREFIX_SEPARATOR = "/"; public static final Splitter PREFIX_SPLITTER = Splitter.on('/'); private static final int INADDR4SZ = 4; private static final int INADDR6SZ = 16; private static final int INT16SZ = 2; private static final int IPV4_ADDRESS_LENGTH = 32; private static final int IPV6_ADDRESS_LENGTH = 128; private static final String DEFAULT_ARBITRARY_BIT_MASK = "255.255.255.255"; private static final String DEFAULT_IPV6_ARBITRARY_BITMASK = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"; /* * Prefix bytearray lookup table. We concatenate the prefixes * to a single byte array and perform offset lookups to ensure * the table is contiguous and save some space. */ private static final byte[] PREFIX_BYTEARRAYS; static { final byte[] a = new byte[(INADDR6SZ * Byte.SIZE + 1) * INADDR6SZ]; int offset = 0; for (int p = 0; p <= INADDR6SZ * Byte.SIZE; ++p) { int prefix = p; for (int i = 0; i < INADDR6SZ; ++i) { a[offset++] = (byte) nextNibble(prefix); prefix -= Byte.SIZE; } } PREFIX_BYTEARRAYS = a; } private IpConversionUtil() { throw new UnsupportedOperationException("This class should not be instantiated."); } public static Iterator<String> splitToParts(final Ipv4Prefix ipv4Prefix) { return PREFIX_SPLITTER.split(ipv4Prefix.getValue()).iterator(); } public static Iterator<String> splitToParts(final Ipv4Address ipv4Address) { /* Invalid (Ab)use of ip address as prefix!!! */ return Iterators.forArray(ipv4Address.getValue(), String.valueOf(IPV4_ADDRESS_LENGTH)); } public static Iterator<String> splitToParts(final Ipv6Prefix ipv6Prefix) { return PREFIX_SPLITTER.split(ipv6Prefix.getValue()).iterator(); } public static Iterator<String> splitToParts(final Ipv6Address ipv6Address) { /* Invalid (Ab)use of ip address as prefix!!! */ return Iterators.forArray(ipv6Address.getValue(), String.valueOf(IPV6_ADDRESS_LENGTH)); } /* This forest of functions has a purpose: * * 1. There are multiple coding styles around the plugin, this is necessary in order to have * one mechanism to convert them all, one mechanism to find them... * 2. I hope that one day yangtools will actually deliver code fit for purpose in a packet * processing application (presently it is not. When this happens, these can be optimized * for "side-load" of pre-vetted data. Example. IP Address (v4 or v6) is prevetted left of the * prefix. It should be loadable into Prefix without _RERUNNING_ 100ms+ of regexps. When (and if) * that happens, it will be a simple fix here without chasing it across the whole plugin. */ public static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address){ return IetfInetUtil.INSTANCE.ipv4PrefixFor(ipv4Address); } public static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address, final String mask){ /* * Ipv4Address has already validated the address part of the prefix, * It is mandated to comply to the same regexp as the address * There is absolutely no point rerunning additional checks vs this * Note - there is no canonical form check here!!! */ if (null != mask && !mask.isEmpty()) { return new Ipv4Prefix(ipv4Address.getValue() + PREFIX_SEPARATOR + mask); } else { return new Ipv4Prefix(ipv4Address.getValue() + PREFIX_SEPARATOR + IPV4_ADDRESS_LENGTH); } } public static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address, final int intmask){ return IetfInetUtil.INSTANCE.ipv4PrefixFor(ipv4Address, intmask); } public static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address, final byte [] bytemask){ if (bytemask == null) { return createPrefix(ipv4Address); } return IetfInetUtil.INSTANCE.ipv4PrefixFor(ipv4Address, countBits(bytemask)); } public static DottedQuad createArbitraryBitMask(final byte [] bitmask) { DottedQuad dottedQuad = null; if (bitmask == null ) { dottedQuad = new DottedQuad(DEFAULT_ARBITRARY_BIT_MASK); } else { try { dottedQuad = new DottedQuad(InetAddress.getByAddress(bitmask).getHostAddress()); } catch (UnknownHostException e) { LOG.error("Failed to create the dottedQuad notation for the given mask ", e); } } return dottedQuad; } public static Ipv6ArbitraryMask createIpv6ArbitraryBitMask(final byte [] bitmask) { Ipv6ArbitraryMask ipv6ArbitraryMask = null; if (bitmask == null ) { ipv6ArbitraryMask = new Ipv6ArbitraryMask(DEFAULT_IPV6_ARBITRARY_BITMASK); } else { try { ipv6ArbitraryMask = new Ipv6ArbitraryMask(InetAddress.getByAddress(bitmask).getHostAddress()); } catch (UnknownHostException e) { LOG.error("Failed to create the Ipv6ArbitraryMask notation for the given mask ", e); } } return ipv6ArbitraryMask; } public static Ipv6Prefix createPrefix(final Ipv6Address ipv6Address){ return IetfInetUtil.INSTANCE.ipv6PrefixFor(ipv6Address); } public static Ipv6Prefix createPrefix(final Ipv6Address ipv6Address, final String mask){ /* * Ipv6Address has already validated the address part of the prefix, * It is mandated to comply to the same regexp as the address * There is absolutely no point rerunning additional checks vs this * Note - there is no canonical form check here!!! */ if (Strings.isNullOrEmpty(mask)) { return new Ipv6Prefix(ipv6Address.getValue() + PREFIX_SEPARATOR + String.valueOf(IPV6_ADDRESS_LENGTH)); } else { return new Ipv6Prefix(ipv6Address.getValue() + PREFIX_SEPARATOR + mask); } } public static Ipv6Prefix createPrefix(final Ipv6Address ipv6Address, final int intmask){ return IetfInetUtil.INSTANCE.ipv6PrefixFor(ipv6Address, intmask); } public static Ipv6Prefix createPrefix(final Ipv6Address ipv6Address, final byte [] bytemask){ if (bytemask == null) { return createPrefix(ipv6Address); } return IetfInetUtil.INSTANCE.ipv6PrefixFor(ipv6Address, countBits(bytemask)); } public static Integer extractPrefix(final Ipv4Prefix ipv4Prefix) { return IetfInetUtil.INSTANCE.splitIpv4Prefix(ipv4Prefix).getValue(); } public static Integer extractPrefix(final Ipv6Prefix ipv6Prefix) { return IetfInetUtil.INSTANCE.splitIpv6Prefix(ipv6Prefix).getValue(); } public static Integer extractPrefix(final Ipv4Address ipv4Prefix) { return IPV4_ADDRESS_LENGTH; } public static Integer extractPrefix(final Ipv6Address ipv6Prefix) { return 128; } /* * BIG FAT WARNING!!! * Read all of the following before you touch any v6 code or decide to * optimize it by invoking a "simple" Guava call * * Java IPv6 is fundamentally broken and Google libraries do not fix it. * 1. Java will allways implicitly rewrite v4 mapped into v6 as a v4 address * and there is absolutely no way to override this behaviour * 2. Guava libraries cannot parse non-canonical IPv6. They will throw an * exception. Even if they did, they re-use the same broken java code * underneath. * * This is why we have to parse v6 by ourselves. * * The following conversion code is based on inet_cidr_pton_ipv6 in NetBSD * * The original BSD code is licensed under standard BSD license. While we * are not obliged to provide an attribution, 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 same variable names, comments and code flow. * */ /** * Convert Ipv6Address object to a valid Canonical v6 address in byte format * * @param ipv6Address - v6 Address object * @return - byte array of size 16. Last byte contains netmask */ public static byte[] canonicalBinaryV6Address(final Ipv6Address ipv6Address) { /* * Do not modify this routine to take direct strings input!!! * Key checks have been removed based on the assumption that * the input is validated via regexps in Ipv6Prefix() */ return canonicalBinaryV6AddressFromString(ipv6Address.getValue()); } private static byte[] canonicalBinaryV6AddressFromString(final String ipv6Address) { Iterable<String> splittedV6Address = Splitter.on("%") .trimResults() .omitEmptyStrings() .split(ipv6Address); List<String> partsV6Address = Lists.newArrayList(splittedV6Address.iterator()); int colonp; char ch; boolean saw_xdigit; /* Isn't it fun - the above variable names are the same in BSD and Sun sources */ int val; char[] src = partsV6Address.get(0).toCharArray(); byte[] dst = new byte[INADDR6SZ]; int src_length = src.length; colonp = -1; int i = 0, j = 0; /* Leading :: requires some special handling. */ /* Isn't it fun - the above comment is again the same in BSD and Sun sources, * We will derive our code from BSD. Shakespear always sounds better * in original Clingon. So does Dilbert. */ if (src[i] == ':') { Preconditions.checkArgument(src[++i] == ':', "Invalid v6 address"); } int curtok = i; saw_xdigit = false; val = 0; while (i < src_length) { ch = src[i++]; int chval = Character.digit(ch, 16); /* Business as usual - ipv6 address digit. * We can remove all checks from the original BSD code because * the regexp has already verified that we are not being fed * anything bigger than 0xffff between the separators. */ if (chval != -1) { val <<= 4; val |= chval; saw_xdigit = true; continue; } /* v6 separator */ if (ch == ':') { curtok = i; if (!saw_xdigit) { /* no need to check separator position validity - regexp does that */ colonp = j; continue; } /* removed overrun check - the regexp checks for valid data */ dst[j++] = (byte) ((val >>> 8) & 0xff); dst[j++] = (byte) (val & 0xff); saw_xdigit = false; val = 0; continue; } /* frankenstein - v4 attached to v6, mixed notation */ if (ch == '.' && ((j + INADDR4SZ) <= INADDR6SZ)) { /* this has passed the regexp so it is fairly safe to parse it * straight away. As v4 addresses do not suffer from the same * defficiencies as the java v6 implementation we can invoke it * straight away and be done with it */ Preconditions.checkArgument(j != (INADDR6SZ - INADDR4SZ - 1), "Invalid v4 in v6 mapping"); InetAddress _inet_form = InetAddresses.forString(partsV6Address.get(0).substring(curtok, src_length)); Preconditions.checkArgument(_inet_form instanceof Inet4Address); System.arraycopy(_inet_form.getAddress(), 0, dst, j, INADDR4SZ); j += INADDR4SZ; saw_xdigit = false; break; } /* removed parser exit on invalid char - no need to do it, regexp checks it */ } if (saw_xdigit) { Preconditions.checkArgument(j + INT16SZ <= INADDR6SZ, "Overrun in v6 parsing, should not occur"); dst[j++] = (byte) ((val >> 8) & 0xff); dst[j++] = (byte) (val & 0xff); } if (colonp != -1) { int n = j - colonp; Preconditions.checkArgument(j != INADDR6SZ, "Overrun in v6 parsing, should not occur"); for (i = 1; i <= n; i++) { dst[INADDR6SZ - i] = dst[colonp + n - i]; dst[colonp + n - i] = 0; } j = INADDR6SZ; } Preconditions.checkArgument(j == INADDR6SZ, "Overrun in v6 parsing, should not occur"); return dst; } public static String byteArrayV6AddressToString (final byte [] _binary_form) throws UnknownHostException { /* DO NOT DIY!!! - InetAddresses will actually print correct canonical * zero compressed form. */ return InetAddresses.toAddrString(InetAddress.getByAddress(_binary_form)); } private static int nextNibble(final int mask) { if (mask <= 0) { return 0; } if (mask > 8) { return 0xff; } return 0xff << (8 - mask); } /** * Convert Ipv6Prefix object to a valid Canonical v6 prefix in byte format * * @param ipv6Prefix - v6 prefix object * @return - byte array of size 16 + 1. Last byte contains netmask */ public static byte[] canonicalBinaryV6Prefix(final Ipv6Prefix ipv6Prefix) { /* * Do not modify this routine to take direct strings input!!! * Key checks have been removed based on the assumption that * the input is validated via regexps in Ipv6Prefix() */ int mask = 128; Iterable<String> splittedV6Prefix = Splitter.on("/") .trimResults() .omitEmptyStrings() .split(ipv6Prefix.getValue()); List<String> partsV6Prefix = Lists.newArrayList(splittedV6Prefix.iterator()); boolean valid = true; try { mask = Integer.parseInt(partsV6Prefix.get(1)); if (mask > 128) { valid = false; } } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) { valid = false; } Preconditions.checkArgument(valid, "Supplied netmask in %s is invalid", ipv6Prefix.getValue()); int colonp; char ch; boolean saw_xdigit; /* Isn't it fun - the above variable names are the same in BSD and Sun sources */ int val; char[] src = partsV6Prefix.get(0).toCharArray(); byte[] dst = new byte[INADDR6SZ + 1]; int m = mask; int src_length = src.length; colonp = -1; int i = 0, j = 0; /* Leading :: requires some special handling. */ /* Isn't it fun - the above comment is again the same in BSD and Sun sources, * We will derive our code from BSD. Shakespear always sounds better * in original Clingon. So does Dilbert. */ if (src[i] == ':') { Preconditions.checkArgument(src[++i] == ':', "Invalid v6 address"); } int curtok = i; saw_xdigit = false; val = 0; while (i < src_length) { ch = src[i++]; int chval = Character.digit(ch, 16); /* Business as usual - ipv6 address digit. * We can remove all checks from the original BSD code because * the regexp has already verified that we are not being fed * anything bigger than 0xffff between the separators. */ if (chval != -1) { val <<= 4; val |= chval; saw_xdigit = true; continue; } /* v6 separator */ if (ch == ':') { curtok = i; if (!saw_xdigit) { /* no need to check separator position validity - regexp does that */ colonp = j; continue; } /* removed overrun check - the regexp checks for valid data */ saw_xdigit = false; if (m < 0) { /* stop parsing if we are past the mask */ break; } dst[j] = (byte) ((val >> 8) & nextNibble(m)); j++; m = m - 8; if (m < 0) { /* stop parsing if we are past the mask */ break; } dst[j] = (byte) (val & nextNibble(m)); j++; m = m - 8; val = 0; continue; } /* frankenstein - v4 attached to v6, mixed notation */ if (ch == '.' && ((j + INADDR4SZ) <= INADDR6SZ)) { /* this has passed the regexp so it is fairly safe to parse it * straight away. As v4 addresses do not suffer from the same * defficiencies as the java v6 implementation we can invoke it * straight away and be done with it */ Preconditions.checkArgument(j != (INADDR6SZ - INADDR4SZ - 1), "Invalid v4 in v6 mapping"); InetAddress _inet_form = InetAddresses.forString(partsV6Prefix.get(0).substring(curtok, src_length)); Preconditions.checkArgument(_inet_form instanceof Inet4Address); System.arraycopy(_inet_form.getAddress(), 0, dst, j, INADDR4SZ); j += INADDR4SZ; saw_xdigit = false; break; } /* removed parser exit on ivalid char - no need to do it, regexp checks it */ } if (saw_xdigit) { Preconditions.checkArgument(j + INT16SZ <= INADDR6SZ, "Overrun in v6 parsing, should not occur"); dst[j] = (byte) ((val >> 8) & nextNibble(m)) ; j++; m = m - 8; dst[j] = (byte) (val & nextNibble(m)); j++; m = m - 8; } if ((j < INADDR6SZ) && (m < 0)) { /* past the mask */ for (i = j; i < INADDR6SZ; i++) { dst[i] = 0; } } else { /* normal parsing */ if (colonp != -1) { int n = j - colonp; Preconditions.checkArgument(j != INADDR6SZ, "Overrun in v6 parsing, should not occur"); for (i = 1; i <= n; i++) { dst[INADDR6SZ - i] = dst[colonp + n - i]; dst[colonp + n - i] = 0; } j = INADDR6SZ; } Preconditions.checkArgument(j == INADDR6SZ, "Overrun in v6 parsing, should not occur"); } dst[INADDR6SZ] = (byte) mask; return dst; } /** * Print a v6 prefix in byte array + 1 notation * @param _binary_form - prefix, in byte [] form, last byte is netmask * @return string of v6 prefix * @throws UnknownHostException unknown host exception */ public static String byteArrayV6PrefixToString(final byte [] _binary_form) throws UnknownHostException { /* NO DIY!!! - InetAddresses will actually print correct canonical * zero compressed form */ StringBuilder sb = new StringBuilder(); /* Yang RFC specifies that the normalized form is RFC 5952, note - java * core type is not RFC compliant, guava is. */ sb.append( InetAddresses.toAddrString( InetAddress.getByAddress( Arrays.copyOf(_binary_form, INADDR6SZ) ) ) ); sb.append('/'); sb.append(_binary_form[INADDR6SZ] & 0xff); return sb.toString(); } private static int ipv6PrefixByteArrayOffset(final int mask) { if (mask < 0) { return 0; } final int ret = mask * INADDR6SZ; if (ret < PREFIX_BYTEARRAYS.length) { return ret; } else { return PREFIX_BYTEARRAYS.length - INADDR6SZ; } } /** * Canonicalize a v6 prefix while in binary form * * @param prefix - prefix, in byte [] form * @param mask - mask - number of bits */ public static void canonicalizeIpv6Prefix(final byte [] prefix, final int mask) { final int offset = ipv6PrefixByteArrayOffset(mask); for (int i = 0; i < INADDR6SZ; i++) { prefix[i] &= PREFIX_BYTEARRAYS[offset + i]; } } public static byte[] convertIpv6PrefixToByteArray(final int prefix) { final int offset = ipv6PrefixByteArrayOffset(prefix); return Arrays.copyOfRange(PREFIX_BYTEARRAYS, offset, offset + INADDR6SZ); } public static Ipv6Address extractIpv6Address(final Ipv6Prefix ipv6Prefix) { return IetfInetUtil.INSTANCE.ipv6AddressFrom(ipv6Prefix); } public static Ipv4Address extractIpv4Address(final Ipv4Prefix ipv4Prefix) { Iterator<String> addressParts = PREFIX_SPLITTER.split(ipv4Prefix.getValue()).iterator(); return new Ipv4Address(addressParts.next()); } public static DottedQuad extractIpv4AddressMask(final Ipv4Prefix ipv4Prefix) { Iterator<String> addressParts = PREFIX_SPLITTER.split(ipv4Prefix.getValue()).iterator(); addressParts.next(); Integer cidrMask =0; if (addressParts.hasNext()) { cidrMask = Integer.parseInt(addressParts.next()); } long maskBits = 0; maskBits = 0xffffffff << IPV4_ADDRESS_LENGTH - cidrMask; String mask = String.format("%d.%d.%d.%d", (maskBits & 0x0000000000ff000000L) >> 24, (maskBits & 0x0000000000ff0000) >> 16, (maskBits & 0x0000000000ff00) >> 8, maskBits & 0xff); DottedQuad netMask = new DottedQuad(mask); return netMask; } public static Ipv6ArbitraryMask extractIpv6AddressMask(final Ipv6Prefix ipv6Prefix) { Iterator<String> addressParts = PREFIX_SPLITTER.split(ipv6Prefix.getValue()).iterator(); addressParts.next(); int maskLength = 0; if (addressParts.hasNext()) { maskLength = Integer.parseInt(addressParts.next()); } BitSet ipmask = new BitSet(128); ipmask.set(0,maskLength,true); ipmask.set(maskLength+1,128,false); byte[] finalmask = new byte[16]; System.arraycopy(ipmask.toByteArray(),0,finalmask,0,ipmask.toByteArray().length); InetAddress inetAddress = null; try { inetAddress = InetAddress.getByAddress(finalmask); } catch (UnknownHostException e) { LOG.error("Failed to convert the Ipv6 subnetmask from integer to mask value ", e); } return new Ipv6ArbitraryMask(inetAddress.getHostAddress()); } public static Integer extractIpv6Prefix(final Ipv6Prefix ipv6Prefix) { return IetfInetUtil.INSTANCE.splitIpv6Prefix(ipv6Prefix).getValue(); } public static int countBits(final byte[] mask) { int netmask = 0; for (byte b : mask) { netmask += Integer.bitCount(UnsignedBytes.toInt(b)); } return netmask; } public static final byte[] convertArbitraryMaskToByteArray(DottedQuad mask) { String maskValue; if (mask != null && mask.getValue() != null) { maskValue = mask.getValue(); } else { maskValue = DEFAULT_ARBITRARY_BIT_MASK; } InetAddress maskInIpFormat = null; try { maskInIpFormat = InetAddress.getByName(maskValue); } catch (UnknownHostException e) { LOG.error ("Failed to resolve the ip address of the mask ",e); } byte[] bytes = maskInIpFormat.getAddress(); return bytes; } public static boolean isArbitraryBitMask(byte[] byteMask) { if (byteMask == null) { return false; } else { ArrayList<Integer> integerMaskArrayList = new ArrayList<Integer>(); String maskInBits; // converting byte array to bits maskInBits = new BigInteger(1, byteMask).toString(2); ArrayList<String> stringMaskArrayList = new ArrayList<String>(Arrays.asList(maskInBits.split("(?!^)"))); for (String string:stringMaskArrayList) { integerMaskArrayList.add(Integer.parseInt(string)); } return checkArbitraryBitMask(integerMaskArrayList); } } private static boolean checkArbitraryBitMask(ArrayList<Integer> arrayList) { // checks 0*1* case - Leading zeros in arrayList are truncated if (arrayList.size()>0 && arrayList.size()<IPV4_ADDRESS_LENGTH) { return true; } else { //checks 1*0*1 case for (int i=0; i<arrayList.size()-1;i++) { if (arrayList.get(i) ==0 && arrayList.get(i+1) == 1) { return true; } } } return false; } public static final byte[] convertIpv6ArbitraryMaskToByteArray(final Ipv6ArbitraryMask mask) { String maskValue; if (mask != null && mask.getValue() != null) { maskValue = mask.getValue(); } else { maskValue = DEFAULT_IPV6_ARBITRARY_BITMASK; } InetAddress maskInIpFormat = null; try { maskInIpFormat = InetAddress.getByName(maskValue); } catch (UnknownHostException e) { LOG.error ("Failed to convert mask string to ipv6 format mask ",e); } return maskInIpFormat.getAddress(); } public static boolean isIpv6ArbitraryBitMask(final byte[] byteMask) { if (byteMask == null) { return false; } else { ArrayList<Integer> integerMaskArrayList = new ArrayList<Integer>(); String maskInBits; // converting byte array to bits maskInBits = new BigInteger(1, byteMask).toString(2); ArrayList<String> stringMaskArrayList = new ArrayList<String>(Arrays.asList(maskInBits.split("(?!^)"))); for (String string:stringMaskArrayList) { integerMaskArrayList.add(Integer.parseInt(string)); } return checkIpv6ArbitraryBitMask(integerMaskArrayList); } } private static boolean checkIpv6ArbitraryBitMask(final ArrayList<Integer> arrayList) { // checks 0*1* case - Leading zeros in arrayList are truncated if (arrayList.size() > 0 && arrayList.size() < IPV6_ADDRESS_LENGTH) { return true; } else { //checks 1*0*1 case for (int i=0; i<arrayList.size()-1;i++) { if (arrayList.get(i) ==0 && arrayList.get(i+1) == 1) { return true; } } } return false; } private static String compressedIpv6FormatFromString(final String ipv6Address) { try { return byteArrayV6AddressToString(canonicalBinaryV6AddressFromString(ipv6Address)); } catch (UnknownHostException e) { LOG.warn("Failed to compress IPv6 address {} because it is invalid", ipv6Address); return ipv6Address; } } public static Ipv6Address compressedIpv6AddressFormat(final Ipv6Address ipv6Address) { return new Ipv6Address(compressedIpv6FormatFromString(ipv6Address.getValue())); } public static Ipv6ArbitraryMask compressedIpv6MaskFormat(final Ipv6ArbitraryMask ipv6Mask) { return new Ipv6ArbitraryMask(compressedIpv6FormatFromString(ipv6Mask.getValue())); } }