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;
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;
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;
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;
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,
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;
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;
}
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;
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;
// 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) {
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");
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) {
}
- 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;
}
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;
* 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())) {
return null;
}
- private boolean isIpv4Address(IpAddress ip) {
+ private static boolean isIpv4Address(IpAddress ip) {
return ip != null && ip.getIpv4Address() != null;
}
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();
return rawPkt;
}
- private byte[] getServerMacAddress(String phyAddress) {
+ private static byte[] getServerMacAddress(String phyAddress) {
// Should we return ControllerMac instead?
return DHCPUtils.strMacAddrtoByteArray(phyAddress);
}
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) {
}
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();
}
}