Fixes for few NullPointerException
[netvirt.git] / dhcpservice / impl / src / main / java / org / opendaylight / netvirt / dhcpservice / DhcpPktHandler.java
index fa83285d13331272acc6b905a9afe92656c49ebb..1c4c2b007f212bc14fcf2c0cd1a18e42a24d611b 100644 (file)
@@ -25,6 +25,7 @@ import javax.inject.Singleton;
 import org.apache.commons.net.util.SubnetUtils;
 import org.apache.commons.net.util.SubnetUtils.SubnetInfo;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.genius.interfacemanager.globals.InterfaceInfo;
 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
 import org.opendaylight.genius.mdsalutil.MDSALUtil;
@@ -35,6 +36,10 @@ import org.opendaylight.genius.mdsalutil.packet.IEEE8021Q;
 import org.opendaylight.genius.mdsalutil.packet.IPProtocols;
 import org.opendaylight.genius.mdsalutil.packet.IPv4;
 import org.opendaylight.genius.mdsalutil.packet.UDP;
+import org.opendaylight.infrautils.metrics.Counter;
+import org.opendaylight.infrautils.metrics.Labeled;
+import org.opendaylight.infrautils.metrics.MetricDescriptor;
+import org.opendaylight.infrautils.metrics.MetricProvider;
 import org.opendaylight.infrautils.utils.concurrent.JdkFutures;
 import org.opendaylight.netvirt.dhcpservice.api.DHCP;
 import org.opendaylight.netvirt.dhcpservice.api.DHCPConstants;
@@ -51,6 +56,9 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpc
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp_allocation_pool.rev161214.dhcp_allocation_pool.network.AllocationPool;
 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.Port;
@@ -74,6 +82,17 @@ public class DhcpPktHandler implements PacketProcessingListener {
 
     private static final Logger LOG = LoggerFactory.getLogger(DhcpPktHandler.class);
 
+    private static final String UNKNOWN_LABEL = "unknown";
+
+    private enum PktDropReason {
+        INTERFACE_NAME_NOT_FOUND,
+        INTERFACE_INFO_NOT_FOUND,
+        SUBNET_NOT_FOUND,
+        EGRESS_ACTIONS_NOT_FOUND,
+        EXCEPTION,
+        PKT_DESERIALIZATION_ERROR
+    }
+
     private final DhcpManager dhcpMgr;
     private final OdlInterfaceRpcService interfaceManagerRpc;
     private final PacketProcessingService pktService;
@@ -82,6 +101,9 @@ public class DhcpPktHandler implements PacketProcessingListener {
     private final DhcpserviceConfig config;
     private final DhcpAllocationPoolManager dhcpAllocationPoolMgr;
     private final DataBroker broker;
+    private final ItmRpcService itmRpcService;
+    private final Labeled<Labeled<Labeled<Counter>>> pktDropCounter;
+    private final Labeled<Counter> pktInCounter;
 
     @Inject
     public DhcpPktHandler(final DhcpManager dhcpManager,
@@ -91,7 +113,9 @@ public class DhcpPktHandler implements PacketProcessingListener {
                           final IInterfaceManager interfaceManager,
                           final DhcpserviceConfig config,
                           final DhcpAllocationPoolManager dhcpAllocationPoolMgr,
-                          final DataBroker dataBroker) {
+                          final DataBroker dataBroker,
+                          final ItmRpcService itmRpcService,
+                          final MetricProvider metricProvider) {
         this.interfaceManagerRpc = interfaceManagerRpc;
         this.pktService = pktService;
         this.dhcpExternalTunnelManager = dhcpExternalTunnelManager;
@@ -100,11 +124,41 @@ public class DhcpPktHandler implements PacketProcessingListener {
         this.config = config;
         this.dhcpAllocationPoolMgr = dhcpAllocationPoolMgr;
         this.broker = dataBroker;
+        this.itmRpcService = itmRpcService;
+        this.pktDropCounter = metricProvider.newCounter(buildDhcpMetricDescriptor("packet_drop"),
+                "mac", "interface", "reason");
+        this.pktInCounter = metricProvider.newCounter(buildDhcpMetricDescriptor("packet_in"), "mac");
+    }
+
+    private MetricDescriptor buildDhcpMetricDescriptor(String id) {
+        return MetricDescriptor.builder().anchor(this).project("netvirt").module("dhcpservice").id(id).build();
     }
 
-    //TODO: Handle this in a separate thread
     @Override
+    @SuppressWarnings("checkstyle:IllegalCatch")
     public void onPacketReceived(PacketReceived packet) {
+        try {
+            onPacketReceivedInternal(packet);
+        } catch (Exception e) {
+            pktDropCounter.label(getSourceMacAddress(packet)).label(UNKNOWN_LABEL)
+                    .label(PktDropReason.EXCEPTION.name()).increment();
+            LOG.error("Failed to handle dhcp packet in ", e);
+        }
+    }
+
+    private String getSourceMacAddress(PacketReceived packet) {
+        Ethernet ethPkt = new Ethernet();
+        try {
+            byte[] inPayload = packet.getPayload();
+            ethPkt.deserialize(inPayload, 0, inPayload.length * NetUtils.NUM_BITS_IN_A_BYTE);
+            return DHCPUtils.byteArrayToString(ethPkt.getSourceMACAddress());
+        } catch (PacketException pktException) {
+            LOG.error("Failed to parse the packet {}", packet);
+        }
+        return UNKNOWN_LABEL;
+    }
+
+    public void onPacketReceivedInternal(PacketReceived packet) {
         if (!config.isControllerDhcpEnabled()) {
             return;
         }
@@ -117,6 +171,8 @@ public class DhcpPktHandler implements PacketProcessingListener {
             try {
                 ethPkt.deserialize(inPayload, 0, inPayload.length * NetUtils.NUM_BITS_IN_A_BYTE);
             } catch (PacketException e) {
+                pktDropCounter.label(UNKNOWN_LABEL).label(UNKNOWN_LABEL).label(
+                        PktDropReason.PKT_DESERIALIZATION_ERROR.name()).increment();
                 LOG.warn("Failed to decode DHCP Packet.", e);
                 LOG.trace("Received packet {}", packet);
                 return;
@@ -129,13 +185,21 @@ public class DhcpPktHandler implements PacketProcessingListener {
                 BigInteger metadata = packet.getMatch().getMetadata().getMetadata();
                 long portTag = MetaDataUtil.getLportFromMetadata(metadata).intValue();
                 String macAddress = DHCPUtils.byteArrayToString(ethPkt.getSourceMACAddress());
+                pktInCounter.label(macAddress).increment();
                 BigInteger tunnelId =
                         packet.getMatch().getTunnel() == null ? null : packet.getMatch().getTunnel().getTunnelId();
                 String interfaceName = getInterfaceNameFromTag(portTag);
+                if (interfaceName == null) {
+                    pktDropCounter.label(macAddress).label(UNKNOWN_LABEL).label(
+                            PktDropReason.INTERFACE_NAME_NOT_FOUND.name()).increment();
+                    return;
+                }
                 InterfaceInfo interfaceInfo =
                         interfaceManager.getInterfaceInfoFromOperationalDataStore(interfaceName);
                 if (interfaceInfo == null) {
                     LOG.error("Failed to get interface info for interface name {}", interfaceName);
+                    pktDropCounter.label(macAddress).label(interfaceName).label(
+                            PktDropReason.INTERFACE_INFO_NOT_FOUND.name()).increment();
                     return;
                 }
                 Port port;
@@ -160,8 +224,13 @@ public class DhcpPktHandler implements PacketProcessingListener {
                         // DHCP Neutron Port not found for this network
                         LOG.error("Neutron DHCP port is not available for the Subnet {} and port {}.", subnet.getUuid(),
                                 port.getUuid());
+                        pktDropCounter.label(macAddress).label(interfaceName).label(
+                                PktDropReason.SUBNET_NOT_FOUND.name()).increment();
                         return;
                     }
+                } else {
+                    pktDropCounter.label(macAddress).label(interfaceName).label(
+                            PktDropReason.SUBNET_NOT_FOUND.name()).increment();
                 }
                 DHCP replyPkt = handleDhcpPacket(pktIn, interfaceName, macAddress, port, subnet, serverIp);
                 if (replyPkt == null) {
@@ -169,13 +238,19 @@ public class DhcpPktHandler implements PacketProcessingListener {
                     return;
                 }
                 byte[] pktOut = getDhcpPacketOut(replyPkt, ethPkt, serverMacAddress);
-                sendPacketOut(pktOut, interfaceInfo.getDpId(), interfaceName, tunnelId);
+                sendPacketOut(pktOut, macAddress, interfaceInfo.getDpId(), interfaceName, tunnelId);
             }
         }
     }
 
-    private void sendPacketOut(byte[] pktOut, BigInteger dpnId, String interfaceName, BigInteger tunnelId) {
+    private void sendPacketOut(byte[] pktOut, String mac, BigInteger dpnId, String interfaceName,
+                               BigInteger tunnelId) {
         List<Action> action = getEgressAction(interfaceName, tunnelId);
+        if (action == null) {
+            pktDropCounter.label(mac).label(interfaceName).label(
+                    PktDropReason.EGRESS_ACTIONS_NOT_FOUND.name()).increment();
+            return;
+        }
         TransmitPacketInput output = MDSALUtil.getPacketOut(action, pktOut, dpnId);
         LOG.trace("Transmitting packet: {}", output);
         JdkFutures.addErrorLogging(pktService.transmitPacket(output), LOG, "Transmit packet");
@@ -189,7 +264,7 @@ public class DhcpPktHandler implements PacketProcessingListener {
         if (interfacePort != null) {
             dhcpInfo = handleDhcpNeutronPacket(msgType, interfacePort, subnet, serverIp);
         } else if (config.isDhcpDynamicAllocationPoolEnabled()) {
-            dhcpInfo = handleDhcpAllocationPoolPacket(msgType, dhcpPkt, interfaceName, macAddress);
+            dhcpInfo = handleDhcpAllocationPoolPacket(msgType, interfaceName, macAddress);
         }
         DHCP reply = null;
         if (dhcpInfo != null) {
@@ -215,25 +290,28 @@ public class DhcpPktHandler implements PacketProcessingListener {
     }
 
 
-    private DhcpInfo handleDhcpAllocationPoolPacket(byte msgType, DHCP dhcpPkt, String interfaceName,
-            String macAddress) {
-        String networkId = dhcpAllocationPoolMgr.getNetworkByPort(interfaceName);
-        AllocationPool pool = networkId != null ? dhcpAllocationPoolMgr.getAllocationPoolByNetwork(networkId)
-                : null;
-        if (networkId == null || pool == null) {
-            LOG.warn("No Dhcp Allocation Pool was found for interface: {}", interfaceName);
-            return null;
-        }
-        switch (msgType) {
-            case DHCPConstants.MSG_DISCOVER:
-            case DHCPConstants.MSG_REQUEST:
-                // FIXME: requested ip is currently ignored in moment of allocation
-                return getDhcpInfoFromAllocationPool(networkId, pool, macAddress);
-            case DHCPConstants.MSG_RELEASE:
-                dhcpAllocationPoolMgr.releaseIpAllocation(networkId, pool, macAddress);
-                break;
-            default:
-                break;
+    private DhcpInfo handleDhcpAllocationPoolPacket(byte msgType, String interfaceName, String macAddress) {
+        try {
+            String networkId = dhcpAllocationPoolMgr.getNetworkByPort(interfaceName);
+            AllocationPool pool = networkId != null ? dhcpAllocationPoolMgr.getAllocationPoolByNetwork(networkId)
+                    : null;
+            if (networkId == null || pool == null) {
+                LOG.warn("No Dhcp Allocation Pool was found for interface: {}", interfaceName);
+                return null;
+            }
+            switch (msgType) {
+                case DHCPConstants.MSG_DISCOVER:
+                case DHCPConstants.MSG_REQUEST:
+                    // FIXME: requested ip is currently ignored in moment of allocation
+                    return getDhcpInfoFromAllocationPool(networkId, pool, macAddress);
+                case DHCPConstants.MSG_RELEASE:
+                    dhcpAllocationPoolMgr.releaseIpAllocation(networkId, pool, macAddress);
+                    break;
+                default:
+                    break;
+            }
+        } catch (ReadFailedException e) {
+            LOG.error("Error reading from MD-SAL", e);
         }
         return null;
     }
@@ -254,35 +332,40 @@ public class DhcpPktHandler implements PacketProcessingListener {
     private DhcpInfo getDhcpInfo(Port port, Subnet subnet, String serverIp) {
         DhcpInfo dhcpInfo = null;
         if (port != null && subnet != null) {
-            String clientIp = getIpv4Address(port);
-            List<IpAddress> dnsServers = subnet.getDnsNameservers();
+            List<IpAddress> dnsServers = new ArrayList<>();
+            if (subnet.getDnsNameservers() != null && !subnet.getDnsNameservers().isEmpty()) {
+                dnsServers = subnet.getDnsNameservers();
+            }
             dhcpInfo = new DhcpInfo();
             if (isIpv4Address(subnet.getGatewayIp())) {
                 dhcpInfo.setGatewayIp(subnet.getGatewayIp().getIpv4Address().getValue());
             }
+            String clientIp = getIpv4Address(port);
             if (clientIp != null && serverIp != null) {
-                List<HostRoutes> subnetHostRoutes = new ArrayList<>(subnet.getHostRoutes().size());
-                for (HostRoutes hostRoute : subnet.getHostRoutes()) {
-                    if (!String.valueOf(hostRoute.getNexthop().getValue()).equals(clientIp)) {
-                        subnetHostRoutes.add(hostRoute);
+                List<HostRoutes> subnetHostRoutes = new ArrayList<>();
+                if (subnet.getHostRoutes() != null && !subnet.getHostRoutes().isEmpty()) {
+                    for (HostRoutes hostRoute : subnet.getHostRoutes()) {
+                        if (!hostRoute.getNexthop().stringValue().equals(clientIp)) {
+                            subnetHostRoutes.add(hostRoute);
+                        }
                     }
                 }
                 dhcpInfo.setClientIp(clientIp).setServerIp(serverIp)
-                        .setCidr(String.valueOf(subnet.getCidr().getValue())).setHostRoutes(subnetHostRoutes)
+                        .setCidr(subnet.getCidr().stringValue()).setHostRoutes(subnetHostRoutes)
                         .setDnsServersIpAddrs(dnsServers);
             }
         }
         return dhcpInfo;
     }
 
-    private DhcpInfo getApDhcpInfo(AllocationPool ap, IpAddress allocatedIp) {
+    private static DhcpInfo getApDhcpInfo(AllocationPool ap, IpAddress allocatedIp) {
         DhcpInfo dhcpInfo = null;
 
-        String clientIp = String.valueOf(allocatedIp.getValue());
-        String serverIp = String.valueOf(ap.getGateway().getValue());
+        String clientIp = allocatedIp.stringValue();
+        String serverIp = ap.getGateway().stringValue();
         List<IpAddress> dnsServers = ap.getDnsServers();
         dhcpInfo = new DhcpInfo();
-        dhcpInfo.setClientIp(clientIp).setServerIp(serverIp).setCidr(String.valueOf(ap.getSubnet().getValue()))
+        dhcpInfo.setClientIp(clientIp).setServerIp(serverIp).setCidr(ap.getSubnet().stringValue())
             .setHostRoutes(Collections.emptyList()).setDnsServersIpAddrs(dnsServers).setGatewayIp(serverIp);
 
         return dhcpInfo;
@@ -293,7 +376,7 @@ public class DhcpPktHandler implements PacketProcessingListener {
      * Many other modules use/need similar methods. Should
      * be refactored to a common NeutronUtils module.     *
      */
-    private String getIpv4Address(Port port) {
+    private static String getIpv4Address(Port port) {
 
         for (FixedIps fixedIp : port.getFixedIps()) {
             if (isIpv4Address(fixedIp.getIpAddress())) {
@@ -304,7 +387,7 @@ public class DhcpPktHandler implements PacketProcessingListener {
         return null;
     }
 
-    private boolean isIpv4Address(IpAddress ip) {
+    private static boolean isIpv4Address(IpAddress ip) {
         return ip != null && ip.getIpv4Address() != null;
     }
 
@@ -316,7 +399,7 @@ public class DhcpPktHandler implements PacketProcessingListener {
         return dhcpMgr.getNeutronPort(interfaceName);
     }
 
-    private DHCP getDhcpPktIn(Ethernet actualEthernetPacket) {
+    private static DHCP getDhcpPktIn(Ethernet actualEthernetPacket) {
         Ethernet ethPkt = actualEthernetPacket;
         if (ethPkt.getEtherType() == (short)NwConstants.ETHTYPE_802_1Q) {
             ethPkt = (Ethernet)ethPkt.getPayload();
@@ -488,7 +571,7 @@ public class DhcpPktHandler implements PacketProcessingListener {
         return rawPkt;
     }
 
-    private byte[] getServerMacAddress(String phyAddress) {
+    private static byte[] getServerMacAddress(String phyAddress) {
         // Should we return ControllerMac instead?
         return DHCPUtils.strMacAddrtoByteArray(phyAddress);
     }
@@ -700,6 +783,10 @@ public class DhcpPktHandler implements PacketProcessingListener {
         Future<RpcResult<GetInterfaceFromIfIndexOutput>> futureOutput =
                 interfaceManagerRpc.getInterfaceFromIfIndex(input);
         try {
+            if (!futureOutput.get().isSuccessful()) {
+                LOG.error("Failed to get the interface name from tag {} using getInterfaceFromIfIndex RPC", portTag);
+                return null;
+            }
             GetInterfaceFromIfIndexOutput output = futureOutput.get().getResult();
             interfaceName = output.getInterfaceName();
         } catch (InterruptedException | ExecutionException e) {
@@ -710,25 +797,38 @@ public class DhcpPktHandler implements PacketProcessingListener {
     }
 
     private List<Action> getEgressAction(String interfaceName, BigInteger tunnelId) {
-        List<Action> actions = null;
         try {
-            GetEgressActionsForInterfaceInputBuilder egressAction =
-                    new GetEgressActionsForInterfaceInputBuilder().setIntfName(interfaceName);
-            if (tunnelId != null) {
+            if (interfaceManager.isItmDirectTunnelsEnabled() && tunnelId != null) {
+                GetEgressActionsForTunnelInputBuilder egressAction =
+                        new GetEgressActionsForTunnelInputBuilder().setIntfName(interfaceName);
                 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());
+                RpcResult<GetEgressActionsForTunnelOutput> rpcResult =
+                        itmRpcService.getEgressActionsForTunnel(egressAction.build()).get();
+                if (!rpcResult.isSuccessful()) {
+                    LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
+                            interfaceName, rpcResult.getErrors());
+                } else {
+                    return rpcResult.getResult().getAction();
+                }
             } else {
-                actions = rpcResult.getResult().getAction();
+                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 {
+                    return rpcResult.getResult().getAction();
+                }
             }
         } catch (InterruptedException | ExecutionException e) {
             LOG.warn("Exception when egress actions for interface {}", interfaceName, e);
         }
-        return actions;
+        return Collections.emptyList();
     }
 }