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.math.BigInteger;
12 import java.net.Inet4Address;
13 import java.net.InetAddress;
14 import java.net.UnknownHostException;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.Iterator;
18 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IetfInetUtil;
19 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
20 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
21 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Address;
22 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
23 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DottedQuad;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
27 import com.google.common.base.Preconditions;
28 import com.google.common.base.Splitter;
29 import com.google.common.base.Strings;
30 import com.google.common.collect.Iterators;
31 import com.google.common.net.InetAddresses;
32 import com.google.common.primitives.UnsignedBytes;
36 * Created by Martin Bobak <mbobak@cisco.com> on 5.3.2015.
37 * v6 routines added by Anton Ivanov on 14.6.2015
38 * Arbitrary masks by sai.marapareddy@gmail.com
40 public final class IpConversionUtil {
42 private static final Logger LOG = LoggerFactory.getLogger(IpConversionUtil.class);
43 public static final String PREFIX_SEPARATOR = "/";
44 public static final Splitter PREFIX_SPLITTER = Splitter.on('/');
45 private static final int INADDR4SZ = 4;
46 private static final int INADDR6SZ = 16;
47 private static final int INT16SZ = 2;
48 private static final int IPV4_ADDRESS_LENGTH = 32;
49 private static final int IPV6_ADDRESS_LENGTH = 128;
50 private static final String DEFAULT_ARBITRARY_BIT_MASK = "255.255.255.255";
53 * Prefix bytearray lookup table. We concatenate the prefixes
54 * to a single byte array and perform offset lookups to ensure
55 * the table is contiguous and save some space.
57 private static final byte[] PREFIX_BYTEARRAYS;
59 final byte[] a = new byte[(INADDR6SZ * Byte.SIZE + 1) * INADDR6SZ];
62 for (int p = 0; p <= INADDR6SZ * Byte.SIZE; ++p) {
64 for (int i = 0; i < INADDR6SZ; ++i) {
65 a[offset++] = (byte) nextNibble(prefix);
70 PREFIX_BYTEARRAYS = a;
73 private IpConversionUtil() {
74 throw new UnsupportedOperationException("This class should not be instantiated.");
77 public static Iterator<String> splitToParts(final Ipv4Prefix ipv4Prefix) {
78 return PREFIX_SPLITTER.split(ipv4Prefix.getValue()).iterator();
81 public static Iterator<String> splitToParts(final Ipv4Address ipv4Address) {
82 /* Invalid (Ab)use of ip address as prefix!!! */
83 return Iterators.forArray(ipv4Address.getValue(), String.valueOf(IPV4_ADDRESS_LENGTH));
86 public static Iterator<String> splitToParts(final Ipv6Prefix ipv6Prefix) {
87 return PREFIX_SPLITTER.split(ipv6Prefix.getValue()).iterator();
90 public static Iterator<String> splitToParts(final Ipv6Address ipv6Address) {
91 /* Invalid (Ab)use of ip address as prefix!!! */
92 return Iterators.forArray(ipv6Address.getValue(), String.valueOf(IPV6_ADDRESS_LENGTH));
95 /* This forest of functions has a purpose:
97 * 1. There are multiple coding styles around the plugin, this is necessary in order to have
98 * one mechanism to convert them all, one mechanism to find them...
99 * 2. I hope that one day yangtools will actually deliver code fit for purpose in a packet
100 * processing application (presently it is not. When this happens, these can be optimized
101 * for "side-load" of pre-vetted data. Example. IP Address (v4 or v6) is prevetted left of the
102 * prefix. It should be loadable into Prefix without _RERUNNING_ 100ms+ of regexps. When (and if)
103 * that happens, it will be a simple fix here without chasing it across the whole plugin.
106 public static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address){
107 return IetfInetUtil.INSTANCE.ipv4PrefixFor(ipv4Address);
110 public static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address, final String mask){
112 * Ipv4Address has already validated the address part of the prefix,
113 * It is mandated to comply to the same regexp as the address
114 * There is absolutely no point rerunning additional checks vs this
115 * Note - there is no canonical form check here!!!
117 if (null != mask && !mask.isEmpty()) {
118 return new Ipv4Prefix(ipv4Address.getValue() + PREFIX_SEPARATOR + mask);
120 return new Ipv4Prefix(ipv4Address.getValue() + PREFIX_SEPARATOR + IPV4_ADDRESS_LENGTH);
124 public static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address, final int intmask){
125 return IetfInetUtil.INSTANCE.ipv4PrefixFor(ipv4Address, intmask);
128 public static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address, final byte [] bytemask){
129 return IetfInetUtil.INSTANCE.ipv4PrefixFor(ipv4Address, countBits(bytemask));
132 public static DottedQuad createArbitraryBitMask(final byte [] bytemask) {
133 DottedQuad dottedQuad = null;
134 if (bytemask == null ) {
135 dottedQuad = new DottedQuad(DEFAULT_ARBITRARY_BIT_MASK);
138 dottedQuad = new DottedQuad(InetAddress.getByAddress(bytemask).getHostAddress());
139 } catch (UnknownHostException e) {
140 LOG.error("Failed to create the dottedQuad notation for the given mask ", e);
146 public static Ipv6Prefix createPrefix(final Ipv6Address ipv6Address){
147 return IetfInetUtil.INSTANCE.ipv6PrefixFor(ipv6Address);
150 public static Ipv6Prefix createPrefix(final Ipv6Address ipv6Address, final String mask){
152 * Ipv6Address has already validated the address part of the prefix,
153 * It is mandated to comply to the same regexp as the address
154 * There is absolutely no point rerunning additional checks vs this
155 * Note - there is no canonical form check here!!!
157 if (Strings.isNullOrEmpty(mask)) {
158 return new Ipv6Prefix(ipv6Address.getValue() + PREFIX_SEPARATOR + String.valueOf(IPV6_ADDRESS_LENGTH));
160 return new Ipv6Prefix(ipv6Address.getValue() + PREFIX_SEPARATOR + mask);
164 public static Ipv6Prefix createPrefix(final Ipv6Address ipv6Address, final int intmask){
165 return IetfInetUtil.INSTANCE.ipv6PrefixFor(ipv6Address, intmask);
168 public static Ipv6Prefix createPrefix(final Ipv6Address ipv6Address, final byte [] bytemask){
169 return IetfInetUtil.INSTANCE.ipv6PrefixFor(ipv6Address, countBits(bytemask));
172 public static Integer extractPrefix(final Ipv4Prefix ipv4Prefix) {
173 return IetfInetUtil.INSTANCE.splitIpv4Prefix(ipv4Prefix).getValue();
176 public static Integer extractPrefix(final Ipv6Prefix ipv6Prefix) {
177 return IetfInetUtil.INSTANCE.splitIpv6Prefix(ipv6Prefix).getValue();
180 public static Integer extractPrefix(final Ipv4Address ipv4Prefix) {
181 return IPV4_ADDRESS_LENGTH;
184 public static Integer extractPrefix(final Ipv6Address ipv6Prefix) {
190 * Read all of the following before you touch any v6 code or decide to
191 * optimize it by invoking a "simple" Guava call
193 * Java IPv6 is fundamentally broken and Google libraries do not fix it.
194 * 1. Java will allways implicitly rewrite v4 mapped into v6 as a v4 address
195 * and there is absolutely no way to override this behaviour
196 * 2. Guava libraries cannot parse non-canonical IPv6. They will throw an
197 * exception. Even if they did, they re-use the same broken java code
200 * This is why we have to parse v6 by ourselves.
202 * The following conversion code is based on inet_cidr_pton_ipv6 in NetBSD
204 * The original BSD code is licensed under standard BSD license. While we
205 * are not obliged to provide an attribution, credit where credit is due.
206 * As far as why it is similar to Sun's sun.net.util please ask Sun why
207 * their code has the same variable names, comments and code flow.
213 * Convert Ipv6Address object to a valid Canonical v6 address in byte format
215 * @param ipv6Address - v6 Address object
216 * @return - byte array of size 16. Last byte contains netmask
220 public static byte[] canonicalBinaryV6Address(final Ipv6Address ipv6Address) {
222 * Do not modify this routine to take direct strings input!!!
223 * Key checks have been removed based on the assumption that
224 * the input is validated via regexps in Ipv6Prefix()
227 String [] address = (ipv6Address.getValue()).split("%");
233 /* Isn't it fun - the above variable names are the same in BSD and Sun sources */
237 char[] src = address[0].toCharArray();
239 byte[] dst = new byte[INADDR6SZ];
241 int src_length = src.length;
246 /* Leading :: requires some special handling. */
248 /* Isn't it fun - the above comment is again the same in BSD and Sun sources,
249 * We will derive our code from BSD. Shakespear always sounds better
250 * in original Clingon. So does Dilbert.
254 Preconditions.checkArgument(src[++i] == ':', "Invalid v6 address");
262 while (i < src_length) {
264 int chval = Character.digit(ch, 16);
266 /* Business as usual - ipv6 address digit.
267 * We can remove all checks from the original BSD code because
268 * the regexp has already verified that we are not being fed
269 * anything bigger than 0xffff between the separators.
284 /* no need to check separator position validity - regexp does that */
289 /* removed overrun check - the regexp checks for valid data */
291 dst[j++] = (byte) ((val >>> 8) & 0xff);
292 dst[j++] = (byte) (val & 0xff);
298 /* frankenstein - v4 attached to v6, mixed notation */
300 if (ch == '.' && ((j + INADDR4SZ) <= INADDR6SZ)) {
302 /* this has passed the regexp so it is fairly safe to parse it
303 * straight away. As v4 addresses do not suffer from the same
304 * defficiencies as the java v6 implementation we can invoke it
305 * straight away and be done with it
308 Preconditions.checkArgument(j != (INADDR6SZ - INADDR4SZ - 1), "Invalid v4 in v6 mapping");
310 InetAddress _inet_form = InetAddresses.forString(address[0].substring(curtok, src_length));
312 Preconditions.checkArgument(_inet_form instanceof Inet4Address);
313 System.arraycopy(_inet_form.getAddress(), 0, dst, j, INADDR4SZ);
319 /* removed parser exit on invalid char - no need to do it, regexp checks it */
322 Preconditions.checkArgument(j + INT16SZ <= INADDR6SZ, "Overrun in v6 parsing, should not occur");
323 dst[j++] = (byte) ((val >> 8) & 0xff);
324 dst[j++] = (byte) (val & 0xff);
330 Preconditions.checkArgument(j != INADDR6SZ, "Overrun in v6 parsing, should not occur");
331 for (i = 1; i <= n; i++) {
332 dst[INADDR6SZ - i] = dst[colonp + n - i];
333 dst[colonp + n - i] = 0;
338 Preconditions.checkArgument(j == INADDR6SZ, "Overrun in v6 parsing, should not occur");
343 public static String byteArrayV6AddressToString (final byte [] _binary_form) throws UnknownHostException{
344 /* DO NOT DIY!!! - InetAddresses will actually print correct canonical
345 * zero compressed form.
347 return InetAddresses.toAddrString(InetAddress.getByAddress(_binary_form));
350 private static int nextNibble(final int mask) {
357 return 0xff << (8 - mask);
361 * Convert Ipv6Prefix object to a valid Canonical v6 prefix in byte format
363 * @param ipv6Prefix - v6 prefix object
364 * @return - byte array of size 16 + 1. Last byte contains netmask
366 public static byte[] canonicalBinaryV6Prefix(final Ipv6Prefix ipv6Prefix) {
368 * Do not modify this routine to take direct strings input!!!
369 * Key checks have been removed based on the assumption that
370 * the input is validated via regexps in Ipv6Prefix()
375 String [] address = null;
377 boolean valid = true;
379 address = (ipv6Prefix.getValue()).split("/");
381 mask = Integer.parseInt(address[1]);
385 } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
389 Preconditions.checkArgument(valid, "Supplied netmask in %s is invalid", ipv6Prefix.getValue());
396 /* Isn't it fun - the above variable names are the same in BSD and Sun sources */
400 char[] src = address[0].toCharArray();
402 byte[] dst = new byte[INADDR6SZ + 1];
406 int src_length = src.length;
411 /* Leading :: requires some special handling. */
413 /* Isn't it fun - the above comment is again the same in BSD and Sun sources,
414 * We will derive our code from BSD. Shakespear always sounds better
415 * in original Clingon. So does Dilbert.
419 Preconditions.checkArgument(src[++i] == ':', "Invalid v6 address");
427 while (i < src_length) {
429 int chval = Character.digit(ch, 16);
431 /* Business as usual - ipv6 address digit.
432 * We can remove all checks from the original BSD code because
433 * the regexp has already verified that we are not being fed
434 * anything bigger than 0xffff between the separators.
449 /* no need to check separator position validity - regexp does that */
454 /* removed overrun check - the regexp checks for valid data */
459 /* stop parsing if we are past the mask */
463 dst[j] = (byte) ((val >> 8) & nextNibble(m)); j++; m = m - 8;
466 /* stop parsing if we are past the mask */
470 dst[j] = (byte) (val & nextNibble(m)); j++; m = m - 8;
476 /* frankenstein - v4 attached to v6, mixed notation */
478 if (ch == '.' && ((j + INADDR4SZ) <= INADDR6SZ)) {
480 /* this has passed the regexp so it is fairly safe to parse it
481 * straight away. As v4 addresses do not suffer from the same
482 * defficiencies as the java v6 implementation we can invoke it
483 * straight away and be done with it
486 Preconditions.checkArgument(j != (INADDR6SZ - INADDR4SZ - 1), "Invalid v4 in v6 mapping");
488 InetAddress _inet_form = InetAddresses.forString(address[0].substring(curtok, src_length));
490 Preconditions.checkArgument(_inet_form instanceof Inet4Address);
491 System.arraycopy(_inet_form.getAddress(), 0, dst, j, INADDR4SZ);
497 /* removed parser exit on ivalid char - no need to do it, regexp checks it */
500 Preconditions.checkArgument(j + INT16SZ <= INADDR6SZ, "Overrun in v6 parsing, should not occur");
501 dst[j] = (byte) ((val >> 8) & nextNibble(m)) ; j++; m = m - 8;
502 dst[j] = (byte) (val & nextNibble(m)); j++; m = m - 8;
505 if ((j < INADDR6SZ) && (m < 0)) {
507 for (i = j; i < INADDR6SZ; i++) {
515 Preconditions.checkArgument(j != INADDR6SZ, "Overrun in v6 parsing, should not occur");
516 for (i = 1; i <= n; i++) {
517 dst[INADDR6SZ - i] = dst[colonp + n - i];
518 dst[colonp + n - i] = 0;
522 Preconditions.checkArgument(j == INADDR6SZ, "Overrun in v6 parsing, should not occur");
525 dst[INADDR6SZ] = (byte) mask;
530 * Print a v6 prefix in byte array + 1 notation
531 * @param _binary_form - prefix, in byte [] form, last byte is netmask
532 * @return string of v6 prefix
533 * @throws UnknownHostException unknown host exception
535 public static String byteArrayV6PrefixToString(final byte [] _binary_form) throws UnknownHostException {
536 /* NO DIY!!! - InetAddresses will actually print correct canonical
537 * zero compressed form
539 StringBuilder sb = new StringBuilder();
540 /* Yang RFC specifies that the normalized form is RFC 5952, note - java
541 * core type is not RFC compliant, guava is.
544 InetAddresses.toAddrString(
545 InetAddress.getByAddress(
546 Arrays.copyOf(_binary_form, INADDR6SZ)
551 sb.append(_binary_form[INADDR6SZ] & 0xff);
552 return sb.toString();
555 private static int ipv6PrefixByteArrayOffset(final int mask) {
560 final int ret = mask * INADDR6SZ;
561 if (ret < PREFIX_BYTEARRAYS.length) {
564 return PREFIX_BYTEARRAYS.length - INADDR6SZ;
569 * Canonicalize a v6 prefix while in binary form
571 * @param prefix - prefix, in byte [] form
572 * @param mask - mask - number of bits
574 public static void canonicalizeIpv6Prefix(final byte [] prefix, final int mask) {
575 final int offset = ipv6PrefixByteArrayOffset(mask);
577 for (int i = 0; i < INADDR6SZ; i++) {
578 prefix[i] &= PREFIX_BYTEARRAYS[offset + i];
582 public static byte[] convertIpv6PrefixToByteArray(final int prefix) {
583 final int offset = ipv6PrefixByteArrayOffset(prefix);
585 return Arrays.copyOfRange(PREFIX_BYTEARRAYS, offset, offset + INADDR6SZ);
588 public static Ipv6Address extractIpv6Address(final Ipv6Prefix ipv6Prefix) {
589 return IetfInetUtil.INSTANCE.ipv6AddressFrom(ipv6Prefix);
592 public static Ipv4Address extractIpv4Address(final Ipv4Prefix ipv4Prefix) {
593 Iterator<String> addressParts = PREFIX_SPLITTER.split(ipv4Prefix.getValue()).iterator();
594 return new Ipv4Address(addressParts.next());
597 public static DottedQuad extractIpv4AddressMask(final Ipv4Prefix ipv4Prefix) {
598 Iterator<String> addressParts = PREFIX_SPLITTER.split(ipv4Prefix.getValue()).iterator();
601 if (addressParts.hasNext()) {
602 cidrMask = Integer.parseInt(addressParts.next());
605 maskBits = 0xffffffff << IPV4_ADDRESS_LENGTH - cidrMask;
606 String mask = String.format("%d.%d.%d.%d", (maskBits & 0x0000000000ff000000L) >> 24, (maskBits & 0x0000000000ff0000) >> 16, (maskBits & 0x0000000000ff00) >> 8, maskBits & 0xff);
607 DottedQuad netMask = new DottedQuad(mask);
611 public static Integer extractIpv6Prefix(final Ipv6Prefix ipv6Prefix) {
612 return IetfInetUtil.INSTANCE.splitIpv6Prefix(ipv6Prefix).getValue();
615 public static int countBits(final byte[] mask) {
617 for (byte b : mask) {
618 netmask += Integer.bitCount(UnsignedBytes.toInt(b));
623 public static final byte[] convertArbitraryMaskToByteArray(DottedQuad mask) {
625 if (mask != null && mask.getValue() != null) {
626 maskValue = mask.getValue();
628 maskValue = DEFAULT_ARBITRARY_BIT_MASK;
630 InetAddress maskInIpFormat = null;
632 maskInIpFormat = InetAddress.getByName(maskValue);
633 } catch (UnknownHostException e) {
634 LOG.error ("Failed to resolve the ip address of the mask",e);
636 byte[] bytes = maskInIpFormat.getAddress();
640 public static boolean isArbitraryBitMask(byte[] byteMask) {
641 if (byteMask == null) {
644 ArrayList<Integer> integerMaskArrayList = new ArrayList<Integer>();
646 // converting byte array to bits
647 maskInBits = new BigInteger(1, byteMask).toString(2);
648 ArrayList<String> stringMaskArrayList = new ArrayList<String>(Arrays.asList(maskInBits.split("(?!^)")));
649 for (String string:stringMaskArrayList) {
650 integerMaskArrayList.add(Integer.parseInt(string));
652 return checkArbitraryBitMask(integerMaskArrayList);
656 private static boolean checkArbitraryBitMask(ArrayList<Integer> arrayList) {
657 // checks 0*1* case - Leading zeros in arrayList are truncated
658 if (arrayList.size()>0 && arrayList.size()<IPV4_ADDRESS_LENGTH) {
662 for (int i=0; i<arrayList.size()-1;i++) {
663 if (arrayList.get(i) ==0 && arrayList.get(i+1) == 1) {