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.math.BigInteger;
14 import java.net.InetAddress;
15 import java.net.UnknownHostException;
16 import java.util.ArrayList;
17 import java.util.Arrays;
18 import java.util.Collections;
19 import java.util.List;
20 import java.util.concurrent.ExecutionException;
21 import java.util.concurrent.Future;
22 import javax.inject.Inject;
23 import javax.inject.Singleton;
24 import org.apache.commons.net.util.SubnetUtils;
25 import org.apache.commons.net.util.SubnetUtils.SubnetInfo;
26 import org.eclipse.jdt.annotation.NonNull;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
29 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
30 import org.opendaylight.genius.interfacemanager.globals.InterfaceInfo;
31 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
32 import org.opendaylight.genius.mdsalutil.MDSALUtil;
33 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
34 import org.opendaylight.genius.mdsalutil.NwConstants;
35 import org.opendaylight.genius.mdsalutil.packet.Ethernet;
36 import org.opendaylight.genius.mdsalutil.packet.IEEE8021Q;
37 import org.opendaylight.genius.mdsalutil.packet.IPProtocols;
38 import org.opendaylight.genius.mdsalutil.packet.IPv4;
39 import org.opendaylight.genius.mdsalutil.packet.UDP;
40 import org.opendaylight.infrautils.metrics.Counter;
41 import org.opendaylight.infrautils.metrics.Labeled;
42 import org.opendaylight.infrautils.metrics.MetricDescriptor;
43 import org.opendaylight.infrautils.metrics.MetricProvider;
44 import org.opendaylight.infrautils.utils.concurrent.JdkFutures;
45 import org.opendaylight.netvirt.dhcpservice.api.DHCP;
46 import org.opendaylight.netvirt.dhcpservice.api.DHCPConstants;
47 import org.opendaylight.netvirt.dhcpservice.api.DHCPUtils;
48 import org.opendaylight.netvirt.dhcpservice.api.DhcpMConstants;
49 import org.opendaylight.openflowplugin.libraries.liblldp.EtherTypes;
50 import org.opendaylight.openflowplugin.libraries.liblldp.NetUtils;
51 import org.opendaylight.openflowplugin.libraries.liblldp.PacketException;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceInputBuilder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceOutput;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInput;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInputBuilder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexOutput;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelInputBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelOutput;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp_allocation_pool.rev161214.dhcp_allocation_pool.network.AllocationPool;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnet.attributes.HostRoutes;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketInReason;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.SendToController;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.api.rev150710.subnet.dhcp.port.data.SubnetToDhcpPort;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.config.rev150710.DhcpserviceConfig;
76 import org.opendaylight.yangtools.yang.common.RpcResult;
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();
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 BigInteger metadata = packet.getMatch().getMetadata().getMetadata();
187 long portTag = MetaDataUtil.getLportFromMetadata(metadata).intValue();
188 String macAddress = DHCPUtils.byteArrayToString(ethPkt.getSourceMACAddress());
189 pktInCounter.label(macAddress).increment();
190 BigInteger tunnelId =
191 packet.getMatch().getTunnel() == null ? null : packet.getMatch().getTunnel().getTunnelId();
192 String interfaceName = getInterfaceNameFromTag(portTag);
193 if (interfaceName == null) {
194 pktDropCounter.label(macAddress).label(UNKNOWN_LABEL).label(
195 PktDropReason.INTERFACE_NAME_NOT_FOUND.name()).increment();
198 InterfaceInfo interfaceInfo =
199 interfaceManager.getInterfaceInfoFromOperationalDataStore(interfaceName);
200 if (interfaceInfo == null) {
201 LOG.error("Failed to get interface info for interface name {}", interfaceName);
202 pktDropCounter.label(macAddress).label(interfaceName).label(
203 PktDropReason.INTERFACE_INFO_NOT_FOUND.name()).increment();
207 if (tunnelId != null) {
208 port = dhcpExternalTunnelManager.readVniMacToPortCache(tunnelId, macAddress);
210 port = getNeutronPort(interfaceName);
212 Subnet subnet = getNeutronSubnet(port);
213 String serverMacAddress = interfaceInfo.getMacAddress();
214 String serverIp = null;
215 if (subnet != null) {
216 java.util.Optional<SubnetToDhcpPort> dhcpPortData = DhcpServiceUtils
217 .getSubnetDhcpPortData(broker, subnet.getUuid().getValue());
218 /* If enable_dhcp_service flag was enabled and an ODL network DHCP Port data was made available use
219 * the ports Fixed IP as server IP for DHCP communication.
221 if (dhcpPortData.isPresent()) {
222 serverIp = dhcpPortData.get().getPortFixedip();
223 serverMacAddress = dhcpPortData.get().getPortMacaddress();
225 // DHCP Neutron Port not found for this network
226 LOG.error("Neutron DHCP port is not available for the Subnet {} and port {}.", subnet.getUuid(),
228 pktDropCounter.label(macAddress).label(interfaceName).label(
229 PktDropReason.SUBNET_NOT_FOUND.name()).increment();
233 pktDropCounter.label(macAddress).label(interfaceName).label(
234 PktDropReason.SUBNET_NOT_FOUND.name()).increment();
236 DHCP replyPkt = handleDhcpPacket(pktIn, interfaceName, macAddress, port, subnet, serverIp);
237 if (replyPkt == null) {
238 LOG.warn("Unable to construct reply packet for interface name {}", interfaceName);
241 byte[] pktOut = getDhcpPacketOut(replyPkt, ethPkt, serverMacAddress);
242 sendPacketOut(pktOut, macAddress, interfaceInfo.getDpId(), interfaceName, tunnelId);
247 private void sendPacketOut(byte[] pktOut, String mac, BigInteger dpnId, String interfaceName,
248 BigInteger tunnelId) {
249 List<Action> action = getEgressAction(interfaceName, tunnelId);
250 if (action == null) {
251 pktDropCounter.label(mac).label(interfaceName).label(
252 PktDropReason.EGRESS_ACTIONS_NOT_FOUND.name()).increment();
255 TransmitPacketInput output = MDSALUtil.getPacketOut(action, pktOut, dpnId);
256 LOG.trace("Transmitting packet: {}", output);
257 JdkFutures.addErrorLogging(pktService.transmitPacket(output), LOG, "Transmit packet");
261 private DHCP handleDhcpPacket(DHCP dhcpPkt, String interfaceName, String macAddress, Port interfacePort,
262 Subnet subnet, String serverIp) {
263 LOG.trace("DHCP pkt rcvd {}", dhcpPkt);
264 byte msgType = dhcpPkt.getMsgType();
265 DhcpInfo dhcpInfo = null;
266 if (interfacePort != null) {
267 dhcpInfo = handleDhcpNeutronPacket(msgType, interfacePort, subnet, serverIp);
268 } else if (config.isDhcpDynamicAllocationPoolEnabled()) {
269 dhcpInfo = handleDhcpAllocationPoolPacket(msgType, interfaceName, macAddress);
272 if (dhcpInfo != null) {
273 if (msgType == DHCPConstants.MSG_DISCOVER) {
274 reply = getReplyToDiscover(dhcpPkt, dhcpInfo);
275 } else if (msgType == DHCPConstants.MSG_REQUEST) {
276 reply = getReplyToRequest(dhcpPkt, dhcpInfo);
284 private DhcpInfo handleDhcpNeutronPacket(byte msgType, Port port, Subnet subnet, String serverIp) {
285 if (msgType == DHCPConstants.MSG_DECLINE) {
286 LOG.trace("DHCPDECLINE received");
288 } else if (msgType == DHCPConstants.MSG_RELEASE) {
289 LOG.trace("DHCPRELEASE received");
292 return getDhcpInfoFromNeutronPort(port, subnet, serverIp);
297 private DhcpInfo handleDhcpAllocationPoolPacket(byte msgType, String interfaceName, String macAddress) {
299 String networkId = dhcpAllocationPoolMgr.getNetworkByPort(interfaceName);
300 AllocationPool pool = networkId != null ? dhcpAllocationPoolMgr.getAllocationPoolByNetwork(networkId)
302 if (networkId == null || pool == null) {
303 LOG.warn("No Dhcp Allocation Pool was found for interface: {}", interfaceName);
307 case DHCPConstants.MSG_DISCOVER:
308 case DHCPConstants.MSG_REQUEST:
309 // FIXME: requested ip is currently ignored in moment of allocation
310 return getDhcpInfoFromAllocationPool(networkId, pool, macAddress);
311 case DHCPConstants.MSG_RELEASE:
312 dhcpAllocationPoolMgr.releaseIpAllocation(networkId, pool, macAddress);
317 } catch (ReadFailedException e) {
318 LOG.error("Error reading from MD-SAL", e);
323 private DhcpInfo getDhcpInfoFromNeutronPort(Port port, Subnet subnet, String serverIp) {
324 DhcpInfo dhcpInfo = getDhcpInfo(port, subnet, serverIp);
325 LOG.trace("NeutronPort: {} \n NeutronSubnet: {}, dhcpInfo{}", port, subnet, dhcpInfo);
329 private DhcpInfo getDhcpInfoFromAllocationPool(String networkId, AllocationPool pool, String macAddress) {
330 IpAddress allocatedIp = dhcpAllocationPoolMgr.getIpAllocation(networkId, pool, macAddress);
331 DhcpInfo dhcpInfo = getApDhcpInfo(pool, allocatedIp);
332 LOG.info("AllocationPoolNetwork: {}, dhcpInfo {}", networkId, dhcpInfo);
337 private DhcpInfo getDhcpInfo(Port port, Subnet subnet, String serverIp) {
338 DhcpInfo dhcpInfo = null;
339 if (port != null && subnet != null) {
340 List<IpAddress> dnsServers = new ArrayList<>();
341 if (subnet.getDnsNameservers() != null && !subnet.getDnsNameservers().isEmpty()) {
342 dnsServers = subnet.getDnsNameservers();
344 dhcpInfo = new DhcpInfo();
345 if (isIpv4Address(subnet.getGatewayIp())) {
346 dhcpInfo.setGatewayIp(subnet.getGatewayIp().getIpv4Address().getValue());
348 String clientIp = getIpv4Address(port);
349 if (clientIp != null && serverIp != null) {
350 List<HostRoutes> subnetHostRoutes = new ArrayList<>();
351 if (subnet.getHostRoutes() != null && !subnet.getHostRoutes().isEmpty()) {
352 for (HostRoutes hostRoute : subnet.getHostRoutes()) {
353 if (!hostRoute.getNexthop().stringValue().equals(clientIp)) {
354 subnetHostRoutes.add(hostRoute);
358 dhcpInfo.setClientIp(clientIp).setServerIp(serverIp)
359 .setCidr(subnet.getCidr().stringValue()).setHostRoutes(subnetHostRoutes)
360 .setDnsServersIpAddrs(dnsServers);
367 private static DhcpInfo getApDhcpInfo(AllocationPool ap, IpAddress allocatedIp) {
368 String clientIp = allocatedIp.stringValue();
369 String serverIp = ap.getGateway().stringValue();
370 List<IpAddress> dnsServers = ap.getDnsServers();
371 DhcpInfo dhcpInfo = new DhcpInfo();
372 dhcpInfo.setClientIp(clientIp).setServerIp(serverIp).setCidr(ap.getSubnet().stringValue())
373 .setHostRoutes(Collections.emptyList()).setDnsServersIpAddrs(dnsServers).setGatewayIp(serverIp);
379 * getIpv4Address and isIpv4Address
380 * Many other modules use/need similar methods. Should
381 * be refactored to a common NeutronUtils module. *
384 private static String getIpv4Address(Port port) {
386 for (FixedIps fixedIp : port.nonnullFixedIps()) {
387 if (isIpv4Address(fixedIp.getIpAddress())) {
388 return fixedIp.getIpAddress().getIpv4Address().getValue();
391 LOG.error("Could not find ipv4 address for port {}", port);
395 private static boolean isIpv4Address(@Nullable IpAddress ip) {
396 return ip != null && ip.getIpv4Address() != null;
400 private Subnet getNeutronSubnet(Port port) {
401 return dhcpMgr.getNeutronSubnet(port);
405 private Port getNeutronPort(String interfaceName) {
406 return dhcpMgr.getNeutronPort(interfaceName);
410 private static DHCP getDhcpPktIn(Ethernet actualEthernetPacket) {
411 Ethernet ethPkt = actualEthernetPacket;
412 if (ethPkt.getEtherType() == (short)NwConstants.ETHTYPE_802_1Q) {
413 ethPkt = (Ethernet)ethPkt.getPayload();
415 // Currently only IPv4 is supported
416 if (ethPkt.getPayload() instanceof IPv4) {
417 IPv4 ipPkt = (IPv4) ethPkt.getPayload();
418 if (ipPkt.getPayload() instanceof UDP) {
419 UDP udpPkt = (UDP) ipPkt.getPayload();
420 if (udpPkt.getSourcePort() == DhcpMConstants.DHCP_CLIENT_PORT
421 && udpPkt.getDestinationPort() == DhcpMConstants.DHCP_SERVER_PORT) {
422 LOG.trace("Matched DHCP_CLIENT_PORT and DHCP_SERVER_PORT");
423 byte[] rawDhcpPayload = udpPkt.getRawPayload();
424 DHCP reply = new DHCP();
426 reply.deserialize(rawDhcpPayload, 0, rawDhcpPayload.length);
427 } catch (PacketException e) {
428 LOG.warn("Failed to deserialize DHCP pkt");
429 LOG.trace("Reason for failure", e);
439 DHCP getReplyToDiscover(DHCP dhcpPkt, DhcpInfo dhcpInfo) {
440 DHCP reply = new DHCP();
441 reply.setOp(DHCPConstants.BOOTREPLY);
442 reply.setHtype(dhcpPkt.getHtype());
443 reply.setHlen(dhcpPkt.getHlen());
444 reply.setHops((byte) 0);
445 reply.setXid(dhcpPkt.getXid());
446 reply.setSecs((short) 0);
448 reply.setYiaddr(dhcpInfo.getClientIp());
449 reply.setSiaddr(dhcpInfo.getServerIp());
451 reply.setFlags(dhcpPkt.getFlags());
452 reply.setGiaddr(dhcpPkt.getGiaddr());
453 reply.setChaddr(dhcpPkt.getChaddr());
455 reply.setMsgType(DHCPConstants.MSG_OFFER);
456 if (dhcpPkt.containsOption(DHCPConstants.OPT_PARAMETER_REQUEST_LIST)) {
457 setParameterListOptions(dhcpPkt, reply, dhcpInfo);
459 setCommonOptions(reply, dhcpInfo);
463 DHCP getReplyToRequest(DHCP dhcpPkt, DhcpInfo dhcpInfo) {
464 boolean sendAck = false;
465 byte[] requestedIp = null;
466 DHCP reply = new DHCP();
467 reply.setOp(DHCPConstants.BOOTREPLY);
468 reply.setHtype(dhcpPkt.getHtype());
469 reply.setHlen(dhcpPkt.getHlen());
470 reply.setHops((byte) 0);
471 reply.setXid(dhcpPkt.getXid());
472 reply.setSecs((short) 0);
474 reply.setFlags(dhcpPkt.getFlags());
475 reply.setGiaddr(dhcpPkt.getGiaddr());
476 reply.setChaddr(dhcpPkt.getChaddr());
479 allocatedIp = DHCPUtils.strAddrToByteArray(dhcpInfo.getClientIp());
480 } catch (UnknownHostException e) {
481 LOG.debug("strAddrToByteArray", e);
485 if (Arrays.equals(allocatedIp, dhcpPkt.getCiaddr())) {
486 //This means a renew request
489 requestedIp = dhcpPkt.getOptionBytes(DHCPConstants.OPT_REQUESTED_ADDRESS);
490 sendAck = Arrays.equals(allocatedIp, requestedIp);
494 reply.setCiaddr(dhcpPkt.getCiaddr());
495 reply.setYiaddr(dhcpInfo.getClientIp());
496 reply.setSiaddr(dhcpInfo.getServerIp());
497 reply.setMsgType(DHCPConstants.MSG_ACK);
498 if (dhcpPkt.containsOption(DHCPConstants.OPT_PARAMETER_REQUEST_LIST)) {
499 setParameterListOptions(dhcpPkt, reply, dhcpInfo);
502 reply.setMsgType(DHCPConstants.MSG_NAK);
504 setCommonOptions(reply, dhcpInfo);
508 // "Consider returning a zero length array rather than null" - the eventual user of the returned byte[] likely
509 // expects null and it's unclear what the behavior would be if empty array was returned.
510 @SuppressFBWarnings("PZLA_PREFER_ZERO_LENGTH_ARRAYS")
512 protected byte[] getDhcpPacketOut(DHCP reply, Ethernet etherPkt, String phyAddrees) {
515 * DECLINE or RELEASE don't result in reply packet
519 LOG.trace("Sending DHCP Pkt {}", reply);
520 InetAddress serverIp = reply.getOptionInetAddr(DHCPConstants.OPT_SERVER_IDENTIFIER);
522 UDP udpPkt = new UDP();
525 rawPkt = reply.serialize();
526 } catch (PacketException e) {
527 LOG.warn("Failed to serialize packet", e);
530 udpPkt.setRawPayload(rawPkt);
531 udpPkt.setDestinationPort(DhcpMConstants.DHCP_CLIENT_PORT);
532 udpPkt.setSourcePort(DhcpMConstants.DHCP_SERVER_PORT);
533 udpPkt.setLength((short) (rawPkt.length + 8));
536 rawPkt = udpPkt.serialize();
537 } catch (PacketException e) {
538 LOG.warn("Failed to serialize packet", e);
542 boolean computeUdpChecksum = true;
543 if (computeUdpChecksum) {
544 checkSum = computeChecksum(rawPkt, serverIp.getAddress(),
545 NetUtils.intToByteArray4(DhcpMConstants.BCAST_IP));
547 udpPkt.setChecksum(checkSum);
548 IPv4 ip4Reply = new IPv4();
549 ip4Reply.setPayload(udpPkt);
550 ip4Reply.setProtocol(IPProtocols.UDP.byteValue());
551 ip4Reply.setSourceAddress(serverIp);
552 ip4Reply.setDestinationAddress(DhcpMConstants.BCAST_IP);
553 ip4Reply.setTotalLength((short) (rawPkt.length + 20));
554 ip4Reply.setTtl((byte) 32);
555 // create Ethernet Frame
556 Ethernet ether = new Ethernet();
557 if (etherPkt.getEtherType() == (short)NwConstants.ETHTYPE_802_1Q) {
558 IEEE8021Q vlanPacket = (IEEE8021Q) etherPkt.getPayload();
559 IEEE8021Q vlanTagged = new IEEE8021Q();
560 vlanTagged.setCFI(vlanPacket.getCfi());
561 vlanTagged.setPriority(vlanPacket.getPriority());
562 vlanTagged.setVlanId(vlanPacket.getVlanId());
563 vlanTagged.setPayload(ip4Reply);
564 vlanTagged.setEtherType(EtherTypes.IPv4.shortValue());
565 ether.setPayload(vlanTagged);
566 ether.setEtherType((short) NwConstants.ETHTYPE_802_1Q);
568 ether.setEtherType(EtherTypes.IPv4.shortValue());
569 ether.setPayload(ip4Reply);
571 ether.setSourceMACAddress(getServerMacAddress(phyAddrees));
572 ether.setDestinationMACAddress(etherPkt.getSourceMACAddress());
575 rawPkt = ether.serialize();
576 } catch (PacketException e) {
577 LOG.warn("Failed to serialize ethernet reply",e);
583 private static byte[] getServerMacAddress(String phyAddress) {
584 // Should we return ControllerMac instead?
585 return DHCPUtils.strMacAddrtoByteArray(phyAddress);
588 public short computeChecksum(byte[] inData, byte[] srcAddr, byte[] destAddr) {
594 for (index = 0; index < inData.length - 1; index = index + 2) {
595 // Skip, if the current bytes are checkSum bytes
596 wordData = (inData[index] << 8 & 0xFF00) + (inData[index + 1] & 0xFF);
597 sum = sum + wordData;
600 if (index < inData.length) {
601 wordData = (inData[index] << 8 & 0xFF00) + (0 & 0xFF);
602 sum = sum + wordData;
605 for (index = 0; index < 4; index = index + 2) {
606 wordData = (srcAddr[index] << 8 & 0xFF00) + (srcAddr[index + 1] & 0xFF);
607 sum = sum + wordData;
610 for (index = 0; index < 4; index = index + 2) {
611 wordData = (destAddr[index] << 8 & 0xFF00) + (destAddr[index + 1] & 0xFF);
612 sum = sum + wordData;
614 sum = sum + 17 + inData.length;
616 while (sum >> 16 != 0) {
618 sum = (sum & 0xFFFF) + carry;
620 short checkSum = (short) ~((short) sum & 0xFFFF);
622 checkSum = (short)0xffff;
627 private void setCommonOptions(DHCP pkt, DhcpInfo dhcpInfo) {
628 String serverIp = dhcpInfo.getServerIp();
629 if (pkt.getMsgType() != DHCPConstants.MSG_NAK) {
630 setNonNakOptions(pkt, dhcpInfo);
634 * setParameterListOptions may have initialized some of these
635 * options to maintain order. If we can't fill them, unset to avoid
636 * sending wrong information in reply.
638 if (serverIp != null) {
639 pkt.setOptionInetAddr(DHCPConstants.OPT_SERVER_IDENTIFIER, serverIp);
641 pkt.unsetOption(DHCPConstants.OPT_SERVER_IDENTIFIER);
643 } catch (UnknownHostException e) {
644 LOG.warn("Failed to set option", e);
648 private void setNonNakOptions(DHCP pkt, DhcpInfo dhcpInfo) {
649 pkt.setOptionInt(DHCPConstants.OPT_LEASE_TIME, dhcpMgr.getDhcpLeaseTime());
650 if (dhcpMgr.getDhcpDefDomain() != null) {
651 pkt.setOptionString(DHCPConstants.OPT_DOMAIN_NAME, dhcpMgr.getDhcpDefDomain());
653 if (dhcpMgr.getDhcpLeaseTime() > 0) {
654 pkt.setOptionInt(DHCPConstants.OPT_REBINDING_TIME, dhcpMgr.getDhcpRebindingTime());
655 pkt.setOptionInt(DHCPConstants.OPT_RENEWAL_TIME, dhcpMgr.getDhcpRenewalTime());
657 SubnetUtils util = null;
658 SubnetInfo info = null;
659 util = new SubnetUtils(dhcpInfo.getCidr());
660 info = util.getInfo();
661 String gwIp = dhcpInfo.getGatewayIp();
662 List<String> dnServers = dhcpInfo.getDnsServers();
665 * setParameterListOptions may have initialized some of these
666 * options to maintain order. If we can't fill them, unset to avoid
667 * sending wrong information in reply.
670 pkt.setOptionInetAddr(DHCPConstants.OPT_ROUTERS, gwIp);
672 pkt.unsetOption(DHCPConstants.OPT_ROUTERS);
675 pkt.setOptionInetAddr(DHCPConstants.OPT_SUBNET_MASK, info.getNetmask());
676 pkt.setOptionInetAddr(DHCPConstants.OPT_BROADCAST_ADDRESS, info.getBroadcastAddress());
678 pkt.unsetOption(DHCPConstants.OPT_SUBNET_MASK);
679 pkt.unsetOption(DHCPConstants.OPT_BROADCAST_ADDRESS);
681 if (dnServers != null && dnServers.size() > 0) {
682 pkt.setOptionStrAddrs(DHCPConstants.OPT_DOMAIN_NAME_SERVERS, dnServers);
684 pkt.unsetOption(DHCPConstants.OPT_DOMAIN_NAME_SERVERS);
686 } catch (UnknownHostException e) {
687 // TODO Auto-generated catch block
688 LOG.warn("Failed to set option", e);
692 private void setParameterListOptions(DHCP req, DHCP reply, DhcpInfo dhcpInfo) {
693 byte[] paramList = req.getOptionBytes(DHCPConstants.OPT_PARAMETER_REQUEST_LIST);
694 for (byte element : paramList) {
696 case DHCPConstants.OPT_SUBNET_MASK:
697 case DHCPConstants.OPT_ROUTERS:
698 case DHCPConstants.OPT_SERVER_IDENTIFIER:
699 case DHCPConstants.OPT_DOMAIN_NAME_SERVERS:
700 case DHCPConstants.OPT_BROADCAST_ADDRESS:
701 case DHCPConstants.OPT_LEASE_TIME:
702 case DHCPConstants.OPT_RENEWAL_TIME:
703 case DHCPConstants.OPT_REBINDING_TIME:
704 /* These values will be filled in setCommonOptions
705 * Setting these just to preserve order as
706 * specified in PARAMETER_REQUEST_LIST.
708 reply.setOptionInt(element, 0);
710 case DHCPConstants.OPT_DOMAIN_NAME:
711 reply.setOptionString(element, " ");
713 case DHCPConstants.OPT_CLASSLESS_ROUTE:
714 setOptionClasslessRoute(reply, dhcpInfo);
717 LOG.trace("DHCP Option code {} not supported yet", element);
723 private void setOptionClasslessRoute(DHCP reply, DhcpInfo dhcpInfo) {
724 List<HostRoutes> hostRoutes = dhcpInfo.getHostRoutes();
725 if (hostRoutes == null) {
726 //we can't set this option, so return
729 ByteArrayOutputStream result = new ByteArrayOutputStream();
730 for (HostRoutes hostRoute : hostRoutes) {
731 if (hostRoute.getNexthop().getIpv4Address() == null
732 || hostRoute.getDestination().getIpv4Prefix() == null) {
733 // we only deal with IPv4 addresses
736 String router = hostRoute.getNexthop().getIpv4Address().getValue();
737 String dest = hostRoute.getDestination().getIpv4Prefix().getValue();
739 result.write(convertToClasslessRouteOption(dest, router));
740 } catch (IOException | NullPointerException e) {
741 LOG.trace("Exception {}", e.getMessage());
744 if (result.size() > 0) {
745 reply.setOptionBytes(DHCPConstants.OPT_CLASSLESS_ROUTE , result.toByteArray());
750 protected byte[] convertToClasslessRouteOption(String dest, String router) {
751 ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
752 if (dest == null || router == null) {
758 String[] parts = dest.split("/");
759 if (parts.length < 2) {
762 prefix = Short.valueOf(parts[1]);
765 byteArray.write(prefix.byteValue());
766 SubnetUtils util = new SubnetUtils(dest);
767 SubnetInfo info = util.getInfo();
768 String strNetAddr = info.getNetworkAddress();
770 byte[] netAddr = InetAddress.getByName(strNetAddr).getAddress();
771 //Strip any trailing 0s from netAddr
772 for (int i = 0; i < netAddr.length;i++) {
773 if (netAddr[i] != 0) {
774 byteArray.write(netAddr,i,1);
777 byteArray.write(InetAddress.getByName(router).getAddress());
778 } catch (IOException e) {
781 return byteArray.toByteArray();
784 private boolean isPktInReasonSendtoCtrl(Class<? extends PacketInReason> pktInReason) {
785 return pktInReason == SendToController.class;
789 private String getInterfaceNameFromTag(long portTag) {
790 String interfaceName = null;
791 GetInterfaceFromIfIndexInput input =
792 new GetInterfaceFromIfIndexInputBuilder().setIfIndex((int) portTag).build();
793 Future<RpcResult<GetInterfaceFromIfIndexOutput>> futureOutput =
794 interfaceManagerRpc.getInterfaceFromIfIndex(input);
796 if (!futureOutput.get().isSuccessful()) {
797 LOG.error("Failed to get the interface name from tag {} using getInterfaceFromIfIndex RPC", portTag);
800 GetInterfaceFromIfIndexOutput output = futureOutput.get().getResult();
801 interfaceName = output.getInterfaceName();
802 } catch (InterruptedException | ExecutionException e) {
803 LOG.error("Error while retrieving the interfaceName from tag using getInterfaceFromIfIndex RPC");
805 LOG.trace("Returning interfaceName {} for tag {} form getInterfaceNameFromTag", interfaceName, portTag);
806 return interfaceName;
809 private List<Action> getEgressAction(String interfaceName, BigInteger tunnelId) {
811 if (interfaceManager.isItmDirectTunnelsEnabled() && tunnelId != null) {
812 GetEgressActionsForTunnelInputBuilder egressAction =
813 new GetEgressActionsForTunnelInputBuilder().setIntfName(interfaceName);
814 egressAction.setTunnelKey(tunnelId.longValue());
815 RpcResult<GetEgressActionsForTunnelOutput> rpcResult =
816 itmRpcService.getEgressActionsForTunnel(egressAction.build()).get();
817 if (!rpcResult.isSuccessful()) {
818 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
819 interfaceName, rpcResult.getErrors());
821 return rpcResult.getResult().getAction();
824 GetEgressActionsForInterfaceInputBuilder egressAction =
825 new GetEgressActionsForInterfaceInputBuilder().setIntfName(interfaceName);
826 if (tunnelId != null) {
827 egressAction.setTunnelKey(tunnelId.longValue());
829 Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
830 interfaceManagerRpc.getEgressActionsForInterface(egressAction.build());
831 RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
832 if (!rpcResult.isSuccessful()) {
833 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
834 interfaceName, rpcResult.getErrors());
836 return rpcResult.getResult().getAction();
839 } catch (InterruptedException | ExecutionException e) {
840 LOG.warn("Exception when egress actions for interface {}", interfaceName, e);
842 return Collections.emptyList();