2 * Copyright (c) 2015 Cisco Systems, Inc., Brocade, Communications Systems, Inc. and others. All rights reserved.
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
9 package org.opendaylight.openflowplugin.openflow.md.core.sal.convertor;
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;
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;
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;
31 * Created by Martin Bobak <mbobak@cisco.com> on 5.3.2015.
32 * v6 routines added by Anton Ivanov on 14.6.2015
34 public final class IpConversionUtil {
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;
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.
49 private static final byte[] PREFIX_BYTEARRAYS;
51 final byte[] a = new byte[(INADDR6SZ * Byte.SIZE + 1) * INADDR6SZ];
54 for (int p = 0; p <= INADDR6SZ * Byte.SIZE; ++p) {
56 for (int i = 0; i < INADDR6SZ; ++i) {
57 a[offset++] = (byte) nextNibble(prefix);
62 PREFIX_BYTEARRAYS = a;
65 private IpConversionUtil() {
66 throw new UnsupportedOperationException("This class should not be instantiated.");
69 public static Iterator<String> splitToParts(final Ipv4Prefix ipv4Prefix) {
70 return PREFIX_SPLITTER.split(ipv4Prefix.getValue()).iterator();
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));
78 public static Iterator<String> splitToParts(final Ipv6Prefix ipv6Prefix) {
79 return PREFIX_SPLITTER.split(ipv6Prefix.getValue()).iterator();
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));
87 /* This forest of functions has a purpose:
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.
98 public static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address){
99 return new Ipv4Prefix(ipv4Address.getValue() + PREFIX_SEPARATOR + IPV4_ADDRESS_LENGTH);
102 public static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address, final String mask){
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!!!
109 if (null != mask && !mask.isEmpty()) {
110 return new Ipv4Prefix(ipv4Address.getValue() + PREFIX_SEPARATOR + mask);
112 return new Ipv4Prefix(ipv4Address.getValue() + PREFIX_SEPARATOR + IPV4_ADDRESS_LENGTH);
116 public static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address, final int intmask){
117 return createPrefix(ipv4Address, String.valueOf(intmask));
120 public static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address, final byte [] bytemask){
121 return createPrefix(ipv4Address, String.valueOf(countBits(bytemask)));
124 public static Ipv6Prefix createPrefix(final Ipv6Address ipv6Address){
125 return new Ipv6Prefix(ipv6Address.getValue() + PREFIX_SEPARATOR + IPV6_ADDRESS_LENGTH);
128 public static Ipv6Prefix createPrefix(final Ipv6Address ipv6Address, final String mask){
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!!!
135 if (Strings.isNullOrEmpty(mask)) {
136 return new Ipv6Prefix(ipv6Address.getValue() + PREFIX_SEPARATOR + String.valueOf(IPV6_ADDRESS_LENGTH));
138 return new Ipv6Prefix(ipv6Address.getValue() + PREFIX_SEPARATOR + mask);
142 public static Ipv6Prefix createPrefix(final Ipv6Address ipv6Address, final int intmask){
143 return createPrefix(ipv6Address, String.valueOf(intmask));
146 public static Ipv6Prefix createPrefix(final Ipv6Address ipv6Address, final byte [] bytemask){
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!!!
153 return createPrefix(ipv6Address, String.valueOf(countBits(bytemask)));
156 public static Integer extractPrefix(final Ipv4Prefix ipv4Prefix) {
157 Iterator<String> addressParts = splitToParts(ipv4Prefix);
159 Integer retval = null;
160 if (addressParts.hasNext()) {
161 retval = Integer.parseInt(addressParts.next());
166 public static Integer extractPrefix(final Ipv6Prefix ipv6Prefix) {
167 Iterator<String> addressParts = splitToParts(ipv6Prefix);
169 Integer retval = null;
170 if (addressParts.hasNext()) {
171 retval = Integer.parseInt(addressParts.next());
176 public static Integer extractPrefix(final Ipv4Address ipv4Prefix) {
177 return IPV4_ADDRESS_LENGTH;
180 public static Integer extractPrefix(final Ipv6Address ipv6Prefix) {
186 * Read all of the following before you touch any v6 code or decide to
187 * optimize it by invoking a "simple" Guava call
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
196 * This is why we have to parse v6 by ourselves.
198 * The following conversion code is based on inet_cidr_pton_ipv6 in NetBSD
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.
209 * Convert Ipv6Address object to a valid Canonical v6 address in byte format
211 * @param ipv6Address - v6 Address object
212 * @return - byte array of size 16. Last byte contains netmask
216 public static byte[] canonicalBinaryV6Address(final Ipv6Address ipv6Address) {
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()
223 String [] address = (ipv6Address.getValue()).split("%");
229 /* Isn't it fun - the above variable names are the same in BSD and Sun sources */
233 char[] src = address[0].toCharArray();
235 byte[] dst = new byte[INADDR6SZ];
237 int src_length = src.length;
242 /* Leading :: requires some special handling. */
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.
250 Preconditions.checkArgument(src[++i] == ':', "Invalid v6 address");
258 while (i < src_length) {
260 int chval = Character.digit(ch, 16);
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.
280 /* no need to check separator position validity - regexp does that */
285 /* removed overrun check - the regexp checks for valid data */
287 dst[j++] = (byte) ((val >>> 8) & 0xff);
288 dst[j++] = (byte) (val & 0xff);
294 /* frankenstein - v4 attached to v6, mixed notation */
296 if (ch == '.' && ((j + INADDR4SZ) <= INADDR6SZ)) {
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
304 Preconditions.checkArgument(j != (INADDR6SZ - INADDR4SZ - 1), "Invalid v4 in v6 mapping");
306 InetAddress _inet_form = InetAddresses.forString(address[0].substring(curtok, src_length));
308 Preconditions.checkArgument(_inet_form instanceof Inet4Address);
309 System.arraycopy(_inet_form.getAddress(), 0, dst, j, INADDR4SZ);
315 /* removed parser exit on invalid char - no need to do it, regexp checks it */
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);
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;
334 Preconditions.checkArgument(j == INADDR6SZ, "Overrun in v6 parsing, should not occur");
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.
343 return InetAddresses.toAddrString(InetAddress.getByAddress(_binary_form));
346 private static int nextNibble(final int mask) {
353 return 0xff << (8 - mask);
357 * Convert Ipv6Prefix object to a valid Canonical v6 prefix in byte format
359 * @param ipv6Prefix - v6 prefix object
360 * @return - byte array of size 16 + 1. Last byte contains netmask
362 public static byte[] canonicalBinaryV6Prefix(final Ipv6Prefix ipv6Prefix) {
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()
371 String [] address = null;
373 boolean valid = true;
375 address = (ipv6Prefix.getValue()).split("/");
377 mask = Integer.parseInt(address[1]);
381 } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
385 Preconditions.checkArgument(valid, "Supplied netmask in %s is invalid", ipv6Prefix.getValue());
392 /* Isn't it fun - the above variable names are the same in BSD and Sun sources */
396 char[] src = address[0].toCharArray();
398 byte[] dst = new byte[INADDR6SZ + 1];
402 int src_length = src.length;
407 /* Leading :: requires some special handling. */
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.
415 Preconditions.checkArgument(src[++i] == ':', "Invalid v6 address");
423 while (i < src_length) {
425 int chval = Character.digit(ch, 16);
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.
445 /* no need to check separator position validity - regexp does that */
450 /* removed overrun check - the regexp checks for valid data */
455 /* stop parsing if we are past the mask */
459 dst[j] = (byte) ((val >> 8) & nextNibble(m)); j++; m = m - 8;
462 /* stop parsing if we are past the mask */
466 dst[j] = (byte) (val & nextNibble(m)); j++; m = m - 8;
472 /* frankenstein - v4 attached to v6, mixed notation */
474 if (ch == '.' && ((j + INADDR4SZ) <= INADDR6SZ)) {
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
482 Preconditions.checkArgument(j != (INADDR6SZ - INADDR4SZ - 1), "Invalid v4 in v6 mapping");
484 InetAddress _inet_form = InetAddresses.forString(address[0].substring(curtok, src_length));
486 Preconditions.checkArgument(_inet_form instanceof Inet4Address);
487 System.arraycopy(_inet_form.getAddress(), 0, dst, j, INADDR4SZ);
493 /* removed parser exit on ivalid char - no need to do it, regexp checks it */
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;
501 if ((j < INADDR6SZ) && (m < 0)) {
503 for (i = j; i < INADDR6SZ; i++) {
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;
518 Preconditions.checkArgument(j == INADDR6SZ, "Overrun in v6 parsing, should not occur");
521 dst[INADDR6SZ] = (byte) mask;
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
531 public static String byteArrayV6PrefixToString(final byte [] _binary_form) throws UnknownHostException {
532 /* NO DIY!!! - InetAddresses will actually print correct canonical
533 * zero compressed form
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.
540 InetAddresses.toAddrString(
541 InetAddress.getByAddress(
542 Arrays.copyOf(_binary_form, INADDR6SZ)
547 sb.append(_binary_form[INADDR6SZ] & 0xff);
548 return sb.toString();
551 private static int ipv6PrefixByteArrayOffset(final int mask) {
556 final int ret = mask * INADDR6SZ;
557 if (ret < PREFIX_BYTEARRAYS.length) {
560 return PREFIX_BYTEARRAYS.length - INADDR6SZ;
565 * Canonicalize a v6 prefix while in binary form
567 * @param prefix - prefix, in byte [] form
568 * @param mask - mask - number of bits
570 public static void canonicalizeIpv6Prefix(final byte [] prefix, final int mask) {
571 final int offset = ipv6PrefixByteArrayOffset(mask);
573 for (int i = 0; i < INADDR6SZ; i++) {
574 prefix[i] &= PREFIX_BYTEARRAYS[offset + i];
578 public static byte[] convertIpv6PrefixToByteArray(final int prefix) {
579 final int offset = ipv6PrefixByteArrayOffset(prefix);
581 return Arrays.copyOfRange(PREFIX_BYTEARRAYS, offset, offset + INADDR6SZ);
584 public static Ipv6Address extractIpv6Address(final Ipv6Prefix ipv6Prefix) {
585 Iterator<String> addressParts = PREFIX_SPLITTER.split(ipv6Prefix.getValue()).iterator();
586 return new Ipv6Address(addressParts.next());
589 public static Integer extractIpv6Prefix(final Ipv6Prefix ipv6Prefix) {
590 Iterator<String> addressParts = PREFIX_SPLITTER.split(ipv6Prefix.getValue()).iterator();
593 Integer prefix = null;
594 if (addressParts.hasNext()) {
595 prefix = Integer.parseInt(addressParts.next());
600 public static int countBits(final byte[] mask) {
602 for (byte b : mask) {
603 netmask += Integer.bitCount(UnsignedBytes.toInt(b));