2 * Copyright © 2015, 2017 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.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.annotation.Nonnull;
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.opendaylight.controller.md.sal.binding.api.DataBroker;
27 import org.opendaylight.genius.interfacemanager.globals.InterfaceInfo;
28 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
29 import org.opendaylight.genius.mdsalutil.MDSALUtil;
30 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
31 import org.opendaylight.genius.mdsalutil.NwConstants;
32 import org.opendaylight.genius.mdsalutil.packet.Ethernet;
33 import org.opendaylight.genius.mdsalutil.packet.IEEE8021Q;
34 import org.opendaylight.genius.mdsalutil.packet.IPProtocols;
35 import org.opendaylight.genius.mdsalutil.packet.IPv4;
36 import org.opendaylight.genius.mdsalutil.packet.UDP;
37 import org.opendaylight.infrautils.utils.concurrent.JdkFutures;
38 import org.opendaylight.netvirt.dhcpservice.api.DHCP;
39 import org.opendaylight.netvirt.dhcpservice.api.DHCPConstants;
40 import org.opendaylight.netvirt.dhcpservice.api.DHCPUtils;
41 import org.opendaylight.netvirt.dhcpservice.api.DhcpMConstants;
42 import org.opendaylight.openflowplugin.libraries.liblldp.EtherTypes;
43 import org.opendaylight.openflowplugin.libraries.liblldp.NetUtils;
44 import org.opendaylight.openflowplugin.libraries.liblldp.PacketException;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceInputBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceOutput;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInput;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInputBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexOutput;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp_allocation_pool.rev161214.dhcp_allocation_pool.network.AllocationPool;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnet.attributes.HostRoutes;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketInReason;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.SendToController;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.api.rev150710.subnet.dhcp.port.data.SubnetToDhcpPort;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.config.rev150710.DhcpserviceConfig;
66 import org.opendaylight.yangtools.yang.common.RpcResult;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
72 public class DhcpPktHandler implements PacketProcessingListener {
74 private static final Logger LOG = LoggerFactory.getLogger(DhcpPktHandler.class);
76 private final DhcpManager dhcpMgr;
77 private final OdlInterfaceRpcService interfaceManagerRpc;
78 private final PacketProcessingService pktService;
79 private final DhcpExternalTunnelManager dhcpExternalTunnelManager;
80 private final IInterfaceManager interfaceManager;
81 private final DhcpserviceConfig config;
82 private final DhcpAllocationPoolManager dhcpAllocationPoolMgr;
83 private final DataBroker broker;
86 public DhcpPktHandler(final DhcpManager dhcpManager,
87 final DhcpExternalTunnelManager dhcpExternalTunnelManager,
88 final OdlInterfaceRpcService interfaceManagerRpc,
89 final PacketProcessingService pktService,
90 final IInterfaceManager interfaceManager,
91 final DhcpserviceConfig config,
92 final DhcpAllocationPoolManager dhcpAllocationPoolMgr,
93 final DataBroker dataBroker) {
94 this.interfaceManagerRpc = interfaceManagerRpc;
95 this.pktService = pktService;
96 this.dhcpExternalTunnelManager = dhcpExternalTunnelManager;
97 this.dhcpMgr = dhcpManager;
98 this.interfaceManager = interfaceManager;
100 this.dhcpAllocationPoolMgr = dhcpAllocationPoolMgr;
101 this.broker = dataBroker;
104 //TODO: Handle this in a separate thread
106 public void onPacketReceived(PacketReceived packet) {
107 if (!config.isControllerDhcpEnabled()) {
110 Class<? extends PacketInReason> pktInReason = packet.getPacketInReason();
111 short tableId = packet.getTableId().getValue();
112 if ((tableId == NwConstants.DHCP_TABLE || tableId == NwConstants.DHCP_TABLE_EXTERNAL_TUNNEL)
113 && isPktInReasonSendtoCtrl(pktInReason)) {
114 byte[] inPayload = packet.getPayload();
115 Ethernet ethPkt = new Ethernet();
117 ethPkt.deserialize(inPayload, 0, inPayload.length * NetUtils.NumBitsInAByte);
118 } catch (PacketException e) {
119 LOG.warn("Failed to decode DHCP Packet.", e);
120 LOG.trace("Received packet {}", packet);
124 pktIn = getDhcpPktIn(ethPkt);
126 LOG.trace("DHCPPkt received: {}", pktIn);
127 LOG.trace("Received Packet: {}", packet);
128 BigInteger metadata = packet.getMatch().getMetadata().getMetadata();
129 long portTag = MetaDataUtil.getLportFromMetadata(metadata).intValue();
130 String macAddress = DHCPUtils.byteArrayToString(ethPkt.getSourceMACAddress());
131 BigInteger tunnelId =
132 packet.getMatch().getTunnel() == null ? null : packet.getMatch().getTunnel().getTunnelId();
133 String interfaceName = getInterfaceNameFromTag(portTag);
134 InterfaceInfo interfaceInfo =
135 interfaceManager.getInterfaceInfoFromOperationalDataStore(interfaceName);
136 if (interfaceInfo == null) {
137 LOG.error("Failed to get interface info for interface name {}", interfaceName);
141 if (tunnelId != null) {
142 port = dhcpExternalTunnelManager.readVniMacToPortCache(tunnelId, macAddress);
144 port = getNeutronPort(interfaceName);
146 Subnet subnet = getNeutronSubnet(port);
147 String serverMacAddress = interfaceInfo.getMacAddress();
148 String serverIp = null;
149 if (subnet != null) {
150 java.util.Optional<SubnetToDhcpPort> dhcpPortData = DhcpServiceUtils
151 .getSubnetDhcpPortData(broker, subnet.getUuid().getValue());
152 /* If enable_dhcp_service flag was enabled and an ODL network DHCP Port data was made available use
153 * the ports Fixed IP as server IP for DHCP communication.
155 if (dhcpPortData.isPresent()) {
156 serverIp = dhcpPortData.get().getPortFixedip();
157 serverMacAddress = dhcpPortData.get().getPortMacaddress();
159 // DHCP Neutron Port not found for this network
160 LOG.error("Neutron DHCP port is not available for the Subnet {} and port {}.", subnet.getUuid(),
165 DHCP replyPkt = handleDhcpPacket(pktIn, interfaceName, macAddress, port, subnet, serverIp);
166 if (replyPkt == null) {
167 LOG.warn("Unable to construct reply packet for interface name {}", interfaceName);
170 byte[] pktOut = getDhcpPacketOut(replyPkt, ethPkt, serverMacAddress);
171 sendPacketOut(pktOut, interfaceInfo.getDpId(), interfaceName, tunnelId);
176 private void sendPacketOut(byte[] pktOut, BigInteger dpnId, String interfaceName, BigInteger tunnelId) {
177 List<Action> action = getEgressAction(interfaceName, tunnelId);
178 TransmitPacketInput output = MDSALUtil.getPacketOut(action, pktOut, dpnId);
179 LOG.trace("Transmitting packet: {}", output);
180 JdkFutures.addErrorLogging(pktService.transmitPacket(output), LOG, "Transmit packet");
183 private DHCP handleDhcpPacket(DHCP dhcpPkt, String interfaceName, String macAddress, Port interfacePort,
184 Subnet subnet, String serverIp) {
185 LOG.trace("DHCP pkt rcvd {}", dhcpPkt);
186 byte msgType = dhcpPkt.getMsgType();
187 DhcpInfo dhcpInfo = null;
188 if (interfacePort != null) {
189 dhcpInfo = handleDhcpNeutronPacket(msgType, interfacePort, subnet, serverIp);
190 } else if (config.isDhcpDynamicAllocationPoolEnabled()) {
191 dhcpInfo = handleDhcpAllocationPoolPacket(msgType, dhcpPkt, interfaceName, macAddress);
194 if (dhcpInfo != null) {
195 if (msgType == DHCPConstants.MSG_DISCOVER) {
196 reply = getReplyToDiscover(dhcpPkt, dhcpInfo);
197 } else if (msgType == DHCPConstants.MSG_REQUEST) {
198 reply = getReplyToRequest(dhcpPkt, dhcpInfo);
205 private DhcpInfo handleDhcpNeutronPacket(byte msgType, Port port, Subnet subnet, String serverIp) {
206 if (msgType == DHCPConstants.MSG_DECLINE) {
207 LOG.trace("DHCPDECLINE received");
209 } else if (msgType == DHCPConstants.MSG_RELEASE) {
210 LOG.trace("DHCPRELEASE received");
213 return getDhcpInfoFromNeutronPort(port, subnet, serverIp);
217 private DhcpInfo handleDhcpAllocationPoolPacket(byte msgType, DHCP dhcpPkt, String interfaceName,
219 String networkId = dhcpAllocationPoolMgr.getNetworkByPort(interfaceName);
220 AllocationPool pool = networkId != null ? dhcpAllocationPoolMgr.getAllocationPoolByNetwork(networkId)
222 if (networkId == null || pool == null) {
223 LOG.warn("No Dhcp Allocation Pool was found for interface: {}", interfaceName);
227 case DHCPConstants.MSG_DISCOVER:
228 case DHCPConstants.MSG_REQUEST:
229 // FIXME: requested ip is currently ignored in moment of allocation
230 return getDhcpInfoFromAllocationPool(networkId, pool, macAddress);
231 case DHCPConstants.MSG_RELEASE:
232 dhcpAllocationPoolMgr.releaseIpAllocation(networkId, pool, macAddress);
240 private DhcpInfo getDhcpInfoFromNeutronPort(Port port, Subnet subnet, String serverIp) {
241 DhcpInfo dhcpInfo = getDhcpInfo(port, subnet, serverIp);
242 LOG.trace("NeutronPort: {} \n NeutronSubnet: {}, dhcpInfo{}", port, subnet, dhcpInfo);
246 private DhcpInfo getDhcpInfoFromAllocationPool(String networkId, AllocationPool pool, String macAddress) {
247 IpAddress allocatedIp = dhcpAllocationPoolMgr.getIpAllocation(networkId, pool, macAddress);
248 DhcpInfo dhcpInfo = getApDhcpInfo(pool, allocatedIp);
249 LOG.info("AllocationPoolNetwork: {}, dhcpInfo {}", networkId, dhcpInfo);
253 private DhcpInfo getDhcpInfo(Port port, Subnet subnet, String serverIp) {
254 DhcpInfo dhcpInfo = null;
255 if (port != null && subnet != null) {
256 String clientIp = getIpv4Address(port);
257 List<IpAddress> dnsServers = subnet.getDnsNameservers();
258 dhcpInfo = new DhcpInfo();
259 if (isIpv4Address(subnet.getGatewayIp())) {
260 dhcpInfo.setGatewayIp(subnet.getGatewayIp().getIpv4Address().getValue());
262 if (clientIp != null && serverIp != null) {
263 dhcpInfo.setClientIp(clientIp).setServerIp(serverIp)
264 .setCidr(String.valueOf(subnet.getCidr().getValue())).setHostRoutes(subnet.getHostRoutes())
265 .setDnsServersIpAddrs(dnsServers);
271 private DhcpInfo getApDhcpInfo(AllocationPool ap, IpAddress allocatedIp) {
272 DhcpInfo dhcpInfo = null;
274 String clientIp = String.valueOf(allocatedIp.getValue());
275 String serverIp = String.valueOf(ap.getGateway().getValue());
276 List<IpAddress> dnsServers = ap.getDnsServers();
277 dhcpInfo = new DhcpInfo();
278 dhcpInfo.setClientIp(clientIp).setServerIp(serverIp).setCidr(String.valueOf(ap.getSubnet().getValue()))
279 .setHostRoutes(Collections.emptyList()).setDnsServersIpAddrs(dnsServers).setGatewayIp(serverIp);
285 * getIpv4Address and isIpv4Address
286 * Many other modules use/need similar methods. Should
287 * be refactored to a common NeutronUtils module. *
289 private String getIpv4Address(Port port) {
291 for (FixedIps fixedIp : port.getFixedIps()) {
292 if (isIpv4Address(fixedIp.getIpAddress())) {
293 return fixedIp.getIpAddress().getIpv4Address().getValue();
296 LOG.error("Could not find ipv4 address for port {}", port);
300 private boolean isIpv4Address(IpAddress ip) {
301 return ip != null && ip.getIpv4Address() != null;
304 private Subnet getNeutronSubnet(Port port) {
305 return dhcpMgr.getNeutronSubnet(port);
308 private Port getNeutronPort(String interfaceName) {
309 return dhcpMgr.getNeutronPort(interfaceName);
312 private DHCP getDhcpPktIn(Ethernet actualEthernetPacket) {
313 Ethernet ethPkt = actualEthernetPacket;
314 if (ethPkt.getEtherType() == (short)NwConstants.ETHTYPE_802_1Q) {
315 ethPkt = (Ethernet)ethPkt.getPayload();
317 // Currently only IPv4 is supported
318 if (ethPkt.getPayload() instanceof IPv4) {
319 IPv4 ipPkt = (IPv4) ethPkt.getPayload();
320 if (ipPkt.getPayload() instanceof UDP) {
321 UDP udpPkt = (UDP) ipPkt.getPayload();
322 if (udpPkt.getSourcePort() == DhcpMConstants.DHCP_CLIENT_PORT
323 && udpPkt.getDestinationPort() == DhcpMConstants.DHCP_SERVER_PORT) {
324 LOG.trace("Matched DHCP_CLIENT_PORT and DHCP_SERVER_PORT");
325 byte[] rawDhcpPayload = udpPkt.getRawPayload();
326 DHCP reply = new DHCP();
328 reply.deserialize(rawDhcpPayload, 0, rawDhcpPayload.length);
329 } catch (PacketException e) {
330 LOG.warn("Failed to deserialize DHCP pkt");
331 LOG.trace("Reason for failure", e);
341 DHCP getReplyToDiscover(DHCP dhcpPkt, DhcpInfo dhcpInfo) {
342 DHCP reply = new DHCP();
343 reply.setOp(DHCPConstants.BOOTREPLY);
344 reply.setHtype(dhcpPkt.getHtype());
345 reply.setHlen(dhcpPkt.getHlen());
346 reply.setHops((byte) 0);
347 reply.setXid(dhcpPkt.getXid());
348 reply.setSecs((short) 0);
350 reply.setYiaddr(dhcpInfo.getClientIp());
351 reply.setSiaddr(dhcpInfo.getServerIp());
353 reply.setFlags(dhcpPkt.getFlags());
354 reply.setGiaddr(dhcpPkt.getGiaddr());
355 reply.setChaddr(dhcpPkt.getChaddr());
357 reply.setMsgType(DHCPConstants.MSG_OFFER);
358 if (dhcpPkt.containsOption(DHCPConstants.OPT_PARAMETER_REQUEST_LIST)) {
359 setParameterListOptions(dhcpPkt, reply, dhcpInfo);
361 setCommonOptions(reply, dhcpInfo);
365 DHCP getReplyToRequest(DHCP dhcpPkt, DhcpInfo dhcpInfo) {
366 boolean sendAck = false;
367 byte[] requestedIp = null;
368 DHCP reply = new DHCP();
369 reply.setOp(DHCPConstants.BOOTREPLY);
370 reply.setHtype(dhcpPkt.getHtype());
371 reply.setHlen(dhcpPkt.getHlen());
372 reply.setHops((byte) 0);
373 reply.setXid(dhcpPkt.getXid());
374 reply.setSecs((short) 0);
376 reply.setFlags(dhcpPkt.getFlags());
377 reply.setGiaddr(dhcpPkt.getGiaddr());
378 reply.setChaddr(dhcpPkt.getChaddr());
381 allocatedIp = DHCPUtils.strAddrToByteArray(dhcpInfo.getClientIp());
382 } catch (UnknownHostException e) {
383 LOG.debug("strAddrToByteArray", e);
387 if (Arrays.equals(allocatedIp, dhcpPkt.getCiaddr())) {
388 //This means a renew request
391 requestedIp = dhcpPkt.getOptionBytes(DHCPConstants.OPT_REQUESTED_ADDRESS);
392 sendAck = Arrays.equals(allocatedIp, requestedIp);
396 reply.setCiaddr(dhcpPkt.getCiaddr());
397 reply.setYiaddr(dhcpInfo.getClientIp());
398 reply.setSiaddr(dhcpInfo.getServerIp());
399 reply.setMsgType(DHCPConstants.MSG_ACK);
400 if (dhcpPkt.containsOption(DHCPConstants.OPT_PARAMETER_REQUEST_LIST)) {
401 setParameterListOptions(dhcpPkt, reply, dhcpInfo);
404 reply.setMsgType(DHCPConstants.MSG_NAK);
406 setCommonOptions(reply, dhcpInfo);
410 // "Consider returning a zero length array rather than null" - the eventual user of the returned byte[] likely
411 // expects null and it's unclear what the behavior would be if empty array was returned.
412 @SuppressFBWarnings("PZLA_PREFER_ZERO_LENGTH_ARRAYS")
413 protected byte[] getDhcpPacketOut(DHCP reply, Ethernet etherPkt, String phyAddrees) {
416 * DECLINE or RELEASE don't result in reply packet
420 LOG.trace("Sending DHCP Pkt {}", reply);
421 InetAddress serverIp = reply.getOptionInetAddr(DHCPConstants.OPT_SERVER_IDENTIFIER);
423 UDP udpPkt = new UDP();
426 rawPkt = reply.serialize();
427 } catch (PacketException e) {
428 LOG.warn("Failed to serialize packet", e);
431 udpPkt.setRawPayload(rawPkt);
432 udpPkt.setDestinationPort(DhcpMConstants.DHCP_CLIENT_PORT);
433 udpPkt.setSourcePort(DhcpMConstants.DHCP_SERVER_PORT);
434 udpPkt.setLength((short) (rawPkt.length + 8));
437 rawPkt = udpPkt.serialize();
438 } catch (PacketException e) {
439 LOG.warn("Failed to serialize packet", e);
443 boolean computeUdpChecksum = true;
444 if (computeUdpChecksum) {
445 checkSum = computeChecksum(rawPkt, serverIp.getAddress(),
446 NetUtils.intToByteArray4(DhcpMConstants.BCAST_IP));
448 udpPkt.setChecksum(checkSum);
449 IPv4 ip4Reply = new IPv4();
450 ip4Reply.setPayload(udpPkt);
451 ip4Reply.setProtocol(IPProtocols.UDP.byteValue());
452 ip4Reply.setSourceAddress(serverIp);
453 ip4Reply.setDestinationAddress(DhcpMConstants.BCAST_IP);
454 ip4Reply.setTotalLength((short) (rawPkt.length + 20));
455 ip4Reply.setTtl((byte) 32);
456 // create Ethernet Frame
457 Ethernet ether = new Ethernet();
458 if (etherPkt.getEtherType() == (short)NwConstants.ETHTYPE_802_1Q) {
459 IEEE8021Q vlanPacket = (IEEE8021Q) etherPkt.getPayload();
460 IEEE8021Q vlanTagged = new IEEE8021Q();
461 vlanTagged.setCFI(vlanPacket.getCfi());
462 vlanTagged.setPriority(vlanPacket.getPriority());
463 vlanTagged.setVlanId(vlanPacket.getVlanId());
464 vlanTagged.setPayload(ip4Reply);
465 vlanTagged.setEtherType(EtherTypes.IPv4.shortValue());
466 ether.setPayload(vlanTagged);
467 ether.setEtherType((short) NwConstants.ETHTYPE_802_1Q);
469 ether.setEtherType(EtherTypes.IPv4.shortValue());
470 ether.setPayload(ip4Reply);
472 ether.setSourceMACAddress(getServerMacAddress(phyAddrees));
473 ether.setDestinationMACAddress(etherPkt.getSourceMACAddress());
476 rawPkt = ether.serialize();
477 } catch (PacketException e) {
478 LOG.warn("Failed to serialize ethernet reply",e);
484 private byte[] getServerMacAddress(String phyAddress) {
485 // Should we return ControllerMac instead?
486 return DHCPUtils.strMacAddrtoByteArray(phyAddress);
489 public short computeChecksum(byte[] inData, byte[] srcAddr, byte[] destAddr) {
495 for (index = 0; index < inData.length - 1; index = index + 2) {
496 // Skip, if the current bytes are checkSum bytes
497 wordData = (inData[index] << 8 & 0xFF00) + (inData[index + 1] & 0xFF);
498 sum = sum + wordData;
501 if (index < inData.length) {
502 wordData = (inData[index] << 8 & 0xFF00) + (0 & 0xFF);
503 sum = sum + wordData;
506 for (index = 0; index < 4; index = index + 2) {
507 wordData = (srcAddr[index] << 8 & 0xFF00) + (srcAddr[index + 1] & 0xFF);
508 sum = sum + wordData;
511 for (index = 0; index < 4; index = index + 2) {
512 wordData = (destAddr[index] << 8 & 0xFF00) + (destAddr[index + 1] & 0xFF);
513 sum = sum + wordData;
515 sum = sum + 17 + inData.length;
517 while (sum >> 16 != 0) {
519 sum = (sum & 0xFFFF) + carry;
521 short checkSum = (short) ~((short) sum & 0xFFFF);
523 checkSum = (short)0xffff;
528 private void setCommonOptions(DHCP pkt, DhcpInfo dhcpInfo) {
529 String serverIp = dhcpInfo.getServerIp();
530 if (pkt.getMsgType() != DHCPConstants.MSG_NAK) {
531 setNonNakOptions(pkt, dhcpInfo);
535 * setParameterListOptions may have initialized some of these
536 * options to maintain order. If we can't fill them, unset to avoid
537 * sending wrong information in reply.
539 if (serverIp != null) {
540 pkt.setOptionInetAddr(DHCPConstants.OPT_SERVER_IDENTIFIER, serverIp);
542 pkt.unsetOption(DHCPConstants.OPT_SERVER_IDENTIFIER);
544 } catch (UnknownHostException e) {
545 LOG.warn("Failed to set option", e);
549 private void setNonNakOptions(DHCP pkt, DhcpInfo dhcpInfo) {
550 pkt.setOptionInt(DHCPConstants.OPT_LEASE_TIME, dhcpMgr.getDhcpLeaseTime());
551 if (dhcpMgr.getDhcpDefDomain() != null) {
552 pkt.setOptionString(DHCPConstants.OPT_DOMAIN_NAME, dhcpMgr.getDhcpDefDomain());
554 if (dhcpMgr.getDhcpLeaseTime() > 0) {
555 pkt.setOptionInt(DHCPConstants.OPT_REBINDING_TIME, dhcpMgr.getDhcpRebindingTime());
556 pkt.setOptionInt(DHCPConstants.OPT_RENEWAL_TIME, dhcpMgr.getDhcpRenewalTime());
558 SubnetUtils util = null;
559 SubnetInfo info = null;
560 util = new SubnetUtils(dhcpInfo.getCidr());
561 info = util.getInfo();
562 String gwIp = dhcpInfo.getGatewayIp();
563 List<String> dnServers = dhcpInfo.getDnsServers();
566 * setParameterListOptions may have initialized some of these
567 * options to maintain order. If we can't fill them, unset to avoid
568 * sending wrong information in reply.
571 pkt.setOptionInetAddr(DHCPConstants.OPT_ROUTERS, gwIp);
573 pkt.unsetOption(DHCPConstants.OPT_ROUTERS);
576 pkt.setOptionInetAddr(DHCPConstants.OPT_SUBNET_MASK, info.getNetmask());
577 pkt.setOptionInetAddr(DHCPConstants.OPT_BROADCAST_ADDRESS, info.getBroadcastAddress());
579 pkt.unsetOption(DHCPConstants.OPT_SUBNET_MASK);
580 pkt.unsetOption(DHCPConstants.OPT_BROADCAST_ADDRESS);
582 if (dnServers != null && dnServers.size() > 0) {
583 pkt.setOptionStrAddrs(DHCPConstants.OPT_DOMAIN_NAME_SERVERS, dnServers);
585 pkt.unsetOption(DHCPConstants.OPT_DOMAIN_NAME_SERVERS);
587 } catch (UnknownHostException e) {
588 // TODO Auto-generated catch block
589 LOG.warn("Failed to set option", e);
593 private void setParameterListOptions(DHCP req, DHCP reply, DhcpInfo dhcpInfo) {
594 byte[] paramList = req.getOptionBytes(DHCPConstants.OPT_PARAMETER_REQUEST_LIST);
595 for (byte element : paramList) {
597 case DHCPConstants.OPT_SUBNET_MASK:
598 case DHCPConstants.OPT_ROUTERS:
599 case DHCPConstants.OPT_SERVER_IDENTIFIER:
600 case DHCPConstants.OPT_DOMAIN_NAME_SERVERS:
601 case DHCPConstants.OPT_BROADCAST_ADDRESS:
602 case DHCPConstants.OPT_LEASE_TIME:
603 case DHCPConstants.OPT_RENEWAL_TIME:
604 case DHCPConstants.OPT_REBINDING_TIME:
605 /* These values will be filled in setCommonOptions
606 * Setting these just to preserve order as
607 * specified in PARAMETER_REQUEST_LIST.
609 reply.setOptionInt(element, 0);
611 case DHCPConstants.OPT_DOMAIN_NAME:
612 reply.setOptionString(element, " ");
614 case DHCPConstants.OPT_CLASSLESS_ROUTE:
615 setOptionClasslessRoute(reply, dhcpInfo);
618 LOG.trace("DHCP Option code {} not supported yet", element);
624 private void setOptionClasslessRoute(DHCP reply, DhcpInfo dhcpInfo) {
625 List<HostRoutes> hostRoutes = dhcpInfo.getHostRoutes();
626 if (hostRoutes == null) {
627 //we can't set this option, so return
630 ByteArrayOutputStream result = new ByteArrayOutputStream();
631 for (HostRoutes hostRoute : hostRoutes) {
632 if (hostRoute.getNexthop().getIpv4Address() == null
633 || hostRoute.getDestination().getIpv4Prefix() == null) {
634 // we only deal with IPv4 addresses
637 String router = hostRoute.getNexthop().getIpv4Address().getValue();
638 String dest = hostRoute.getDestination().getIpv4Prefix().getValue();
640 result.write(convertToClasslessRouteOption(dest, router));
641 } catch (IOException | NullPointerException e) {
642 LOG.trace("Exception {}", e.getMessage());
645 if (result.size() > 0) {
646 reply.setOptionBytes(DHCPConstants.OPT_CLASSLESS_ROUTE , result.toByteArray());
651 protected byte[] convertToClasslessRouteOption(String dest, String router) {
652 ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
653 if (dest == null || router == null) {
659 String[] parts = dest.split("/");
660 if (parts.length < 2) {
663 prefix = Short.valueOf(parts[1]);
666 byteArray.write(prefix.byteValue());
667 SubnetUtils util = new SubnetUtils(dest);
668 SubnetInfo info = util.getInfo();
669 String strNetAddr = info.getNetworkAddress();
671 byte[] netAddr = InetAddress.getByName(strNetAddr).getAddress();
672 //Strip any trailing 0s from netAddr
673 for (int i = 0; i < netAddr.length;i++) {
674 if (netAddr[i] != 0) {
675 byteArray.write(netAddr,i,1);
678 byteArray.write(InetAddress.getByName(router).getAddress());
679 } catch (IOException e) {
682 return byteArray.toByteArray();
685 private boolean isPktInReasonSendtoCtrl(Class<? extends PacketInReason> pktInReason) {
686 return pktInReason == SendToController.class;
689 private String getInterfaceNameFromTag(long portTag) {
690 String interfaceName = null;
691 GetInterfaceFromIfIndexInput input =
692 new GetInterfaceFromIfIndexInputBuilder().setIfIndex((int) portTag).build();
693 Future<RpcResult<GetInterfaceFromIfIndexOutput>> futureOutput =
694 interfaceManagerRpc.getInterfaceFromIfIndex(input);
696 GetInterfaceFromIfIndexOutput output = futureOutput.get().getResult();
697 interfaceName = output.getInterfaceName();
698 } catch (InterruptedException | ExecutionException e) {
699 LOG.error("Error while retrieving the interfaceName from tag using getInterfaceFromIfIndex RPC");
701 LOG.trace("Returning interfaceName {} for tag {} form getInterfaceNameFromTag", interfaceName, portTag);
702 return interfaceName;
705 private List<Action> getEgressAction(String interfaceName, BigInteger tunnelId) {
706 List<Action> actions = null;
708 GetEgressActionsForInterfaceInputBuilder egressAction =
709 new GetEgressActionsForInterfaceInputBuilder().setIntfName(interfaceName);
710 if (tunnelId != null) {
711 egressAction.setTunnelKey(tunnelId.longValue());
713 Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
714 interfaceManagerRpc.getEgressActionsForInterface(egressAction.build());
715 RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
716 if (!rpcResult.isSuccessful()) {
717 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
718 interfaceName, rpcResult.getErrors());
720 actions = rpcResult.getResult().getAction();
722 } catch (InterruptedException | ExecutionException e) {
723 LOG.warn("Exception when egress actions for interface {}", interfaceName, e);