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