Support Neighbor Advertisement functionality for router iface 21/41821/3
authorSridhar Gaddam <sgaddam@redhat.com>
Thu, 14 Jul 2016 11:48:13 +0000 (17:18 +0530)
committerSam Hague <shague@redhat.com>
Sat, 16 Jul 2016 12:08:16 +0000 (12:08 +0000)
This patch supports NA for router interface (including LLA).

Change-Id: I4d3d4c41dec12abef3b1d04c067a2e21930ed021
Signed-off-by: Sridhar Gaddam <sgaddam@redhat.com>
Signed-off-by: kalaiselvik <Kalaiselvi_K@Dell.com>
vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/IfMgr.java
vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/Ipv6PktHandler.java
vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/NeutronPortChangeListener.java
vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/VirtualPort.java
vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/utils/Ipv6Constants.java
vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/utils/Ipv6ServiceUtils.java
vpnservice/ipv6service/impl/src/test/java/org/opendaylight/netvirt/ipv6service/Ipv6PktHandlerTest.java
vpnservice/ipv6service/impl/src/test/java/org/opendaylight/netvirt/ipv6service/utils/Ipv6ServiceUtilsTest.java [new file with mode: 0644]

index ea4fc8e51bb5fae235443f6e40ef0fd724548281..27fc0bdaee3ea2a5e50f7ba3d644d92a4c91ea91 100644 (file)
@@ -14,6 +14,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
+import org.opendaylight.netvirt.ipv6service.utils.Ipv6Constants;
 import org.opendaylight.netvirt.ipv6service.utils.Ipv6ServiceUtils;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
@@ -31,7 +32,6 @@ import org.slf4j.LoggerFactory;
 
 public class IfMgr {
     static final Logger logger = LoggerFactory.getLogger(IfMgr.class);
-    public static final String NETWORK_ROUTER_INTERFACE = "network:router_interface";
 
     private HashMap<Uuid, VirtualRouter> vrouters;
     private HashMap<Uuid, VirtualSubnet> vsubnets;
@@ -404,7 +404,7 @@ public class IfMgr {
         VirtualPort intf = vintfs.get(portId);
         if (intf != null) {
             intf.removeSelf();
-            if (intf.getDeviceOwner().equalsIgnoreCase(NETWORK_ROUTER_INTERFACE)) {
+            if (intf.getDeviceOwner().equalsIgnoreCase(Ipv6Constants.NETWORK_ROUTER_INTERFACE)) {
                 MacAddress ifaceMac = MacAddress.getDefaultInstance(intf.getMacAddress());
                 Ipv6Address llAddr = ipv6Utils.getIpv6LinkLocalAddressFromMac(ifaceMac);
                 vrouterv6IntfMap.remove(intf.getNetworkID(), intf);
@@ -412,7 +412,7 @@ public class IfMgr {
             }
             for (IpAddress ipAddr : intf.getIpAddresses()) {
                 if (ipAddr.getIpv6Address() != null) {
-                    if (intf.getDeviceOwner().equalsIgnoreCase(NETWORK_ROUTER_INTERFACE)) {
+                    if (intf.getDeviceOwner().equalsIgnoreCase(Ipv6Constants.NETWORK_ROUTER_INTERFACE)) {
                         programNSFlowForAddress(intf, ipAddr.getIpv6Address(), false);
                     }
                 }
index 4b46f7c9bcb9346db4e49f0d971812efba60f2a2..7ef19d2a7f2ca54152e5a7057387b4294f704715 100644 (file)
@@ -31,6 +31,10 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.EthernetHeader;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.Ipv6Header;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.NeighborAdvertisePacket;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.NeighborAdvertisePacketBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.NeighborSolicitationPacket;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.NeighborSolicitationPacketBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.RouterAdvertisementPacket;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.RouterAdvertisementPacketBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.RouterSolicitationPacket;
@@ -123,12 +127,176 @@ public class Ipv6PktHandler implements AutoCloseable, PacketProcessingListener {
         public void run() {
             if (type == Ipv6Constants.ICMPv6_NS_CODE) {
                 LOG.info("Received Neighbor Solicitation request");
+                processNeighborSolicitationRequest();
             } else if (type == Ipv6Constants.ICMPv6_RS_CODE) {
                 LOG.info("Received Router Solicitation request");
                 processRouterSolicitationRequest();
             }
         }
 
+        private void processNeighborSolicitationRequest() {
+            byte[] data = packet.getPayload();
+            NeighborSolicitationPacket nsPdu = deserializeNSPacket(data);
+            Ipv6Header ipv6Header = (Ipv6Header) nsPdu;
+            if (ipv6Utils.validateChecksum(data, ipv6Header, nsPdu.getIcmp6Chksum()) == false) {
+                pktProccessedCounter++;
+                LOG.warn("Received Neighbor Solicitation with invalid checksum on {}. Ignoring the packet.",
+                        packet.getIngress());
+                return;
+            }
+
+            BigInteger metadata = packet.getMatch().getMetadata().getMetadata();
+            long portTag = MetaDataUtil.getLportFromMetadata(metadata).intValue();
+            String interfaceName = ifMgr.getInterfaceNameFromTag(portTag);
+            VirtualPort port = ifMgr.obtainV6Interface(new Uuid(interfaceName));
+            if (port == null) {
+                pktProccessedCounter++;
+                LOG.warn("Port {} not found, skipping.", port);
+                return;
+            }
+
+            VirtualPort routerPort = ifMgr.getRouterV6InterfaceForNetwork(port.getNetworkID());
+            if (routerPort == null) {
+                pktProccessedCounter++;
+                LOG.warn("Port {} is not associated to a Router, skipping NS request.", routerPort);
+                return;
+            }
+
+            if (!routerPort.getIpv6Addresses().contains(nsPdu.getTargetIpAddress())) {
+                pktProccessedCounter++;
+                LOG.warn("No Router interface with address {} on the network {}, skipping NS request.",
+                        nsPdu.getTargetIpAddress(), port.getNetworkID());
+                return;
+            }
+
+            //formulate the NA response
+            NeighborAdvertisePacketBuilder naPacket = new NeighborAdvertisePacketBuilder();
+            updateNAResponse(nsPdu, routerPort, naPacket);
+            // serialize the response packet
+            byte[] txPayload = fillNeighborAdvertisementPacket(naPacket.build());
+            InstanceIdentifier<Node> outNode = packet.getIngress().getValue().firstIdentifierOf(Node.class);
+            TransmitPacketInput input = new TransmitPacketInputBuilder().setPayload(txPayload)
+                    .setNode(new NodeRef(outNode))
+                    .setEgress(packet.getIngress()).build();
+            // Tx the packet out of the controller.
+            if (pktService != null) {
+                LOG.debug("Transmitting the Neighbor Advt packet out on {}", packet.getIngress());
+                pktService.transmitPacket(input);
+                pktProccessedCounter++;
+            }
+        }
+
+        private NeighborSolicitationPacket deserializeNSPacket(byte[] data) {
+            NeighborSolicitationPacketBuilder nsPdu = new NeighborSolicitationPacketBuilder();
+            int bitOffset = 0;
+
+            try {
+                nsPdu.setDestinationMac(new MacAddress(
+                        ipv6Utils.bytesToHexString(BitBufferHelper.getBits(data, bitOffset, 48))));
+                bitOffset = bitOffset + 48;
+                nsPdu.setSourceMac(new MacAddress(
+                        ipv6Utils.bytesToHexString(BitBufferHelper.getBits(data, bitOffset, 48))));
+                bitOffset = bitOffset + 48;
+                nsPdu.setEthertype(BitBufferHelper.getInt(BitBufferHelper.getBits(data, bitOffset, 16)));
+
+                bitOffset = Ipv6Constants.IPv6_HDR_START;
+                nsPdu.setVersion(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 4)));
+                bitOffset = bitOffset + 4;
+                nsPdu.setFlowLabel(BitBufferHelper.getLong(BitBufferHelper.getBits(data, bitOffset, 28)));
+                bitOffset = bitOffset + 28;
+                nsPdu.setIpv6Length(BitBufferHelper.getInt(BitBufferHelper.getBits(data, bitOffset, 16)));
+                bitOffset = bitOffset + 16;
+                nsPdu.setNextHeader(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
+                bitOffset = bitOffset + 8;
+                nsPdu.setHopLimit(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
+                bitOffset = bitOffset + 8;
+                nsPdu.setSourceIpv6(Ipv6Address.getDefaultInstance(
+                        InetAddress.getByAddress(BitBufferHelper.getBits(data, bitOffset, 128)).getHostAddress()));
+                bitOffset = bitOffset + 128;
+                nsPdu.setDestinationIpv6(Ipv6Address.getDefaultInstance(
+                        InetAddress.getByAddress(BitBufferHelper.getBits(data, bitOffset, 128)).getHostAddress()));
+                bitOffset = bitOffset + 128;
+
+                nsPdu.setIcmp6Type(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
+                bitOffset = bitOffset + 8;
+                nsPdu.setIcmp6Code(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
+                bitOffset = bitOffset + 8;
+                nsPdu.setIcmp6Chksum(BitBufferHelper.getInt(BitBufferHelper.getBits(data, bitOffset, 16)));
+                bitOffset = bitOffset + 16;
+                nsPdu.setReserved(Long.valueOf(0));
+                bitOffset = bitOffset + 32;
+                nsPdu.setTargetIpAddress(Ipv6Address.getDefaultInstance(
+                        InetAddress.getByAddress(BitBufferHelper.getBits(data, bitOffset, 128)).getHostAddress()));
+            } catch (BufferException | UnknownHostException  e) {
+                LOG.warn("Exception obtained when deserializing NS packet", e.toString());
+            }
+            return nsPdu.build();
+        }
+
+        private void updateNAResponse(NeighborSolicitationPacket pdu,
+                                      VirtualPort port, NeighborAdvertisePacketBuilder naPacket) {
+            long flag = 0;
+            if (!pdu.getSourceIpv6().equals(ipv6Utils.UNSPECIFIED_ADDR)) {
+                naPacket.setDestinationIpv6(pdu.getSourceIpv6());
+                flag = 0xE0; // Set Router, Solicited and Override Flag.
+            } else {
+                naPacket.setDestinationIpv6(ipv6Utils.ALL_NODES_MCAST_ADDR);
+                flag = 0xA0; // Set Router and Override Flag.
+            }
+            naPacket.setDestinationMac(pdu.getSourceMac());
+            naPacket.setEthertype(pdu.getEthertype());
+            naPacket.setSourceIpv6(pdu.getTargetIpAddress());
+            naPacket.setSourceMac(new MacAddress(port.getMacAddress()));
+            naPacket.setHopLimit(Ipv6Constants.ICMPv6_MAX_HOP_LIMIT);
+            naPacket.setIcmp6Type(Ipv6Constants.ICMPv6_NA_CODE);
+            naPacket.setIcmp6Code(pdu.getIcmp6Code());
+            flag = flag << 24;
+            naPacket.setFlags(flag);
+            naPacket.setFlowLabel(pdu.getFlowLabel());
+            naPacket.setIpv6Length(32);
+            naPacket.setNextHeader(pdu.getNextHeader());
+            naPacket.setOptionType((short)2);
+            naPacket.setTargetAddrLength((short)1);
+            naPacket.setTargetAddress(pdu.getTargetIpAddress());
+            naPacket.setTargetLlAddress(new MacAddress(port.getMacAddress()));
+            naPacket.setVersion(pdu.getVersion());
+            naPacket.setIcmp6Chksum(0);
+            return;
+        }
+
+        private byte[] icmp6NAPayloadtoByte(NeighborAdvertisePacket pdu) {
+            byte[] data = new byte[36];
+            Arrays.fill(data, (byte)0);
+
+            ByteBuffer buf = ByteBuffer.wrap(data);
+            buf.put((byte)pdu.getIcmp6Type().shortValue());
+            buf.put((byte)pdu.getIcmp6Code().shortValue());
+            buf.putShort((short)pdu.getIcmp6Chksum().intValue());
+            buf.putInt((int)pdu.getFlags().longValue());
+            try {
+                byte[] address = null;
+                address = InetAddress.getByName(pdu.getTargetAddress().getValue()).getAddress();
+                buf.put(address);
+            } catch (UnknownHostException e) {
+                LOG.error("Serializing NA target address failed", e);
+            }
+            buf.put((byte)pdu.getOptionType().shortValue());
+            buf.put((byte)pdu.getTargetAddrLength().shortValue());
+            buf.put(ipv6Utils.bytesFromHexString(pdu.getTargetLlAddress().getValue().toString()));
+            return data;
+        }
+
+        private byte[] fillNeighborAdvertisementPacket(NeighborAdvertisePacket pdu) {
+            ByteBuffer buf = ByteBuffer.allocate(Ipv6Constants.ICMPV6_OFFSET + pdu.getIpv6Length());
+
+            buf.put(ipv6Utils.convertEthernetHeaderToByte((EthernetHeader)pdu), 0, 14);
+            buf.put(ipv6Utils.convertIpv6HeaderToByte((Ipv6Header)pdu), 0, 40);
+            buf.put(icmp6NAPayloadtoByte(pdu), 0, pdu.getIpv6Length());
+            int checksum = ipv6Utils.calcIcmpv6Checksum(buf.array(), (Ipv6Header) pdu);
+            buf.putShort((Ipv6Constants.ICMPV6_OFFSET + 2), (short)checksum);
+            return (buf.array());
+        }
+
         private void processRouterSolicitationRequest() {
             byte[] data = packet.getPayload();
             List<String> prefixList;
index 911039625fed7a08581e9187d8d95d023975565d..3d460c1c72e93b9d36a44b2c23c8c7d0a04aa1db 100644 (file)
@@ -14,6 +14,7 @@ import org.opendaylight.controller.md.sal.binding.api.NotificationService;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.genius.mdsalutil.AbstractDataChangeListener;
+import org.opendaylight.netvirt.ipv6service.utils.Ipv6Constants;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
@@ -59,7 +60,7 @@ public class NeutronPortChangeListener extends AbstractDataChangeListener<Port>
         List<FixedIps> ipList = port.getFixedIps();
 
         for (FixedIps fixedip : ipList) {
-            if (port.getDeviceOwner().equalsIgnoreCase(ifMgr.NETWORK_ROUTER_INTERFACE)) {
+            if (port.getDeviceOwner().equalsIgnoreCase(Ipv6Constants.NETWORK_ROUTER_INTERFACE)) {
 
                 // Add router interface
                 ifMgr.addRouterIntf(port.getUuid(),
@@ -90,7 +91,7 @@ public class NeutronPortChangeListener extends AbstractDataChangeListener<Port>
     @Override
     protected void update(InstanceIdentifier<Port> identifier, Port original, Port update) {
         LOG.info("update port notification handler is invoked...");
-        if (update.getDeviceOwner().equalsIgnoreCase(ifMgr.NETWORK_ROUTER_INTERFACE)) {
+        if (update.getDeviceOwner().equalsIgnoreCase(Ipv6Constants.NETWORK_ROUTER_INTERFACE)) {
             ifMgr.updateRouterIntf(update.getUuid(), new Uuid(update.getDeviceId()), update.getFixedIps());
         } else {
             ifMgr.updateHostIntf(update.getUuid(), update.getFixedIps());
index f23bb432ebaae4795ed24b4966157d706006788a..03a0bf3f5408f10912c29285f944b5766fb62470 100644 (file)
@@ -11,10 +11,12 @@ package org.opendaylight.netvirt.ipv6service;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
-
+import org.opendaylight.netvirt.ipv6service.utils.Ipv6Constants;
+import org.opendaylight.netvirt.ipv6service.utils.Ipv6ServiceUtils;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -108,6 +110,21 @@ public class VirtualPort  {
         return ipAddrList;
     }
 
+    public List<Ipv6Address> getIpv6Addresses() {
+        List<Ipv6Address> ipv6AddrList = new ArrayList<>();
+        for (SubnetInfo subnetInfo : snetInfo.values()) {
+            if (subnetInfo.getIpAddr().getIpv6Address() instanceof Ipv6Address) {
+                ipv6AddrList.add(subnetInfo.getIpAddr().getIpv6Address());
+            }
+        }
+        if (deviceOwner.equalsIgnoreCase(Ipv6Constants.NETWORK_ROUTER_INTERFACE)) {
+            Ipv6ServiceUtils ipv6Utils = Ipv6ServiceUtils.getInstance();
+            Ipv6Address llAddr = ipv6Utils.getIpv6LinkLocalAddressFromMac(new MacAddress(macAddress));
+            ipv6AddrList.add(llAddr);
+        }
+        return ipv6AddrList;
+    }
+
     public String getMacAddress() {
         return macAddress;
     }
index 38303d47c7881874209488a7f18328d5b3284988..421aa5260ef74bb8e091a6b4773b88847f01e0d1 100644 (file)
@@ -44,4 +44,5 @@ public class Ipv6Constants {
 
     public static final String IP_VERSION_V4 = "IPv4";
     public static final String IP_VERSION_V6 = "IPv6";
+    public static final String NETWORK_ROUTER_INTERFACE = "network:router_interface";
 }
index cbc22c9bfecf09a8ab8c5ce5135214f4e9e95fa3..44530d996a3e0e21aa477b2241c61e422a5653dd 100644 (file)
@@ -36,9 +36,18 @@ public class Ipv6ServiceUtils {
     private static final Logger LOG = LoggerFactory.getLogger(Ipv6ServiceUtils.class);
     private ConcurrentMap<String, InstanceIdentifier<Flow>> icmpv6FlowMap;
     public static final Ipv6ServiceUtils instance = new Ipv6ServiceUtils();
+    public static Ipv6Address ALL_NODES_MCAST_ADDR;
+    public static Ipv6Address UNSPECIFIED_ADDR;
 
     public Ipv6ServiceUtils() {
         icmpv6FlowMap = new ConcurrentHashMap<>();
+        try {
+            UNSPECIFIED_ADDR = Ipv6Address.getDefaultInstance(
+                    InetAddress.getByName("0:0:0:0:0:0:0:0").getHostAddress());
+            ALL_NODES_MCAST_ADDR = Ipv6Address.getDefaultInstance(InetAddress.getByName("FF02::1").getHostAddress());
+        } catch (UnknownHostException e) {
+            LOG.error("Ipv6ServiceUtils: Failed to instantiate the ipv6 address", e);
+        }
     }
 
     public static Ipv6ServiceUtils getInstance() {
@@ -246,14 +255,14 @@ public class Ipv6ServiceUtils {
         StringBuffer interfaceID = new StringBuffer();
         short u8byte = (short) (octets[0] & 0xff);
         u8byte ^= 1 << 1;
-        interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & u8byte), 2, "0"));
+        interfaceID.append(Integer.toHexString(0xFF & u8byte));
         interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[1]), 2, "0"));
         interfaceID.append(":");
-        interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[2]), 2, "0"));
+        interfaceID.append(Integer.toHexString(0xFF & octets[2]));
         interfaceID.append("ff:fe");
         interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[3]), 2, "0"));
         interfaceID.append(":");
-        interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[4]), 2, "0"));
+        interfaceID.append(Integer.toHexString(0xFF & octets[4]));
         interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[5]), 2, "0"));
 
         Ipv6Address ipv6LLA = new Ipv6Address("fe80:0:0:0:" + interfaceID.toString());
index 9b2e2ff3f762dd60708ffc974e829e97fa69bc4b..561b03520a45290cb3bb9305c1c9b89e6b9e526c 100644 (file)
@@ -18,9 +18,12 @@ import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
 import org.opendaylight.netvirt.ipv6service.utils.Ipv6Constants;
+import org.opendaylight.netvirt.ipv6service.utils.Ipv6ServiceUtils;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
@@ -115,6 +118,53 @@ public class Ipv6PktHandlerTest {
         verify(pktProcessService, times(0)).transmitPacket(any(TransmitPacketInput.class));
     }
 
+    @Test
+    public void testonPacketReceivedNeighborSolicitationWithInvalidPayload() throws Exception {
+        //incorrect checksum
+        pktHandler.onPacketReceived(new PacketReceivedBuilder().setPayload(buildPacket(
+                "33 33 FF F5 00 00",                               // Destination MAC
+                "00 01 02 03 04 05",                               // Source MAC
+                "86 DD",                                           // IPv6
+                "6E 00 00 00",                                     // Version 6, traffic class E0, no flowlabel
+                "00 18",                                           // Payload length
+                "3A",                                              // Next header is ICMPv6
+                "FF",                                              // Hop limit
+                "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // Source IP
+                "FF 02 00 00 00 00 00 00 00 00 00 01 FF F5 00 00", // Destination IP
+                "87",                                              // ICMPv6 neighbor solicitation
+                "00",                                              // Code
+                "67 3E",                                           // Checksum (invalid, should be 67 3C)
+                "00 00 00 00",                                     // ICMPv6 message body
+                "FE 80 00 00 00 00 00 00 C0 00 54 FF FE F5 00 00"  // Target
+        )).build());
+        //wait on this thread until the async job is completed in the packet handler.
+        waitForPacketProcessing();
+        verify(pktProcessService, times(0)).transmitPacket(any(TransmitPacketInput.class));
+
+        //unavailable ip
+        when(ifMgrInstance.obtainV6Interface(any())).thenReturn(null);
+        counter = pktHandler.getPacketProcessedCounter();
+        pktHandler.onPacketReceived(new PacketReceivedBuilder().setPayload(buildPacket(
+                "33 33 FF F5 00 00",                               // Destination MAC
+                "00 01 02 03 04 05",                               // Source MAC
+                "86 DD",                                           // IPv6
+                "6E 00 00 00",                                     // Version 6, traffic class E0, no flowlabel
+                "00 18",                                           // Payload length
+                "3A",                                              // Next header is ICMPv6
+                "FF",                                              // Hop limit
+                "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // Source IP
+                "FF 02 00 00 00 00 00 00 00 00 00 01 FF F5 00 00", // Destination IP
+                "87",                                              // ICMPv6 neighbor solicitation
+                "00",                                              // Code
+                "67 3C",                                           // Checksum (valid)
+                "00 00 00 00",                                     // ICMPv6 message body
+                "FE 80 00 00 00 00 00 00 C0 00 54 FF FE F5 00 00"  // Target
+        )).build());
+        //wait on this thread until the async job is completed in the packet handler.
+        waitForPacketProcessing();
+        verify(pktProcessService, times(0)).transmitPacket(any(TransmitPacketInput.class));
+    }
+
     @Test
     public void testonPacketReceivedRouterSolicitationWithInvalidPayload() throws Exception {
         // incorrect checksum in Router Solicitation
@@ -162,6 +212,75 @@ public class Ipv6PktHandlerTest {
         verify(pktProcessService, times(0)).transmitPacket(any(TransmitPacketInput.class));
     }
 
+    @Test
+    public void testonPacketReceivedNeighborSolicitationWithValidPayload() throws Exception {
+        VirtualPort intf = Mockito.mock(VirtualPort.class);
+        when(intf.getNetworkID()).thenReturn(new Uuid("eeec9dba-d831-4ad7-84b9-00d7f65f0555"));
+        when(ifMgrInstance.getInterfaceNameFromTag(anyLong())).thenReturn("ddec9dba-d831-4ad7-84b9-00d7f65f052f");
+        when(ifMgrInstance.obtainV6Interface(any())).thenReturn(intf);
+        VirtualPort routerIntf = Mockito.mock(VirtualPort.class);
+        when(ifMgrInstance.getRouterV6InterfaceForNetwork(any())).thenReturn(routerIntf);
+        List<Ipv6Address> ipv6AddrList = new ArrayList<>();
+        when(routerIntf.getMacAddress()).thenReturn("08:00:27:FE:8F:95");
+        Ipv6ServiceUtils ipv6Utils = Ipv6ServiceUtils.getInstance();
+        Ipv6Address llAddr = ipv6Utils.getIpv6LinkLocalAddressFromMac(new MacAddress("08:00:27:FE:8F:95"));
+        ipv6AddrList.add(llAddr);
+        when(routerIntf.getIpv6Addresses()).thenReturn(ipv6AddrList);
+
+        InstanceIdentifier<Node> ncId = InstanceIdentifier.builder(Nodes.class)
+                .child(Node.class, new NodeKey(new NodeId("openflow:1"))).build();
+        NodeConnectorRef ncRef = new NodeConnectorRef(ncId);
+
+        byte[] expected_payload = buildPacket(
+                "08 00 27 D4 10 BB",                               // Destination MAC
+                "08 00 27 FE 8F 95",                               // Source MAC
+                "86 DD",                                           // Ethertype - IPv6
+                "60 00 00 00",                                     // Version 6, traffic class 0, no flowlabel
+                "00 20",                                           // Payload length
+                "3A",                                              // Next header is ICMPv6
+                "FF",                                              // Hop limit
+                "FE 80 00 00 00 00 00 00 0A 00 27 FF FE FE 8F 95", // Source IP
+                "FE 80 00 00 00 00 00 00 0A 00 27 FF FE D4 10 BB", // Destination IP
+                "88",                                              // ICMPv6 neighbor advertisement.
+                "00",                                              // Code
+                "17 D6",                                           // Checksum (valid)
+                "E0 00 00 00",                                     // Flags
+                "FE 80 00 00 00 00 00 00 0A 00 27 FF FE FE 8F 95", // Target Address
+                "02",                                              // Type: Target Link-Layer Option
+                "01",                                              // Option length
+                "08 00 27 FE 8F 95"                                // Target Link layer address
+        );
+
+        BigInteger mdata = new BigInteger(String.valueOf(0x1000000));
+        Metadata metadata = new MetadataBuilder().setMetadata(mdata).build();
+        MatchBuilder matchbuilder = new MatchBuilder().setMetadata(metadata);
+        pktHandler.onPacketReceived(new PacketReceivedBuilder().setPayload(buildPacket(
+                "33 33 FF FE 8F 95",                               // Destination MAC
+                "08 00 27 D4 10 BB",                               // Source MAC
+                "86 DD",                                           // IPv6
+                "60 00 00 00",                                     // Version 6, traffic class 0, no flowlabel
+                "00 20",                                           // Payload length
+                "3A",                                              // Next header is ICMPv6
+                "FF",                                              // Hop limit
+                "FE 80 00 00 00 00 00 00 0A 00 27 FF FE D4 10 BB", // Source IP
+                "FF 02 00 00 00 00 00 00 00 00 00 01 FF FE 8F 95", // Destination IP
+                "87",                                              // ICMPv6 neighbor solicitation
+                "00",                                              // Code
+                "A9 57",                                           // Checksum (valid)
+                "00 00 00 00",                                     // ICMPv6 message body
+                "FE 80 00 00 00 00 00 00 0A 00 27 FF FE FE 8F 95", // Target
+                "01",                                              // ICMPv6 Option: Source Link Layer Address
+                "01",                                              // Length
+                "08 00 27 D4 10 BB"                                // Link Layer Address
+        )).setIngress(ncRef).setMatch(matchbuilder.build()).build());
+        //wait on this thread until the async job is completed in the packet handler.
+        waitForPacketProcessing();
+        verify(pktProcessService, times(1)).transmitPacket(any(TransmitPacketInput.class));
+        verify(pktProcessService).transmitPacket(new TransmitPacketInputBuilder().setPayload(expected_payload).
+                setNode(new NodeRef(ncId)).
+                setEgress(ncRef).build());
+    }
+
     @Test
     public void testonPacketReceivedRouterSolicitationWithSingleSubnet() throws Exception {
         VirtualPort intf = Mockito.mock(VirtualPort.class);
diff --git a/vpnservice/ipv6service/impl/src/test/java/org/opendaylight/netvirt/ipv6service/utils/Ipv6ServiceUtilsTest.java b/vpnservice/ipv6service/impl/src/test/java/org/opendaylight/netvirt/ipv6service/utils/Ipv6ServiceUtilsTest.java
new file mode 100644 (file)
index 0000000..698d1e8
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.ipv6service.utils;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
+
+/**
+ * Unit test fort {@link Ipv6ServiceUtilsTest}
+ */
+public class Ipv6ServiceUtilsTest {
+    private Ipv6ServiceUtils instance;
+
+    @Before
+    public void initTest() {
+         instance = Ipv6ServiceUtils.getInstance();
+    }
+
+    /**
+     *  Test getIpv6LinkLocalAddressFromMac with different MACAddress values.
+     */
+    @Test
+    public void testgetIpv6LinkLocalAddressFromMac() {
+        MacAddress mac = new MacAddress("fa:16:3e:4e:18:0c");
+        Ipv6Address expectedLinkLocalAddress = new Ipv6Address("fe80:0:0:0:f816:3eff:fe4e:180c");
+        assertEquals(expectedLinkLocalAddress, instance.getIpv6LinkLocalAddressFromMac(mac));
+
+        mac = new MacAddress("fa:16:3e:4e:18:c0");
+        expectedLinkLocalAddress = new Ipv6Address("fe80:0:0:0:f816:3eff:fe4e:18c0");
+        assertEquals(expectedLinkLocalAddress, instance.getIpv6LinkLocalAddressFromMac(mac));
+
+        mac = new MacAddress("0a:16:03:04:08:0c");
+        expectedLinkLocalAddress = new Ipv6Address("fe80:0:0:0:816:3ff:fe04:80c");
+        assertEquals(expectedLinkLocalAddress, instance.getIpv6LinkLocalAddressFromMac(mac));
+
+        mac = new MacAddress("50:7B:9D:78:54:F3");
+        expectedLinkLocalAddress = new Ipv6Address("fe80:0:0:0:527b:9dff:fe78:54f3");
+        assertEquals(expectedLinkLocalAddress, instance.getIpv6LinkLocalAddressFromMac(mac));
+    }
+}
\ No newline at end of file