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