Merge changes from topic 'BUG-3774'
[openflowplugin.git] / openflowplugin / src / main / java / org / opendaylight / openflowplugin / openflow / md / core / sal / convertor / IpConversionUtil.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc., Brocade, Communications 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;
10
11 import java.net.Inet4Address;
12 import java.net.InetAddress;
13 import java.net.UnknownHostException;
14 import java.util.Arrays;
15 import java.util.Iterator;
16
17 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
18 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
19 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Address;
20 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
21
22 import com.google.common.base.Preconditions;
23 import com.google.common.base.Splitter;
24 import com.google.common.base.Strings;
25 import com.google.common.collect.Iterators;
26 import com.google.common.net.InetAddresses;
27 import com.google.common.primitives.UnsignedBytes;
28
29
30 /**
31  * Created by Martin Bobak <mbobak@cisco.com> on 5.3.2015.
32  * v6 routines added by Anton Ivanov on 14.6.2015
33  */
34 public final class IpConversionUtil {
35
36     public static final String PREFIX_SEPARATOR = "/";
37     public static final Splitter PREFIX_SPLITTER = Splitter.on('/');
38     private static final int INADDR4SZ = 4;
39     private static final int INADDR6SZ = 16;
40     private static final int INT16SZ = 2;
41     private static final int IPV4_ADDRESS_LENGTH = 32;
42     private static final int IPV6_ADDRESS_LENGTH = 128;
43
44     /*
45      * Prefix bytearray lookup table. We concatenate the prefixes
46      * to a single byte array and perform offset lookups to ensure
47      * the table is contiguous and save some space.
48      */
49     private static final byte[] PREFIX_BYTEARRAYS;
50     static {
51         final byte[] a = new byte[(INADDR6SZ * Byte.SIZE + 1) * INADDR6SZ];
52
53         int offset = 0;
54         for (int p = 0; p <= INADDR6SZ * Byte.SIZE; ++p) {
55             int prefix = p;
56             for (int i = 0; i < INADDR6SZ; ++i) {
57                 a[offset++] = (byte) nextNibble(prefix);
58                 prefix -= Byte.SIZE;
59             }
60         }
61
62         PREFIX_BYTEARRAYS = a;
63     }
64
65     private IpConversionUtil() {
66         throw new UnsupportedOperationException("This class should not be instantiated.");
67     }
68
69     public static Iterator<String> splitToParts(final Ipv4Prefix ipv4Prefix) {
70         return PREFIX_SPLITTER.split(ipv4Prefix.getValue()).iterator();
71     }
72
73     public static Iterator<String> splitToParts(final Ipv4Address ipv4Address) {
74         /* Invalid (Ab)use of ip address as prefix!!! */
75         return Iterators.forArray(ipv4Address.getValue(), String.valueOf(IPV4_ADDRESS_LENGTH));
76     }
77
78     public static Iterator<String> splitToParts(final Ipv6Prefix ipv6Prefix) {
79         return PREFIX_SPLITTER.split(ipv6Prefix.getValue()).iterator();
80     }
81
82     public static Iterator<String> splitToParts(final Ipv6Address ipv6Address) {
83         /* Invalid (Ab)use of ip address as prefix!!! */
84         return Iterators.forArray(ipv6Address.getValue(), String.valueOf(IPV6_ADDRESS_LENGTH));
85     }
86
87     /* This forest of functions has a purpose:
88      *
89      * 1. There are multiple coding styles around the plugin, this is necessary in order to have
90      *   one mechanism to convert them all, one mechanism to find them...
91      * 2. I hope that one day yangtools will actually deliver code fit for purpose in a packet
92      *   processing application (presently it is not. When this happens, these can be optimized
93      *   for "side-load" of pre-vetted data. Example. IP Address (v4 or v6) is prevetted left of the
94      *   prefix. It should be loadable into Prefix without _RERUNNING_ 100ms+ of regexps. When (and if)
95      *   that happens, it will be a simple fix here without chasing it across the whole plugin.
96     */
97
98     public static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address){
99         return new Ipv4Prefix(ipv4Address.getValue() + PREFIX_SEPARATOR + IPV4_ADDRESS_LENGTH);
100     }
101
102     public static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address, final String mask){
103         /*
104          * Ipv4Address has already validated the address part of the prefix,
105          * It is mandated to comply to the same regexp as the address
106          * There is absolutely no point rerunning additional checks vs this
107          * Note - there is no canonical form check here!!!
108          */
109         if (null != mask && !mask.isEmpty()) {
110             return new Ipv4Prefix(ipv4Address.getValue() + PREFIX_SEPARATOR + mask);
111         } else {
112             return new Ipv4Prefix(ipv4Address.getValue() + PREFIX_SEPARATOR + IPV4_ADDRESS_LENGTH);
113         }
114     }
115
116     public static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address, final int intmask){
117         return createPrefix(ipv4Address, String.valueOf(intmask));
118     }
119
120     public static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address, final byte [] bytemask){
121         return createPrefix(ipv4Address, String.valueOf(countBits(bytemask)));
122     }
123
124     public static Ipv6Prefix createPrefix(final Ipv6Address ipv6Address){
125         return new Ipv6Prefix(ipv6Address.getValue() + PREFIX_SEPARATOR + IPV6_ADDRESS_LENGTH);
126     }
127
128     public static Ipv6Prefix createPrefix(final Ipv6Address ipv6Address, final String mask){
129         /*
130          * Ipv6Address has already validated the address part of the prefix,
131          * It is mandated to comply to the same regexp as the address
132          * There is absolutely no point rerunning additional checks vs this
133          * Note - there is no canonical form check here!!!
134          */
135         if (Strings.isNullOrEmpty(mask)) {
136             return new Ipv6Prefix(ipv6Address.getValue() + PREFIX_SEPARATOR + String.valueOf(IPV6_ADDRESS_LENGTH));
137         } else {
138             return new Ipv6Prefix(ipv6Address.getValue() + PREFIX_SEPARATOR + mask);
139         }
140     }
141
142     public static Ipv6Prefix createPrefix(final Ipv6Address ipv6Address, final int intmask){
143         return createPrefix(ipv6Address, String.valueOf(intmask));
144     }
145
146     public static Ipv6Prefix createPrefix(final Ipv6Address ipv6Address, final byte [] bytemask){
147         /*
148          * Ipv4Address has already validated the address part of the prefix,
149          * It is mandated to comply to the same regexp as the address
150          * There is absolutely no point rerunning additional checks vs this
151          * Note - there is no canonical form check here!!!
152          */
153          return createPrefix(ipv6Address, String.valueOf(countBits(bytemask)));
154     }
155
156     public static Integer extractPrefix(final Ipv4Prefix ipv4Prefix) {
157         Iterator<String> addressParts = splitToParts(ipv4Prefix);
158         addressParts.next();
159         Integer retval = null;
160         if (addressParts.hasNext()) {
161             retval = Integer.parseInt(addressParts.next());
162         }
163         return retval;
164     }
165
166     public static Integer extractPrefix(final Ipv6Prefix ipv6Prefix) {
167         Iterator<String> addressParts = splitToParts(ipv6Prefix);
168         addressParts.next();
169         Integer retval = null;
170         if (addressParts.hasNext()) {
171             retval = Integer.parseInt(addressParts.next());
172         }
173         return retval;
174     }
175
176     public static Integer extractPrefix(final Ipv4Address ipv4Prefix) {
177         return IPV4_ADDRESS_LENGTH;
178     }
179
180     public static Integer extractPrefix(final Ipv6Address ipv6Prefix) {
181         return 128;
182     }
183
184     /*
185      * BIG FAT WARNING!!!
186      * Read all of the following before you touch any v6 code or decide to
187      * optimize it by invoking a "simple" Guava call
188      *
189      * Java IPv6 is fundamentally broken and Google libraries do not fix it.
190      * 1. Java will allways implicitly rewrite v4 mapped into v6 as a v4 address
191      *      and there is absolutely no way to override this behaviour
192      * 2. Guava libraries cannot parse non-canonical IPv6. They will throw an
193      *      exception. Even if they did, they re-use the same broken java code
194      *      underneath.
195      *
196      * This is why we have to parse v6 by ourselves.
197      *
198      * The following conversion code is based on inet_cidr_pton_ipv6 in NetBSD
199      *
200      * The original BSD code is licensed under standard BSD license. While we
201      * are not obliged to provide an attribution, credit where credit is due.
202      * As far as why it is similar to Sun's sun.net.util please ask Sun why
203      * their code has the same variable names, comments and code flow.
204      *
205      */
206
207
208      /**
209      * Convert Ipv6Address object to a valid Canonical v6 address in byte format
210      *
211      * @param ipv6Address - v6 Address object
212      * @return - byte array of size 16. Last byte contains netmask
213      */
214
215
216     public static byte[] canonicalBinaryV6Address(final Ipv6Address ipv6Address) {
217         /*
218          * Do not modify this routine to take direct strings input!!!
219          * Key checks have been removed based on the assumption that
220          * the input is validated via regexps in Ipv6Prefix()
221          */
222
223         String [] address =  (ipv6Address.getValue()).split("%");
224
225         int colonp;
226         char ch;
227         boolean saw_xdigit;
228
229         /* Isn't it fun - the above variable names are the same in BSD and Sun sources */
230
231         int val;
232
233         char[] src = address[0].toCharArray();
234
235         byte[] dst = new byte[INADDR6SZ];
236
237         int src_length = src.length;
238
239         colonp = -1;
240         int i = 0, j = 0;
241
242         /* Leading :: requires some special handling. */
243
244         /* Isn't it fun - the above comment is again the same in BSD and Sun sources,
245          * We will derive our code from BSD. Shakespear always sounds better
246          * in original Clingon. So does Dilbert.
247          */
248
249         if (src[i] == ':') {
250             Preconditions.checkArgument(src[++i] == ':', "Invalid v6 address");
251         }
252
253         int curtok = i;
254         saw_xdigit = false;
255
256
257         val = 0;
258         while (i < src_length) {
259             ch = src[i++];
260             int chval = Character.digit(ch, 16);
261
262             /* Business as usual - ipv6 address digit.
263              * We can remove all checks from the original BSD code because
264              * the regexp has already verified that we are not being fed
265              * anything bigger than 0xffff between the separators.
266              */
267
268             if (chval != -1) {
269                 val <<= 4;
270                 val |= chval;
271                 saw_xdigit = true;
272                 continue;
273             }
274
275             /* v6 separator */
276
277             if (ch == ':') {
278                 curtok = i;
279                 if (!saw_xdigit) {
280                     /* no need to check separator position validity - regexp does that */
281                     colonp = j;
282                     continue;
283                 }
284
285                 /* removed overrun check - the regexp checks for valid data */
286
287                 dst[j++] = (byte) ((val >>> 8) & 0xff);
288                 dst[j++] = (byte) (val & 0xff);
289                 saw_xdigit = false;
290                 val = 0;
291                 continue;
292             }
293
294             /* frankenstein - v4 attached to v6, mixed notation */
295
296             if (ch == '.' && ((j + INADDR4SZ) <= INADDR6SZ)) {
297
298                 /* this has passed the regexp so it is fairly safe to parse it
299                  * straight away. As v4 addresses do not suffer from the same
300                  * defficiencies as the java v6 implementation we can invoke it
301                  * straight away and be done with it
302                  */
303
304                 Preconditions.checkArgument(j != (INADDR6SZ - INADDR4SZ - 1), "Invalid v4 in v6 mapping");
305
306                 InetAddress _inet_form = InetAddresses.forString(address[0].substring(curtok, src_length));
307
308                 Preconditions.checkArgument(_inet_form instanceof Inet4Address);
309                 System.arraycopy(_inet_form.getAddress(), 0, dst, j, INADDR4SZ);
310                 j += INADDR4SZ;
311
312                 saw_xdigit = false;
313                 break;
314             }
315             /* removed parser exit on invalid char - no need to do it, regexp checks it */
316         }
317         if (saw_xdigit) {
318             Preconditions.checkArgument(j + INT16SZ <= INADDR6SZ, "Overrun in v6 parsing, should not occur");
319             dst[j++] = (byte) ((val >> 8) & 0xff);
320             dst[j++] = (byte) (val & 0xff);
321         }
322
323         if (colonp != -1) {
324             int n = j - colonp;
325
326             Preconditions.checkArgument(j != INADDR6SZ, "Overrun in v6 parsing, should not occur");
327             for (i = 1; i <= n; i++) {
328                 dst[INADDR6SZ - i] = dst[colonp + n - i];
329                 dst[colonp + n - i] = 0;
330             }
331             j = INADDR6SZ;
332         }
333
334         Preconditions.checkArgument(j == INADDR6SZ, "Overrun in v6 parsing, should not occur");
335
336         return dst;
337     }
338
339     public static String byteArrayV6AddressToString (final byte [] _binary_form) throws UnknownHostException{
340         /* DO NOT DIY!!! - InetAddresses will actually print correct canonical
341          * zero compressed form.
342          */
343         return InetAddresses.toAddrString(InetAddress.getByAddress(_binary_form));
344     }
345
346     private static int nextNibble(final int mask) {
347         if (mask <= 0) {
348             return 0;
349         }
350         if (mask > 8) {
351             return 0xff;
352         }
353         return 0xff << (8 - mask);
354     }
355
356     /**
357      * Convert Ipv6Prefix object to a valid Canonical v6 prefix in byte format
358      *
359      * @param ipv6Prefix - v6 prefix object
360      * @return - byte array of size 16 + 1. Last byte contains netmask
361      */
362     public static byte[] canonicalBinaryV6Prefix(final Ipv6Prefix ipv6Prefix) {
363         /*
364          * Do not modify this routine to take direct strings input!!!
365          * Key checks have been removed based on the assumption that
366          * the input is validated via regexps in Ipv6Prefix()
367          */
368
369         int mask = 128;
370
371         String [] address = null;
372
373         boolean valid = true;
374
375         address =  (ipv6Prefix.getValue()).split("/");
376         try {
377             mask = Integer.parseInt(address[1]);
378             if (mask > 128) {
379                 valid = false;
380             }
381         } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
382             valid = false;
383         }
384
385         Preconditions.checkArgument(valid, "Supplied netmask in %s is invalid", ipv6Prefix.getValue());
386
387
388         int colonp;
389         char ch;
390         boolean saw_xdigit;
391
392         /* Isn't it fun - the above variable names are the same in BSD and Sun sources */
393
394         int val;
395
396         char[] src = address[0].toCharArray();
397
398         byte[] dst = new byte[INADDR6SZ + 1];
399
400         int m = mask;
401
402         int src_length = src.length;
403
404         colonp = -1;
405         int i = 0, j = 0;
406
407         /* Leading :: requires some special handling. */
408
409         /* Isn't it fun - the above comment is again the same in BSD and Sun sources,
410          * We will derive our code from BSD. Shakespear always sounds better
411          * in original Clingon. So does Dilbert.
412          */
413
414         if (src[i] == ':') {
415             Preconditions.checkArgument(src[++i] == ':', "Invalid v6 address");
416         }
417
418         int curtok = i;
419         saw_xdigit = false;
420
421
422         val = 0;
423         while (i < src_length) {
424             ch = src[i++];
425             int chval = Character.digit(ch, 16);
426
427             /* Business as usual - ipv6 address digit.
428              * We can remove all checks from the original BSD code because
429              * the regexp has already verified that we are not being fed
430              * anything bigger than 0xffff between the separators.
431              */
432
433             if (chval != -1) {
434                 val <<= 4;
435                 val |= chval;
436                 saw_xdigit = true;
437                 continue;
438             }
439
440             /* v6 separator */
441
442             if (ch == ':') {
443                 curtok = i;
444                 if (!saw_xdigit) {
445                     /* no need to check separator position validity - regexp does that */
446                     colonp = j;
447                     continue;
448                 }
449
450                 /* removed overrun check - the regexp checks for valid data */
451
452                 saw_xdigit = false;
453
454                 if (m < 0) {
455                     /* stop parsing if we are past the mask */
456                     break;
457                 }
458
459                 dst[j] = (byte) ((val >> 8) & nextNibble(m)); j++; m = m - 8;
460
461                 if (m < 0) {
462                     /* stop parsing if we are past the mask */
463                     break;
464                 }
465
466                 dst[j] = (byte) (val & nextNibble(m)); j++; m = m - 8;
467
468                 val = 0;
469                 continue;
470             }
471
472             /* frankenstein - v4 attached to v6, mixed notation */
473
474             if (ch == '.' && ((j + INADDR4SZ) <= INADDR6SZ)) {
475
476                 /* this has passed the regexp so it is fairly safe to parse it
477                  * straight away. As v4 addresses do not suffer from the same
478                  * defficiencies as the java v6 implementation we can invoke it
479                  * straight away and be done with it
480                  */
481
482                 Preconditions.checkArgument(j != (INADDR6SZ - INADDR4SZ - 1), "Invalid v4 in v6 mapping");
483
484                 InetAddress _inet_form = InetAddresses.forString(address[0].substring(curtok, src_length));
485
486                 Preconditions.checkArgument(_inet_form instanceof Inet4Address);
487                 System.arraycopy(_inet_form.getAddress(), 0, dst, j, INADDR4SZ);
488                 j +=  INADDR4SZ;
489
490                 saw_xdigit = false;
491                 break;
492             }
493             /* removed parser exit on ivalid char - no need to do it, regexp checks it */
494         }
495         if (saw_xdigit) {
496             Preconditions.checkArgument(j + INT16SZ <= INADDR6SZ, "Overrun in v6 parsing, should not occur");
497             dst[j] = (byte) ((val >> 8) & nextNibble(m)) ; j++; m = m - 8;
498             dst[j] = (byte) (val & nextNibble(m)); j++; m = m - 8;
499         }
500
501         if ((j < INADDR6SZ) && (m < 0)) {
502             /* past the mask */
503             for (i = j; i < INADDR6SZ; i++) {
504                 dst[i] = 0;
505             }
506         } else {
507             /* normal parsing */
508             if (colonp != -1) {
509                 int n = j - colonp;
510
511                 Preconditions.checkArgument(j != INADDR6SZ, "Overrun in v6 parsing, should not occur");
512                 for (i = 1; i <= n; i++) {
513                     dst[INADDR6SZ - i] = dst[colonp + n - i];
514                     dst[colonp + n - i] = 0;
515                 }
516                 j = INADDR6SZ;
517             }
518             Preconditions.checkArgument(j == INADDR6SZ, "Overrun in v6 parsing, should not occur");
519         }
520
521         dst[INADDR6SZ] = (byte) mask;
522         return dst;
523     }
524
525     /**
526      * Print a v6 prefix in byte array + 1 notation
527      * @param _binary_form - prefix, in byte [] form, last byte is netmask
528      * @return string of v6 prefix
529      * @throws UnknownHostException unknown host exception
530      */
531     public static String byteArrayV6PrefixToString(final byte [] _binary_form) throws UnknownHostException {
532         /* NO DIY!!! - InetAddresses will actually print correct canonical
533          * zero compressed form
534          */
535         StringBuilder sb = new StringBuilder();
536         /* Yang RFC specifies that the normalized form is RFC 5952, note - java
537          * core type is not RFC compliant, guava is.
538          */
539         sb.append(
540             InetAddresses.toAddrString(
541                 InetAddress.getByAddress(
542                     Arrays.copyOf(_binary_form, INADDR6SZ)
543                 )
544             )
545         );
546         sb.append('/');
547         sb.append(_binary_form[INADDR6SZ] & 0xff);
548         return sb.toString();
549     }
550
551     private static int ipv6PrefixByteArrayOffset(final int mask) {
552         if (mask < 0) {
553             return 0;
554         }
555
556         final int ret = mask * INADDR6SZ;
557         if (ret < PREFIX_BYTEARRAYS.length) {
558             return ret;
559         } else {
560             return PREFIX_BYTEARRAYS.length - INADDR6SZ;
561         }
562     }
563
564      /**
565      * Canonicalize a v6 prefix while in binary form
566      *
567      * @param prefix - prefix, in byte [] form
568      * @param mask - mask - number of bits
569      */
570     public static void canonicalizeIpv6Prefix(final byte [] prefix, final int mask) {
571         final int offset = ipv6PrefixByteArrayOffset(mask);
572
573         for (int i = 0; i < INADDR6SZ; i++) {
574             prefix[i] &= PREFIX_BYTEARRAYS[offset + i];
575         }
576     }
577
578     public static byte[] convertIpv6PrefixToByteArray(final int prefix) {
579         final int offset = ipv6PrefixByteArrayOffset(prefix);
580
581         return Arrays.copyOfRange(PREFIX_BYTEARRAYS, offset, offset + INADDR6SZ);
582     }
583
584     public static Ipv6Address extractIpv6Address(final Ipv6Prefix ipv6Prefix) {
585         Iterator<String> addressParts = PREFIX_SPLITTER.split(ipv6Prefix.getValue()).iterator();
586         return new Ipv6Address(addressParts.next());
587     }
588
589     public static Integer extractIpv6Prefix(final Ipv6Prefix ipv6Prefix) {
590         Iterator<String> addressParts = PREFIX_SPLITTER.split(ipv6Prefix.getValue()).iterator();
591         addressParts.next();
592
593         Integer prefix = null;
594         if (addressParts.hasNext()) {
595             prefix = Integer.parseInt(addressParts.next());
596         }
597         return prefix;
598     }
599
600     public static int countBits(final byte[] mask) {
601         int netmask = 0;
602         for (byte b : mask) {
603             netmask += Integer.bitCount(UnsignedBytes.toInt(b));
604         }
605         return netmask;
606     }
607 }