Upgrade ietf-{inet,yang}-types to 2013-07-15
[vpnservice.git] / dhcpservice / dhcpservice-impl / src / main / java / org / opendaylight / vpnservice / dhcpservice / DhcpPktHandler.java
index 6a4539f11f4ab30b5f69e3ab9f1010d9e55bef6e..565c941d2188de01dc7238dcc80dc0d4f9a727e1 100644 (file)
@@ -9,18 +9,19 @@ package org.opendaylight.vpnservice.dhcpservice;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.math.BigInteger;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
 
+import org.apache.commons.lang3.tuple.ImmutablePair;
 import org.apache.commons.net.util.SubnetUtils;
 import org.apache.commons.net.util.SubnetUtils.SubnetInfo;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.opendaylight.controller.liblldp.EtherTypes;
-import org.opendaylight.controller.liblldp.HexEncode;
 import org.opendaylight.controller.liblldp.NetUtils;
 import org.opendaylight.controller.liblldp.PacketException;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
@@ -29,42 +30,53 @@ import org.opendaylight.vpnservice.dhcpservice.api.DHCP;
 import org.opendaylight.vpnservice.dhcpservice.api.DHCPConstants;
 import org.opendaylight.vpnservice.dhcpservice.api.DHCPMConstants;
 import org.opendaylight.vpnservice.dhcpservice.api.DHCPUtils;
-import org.opendaylight.vpnservice.mdsalutil.MDSALDataStoreUtils;
+import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
+import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
+import org.opendaylight.vpnservice.mdsalutil.NwConstants;
 import org.opendaylight.vpnservice.mdsalutil.packet.Ethernet;
+import org.opendaylight.vpnservice.mdsalutil.packet.IEEE8021Q;
 import org.opendaylight.vpnservice.mdsalutil.packet.IPProtocols;
 import org.opendaylight.vpnservice.mdsalutil.packet.IPv4;
 import org.opendaylight.vpnservice.mdsalutil.packet.UDP;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+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.interfaces.rev140508.InterfacesState;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnet.attributes.HostRoutes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketInReason;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.SendToController;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetEgressActionsForInterfaceInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetEgressActionsForInterfaceOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetInterfaceFromIfIndexInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetInterfaceFromIfIndexInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetInterfaceFromIfIndexOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.OdlInterfaceRpcService;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Optional;
 
 public class DhcpPktHandler implements AutoCloseable, PacketProcessingListener {
-    
+
     private static final Logger LOG = LoggerFactory.getLogger(DhcpPktHandler.class);
     private final DataBroker dataBroker;
     private final DhcpManager dhcpMgr;
-
+    private OdlInterfaceRpcService interfaceManagerRpc;
     private boolean computeUdpChecksum = true;
     private PacketProcessingService pktService;
+    private DhcpExternalTunnelManager dhcpExternalTunnelManager;
 
-    public DhcpPktHandler(final DataBroker broker, final DhcpManager dhcpManager) {
+    public DhcpPktHandler(final DataBroker broker, final DhcpManager dhcpManager, final DhcpExternalTunnelManager dhcpExternalTunnelManager) {
+        this.dhcpExternalTunnelManager = dhcpExternalTunnelManager;
         this.dataBroker = broker;
         dhcpMgr = dhcpManager;
     }
@@ -72,49 +84,49 @@ public class DhcpPktHandler implements AutoCloseable, PacketProcessingListener {
     //TODO: Handle this in a separate thread
     @Override
     public void onPacketReceived(PacketReceived packet) {
-        LOG.trace("Pkt received: {}", packet);
         Class<? extends PacketInReason> pktInReason = packet.getPacketInReason();
-        short tableId = packet.getTableId().getValue();
-        if (isPktInReasonSendtoCtrl(pktInReason) && ((DHCPMConstants.DHCP_TABLE == tableId))) {
+        if (isPktInReasonSendtoCtrl(pktInReason)) {
             byte[] inPayload = packet.getPayload();
             Ethernet ethPkt = new Ethernet();
             try {
                 ethPkt.deserialize(inPayload, 0, inPayload.length * NetUtils.NumBitsInAByte);
             } catch (Exception e) {
-                LOG.warn("Failed to decode DHCP Packet", e);
+                LOG.warn("Failed to decode DHCP Packet {}", e);
+                LOG.trace("Received packet {}", packet);
                 return;
             }
             try {
-                DHCP pktIn = getDhcpPktIn(ethPkt);
-                LOG.trace("DHCPPkt received: {}", pktIn);
+                DHCP pktIn;
+                pktIn = getDhcpPktIn(ethPkt);
                 if (pktIn != null) {
-                    NodeConnectorRef inNcRef = packet.getIngress();
-                    FlowCapableNodeConnector fcNc = this.getFlowCapableNodeConnector(inNcRef);
-                    DHCP replyPkt = handleDhcpPacket(pktIn, fcNc);
-                    byte[] pktOut = getDhcpPacketOut(replyPkt, ethPkt, fcNc);
-                    sendPacketOut(pktOut, inNcRef);
+                    LOG.trace("DHCPPkt received: {}", pktIn);
+                    LOG.trace("Received Packet: {}", packet);
+                    BigInteger metadata = packet.getMatch().getMetadata().getMetadata();
+                    long portTag = MetaDataUtil.getLportFromMetadata(metadata).intValue();
+                    String macAddress = DHCPUtils.byteArrayToString(ethPkt.getSourceMACAddress());
+                    BigInteger tunnelId = packet.getMatch().getTunnel() == null ? null : packet.getMatch().getTunnel().getTunnelId();
+                    String interfaceName = getInterfaceNameFromTag(portTag);
+                    ImmutablePair<BigInteger, String> pair = getDpnIdPhysicalAddressFromInterfaceName(interfaceName);
+                    DHCP replyPkt = handleDhcpPacket(pktIn, interfaceName, macAddress, tunnelId);
+                    byte[] pktOut = getDhcpPacketOut(replyPkt, ethPkt, pair.getRight());
+                    sendPacketOut(pktOut, pair.getLeft(), interfaceName, tunnelId);
                 }
             } catch (Exception e) {
-                LOG.warn("Failed to get DHCP Reply", e);
+                LOG.warn("Failed to get DHCP Reply");
+                LOG.trace("Reason for failure {}", e);
             }
         }
     }
 
-    private void sendPacketOut(byte[] pktOut, NodeConnectorRef ingress) {
-        // We go out the same port we came in on
-        InstanceIdentifier<Node> egressNodePath = getNodePath(ingress.getValue());
-        TransmitPacketInput input = new TransmitPacketInputBuilder()
-            .setPayload(pktOut).setNode(new NodeRef(egressNodePath))
-            .setEgress(ingress).build();
-        LOG.trace("Transmitting packet: {}",input);
-        this.pktService.transmitPacket(input);
+    private void sendPacketOut(byte[] pktOut, BigInteger dpnId, String interfaceName, BigInteger tunnelId) {
+        LOG.trace("Sending packet out DpId {}, portId {}, vlanId {}, interfaceName {}", dpnId, interfaceName);
+        List<Action> action = getEgressAction(interfaceName, tunnelId);
+        TransmitPacketInput output = MDSALUtil.getPacketOut(action, pktOut, dpnId);
+        LOG.trace("Transmitting packet: {}",output);
+        this.pktService.transmitPacket(output);
     }
 
-    private InstanceIdentifier<Node> getNodePath(InstanceIdentifier<?> nodeInstanceId) {
-        return nodeInstanceId.firstIdentifierOf(Node.class);
-    }
-
-    private DHCP handleDhcpPacket(DHCP dhcpPkt, FlowCapableNodeConnector fcNc) {
+    private DHCP handleDhcpPacket(DHCP dhcpPkt, String interfaceName, String macAddress, BigInteger tunnelId) {
         LOG.debug("DHCP pkt rcvd {}", dhcpPkt);
         byte msgType = dhcpPkt.getMsgType();
         if (msgType == DHCPConstants.MSG_DECLINE) {
@@ -124,8 +136,12 @@ public class DhcpPktHandler implements AutoCloseable, PacketProcessingListener {
             LOG.debug("DHCPRELEASE received");
             return null;
         }
-
-        Port nPort = getNeutronPort(fcNc);
+        Port nPort;
+        if (tunnelId != null) {
+            nPort = dhcpExternalTunnelManager.readVniMacToPortCache(tunnelId, macAddress);
+        } else {
+            nPort = getNeutronPort(interfaceName);
+        }
         Subnet nSubnet = getNeutronSubnet(nPort);
         DhcpInfo dhcpInfo = getDhcpInfo(nPort, nSubnet);
         LOG.trace("NeutronPort: {} \n NeutronSubnet: {}, dhcpInfo{}",nPort, nSubnet, dhcpInfo);
@@ -149,15 +165,9 @@ public class DhcpPktHandler implements AutoCloseable, PacketProcessingListener {
             List<IpAddress> dnsServers = nSubnet.getDnsNameservers();
             dhcpInfo = new DhcpInfo();
             dhcpInfo.setClientIp(clientIp).setServerIp(serverIp)
-                .setCidr(nSubnet.getCidr()).setHostRoutes(nSubnet.getHostRoutes())
+                .setCidr(String.valueOf(nSubnet.getCidr().getValue()))
+                .setHostRoutes(nSubnet.getHostRoutes())
                 .setDnsServersIpAddrs(dnsServers).setGatewayIp(serverIp);
-        } else {
-            //FIXME: Delete this test code
-            LOG.error("TestOnly Code");
-            dhcpInfo = new DhcpInfo();
-            dhcpInfo.setClientIp("1.1.1.3").setServerIp("1.1.1.1")
-                .setCidr("1.1.1.0/24").addDnsServer("1.1.1.1");
-            LOG.warn("Failed to get Subnet info for DHCP reply");
         }
         return dhcpInfo;
     }
@@ -166,36 +176,29 @@ public class DhcpPktHandler implements AutoCloseable, PacketProcessingListener {
         return dhcpMgr.getNeutronSubnet(nPort);
     }
 
-    private Port getNeutronPort(FlowCapableNodeConnector fcNc) {
-            return dhcpMgr.getNeutronPort(fcNc.getName());
+    private Port getNeutronPort(String interfaceName) {
+            return dhcpMgr.getNeutronPort(interfaceName);
     }
 
-    private FlowCapableNodeConnector getFlowCapableNodeConnector(NodeConnectorRef inNcRef) {
-        InstanceIdentifier<NodeConnector> ncId = inNcRef.getValue().firstIdentifierOf(NodeConnector.class);
-        Optional<NodeConnector> nodeConnector = 
-                        MDSALDataStoreUtils.read(dataBroker, LogicalDatastoreType.OPERATIONAL, ncId);
-        if(nodeConnector.isPresent()) {
-            NodeConnector nc = nodeConnector.get();
-            LOG.trace("Incoming pkt's NodeConnector: {}", nc);
-            FlowCapableNodeConnector fcnc = nc.getAugmentation(FlowCapableNodeConnector.class);
-            return fcnc;
+    private DHCP getDhcpPktIn(Ethernet actualEthernetPacket) {
+        Ethernet ethPkt = actualEthernetPacket;
+        if (ethPkt.getEtherType() == (short)NwConstants.ETHTYPE_802_1Q) {
+            ethPkt = (Ethernet)ethPkt.getPayload();
         }
-        return null;
-    }
-
-    private DHCP getDhcpPktIn(Ethernet ethPkt) {
         if (ethPkt.getPayload() instanceof IPv4) {
             IPv4 ipPkt = (IPv4) ethPkt.getPayload();
             if (ipPkt.getPayload() instanceof UDP) {
                 UDP udpPkt = (UDP) ipPkt.getPayload();
                 if ((udpPkt.getSourcePort() == DHCPMConstants.dhcpClientPort)
                         && (udpPkt.getDestinationPort() == DHCPMConstants.dhcpServerPort)) {
+                    LOG.trace("Matched dhcpClientPort and dhcpServerPort");
                     byte[] rawDhcpPayload = udpPkt.getRawPayload();
                     DHCP reply = new DHCP();
                     try {
                         reply.deserialize(rawDhcpPayload, 0, rawDhcpPayload.length);
                     } catch (PacketException e) {
-                        LOG.warn("Failed to deserialize DHCP pkt", e);
+                        LOG.warn("Failed to deserialize DHCP pkt");
+                        LOG.trace("Reason for failure {}", e);
                         return null;
                     }
                     return reply;
@@ -268,7 +271,7 @@ public class DhcpPktHandler implements AutoCloseable, PacketProcessingListener {
         return reply;
     }
 
-    protected byte[] getDhcpPacketOut(DHCP reply, Ethernet etherPkt, FlowCapableNodeConnector fcNc) {
+    protected byte[] getDhcpPacketOut(DHCP reply, Ethernet etherPkt, String phyAddrees) {
         if (reply == null) {
             /*
              * DECLINE or RELEASE don't result in reply packet
@@ -313,11 +316,23 @@ public class DhcpPktHandler implements AutoCloseable, PacketProcessingListener {
         ip4Reply.setTtl((byte) 32);
         // create Ethernet Frame
         Ethernet ether = new Ethernet();
-        //TODO: 
-        ether.setSourceMACAddress(getServerMacAddress(fcNc));
+        if (etherPkt.getEtherType() == (short)NwConstants.ETHTYPE_802_1Q) {
+            IEEE8021Q vlanPacket = (IEEE8021Q) etherPkt.getPayload();
+            IEEE8021Q vlanTagged = new IEEE8021Q();
+            vlanTagged.setCFI(vlanPacket.getCfi());
+            vlanTagged.setPriority(vlanPacket.getPriority());
+            vlanTagged.setVlanId(vlanPacket.getVlanId());
+            vlanTagged.setPayload(ip4Reply);
+            vlanTagged.setEtherType(EtherTypes.IPv4.shortValue());
+            ether.setPayload(vlanTagged);
+            ether.setEtherType((short) NwConstants.ETHTYPE_802_1Q);
+        } else {
+            ether.setEtherType(EtherTypes.IPv4.shortValue());
+            ether.setPayload(ip4Reply);
+        }
+        ether.setSourceMACAddress(getServerMacAddress(phyAddrees));
         ether.setDestinationMACAddress(etherPkt.getSourceMACAddress());
-        ether.setEtherType(EtherTypes.IPv4.shortValue());
-        ether.setPayload(ip4Reply);
+
         try {
             rawPkt = ether.serialize();
         } catch (PacketException e) {
@@ -327,10 +342,9 @@ public class DhcpPktHandler implements AutoCloseable, PacketProcessingListener {
         return rawPkt;
     }
 
-    private byte[] getServerMacAddress(FlowCapableNodeConnector fcNc) {
+    private byte[] getServerMacAddress(String phyAddress) {
         // Should we return ControllerMac instead?
-        MacAddress macAddress = fcNc.getHardwareAddress();
-        return DHCPUtils.strMacAddrtoByteArray(macAddress.getValue());
+        return DHCPUtils.strMacAddrtoByteArray(phyAddress);
     }
 
     public short computeChecksum(byte[] inData, byte[] srcAddr, byte[] destAddr) {
@@ -523,4 +537,82 @@ public class DhcpPktHandler implements AutoCloseable, PacketProcessingListener {
         this.pktService = packetService;
     }
 
+    public void setInterfaceManagerRpc(OdlInterfaceRpcService interfaceManagerRpc) {
+        LOG.trace("Registered interfaceManager successfully");;
+        this.interfaceManagerRpc = interfaceManagerRpc;
+    }
+
+    private String getInterfaceNameFromTag(long portTag) {
+        String interfaceName = null;
+        GetInterfaceFromIfIndexInput input = new GetInterfaceFromIfIndexInputBuilder().setIfIndex(new Integer((int)portTag)).build();
+        Future<RpcResult<GetInterfaceFromIfIndexOutput>> futureOutput = interfaceManagerRpc.getInterfaceFromIfIndex(input);
+        try {
+            GetInterfaceFromIfIndexOutput output = futureOutput.get().getResult();
+            interfaceName = output.getInterfaceName();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Error while retrieving the interfaceName from tag using getInterfaceFromIfIndex RPC");
+        }
+        LOG.trace("Returning interfaceName {} for tag {} form getInterfaceNameFromTag", interfaceName, portTag);
+        return interfaceName;
+    }
+
+    private org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface getInterfaceStateFromOperDS(String interfaceName) {
+        InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface> ifStateId =
+                buildStateInterfaceId(interfaceName);
+        Optional<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface> ifStateOptional =
+                MDSALUtil.read(LogicalDatastoreType.OPERATIONAL, ifStateId, dataBroker);
+        if (!ifStateOptional.isPresent()) {
+            return null;
+        }
+
+        return ifStateOptional.get();
+    }
+
+    private InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface> buildStateInterfaceId(String interfaceName) {
+        InstanceIdentifierBuilder<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface> idBuilder =
+                InstanceIdentifier.builder(InterfacesState.class)
+                .child(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.class,
+                        new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey(interfaceName));
+        InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface> id = idBuilder.build();
+        return id;
+    }
+
+    private List<Action> getEgressAction(String interfaceName, BigInteger tunnelId) {
+        List<Action> actions = null;
+        try {
+            GetEgressActionsForInterfaceInputBuilder egressAction = new GetEgressActionsForInterfaceInputBuilder().setIntfName(interfaceName);
+            if (tunnelId != null) {
+                egressAction.setTunnelKey(tunnelId.longValue());
+            }
+            Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
+                    interfaceManagerRpc.getEgressActionsForInterface(egressAction.build());
+            RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
+            if(!rpcResult.isSuccessful()) {
+                LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}", interfaceName, rpcResult.getErrors());
+            } else {
+                actions = rpcResult.getResult().getAction();
+            }
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.warn("Exception when egress actions for interface {}", interfaceName, e);
+        }
+        return actions;
+    }
+
+    private ImmutablePair<BigInteger, String> getDpnIdPhysicalAddressFromInterfaceName(String interfaceName) {
+        ImmutablePair<BigInteger, String> pair = dhcpMgr.getInterfaceCache(interfaceName);
+        if (pair!=null && pair.getLeft() != null && pair.getRight() != null) {
+            return pair;
+        }
+        NodeConnectorId nodeConnectorId = null;
+        org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface interfaceState = getInterfaceStateFromOperDS(interfaceName);
+        if(interfaceState != null) {
+            List<String> ofportIds = interfaceState.getLowerLayerIf();
+            nodeConnectorId = new NodeConnectorId(ofportIds.get(0));
+        }
+        BigInteger dpId = BigInteger.valueOf(MDSALUtil.getDpnIdFromPortName(nodeConnectorId));
+        String phyAddress = interfaceState==null ? "":interfaceState.getPhysAddress().getValue();
+        pair = new ImmutablePair<BigInteger, String>(dpId, phyAddress);
+        dhcpMgr.updateInterfaceCache(interfaceName, pair);
+        return pair;
+    }
 }