dd158ccaeda82f1a23c941c307a094974979c8ea
[openflowplugin.git] / openflowplugin / src / main / java / org / opendaylight / openflowplugin / openflow / md / core / sal / convertor / IpConversionUtil.java
1 /*
2  * Copyright (c) 2015 Brocade, Communications Systems, Inc
3  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
4  *
5  * This program and the accompanying materials are made available under the
6  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7  * and is available at http://www.eclipse.org/legal/epl-v10.html
8  */
9
10 package org.opendaylight.openflowplugin.openflow.md.core.sal.convertor;
11
12 import com.google.common.base.Splitter;
13 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
14 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
15 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Address;
16 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
17 import java.util.Iterator;
18 import com.google.common.collect.Iterators;
19 import java.util.Arrays;
20 import java.net.UnknownHostException;
21 import java.net.Inet4Address;
22 import java.net.InetAddress;
23 import com.google.common.net.InetAddresses;
24 import com.google.common.primitives.UnsignedBytes;
25 import com.google.common.base.Preconditions;
26
27
28 /**
29  * Created by Martin Bobak <mbobak@cisco.com> on 5.3.2015.
30  * v6 routines added by Anton Ivanov on 14.6.2015
31  */
32 public final class IpConversionUtil {
33
34     public static final String PREFIX_SEPARATOR = "/";
35     public static final Splitter PREFIX_SPLITTER = Splitter.on('/');
36     private static final int INADDR4SZ = 4;
37     private static final int INADDR6SZ = 16;
38     private static final int INT16SZ = 2;
39
40     private IpConversionUtil() {
41         throw new UnsupportedOperationException("This class should not be instantiated.");
42     }
43
44     public static Iterator<String> splitToParts(final Ipv4Prefix ipv4Prefix) {
45         return PREFIX_SPLITTER.split(ipv4Prefix.getValue()).iterator();
46     }
47
48     public static Iterator<String> splitToParts(final Ipv4Address ipv4Address) {
49         /* Invalid (Ab)use of ip address as prefix!!! */
50         String [] tempPrefix = {ipv4Address.getValue(), "32"};
51         return Iterators.forArray(tempPrefix);
52     }
53
54     public static Iterator<String> splitToParts(final Ipv6Prefix ipv6Prefix) {
55         return PREFIX_SPLITTER.split(ipv6Prefix.getValue()).iterator();
56     }
57
58     public static Iterator<String> splitToParts(final Ipv6Address ipv6Address) {
59         /* Invalid (Ab)use of ip address as prefix!!! */
60         String [] tempPrefix = {ipv6Address.getValue(), "128"};
61         return Iterators.forArray(tempPrefix);
62     }
63
64
65     /* This forest of functions has a purpose:
66      *
67      * 1. There are multiple coding styles around the plugin, this is necessary in order to have
68      *   one mechanism to convert them all, one mechanism to find them...
69      * 2. I hope that one day yangtools will actually deliver code fit for purpose in a packet
70      *   processing application (presently it is not. When this happens, these can be optimized
71      *   for "side-load" of pre-vetted data. Example. IP Address (v4 or v6) is prevetted left of the
72      *   prefix. It should be loadable into Prefix without _RERUNNING_ 100ms+ of regexps. When (and if)
73      *   that happens, it will be a simple fix here without chasing it across the whole plugin.
74     */
75
76     public static Ipv4Prefix createPrefix(Ipv4Address ipv4Address){
77         return new Ipv4Prefix(ipv4Address.getValue() + PREFIX_SEPARATOR + 32);
78     }
79
80     public static Ipv4Prefix createPrefix(Ipv4Address ipv4Address, String mask){
81         /*
82          * Ipv4Address has already validated the address part of the prefix,
83          * It is mandated to comply to the same regexp as the address
84          * There is absolutely no point rerunning additional checks vs this
85          * Note - there is no canonical form check here!!!
86          */
87         if (null != mask && !mask.isEmpty()) {
88             return new Ipv4Prefix(ipv4Address.getValue() + PREFIX_SEPARATOR + mask);
89         } else {
90             return new Ipv4Prefix(ipv4Address.getValue() + PREFIX_SEPARATOR + "32");
91         }
92     }
93
94     public static Ipv4Prefix createPrefix(Ipv4Address ipv4Address, int intmask){
95         return createPrefix(ipv4Address, String.valueOf(intmask));
96     }
97
98     public static Ipv4Prefix createPrefix(Ipv4Address ipv4Address, byte [] bytemask){
99         return createPrefix(ipv4Address, String.valueOf(countBits(bytemask)));
100     }
101
102     public static Ipv6Prefix createPrefix(Ipv6Address ipv6Address){
103         return new Ipv6Prefix(ipv6Address.getValue() + PREFIX_SEPARATOR + 128);
104     }
105
106     public static Ipv6Prefix createPrefix(Ipv6Address ipv6Address, String mask){
107         /*
108          * Ipv6Address has already validated the address part of the prefix,
109          * It is mandated to comply to the same regexp as the address
110          * There is absolutely no point rerunning additional checks vs this
111          * Note - there is no canonical form check here!!!
112          */
113         if (null != mask && !mask.isEmpty()) {
114             return new Ipv6Prefix(ipv6Address.getValue() + PREFIX_SEPARATOR + mask);
115         } else {
116             return new Ipv6Prefix(ipv6Address.getValue() + PREFIX_SEPARATOR + "128");
117         }
118     }
119
120     public static Ipv6Prefix createPrefix(Ipv6Address ipv6Address, int intmask){
121         return createPrefix(ipv6Address, String.valueOf(intmask));
122     }
123
124     public static Ipv6Prefix createPrefix(Ipv6Address ipv6Address, byte [] bytemask){
125         /*
126          * Ipv4Address has already validated the address part of the prefix,
127          * It is mandated to comply to the same regexp as the address
128          * There is absolutely no point rerunning additional checks vs this
129          * Note - there is no canonical form check here!!!
130          */
131          return createPrefix(ipv6Address, String.valueOf(countBits(bytemask)));
132     }
133
134     public static Integer extractPrefix(Ipv4Prefix ipv4Prefix) {
135         Iterator<String> addressParts = splitToParts(ipv4Prefix);
136         addressParts.next();
137         Integer retval = null;
138         if (addressParts.hasNext()) {
139             retval = Integer.parseInt(addressParts.next());
140         }
141         return retval;
142     }
143
144     public static Integer extractPrefix(Ipv6Prefix ipv6Prefix) {
145         Iterator<String> addressParts = splitToParts(ipv6Prefix);
146         addressParts.next();
147         Integer retval = null;
148         if (addressParts.hasNext()) {
149             retval = Integer.parseInt(addressParts.next());
150         }
151         return retval;
152     }
153
154     public static Integer extractPrefix(Ipv4Address ipv4Prefix) {
155         return 32;
156     }
157
158     public static Integer extractPrefix(Ipv6Address ipv6Prefix) {
159         return 128;
160     }
161
162     /*
163      * BIG FAT WARNING!!!
164      * Read all of the following before you touch any v6 code or decide to
165      * optimize it by invoking a "simple" Guava call
166      *
167      * Java IPv6 is fundamentally broken and Google libraries do not fix it.
168      * 1. Java will allways implicitly rewrite v4 mapped into v6 as a v4 address
169      *      and there is absolutely no way to override this behaviour
170      * 2. Guava libraries cannot parse non-canonical IPv6. They will throw an
171      *      exception. Even if they did, they re-use the same broken java code
172      *      underneath.
173      *
174      * This is why we have to parse v6 by ourselves.
175      *
176      * The following conversion code is based on inet_cidr_pton_ipv6 in NetBSD
177      *
178      * The original BSD code is licensed under standard BSD license. While we
179      * are not obliged to provide an attribution, credit where credit is due.
180      * As far as why it is similar to Sun's sun.net.util please ask Sun why
181      * their code has the same variable names, comments and code flow.
182      *
183      */
184
185
186      /**
187      * Convert Ipv6Address object to a valid Canonical v6 address in byte format
188      *
189      * @param ipv6Address - v6 Address object
190      * @return - byte array of size 16. Last byte contains netmask
191      */
192
193
194     public static byte[] canonicalBinaryV6Address(Ipv6Address ipv6Address) {
195         /*
196          * Do not modify this routine to take direct strings input!!!
197          * Key checks have been removed based on the assumption that
198          * the input is validated via regexps in Ipv6Prefix()
199          */
200
201         String [] address =  (ipv6Address.getValue()).split("%");
202
203         int colonp;
204         char ch;
205         boolean saw_xdigit;
206
207         /* Isn't it fun - the above variable names are the same in BSD and Sun sources */
208
209         int val;
210
211         char[] src = address[0].toCharArray();
212
213         byte[] dst = new byte[INADDR6SZ];
214
215         int src_length = src.length;
216
217         colonp = -1;
218         int i = 0, j = 0;
219
220         /* Leading :: requires some special handling. */
221
222         /* Isn't it fun - the above comment is again the same in BSD and Sun sources,
223          * We will derive our code from BSD. Shakespear always sounds better
224          * in original Clingon. So does Dilbert.
225          */
226
227         if (src[i] == ':') {
228             Preconditions.checkArgument(src[++i] == ':', "Invalid v6 address");
229         }
230
231         int curtok = i;
232         saw_xdigit = false;
233
234
235         val = 0;
236         while (i < src_length) {
237             ch = src[i++];
238             int chval = Character.digit(ch, 16);
239
240             /* Business as usual - ipv6 address digit.
241              * We can remove all checks from the original BSD code because
242              * the regexp has already verified that we are not being fed
243              * anything bigger than 0xffff between the separators.
244              */
245
246             if (chval != -1) {
247                 val <<= 4;
248                 val |= chval;
249                 saw_xdigit = true;
250                 continue;
251             }
252
253             /* v6 separator */
254
255             if (ch == ':') {
256                 curtok = i;
257                 if (!saw_xdigit) {
258                     /* no need to check separator position validity - regexp does that */
259                     colonp = j;
260                     continue;
261                 }
262
263                 /* removed overrun check - the regexp checks for valid data */
264
265                 dst[j++] = (byte) ((val >> 8) & 0xff);
266                 dst[j++] = (byte) (val & 0xff);
267                 saw_xdigit = false;
268                 val = 0;
269                 continue;
270             }
271
272             /* frankenstein - v4 attached to v6, mixed notation */
273
274             if (ch == '.' && ((j + INADDR4SZ) <= INADDR6SZ)) {
275
276                 /* this has passed the regexp so it is fairly safe to parse it
277                  * straight away. As v4 addresses do not suffer from the same
278                  * defficiencies as the java v6 implementation we can invoke it
279                  * straight away and be done with it
280                  */
281
282                 Preconditions.checkArgument(j != (INADDR6SZ - INADDR4SZ - 1), "Invalid v4 in v6 mapping");
283
284                 InetAddress _inet_form = InetAddresses.forString(address[0].substring(curtok, src_length));
285
286                 Preconditions.checkArgument(_inet_form instanceof Inet4Address);
287
288                 byte[] v4addr =  _inet_form.getAddress();
289
290                 for (int k = 0; k < INADDR4SZ; k++) {
291                     dst[j++] = v4addr[k];
292                 }
293                 saw_xdigit = false;
294                 break;
295             }
296             /* removed parser exit on ivalid 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 (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(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(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
469                 byte[] v4addr =  _inet_form.getAddress();
470
471                 for (int k = 0; k < INADDR4SZ; k++) {
472                     dst[j++] = v4addr[k];
473                 }
474                 saw_xdigit = false;
475                 break;
476             }
477             /* removed parser exit on ivalid char - no need to do it, regexp checks it */
478         }
479         if (saw_xdigit) {
480             Preconditions.checkArgument(j + INT16SZ <= INADDR6SZ, "Overrun in v6 parsing, should not occur");
481             dst[j] = (byte) ((val >> 8) & nextNibble(m)) ; j++; m = m - 8;
482             dst[j] = (byte) (val & nextNibble(m)); j++; m = m - 8;
483         }
484
485         if ((j < INADDR6SZ) && (m < 0)) {
486             /* past the mask */
487             for (i = j; i < INADDR6SZ; i++) {
488                 dst[i] = 0;
489             }
490         } else {
491             /* normal parsing */
492             if (colonp != -1) {
493                 int n = j - colonp;
494
495                 Preconditions.checkArgument(j != INADDR6SZ, "Overrun in v6 parsing, should not occur");
496                 for (i = 1; i <= n; i++) {
497                     dst[INADDR6SZ - i] = dst[colonp + n - i];
498                     dst[colonp + n - i] = 0;
499                 }
500                 j = INADDR6SZ;
501             }
502             Preconditions.checkArgument(j == INADDR6SZ, "Overrun in v6 parsing, should not occur");
503         }
504
505         dst[INADDR6SZ] = (byte) mask;
506         return dst;
507     }
508
509     /**
510      * Print a v6 prefix in byte array + 1 notation
511      *
512      * @param _binary_form - prefix, in byte [] form, last byte is netmask
513      */
514     public static String byteArrayV6PrefixToString(byte [] _binary_form) throws UnknownHostException {
515         /* NO DIY!!! - InetAddresses will actually print correct canonical
516          * zero compressed form
517          */
518         StringBuilder sb = new StringBuilder();
519         /* Yang RFC specifies that the normalized form is RFC 5952, note - java
520          * core type is not RFC compliant, guava is.
521          */
522         sb.append(
523             InetAddresses.toAddrString(
524                 InetAddress.getByAddress(
525                     Arrays.copyOfRange(_binary_form, 0, INADDR6SZ)
526                 )
527             )
528         );
529         sb.append('/');
530         sb.append(_binary_form[INADDR6SZ] & 0xff);
531         return sb.toString();
532     }
533
534
535      /**
536      * Canonicalize a v6 prefix while in binary form
537      *
538      * @param _prefix - prefix, in byte [] form
539      * @param mask - mask - number of bits
540      */
541     public static void canonicalizeIpv6Prefix(byte [] _prefix, int mask) {
542
543         for (int i=0; i < INADDR6SZ; i++) {
544             _prefix[i] = (byte) (_prefix[i] & nextNibble(mask));
545             mask = mask - 8;
546         }
547     }
548
549     public static byte[] convertIpv6PrefixToByteArray(int prefix) {
550         byte[] mask = new byte[16];
551         for (int count = 0; count < 16; count++) {
552             mask[count] = (byte) nextNibble(prefix);
553             prefix = prefix - 8;
554         }
555         return mask;
556     }
557
558     public static Ipv6Address extractIpv6Address(final Ipv6Prefix ipv6Prefix) {
559         Iterator<String> addressParts = PREFIX_SPLITTER.split(ipv6Prefix.getValue()).iterator();
560         return new Ipv6Address(addressParts.next());
561     }
562
563     public static Integer extractIpv6Prefix(final Ipv6Prefix ipv6Prefix) {
564         Iterator<String> addressParts = PREFIX_SPLITTER.split(ipv6Prefix.getValue()).iterator();
565         addressParts.next();
566
567         Integer prefix = null;
568         if (addressParts.hasNext()) {
569             prefix = Integer.parseInt(addressParts.next());
570         }
571         return prefix;
572     }
573
574     public static int countBits(byte[] mask) {
575         int netmask = 0;
576         for (byte b : mask) {
577             netmask += Integer.bitCount(UnsignedBytes.toInt(b));
578         }
579         return netmask;
580     }
581 }