Adds utility functions related to the link local addresses

Change-Id: I26045542d4f9d60a0d7d0905087136b995f8c03e
diff --git a/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java b/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java
index 66b5fa5..7375105 100644
--- a/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java
+++ b/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java
@@ -221,13 +221,13 @@
              * we should use the solicitation node address as IPv6 destination
              * and the multicast mac address as Ethernet destination.
              */
-            byte[] destIp = IPv6.solicitationNodeAddress(targetIp.toOctets());
+            byte[] destIp = IPv6.getSolicitNodeAddress(targetIp.toOctets());
             probePacket = NeighborSolicitation.buildNdpSolicit(
                     targetIp.toOctets(),
                     sourceIp.toOctets(),
                     destIp,
                     sourceMac.toBytes(),
-                    IPv6.multicastMacAddress(destIp),
+                    IPv6.getMCastMacAddress(destIp),
                     vlan
             );
         }
diff --git a/utils/misc/src/main/java/org/onlab/packet/IPv6.java b/utils/misc/src/main/java/org/onlab/packet/IPv6.java
index f466ca4..e88998d 100644
--- a/utils/misc/src/main/java/org/onlab/packet/IPv6.java
+++ b/utils/misc/src/main/java/org/onlab/packet/IPv6.java
@@ -30,6 +30,7 @@
 import java.util.Map;
 
 import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkArgument;
 import static org.onlab.packet.PacketUtils.checkInput;
 
 /**
@@ -48,6 +49,8 @@
     public static final byte PROTOCOL_AH = 0x33;
     public static final byte PROTOCOL_DSTOPT = 0x3C;
 
+    public static final byte LINK_LOCAL_0 = (byte) 0xfe;
+    public static final byte LINK_LOCAL_1 = (byte) 0x80;
 
     public static final Map<Byte, Deserializer<? extends IPacket>> PROTOCOL_DESERIALIZER_MAP =
             new HashMap<>();
@@ -396,8 +399,9 @@
      * @param targetIp the unicast or anycast address
      * @return the computed solicitation node address
      */
-    public static byte[] solicitationNodeAddress(byte[] targetIp) {
-        return targetIp.length != Ip6Address.BYTE_LENGTH ? null : new byte[] {
+    public static byte[] getSolicitNodeAddress(byte[] targetIp) {
+        checkArgument(targetIp.length == Ip6Address.BYTE_LENGTH);
+        return new byte[] {
                 (byte) 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                 0x00, 0x00, 0x00, 0x01, (byte) 0xff,
                 targetIp[targetIp.length - 3],
@@ -424,8 +428,9 @@
      * @param targetIp the multicast address.
      * @return the multicast mac address
      */
-    public static byte[] multicastMacAddress(byte[] targetIp) {
-        return targetIp.length != Ip6Address.BYTE_LENGTH ? null : new byte[] {
+    public static byte[] getMCastMacAddress(byte[] targetIp) {
+        checkArgument(targetIp.length == Ip6Address.BYTE_LENGTH);
+        return new byte[] {
                 0x33, 0x33,
                 targetIp[targetIp.length - 4],
                 targetIp[targetIp.length - 3],
@@ -433,4 +438,70 @@
                 targetIp[targetIp.length - 1],
         };
     }
+
+    /**
+     * According to the RFC 4291, an IPv6 link local address is an IPv6
+     * unicast address that can be automatically configured on any interface
+     * using the link-local prefix FE80::/10 (1111 1110 10) and the interface
+     * identifier in the modified EUI-64 format.
+     *
+     *    +----------------------------------------------------------------+
+     *    |  10 bits   |         54 bits         |          64 bits        |
+     *    +----------- +-------------------------+-------------------------+
+     *    | 1111111010 |           0             |       interface ID      |
+     *    +----------- +-------------------------+-------------------------+
+     *
+     * @param targetIp the ip address to verify
+     * @return true if the ipv6 address is link local,
+     * false otherwise
+     */
+    public static boolean isLinkLocalAddress(byte[] targetIp) {
+        checkArgument(targetIp.length == Ip6Address.BYTE_LENGTH);
+        return (targetIp[0] & 0xff) == 0xfe && (targetIp[1] & 0xc0) == 0x80;
+    }
+
+    /**
+     * Returns the auto-generated link local address using the
+     * mac address as parameter.
+     *
+     * @param macAddress the mac address to use
+     * @return the ipv6 link local address
+     */
+    public static byte[] getLinkLocalAddress(byte[] macAddress) {
+        checkArgument(macAddress.length == MacAddress.MAC_ADDRESS_LENGTH);
+        return new byte[] {
+                LINK_LOCAL_0,
+                LINK_LOCAL_1,
+                0, 0, 0, 0, 0, 0,
+                (byte) (macAddress[0] ^ (1 << 1)),
+                macAddress[1],
+                macAddress[2],
+                (byte) 0xff,
+                (byte) 0xfe,
+                macAddress[3],
+                macAddress[4],
+                macAddress[5],
+        };
+    }
+
+    /**
+     * Returns the mac address from the auto-generated
+     * link local address.
+     *
+     * @param linkLocalAddress the ipv6 to use
+     * @return the mac address
+     */
+    public static byte[] getMacAddress(byte[] linkLocalAddress) {
+        return !isLinkLocalAddress(linkLocalAddress) ? null : new byte[] {
+                (byte) (linkLocalAddress[8] ^ (1 << 1)),
+                linkLocalAddress[9],
+                linkLocalAddress[10],
+                linkLocalAddress[13],
+                linkLocalAddress[14],
+                linkLocalAddress[15],
+        };
+    }
+
+
+
 }
diff --git a/utils/misc/src/test/java/org/onlab/packet/IPv6Test.java b/utils/misc/src/test/java/org/onlab/packet/IPv6Test.java
index b1b555d..fef26ed 100644
--- a/utils/misc/src/test/java/org/onlab/packet/IPv6Test.java
+++ b/utils/misc/src/test/java/org/onlab/packet/IPv6Test.java
@@ -24,10 +24,10 @@
 import org.junit.Test;
 
 import java.nio.ByteBuffer;
-import java.util.Arrays;
 
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.*;
+import static org.onlab.packet.IPv6.*;
 
 /**
  * Tests for class {@link IPv6}.
@@ -48,6 +48,21 @@
     private static final byte[] MULTICAST_ADDRESS = {
             (byte) 0x33, (byte) 0x33, (byte) 0xfe, (byte) 0x54, (byte) 0x37, (byte) 0xc8
     };
+    private static final byte[] LINK_LOCAL_ADDRESS_1 = {
+            (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+            (byte) 0x50, (byte) 0x74, (byte) 0xf2, (byte) 0xff, (byte) 0xfe, (byte) 0xb1, (byte) 0xa8, (byte) 0x7f
+    };
+    private static final byte[] MAC_ADDRESS_1 = {
+            (byte) 0x52, (byte) 0x74, (byte) 0xf2, (byte) 0xb1, (byte) 0xa8, (byte) 0x7f
+    };
+    private static final byte[] LINK_LOCAL_ADDRESS_2 = {
+            (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+            (byte) 0x02, (byte) 0x0c, (byte) 0x29, (byte) 0xff, (byte) 0xfe, (byte) 0x8f, (byte) 0x99, (byte) 0xd2
+    };
+    private static final byte[] MAC_ADDRESS_2 = {
+            (byte) 0x00, (byte) 0x0c, (byte) 0x29, (byte) 0x8f, (byte) 0x99, (byte) 0xd2
+    };
+
     private static Data data;
     private static UDP udp;
     private static byte[] bytePacket;
@@ -78,7 +93,7 @@
 
     @Before
     public void setUp() {
-        deserializer = IPv6.deserializer();
+        deserializer = deserializer();
     }
 
     /**
@@ -91,7 +106,7 @@
         ipv6.setVersion((byte) 6);
         ipv6.setTrafficClass((byte) 0x93);
         ipv6.setFlowLabel(0x13579);
-        ipv6.setNextHeader(IPv6.PROTOCOL_UDP);
+        ipv6.setNextHeader(PROTOCOL_UDP);
         ipv6.setHopLimit((byte) 32);
         ipv6.setSourceAddress(SOURCE_ADDRESS);
         ipv6.setDestinationAddress(DESTINATION_ADDRESS);
@@ -107,7 +122,7 @@
     @Test
     public void testDeserializeTruncated() throws Exception {
         // Run the truncation test only on the IPv6 header
-        byte[] ipv6Header = new byte[IPv6.FIXED_HEADER_LENGTH];
+        byte[] ipv6Header = new byte[FIXED_HEADER_LENGTH];
         ByteBuffer.wrap(bytePacket).get(ipv6Header);
 
         PacketTestUtils.testDeserializeTruncated(deserializer, ipv6Header);
@@ -123,7 +138,7 @@
         assertThat(ipv6.getVersion(), is((byte) 6));
         assertThat(ipv6.getTrafficClass(), is((byte) 0x93));
         assertThat(ipv6.getFlowLabel(), is(0x13579));
-        assertThat(ipv6.getNextHeader(), is(IPv6.PROTOCOL_UDP));
+        assertThat(ipv6.getNextHeader(), is(PROTOCOL_UDP));
         assertThat(ipv6.getHopLimit(), is((byte) 32));
         assertArrayEquals(ipv6.getSourceAddress(), SOURCE_ADDRESS);
         assertArrayEquals(ipv6.getDestinationAddress(), DESTINATION_ADDRESS);
@@ -139,7 +154,7 @@
         packet1.setVersion((byte) 6);
         packet1.setTrafficClass((byte) 0x93);
         packet1.setFlowLabel(0x13579);
-        packet1.setNextHeader(IPv6.PROTOCOL_UDP);
+        packet1.setNextHeader(PROTOCOL_UDP);
         packet1.setHopLimit((byte) 32);
         packet1.setSourceAddress(SOURCE_ADDRESS);
         packet1.setDestinationAddress(DESTINATION_ADDRESS);
@@ -149,7 +164,7 @@
         packet2.setVersion((byte) 6);
         packet2.setTrafficClass((byte) 0x93);
         packet2.setFlowLabel(0x13579);
-        packet2.setNextHeader(IPv6.PROTOCOL_UDP);
+        packet2.setNextHeader(PROTOCOL_UDP);
         packet2.setHopLimit((byte) 32);
         packet2.setSourceAddress(DESTINATION_ADDRESS);
         packet2.setDestinationAddress(SOURCE_ADDRESS);
@@ -169,7 +184,7 @@
         assertTrue(StringUtils.contains(str, "version=" + (byte) 6));
         assertTrue(StringUtils.contains(str, "trafficClass=" + (byte) 0x93));
         assertTrue(StringUtils.contains(str, "flowLabel=" + 0x13579));
-        assertTrue(StringUtils.contains(str, "nextHeader=" + IPv6.PROTOCOL_UDP));
+        assertTrue(StringUtils.contains(str, "nextHeader=" + PROTOCOL_UDP));
         assertTrue(StringUtils.contains(str, "hopLimit=" + (byte) 32));
         // TODO: test IPv6 source and destination address
     }
@@ -179,7 +194,7 @@
      */
     @Test
     public void testSolicitationNodeAddress() {
-        assertTrue(Arrays.equals(SOLICITATION_NODE_ADDRESS, IPv6.solicitationNodeAddress(DESTINATION_ADDRESS)));
+        assertArrayEquals(SOLICITATION_NODE_ADDRESS, getSolicitNodeAddress(DESTINATION_ADDRESS));
     }
 
     /**
@@ -187,6 +202,36 @@
      */
     @Test
     public void testMulticastAddress() {
-        assertTrue(Arrays.equals(MULTICAST_ADDRESS, IPv6.multicastMacAddress(DESTINATION_ADDRESS)));
+        assertArrayEquals(MULTICAST_ADDRESS, getMCastMacAddress(DESTINATION_ADDRESS));
+    }
+
+    /**
+     * Tests the proper operation of the isLinkLocalAddress function.
+     */
+    @Test
+    public void testIsLinkLocalAddress() {
+        assertFalse(isLinkLocalAddress(SOURCE_ADDRESS));
+        assertFalse(isLinkLocalAddress(DESTINATION_ADDRESS));
+        assertFalse(isLinkLocalAddress(SOLICITATION_NODE_ADDRESS));
+        assertTrue(isLinkLocalAddress(LINK_LOCAL_ADDRESS_1));
+        assertTrue(isLinkLocalAddress(LINK_LOCAL_ADDRESS_2));
+    }
+
+    /**
+     * Tests the proper operation of the linkLocalAddress function.
+     */
+    @Test
+    public void testLinkLocalAddress() {
+        assertArrayEquals(getLinkLocalAddress(MAC_ADDRESS_1), LINK_LOCAL_ADDRESS_1);
+        assertArrayEquals(getLinkLocalAddress(MAC_ADDRESS_2), LINK_LOCAL_ADDRESS_2);
+    }
+
+    /**
+     * Tests the proper operation of the macAddress function.
+     */
+    @Test
+    public void testMacAddress() {
+        assertArrayEquals(getMacAddress(LINK_LOCAL_ADDRESS_1), MAC_ADDRESS_1);
+        assertArrayEquals(getMacAddress(LINK_LOCAL_ADDRESS_2), MAC_ADDRESS_2);
     }
 }