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