From: Sridhar Gaddam Date: Thu, 14 Jul 2016 11:48:13 +0000 (+0530) Subject: Support Neighbor Advertisement functionality for router iface X-Git-Tag: release/boron~253 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;ds=sidebyside;h=d446edf4dba39133f564a4aa485cc4a8c62ab9e6;hp=-c;p=netvirt.git Support Neighbor Advertisement functionality for router iface This patch supports NA for router interface (including LLA). Change-Id: I4d3d4c41dec12abef3b1d04c067a2e21930ed021 Signed-off-by: Sridhar Gaddam Signed-off-by: kalaiselvik --- d446edf4dba39133f564a4aa485cc4a8c62ab9e6 diff --git a/vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/IfMgr.java b/vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/IfMgr.java index ea4fc8e51b..27fc0bdaee 100644 --- a/vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/IfMgr.java +++ b/vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/IfMgr.java @@ -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 vrouters; private HashMap 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); } } diff --git a/vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/Ipv6PktHandler.java b/vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/Ipv6PktHandler.java index 4b46f7c9bc..7ef19d2a7f 100644 --- a/vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/Ipv6PktHandler.java +++ b/vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/Ipv6PktHandler.java @@ -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 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 prefixList; diff --git a/vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/NeutronPortChangeListener.java b/vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/NeutronPortChangeListener.java index 911039625f..3d460c1c72 100644 --- a/vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/NeutronPortChangeListener.java +++ b/vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/NeutronPortChangeListener.java @@ -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 List 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 @Override protected void update(InstanceIdentifier 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()); diff --git a/vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/VirtualPort.java b/vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/VirtualPort.java index f23bb432eb..03a0bf3f54 100644 --- a/vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/VirtualPort.java +++ b/vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/VirtualPort.java @@ -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 getIpv6Addresses() { + List 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; } diff --git a/vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/utils/Ipv6Constants.java b/vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/utils/Ipv6Constants.java index 38303d47c7..421aa5260e 100644 --- a/vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/utils/Ipv6Constants.java +++ b/vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/utils/Ipv6Constants.java @@ -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"; } diff --git a/vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/utils/Ipv6ServiceUtils.java b/vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/utils/Ipv6ServiceUtils.java index cbc22c9bfe..44530d996a 100644 --- a/vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/utils/Ipv6ServiceUtils.java +++ b/vpnservice/ipv6service/impl/src/main/java/org/opendaylight/netvirt/ipv6service/utils/Ipv6ServiceUtils.java @@ -36,9 +36,18 @@ public class Ipv6ServiceUtils { private static final Logger LOG = LoggerFactory.getLogger(Ipv6ServiceUtils.class); private ConcurrentMap> 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()); diff --git a/vpnservice/ipv6service/impl/src/test/java/org/opendaylight/netvirt/ipv6service/Ipv6PktHandlerTest.java b/vpnservice/ipv6service/impl/src/test/java/org/opendaylight/netvirt/ipv6service/Ipv6PktHandlerTest.java index 9b2e2ff3f7..561b03520a 100644 --- a/vpnservice/ipv6service/impl/src/test/java/org/opendaylight/netvirt/ipv6service/Ipv6PktHandlerTest.java +++ b/vpnservice/ipv6service/impl/src/test/java/org/opendaylight/netvirt/ipv6service/Ipv6PktHandlerTest.java @@ -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 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 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 index 0000000000..698d1e8481 --- /dev/null +++ b/vpnservice/ipv6service/impl/src/test/java/org/opendaylight/netvirt/ipv6service/utils/Ipv6ServiceUtilsTest.java @@ -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