ADD/REMOVE Option82

Change-Id: I51cfb92f4b2d07c5b588fd96ceb6a37afc6f8f38
diff --git a/src/main/java/org/opencord/dhcpl2relay/DhcpL2Relay.java b/src/main/java/org/opencord/dhcpl2relay/DhcpL2Relay.java
index a224b6d..cdffe4e 100755
--- a/src/main/java/org/opencord/dhcpl2relay/DhcpL2Relay.java
+++ b/src/main/java/org/opencord/dhcpl2relay/DhcpL2Relay.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017-present Open Networking Laboratory
+ * Copyright 2017-present Open Networking Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
 package org.opencord.dhcpl2relay;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -23,17 +25,19 @@
 import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.MacAddress;
+import org.onlab.packet.DeserializationException;
 import org.onlab.packet.DHCP;
 import org.onlab.packet.DHCPOption;
 import org.onlab.packet.DHCPPacketType;
-import org.onlab.packet.Ip4Address;
+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.opencord.dhcpl2relay.packet.DhcpEthernet;
+import org.opencord.dhcpl2relay.packet.DhcpOption82;
 import org.onlab.util.Tools;
 import org.onosproject.cfg.ComponentConfigService;
 import org.onosproject.core.ApplicationId;
@@ -61,15 +65,16 @@
 
 import org.opencord.sadis.SubscriberAndDeviceInformation;
 import org.opencord.sadis.SubscriberAndDeviceInformationService;
-
 import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.nio.ByteBuffer;
 import java.util.Dictionary;
-import java.util.Set;
+import java.util.List;
 import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
 import static org.onlab.packet.MacAddress.valueOf;
@@ -144,7 +149,9 @@
         packetService.addProcessor(dhcpRelayPacketProcessor,
                 PacketProcessor.director(0));
         requestDhcpPackets();
-        modified(context);
+        if (context != null) {
+            modified(context);
+        }
 
         log.info("DHCP-L2-RELAY Started");
     }
@@ -163,6 +170,7 @@
 
     @Modified
     protected void modified(ComponentContext context) {
+
         Dictionary<?, ?> properties = context.getProperties();
 
         Boolean o = Tools.isPropertyEnabled(properties, "option82");
@@ -186,8 +194,15 @@
             log.warn("Dhcp Server info not available");
             return;
         }
+        if (dhcpServerConnectPoint == null) {
+            dhcpServerConnectPoint = cfg.getDhcpServerConnectPoint();
+            requestDhcpPackets();
+        } else {
+            cancelDhcpPackets();
+            dhcpServerConnectPoint = cfg.getDhcpServerConnectPoint();
+            requestDhcpPackets();
+        }
 
-        dhcpServerConnectPoint = cfg.getDhcpServerConnectPoint();
         log.info("dhcp server connect point: " + dhcpServerConnectPoint);
     }
 
@@ -224,16 +239,14 @@
                     .matchIPProtocol(IPv4.PROTOCOL_UDP)
                     .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
             packetService.cancelPackets(selectorServer.build(),
-                    PacketPriority.CONTROL, appId,
-                    Optional.of(dhcpServerConnectPoint.deviceId()));
+                    PacketPriority.CONTROL, appId, Optional.of(dhcpServerConnectPoint.deviceId()));
 
             TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
                     .matchEthType(Ethernet.TYPE_IPV4)
                     .matchIPProtocol(IPv4.PROTOCOL_UDP)
                     .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
             packetService.cancelPackets(selectorClient.build(),
-                    PacketPriority.CONTROL, appId,
-                    Optional.of(dhcpServerConnectPoint.deviceId()));
+                    PacketPriority.CONTROL, appId, Optional.of(dhcpServerConnectPoint.deviceId()));
         }
     }
 
@@ -263,7 +276,7 @@
 
     private MacAddress relayAgentMacAddress(PacketContext context) {
 
-        SubscriberAndDeviceInformation device = getDevice(context);
+        SubscriberAndDeviceInformation device = this.getDevice(context);
         if (device == null) {
             log.warn("Device not found for {}", context.inPacket().
                     receivedFrom());
@@ -275,12 +288,10 @@
 
     private String nasPortId(PacketContext context) {
         Port p = deviceService.getPort(context.inPacket().receivedFrom());
-
         return p.annotations().value(AnnotationKeys.PORT_NAME);
     }
 
     private SubscriberAndDeviceInformation getSubscriber(PacketContext context) {
-
         return subsService.get(nasPortId(context));
     }
 
@@ -314,21 +325,28 @@
             }
 
             // process the packet and get the payload
-            Ethernet packet = context.inPacket().parsed();
+            DhcpEthernet packet = null;
+            ByteBuffer byteBuffer = context.inPacket().unparsed();
+            try {
+                packet = DhcpEthernet.deserializer().deserialize(byteBuffer.array(), 0, byteBuffer.array().length);
+            } catch (DeserializationException e) {
+                log.warn("Unable to deserialize packet");
+            }
+
             if (packet == null) {
                 log.warn("Packet is null");
                 return;
             }
 
-            //log.info("Got a packet of type {}", packet.getEtherType());
+            log.debug("Got a packet ", packet);
 
-            if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
+            if (packet.getEtherType() == DhcpEthernet.TYPE_IPV4) {
                 IPv4 ipv4Packet = (IPv4) packet.getPayload();
 
                 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
                     UDP udpPacket = (UDP) ipv4Packet.getPayload();
                     if (udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT ||
-                        udpPacket.getSourcePort() == UDP.DHCP_SERVER_PORT) {
+                            udpPacket.getSourcePort() == UDP.DHCP_SERVER_PORT) {
                         DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
                         //This packet is dhcp.
                         processDhcpPacket(context, packet, dhcpPayload);
@@ -338,7 +356,7 @@
         }
 
         //forward the packet to ConnectPoint where the DHCP server is attached.
-        private void forwardPacket(Ethernet packet) {
+        private void forwardPacket(DhcpEthernet packet) {
 
             if (dhcpServerConnectPoint != null) {
                 TrafficTreatment t = DefaultTrafficTreatment.builder()
@@ -347,8 +365,8 @@
                         dhcpServerConnectPoint.deviceId(), t,
                         ByteBuffer.wrap(packet.serialize()));
                 if (log.isTraceEnabled()) {
-                    log.trace("Relaying packet to dhcp server {} at {}",
-                            packet, dhcpServerConnectPoint);
+                log.trace("Relaying packet to dhcp server {} at {}",
+                        packet, dhcpServerConnectPoint);
                 }
                 packetService.emit(o);
             } else {
@@ -357,7 +375,7 @@
         }
 
         //process the dhcp packet before sending to server
-        private void processDhcpPacket(PacketContext context, Ethernet packet,
+        private void processDhcpPacket(PacketContext context, DhcpEthernet packet,
                                        DHCP dhcpPayload) {
             if (dhcpPayload == null) {
                 log.warn("DHCP payload is null");
@@ -365,6 +383,7 @@
             }
 
             DHCPPacketType incomingPacketType = null;
+
             for (DHCPOption option : dhcpPayload.getOptions()) {
                 if (option.getCode() == OptionCode_MessageType.getValue()) {
                     byte[] data = option.getData();
@@ -372,75 +391,86 @@
                 }
             }
             log.info("Received DHCP Packet of type {}", incomingPacketType);
-            log.trace("Processing Packet {}", packet);
 
             switch (incomingPacketType) {
-            case DHCPDISCOVER:
-                Ethernet ethernetPacketDiscover =
-                    processDhcpPacketFromClient(context, packet);
-                if (ethernetPacketDiscover != null) {
-                    forwardPacket(ethernetPacketDiscover);
-                }
-                break;
-            case DHCPOFFER:
-                //reply to dhcp client.
-                Ethernet ethernetPacketOffer = processDhcpPacketFromServer(packet);
-                if (ethernetPacketOffer != null) {
-                    sendReply(ethernetPacketOffer, dhcpPayload);
-                }
-                break;
-            case DHCPREQUEST:
-                Ethernet ethernetPacketRequest =
-                    processDhcpPacketFromClient(context, packet);
-                if (ethernetPacketRequest != null) {
-                    forwardPacket(ethernetPacketRequest);
-                }
-                break;
-            case DHCPACK:
-                //reply to dhcp client.
-                Ethernet ethernetPacketAck = processDhcpPacketFromServer(packet);
-                if (ethernetPacketAck != null) {
-                    sendReply(ethernetPacketAck, dhcpPayload);
-                }
-                break;
-            default:
-                break;
+                case DHCPDISCOVER:
+                    DhcpEthernet ethernetPacketDiscover =
+                            processDhcpPacketFromClient(context, packet);
+                    if (ethernetPacketDiscover != null) {
+                        forwardPacket(ethernetPacketDiscover);
+                    }
+                    break;
+                case DHCPOFFER:
+                    //reply to dhcp client.
+                    DhcpEthernet ethernetPacketOffer = processDhcpPacketFromServer(packet);
+                    if (ethernetPacketOffer != null) {
+                        sendReply(ethernetPacketOffer, dhcpPayload);
+                    }
+                    break;
+                case DHCPREQUEST:
+                    DhcpEthernet ethernetPacketRequest =
+                            processDhcpPacketFromClient(context, packet);
+                    if (ethernetPacketRequest != null) {
+                        forwardPacket(ethernetPacketRequest);
+                    }
+                    break;
+                case DHCPACK:
+                    //reply to dhcp client.
+                    DhcpEthernet ethernetPacketAck = processDhcpPacketFromServer(packet);
+                    if (ethernetPacketAck != null) {
+                        sendReply(ethernetPacketAck, dhcpPayload);
+                    }
+                    break;
+                default:
+                    break;
             }
         }
 
-        private Ethernet processDhcpPacketFromClient(PacketContext context,
-                                                     Ethernet ethernetPacket) {
+        private DhcpEthernet processDhcpPacketFromClient(PacketContext context,
+                                                         DhcpEthernet ethernetPacket) {
             log.info("Processing packet from client");
 
             MacAddress relayAgentMac = relayAgentMacAddress(context);
             if (relayAgentMac == null) {
                 log.warn("RelayAgent MAC not found ");
-
                 return null;
             }
 
-            Ethernet etherReply = ethernetPacket;
+            DhcpEthernet etherReply = ethernetPacket;
 
             IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
             UDP udpPacket = (UDP) ipv4Packet.getPayload();
             DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
 
-            etherReply.setSourceMACAddress(relayAgentMac);
-            etherReply.setDestinationMACAddress(dhcpConnectMac);
+            if (option82) {
+                SubscriberAndDeviceInformation entry = getSubscriber(context);
+                if (entry == null) {
+                    log.info("Dropping packet as subscriber entry is not available");
+                    return null;
+                }
+
+                DHCP dhcpPacketWithOption82 = addOption82(dhcpPacket, entry);
+                udpPacket.setPayload(dhcpPacketWithOption82);
+            }
+
+            ipv4Packet.setPayload(udpPacket);
+            etherReply.setPayload(ipv4Packet);
+            etherReply.setSourceMacAddress(relayAgentMac);
+            etherReply.setDestinationMacAddress(dhcpConnectMac);
 
             etherReply.setVlanID(cTag(context).toShort());
-            etherReply.setQinQTPID(Ethernet.TYPE_VLAN);
-            etherReply.setQinQVID(sTag(context).toShort());
+            etherReply.setQinQtpid(DhcpEthernet.TYPE_QINQ);
+            etherReply.setQinQVid(sTag(context).toShort());
 
-            log.info("Finished processing");
+            log.info("Finished processing packet");
             return etherReply;
         }
 
         //build the DHCP offer/ack with proper client port.
-        private Ethernet processDhcpPacketFromServer(Ethernet ethernetPacket) {
-            log.warn("Processing DHCP packet from server");
+        private DhcpEthernet processDhcpPacketFromServer(DhcpEthernet ethernetPacket) {
+            log.info("Processing DHCP packet from server");
             // get dhcp header.
-            Ethernet etherReply = (Ethernet) ethernetPacket.clone();
+            DhcpEthernet etherReply = (DhcpEthernet) ethernetPacket.clone();
             IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
             UDP udpPacket = (UDP) ipv4Packet.getPayload();
             DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
@@ -450,8 +480,8 @@
             Set<Host> hosts = hostService.getHostsByMac(dstMac);
             if (hosts == null || hosts.isEmpty()) {
                 log.warn("Cannot determine host for DHCP client: {}. Aborting "
-                        + "relay for dhcp packet from server {}",
-                         dstMac, ethernetPacket);
+                                + "relay for dhcp packet from server {}",
+                        dstMac, ethernetPacket);
                 return null;
             } else if (hosts.size() > 1) {
                 // XXX  redo to send reply to all hosts found
@@ -460,8 +490,8 @@
             }
             Host host = hosts.iterator().next();
 
-            etherReply.setDestinationMACAddress(dstMac);
-            etherReply.setQinQVID(Ethernet.VLAN_UNTAGGED);
+            etherReply.setDestinationMacAddress(dstMac);
+            etherReply.setQinQVid(Ethernet.VLAN_UNTAGGED);
             etherReply.setPriorityCode(ethernetPacket.getPriorityCode());
             etherReply.setVlanID((short) 0);
 
@@ -473,7 +503,7 @@
                             host.location().port()));
             if (relayAgentIP == null) {
                 log.warn("Cannot determine relay agent Ipv4 addr for host {}. "
-                        + "Aborting relay for dhcp packet from server {}",
+                                + "Aborting relay for dhcp packet from server {}",
                         host, ethernetPacket);
                 return null;
             }
@@ -482,7 +512,11 @@
             ipv4Packet.setDestinationAddress(dhcpPayload.getYourIPAddress());
 
             udpPacket.setDestinationPort(UDP.DHCP_CLIENT_PORT);
-            udpPacket.setPayload(dhcpPayload);
+            if (option82) {
+                udpPacket.setPayload(removeOption82(dhcpPayload));
+            } else {
+                udpPacket.setPayload(dhcpPayload);
+            }
             ipv4Packet.setPayload(udpPacket);
             etherReply.setPayload(ipv4Packet);
 
@@ -491,7 +525,7 @@
         }
 
         //send the response to the requester host.
-        private void sendReply(Ethernet ethPacket, DHCP dhcpPayload) {
+        private void sendReply(DhcpEthernet ethPacket, DHCP dhcpPayload) {
             MacAddress descMac = valueOf(dhcpPayload.getClientHardwareAddress());
             Host host = hostService.getHostsByMac(descMac).stream().findFirst().orElse(null);
 
@@ -506,13 +540,39 @@
                     log.trace("Relaying packet to dhcp client {}", ethPacket);
                 }
                 packetService.emit(o);
-                log.error("DHCP Packet sent to {}", host.location());
+                log.info("DHCP Packet sent to {}", host.location());
             } else {
-                log.info("Dropping DHCP packet because can't find host for {}", descMac);
+                log.error("Dropping DHCP packet because can't find host for {}", descMac);
             }
         }
     }
 
+    private DHCP addOption82(DHCP dhcpPacket, SubscriberAndDeviceInformation entry) {
+        log.debug("option82data {} ", entry);
+
+        List<DHCPOption> options = Lists.newArrayList(dhcpPacket.getOptions());
+        DhcpOption82 option82 = new DhcpOption82();
+        option82.setAgentCircuitId(entry.circuitId());
+        option82.setAgentRemoteId(entry.remoteId());
+        DHCPOption option = new DHCPOption()
+                                .setCode(DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
+                                .setData(option82.toByteArray())
+                                .setLength(option82.length());
+
+        options.add(options.size() - 1, option);
+        dhcpPacket.setOptions(options);
+        return dhcpPacket;
+
+    }
+
+    private DHCP removeOption82(DHCP dhcpPacket) {
+        List<DHCPOption> options = dhcpPacket.getOptions();
+        List<DHCPOption> newoptions = options.stream()
+                .filter(option -> option.getCode() != DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
+                .collect(Collectors.toList());
+
+        return dhcpPacket.setOptions(newoptions);
+    }
     /**
      * Listener for network config events.
      */
@@ -529,4 +589,7 @@
             }
         }
     }
+
+
+
 }