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.genius.interfacemanager.globals.InterfaceInfo;
29 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
30 import org.opendaylight.genius.mdsalutil.MDSALUtil;
31 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
32 import org.opendaylight.genius.mdsalutil.NwConstants;
33 import org.opendaylight.genius.mdsalutil.packet.Ethernet;
34 import org.opendaylight.genius.mdsalutil.packet.IEEE8021Q;
35 import org.opendaylight.genius.mdsalutil.packet.IPProtocols;
36 import org.opendaylight.genius.mdsalutil.packet.IPv4;
37 import org.opendaylight.genius.mdsalutil.packet.UDP;
38 import org.opendaylight.infrautils.utils.concurrent.JdkFutures;
39 import org.opendaylight.netvirt.dhcpservice.api.DHCP;
40 import org.opendaylight.netvirt.dhcpservice.api.DHCPConstants;
41 import org.opendaylight.netvirt.dhcpservice.api.DHCPUtils;
42 import org.opendaylight.netvirt.dhcpservice.api.DhcpMConstants;
43 import org.opendaylight.openflowplugin.libraries.liblldp.EtherTypes;
44 import org.opendaylight.openflowplugin.libraries.liblldp.NetUtils;
45 import org.opendaylight.openflowplugin.libraries.liblldp.PacketException;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceInputBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceOutput;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInput;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInputBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexOutput;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.dhcp_allocation_pool.rev161214.dhcp_allocation_pool.network.AllocationPool;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnet.attributes.HostRoutes;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketInReason;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.SendToController;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.api.rev150710.subnet.dhcp.port.data.SubnetToDhcpPort;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.config.rev150710.DhcpserviceConfig;
67 import org.opendaylight.yangtools.yang.common.RpcResult;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
73 public class DhcpPktHandler implements PacketProcessingListener {
75 private static final Logger LOG = LoggerFactory.getLogger(DhcpPktHandler.class);
77 private final DhcpManager dhcpMgr;
78 private final OdlInterfaceRpcService interfaceManagerRpc;
79 private final PacketProcessingService pktService;
80 private final DhcpExternalTunnelManager dhcpExternalTunnelManager;
81 private final IInterfaceManager interfaceManager;
82 private final DhcpserviceConfig config;
83 private final DhcpAllocationPoolManager dhcpAllocationPoolMgr;
84 private final DataBroker broker;
87 public DhcpPktHandler(final DhcpManager dhcpManager,
88 final DhcpExternalTunnelManager dhcpExternalTunnelManager,
89 final OdlInterfaceRpcService interfaceManagerRpc,
90 final PacketProcessingService pktService,
91 final IInterfaceManager interfaceManager,
92 final DhcpserviceConfig config,
93 final DhcpAllocationPoolManager dhcpAllocationPoolMgr,
94 final DataBroker dataBroker) {
95 this.interfaceManagerRpc = interfaceManagerRpc;
96 this.pktService = pktService;
97 this.dhcpExternalTunnelManager = dhcpExternalTunnelManager;
98 this.dhcpMgr = dhcpManager;
99 this.interfaceManager = interfaceManager;
100 this.config = config;
101 this.dhcpAllocationPoolMgr = dhcpAllocationPoolMgr;
102 this.broker = dataBroker;
105 //TODO: Handle this in a separate thread
107 public void onPacketReceived(PacketReceived packet) {
108 if (!config.isControllerDhcpEnabled()) {
111 Class<? extends PacketInReason> pktInReason = packet.getPacketInReason();
112 short tableId = packet.getTableId().getValue();
113 if ((tableId == NwConstants.DHCP_TABLE || tableId == NwConstants.DHCP_TABLE_EXTERNAL_TUNNEL)
114 && isPktInReasonSendtoCtrl(pktInReason)) {
115 byte[] inPayload = packet.getPayload();
116 Ethernet ethPkt = new Ethernet();
118 ethPkt.deserialize(inPayload, 0, inPayload.length * NetUtils.NUM_BITS_IN_A_BYTE);
119 } catch (PacketException e) {
120 LOG.warn("Failed to decode DHCP Packet.", e);
121 LOG.trace("Received packet {}", packet);
125 pktIn = getDhcpPktIn(ethPkt);
127 LOG.trace("DHCPPkt received: {}", pktIn);
128 LOG.trace("Received Packet: {}", packet);
129 BigInteger metadata = packet.getMatch().getMetadata().getMetadata();
130 long portTag = MetaDataUtil.getLportFromMetadata(metadata).intValue();
131 String macAddress = DHCPUtils.byteArrayToString(ethPkt.getSourceMACAddress());
132 BigInteger tunnelId =
133 packet.getMatch().getTunnel() == null ? null : packet.getMatch().getTunnel().getTunnelId();
134 String interfaceName = getInterfaceNameFromTag(portTag);
135 InterfaceInfo interfaceInfo =
136 interfaceManager.getInterfaceInfoFromOperationalDataStore(interfaceName);
137 if (interfaceInfo == null) {
138 LOG.error("Failed to get interface info for interface name {}", interfaceName);
142 if (tunnelId != null) {
143 port = dhcpExternalTunnelManager.readVniMacToPortCache(tunnelId, macAddress);
145 port = getNeutronPort(interfaceName);
147 Subnet subnet = getNeutronSubnet(port);
148 String serverMacAddress = interfaceInfo.getMacAddress();
149 String serverIp = null;
150 if (subnet != null) {
151 java.util.Optional<SubnetToDhcpPort> dhcpPortData = DhcpServiceUtils
152 .getSubnetDhcpPortData(broker, subnet.getUuid().getValue());
153 /* If enable_dhcp_service flag was enabled and an ODL network DHCP Port data was made available use
154 * the ports Fixed IP as server IP for DHCP communication.
156 if (dhcpPortData.isPresent()) {
157 serverIp = dhcpPortData.get().getPortFixedip();
158 serverMacAddress = dhcpPortData.get().getPortMacaddress();
160 // DHCP Neutron Port not found for this network
161 LOG.error("Neutron DHCP port is not available for the Subnet {} and port {}.", subnet.getUuid(),
166 DHCP replyPkt = handleDhcpPacket(pktIn, interfaceName, macAddress, port, subnet, serverIp);
167 if (replyPkt == null) {
168 LOG.warn("Unable to construct reply packet for interface name {}", interfaceName);
171 byte[] pktOut = getDhcpPacketOut(replyPkt, ethPkt, serverMacAddress);
172 sendPacketOut(pktOut, interfaceInfo.getDpId(), interfaceName, tunnelId);
177 private void sendPacketOut(byte[] pktOut, BigInteger dpnId, String interfaceName, BigInteger tunnelId) {
178 List<Action> action = getEgressAction(interfaceName, tunnelId);
179 TransmitPacketInput output = MDSALUtil.getPacketOut(action, pktOut, dpnId);
180 LOG.trace("Transmitting packet: {}", output);
181 JdkFutures.addErrorLogging(pktService.transmitPacket(output), LOG, "Transmit packet");
184 private DHCP handleDhcpPacket(DHCP dhcpPkt, String interfaceName, String macAddress, Port interfacePort,
185 Subnet subnet, String serverIp) {
186 LOG.trace("DHCP pkt rcvd {}", dhcpPkt);
187 byte msgType = dhcpPkt.getMsgType();
188 DhcpInfo dhcpInfo = null;
189 if (interfacePort != null) {
190 dhcpInfo = handleDhcpNeutronPacket(msgType, interfacePort, subnet, serverIp);
191 } else if (config.isDhcpDynamicAllocationPoolEnabled()) {
192 dhcpInfo = handleDhcpAllocationPoolPacket(msgType, interfaceName, macAddress);
195 if (dhcpInfo != null) {
196 if (msgType == DHCPConstants.MSG_DISCOVER) {
197 reply = getReplyToDiscover(dhcpPkt, dhcpInfo);
198 } else if (msgType == DHCPConstants.MSG_REQUEST) {
199 reply = getReplyToRequest(dhcpPkt, dhcpInfo);
206 private DhcpInfo handleDhcpNeutronPacket(byte msgType, Port port, Subnet subnet, String serverIp) {
207 if (msgType == DHCPConstants.MSG_DECLINE) {
208 LOG.trace("DHCPDECLINE received");
210 } else if (msgType == DHCPConstants.MSG_RELEASE) {
211 LOG.trace("DHCPRELEASE received");
214 return getDhcpInfoFromNeutronPort(port, subnet, serverIp);
218 private DhcpInfo handleDhcpAllocationPoolPacket(byte msgType, String interfaceName, String macAddress) {
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 List<HostRoutes> subnetHostRoutes = new ArrayList<>(subnet.getHostRoutes().size());
264 for (HostRoutes hostRoute : subnet.getHostRoutes()) {
265 if (!String.valueOf(hostRoute.getNexthop().getValue()).equals(clientIp)) {
266 subnetHostRoutes.add(hostRoute);
269 dhcpInfo.setClientIp(clientIp).setServerIp(serverIp)
270 .setCidr(String.valueOf(subnet.getCidr().getValue())).setHostRoutes(subnetHostRoutes)
271 .setDnsServersIpAddrs(dnsServers);
277 private DhcpInfo getApDhcpInfo(AllocationPool ap, IpAddress allocatedIp) {
278 DhcpInfo dhcpInfo = null;
280 String clientIp = String.valueOf(allocatedIp.getValue());
281 String serverIp = String.valueOf(ap.getGateway().getValue());
282 List<IpAddress> dnsServers = ap.getDnsServers();
283 dhcpInfo = new DhcpInfo();
284 dhcpInfo.setClientIp(clientIp).setServerIp(serverIp).setCidr(String.valueOf(ap.getSubnet().getValue()))
285 .setHostRoutes(Collections.emptyList()).setDnsServersIpAddrs(dnsServers).setGatewayIp(serverIp);
291 * getIpv4Address and isIpv4Address
292 * Many other modules use/need similar methods. Should
293 * be refactored to a common NeutronUtils module. *
295 private String getIpv4Address(Port port) {
297 for (FixedIps fixedIp : port.getFixedIps()) {
298 if (isIpv4Address(fixedIp.getIpAddress())) {
299 return fixedIp.getIpAddress().getIpv4Address().getValue();
302 LOG.error("Could not find ipv4 address for port {}", port);
306 private boolean isIpv4Address(IpAddress ip) {
307 return ip != null && ip.getIpv4Address() != null;
310 private Subnet getNeutronSubnet(Port port) {
311 return dhcpMgr.getNeutronSubnet(port);
314 private Port getNeutronPort(String interfaceName) {
315 return dhcpMgr.getNeutronPort(interfaceName);
318 private DHCP getDhcpPktIn(Ethernet actualEthernetPacket) {
319 Ethernet ethPkt = actualEthernetPacket;
320 if (ethPkt.getEtherType() == (short)NwConstants.ETHTYPE_802_1Q) {
321 ethPkt = (Ethernet)ethPkt.getPayload();
323 // Currently only IPv4 is supported
324 if (ethPkt.getPayload() instanceof IPv4) {
325 IPv4 ipPkt = (IPv4) ethPkt.getPayload();
326 if (ipPkt.getPayload() instanceof UDP) {
327 UDP udpPkt = (UDP) ipPkt.getPayload();
328 if (udpPkt.getSourcePort() == DhcpMConstants.DHCP_CLIENT_PORT
329 && udpPkt.getDestinationPort() == DhcpMConstants.DHCP_SERVER_PORT) {
330 LOG.trace("Matched DHCP_CLIENT_PORT and DHCP_SERVER_PORT");
331 byte[] rawDhcpPayload = udpPkt.getRawPayload();
332 DHCP reply = new DHCP();
334 reply.deserialize(rawDhcpPayload, 0, rawDhcpPayload.length);
335 } catch (PacketException e) {
336 LOG.warn("Failed to deserialize DHCP pkt");
337 LOG.trace("Reason for failure", e);
347 DHCP getReplyToDiscover(DHCP dhcpPkt, DhcpInfo dhcpInfo) {
348 DHCP reply = new DHCP();
349 reply.setOp(DHCPConstants.BOOTREPLY);
350 reply.setHtype(dhcpPkt.getHtype());
351 reply.setHlen(dhcpPkt.getHlen());
352 reply.setHops((byte) 0);
353 reply.setXid(dhcpPkt.getXid());
354 reply.setSecs((short) 0);
356 reply.setYiaddr(dhcpInfo.getClientIp());
357 reply.setSiaddr(dhcpInfo.getServerIp());
359 reply.setFlags(dhcpPkt.getFlags());
360 reply.setGiaddr(dhcpPkt.getGiaddr());
361 reply.setChaddr(dhcpPkt.getChaddr());
363 reply.setMsgType(DHCPConstants.MSG_OFFER);
364 if (dhcpPkt.containsOption(DHCPConstants.OPT_PARAMETER_REQUEST_LIST)) {
365 setParameterListOptions(dhcpPkt, reply, dhcpInfo);
367 setCommonOptions(reply, dhcpInfo);
371 DHCP getReplyToRequest(DHCP dhcpPkt, DhcpInfo dhcpInfo) {
372 boolean sendAck = false;
373 byte[] requestedIp = null;
374 DHCP reply = new DHCP();
375 reply.setOp(DHCPConstants.BOOTREPLY);
376 reply.setHtype(dhcpPkt.getHtype());
377 reply.setHlen(dhcpPkt.getHlen());
378 reply.setHops((byte) 0);
379 reply.setXid(dhcpPkt.getXid());
380 reply.setSecs((short) 0);
382 reply.setFlags(dhcpPkt.getFlags());
383 reply.setGiaddr(dhcpPkt.getGiaddr());
384 reply.setChaddr(dhcpPkt.getChaddr());
387 allocatedIp = DHCPUtils.strAddrToByteArray(dhcpInfo.getClientIp());
388 } catch (UnknownHostException e) {
389 LOG.debug("strAddrToByteArray", e);
393 if (Arrays.equals(allocatedIp, dhcpPkt.getCiaddr())) {
394 //This means a renew request
397 requestedIp = dhcpPkt.getOptionBytes(DHCPConstants.OPT_REQUESTED_ADDRESS);
398 sendAck = Arrays.equals(allocatedIp, requestedIp);
402 reply.setCiaddr(dhcpPkt.getCiaddr());
403 reply.setYiaddr(dhcpInfo.getClientIp());
404 reply.setSiaddr(dhcpInfo.getServerIp());
405 reply.setMsgType(DHCPConstants.MSG_ACK);
406 if (dhcpPkt.containsOption(DHCPConstants.OPT_PARAMETER_REQUEST_LIST)) {
407 setParameterListOptions(dhcpPkt, reply, dhcpInfo);
410 reply.setMsgType(DHCPConstants.MSG_NAK);
412 setCommonOptions(reply, dhcpInfo);
416 // "Consider returning a zero length array rather than null" - the eventual user of the returned byte[] likely
417 // expects null and it's unclear what the behavior would be if empty array was returned.
418 @SuppressFBWarnings("PZLA_PREFER_ZERO_LENGTH_ARRAYS")
419 protected byte[] getDhcpPacketOut(DHCP reply, Ethernet etherPkt, String phyAddrees) {
422 * DECLINE or RELEASE don't result in reply packet
426 LOG.trace("Sending DHCP Pkt {}", reply);
427 InetAddress serverIp = reply.getOptionInetAddr(DHCPConstants.OPT_SERVER_IDENTIFIER);
429 UDP udpPkt = new UDP();
432 rawPkt = reply.serialize();
433 } catch (PacketException e) {
434 LOG.warn("Failed to serialize packet", e);
437 udpPkt.setRawPayload(rawPkt);
438 udpPkt.setDestinationPort(DhcpMConstants.DHCP_CLIENT_PORT);
439 udpPkt.setSourcePort(DhcpMConstants.DHCP_SERVER_PORT);
440 udpPkt.setLength((short) (rawPkt.length + 8));
443 rawPkt = udpPkt.serialize();
444 } catch (PacketException e) {
445 LOG.warn("Failed to serialize packet", e);
449 boolean computeUdpChecksum = true;
450 if (computeUdpChecksum) {
451 checkSum = computeChecksum(rawPkt, serverIp.getAddress(),
452 NetUtils.intToByteArray4(DhcpMConstants.BCAST_IP));
454 udpPkt.setChecksum(checkSum);
455 IPv4 ip4Reply = new IPv4();
456 ip4Reply.setPayload(udpPkt);
457 ip4Reply.setProtocol(IPProtocols.UDP.byteValue());
458 ip4Reply.setSourceAddress(serverIp);
459 ip4Reply.setDestinationAddress(DhcpMConstants.BCAST_IP);
460 ip4Reply.setTotalLength((short) (rawPkt.length + 20));
461 ip4Reply.setTtl((byte) 32);
462 // create Ethernet Frame
463 Ethernet ether = new Ethernet();
464 if (etherPkt.getEtherType() == (short)NwConstants.ETHTYPE_802_1Q) {
465 IEEE8021Q vlanPacket = (IEEE8021Q) etherPkt.getPayload();
466 IEEE8021Q vlanTagged = new IEEE8021Q();
467 vlanTagged.setCFI(vlanPacket.getCfi());
468 vlanTagged.setPriority(vlanPacket.getPriority());
469 vlanTagged.setVlanId(vlanPacket.getVlanId());
470 vlanTagged.setPayload(ip4Reply);
471 vlanTagged.setEtherType(EtherTypes.IPv4.shortValue());
472 ether.setPayload(vlanTagged);
473 ether.setEtherType((short) NwConstants.ETHTYPE_802_1Q);
475 ether.setEtherType(EtherTypes.IPv4.shortValue());
476 ether.setPayload(ip4Reply);
478 ether.setSourceMACAddress(getServerMacAddress(phyAddrees));
479 ether.setDestinationMACAddress(etherPkt.getSourceMACAddress());
482 rawPkt = ether.serialize();
483 } catch (PacketException e) {
484 LOG.warn("Failed to serialize ethernet reply",e);
490 private byte[] getServerMacAddress(String phyAddress) {
491 // Should we return ControllerMac instead?
492 return DHCPUtils.strMacAddrtoByteArray(phyAddress);
495 public short computeChecksum(byte[] inData, byte[] srcAddr, byte[] destAddr) {
501 for (index = 0; index < inData.length - 1; index = index + 2) {
502 // Skip, if the current bytes are checkSum bytes
503 wordData = (inData[index] << 8 & 0xFF00) + (inData[index + 1] & 0xFF);
504 sum = sum + wordData;
507 if (index < inData.length) {
508 wordData = (inData[index] << 8 & 0xFF00) + (0 & 0xFF);
509 sum = sum + wordData;
512 for (index = 0; index < 4; index = index + 2) {
513 wordData = (srcAddr[index] << 8 & 0xFF00) + (srcAddr[index + 1] & 0xFF);
514 sum = sum + wordData;
517 for (index = 0; index < 4; index = index + 2) {
518 wordData = (destAddr[index] << 8 & 0xFF00) + (destAddr[index + 1] & 0xFF);
519 sum = sum + wordData;
521 sum = sum + 17 + inData.length;
523 while (sum >> 16 != 0) {
525 sum = (sum & 0xFFFF) + carry;
527 short checkSum = (short) ~((short) sum & 0xFFFF);
529 checkSum = (short)0xffff;
534 private void setCommonOptions(DHCP pkt, DhcpInfo dhcpInfo) {
535 String serverIp = dhcpInfo.getServerIp();
536 if (pkt.getMsgType() != DHCPConstants.MSG_NAK) {
537 setNonNakOptions(pkt, dhcpInfo);
541 * setParameterListOptions may have initialized some of these
542 * options to maintain order. If we can't fill them, unset to avoid
543 * sending wrong information in reply.
545 if (serverIp != null) {
546 pkt.setOptionInetAddr(DHCPConstants.OPT_SERVER_IDENTIFIER, serverIp);
548 pkt.unsetOption(DHCPConstants.OPT_SERVER_IDENTIFIER);
550 } catch (UnknownHostException e) {
551 LOG.warn("Failed to set option", e);
555 private void setNonNakOptions(DHCP pkt, DhcpInfo dhcpInfo) {
556 pkt.setOptionInt(DHCPConstants.OPT_LEASE_TIME, dhcpMgr.getDhcpLeaseTime());
557 if (dhcpMgr.getDhcpDefDomain() != null) {
558 pkt.setOptionString(DHCPConstants.OPT_DOMAIN_NAME, dhcpMgr.getDhcpDefDomain());
560 if (dhcpMgr.getDhcpLeaseTime() > 0) {
561 pkt.setOptionInt(DHCPConstants.OPT_REBINDING_TIME, dhcpMgr.getDhcpRebindingTime());
562 pkt.setOptionInt(DHCPConstants.OPT_RENEWAL_TIME, dhcpMgr.getDhcpRenewalTime());
564 SubnetUtils util = null;
565 SubnetInfo info = null;
566 util = new SubnetUtils(dhcpInfo.getCidr());
567 info = util.getInfo();
568 String gwIp = dhcpInfo.getGatewayIp();
569 List<String> dnServers = dhcpInfo.getDnsServers();
572 * setParameterListOptions may have initialized some of these
573 * options to maintain order. If we can't fill them, unset to avoid
574 * sending wrong information in reply.
577 pkt.setOptionInetAddr(DHCPConstants.OPT_ROUTERS, gwIp);
579 pkt.unsetOption(DHCPConstants.OPT_ROUTERS);
582 pkt.setOptionInetAddr(DHCPConstants.OPT_SUBNET_MASK, info.getNetmask());
583 pkt.setOptionInetAddr(DHCPConstants.OPT_BROADCAST_ADDRESS, info.getBroadcastAddress());
585 pkt.unsetOption(DHCPConstants.OPT_SUBNET_MASK);
586 pkt.unsetOption(DHCPConstants.OPT_BROADCAST_ADDRESS);
588 if (dnServers != null && dnServers.size() > 0) {
589 pkt.setOptionStrAddrs(DHCPConstants.OPT_DOMAIN_NAME_SERVERS, dnServers);
591 pkt.unsetOption(DHCPConstants.OPT_DOMAIN_NAME_SERVERS);
593 } catch (UnknownHostException e) {
594 // TODO Auto-generated catch block
595 LOG.warn("Failed to set option", e);
599 private void setParameterListOptions(DHCP req, DHCP reply, DhcpInfo dhcpInfo) {
600 byte[] paramList = req.getOptionBytes(DHCPConstants.OPT_PARAMETER_REQUEST_LIST);
601 for (byte element : paramList) {
603 case DHCPConstants.OPT_SUBNET_MASK:
604 case DHCPConstants.OPT_ROUTERS:
605 case DHCPConstants.OPT_SERVER_IDENTIFIER:
606 case DHCPConstants.OPT_DOMAIN_NAME_SERVERS:
607 case DHCPConstants.OPT_BROADCAST_ADDRESS:
608 case DHCPConstants.OPT_LEASE_TIME:
609 case DHCPConstants.OPT_RENEWAL_TIME:
610 case DHCPConstants.OPT_REBINDING_TIME:
611 /* These values will be filled in setCommonOptions
612 * Setting these just to preserve order as
613 * specified in PARAMETER_REQUEST_LIST.
615 reply.setOptionInt(element, 0);
617 case DHCPConstants.OPT_DOMAIN_NAME:
618 reply.setOptionString(element, " ");
620 case DHCPConstants.OPT_CLASSLESS_ROUTE:
621 setOptionClasslessRoute(reply, dhcpInfo);
624 LOG.trace("DHCP Option code {} not supported yet", element);
630 private void setOptionClasslessRoute(DHCP reply, DhcpInfo dhcpInfo) {
631 List<HostRoutes> hostRoutes = dhcpInfo.getHostRoutes();
632 if (hostRoutes == null) {
633 //we can't set this option, so return
636 ByteArrayOutputStream result = new ByteArrayOutputStream();
637 for (HostRoutes hostRoute : hostRoutes) {
638 if (hostRoute.getNexthop().getIpv4Address() == null
639 || hostRoute.getDestination().getIpv4Prefix() == null) {
640 // we only deal with IPv4 addresses
643 String router = hostRoute.getNexthop().getIpv4Address().getValue();
644 String dest = hostRoute.getDestination().getIpv4Prefix().getValue();
646 result.write(convertToClasslessRouteOption(dest, router));
647 } catch (IOException | NullPointerException e) {
648 LOG.trace("Exception {}", e.getMessage());
651 if (result.size() > 0) {
652 reply.setOptionBytes(DHCPConstants.OPT_CLASSLESS_ROUTE , result.toByteArray());
657 protected byte[] convertToClasslessRouteOption(String dest, String router) {
658 ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
659 if (dest == null || router == null) {
665 String[] parts = dest.split("/");
666 if (parts.length < 2) {
669 prefix = Short.valueOf(parts[1]);
672 byteArray.write(prefix.byteValue());
673 SubnetUtils util = new SubnetUtils(dest);
674 SubnetInfo info = util.getInfo();
675 String strNetAddr = info.getNetworkAddress();
677 byte[] netAddr = InetAddress.getByName(strNetAddr).getAddress();
678 //Strip any trailing 0s from netAddr
679 for (int i = 0; i < netAddr.length;i++) {
680 if (netAddr[i] != 0) {
681 byteArray.write(netAddr,i,1);
684 byteArray.write(InetAddress.getByName(router).getAddress());
685 } catch (IOException e) {
688 return byteArray.toByteArray();
691 private boolean isPktInReasonSendtoCtrl(Class<? extends PacketInReason> pktInReason) {
692 return pktInReason == SendToController.class;
695 private String getInterfaceNameFromTag(long portTag) {
696 String interfaceName = null;
697 GetInterfaceFromIfIndexInput input =
698 new GetInterfaceFromIfIndexInputBuilder().setIfIndex((int) portTag).build();
699 Future<RpcResult<GetInterfaceFromIfIndexOutput>> futureOutput =
700 interfaceManagerRpc.getInterfaceFromIfIndex(input);
702 GetInterfaceFromIfIndexOutput output = futureOutput.get().getResult();
703 interfaceName = output.getInterfaceName();
704 } catch (InterruptedException | ExecutionException e) {
705 LOG.error("Error while retrieving the interfaceName from tag using getInterfaceFromIfIndex RPC");
707 LOG.trace("Returning interfaceName {} for tag {} form getInterfaceNameFromTag", interfaceName, portTag);
708 return interfaceName;
711 private List<Action> getEgressAction(String interfaceName, BigInteger tunnelId) {
712 List<Action> actions = null;
714 GetEgressActionsForInterfaceInputBuilder egressAction =
715 new GetEgressActionsForInterfaceInputBuilder().setIntfName(interfaceName);
716 if (tunnelId != null) {
717 egressAction.setTunnelKey(tunnelId.longValue());
719 Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
720 interfaceManagerRpc.getEgressActionsForInterface(egressAction.build());
721 RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
722 if (!rpcResult.isSuccessful()) {
723 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
724 interfaceName, rpcResult.getErrors());
726 actions = rpcResult.getResult().getAction();
728 } catch (InterruptedException | ExecutionException e) {
729 LOG.warn("Exception when egress actions for interface {}", interfaceName, e);