using subnet as lease/range selection criteria in DHCP Relay App, addressed review comments

Change-Id: Id2a81faa00592df4ede33f6679c2ba2dd8fb2293
diff --git a/apps/dhcprelay/BUCK b/apps/dhcprelay/BUCK
index aad1a8d..6f28dea 100644
--- a/apps/dhcprelay/BUCK
+++ b/apps/dhcprelay/BUCK
@@ -1,5 +1,6 @@
 COMPILE_DEPS = [
     '//lib:CORE_DEPS',
+    '//incubator/api:onos-incubator-api',
 ]
 
 osgi_jar (
diff --git a/apps/dhcprelay/pom.xml b/apps/dhcprelay/pom.xml
index fc24d33..eb27c3e 100644
--- a/apps/dhcprelay/pom.xml
+++ b/apps/dhcprelay/pom.xml
@@ -49,6 +49,11 @@
 
         <dependency>
             <groupId>org.onosproject</groupId>
+            <artifactId>onos-incubator-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
             <artifactId>onlab-osgi</artifactId>
             <version>${project.version}</version>
         </dependency>
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelay.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelay.java
index 79bfaa8..0bb5dff 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelay.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelay.java
@@ -16,25 +16,28 @@
 package org.onosproject.dhcprelay;
 
 import java.nio.ByteBuffer;
-import java.util.List;
+import java.util.Objects;
 import java.util.Set;
-
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.ARP;
 import org.onlab.packet.DHCP;
 import org.onlab.packet.DHCPOption;
 import org.onlab.packet.DHCPPacketType;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.MacAddress;
 import org.onlab.packet.TpPort;
 import org.onlab.packet.UDP;
 import org.onlab.packet.VlanId;
-import org.onlab.util.HexString;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.Host;
 import org.onosproject.net.HostId;
@@ -57,9 +60,10 @@
 import org.slf4j.LoggerFactory;
 
 import com.google.common.collect.ImmutableSet;
+
 import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
-import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_CircuitID;
 import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
+import static org.onlab.packet.MacAddress.valueOf;
 /**
  * DHCP Relay Agent Application Component.
  */
@@ -69,6 +73,9 @@
     public static final String DHCP_RELAY_APP = "org.onosproject.dhcp-relay";
     private final Logger log = LoggerFactory.getLogger(getClass());
     private final InternalConfigListener cfgListener = new InternalConfigListener();
+    private static Ip4Address relayAgentIP = null;
+    private static MacAddress relayAgentMAC = null;
+    private static MacAddress myMAC = valueOf("4f:4f:4f:4f:4f:4f");
 
     private final Set<ConfigFactory> factories = ImmutableSet.of(
             new ConfigFactory<ApplicationId, DhcpRelayConfig>(APP_SUBJECT_FACTORY,
@@ -93,8 +100,13 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected HostService hostService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected InterfaceService interfaceService;
+
     private DhcpRelayPacketProcessor dhcpRelayPacketProcessor = new DhcpRelayPacketProcessor();
     private ConnectPoint dhcpServerConnectPoint = null;
+    private Ip4Address dhcpServerIp = null;
+    private MacAddress dhcpServerMac = null;
     private ApplicationId appId;
 
     @Activate
@@ -132,8 +144,11 @@
         }
 
         dhcpServerConnectPoint = cfg.getDhcpServerConnectPoint();
-        log.info("Reconfigured the dhcp server info");
+        dhcpServerIp = cfg.getDhcpServerIp();
+        dhcpServerMac = cfg.getDhcpServermac();
         log.info("dhcp server connect points are " + dhcpServerConnectPoint);
+        log.info("dhcp server ipaddress " + dhcpServerIp);
+        log.info("dhcp server mac address " + dhcpServerMac);
     }
 
     /**
@@ -144,16 +159,18 @@
         TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
                 .matchIPProtocol(IPv4.PROTOCOL_UDP)
-                .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
-                .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
+                .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
         packetService.requestPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
 
         TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
                 .matchIPProtocol(IPv4.PROTOCOL_UDP)
-                .matchUdpDst(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
-                .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
+                .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
         packetService.requestPackets(selectorClient.build(), PacketPriority.CONTROL, appId);
+
+        TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_ARP);
+        packetService.requestPackets(selectorArpServer.build(), PacketPriority.CONTROL, appId);
     }
 
     /**
@@ -163,16 +180,18 @@
         TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
                 .matchIPProtocol(IPv4.PROTOCOL_UDP)
-                .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
-                .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
+                .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
         packetService.cancelPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
 
         TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
                 .matchIPProtocol(IPv4.PROTOCOL_UDP)
-                .matchUdpDst(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
-                .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
+                .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
         packetService.cancelPackets(selectorClient.build(), PacketPriority.CONTROL, appId);
+
+        TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_ARP);
+        packetService.cancelPackets(selectorArpServer.build(), PacketPriority.CONTROL, appId);
     }
 
     private class DhcpRelayPacketProcessor implements PacketProcessor {
@@ -192,17 +211,38 @@
                 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
                     UDP udpPacket = (UDP) ipv4Packet.getPayload();
                     DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
-                    if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
-                        udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT ||
-                        udpPacket.getSourcePort() == UDP.DHCP_SERVER_PORT &&
-                        udpPacket.getDestinationPort() == UDP.DHCP_CLIENT_PORT) {
+                    if (udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT ||
+                        udpPacket.getSourcePort() == UDP.DHCP_SERVER_PORT) {
                         //This packet is dhcp.
                         processDhcpPacket(context, dhcpPayload);
                     }
                 }
+            } else if (packet.getEtherType() == Ethernet.TYPE_ARP) {
+                ARP arpPacket = (ARP) packet.getPayload();
+                Set<Interface> serverInterfaces = interfaceService.
+                        getInterfacesByPort(context.inPacket().receivedFrom());
+                //ignore the packets if dhcp server interface is not configured on onos.
+                if (serverInterfaces.isEmpty()) {
+                    log.warn("server virtual interface not configured");
+                    return;
+                }
+                if ((arpPacket.getOpCode() == ARP.OP_REQUEST) &&
+                        checkArpRequestFrmDhcpServ(serverInterfaces, arpPacket)) {
+                    processArpPacket(context, packet);
+                }
             }
         }
 
+        //method to check the arp request is from dhcp server for default-gateway.
+        private boolean checkArpRequestFrmDhcpServ(Set<Interface> serverInterfaces, ARP arpPacket) {
+            if (Objects.equals(serverInterfaces.iterator().next().ipAddressesList().get(0).
+                    ipAddress().getIp4Address(),
+                    Ip4Address.valueOf(arpPacket.getTargetProtocolAddress()))) {
+                return true;
+            }
+            return false;
+        }
+
         //forward the packet to ConnectPoint where the DHCP server is attached.
         private void forwardPacket(Ethernet packet) {
 
@@ -216,15 +256,51 @@
             }
         }
 
+        /**
+         * Processes the ARP Payload and initiates a reply to the client.
+         *
+         * @param context context of the incoming message
+         * @param packet the ethernet payload
+         */
+        private void processArpPacket(PacketContext context, Ethernet packet) {
+
+            ARP arpPacket = (ARP) packet.getPayload();
+
+            ARP arpReply = (ARP) arpPacket.clone();
+            arpReply.setOpCode(ARP.OP_REPLY);
+
+            arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
+            arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
+            arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
+            arpReply.setSenderHardwareAddress(myMAC.toBytes());
+
+            // Ethernet Frame.
+            Ethernet ethReply = new Ethernet();
+            ethReply.setSourceMACAddress(myMAC);
+            ethReply.setDestinationMACAddress(packet.getSourceMAC());
+            ethReply.setEtherType(Ethernet.TYPE_ARP);
+            ethReply.setVlanID(packet.getVlanID());
+
+            ethReply.setPayload(arpReply);
+            forwardPacket(ethReply);
+        }
+
         //process the dhcp packet before sending to server
         private void processDhcpPacket(PacketContext context, DHCP dhcpPayload) {
 
+            Set<Interface> clientServerInterfaces = interfaceService.
+                    getInterfacesByPort(context.inPacket().receivedFrom());
+            //ignore the packets if dhcp client interface is not configured on onos.
+            if (clientServerInterfaces.isEmpty()) {
+                log.warn("client virtual interface not configured");
+                return;
+            }
+
             if (dhcpPayload == null) {
                 return;
             }
 
             Ethernet packet = context.inPacket().parsed();
-            String circuitIdFrmClient = context.inPacket().receivedFrom().elementId().toString();
             DHCPPacketType incomingPacketType = null;
             for (DHCPOption option : dhcpPayload.getOptions()) {
                 if (option.getCode() == OptionCode_MessageType.getValue()) {
@@ -232,79 +308,81 @@
                     incomingPacketType = DHCPPacketType.getType(data[0]);
                 }
             }
+
             switch (incomingPacketType) {
             case DHCPDISCOVER:
-                //add the circuit id as switch dpid and forward the packet to dhcp server.
-                Ethernet ethernetPacketDiscover = processDhcpPacketFrmClient(packet, circuitIdFrmClient,
-                        (byte) DHCPPacketType.DHCPDISCOVER.getValue());
+                //add the gatewayip as virtual interface ip for server to understand the lease to be assigned
+                //and forward the packet to dhcp server.
+                Ethernet ethernetPacketDiscover = processDhcpPacketFrmClient(context, packet, clientServerInterfaces);
                 forwardPacket(ethernetPacketDiscover);
                 break;
             case DHCPOFFER:
                 //reply to dhcp client.
-                sendReply(packet);
+                Ethernet ethernetPacketOffer = processDhcpPacketFrmServer(packet);
+                sendReply(ethernetPacketOffer, dhcpPayload);
                 break;
             case DHCPREQUEST:
-                //add the circuit id as switch dpid and forward the packet to dhcp server.
-                Ethernet ethernetPacketRequest = processDhcpPacketFrmClient(packet, circuitIdFrmClient,
-                        (byte) DHCPPacketType.DHCPREQUEST.getValue());
+                //add the gatewayip as virtual interface ip for server to understand the lease to be assigned
+                //and forward the packet to dhcp server.
+                Ethernet ethernetPacketRequest = processDhcpPacketFrmClient(context, packet, clientServerInterfaces);
                 forwardPacket(ethernetPacketRequest);
                 break;
             case DHCPACK:
                 //reply to dhcp client.
-                sendReply(packet);
+                Ethernet ethernetPacketAck = processDhcpPacketFrmServer(packet);
+                sendReply(ethernetPacketAck, dhcpPayload);
                 break;
             default:
                 break;
             }
         }
 
-        //build the DHCP discover/request packet with circuitid(DpId) suboption.
-        private Ethernet processDhcpPacketFrmClient(Ethernet ethernetPacket, String circuitId,
-                byte incomingPacketType) {
+        //build the DHCP discover/request packet with gatewayip(unicast packet)
+        private Ethernet processDhcpPacketFrmClient(PacketContext context, Ethernet ethernetPacket,
+                Set<Interface> clientInterfaces) {
 
+            //assuming one interface per port for now.
+            relayAgentIP = clientInterfaces.iterator().next().ipAddressesList().get(0).
+                    ipAddress().getIp4Address();
+            relayAgentMAC = clientInterfaces.iterator().next().mac();
             // get dhcp header.
             Ethernet etherReply = (Ethernet) ethernetPacket.clone();
+            etherReply.setSourceMACAddress(relayAgentMAC);
+            etherReply.setDestinationMACAddress(dhcpServerMac);
             IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
+            ipv4Packet.setSourceAddress(relayAgentIP.toInt());
+            ipv4Packet.setDestinationAddress(dhcpServerIp.toInt());
             UDP udpPacket = (UDP) ipv4Packet.getPayload();
             DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
-
-            // DHCP Options.
-            List<DHCPOption> optionList = dhcpPacket.getOptions();
-
-            // Dhcp SubOption as CircuitID
-            DHCPOption option = new DHCPOption();
-            option.setCode(OptionCode_CircuitID.getValue());
-            option.setLength((byte) 10);
-
-            // start object for suboption circuit id.
-            String[] actualDpId = circuitId.split(":", 2);
-            DHCPOption subOption = new DHCPOption();
-            subOption.setCode((byte) 1);
-            byte[] subOptionData = HexString.fromHexString(actualDpId[1], null);
-            subOption.setData(subOptionData);
-            subOption.setLength((byte) 8);
-            // end object for suboption circuit id.
-
-            // converting suboption to byte array
-            byte[] data = new byte[10];
-            ByteBuffer bb = ByteBuffer.wrap(data);
-            DHCP.dhcpOptionToByteArray(subOption, bb);
-
-            option.setData(data);
-            optionList.add(optionList.size() - 1, option);
-
-            dhcpPacket.setOptions(optionList);
+            dhcpPacket.setGatewayIPAddress(relayAgentIP.toInt());
             udpPacket.setPayload(dhcpPacket);
             ipv4Packet.setPayload(udpPacket);
             etherReply.setPayload(ipv4Packet);
             return etherReply;
         }
 
-        //send the response to the requestor host.
-        private void sendReply(Ethernet ethPacket) {
+        //build the DHCP offer/ack with proper client port.
+        private Ethernet processDhcpPacketFrmServer(Ethernet ethernetPacket) {
 
-            //get the host info
-            Host host = hostService.getHost(HostId.hostId(ethPacket.getDestinationMAC(),
+            // get dhcp header.
+            Ethernet etherReply = (Ethernet) ethernetPacket.clone();
+            IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
+            UDP udpPacket = (UDP) ipv4Packet.getPayload();
+            DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
+            //set the ethernet frame.
+            etherReply.setDestinationMACAddress(dhcpPayload.getClientHardwareAddress());
+            udpPacket.setDestinationPort(UDP.DHCP_CLIENT_PORT);
+            udpPacket.setPayload(dhcpPayload);
+            ipv4Packet.setPayload(udpPacket);
+            etherReply.setPayload(ipv4Packet);
+            return etherReply;
+        }
+
+        //send the response to the requestor host.
+        private void sendReply(Ethernet ethPacket, DHCP dhcpPayload) {
+
+            MacAddress descMac = new MacAddress(dhcpPayload.getClientHardwareAddress());
+            Host host = hostService.getHost(HostId.hostId(descMac,
                     VlanId.vlanId(ethPacket.getVlanID())));
             ConnectPoint dhcpRequestor = new ConnectPoint(host.location().elementId(),
                                                     host.location().port());
@@ -336,4 +414,4 @@
             }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayConfig.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayConfig.java
index 24ad67b..90921d0 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayConfig.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayConfig.java
@@ -20,18 +20,25 @@
 import org.onosproject.net.config.Config;
 
 import static org.onosproject.net.config.Config.FieldPresence.MANDATORY;
+
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.MacAddress;
 /**
  * DHCP Relay Config class.
  */
 public class DhcpRelayConfig extends Config<ApplicationId> {
 
     private static final String DHCP_CONNECT_POINT = "dhcpserverConnectPoint";
+    private static final String DHCP_SERVER_IP = "serverip";
+    private static final String DHCP_SERVER_MAC = "servermac";
 
     @Override
     public boolean isValid() {
 
-        return hasOnlyFields(DHCP_CONNECT_POINT) &&
-                isConnectPoint(DHCP_CONNECT_POINT, MANDATORY);
+        return hasOnlyFields(DHCP_CONNECT_POINT, DHCP_SERVER_IP, DHCP_SERVER_MAC) &&
+                isConnectPoint(DHCP_CONNECT_POINT, MANDATORY) &&
+                isIpAddress(DHCP_SERVER_IP, MANDATORY) &&
+                isMacAddress(DHCP_SERVER_MAC, MANDATORY);
     }
 
     /**
@@ -42,4 +49,24 @@
     public ConnectPoint getDhcpServerConnectPoint() {
         return ConnectPoint.deviceConnectPoint(object.path(DHCP_CONNECT_POINT).asText());
     }
+
+    /**
+     * Returns the dhcp server ip.
+     *
+     * @return ip address or null if not set
+     */
+    public Ip4Address getDhcpServerIp() {
+        String ip = get(DHCP_SERVER_IP, null);
+        return ip != null ? Ip4Address.valueOf(ip) : null;
+    }
+
+    /**
+     * Returns the dhcp server mac.
+     *
+     * @return server mac or null if not set
+     */
+    public MacAddress getDhcpServermac() {
+        String mac = get(DHCP_SERVER_MAC, null);
+        return mac != null ? MacAddress.valueOf(mac) : null;
+    }
 }
diff --git a/apps/dhcprelay/src/main/resources/dhcp-relay.json b/apps/dhcprelay/src/main/resources/dhcp-relay.json
index f94f15c..616c4ab 100644
--- a/apps/dhcprelay/src/main/resources/dhcp-relay.json
+++ b/apps/dhcprelay/src/main/resources/dhcp-relay.json
@@ -2,7 +2,9 @@
   "apps": {
     "org.onosproject.dhcp-relay" : {
       "dhcprelay" : {
-        "dhcpserverConnectPoint": "of:0000000000000002/2"
+        "dhcpserverConnectPoint": "of:0000000000000002/2",
+        "serverip": "172.168.10.2",
+        "servermac": "d2:70:98:90:8c:44"
       }
     }
   }