Remove unused parameters
[netvirt.git] / dhcpservice / impl / src / main / java / org / opendaylight / netvirt / dhcpservice / DhcpPktHandler.java
1 /*
2  * Copyright © 2015, 2018 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.netvirt.dhcpservice;
9
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;
70
71
72 @Singleton
73 public class DhcpPktHandler implements PacketProcessingListener {
74
75     private static final Logger LOG = LoggerFactory.getLogger(DhcpPktHandler.class);
76
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;
85
86     @Inject
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;
103     }
104
105     //TODO: Handle this in a separate thread
106     @Override
107     public void onPacketReceived(PacketReceived packet) {
108         if (!config.isControllerDhcpEnabled()) {
109             return;
110         }
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();
117             try {
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);
122                 return;
123             }
124             DHCP pktIn;
125             pktIn = getDhcpPktIn(ethPkt);
126             if (pktIn != null) {
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);
139                     return;
140                 }
141                 Port port;
142                 if (tunnelId != null) {
143                     port = dhcpExternalTunnelManager.readVniMacToPortCache(tunnelId, macAddress);
144                 } else {
145                     port = getNeutronPort(interfaceName);
146                 }
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.
155                      */
156                     if (dhcpPortData.isPresent()) {
157                         serverIp = dhcpPortData.get().getPortFixedip();
158                         serverMacAddress = dhcpPortData.get().getPortMacaddress();
159                     } else {
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(),
162                                 port.getUuid());
163                         return;
164                     }
165                 }
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);
169                     return;
170                 }
171                 byte[] pktOut = getDhcpPacketOut(replyPkt, ethPkt, serverMacAddress);
172                 sendPacketOut(pktOut, interfaceInfo.getDpId(), interfaceName, tunnelId);
173             }
174         }
175     }
176
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");
182     }
183
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);
193         }
194         DHCP reply = null;
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);
200             }
201         }
202
203         return reply;
204     }
205
206     private DhcpInfo handleDhcpNeutronPacket(byte msgType, Port port, Subnet subnet, String serverIp) {
207         if (msgType == DHCPConstants.MSG_DECLINE) {
208             LOG.trace("DHCPDECLINE received");
209             return null;
210         } else if (msgType == DHCPConstants.MSG_RELEASE) {
211             LOG.trace("DHCPRELEASE received");
212             return null;
213         }
214         return getDhcpInfoFromNeutronPort(port, subnet, serverIp);
215     }
216
217
218     private DhcpInfo handleDhcpAllocationPoolPacket(byte msgType, String interfaceName, String macAddress) {
219         String networkId = dhcpAllocationPoolMgr.getNetworkByPort(interfaceName);
220         AllocationPool pool = networkId != null ? dhcpAllocationPoolMgr.getAllocationPoolByNetwork(networkId)
221                 : null;
222         if (networkId == null || pool == null) {
223             LOG.warn("No Dhcp Allocation Pool was found for interface: {}", interfaceName);
224             return null;
225         }
226         switch (msgType) {
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);
233                 break;
234             default:
235                 break;
236         }
237         return null;
238     }
239
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);
243         return dhcpInfo;
244     }
245
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);
250         return dhcpInfo;
251     }
252
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());
261             }
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);
267                     }
268                 }
269                 dhcpInfo.setClientIp(clientIp).setServerIp(serverIp)
270                         .setCidr(String.valueOf(subnet.getCidr().getValue())).setHostRoutes(subnetHostRoutes)
271                         .setDnsServersIpAddrs(dnsServers);
272             }
273         }
274         return dhcpInfo;
275     }
276
277     private DhcpInfo getApDhcpInfo(AllocationPool ap, IpAddress allocatedIp) {
278         DhcpInfo dhcpInfo = null;
279
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);
286
287         return dhcpInfo;
288     }
289
290     /* TODO:
291      * getIpv4Address and isIpv4Address
292      * Many other modules use/need similar methods. Should
293      * be refactored to a common NeutronUtils module.     *
294      */
295     private String getIpv4Address(Port port) {
296
297         for (FixedIps fixedIp : port.getFixedIps()) {
298             if (isIpv4Address(fixedIp.getIpAddress())) {
299                 return fixedIp.getIpAddress().getIpv4Address().getValue();
300             }
301         }
302         LOG.error("Could not find ipv4 address for port {}", port);
303         return null;
304     }
305
306     private boolean isIpv4Address(IpAddress ip) {
307         return ip != null && ip.getIpv4Address() != null;
308     }
309
310     private Subnet getNeutronSubnet(Port port) {
311         return dhcpMgr.getNeutronSubnet(port);
312     }
313
314     private Port getNeutronPort(String interfaceName) {
315         return dhcpMgr.getNeutronPort(interfaceName);
316     }
317
318     private DHCP getDhcpPktIn(Ethernet actualEthernetPacket) {
319         Ethernet ethPkt = actualEthernetPacket;
320         if (ethPkt.getEtherType() == (short)NwConstants.ETHTYPE_802_1Q) {
321             ethPkt = (Ethernet)ethPkt.getPayload();
322         }
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();
333                     try {
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);
338                         return null;
339                     }
340                     return reply;
341                 }
342             }
343         }
344         return null;
345     }
346
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);
355
356         reply.setYiaddr(dhcpInfo.getClientIp());
357         reply.setSiaddr(dhcpInfo.getServerIp());
358
359         reply.setFlags(dhcpPkt.getFlags());
360         reply.setGiaddr(dhcpPkt.getGiaddr());
361         reply.setChaddr(dhcpPkt.getChaddr());
362
363         reply.setMsgType(DHCPConstants.MSG_OFFER);
364         if (dhcpPkt.containsOption(DHCPConstants.OPT_PARAMETER_REQUEST_LIST)) {
365             setParameterListOptions(dhcpPkt, reply, dhcpInfo);
366         }
367         setCommonOptions(reply, dhcpInfo);
368         return reply;
369     }
370
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);
381
382         reply.setFlags(dhcpPkt.getFlags());
383         reply.setGiaddr(dhcpPkt.getGiaddr());
384         reply.setChaddr(dhcpPkt.getChaddr());
385         byte[] allocatedIp;
386         try {
387             allocatedIp = DHCPUtils.strAddrToByteArray(dhcpInfo.getClientIp());
388         } catch (UnknownHostException e) {
389             LOG.debug("strAddrToByteArray", e);
390             allocatedIp = null;
391         }
392
393         if (Arrays.equals(allocatedIp, dhcpPkt.getCiaddr())) {
394             //This means a renew request
395             sendAck = true;
396         } else {
397             requestedIp = dhcpPkt.getOptionBytes(DHCPConstants.OPT_REQUESTED_ADDRESS);
398             sendAck = Arrays.equals(allocatedIp, requestedIp);
399         }
400
401         if (sendAck) {
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);
408             }
409         } else {
410             reply.setMsgType(DHCPConstants.MSG_NAK);
411         }
412         setCommonOptions(reply, dhcpInfo);
413         return reply;
414     }
415
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) {
420         if (reply == null) {
421             /*
422              * DECLINE or RELEASE don't result in reply packet
423              */
424             return null;
425         }
426         LOG.trace("Sending DHCP Pkt {}", reply);
427         InetAddress serverIp = reply.getOptionInetAddr(DHCPConstants.OPT_SERVER_IDENTIFIER);
428         // create UDP pkt
429         UDP udpPkt = new UDP();
430         byte[] rawPkt;
431         try {
432             rawPkt = reply.serialize();
433         } catch (PacketException e) {
434             LOG.warn("Failed to serialize packet", e);
435             return null;
436         }
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));
441         //Create IP Pkt
442         try {
443             rawPkt = udpPkt.serialize();
444         } catch (PacketException e) {
445             LOG.warn("Failed to serialize packet", e);
446             return null;
447         }
448         short checkSum = 0;
449         boolean computeUdpChecksum = true;
450         if (computeUdpChecksum) {
451             checkSum = computeChecksum(rawPkt, serverIp.getAddress(),
452                     NetUtils.intToByteArray4(DhcpMConstants.BCAST_IP));
453         }
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);
474         } else {
475             ether.setEtherType(EtherTypes.IPv4.shortValue());
476             ether.setPayload(ip4Reply);
477         }
478         ether.setSourceMACAddress(getServerMacAddress(phyAddrees));
479         ether.setDestinationMACAddress(etherPkt.getSourceMACAddress());
480
481         try {
482             rawPkt = ether.serialize();
483         } catch (PacketException e) {
484             LOG.warn("Failed to serialize ethernet reply",e);
485             return null;
486         }
487         return rawPkt;
488     }
489
490     private byte[] getServerMacAddress(String phyAddress) {
491         // Should we return ControllerMac instead?
492         return DHCPUtils.strMacAddrtoByteArray(phyAddress);
493     }
494
495     public short computeChecksum(byte[] inData, byte[] srcAddr, byte[] destAddr) {
496         int sum = 0;
497         int carry = 0;
498         int wordData;
499         int index;
500
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;
505         }
506
507         if (index < inData.length) {
508             wordData = (inData[index] << 8 & 0xFF00) + (0 & 0xFF);
509             sum = sum + wordData;
510         }
511
512         for (index = 0; index < 4; index = index + 2) {
513             wordData = (srcAddr[index] << 8 & 0xFF00) + (srcAddr[index + 1] & 0xFF);
514             sum = sum + wordData;
515         }
516
517         for (index = 0; index < 4; index = index + 2) {
518             wordData = (destAddr[index] << 8 & 0xFF00) + (destAddr[index + 1] & 0xFF);
519             sum = sum + wordData;
520         }
521         sum = sum + 17 + inData.length;
522
523         while (sum >> 16 != 0) {
524             carry = sum >> 16;
525             sum = (sum & 0xFFFF) + carry;
526         }
527         short checkSum = (short) ~((short) sum & 0xFFFF);
528         if (checkSum == 0) {
529             checkSum = (short)0xffff;
530         }
531         return checkSum;
532     }
533
534     private void setCommonOptions(DHCP pkt, DhcpInfo dhcpInfo) {
535         String serverIp = dhcpInfo.getServerIp();
536         if (pkt.getMsgType() != DHCPConstants.MSG_NAK) {
537             setNonNakOptions(pkt, dhcpInfo);
538         }
539         try {
540             /*
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.
544              */
545             if (serverIp != null) {
546                 pkt.setOptionInetAddr(DHCPConstants.OPT_SERVER_IDENTIFIER, serverIp);
547             } else {
548                 pkt.unsetOption(DHCPConstants.OPT_SERVER_IDENTIFIER);
549             }
550         } catch (UnknownHostException e) {
551             LOG.warn("Failed to set option", e);
552         }
553     }
554
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());
559         }
560         if (dhcpMgr.getDhcpLeaseTime() > 0) {
561             pkt.setOptionInt(DHCPConstants.OPT_REBINDING_TIME, dhcpMgr.getDhcpRebindingTime());
562             pkt.setOptionInt(DHCPConstants.OPT_RENEWAL_TIME, dhcpMgr.getDhcpRenewalTime());
563         }
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();
570         try {
571             /*
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.
575              */
576             if (gwIp != null) {
577                 pkt.setOptionInetAddr(DHCPConstants.OPT_ROUTERS, gwIp);
578             } else {
579                 pkt.unsetOption(DHCPConstants.OPT_ROUTERS);
580             }
581             if (info != null) {
582                 pkt.setOptionInetAddr(DHCPConstants.OPT_SUBNET_MASK, info.getNetmask());
583                 pkt.setOptionInetAddr(DHCPConstants.OPT_BROADCAST_ADDRESS, info.getBroadcastAddress());
584             } else {
585                 pkt.unsetOption(DHCPConstants.OPT_SUBNET_MASK);
586                 pkt.unsetOption(DHCPConstants.OPT_BROADCAST_ADDRESS);
587             }
588             if (dnServers != null && dnServers.size() > 0) {
589                 pkt.setOptionStrAddrs(DHCPConstants.OPT_DOMAIN_NAME_SERVERS, dnServers);
590             } else {
591                 pkt.unsetOption(DHCPConstants.OPT_DOMAIN_NAME_SERVERS);
592             }
593         } catch (UnknownHostException e) {
594             // TODO Auto-generated catch block
595             LOG.warn("Failed to set option", e);
596         }
597     }
598
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) {
602             switch (element) {
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.
614                      */
615                     reply.setOptionInt(element, 0);
616                     break;
617                 case DHCPConstants.OPT_DOMAIN_NAME:
618                     reply.setOptionString(element, " ");
619                     break;
620                 case DHCPConstants.OPT_CLASSLESS_ROUTE:
621                     setOptionClasslessRoute(reply, dhcpInfo);
622                     break;
623                 default:
624                     LOG.trace("DHCP Option code {} not supported yet", element);
625                     break;
626             }
627         }
628     }
629
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
634             return;
635         }
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
641                 return;
642             }
643             String router = hostRoute.getNexthop().getIpv4Address().getValue();
644             String dest = hostRoute.getDestination().getIpv4Prefix().getValue();
645             try {
646                 result.write(convertToClasslessRouteOption(dest, router));
647             } catch (IOException | NullPointerException e) {
648                 LOG.trace("Exception {}", e.getMessage());
649             }
650         }
651         if (result.size() > 0) {
652             reply.setOptionBytes(DHCPConstants.OPT_CLASSLESS_ROUTE , result.toByteArray());
653         }
654     }
655
656     @Nonnull
657     protected byte[] convertToClasslessRouteOption(String dest, String router) {
658         ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
659         if (dest == null || router == null) {
660             return new byte[0];
661         }
662
663         //get prefix
664         Short prefix = null;
665         String[] parts = dest.split("/");
666         if (parts.length < 2) {
667             prefix = (short) 0;
668         } else {
669             prefix = Short.valueOf(parts[1]);
670         }
671
672         byteArray.write(prefix.byteValue());
673         SubnetUtils util = new SubnetUtils(dest);
674         SubnetInfo info = util.getInfo();
675         String strNetAddr = info.getNetworkAddress();
676         try {
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);
682                 }
683             }
684             byteArray.write(InetAddress.getByName(router).getAddress());
685         } catch (IOException e) {
686             return new byte[0];
687         }
688         return byteArray.toByteArray();
689     }
690
691     private boolean isPktInReasonSendtoCtrl(Class<? extends PacketInReason> pktInReason) {
692         return pktInReason == SendToController.class;
693     }
694
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);
701         try {
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");
706         }
707         LOG.trace("Returning interfaceName {} for tag {} form getInterfaceNameFromTag", interfaceName, portTag);
708         return interfaceName;
709     }
710
711     private List<Action> getEgressAction(String interfaceName, BigInteger tunnelId) {
712         List<Action> actions = null;
713         try {
714             GetEgressActionsForInterfaceInputBuilder egressAction =
715                     new GetEgressActionsForInterfaceInputBuilder().setIntfName(interfaceName);
716             if (tunnelId != null) {
717                 egressAction.setTunnelKey(tunnelId.longValue());
718             }
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());
725             } else {
726                 actions = rpcResult.getResult().getAction();
727             }
728         } catch (InterruptedException | ExecutionException e) {
729             LOG.warn("Exception when egress actions for interface {}", interfaceName, e);
730         }
731         return actions;
732     }
733 }