Bump upstreams
[openflowplugin.git] / openflowplugin / src / main / java / org / opendaylight / openflowplugin / openflow / md / core / sal / convertor / common / IpConversionUtil.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.openflowplugin.openflow.md.core.sal.convertor.common;
9
10 import com.google.common.base.Preconditions;
11 import com.google.common.base.Splitter;
12 import com.google.common.base.Strings;
13 import com.google.common.net.InetAddresses;
14 import com.google.common.primitives.UnsignedBytes;
15 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
16 import java.math.BigInteger;
17 import java.net.Inet4Address;
18 import java.net.InetAddress;
19 import java.net.UnknownHostException;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.BitSet;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.regex.Pattern;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DottedQuad;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.opendaylight.ipv6.arbitrary.bitmask.fields.rev160224.Ipv6ArbitraryMask;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 public final class IpConversionUtil {
38
39     private static final Logger LOG = LoggerFactory.getLogger(IpConversionUtil.class);
40     public static final String PREFIX_SEPARATOR = "/";
41     public static final Splitter PREFIX_SPLITTER = Splitter.on('/');
42     private static final Splitter PREFIX_TRIM_SPLITTER = PREFIX_SPLITTER.trimResults().omitEmptyStrings();
43     private static final Splitter PERCENT_SPLITTER = Splitter.on('%').trimResults().omitEmptyStrings();
44     private static final Pattern BITMASK_SPLIT_PATTERN = Pattern.compile("(?!^)");
45     private static final int INADDR4SZ = 4;
46     private static final int INADDR6SZ = 16;
47     private static final int INT16SZ = 2;
48     private static final int IPV4_ADDRESS_LENGTH = 32;
49     private static final int IPV6_ADDRESS_LENGTH = 128;
50     private static final Ipv6ArbitraryMask DEFAULT_IPV6_ARBITRARY_BITMASK =
51             new Ipv6ArbitraryMask("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
52
53     /*
54      * Prefix bytearray lookup table. We concatenate the prefixes
55      * to a single byte array and perform offset lookups to ensure
56      * the table is contiguous and save some space.
57      */
58     private static final byte[] PREFIX_BYTEARRAYS;
59
60     static {
61         final byte[] a = new byte[(INADDR6SZ * Byte.SIZE + 1) * INADDR6SZ];
62
63         int offset = 0;
64         for (int p = 0; p <= INADDR6SZ * Byte.SIZE; ++p) {
65             int prefix = p;
66             for (int i = 0; i < INADDR6SZ; ++i) {
67                 a[offset++] = (byte) nextNibble(prefix);
68                 prefix -= Byte.SIZE;
69             }
70         }
71
72         PREFIX_BYTEARRAYS = a;
73     }
74
75     private static final DottedQuad[] IPV4_BITMASKS;
76
77     static {
78         final DottedQuad[] quads = new DottedQuad[IPV4_ADDRESS_LENGTH + 1];
79
80         for (int i = 0; i <= IPV4_ADDRESS_LENGTH; ++i) {
81             final int maskBits = maskForIpv4Prefix(i);
82             quads[i] = new DottedQuad(new StringBuilder(15)
83                 .append(maskBits >>> 24).append('.')
84                 .append(maskBits >>> 16 & 0xff).append('.')
85                 .append(maskBits >>>  8 & 0xff).append('.')
86                 .append(maskBits & 0xff).toString());
87         }
88
89         IPV4_BITMASKS = quads;
90     }
91
92
93     private IpConversionUtil() {
94         // Hidden on purpose
95     }
96
97     public static Iterator<String> splitToParts(final Ipv4Prefix ipv4Prefix) {
98         return PREFIX_SPLITTER.split(ipv4Prefix.getValue()).iterator();
99     }
100
101     public static Iterator<String> splitToParts(final Ipv6Prefix ipv6Prefix) {
102         return PREFIX_SPLITTER.split(ipv6Prefix.getValue()).iterator();
103     }
104
105     /* This forest of functions has a purpose:
106      *
107      * 1. There are multiple coding styles around the plugin, this is necessary in order to have
108      *   one mechanism to convert them all, one mechanism to find them...
109      * 2. I hope that one day yangtools will actually deliver code fit for purpose in a packet
110      *   processing application (presently it is not. When this happens, these can be optimized
111      *   for "side-load" of pre-vetted data. Example. IP Address (v4 or v6) is prevetted left of the
112      *   prefix. It should be loadable into Prefix without _RERUNNING_ 100ms+ of regexps. When (and if)
113      *   that happens, it will be a simple fix here without chasing it across the whole plugin.
114     */
115
116     public static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address) {
117         return IetfInetUtil.ipv4PrefixFor(ipv4Address);
118     }
119
120     public static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address, final String mask) {
121         /*
122          * Ipv4Address has already validated the address part of the prefix,
123          * It is mandated to comply to the same regexp as the address
124          * There is absolutely no point rerunning additional checks vs this
125          * Note - there is no canonical form check here!!!
126          */
127         if (null != mask && !mask.isEmpty()) {
128             return new Ipv4Prefix(ipv4Address.getValue() + PREFIX_SEPARATOR + mask);
129         } else {
130             return new Ipv4Prefix(ipv4Address.getValue() + PREFIX_SEPARATOR + IPV4_ADDRESS_LENGTH);
131         }
132     }
133
134     public static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address, final int intmask) {
135         return IetfInetUtil.ipv4PrefixFor(ipv4Address, intmask);
136     }
137
138     public static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address, final byte [] bytemask) {
139         if (bytemask == null) {
140             return createPrefix(ipv4Address);
141         }
142
143         return IetfInetUtil.ipv4PrefixFor(ipv4Address, countBits(bytemask));
144     }
145
146     public static Ipv6Prefix createPrefix(final Ipv6Address ipv6Address) {
147         return IetfInetUtil.ipv6PrefixFor(ipv6Address);
148     }
149
150     public static Ipv6Prefix createPrefix(final Ipv6Address ipv6Address, final String mask) {
151         /*
152          * Ipv6Address has already validated the address part of the prefix,
153          * It is mandated to comply to the same regexp as the address
154          * There is absolutely no point rerunning additional checks vs this
155          * Note - there is no canonical form check here!!!
156          */
157         if (Strings.isNullOrEmpty(mask)) {
158             return new Ipv6Prefix(ipv6Address.getValue() + PREFIX_SEPARATOR + String.valueOf(IPV6_ADDRESS_LENGTH));
159         } else {
160             return new Ipv6Prefix(ipv6Address.getValue() + PREFIX_SEPARATOR + mask);
161         }
162     }
163
164     public static Ipv6Prefix createPrefix(final Ipv6Address ipv6Address, final int intmask) {
165         return IetfInetUtil.ipv6PrefixFor(ipv6Address, intmask);
166     }
167
168     public static Ipv6Prefix createPrefix(final Ipv6Address ipv6Address, final byte [] bytemask) {
169         if (bytemask == null) {
170             return createPrefix(ipv6Address);
171         }
172
173         return IetfInetUtil.ipv6PrefixFor(ipv6Address, countBits(bytemask));
174     }
175
176     public static DottedQuad createArbitraryBitMask(final byte[] bitmask)  {
177         if (bitmask == null) {
178             return IPV4_BITMASKS[IPV4_ADDRESS_LENGTH];
179         }
180
181         final String hostAddress;
182         try {
183             hostAddress = InetAddress.getByAddress(bitmask).getHostAddress();
184         } catch (UnknownHostException e) {
185             LOG.error("Failed to create the dottedQuad notation for the given mask {}", Arrays.toString(bitmask), e);
186             return null;
187         }
188
189         return new DottedQuad(hostAddress);
190     }
191
192     public static Ipv6ArbitraryMask createIpv6ArbitraryBitMask(final byte[] bitmask) {
193         if (bitmask == null) {
194             return DEFAULT_IPV6_ARBITRARY_BITMASK;
195         }
196
197         final String hostAddress;
198         try {
199             hostAddress = InetAddress.getByAddress(bitmask).getHostAddress();
200         } catch (UnknownHostException e) {
201             LOG.error("Failed to create the Ipv6ArbitraryMask notation for the given mask {}", Arrays.toString(bitmask),
202                 e);
203             return null;
204         }
205
206         return new Ipv6ArbitraryMask(hostAddress);
207     }
208
209     public static Integer extractPrefix(final Ipv4Prefix ipv4Prefix) {
210         return IetfInetUtil.splitIpv4Prefix(ipv4Prefix).getValue();
211     }
212
213     public static Integer extractPrefix(final Ipv6Prefix ipv6Prefix) {
214         return IetfInetUtil.splitIpv6Prefix(ipv6Prefix).getValue();
215     }
216
217     public static Integer extractPrefix(final Ipv4Address ipv4Prefix) {
218         return IPV4_ADDRESS_LENGTH;
219     }
220
221     public static Integer extractPrefix(final Ipv6Address ipv6Prefix) {
222         return 128;
223     }
224
225     /**
226      * Check if the supplied Ipv4Prefix has a prefix shorter than IPv4 address length.
227      *
228      * @param ipv4Prefix Ipv4 prefix
229      * @return prefix if there is one, else null
230      */
231     public static Integer hasIpv4Prefix(final Ipv4Prefix ipv4Prefix) {
232         return hasPrefix(extractPrefix(ipv4Prefix), IPV4_ADDRESS_LENGTH);
233     }
234
235     private static Integer hasPrefix(final Integer prefix, final int addressLength) {
236         return prefix != null && prefix < addressLength ? prefix : null;
237     }
238
239     public static int maskForIpv4Prefix(final int prefixLength) {
240         return (int) (0xffffffffL << IPV4_ADDRESS_LENGTH - prefixLength);
241     }
242
243     /*
244      * BIG FAT WARNING!!!
245      * Read all of the following before you touch any v6 code or decide to
246      * optimize it by invoking a "simple" Guava call
247      *
248      * Java IPv6 is fundamentally broken and Google libraries do not fix it.
249      * 1. Java will allways implicitly rewrite v4 mapped into v6 as a v4 address
250      *      and there is absolutely no way to override this behaviour
251      * 2. Guava libraries cannot parse non-canonical IPv6. They will throw an
252      *      exception. Even if they did, they re-use the same broken java code
253      *      underneath.
254      *
255      * This is why we have to parse v6 by ourselves.
256      *
257      * The following conversion code is based on inet_cidr_pton_ipv6 in NetBSD
258      *
259      * The original BSD code is licensed under standard BSD license. While we
260      * are not obliged to provide an attribution, credit where credit is due.
261      * As far as why it is similar to Sun's sun.net.util please ask Sun why
262      * their code has the same variable names, comments and code flow.
263      *
264      */
265
266
267      /**
268      * Convert Ipv6Address object to a valid Canonical v6 address in byte format.
269      *
270      * @param ipv6Address - v6 Address object
271      * @return - byte array of size 16. Last byte contains netmask
272      */
273     public static byte[] canonicalBinaryV6Address(final Ipv6Address ipv6Address) {
274         /*
275          * Do not modify this routine to take direct strings input!!!
276          * Key checks have been removed based on the assumption that
277          * the input is validated via regexps in Ipv6Prefix()
278          */
279
280         return canonicalBinaryV6AddressFromString(ipv6Address.getValue());
281     }
282
283
284     private static byte[] canonicalBinaryV6AddressFromString(final String ipv6Address) {
285         List<String> partsV6Address = PERCENT_SPLITTER.splitToList(ipv6Address);
286
287         int colonp;
288         char ch;
289         boolean sawXdigit;
290
291         /* Isn't it fun - the above variable names are the same in BSD and Sun sources */
292
293         char[] src = partsV6Address.get(0).toCharArray();
294
295         byte[] dst = new byte[INADDR6SZ];
296
297         colonp = -1;
298         int index1 = 0;
299         int index2 = 0;
300
301         /* Leading :: requires some special handling. */
302
303         /* Isn't it fun - the above comment is again the same in BSD and Sun sources,
304          * We will derive our code from BSD. Shakespear always sounds better
305          * in original Clingon. So does Dilbert.
306          */
307
308         if (src[index1] == ':') {
309             Preconditions.checkArgument(src[++index1] == ':', "Invalid v6 address");
310         }
311
312         int curtok = index1;
313         sawXdigit = false;
314
315         int srcLength = src.length;
316         int val = 0;
317         while (index1 < srcLength) {
318             ch = src[index1++];
319             int chval = Character.digit(ch, 16);
320
321             /* Business as usual - ipv6 address digit.
322              * We can remove all checks from the original BSD code because
323              * the regexp has already verified that we are not being fed
324              * anything bigger than 0xffff between the separators.
325              */
326
327             if (chval != -1) {
328                 val <<= 4;
329                 val |= chval;
330                 sawXdigit = true;
331                 continue;
332             }
333
334             /* v6 separator */
335
336             if (ch == ':') {
337                 curtok = index1;
338                 if (!sawXdigit) {
339                     /* no need to check separator position validity - regexp does that */
340                     colonp = index2;
341                     continue;
342                 }
343
344                 /* removed overrun check - the regexp checks for valid data */
345
346                 dst[index2++] = (byte) (val >>> 8 & 0xff);
347                 dst[index2++] = (byte) (val & 0xff);
348                 sawXdigit = false;
349                 val = 0;
350                 continue;
351             }
352
353             /* frankenstein - v4 attached to v6, mixed notation */
354
355             if (ch == '.' && index2 + INADDR4SZ <= INADDR6SZ) {
356
357                 /* this has passed the regexp so it is fairly safe to parse it
358                  * straight away. As v4 addresses do not suffer from the same
359                  * defficiencies as the java v6 implementation we can invoke it
360                  * straight away and be done with it
361                  */
362
363                 Preconditions.checkArgument(index2 != INADDR6SZ - INADDR4SZ - 1, "Invalid v4 in v6 mapping");
364
365                 InetAddress inetForm = InetAddresses.forString(partsV6Address.get(0).substring(curtok, srcLength));
366
367                 Preconditions.checkArgument(inetForm instanceof Inet4Address);
368                 System.arraycopy(inetForm.getAddress(), 0, dst, index2, INADDR4SZ);
369                 index2 += INADDR4SZ;
370
371                 sawXdigit = false;
372                 break;
373             }
374             /* removed parser exit on invalid char - no need to do it, regexp checks it */
375         }
376         if (sawXdigit) {
377             Preconditions.checkArgument(index2 + INT16SZ <= INADDR6SZ, "Overrun in v6 parsing, should not occur");
378             dst[index2++] = (byte) (val >> 8 & 0xff);
379             dst[index2++] = (byte) (val & 0xff);
380         }
381
382         if (colonp != -1) {
383             int to = index2 - colonp;
384
385             Preconditions.checkArgument(index2 != INADDR6SZ, "Overrun in v6 parsing, should not occur");
386             for (index1 = 1; index1 <= to; index1++) {
387                 dst[INADDR6SZ - index1] = dst[colonp + to - index1];
388                 dst[colonp + to - index1] = 0;
389             }
390             index2 = INADDR6SZ;
391         }
392
393         Preconditions.checkArgument(index2 == INADDR6SZ, "Overrun in v6 parsing, should not occur");
394
395         return dst;
396     }
397
398     public static String byteArrayV6AddressToString(final byte [] binaryForm) throws UnknownHostException {
399         /* DO NOT DIY!!! - InetAddresses will actually print correct canonical
400          * zero compressed form.
401          */
402         return InetAddresses.toAddrString(InetAddress.getByAddress(binaryForm));
403     }
404
405     private static int nextNibble(final int mask) {
406         if (mask <= 0) {
407             return 0;
408         }
409         if (mask > 8) {
410             return 0xff;
411         }
412         return 0xff << 8 - mask;
413     }
414
415     /**
416      * Convert Ipv6Prefix object to a valid Canonical v6 prefix in byte format.
417      *
418      * @param ipv6Prefix - v6 prefix object
419      * @return - byte array of size 16 + 1. Last byte contains netmask
420      */
421     public static byte[] canonicalBinaryV6Prefix(final Ipv6Prefix ipv6Prefix) {
422         /*
423          * Do not modify this routine to take direct strings input!!!
424          * Key checks have been removed based on the assumption that
425          * the input is validated via regexps in Ipv6Prefix()
426          */
427
428         int initialMask = 128;
429
430         List<String> partsV6Prefix = PREFIX_TRIM_SPLITTER.splitToList(ipv6Prefix.getValue());
431
432         boolean valid = true;
433
434         try {
435             initialMask = Integer.parseInt(partsV6Prefix.get(1));
436             if (initialMask > 128) {
437                 valid = false;
438             }
439         } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
440             valid = false;
441         }
442
443         Preconditions.checkArgument(valid, "Supplied netmask in %s is invalid", ipv6Prefix.getValue());
444
445
446         int colonp;
447         char ch;
448         boolean sawXdigit;
449
450         /* Isn't it fun - the above variable names are the same in BSD and Sun sources */
451
452         char[] src = partsV6Prefix.get(0).toCharArray();
453
454         byte[] dst = new byte[INADDR6SZ + 1];
455
456         int mask = initialMask;
457
458         colonp = -1;
459         int index1 = 0;
460         int index2 = 0;
461
462         /* Leading :: requires some special handling. */
463
464         /* Isn't it fun - the above comment is again the same in BSD and Sun sources,
465          * We will derive our code from BSD. Shakespear always sounds better
466          * in original Clingon. So does Dilbert.
467          */
468
469         if (src[index1] == ':') {
470             Preconditions.checkArgument(src[++index1] == ':', "Invalid v6 address");
471         }
472
473         int curtok = index1;
474         sawXdigit = false;
475
476         int srcLength = src.length;
477         int val = 0;
478         while (index1 < srcLength) {
479             ch = src[index1++];
480             int chval = Character.digit(ch, 16);
481
482             /* Business as usual - ipv6 address digit.
483              * We can remove all checks from the original BSD code because
484              * the regexp has already verified that we are not being fed
485              * anything bigger than 0xffff between the separators.
486              */
487
488             if (chval != -1) {
489                 val <<= 4;
490                 val |= chval;
491                 sawXdigit = true;
492                 continue;
493             }
494
495             /* v6 separator */
496
497             if (ch == ':') {
498                 curtok = index1;
499                 if (!sawXdigit) {
500                     /* no need to check separator position validity - regexp does that */
501                     colonp = index2;
502                     continue;
503                 }
504
505                 /* removed overrun check - the regexp checks for valid data */
506
507                 sawXdigit = false;
508
509                 if (mask < 0) {
510                     /* stop parsing if we are past the mask */
511                     break;
512                 }
513
514                 dst[index2] = (byte) (val >> 8 & nextNibble(mask));
515                 index2++;
516                 mask = mask - 8;
517
518                 if (mask < 0) {
519                     /* stop parsing if we are past the mask */
520                     break;
521                 }
522
523                 dst[index2] = (byte) (val & nextNibble(mask));
524                 index2++;
525                 mask = mask - 8;
526
527                 val = 0;
528                 continue;
529             }
530
531             /* frankenstein - v4 attached to v6, mixed notation */
532
533             if (ch == '.' && index2 + INADDR4SZ <= INADDR6SZ) {
534
535                 /* this has passed the regexp so it is fairly safe to parse it
536                  * straight away. As v4 addresses do not suffer from the same
537                  * defficiencies as the java v6 implementation we can invoke it
538                  * straight away and be done with it
539                  */
540
541                 Preconditions.checkArgument(index2 != INADDR6SZ - INADDR4SZ - 1, "Invalid v4 in v6 mapping");
542
543                 InetAddress inetForm = InetAddresses.forString(partsV6Prefix.get(0).substring(curtok, srcLength));
544
545                 Preconditions.checkArgument(inetForm instanceof Inet4Address);
546                 System.arraycopy(inetForm.getAddress(), 0, dst, index2, INADDR4SZ);
547                 index2 +=  INADDR4SZ;
548
549                 sawXdigit = false;
550                 break;
551             }
552             /* removed parser exit on ivalid char - no need to do it, regexp checks it */
553         }
554         if (sawXdigit) {
555             Preconditions.checkArgument(index2 + INT16SZ <= INADDR6SZ, "Overrun in v6 parsing, should not occur");
556             dst[index2] = (byte) (val >> 8 & nextNibble(mask));
557             index2++;
558             mask = mask - 8;
559             dst[index2] = (byte) (val & nextNibble(mask));
560             index2++;
561             mask = mask - 8;
562         }
563
564         if (index2 < INADDR6SZ && mask < 0) {
565             /* past the mask */
566             for (index1 = index2; index1 < INADDR6SZ; index1++) {
567                 dst[index1] = 0;
568             }
569         } else {
570             /* normal parsing */
571             if (colonp != -1) {
572                 int to = index2 - colonp;
573
574                 Preconditions.checkArgument(index2 != INADDR6SZ, "Overrun in v6 parsing, should not occur");
575                 for (index1 = 1; index1 <= to; index1++) {
576                     dst[INADDR6SZ - index1] = dst[colonp + to - index1];
577                     dst[colonp + to - index1] = 0;
578                 }
579                 index2 = INADDR6SZ;
580             }
581             Preconditions.checkArgument(index2 == INADDR6SZ, "Overrun in v6 parsing, should not occur");
582         }
583
584         dst[INADDR6SZ] = (byte) initialMask;
585         return dst;
586     }
587
588     /**
589      * Print a v6 prefix in byte array + 1 notation.
590      *
591      * @param binaryForm - prefix, in byte [] form, last byte is netmask
592      * @return string of v6 prefix
593      * @throws UnknownHostException unknown host exception
594      */
595     public static String byteArrayV6PrefixToString(final byte [] binaryForm) throws UnknownHostException {
596         /* NO DIY!!! - InetAddresses will actually print correct canonical
597          * zero compressed form
598          */
599         StringBuilder sb = new StringBuilder();
600         /* Yang RFC specifies that the normalized form is RFC 5952, note - java
601          * core type is not RFC compliant, guava is.
602          */
603         sb.append(
604             InetAddresses.toAddrString(
605                 InetAddress.getByAddress(
606                     Arrays.copyOf(binaryForm, INADDR6SZ)
607                 )
608             )
609         );
610         sb.append('/');
611         sb.append(binaryForm[INADDR6SZ] & 0xff);
612         return sb.toString();
613     }
614
615     /**
616      * Check if the supplied Ipv6Prefix has a prefix shorter than IPv6 address length.
617      *
618      * @param ipv6Prefix Ipv6 prefix
619      * @return prefix if there is one, else null
620      */
621     public static Integer hasIpv6Prefix(final Ipv6Prefix ipv6Prefix) {
622         return hasPrefix(extractIpv6Prefix(ipv6Prefix), IPV6_ADDRESS_LENGTH);
623     }
624
625     private static int ipv6PrefixByteArrayOffset(final int mask) {
626         if (mask < 0) {
627             return 0;
628         }
629
630         final int ret = mask * INADDR6SZ;
631         if (ret < PREFIX_BYTEARRAYS.length) {
632             return ret;
633         } else {
634             return PREFIX_BYTEARRAYS.length - INADDR6SZ;
635         }
636     }
637
638     /**
639      * Canonicalize a v6 prefix while in binary form.
640      *
641      * @param prefix - prefix, in byte [] form
642      * @param mask - mask - number of bits
643      */
644     public static void canonicalizeIpv6Prefix(final byte [] prefix, final int mask) {
645         final int offset = ipv6PrefixByteArrayOffset(mask);
646
647         for (int i = 0; i < INADDR6SZ; i++) {
648             prefix[i] &= PREFIX_BYTEARRAYS[offset + i];
649         }
650     }
651
652     public static byte[] convertIpv6PrefixToByteArray(final int prefix) {
653         final int offset = ipv6PrefixByteArrayOffset(prefix);
654
655         return Arrays.copyOfRange(PREFIX_BYTEARRAYS, offset, offset + INADDR6SZ);
656     }
657
658     public static Ipv6Address extractIpv6Address(final Ipv6Prefix ipv6Prefix) {
659         return IetfInetUtil.ipv6AddressFrom(ipv6Prefix);
660     }
661
662     public static Ipv4Address extractIpv4Address(final Ipv4Prefix ipv4Prefix) {
663         return IetfInetUtil.ipv4AddressFrom(ipv4Prefix);
664     }
665
666     public static DottedQuad extractIpv4AddressMask(final Ipv4Prefix ipv4Prefix) {
667         final String value = ipv4Prefix.getValue();
668         return IPV4_BITMASKS[Integer.parseInt(value.substring(value.indexOf('/') + 1))];
669     }
670
671     @Nullable
672     public static Ipv6ArbitraryMask extractIpv6AddressMask(final Ipv6Prefix ipv6Prefix) {
673         Iterator<String> addressParts = PREFIX_SPLITTER.split(ipv6Prefix.getValue()).iterator();
674         addressParts.next();
675         int maskLength = 0;
676         if (addressParts.hasNext()) {
677             maskLength = Integer.parseInt(addressParts.next());
678         }
679         BitSet ipmask = new BitSet(128);
680         ipmask.set(0,maskLength,true);
681         ipmask.set(maskLength + 1,128,false);
682         byte[] finalmask = new byte[16];
683         System.arraycopy(ipmask.toByteArray(),0,finalmask,0,ipmask.toByteArray().length);
684         InetAddress inetAddress = null;
685         try {
686             inetAddress = InetAddress.getByAddress(finalmask);
687         } catch (UnknownHostException e) {
688             LOG.error("Failed to convert the Ipv6 subnetmask from integer to mask value ", e);
689             return null;
690         }
691         return new Ipv6ArbitraryMask(inetAddress.getHostAddress());
692     }
693
694     public static Integer extractIpv6Prefix(final Ipv6Prefix ipv6Prefix) {
695         return IetfInetUtil.splitIpv6Prefix(ipv6Prefix).getValue();
696     }
697
698     public static int countBits(final byte[] mask) {
699         int netmask = 0;
700         for (byte b : mask) {
701             netmask += Integer.bitCount(UnsignedBytes.toInt(b));
702         }
703         return netmask;
704     }
705
706     @SuppressFBWarnings("PZLA_PREFER_ZERO_LENGTH_ARRAYS")
707     public static byte @Nullable[] convertArbitraryMaskToByteArray(final DottedQuad mask) {
708         final String maskValue;
709         if (mask != null && mask.getValue() != null) {
710             maskValue = mask.getValue();
711         } else {
712             maskValue = IPV4_BITMASKS[IPV4_ADDRESS_LENGTH].getValue();
713         }
714
715         final InetAddress maskInIpFormat;
716         try {
717             maskInIpFormat = InetAddress.getByName(maskValue);
718         } catch (UnknownHostException e) {
719             LOG.error("Failed to resolve the ip address of the mask ", e);
720             return null;
721         }
722         byte[] bytes = maskInIpFormat.getAddress();
723         return bytes;
724     }
725
726     public static boolean isArbitraryBitMask(final byte[] byteMask) {
727         return isArbitraryBitMask(byteMask, IPV4_ADDRESS_LENGTH);
728     }
729
730     private static boolean isArbitraryBitMask(final byte[] byteMask, final int addressLength) {
731         if (byteMask == null) {
732             return false;
733         }
734
735         ArrayList<Integer> integerMaskArrayList = new ArrayList<>();
736         String maskInBits;
737         // converting byte array to bits
738         maskInBits = new BigInteger(1, byteMask).toString(2);
739         for (String string : BITMASK_SPLIT_PATTERN.split(maskInBits)) {
740             integerMaskArrayList.add(Integer.parseInt(string));
741         }
742
743         final int size = integerMaskArrayList.size();
744         // checks 0*1* case - Leading zeros in arrayList are truncated
745         if (size > 0 && size < addressLength) {
746             return true;
747         }
748
749         // checks 1*0*1 case
750         for (int i = 0; i < size - 1; i++) {
751             if (integerMaskArrayList.get(i) == 0 && integerMaskArrayList.get(i + 1) == 1) {
752                 return true;
753             }
754         }
755         return false;
756     }
757
758     @SuppressFBWarnings("PZLA_PREFER_ZERO_LENGTH_ARRAYS")
759     public static byte @Nullable[] convertIpv6ArbitraryMaskToByteArray(final Ipv6ArbitraryMask mask) {
760         final String maskValue;
761         if (mask != null && mask.getValue() != null) {
762             maskValue = mask.getValue();
763         } else {
764             maskValue = DEFAULT_IPV6_ARBITRARY_BITMASK.getValue();
765         }
766
767         final InetAddress maskInIpFormat;
768         try {
769             maskInIpFormat = InetAddress.getByName(maskValue);
770         } catch (UnknownHostException e) {
771             LOG.error("Failed to convert mask string to ipv6 format mask ",e);
772             return null;
773         }
774         return maskInIpFormat.getAddress();
775     }
776
777     public static boolean isIpv6ArbitraryBitMask(final byte[] byteMask) {
778         return isArbitraryBitMask(byteMask, IPV6_ADDRESS_LENGTH);
779     }
780
781     private static String compressedIpv6FormatFromString(final String ipv6Address) {
782         try {
783             return byteArrayV6AddressToString(canonicalBinaryV6AddressFromString(ipv6Address));
784         } catch (UnknownHostException e) {
785             LOG.warn("Failed to compress IPv6 address {} because it is invalid", ipv6Address);
786             return ipv6Address;
787         }
788     }
789
790     public static Ipv6Address compressedIpv6AddressFormat(final Ipv6Address ipv6Address) {
791         return new Ipv6Address(compressedIpv6FormatFromString(ipv6Address.getValue()));
792     }
793
794     public static Ipv6ArbitraryMask compressedIpv6MaskFormat(final Ipv6ArbitraryMask ipv6Mask) {
795         return new Ipv6ArbitraryMask(compressedIpv6FormatFromString(ipv6Mask.getValue()));
796     }
797 }