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.annotation.Nonnull;
23 import javax.inject.Inject;
24 import javax.inject.Singleton;
25 import org.apache.commons.net.util.SubnetUtils;
26 import org.apache.commons.net.util.SubnetUtils.SubnetInfo;
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.slf4j.Logger;
77 import org.slf4j.LoggerFactory;
81 public class DhcpPktHandler implements PacketProcessingListener {
83 private static final Logger LOG = LoggerFactory.getLogger(DhcpPktHandler.class);
85 private static final String UNKNOWN_LABEL = "unknown";
87 private enum PktDropReason {
88 INTERFACE_NAME_NOT_FOUND,
89 INTERFACE_INFO_NOT_FOUND,
91 EGRESS_ACTIONS_NOT_FOUND,
93 PKT_DESERIALIZATION_ERROR
96 private final DhcpManager dhcpMgr;
97 private final OdlInterfaceRpcService interfaceManagerRpc;
98 private final PacketProcessingService pktService;
99 private final DhcpExternalTunnelManager dhcpExternalTunnelManager;
100 private final IInterfaceManager interfaceManager;
101 private final DhcpserviceConfig config;
102 private final DhcpAllocationPoolManager dhcpAllocationPoolMgr;
103 private final DataBroker broker;
104 private final ItmRpcService itmRpcService;
105 private final Labeled<Labeled<Labeled<Counter>>> pktDropCounter;
106 private final Labeled<Counter> pktInCounter;
109 public DhcpPktHandler(final DhcpManager dhcpManager,
110 final DhcpExternalTunnelManager dhcpExternalTunnelManager,
111 final OdlInterfaceRpcService interfaceManagerRpc,
112 final PacketProcessingService pktService,
113 final IInterfaceManager interfaceManager,
114 final DhcpserviceConfig config,
115 final DhcpAllocationPoolManager dhcpAllocationPoolMgr,
116 final DataBroker dataBroker,
117 final ItmRpcService itmRpcService,
118 final MetricProvider metricProvider) {
119 this.interfaceManagerRpc = interfaceManagerRpc;
120 this.pktService = pktService;
121 this.dhcpExternalTunnelManager = dhcpExternalTunnelManager;
122 this.dhcpMgr = dhcpManager;
123 this.interfaceManager = interfaceManager;
124 this.config = config;
125 this.dhcpAllocationPoolMgr = dhcpAllocationPoolMgr;
126 this.broker = dataBroker;
127 this.itmRpcService = itmRpcService;
128 this.pktDropCounter = metricProvider.newCounter(buildDhcpMetricDescriptor("packet_drop"),
129 "mac", "interface", "reason");
130 this.pktInCounter = metricProvider.newCounter(buildDhcpMetricDescriptor("packet_in"), "mac");
133 private MetricDescriptor buildDhcpMetricDescriptor(String id) {
134 return MetricDescriptor.builder().anchor(this).project("netvirt").module("dhcpservice").id(id).build();
138 @SuppressWarnings("checkstyle:IllegalCatch")
139 public void onPacketReceived(PacketReceived packet) {
141 onPacketReceivedInternal(packet);
142 } catch (Exception e) {
143 pktDropCounter.label(getSourceMacAddress(packet)).label(UNKNOWN_LABEL)
144 .label(PktDropReason.EXCEPTION.name()).increment();
145 LOG.error("Failed to handle dhcp packet in ", e);
149 private String getSourceMacAddress(PacketReceived packet) {
150 Ethernet ethPkt = new Ethernet();
152 byte[] inPayload = packet.getPayload();
153 ethPkt.deserialize(inPayload, 0, inPayload.length * NetUtils.NUM_BITS_IN_A_BYTE);
154 return DHCPUtils.byteArrayToString(ethPkt.getSourceMACAddress());
155 } catch (PacketException pktException) {
156 LOG.error("Failed to parse the packet {}", packet);
158 return UNKNOWN_LABEL;
161 public void onPacketReceivedInternal(PacketReceived packet) {
162 if (!config.isControllerDhcpEnabled()) {
165 Class<? extends PacketInReason> pktInReason = packet.getPacketInReason();
166 short tableId = packet.getTableId().getValue();
167 if ((tableId == NwConstants.DHCP_TABLE || tableId == NwConstants.DHCP_TABLE_EXTERNAL_TUNNEL)
168 && isPktInReasonSendtoCtrl(pktInReason)) {
169 byte[] inPayload = packet.getPayload();
170 Ethernet ethPkt = new Ethernet();
172 ethPkt.deserialize(inPayload, 0, inPayload.length * NetUtils.NUM_BITS_IN_A_BYTE);
173 } catch (PacketException e) {
174 pktDropCounter.label(UNKNOWN_LABEL).label(UNKNOWN_LABEL).label(
175 PktDropReason.PKT_DESERIALIZATION_ERROR.name()).increment();
176 LOG.warn("Failed to decode DHCP Packet.", e);
177 LOG.trace("Received packet {}", packet);
181 pktIn = getDhcpPktIn(ethPkt);
183 LOG.trace("DHCPPkt received: {}", pktIn);
184 LOG.trace("Received Packet: {}", packet);
185 BigInteger metadata = packet.getMatch().getMetadata().getMetadata();
186 long portTag = MetaDataUtil.getLportFromMetadata(metadata).intValue();
187 String macAddress = DHCPUtils.byteArrayToString(ethPkt.getSourceMACAddress());
188 pktInCounter.label(macAddress).increment();
189 BigInteger tunnelId =
190 packet.getMatch().getTunnel() == null ? null : packet.getMatch().getTunnel().getTunnelId();
191 String interfaceName = getInterfaceNameFromTag(portTag);
192 if (interfaceName == null) {
193 pktDropCounter.label(macAddress).label(UNKNOWN_LABEL).label(
194 PktDropReason.INTERFACE_NAME_NOT_FOUND.name()).increment();
196 InterfaceInfo interfaceInfo =
197 interfaceManager.getInterfaceInfoFromOperationalDataStore(interfaceName);
198 if (interfaceInfo == null) {
199 LOG.error("Failed to get interface info for interface name {}", interfaceName);
200 pktDropCounter.label(macAddress).label(interfaceName).label(
201 PktDropReason.INTERFACE_INFO_NOT_FOUND.name()).increment();
205 if (tunnelId != null) {
206 port = dhcpExternalTunnelManager.readVniMacToPortCache(tunnelId, macAddress);
208 port = getNeutronPort(interfaceName);
210 Subnet subnet = getNeutronSubnet(port);
211 String serverMacAddress = interfaceInfo.getMacAddress();
212 String serverIp = null;
213 if (subnet != null) {
214 java.util.Optional<SubnetToDhcpPort> dhcpPortData = DhcpServiceUtils
215 .getSubnetDhcpPortData(broker, subnet.getUuid().getValue());
216 /* If enable_dhcp_service flag was enabled and an ODL network DHCP Port data was made available use
217 * the ports Fixed IP as server IP for DHCP communication.
219 if (dhcpPortData.isPresent()) {
220 serverIp = dhcpPortData.get().getPortFixedip();
221 serverMacAddress = dhcpPortData.get().getPortMacaddress();
223 // DHCP Neutron Port not found for this network
224 LOG.error("Neutron DHCP port is not available for the Subnet {} and port {}.", subnet.getUuid(),
226 pktDropCounter.label(macAddress).label(interfaceName).label(
227 PktDropReason.SUBNET_NOT_FOUND.name()).increment();
231 pktDropCounter.label(macAddress).label(interfaceName).label(
232 PktDropReason.SUBNET_NOT_FOUND.name()).increment();
234 DHCP replyPkt = handleDhcpPacket(pktIn, interfaceName, macAddress, port, subnet, serverIp);
235 if (replyPkt == null) {
236 LOG.warn("Unable to construct reply packet for interface name {}", interfaceName);
239 byte[] pktOut = getDhcpPacketOut(replyPkt, ethPkt, serverMacAddress);
240 sendPacketOut(pktOut, macAddress, interfaceInfo.getDpId(), interfaceName, tunnelId);
245 private void sendPacketOut(byte[] pktOut, String mac, BigInteger dpnId, String interfaceName,
246 BigInteger tunnelId) {
247 List<Action> action = getEgressAction(interfaceName, tunnelId);
248 if (action == null) {
249 pktDropCounter.label(mac).label(interfaceName).label(
250 PktDropReason.EGRESS_ACTIONS_NOT_FOUND.name()).increment();
253 TransmitPacketInput output = MDSALUtil.getPacketOut(action, pktOut, dpnId);
254 LOG.trace("Transmitting packet: {}", output);
255 JdkFutures.addErrorLogging(pktService.transmitPacket(output), LOG, "Transmit packet");
258 private DHCP handleDhcpPacket(DHCP dhcpPkt, String interfaceName, String macAddress, Port interfacePort,
259 Subnet subnet, String serverIp) {
260 LOG.trace("DHCP pkt rcvd {}", dhcpPkt);
261 byte msgType = dhcpPkt.getMsgType();
262 DhcpInfo dhcpInfo = null;
263 if (interfacePort != null) {
264 dhcpInfo = handleDhcpNeutronPacket(msgType, interfacePort, subnet, serverIp);
265 } else if (config.isDhcpDynamicAllocationPoolEnabled()) {
266 dhcpInfo = handleDhcpAllocationPoolPacket(msgType, interfaceName, macAddress);
269 if (dhcpInfo != null) {
270 if (msgType == DHCPConstants.MSG_DISCOVER) {
271 reply = getReplyToDiscover(dhcpPkt, dhcpInfo);
272 } else if (msgType == DHCPConstants.MSG_REQUEST) {
273 reply = getReplyToRequest(dhcpPkt, dhcpInfo);
280 private DhcpInfo handleDhcpNeutronPacket(byte msgType, Port port, Subnet subnet, String serverIp) {
281 if (msgType == DHCPConstants.MSG_DECLINE) {
282 LOG.trace("DHCPDECLINE received");
284 } else if (msgType == DHCPConstants.MSG_RELEASE) {
285 LOG.trace("DHCPRELEASE received");
288 return getDhcpInfoFromNeutronPort(port, subnet, serverIp);
292 private DhcpInfo handleDhcpAllocationPoolPacket(byte msgType, String interfaceName, String macAddress) {
294 String networkId = dhcpAllocationPoolMgr.getNetworkByPort(interfaceName);
295 AllocationPool pool = networkId != null ? dhcpAllocationPoolMgr.getAllocationPoolByNetwork(networkId)
297 if (networkId == null || pool == null) {
298 LOG.warn("No Dhcp Allocation Pool was found for interface: {}", interfaceName);
302 case DHCPConstants.MSG_DISCOVER:
303 case DHCPConstants.MSG_REQUEST:
304 // FIXME: requested ip is currently ignored in moment of allocation
305 return getDhcpInfoFromAllocationPool(networkId, pool, macAddress);
306 case DHCPConstants.MSG_RELEASE:
307 dhcpAllocationPoolMgr.releaseIpAllocation(networkId, pool, macAddress);
312 } catch (ReadFailedException e) {
313 LOG.error("Error reading from MD-SAL", e);
318 private DhcpInfo getDhcpInfoFromNeutronPort(Port port, Subnet subnet, String serverIp) {
319 DhcpInfo dhcpInfo = getDhcpInfo(port, subnet, serverIp);
320 LOG.trace("NeutronPort: {} \n NeutronSubnet: {}, dhcpInfo{}", port, subnet, dhcpInfo);
324 private DhcpInfo getDhcpInfoFromAllocationPool(String networkId, AllocationPool pool, String macAddress) {
325 IpAddress allocatedIp = dhcpAllocationPoolMgr.getIpAllocation(networkId, pool, macAddress);
326 DhcpInfo dhcpInfo = getApDhcpInfo(pool, allocatedIp);
327 LOG.info("AllocationPoolNetwork: {}, dhcpInfo {}", networkId, dhcpInfo);
331 private DhcpInfo getDhcpInfo(Port port, Subnet subnet, String serverIp) {
332 DhcpInfo dhcpInfo = null;
333 if (port != null && subnet != null) {
334 String clientIp = getIpv4Address(port);
335 List<IpAddress> dnsServers = subnet.getDnsNameservers();
336 dhcpInfo = new DhcpInfo();
337 if (isIpv4Address(subnet.getGatewayIp())) {
338 dhcpInfo.setGatewayIp(subnet.getGatewayIp().getIpv4Address().getValue());
340 if (clientIp != null && serverIp != null) {
341 List<HostRoutes> subnetHostRoutes = new ArrayList<>(subnet.getHostRoutes().size());
342 for (HostRoutes hostRoute : subnet.getHostRoutes()) {
343 if (!hostRoute.getNexthop().stringValue().equals(clientIp)) {
344 subnetHostRoutes.add(hostRoute);
347 dhcpInfo.setClientIp(clientIp).setServerIp(serverIp)
348 .setCidr(subnet.getCidr().stringValue()).setHostRoutes(subnetHostRoutes)
349 .setDnsServersIpAddrs(dnsServers);
355 private static DhcpInfo getApDhcpInfo(AllocationPool ap, IpAddress allocatedIp) {
356 DhcpInfo dhcpInfo = null;
358 String clientIp = allocatedIp.stringValue();
359 String serverIp = ap.getGateway().stringValue();
360 List<IpAddress> dnsServers = ap.getDnsServers();
361 dhcpInfo = new DhcpInfo();
362 dhcpInfo.setClientIp(clientIp).setServerIp(serverIp).setCidr(ap.getSubnet().stringValue())
363 .setHostRoutes(Collections.emptyList()).setDnsServersIpAddrs(dnsServers).setGatewayIp(serverIp);
369 * getIpv4Address and isIpv4Address
370 * Many other modules use/need similar methods. Should
371 * be refactored to a common NeutronUtils module. *
373 private static String getIpv4Address(Port port) {
375 for (FixedIps fixedIp : port.getFixedIps()) {
376 if (isIpv4Address(fixedIp.getIpAddress())) {
377 return fixedIp.getIpAddress().getIpv4Address().getValue();
380 LOG.error("Could not find ipv4 address for port {}", port);
384 private static boolean isIpv4Address(IpAddress ip) {
385 return ip != null && ip.getIpv4Address() != null;
388 private Subnet getNeutronSubnet(Port port) {
389 return dhcpMgr.getNeutronSubnet(port);
392 private Port getNeutronPort(String interfaceName) {
393 return dhcpMgr.getNeutronPort(interfaceName);
396 private static DHCP getDhcpPktIn(Ethernet actualEthernetPacket) {
397 Ethernet ethPkt = actualEthernetPacket;
398 if (ethPkt.getEtherType() == (short)NwConstants.ETHTYPE_802_1Q) {
399 ethPkt = (Ethernet)ethPkt.getPayload();
401 // Currently only IPv4 is supported
402 if (ethPkt.getPayload() instanceof IPv4) {
403 IPv4 ipPkt = (IPv4) ethPkt.getPayload();
404 if (ipPkt.getPayload() instanceof UDP) {
405 UDP udpPkt = (UDP) ipPkt.getPayload();
406 if (udpPkt.getSourcePort() == DhcpMConstants.DHCP_CLIENT_PORT
407 && udpPkt.getDestinationPort() == DhcpMConstants.DHCP_SERVER_PORT) {
408 LOG.trace("Matched DHCP_CLIENT_PORT and DHCP_SERVER_PORT");
409 byte[] rawDhcpPayload = udpPkt.getRawPayload();
410 DHCP reply = new DHCP();
412 reply.deserialize(rawDhcpPayload, 0, rawDhcpPayload.length);
413 } catch (PacketException e) {
414 LOG.warn("Failed to deserialize DHCP pkt");
415 LOG.trace("Reason for failure", e);
425 DHCP getReplyToDiscover(DHCP dhcpPkt, DhcpInfo dhcpInfo) {
426 DHCP reply = new DHCP();
427 reply.setOp(DHCPConstants.BOOTREPLY);
428 reply.setHtype(dhcpPkt.getHtype());
429 reply.setHlen(dhcpPkt.getHlen());
430 reply.setHops((byte) 0);
431 reply.setXid(dhcpPkt.getXid());
432 reply.setSecs((short) 0);
434 reply.setYiaddr(dhcpInfo.getClientIp());
435 reply.setSiaddr(dhcpInfo.getServerIp());
437 reply.setFlags(dhcpPkt.getFlags());
438 reply.setGiaddr(dhcpPkt.getGiaddr());
439 reply.setChaddr(dhcpPkt.getChaddr());
441 reply.setMsgType(DHCPConstants.MSG_OFFER);
442 if (dhcpPkt.containsOption(DHCPConstants.OPT_PARAMETER_REQUEST_LIST)) {
443 setParameterListOptions(dhcpPkt, reply, dhcpInfo);
445 setCommonOptions(reply, dhcpInfo);
449 DHCP getReplyToRequest(DHCP dhcpPkt, DhcpInfo dhcpInfo) {
450 boolean sendAck = false;
451 byte[] requestedIp = null;
452 DHCP reply = new DHCP();
453 reply.setOp(DHCPConstants.BOOTREPLY);
454 reply.setHtype(dhcpPkt.getHtype());
455 reply.setHlen(dhcpPkt.getHlen());
456 reply.setHops((byte) 0);
457 reply.setXid(dhcpPkt.getXid());
458 reply.setSecs((short) 0);
460 reply.setFlags(dhcpPkt.getFlags());
461 reply.setGiaddr(dhcpPkt.getGiaddr());
462 reply.setChaddr(dhcpPkt.getChaddr());
465 allocatedIp = DHCPUtils.strAddrToByteArray(dhcpInfo.getClientIp());
466 } catch (UnknownHostException e) {
467 LOG.debug("strAddrToByteArray", e);
471 if (Arrays.equals(allocatedIp, dhcpPkt.getCiaddr())) {
472 //This means a renew request
475 requestedIp = dhcpPkt.getOptionBytes(DHCPConstants.OPT_REQUESTED_ADDRESS);
476 sendAck = Arrays.equals(allocatedIp, requestedIp);
480 reply.setCiaddr(dhcpPkt.getCiaddr());
481 reply.setYiaddr(dhcpInfo.getClientIp());
482 reply.setSiaddr(dhcpInfo.getServerIp());
483 reply.setMsgType(DHCPConstants.MSG_ACK);
484 if (dhcpPkt.containsOption(DHCPConstants.OPT_PARAMETER_REQUEST_LIST)) {
485 setParameterListOptions(dhcpPkt, reply, dhcpInfo);
488 reply.setMsgType(DHCPConstants.MSG_NAK);
490 setCommonOptions(reply, dhcpInfo);
494 // "Consider returning a zero length array rather than null" - the eventual user of the returned byte[] likely
495 // expects null and it's unclear what the behavior would be if empty array was returned.
496 @SuppressFBWarnings("PZLA_PREFER_ZERO_LENGTH_ARRAYS")
497 protected byte[] getDhcpPacketOut(DHCP reply, Ethernet etherPkt, String phyAddrees) {
500 * DECLINE or RELEASE don't result in reply packet
504 LOG.trace("Sending DHCP Pkt {}", reply);
505 InetAddress serverIp = reply.getOptionInetAddr(DHCPConstants.OPT_SERVER_IDENTIFIER);
507 UDP udpPkt = new UDP();
510 rawPkt = reply.serialize();
511 } catch (PacketException e) {
512 LOG.warn("Failed to serialize packet", e);
515 udpPkt.setRawPayload(rawPkt);
516 udpPkt.setDestinationPort(DhcpMConstants.DHCP_CLIENT_PORT);
517 udpPkt.setSourcePort(DhcpMConstants.DHCP_SERVER_PORT);
518 udpPkt.setLength((short) (rawPkt.length + 8));
521 rawPkt = udpPkt.serialize();
522 } catch (PacketException e) {
523 LOG.warn("Failed to serialize packet", e);
527 boolean computeUdpChecksum = true;
528 if (computeUdpChecksum) {
529 checkSum = computeChecksum(rawPkt, serverIp.getAddress(),
530 NetUtils.intToByteArray4(DhcpMConstants.BCAST_IP));
532 udpPkt.setChecksum(checkSum);
533 IPv4 ip4Reply = new IPv4();
534 ip4Reply.setPayload(udpPkt);
535 ip4Reply.setProtocol(IPProtocols.UDP.byteValue());
536 ip4Reply.setSourceAddress(serverIp);
537 ip4Reply.setDestinationAddress(DhcpMConstants.BCAST_IP);
538 ip4Reply.setTotalLength((short) (rawPkt.length + 20));
539 ip4Reply.setTtl((byte) 32);
540 // create Ethernet Frame
541 Ethernet ether = new Ethernet();
542 if (etherPkt.getEtherType() == (short)NwConstants.ETHTYPE_802_1Q) {
543 IEEE8021Q vlanPacket = (IEEE8021Q) etherPkt.getPayload();
544 IEEE8021Q vlanTagged = new IEEE8021Q();
545 vlanTagged.setCFI(vlanPacket.getCfi());
546 vlanTagged.setPriority(vlanPacket.getPriority());
547 vlanTagged.setVlanId(vlanPacket.getVlanId());
548 vlanTagged.setPayload(ip4Reply);
549 vlanTagged.setEtherType(EtherTypes.IPv4.shortValue());
550 ether.setPayload(vlanTagged);
551 ether.setEtherType((short) NwConstants.ETHTYPE_802_1Q);
553 ether.setEtherType(EtherTypes.IPv4.shortValue());
554 ether.setPayload(ip4Reply);
556 ether.setSourceMACAddress(getServerMacAddress(phyAddrees));
557 ether.setDestinationMACAddress(etherPkt.getSourceMACAddress());
560 rawPkt = ether.serialize();
561 } catch (PacketException e) {
562 LOG.warn("Failed to serialize ethernet reply",e);
568 private static byte[] getServerMacAddress(String phyAddress) {
569 // Should we return ControllerMac instead?
570 return DHCPUtils.strMacAddrtoByteArray(phyAddress);
573 public short computeChecksum(byte[] inData, byte[] srcAddr, byte[] destAddr) {
579 for (index = 0; index < inData.length - 1; index = index + 2) {
580 // Skip, if the current bytes are checkSum bytes
581 wordData = (inData[index] << 8 & 0xFF00) + (inData[index + 1] & 0xFF);
582 sum = sum + wordData;
585 if (index < inData.length) {
586 wordData = (inData[index] << 8 & 0xFF00) + (0 & 0xFF);
587 sum = sum + wordData;
590 for (index = 0; index < 4; index = index + 2) {
591 wordData = (srcAddr[index] << 8 & 0xFF00) + (srcAddr[index + 1] & 0xFF);
592 sum = sum + wordData;
595 for (index = 0; index < 4; index = index + 2) {
596 wordData = (destAddr[index] << 8 & 0xFF00) + (destAddr[index + 1] & 0xFF);
597 sum = sum + wordData;
599 sum = sum + 17 + inData.length;
601 while (sum >> 16 != 0) {
603 sum = (sum & 0xFFFF) + carry;
605 short checkSum = (short) ~((short) sum & 0xFFFF);
607 checkSum = (short)0xffff;
612 private void setCommonOptions(DHCP pkt, DhcpInfo dhcpInfo) {
613 String serverIp = dhcpInfo.getServerIp();
614 if (pkt.getMsgType() != DHCPConstants.MSG_NAK) {
615 setNonNakOptions(pkt, dhcpInfo);
619 * setParameterListOptions may have initialized some of these
620 * options to maintain order. If we can't fill them, unset to avoid
621 * sending wrong information in reply.
623 if (serverIp != null) {
624 pkt.setOptionInetAddr(DHCPConstants.OPT_SERVER_IDENTIFIER, serverIp);
626 pkt.unsetOption(DHCPConstants.OPT_SERVER_IDENTIFIER);
628 } catch (UnknownHostException e) {
629 LOG.warn("Failed to set option", e);
633 private void setNonNakOptions(DHCP pkt, DhcpInfo dhcpInfo) {
634 pkt.setOptionInt(DHCPConstants.OPT_LEASE_TIME, dhcpMgr.getDhcpLeaseTime());
635 if (dhcpMgr.getDhcpDefDomain() != null) {
636 pkt.setOptionString(DHCPConstants.OPT_DOMAIN_NAME, dhcpMgr.getDhcpDefDomain());
638 if (dhcpMgr.getDhcpLeaseTime() > 0) {
639 pkt.setOptionInt(DHCPConstants.OPT_REBINDING_TIME, dhcpMgr.getDhcpRebindingTime());
640 pkt.setOptionInt(DHCPConstants.OPT_RENEWAL_TIME, dhcpMgr.getDhcpRenewalTime());
642 SubnetUtils util = null;
643 SubnetInfo info = null;
644 util = new SubnetUtils(dhcpInfo.getCidr());
645 info = util.getInfo();
646 String gwIp = dhcpInfo.getGatewayIp();
647 List<String> dnServers = dhcpInfo.getDnsServers();
650 * setParameterListOptions may have initialized some of these
651 * options to maintain order. If we can't fill them, unset to avoid
652 * sending wrong information in reply.
655 pkt.setOptionInetAddr(DHCPConstants.OPT_ROUTERS, gwIp);
657 pkt.unsetOption(DHCPConstants.OPT_ROUTERS);
660 pkt.setOptionInetAddr(DHCPConstants.OPT_SUBNET_MASK, info.getNetmask());
661 pkt.setOptionInetAddr(DHCPConstants.OPT_BROADCAST_ADDRESS, info.getBroadcastAddress());
663 pkt.unsetOption(DHCPConstants.OPT_SUBNET_MASK);
664 pkt.unsetOption(DHCPConstants.OPT_BROADCAST_ADDRESS);
666 if (dnServers != null && dnServers.size() > 0) {
667 pkt.setOptionStrAddrs(DHCPConstants.OPT_DOMAIN_NAME_SERVERS, dnServers);
669 pkt.unsetOption(DHCPConstants.OPT_DOMAIN_NAME_SERVERS);
671 } catch (UnknownHostException e) {
672 // TODO Auto-generated catch block
673 LOG.warn("Failed to set option", e);
677 private void setParameterListOptions(DHCP req, DHCP reply, DhcpInfo dhcpInfo) {
678 byte[] paramList = req.getOptionBytes(DHCPConstants.OPT_PARAMETER_REQUEST_LIST);
679 for (byte element : paramList) {
681 case DHCPConstants.OPT_SUBNET_MASK:
682 case DHCPConstants.OPT_ROUTERS:
683 case DHCPConstants.OPT_SERVER_IDENTIFIER:
684 case DHCPConstants.OPT_DOMAIN_NAME_SERVERS:
685 case DHCPConstants.OPT_BROADCAST_ADDRESS:
686 case DHCPConstants.OPT_LEASE_TIME:
687 case DHCPConstants.OPT_RENEWAL_TIME:
688 case DHCPConstants.OPT_REBINDING_TIME:
689 /* These values will be filled in setCommonOptions
690 * Setting these just to preserve order as
691 * specified in PARAMETER_REQUEST_LIST.
693 reply.setOptionInt(element, 0);
695 case DHCPConstants.OPT_DOMAIN_NAME:
696 reply.setOptionString(element, " ");
698 case DHCPConstants.OPT_CLASSLESS_ROUTE:
699 setOptionClasslessRoute(reply, dhcpInfo);
702 LOG.trace("DHCP Option code {} not supported yet", element);
708 private void setOptionClasslessRoute(DHCP reply, DhcpInfo dhcpInfo) {
709 List<HostRoutes> hostRoutes = dhcpInfo.getHostRoutes();
710 if (hostRoutes == null) {
711 //we can't set this option, so return
714 ByteArrayOutputStream result = new ByteArrayOutputStream();
715 for (HostRoutes hostRoute : hostRoutes) {
716 if (hostRoute.getNexthop().getIpv4Address() == null
717 || hostRoute.getDestination().getIpv4Prefix() == null) {
718 // we only deal with IPv4 addresses
721 String router = hostRoute.getNexthop().getIpv4Address().getValue();
722 String dest = hostRoute.getDestination().getIpv4Prefix().getValue();
724 result.write(convertToClasslessRouteOption(dest, router));
725 } catch (IOException | NullPointerException e) {
726 LOG.trace("Exception {}", e.getMessage());
729 if (result.size() > 0) {
730 reply.setOptionBytes(DHCPConstants.OPT_CLASSLESS_ROUTE , result.toByteArray());
735 protected byte[] convertToClasslessRouteOption(String dest, String router) {
736 ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
737 if (dest == null || router == null) {
743 String[] parts = dest.split("/");
744 if (parts.length < 2) {
747 prefix = Short.valueOf(parts[1]);
750 byteArray.write(prefix.byteValue());
751 SubnetUtils util = new SubnetUtils(dest);
752 SubnetInfo info = util.getInfo();
753 String strNetAddr = info.getNetworkAddress();
755 byte[] netAddr = InetAddress.getByName(strNetAddr).getAddress();
756 //Strip any trailing 0s from netAddr
757 for (int i = 0; i < netAddr.length;i++) {
758 if (netAddr[i] != 0) {
759 byteArray.write(netAddr,i,1);
762 byteArray.write(InetAddress.getByName(router).getAddress());
763 } catch (IOException e) {
766 return byteArray.toByteArray();
769 private boolean isPktInReasonSendtoCtrl(Class<? extends PacketInReason> pktInReason) {
770 return pktInReason == SendToController.class;
773 private String getInterfaceNameFromTag(long portTag) {
774 String interfaceName = null;
775 GetInterfaceFromIfIndexInput input =
776 new GetInterfaceFromIfIndexInputBuilder().setIfIndex((int) portTag).build();
777 Future<RpcResult<GetInterfaceFromIfIndexOutput>> futureOutput =
778 interfaceManagerRpc.getInterfaceFromIfIndex(input);
780 GetInterfaceFromIfIndexOutput output = futureOutput.get().getResult();
781 interfaceName = output.getInterfaceName();
782 } catch (InterruptedException | ExecutionException e) {
783 LOG.error("Error while retrieving the interfaceName from tag using getInterfaceFromIfIndex RPC");
785 LOG.trace("Returning interfaceName {} for tag {} form getInterfaceNameFromTag", interfaceName, portTag);
786 return interfaceName;
789 private List<Action> getEgressAction(String interfaceName, BigInteger tunnelId) {
791 if (interfaceManager.isItmDirectTunnelsEnabled() && tunnelId != null) {
792 GetEgressActionsForTunnelInputBuilder egressAction =
793 new GetEgressActionsForTunnelInputBuilder().setIntfName(interfaceName);
794 egressAction.setTunnelKey(tunnelId.longValue());
795 RpcResult<GetEgressActionsForTunnelOutput> rpcResult =
796 itmRpcService.getEgressActionsForTunnel(egressAction.build()).get();
797 if (!rpcResult.isSuccessful()) {
798 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
799 interfaceName, rpcResult.getErrors());
801 return rpcResult.getResult().getAction();
804 GetEgressActionsForInterfaceInputBuilder egressAction =
805 new GetEgressActionsForInterfaceInputBuilder().setIntfName(interfaceName);
806 if (tunnelId != null) {
807 egressAction.setTunnelKey(tunnelId.longValue());
809 Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
810 interfaceManagerRpc.getEgressActionsForInterface(egressAction.build());
811 RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
812 if (!rpcResult.isSuccessful()) {
813 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
814 interfaceName, rpcResult.getErrors());
816 return rpcResult.getResult().getAction();
819 } catch (InterruptedException | ExecutionException e) {
820 LOG.warn("Exception when egress actions for interface {}", interfaceName, e);
822 return Collections.emptyList();