Parse IPv4-mapped IPv6 address as an IPv6 address 84/86784/1
authorAjay Lele <ajayslele@gmail.com>
Mon, 6 Jan 2020 18:49:50 +0000 (10:49 -0800)
committerRobert Varga <robert.varga@pantheon.tech>
Tue, 7 Jan 2020 10:30:43 +0000 (11:30 +0100)
Inet6Address.getByAddress(byte[]) does not explicitly exist, and is
really InetAddress.getByAddress(byte[]). That method ends up returning
an Inet4Address for IPv4-mapped IPv6 addresses.

This in turn causes addressStringV6() to do the wrong thing and format
the address as an Ipv4. This is not caught by validation, as we are
skipping it by default -- and ends up causing problems way later when
such an address meets an enforcement point -- such as DOM->Binding
translation.

Fix this by using Inet6Address.getByAddress(String. byte[], NetworkInterface),
which is guaranteed to result in an Inet6Address.

Also add proper defences to addressStringV6(InetAddress), so that any
attempt to mis-use it is caught.

JIRA: BGPCEP-891
Signed-off-by: Ajay Lele <ajayslele@gmail.com>
Change-Id: Ife4cc9e57bd49b45f2beb73462a865ad84483d8f
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
(cherry picked from commit 1d8897caf5cec5f044f870f949a15fa57de54d71)

model/ietf/ietf-type-util/src/main/java/org/opendaylight/mdsal/model/ietf/util/AbstractIetfInetUtil.java
model/ietf/ietf-type-util/src/test/java/org/opendaylight/mdsal/model/ietf/util/AbstractIetfInetUtilTest.java

index 87c5beb893a756640cb44088592681fd111317b8..aca74d459f89f2629f3e8bb066580dd48e86dc0f 100644 (file)
@@ -478,8 +478,6 @@ public abstract class AbstractIetfInetUtil<A4, A4NZ extends A4, P4, A6, A6NZ ext
      * @throws NullPointerException if addr is null
      */
     public final @NonNull A6 ipv6AddressFor(final @NonNull InetAddress addr) {
-        requireNonNull(addr, "Address must not be null");
-        checkArgument(addr instanceof Inet6Address, "Address has to be an Inet6Address");
         return address6Factory.newInstance(addressStringV6(addr));
     }
 
@@ -504,8 +502,6 @@ public abstract class AbstractIetfInetUtil<A4, A4NZ extends A4, P4, A6, A6NZ ext
      * @throws NullPointerException if addr is null
      */
     public final @NonNull A6NZ ipv6AddressNoZoneFor(final @NonNull InetAddress addr) {
-        requireNonNull(addr, "Address must not be null");
-        checkArgument(addr instanceof Inet6Address, "Address has to be an Inet6Address");
         return address6NoZoneFactory.newInstance(addressStringV6(addr));
     }
 
@@ -563,11 +559,11 @@ public abstract class AbstractIetfInetUtil<A4, A4NZ extends A4, P4, A6, A6NZ ext
     }
 
     /**
-     * Create a /128 Ipv6Prefix by interpreting input bytes as an IPv4 address.
+     * Create a /128 Ipv6Prefix by interpreting input bytes as an IPv6 address.
      *
      * @param addr an {@link Inet6Address}
      * @return An Ipv6Prefix object
-     * @throws IllegalArgumentException if addr is not an Inet6Address or if mask is not in range 0-128
+     * @throws IllegalArgumentException if addr is not an Inet6Address
      * @throws NullPointerException if addr is null
      */
     public final @NonNull P6 ipv6PrefixFor(final @NonNull InetAddress addr) {
@@ -586,8 +582,6 @@ public abstract class AbstractIetfInetUtil<A4, A4NZ extends A4, P4, A6, A6NZ ext
      * @throws NullPointerException if addr is null
      */
     public final @NonNull P6 ipv6PrefixFor(final @NonNull InetAddress addr, final int mask) {
-        requireNonNull(addr, "Address must not be null");
-        checkArgument(addr instanceof Inet6Address, "Address has to be an Inet6Address");
         checkArgument(mask >= 0 && mask <= 128, "Invalid mask %s", mask);
         return prefix6Factory.newInstance(addressStringV6(addr) + '/' + mask);
     }
@@ -683,13 +677,19 @@ public abstract class AbstractIetfInetUtil<A4, A4NZ extends A4, P4, A6, A6NZ ext
         checkArgument(bytes.length == INET6_LENGTH, "IPv6 address length is 16 bytes");
 
         try {
-            return addressStringV6(Inet6Address.getByAddress(bytes));
+            return addressStringV6(Inet6Address.getByAddress(null, bytes, null));
         } catch (UnknownHostException e) {
             throw new IllegalArgumentException(String.format("Invalid input %s", bytes), e);
         }
     }
 
     private static String addressStringV6(final InetAddress addr) {
+        requireNonNull(addr, "Address must not be null");
+        checkArgument(addr instanceof Inet6Address, "Address has to be an Inet6Address");
+        return addressStringV6((Inet6Address) addr);
+    }
+
+    private static String addressStringV6(final Inet6Address addr) {
         return InetAddresses.toAddrString(addr);
     }
 
index dee4d2d45dcdca56153abd6777ec6027d909f35c..ab6796150c1bf2e2d31a1725a13713db36c67fe8 100644 (file)
@@ -137,6 +137,11 @@ public class AbstractIetfInetUtilTest {
         assertTrue(UTIL.splitIpv6Prefix(new IpClass("::/32")).getValue().equals(32));
         assertArrayEquals(new byte[] { 0, 10, 0, 0, 0, 0, 0, 0, 0, 11, 0, 12, 0, 13, 0, 14, 64 },
                 UTIL.ipv6PrefixToBytes(new IpClass("A::B:C:D:E/64")));
+
+        // verify that an IPv4-mapped IPv6 address gets parsed as an IPv6 address
+        assertEquals("::ffff:ab0:eb", UTIL.ipv6AddressFor(
+                new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xff, (byte) 0xff, 0x0a, (byte) 0xb0, 0, (byte) 0xeb})
+                .getValue());
     }
 
     @Test