NETVIRT-1033: Adding ArpResponder flows for SR-IOV VMs
[netvirt.git] / vpnservice / dhcpservice / dhcpservice-impl / src / main / java / org / opendaylight / netvirt / dhcpservice / DhcpPktHandler.java
1 /*
2  * Copyright © 2015, 2017 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.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;
69
70
71 @Singleton
72 public class DhcpPktHandler implements PacketProcessingListener {
73
74     private static final Logger LOG = LoggerFactory.getLogger(DhcpPktHandler.class);
75
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;
84
85     @Inject
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;
99         this.config = config;
100         this.dhcpAllocationPoolMgr = dhcpAllocationPoolMgr;
101         this.broker = dataBroker;
102     }
103
104     //TODO: Handle this in a separate thread
105     @Override
106     public void onPacketReceived(PacketReceived packet) {
107         if (!config.isControllerDhcpEnabled()) {
108             return;
109         }
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();
116             try {
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);
121                 return;
122             }
123             DHCP pktIn;
124             pktIn = getDhcpPktIn(ethPkt);
125             if (pktIn != null) {
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);
138                     return;
139                 }
140                 Port port;
141                 if (tunnelId != null) {
142                     port = dhcpExternalTunnelManager.readVniMacToPortCache(tunnelId, macAddress);
143                 } else {
144                     port = getNeutronPort(interfaceName);
145                 }
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.
154                      */
155                     if (dhcpPortData.isPresent()) {
156                         serverIp = dhcpPortData.get().getPortFixedip();
157                         serverMacAddress = dhcpPortData.get().getPortMacaddress();
158                     } else {
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(),
161                                 port.getUuid());
162                         return;
163                     }
164                 }
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);
168                     return;
169                 }
170                 byte[] pktOut = getDhcpPacketOut(replyPkt, ethPkt, serverMacAddress);
171                 sendPacketOut(pktOut, interfaceInfo.getDpId(), interfaceName, tunnelId);
172             }
173         }
174     }
175
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");
181     }
182
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);
192         }
193         DHCP reply = null;
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);
199             }
200         }
201
202         return reply;
203     }
204
205     private DhcpInfo handleDhcpNeutronPacket(byte msgType, Port port, Subnet subnet, String serverIp) {
206         if (msgType == DHCPConstants.MSG_DECLINE) {
207             LOG.trace("DHCPDECLINE received");
208             return null;
209         } else if (msgType == DHCPConstants.MSG_RELEASE) {
210             LOG.trace("DHCPRELEASE received");
211             return null;
212         }
213         return getDhcpInfoFromNeutronPort(port, subnet, serverIp);
214     }
215
216
217     private DhcpInfo handleDhcpAllocationPoolPacket(byte msgType, DHCP dhcpPkt, String interfaceName,
218             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                 dhcpInfo.setClientIp(clientIp).setServerIp(serverIp)
264                         .setCidr(String.valueOf(subnet.getCidr().getValue())).setHostRoutes(subnet.getHostRoutes())
265                         .setDnsServersIpAddrs(dnsServers);
266             }
267         }
268         return dhcpInfo;
269     }
270
271     private DhcpInfo getApDhcpInfo(AllocationPool ap, IpAddress allocatedIp) {
272         DhcpInfo dhcpInfo = null;
273
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);
280
281         return dhcpInfo;
282     }
283
284     /* TODO:
285      * getIpv4Address and isIpv4Address
286      * Many other modules use/need similar methods. Should
287      * be refactored to a common NeutronUtils module.     *
288      */
289     private String getIpv4Address(Port port) {
290
291         for (FixedIps fixedIp : port.getFixedIps()) {
292             if (isIpv4Address(fixedIp.getIpAddress())) {
293                 return fixedIp.getIpAddress().getIpv4Address().getValue();
294             }
295         }
296         LOG.error("Could not find ipv4 address for port {}", port);
297         return null;
298     }
299
300     private boolean isIpv4Address(IpAddress ip) {
301         return ip != null && ip.getIpv4Address() != null;
302     }
303
304     private Subnet getNeutronSubnet(Port port) {
305         return dhcpMgr.getNeutronSubnet(port);
306     }
307
308     private Port getNeutronPort(String interfaceName) {
309         return dhcpMgr.getNeutronPort(interfaceName);
310     }
311
312     private DHCP getDhcpPktIn(Ethernet actualEthernetPacket) {
313         Ethernet ethPkt = actualEthernetPacket;
314         if (ethPkt.getEtherType() == (short)NwConstants.ETHTYPE_802_1Q) {
315             ethPkt = (Ethernet)ethPkt.getPayload();
316         }
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();
327                     try {
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);
332                         return null;
333                     }
334                     return reply;
335                 }
336             }
337         }
338         return null;
339     }
340
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);
349
350         reply.setYiaddr(dhcpInfo.getClientIp());
351         reply.setSiaddr(dhcpInfo.getServerIp());
352
353         reply.setFlags(dhcpPkt.getFlags());
354         reply.setGiaddr(dhcpPkt.getGiaddr());
355         reply.setChaddr(dhcpPkt.getChaddr());
356
357         reply.setMsgType(DHCPConstants.MSG_OFFER);
358         if (dhcpPkt.containsOption(DHCPConstants.OPT_PARAMETER_REQUEST_LIST)) {
359             setParameterListOptions(dhcpPkt, reply, dhcpInfo);
360         }
361         setCommonOptions(reply, dhcpInfo);
362         return reply;
363     }
364
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);
375
376         reply.setFlags(dhcpPkt.getFlags());
377         reply.setGiaddr(dhcpPkt.getGiaddr());
378         reply.setChaddr(dhcpPkt.getChaddr());
379         byte[] allocatedIp;
380         try {
381             allocatedIp = DHCPUtils.strAddrToByteArray(dhcpInfo.getClientIp());
382         } catch (UnknownHostException e) {
383             LOG.debug("strAddrToByteArray", e);
384             allocatedIp = null;
385         }
386
387         if (Arrays.equals(allocatedIp, dhcpPkt.getCiaddr())) {
388             //This means a renew request
389             sendAck = true;
390         } else {
391             requestedIp = dhcpPkt.getOptionBytes(DHCPConstants.OPT_REQUESTED_ADDRESS);
392             sendAck = Arrays.equals(allocatedIp, requestedIp);
393         }
394
395         if (sendAck) {
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);
402             }
403         } else {
404             reply.setMsgType(DHCPConstants.MSG_NAK);
405         }
406         setCommonOptions(reply, dhcpInfo);
407         return reply;
408     }
409
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) {
414         if (reply == null) {
415             /*
416              * DECLINE or RELEASE don't result in reply packet
417              */
418             return null;
419         }
420         LOG.trace("Sending DHCP Pkt {}", reply);
421         InetAddress serverIp = reply.getOptionInetAddr(DHCPConstants.OPT_SERVER_IDENTIFIER);
422         // create UDP pkt
423         UDP udpPkt = new UDP();
424         byte[] rawPkt;
425         try {
426             rawPkt = reply.serialize();
427         } catch (PacketException e) {
428             LOG.warn("Failed to serialize packet", e);
429             return null;
430         }
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));
435         //Create IP Pkt
436         try {
437             rawPkt = udpPkt.serialize();
438         } catch (PacketException e) {
439             LOG.warn("Failed to serialize packet", e);
440             return null;
441         }
442         short checkSum = 0;
443         boolean computeUdpChecksum = true;
444         if (computeUdpChecksum) {
445             checkSum = computeChecksum(rawPkt, serverIp.getAddress(),
446                     NetUtils.intToByteArray4(DhcpMConstants.BCAST_IP));
447         }
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);
468         } else {
469             ether.setEtherType(EtherTypes.IPv4.shortValue());
470             ether.setPayload(ip4Reply);
471         }
472         ether.setSourceMACAddress(getServerMacAddress(phyAddrees));
473         ether.setDestinationMACAddress(etherPkt.getSourceMACAddress());
474
475         try {
476             rawPkt = ether.serialize();
477         } catch (PacketException e) {
478             LOG.warn("Failed to serialize ethernet reply",e);
479             return null;
480         }
481         return rawPkt;
482     }
483
484     private byte[] getServerMacAddress(String phyAddress) {
485         // Should we return ControllerMac instead?
486         return DHCPUtils.strMacAddrtoByteArray(phyAddress);
487     }
488
489     public short computeChecksum(byte[] inData, byte[] srcAddr, byte[] destAddr) {
490         int sum = 0;
491         int carry = 0;
492         int wordData;
493         int index;
494
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;
499         }
500
501         if (index < inData.length) {
502             wordData = (inData[index] << 8 & 0xFF00) + (0 & 0xFF);
503             sum = sum + wordData;
504         }
505
506         for (index = 0; index < 4; index = index + 2) {
507             wordData = (srcAddr[index] << 8 & 0xFF00) + (srcAddr[index + 1] & 0xFF);
508             sum = sum + wordData;
509         }
510
511         for (index = 0; index < 4; index = index + 2) {
512             wordData = (destAddr[index] << 8 & 0xFF00) + (destAddr[index + 1] & 0xFF);
513             sum = sum + wordData;
514         }
515         sum = sum + 17 + inData.length;
516
517         while (sum >> 16 != 0) {
518             carry = sum >> 16;
519             sum = (sum & 0xFFFF) + carry;
520         }
521         short checkSum = (short) ~((short) sum & 0xFFFF);
522         if (checkSum == 0) {
523             checkSum = (short)0xffff;
524         }
525         return checkSum;
526     }
527
528     private void setCommonOptions(DHCP pkt, DhcpInfo dhcpInfo) {
529         String serverIp = dhcpInfo.getServerIp();
530         if (pkt.getMsgType() != DHCPConstants.MSG_NAK) {
531             setNonNakOptions(pkt, dhcpInfo);
532         }
533         try {
534             /*
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.
538              */
539             if (serverIp != null) {
540                 pkt.setOptionInetAddr(DHCPConstants.OPT_SERVER_IDENTIFIER, serverIp);
541             } else {
542                 pkt.unsetOption(DHCPConstants.OPT_SERVER_IDENTIFIER);
543             }
544         } catch (UnknownHostException e) {
545             LOG.warn("Failed to set option", e);
546         }
547     }
548
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());
553         }
554         if (dhcpMgr.getDhcpLeaseTime() > 0) {
555             pkt.setOptionInt(DHCPConstants.OPT_REBINDING_TIME, dhcpMgr.getDhcpRebindingTime());
556             pkt.setOptionInt(DHCPConstants.OPT_RENEWAL_TIME, dhcpMgr.getDhcpRenewalTime());
557         }
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();
564         try {
565             /*
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.
569              */
570             if (gwIp != null) {
571                 pkt.setOptionInetAddr(DHCPConstants.OPT_ROUTERS, gwIp);
572             } else {
573                 pkt.unsetOption(DHCPConstants.OPT_ROUTERS);
574             }
575             if (info != null) {
576                 pkt.setOptionInetAddr(DHCPConstants.OPT_SUBNET_MASK, info.getNetmask());
577                 pkt.setOptionInetAddr(DHCPConstants.OPT_BROADCAST_ADDRESS, info.getBroadcastAddress());
578             } else {
579                 pkt.unsetOption(DHCPConstants.OPT_SUBNET_MASK);
580                 pkt.unsetOption(DHCPConstants.OPT_BROADCAST_ADDRESS);
581             }
582             if (dnServers != null && dnServers.size() > 0) {
583                 pkt.setOptionStrAddrs(DHCPConstants.OPT_DOMAIN_NAME_SERVERS, dnServers);
584             } else {
585                 pkt.unsetOption(DHCPConstants.OPT_DOMAIN_NAME_SERVERS);
586             }
587         } catch (UnknownHostException e) {
588             // TODO Auto-generated catch block
589             LOG.warn("Failed to set option", e);
590         }
591     }
592
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) {
596             switch (element) {
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.
608                      */
609                     reply.setOptionInt(element, 0);
610                     break;
611                 case DHCPConstants.OPT_DOMAIN_NAME:
612                     reply.setOptionString(element, " ");
613                     break;
614                 case DHCPConstants.OPT_CLASSLESS_ROUTE:
615                     setOptionClasslessRoute(reply, dhcpInfo);
616                     break;
617                 default:
618                     LOG.trace("DHCP Option code {} not supported yet", element);
619                     break;
620             }
621         }
622     }
623
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
628             return;
629         }
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
635                 return;
636             }
637             String router = hostRoute.getNexthop().getIpv4Address().getValue();
638             String dest = hostRoute.getDestination().getIpv4Prefix().getValue();
639             try {
640                 result.write(convertToClasslessRouteOption(dest, router));
641             } catch (IOException | NullPointerException e) {
642                 LOG.trace("Exception {}", e.getMessage());
643             }
644         }
645         if (result.size() > 0) {
646             reply.setOptionBytes(DHCPConstants.OPT_CLASSLESS_ROUTE , result.toByteArray());
647         }
648     }
649
650     @Nonnull
651     protected byte[] convertToClasslessRouteOption(String dest, String router) {
652         ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
653         if (dest == null || router == null) {
654             return new byte[0];
655         }
656
657         //get prefix
658         Short prefix = null;
659         String[] parts = dest.split("/");
660         if (parts.length < 2) {
661             prefix = (short) 0;
662         } else {
663             prefix = Short.valueOf(parts[1]);
664         }
665
666         byteArray.write(prefix.byteValue());
667         SubnetUtils util = new SubnetUtils(dest);
668         SubnetInfo info = util.getInfo();
669         String strNetAddr = info.getNetworkAddress();
670         try {
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);
676                 }
677             }
678             byteArray.write(InetAddress.getByName(router).getAddress());
679         } catch (IOException e) {
680             return new byte[0];
681         }
682         return byteArray.toByteArray();
683     }
684
685     private boolean isPktInReasonSendtoCtrl(Class<? extends PacketInReason> pktInReason) {
686         return pktInReason == SendToController.class;
687     }
688
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);
695         try {
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");
700         }
701         LOG.trace("Returning interfaceName {} for tag {} form getInterfaceNameFromTag", interfaceName, portTag);
702         return interfaceName;
703     }
704
705     private List<Action> getEgressAction(String interfaceName, BigInteger tunnelId) {
706         List<Action> actions = null;
707         try {
708             GetEgressActionsForInterfaceInputBuilder egressAction =
709                     new GetEgressActionsForInterfaceInputBuilder().setIntfName(interfaceName);
710             if (tunnelId != null) {
711                 egressAction.setTunnelKey(tunnelId.longValue());
712             }
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());
719             } else {
720                 actions = rpcResult.getResult().getAction();
721             }
722         } catch (InterruptedException | ExecutionException e) {
723             LOG.warn("Exception when egress actions for interface {}", interfaceName, e);
724         }
725         return actions;
726     }
727 }