2 * Copyright © 2015, 2018 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netvirt.dhcpservice;
10 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
11 import java.io.ByteArrayOutputStream;
12 import java.io.IOException;
13 import java.net.InetAddress;
14 import java.net.UnknownHostException;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.Collections;
18 import java.util.List;
19 import java.util.concurrent.ExecutionException;
20 import java.util.concurrent.Future;
21 import javax.inject.Inject;
22 import javax.inject.Singleton;
23 import org.apache.commons.net.util.SubnetUtils;
24 import org.apache.commons.net.util.SubnetUtils.SubnetInfo;
25 import org.eclipse.jdt.annotation.NonNull;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
28 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
29 import org.opendaylight.genius.interfacemanager.globals.InterfaceInfo;
30 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
31 import org.opendaylight.genius.mdsalutil.MDSALUtil;
32 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
33 import org.opendaylight.genius.mdsalutil.NwConstants;
34 import org.opendaylight.genius.mdsalutil.packet.Ethernet;
35 import org.opendaylight.genius.mdsalutil.packet.IEEE8021Q;
36 import org.opendaylight.genius.mdsalutil.packet.IPProtocols;
37 import org.opendaylight.genius.mdsalutil.packet.IPv4;
38 import org.opendaylight.genius.mdsalutil.packet.UDP;
39 import org.opendaylight.infrautils.metrics.Counter;
40 import org.opendaylight.infrautils.metrics.Labeled;
41 import org.opendaylight.infrautils.metrics.MetricDescriptor;
42 import org.opendaylight.infrautils.metrics.MetricProvider;
43 import org.opendaylight.infrautils.utils.concurrent.JdkFutures;
44 import org.opendaylight.netvirt.dhcpservice.api.DHCP;
45 import org.opendaylight.netvirt.dhcpservice.api.DHCPConstants;
46 import org.opendaylight.netvirt.dhcpservice.api.DHCPUtils;
47 import org.opendaylight.netvirt.dhcpservice.api.DhcpMConstants;
48 import org.opendaylight.openflowplugin.libraries.liblldp.EtherTypes;
49 import org.opendaylight.openflowplugin.libraries.liblldp.NetUtils;
50 import org.opendaylight.openflowplugin.libraries.liblldp.PacketException;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceInputBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceOutput;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInput;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInputBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexOutput;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelInputBuilder;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelOutput;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp_allocation_pool.rev161214.dhcp_allocation_pool.network.AllocationPool;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnet.attributes.HostRoutes;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketInReason;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.SendToController;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.api.rev150710.subnet.dhcp.port.data.SubnetToDhcpPort;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.config.rev150710.DhcpserviceConfig;
75 import org.opendaylight.yangtools.yang.common.RpcResult;
76 import org.opendaylight.yangtools.yang.common.Uint64;
77 import org.slf4j.Logger;
78 import org.slf4j.LoggerFactory;
82 public class DhcpPktHandler implements PacketProcessingListener {
84 private static final Logger LOG = LoggerFactory.getLogger(DhcpPktHandler.class);
86 private static final String UNKNOWN_LABEL = "unknown";
88 private enum PktDropReason {
89 INTERFACE_NAME_NOT_FOUND,
90 INTERFACE_INFO_NOT_FOUND,
92 EGRESS_ACTIONS_NOT_FOUND,
94 PKT_DESERIALIZATION_ERROR
97 private final DhcpManager dhcpMgr;
98 private final OdlInterfaceRpcService interfaceManagerRpc;
99 private final PacketProcessingService pktService;
100 private final DhcpExternalTunnelManager dhcpExternalTunnelManager;
101 private final IInterfaceManager interfaceManager;
102 private final DhcpserviceConfig config;
103 private final DhcpAllocationPoolManager dhcpAllocationPoolMgr;
104 private final DataBroker broker;
105 private final ItmRpcService itmRpcService;
106 private final Labeled<Labeled<Labeled<Counter>>> pktDropCounter;
107 private final Labeled<Counter> pktInCounter;
110 public DhcpPktHandler(final DhcpManager dhcpManager,
111 final DhcpExternalTunnelManager dhcpExternalTunnelManager,
112 final OdlInterfaceRpcService interfaceManagerRpc,
113 final PacketProcessingService pktService,
114 final IInterfaceManager interfaceManager,
115 final DhcpserviceConfig config,
116 final DhcpAllocationPoolManager dhcpAllocationPoolMgr,
117 final DataBroker dataBroker,
118 final ItmRpcService itmRpcService,
119 final MetricProvider metricProvider) {
120 this.interfaceManagerRpc = interfaceManagerRpc;
121 this.pktService = pktService;
122 this.dhcpExternalTunnelManager = dhcpExternalTunnelManager;
123 this.dhcpMgr = dhcpManager;
124 this.interfaceManager = interfaceManager;
125 this.config = config;
126 this.dhcpAllocationPoolMgr = dhcpAllocationPoolMgr;
127 this.broker = dataBroker;
128 this.itmRpcService = itmRpcService;
129 this.pktDropCounter = metricProvider.newCounter(buildDhcpMetricDescriptor("packet_drop"),
130 "mac", "interface", "reason");
131 this.pktInCounter = metricProvider.newCounter(buildDhcpMetricDescriptor("packet_in"), "mac");
134 private MetricDescriptor buildDhcpMetricDescriptor(String id) {
135 return MetricDescriptor.builder().anchor(this).project("netvirt").module("dhcpservice").id(id).build();
139 @SuppressWarnings("checkstyle:IllegalCatch")
140 public void onPacketReceived(PacketReceived packet) {
142 onPacketReceivedInternal(packet);
143 } catch (Exception e) {
144 pktDropCounter.label(getSourceMacAddress(packet)).label(UNKNOWN_LABEL)
145 .label(PktDropReason.EXCEPTION.name()).increment();
146 LOG.error("Failed to handle dhcp packet in ", e);
150 private String getSourceMacAddress(PacketReceived packet) {
151 Ethernet ethPkt = new Ethernet();
153 byte[] inPayload = packet.getPayload();
154 ethPkt.deserialize(inPayload, 0, inPayload.length * Byte.SIZE);
155 return DHCPUtils.byteArrayToString(ethPkt.getSourceMACAddress());
156 } catch (PacketException pktException) {
157 LOG.error("Failed to parse the packet {}", packet);
159 return UNKNOWN_LABEL;
162 public void onPacketReceivedInternal(PacketReceived packet) {
163 if (!config.isControllerDhcpEnabled()) {
166 Class<? extends PacketInReason> pktInReason = packet.getPacketInReason();
167 short tableId = packet.getTableId().getValue().toJava();
168 if ((tableId == NwConstants.DHCP_TABLE || tableId == NwConstants.DHCP_TABLE_EXTERNAL_TUNNEL)
169 && isPktInReasonSendtoCtrl(pktInReason)) {
170 byte[] inPayload = packet.getPayload();
171 Ethernet ethPkt = new Ethernet();
173 ethPkt.deserialize(inPayload, 0, inPayload.length * Byte.SIZE);
174 } catch (PacketException e) {
175 pktDropCounter.label(UNKNOWN_LABEL).label(UNKNOWN_LABEL).label(
176 PktDropReason.PKT_DESERIALIZATION_ERROR.name()).increment();
177 LOG.warn("Failed to decode DHCP Packet.", e);
178 LOG.trace("Received packet {}", packet);
182 pktIn = getDhcpPktIn(ethPkt);
184 LOG.trace("DHCPPkt received: {}", pktIn);
185 LOG.trace("Received Packet: {}", packet);
186 Uint64 metadata = packet.getMatch().getMetadata().getMetadata();
187 long portTag = MetaDataUtil.getLportFromMetadata(metadata).intValue();
188 String macAddress = DHCPUtils.byteArrayToString(ethPkt.getSourceMACAddress());
189 pktInCounter.label(macAddress).increment();
191 packet.getMatch().getTunnel() == null ? null
192 : packet.getMatch().getTunnel().getTunnelId();
193 String interfaceName = getInterfaceNameFromTag(portTag);
194 if (interfaceName == null) {
195 pktDropCounter.label(macAddress).label(UNKNOWN_LABEL).label(
196 PktDropReason.INTERFACE_NAME_NOT_FOUND.name()).increment();
199 InterfaceInfo interfaceInfo =
200 interfaceManager.getInterfaceInfoFromOperationalDataStore(interfaceName);
201 if (interfaceInfo == null) {
202 LOG.error("Failed to get interface info for interface name {}", interfaceName);
203 pktDropCounter.label(macAddress).label(interfaceName).label(
204 PktDropReason.INTERFACE_INFO_NOT_FOUND.name()).increment();
208 if (tunnelId != null) {
209 port = dhcpExternalTunnelManager.readVniMacToPortCache(tunnelId, macAddress);
211 port = getNeutronPort(interfaceName);
213 Subnet subnet = getNeutronSubnet(port);
214 String serverMacAddress = interfaceInfo.getMacAddress();
215 String serverIp = null;
216 if (subnet != null) {
217 java.util.Optional<SubnetToDhcpPort> dhcpPortData = DhcpServiceUtils
218 .getSubnetDhcpPortData(broker, subnet.getUuid().getValue());
219 /* If enable_dhcp_service flag was enabled and an ODL network DHCP Port data was made available use
220 * the ports Fixed IP as server IP for DHCP communication.
222 if (dhcpPortData.isPresent()) {
223 serverIp = dhcpPortData.get().getPortFixedip();
224 serverMacAddress = dhcpPortData.get().getPortMacaddress();
226 // DHCP Neutron Port not found for this network
227 LOG.error("Neutron DHCP port is not available for the Subnet {} and port {}.", subnet.getUuid(),
229 pktDropCounter.label(macAddress).label(interfaceName).label(
230 PktDropReason.SUBNET_NOT_FOUND.name()).increment();
234 pktDropCounter.label(macAddress).label(interfaceName).label(
235 PktDropReason.SUBNET_NOT_FOUND.name()).increment();
237 DHCP replyPkt = handleDhcpPacket(pktIn, interfaceName, macAddress, port, subnet, serverIp);
238 if (replyPkt == null) {
239 LOG.warn("Unable to construct reply packet for interface name {}", interfaceName);
242 byte[] pktOut = getDhcpPacketOut(replyPkt, ethPkt, serverMacAddress);
243 sendPacketOut(pktOut, macAddress, interfaceInfo.getDpId(), interfaceName, tunnelId);
248 private void sendPacketOut(byte[] pktOut, String mac, Uint64 dpnId, String interfaceName,
250 List<Action> action = getEgressAction(interfaceName, tunnelId);
251 if (action == null) {
252 pktDropCounter.label(mac).label(interfaceName).label(
253 PktDropReason.EGRESS_ACTIONS_NOT_FOUND.name()).increment();
256 TransmitPacketInput output = MDSALUtil.getPacketOut(action, pktOut, dpnId);
257 LOG.trace("Transmitting packet: {}", output);
258 JdkFutures.addErrorLogging(pktService.transmitPacket(output), LOG, "Transmit packet");
262 private DHCP handleDhcpPacket(DHCP dhcpPkt, String interfaceName, String macAddress, Port interfacePort,
263 Subnet subnet, String serverIp) {
264 LOG.trace("DHCP pkt rcvd {}", dhcpPkt);
265 byte msgType = dhcpPkt.getMsgType();
266 DhcpInfo dhcpInfo = null;
267 if (interfacePort != null) {
268 dhcpInfo = handleDhcpNeutronPacket(msgType, interfacePort, subnet, serverIp);
269 } else if (config.isDhcpDynamicAllocationPoolEnabled()) {
270 dhcpInfo = handleDhcpAllocationPoolPacket(msgType, interfaceName, macAddress);
273 if (dhcpInfo != null) {
274 if (msgType == DHCPConstants.MSG_DISCOVER) {
275 reply = getReplyToDiscover(dhcpPkt, dhcpInfo);
276 } else if (msgType == DHCPConstants.MSG_REQUEST) {
277 reply = getReplyToRequest(dhcpPkt, dhcpInfo);
285 private DhcpInfo handleDhcpNeutronPacket(byte msgType, Port port, Subnet subnet, String serverIp) {
286 if (msgType == DHCPConstants.MSG_DECLINE) {
287 LOG.trace("DHCPDECLINE received");
289 } else if (msgType == DHCPConstants.MSG_RELEASE) {
290 LOG.trace("DHCPRELEASE received");
293 return getDhcpInfoFromNeutronPort(port, subnet, serverIp);
298 private DhcpInfo handleDhcpAllocationPoolPacket(byte msgType, String interfaceName, String macAddress) {
300 String networkId = dhcpAllocationPoolMgr.getNetworkByPort(interfaceName);
301 AllocationPool pool = networkId != null ? dhcpAllocationPoolMgr.getAllocationPoolByNetwork(networkId)
303 if (networkId == null || pool == null) {
304 LOG.warn("No Dhcp Allocation Pool was found for interface: {}", interfaceName);
308 case DHCPConstants.MSG_DISCOVER:
309 case DHCPConstants.MSG_REQUEST:
310 // FIXME: requested ip is currently ignored in moment of allocation
311 return getDhcpInfoFromAllocationPool(networkId, pool, macAddress);
312 case DHCPConstants.MSG_RELEASE:
313 dhcpAllocationPoolMgr.releaseIpAllocation(networkId, pool, macAddress);
318 } catch (ReadFailedException e) {
319 LOG.error("Error reading from MD-SAL", e);
324 private DhcpInfo getDhcpInfoFromNeutronPort(Port port, Subnet subnet, String serverIp) {
325 DhcpInfo dhcpInfo = getDhcpInfo(port, subnet, serverIp);
326 LOG.trace("NeutronPort: {} \n NeutronSubnet: {}, dhcpInfo{}", port, subnet, dhcpInfo);
330 private DhcpInfo getDhcpInfoFromAllocationPool(String networkId, AllocationPool pool, String macAddress) {
331 IpAddress allocatedIp = dhcpAllocationPoolMgr.getIpAllocation(networkId, pool, macAddress);
332 DhcpInfo dhcpInfo = getApDhcpInfo(pool, allocatedIp);
333 LOG.info("AllocationPoolNetwork: {}, dhcpInfo {}", networkId, dhcpInfo);
338 private DhcpInfo getDhcpInfo(Port port, Subnet subnet, String serverIp) {
339 DhcpInfo dhcpInfo = null;
340 if (port != null && subnet != null) {
341 List<IpAddress> dnsServers = new ArrayList<>();
342 if (subnet.getDnsNameservers() != null && !subnet.getDnsNameservers().isEmpty()) {
343 dnsServers = subnet.getDnsNameservers();
345 dhcpInfo = new DhcpInfo();
346 if (isIpv4Address(subnet.getGatewayIp())) {
347 dhcpInfo.setGatewayIp(subnet.getGatewayIp().getIpv4Address().getValue());
349 String clientIp = getIpv4Address(port);
350 if (clientIp != null && serverIp != null) {
351 List<HostRoutes> subnetHostRoutes = new ArrayList<>();
352 if (subnet.getHostRoutes() != null && !subnet.getHostRoutes().isEmpty()) {
353 for (HostRoutes hostRoute : subnet.getHostRoutes()) {
354 if (!hostRoute.getNexthop().stringValue().equals(clientIp)) {
355 subnetHostRoutes.add(hostRoute);
359 dhcpInfo.setClientIp(clientIp).setServerIp(serverIp)
360 .setCidr(subnet.getCidr().stringValue()).setHostRoutes(subnetHostRoutes)
361 .setDnsServersIpAddrs(dnsServers);
368 private static DhcpInfo getApDhcpInfo(AllocationPool ap, IpAddress allocatedIp) {
369 String clientIp = allocatedIp.stringValue();
370 String serverIp = ap.getGateway().stringValue();
371 List<IpAddress> dnsServers = ap.getDnsServers();
372 DhcpInfo dhcpInfo = new DhcpInfo();
373 dhcpInfo.setClientIp(clientIp).setServerIp(serverIp).setCidr(ap.getSubnet().stringValue())
374 .setHostRoutes(Collections.emptyList()).setDnsServersIpAddrs(dnsServers).setGatewayIp(serverIp);
380 * getIpv4Address and isIpv4Address
381 * Many other modules use/need similar methods. Should
382 * be refactored to a common NeutronUtils module. *
385 private static String getIpv4Address(Port port) {
387 for (FixedIps fixedIp : port.nonnullFixedIps()) {
388 if (isIpv4Address(fixedIp.getIpAddress())) {
389 return fixedIp.getIpAddress().getIpv4Address().getValue();
392 LOG.error("Could not find ipv4 address for port {}", port);
396 private static boolean isIpv4Address(@Nullable IpAddress ip) {
397 return ip != null && ip.getIpv4Address() != null;
401 private Subnet getNeutronSubnet(Port port) {
402 return dhcpMgr.getNeutronSubnet(port);
406 private Port getNeutronPort(String interfaceName) {
407 return dhcpMgr.getNeutronPort(interfaceName);
411 private static DHCP getDhcpPktIn(Ethernet actualEthernetPacket) {
412 Ethernet ethPkt = actualEthernetPacket;
413 if (ethPkt.getEtherType() == (short)NwConstants.ETHTYPE_802_1Q) {
414 ethPkt = (Ethernet)ethPkt.getPayload();
416 // Currently only IPv4 is supported
417 if (ethPkt.getPayload() instanceof IPv4) {
418 IPv4 ipPkt = (IPv4) ethPkt.getPayload();
419 if (ipPkt.getPayload() instanceof UDP) {
420 UDP udpPkt = (UDP) ipPkt.getPayload();
421 if (udpPkt.getSourcePort() == DhcpMConstants.DHCP_CLIENT_PORT
422 && udpPkt.getDestinationPort() == DhcpMConstants.DHCP_SERVER_PORT) {
423 LOG.trace("Matched DHCP_CLIENT_PORT and DHCP_SERVER_PORT");
424 byte[] rawDhcpPayload = udpPkt.getRawPayload();
425 DHCP reply = new DHCP();
427 reply.deserialize(rawDhcpPayload, 0, rawDhcpPayload.length);
428 } catch (PacketException e) {
429 LOG.warn("Failed to deserialize DHCP pkt");
430 LOG.trace("Reason for failure", e);
440 DHCP getReplyToDiscover(DHCP dhcpPkt, DhcpInfo dhcpInfo) {
441 DHCP reply = new DHCP();
442 reply.setOp(DHCPConstants.BOOTREPLY);
443 reply.setHtype(dhcpPkt.getHtype());
444 reply.setHlen(dhcpPkt.getHlen());
445 reply.setHops((byte) 0);
446 reply.setXid(dhcpPkt.getXid());
447 reply.setSecs((short) 0);
449 reply.setYiaddr(dhcpInfo.getClientIp());
450 reply.setSiaddr(dhcpInfo.getServerIp());
452 reply.setFlags(dhcpPkt.getFlags());
453 reply.setGiaddr(dhcpPkt.getGiaddr());
454 reply.setChaddr(dhcpPkt.getChaddr());
456 reply.setMsgType(DHCPConstants.MSG_OFFER);
457 if (dhcpPkt.containsOption(DHCPConstants.OPT_PARAMETER_REQUEST_LIST)) {
458 setParameterListOptions(dhcpPkt, reply, dhcpInfo);
460 setCommonOptions(reply, dhcpInfo);
464 DHCP getReplyToRequest(DHCP dhcpPkt, DhcpInfo dhcpInfo) {
465 boolean sendAck = false;
466 byte[] requestedIp = null;
467 DHCP reply = new DHCP();
468 reply.setOp(DHCPConstants.BOOTREPLY);
469 reply.setHtype(dhcpPkt.getHtype());
470 reply.setHlen(dhcpPkt.getHlen());
471 reply.setHops((byte) 0);
472 reply.setXid(dhcpPkt.getXid());
473 reply.setSecs((short) 0);
475 reply.setFlags(dhcpPkt.getFlags());
476 reply.setGiaddr(dhcpPkt.getGiaddr());
477 reply.setChaddr(dhcpPkt.getChaddr());
480 allocatedIp = DHCPUtils.strAddrToByteArray(dhcpInfo.getClientIp());
481 } catch (UnknownHostException e) {
482 LOG.debug("strAddrToByteArray", e);
486 if (Arrays.equals(allocatedIp, dhcpPkt.getCiaddr())) {
487 //This means a renew request
490 requestedIp = dhcpPkt.getOptionBytes(DHCPConstants.OPT_REQUESTED_ADDRESS);
491 sendAck = Arrays.equals(allocatedIp, requestedIp);
495 reply.setCiaddr(dhcpPkt.getCiaddr());
496 reply.setYiaddr(dhcpInfo.getClientIp());
497 reply.setSiaddr(dhcpInfo.getServerIp());
498 reply.setMsgType(DHCPConstants.MSG_ACK);
499 if (dhcpPkt.containsOption(DHCPConstants.OPT_PARAMETER_REQUEST_LIST)) {
500 setParameterListOptions(dhcpPkt, reply, dhcpInfo);
503 reply.setMsgType(DHCPConstants.MSG_NAK);
505 setCommonOptions(reply, dhcpInfo);
509 // "Consider returning a zero length array rather than null" - the eventual user of the returned byte[] likely
510 // expects null and it's unclear what the behavior would be if empty array was returned.
511 @SuppressFBWarnings("PZLA_PREFER_ZERO_LENGTH_ARRAYS")
513 protected byte[] getDhcpPacketOut(DHCP reply, Ethernet etherPkt, String phyAddrees) {
516 * DECLINE or RELEASE don't result in reply packet
520 LOG.trace("Sending DHCP Pkt {}", reply);
521 InetAddress serverIp = reply.getOptionInetAddr(DHCPConstants.OPT_SERVER_IDENTIFIER);
523 UDP udpPkt = new UDP();
526 rawPkt = reply.serialize();
527 } catch (PacketException e) {
528 LOG.warn("Failed to serialize packet", e);
531 udpPkt.setRawPayload(rawPkt);
532 udpPkt.setDestinationPort(DhcpMConstants.DHCP_CLIENT_PORT);
533 udpPkt.setSourcePort(DhcpMConstants.DHCP_SERVER_PORT);
534 udpPkt.setLength((short) (rawPkt.length + 8));
537 rawPkt = udpPkt.serialize();
538 } catch (PacketException e) {
539 LOG.warn("Failed to serialize packet", e);
543 boolean computeUdpChecksum = true;
544 if (computeUdpChecksum) {
545 checkSum = computeChecksum(rawPkt, serverIp.getAddress(),
546 NetUtils.intToByteArray4(DhcpMConstants.BCAST_IP));
548 udpPkt.setChecksum(checkSum);
549 IPv4 ip4Reply = new IPv4();
550 ip4Reply.setPayload(udpPkt);
551 ip4Reply.setProtocol(IPProtocols.UDP.byteValue());
552 ip4Reply.setSourceAddress(serverIp);
553 ip4Reply.setDestinationAddress(DhcpMConstants.BCAST_IP);
554 ip4Reply.setTotalLength((short) (rawPkt.length + 20));
555 ip4Reply.setTtl((byte) 32);
556 // create Ethernet Frame
557 Ethernet ether = new Ethernet();
558 if (etherPkt.getEtherType() == (short)NwConstants.ETHTYPE_802_1Q) {
559 IEEE8021Q vlanPacket = (IEEE8021Q) etherPkt.getPayload();
560 IEEE8021Q vlanTagged = new IEEE8021Q();
561 vlanTagged.setCFI(vlanPacket.getCfi());
562 vlanTagged.setPriority(vlanPacket.getPriority());
563 vlanTagged.setVlanId(vlanPacket.getVlanId());
564 vlanTagged.setPayload(ip4Reply);
565 vlanTagged.setEtherType(EtherTypes.IPv4.shortValue());
566 ether.setPayload(vlanTagged);
567 ether.setEtherType((short) NwConstants.ETHTYPE_802_1Q);
569 ether.setEtherType(EtherTypes.IPv4.shortValue());
570 ether.setPayload(ip4Reply);
572 ether.setSourceMACAddress(getServerMacAddress(phyAddrees));
573 ether.setDestinationMACAddress(etherPkt.getSourceMACAddress());
576 rawPkt = ether.serialize();
577 } catch (PacketException e) {
578 LOG.warn("Failed to serialize ethernet reply",e);
584 private static byte[] getServerMacAddress(String phyAddress) {
585 // Should we return ControllerMac instead?
586 return DHCPUtils.strMacAddrtoByteArray(phyAddress);
589 public short computeChecksum(byte[] inData, byte[] srcAddr, byte[] destAddr) {
595 for (index = 0; index < inData.length - 1; index = index + 2) {
596 // Skip, if the current bytes are checkSum bytes
597 wordData = (inData[index] << 8 & 0xFF00) + (inData[index + 1] & 0xFF);
598 sum = sum + wordData;
601 if (index < inData.length) {
602 wordData = (inData[index] << 8 & 0xFF00) + (0 & 0xFF);
603 sum = sum + wordData;
606 for (index = 0; index < 4; index = index + 2) {
607 wordData = (srcAddr[index] << 8 & 0xFF00) + (srcAddr[index + 1] & 0xFF);
608 sum = sum + wordData;
611 for (index = 0; index < 4; index = index + 2) {
612 wordData = (destAddr[index] << 8 & 0xFF00) + (destAddr[index + 1] & 0xFF);
613 sum = sum + wordData;
615 sum = sum + 17 + inData.length;
617 while (sum >> 16 != 0) {
619 sum = (sum & 0xFFFF) + carry;
621 short checkSum = (short) ~((short) sum & 0xFFFF);
623 checkSum = (short)0xffff;
628 private void setCommonOptions(DHCP pkt, DhcpInfo dhcpInfo) {
629 String serverIp = dhcpInfo.getServerIp();
630 if (pkt.getMsgType() != DHCPConstants.MSG_NAK) {
631 setNonNakOptions(pkt, dhcpInfo);
635 * setParameterListOptions may have initialized some of these
636 * options to maintain order. If we can't fill them, unset to avoid
637 * sending wrong information in reply.
639 if (serverIp != null) {
640 pkt.setOptionInetAddr(DHCPConstants.OPT_SERVER_IDENTIFIER, serverIp);
642 pkt.unsetOption(DHCPConstants.OPT_SERVER_IDENTIFIER);
644 } catch (UnknownHostException e) {
645 LOG.warn("Failed to set option", e);
649 private void setNonNakOptions(DHCP pkt, DhcpInfo dhcpInfo) {
650 pkt.setOptionInt(DHCPConstants.OPT_LEASE_TIME, dhcpMgr.getDhcpLeaseTime());
651 if (dhcpMgr.getDhcpDefDomain() != null) {
652 pkt.setOptionString(DHCPConstants.OPT_DOMAIN_NAME, dhcpMgr.getDhcpDefDomain());
654 if (dhcpMgr.getDhcpLeaseTime() > 0) {
655 pkt.setOptionInt(DHCPConstants.OPT_REBINDING_TIME, dhcpMgr.getDhcpRebindingTime());
656 pkt.setOptionInt(DHCPConstants.OPT_RENEWAL_TIME, dhcpMgr.getDhcpRenewalTime());
658 SubnetUtils util = null;
659 SubnetInfo info = null;
660 util = new SubnetUtils(dhcpInfo.getCidr());
661 info = util.getInfo();
662 String gwIp = dhcpInfo.getGatewayIp();
663 List<String> dnServers = dhcpInfo.getDnsServers();
666 * setParameterListOptions may have initialized some of these
667 * options to maintain order. If we can't fill them, unset to avoid
668 * sending wrong information in reply.
671 pkt.setOptionInetAddr(DHCPConstants.OPT_ROUTERS, gwIp);
673 pkt.unsetOption(DHCPConstants.OPT_ROUTERS);
676 pkt.setOptionInetAddr(DHCPConstants.OPT_SUBNET_MASK, info.getNetmask());
677 pkt.setOptionInetAddr(DHCPConstants.OPT_BROADCAST_ADDRESS, info.getBroadcastAddress());
679 pkt.unsetOption(DHCPConstants.OPT_SUBNET_MASK);
680 pkt.unsetOption(DHCPConstants.OPT_BROADCAST_ADDRESS);
682 if (dnServers != null && dnServers.size() > 0) {
683 pkt.setOptionStrAddrs(DHCPConstants.OPT_DOMAIN_NAME_SERVERS, dnServers);
685 pkt.unsetOption(DHCPConstants.OPT_DOMAIN_NAME_SERVERS);
687 } catch (UnknownHostException e) {
688 // TODO Auto-generated catch block
689 LOG.warn("Failed to set option", e);
693 private void setParameterListOptions(DHCP req, DHCP reply, DhcpInfo dhcpInfo) {
694 byte[] paramList = req.getOptionBytes(DHCPConstants.OPT_PARAMETER_REQUEST_LIST);
695 for (byte element : paramList) {
697 case DHCPConstants.OPT_SUBNET_MASK:
698 case DHCPConstants.OPT_ROUTERS:
699 case DHCPConstants.OPT_SERVER_IDENTIFIER:
700 case DHCPConstants.OPT_DOMAIN_NAME_SERVERS:
701 case DHCPConstants.OPT_BROADCAST_ADDRESS:
702 case DHCPConstants.OPT_LEASE_TIME:
703 case DHCPConstants.OPT_RENEWAL_TIME:
704 case DHCPConstants.OPT_REBINDING_TIME:
705 /* These values will be filled in setCommonOptions
706 * Setting these just to preserve order as
707 * specified in PARAMETER_REQUEST_LIST.
709 reply.setOptionInt(element, 0);
711 case DHCPConstants.OPT_DOMAIN_NAME:
712 reply.setOptionString(element, " ");
714 case DHCPConstants.OPT_CLASSLESS_ROUTE:
715 setOptionClasslessRoute(reply, dhcpInfo);
718 LOG.trace("DHCP Option code {} not supported yet", element);
724 private void setOptionClasslessRoute(DHCP reply, DhcpInfo dhcpInfo) {
725 List<HostRoutes> hostRoutes = dhcpInfo.getHostRoutes();
726 if (hostRoutes == null) {
727 //we can't set this option, so return
730 ByteArrayOutputStream result = new ByteArrayOutputStream();
731 for (HostRoutes hostRoute : hostRoutes) {
732 if (hostRoute.getNexthop().getIpv4Address() == null
733 || hostRoute.getDestination().getIpv4Prefix() == null) {
734 // we only deal with IPv4 addresses
737 String router = hostRoute.getNexthop().getIpv4Address().getValue();
738 String dest = hostRoute.getDestination().getIpv4Prefix().getValue();
740 result.write(convertToClasslessRouteOption(dest, router));
741 } catch (IOException | NullPointerException e) {
742 LOG.trace("Exception {}", e.getMessage());
745 if (result.size() > 0) {
746 reply.setOptionBytes(DHCPConstants.OPT_CLASSLESS_ROUTE , result.toByteArray());
751 protected byte[] convertToClasslessRouteOption(String dest, String router) {
752 ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
753 if (dest == null || router == null) {
759 String[] parts = dest.split("/");
760 if (parts.length < 2) {
763 prefix = Short.valueOf(parts[1]);
766 byteArray.write(prefix.byteValue());
767 SubnetUtils util = new SubnetUtils(dest);
768 SubnetInfo info = util.getInfo();
769 String strNetAddr = info.getNetworkAddress();
771 byte[] netAddr = InetAddress.getByName(strNetAddr).getAddress();
772 //Strip any trailing 0s from netAddr
773 for (int i = 0; i < netAddr.length;i++) {
774 if (netAddr[i] != 0) {
775 byteArray.write(netAddr,i,1);
778 byteArray.write(InetAddress.getByName(router).getAddress());
779 } catch (IOException e) {
782 return byteArray.toByteArray();
785 private boolean isPktInReasonSendtoCtrl(Class<? extends PacketInReason> pktInReason) {
786 return pktInReason == SendToController.class;
790 private String getInterfaceNameFromTag(long portTag) {
791 String interfaceName = null;
792 GetInterfaceFromIfIndexInput input =
793 new GetInterfaceFromIfIndexInputBuilder().setIfIndex((int) portTag).build();
794 Future<RpcResult<GetInterfaceFromIfIndexOutput>> futureOutput =
795 interfaceManagerRpc.getInterfaceFromIfIndex(input);
797 if (!futureOutput.get().isSuccessful()) {
798 LOG.error("Failed to get the interface name from tag {} using getInterfaceFromIfIndex RPC", portTag);
801 GetInterfaceFromIfIndexOutput output = futureOutput.get().getResult();
802 interfaceName = output.getInterfaceName();
803 } catch (InterruptedException | ExecutionException e) {
804 LOG.error("Error while retrieving the interfaceName from tag using getInterfaceFromIfIndex RPC");
806 LOG.trace("Returning interfaceName {} for tag {} form getInterfaceNameFromTag", interfaceName, portTag);
807 return interfaceName;
810 private List<Action> getEgressAction(String interfaceName, Uint64 tunnelId) {
812 if (interfaceManager.isItmDirectTunnelsEnabled() && tunnelId != null) {
813 GetEgressActionsForTunnelInputBuilder egressAction =
814 new GetEgressActionsForTunnelInputBuilder().setIntfName(interfaceName);
815 egressAction.setTunnelKey(tunnelId.longValue());
816 RpcResult<GetEgressActionsForTunnelOutput> rpcResult =
817 itmRpcService.getEgressActionsForTunnel(egressAction.build()).get();
818 if (!rpcResult.isSuccessful()) {
819 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
820 interfaceName, rpcResult.getErrors());
822 return rpcResult.getResult().getAction();
825 GetEgressActionsForInterfaceInputBuilder egressAction =
826 new GetEgressActionsForInterfaceInputBuilder().setIntfName(interfaceName);
827 if (tunnelId != null) {
828 egressAction.setTunnelKey(tunnelId.longValue());
830 Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
831 interfaceManagerRpc.getEgressActionsForInterface(egressAction.build());
832 RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
833 if (!rpcResult.isSuccessful()) {
834 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
835 interfaceName, rpcResult.getErrors());
837 return rpcResult.getResult().getAction();
840 } catch (InterruptedException | ExecutionException e) {
841 LOG.warn("Exception when egress actions for interface {}", interfaceName, e);
843 return Collections.emptyList();