VOL-535 : Use OLT uplink port for packet-in/packet-out towards DHCP server

It is configurable to use this approach or to use an OVS/switch for the same

Change-Id: Iafbdcf3d70eea029c7949622a7048639c333a4d9
diff --git a/src/main/java/org/opencord/dhcpl2relay/DhcpL2Relay.java b/src/main/java/org/opencord/dhcpl2relay/DhcpL2Relay.java
index 5516086..b25f615 100755
--- a/src/main/java/org/opencord/dhcpl2relay/DhcpL2Relay.java
+++ b/src/main/java/org/opencord/dhcpl2relay/DhcpL2Relay.java
@@ -45,8 +45,11 @@
 import org.onosproject.mastership.MastershipService;
 import org.onosproject.net.AnnotationKeys;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.Host;
 import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
 import org.onosproject.net.config.ConfigFactory;
 import org.onosproject.net.config.NetworkConfigEvent;
 import org.onosproject.net.config.NetworkConfigListener;
@@ -74,9 +77,11 @@
 
 import java.nio.ByteBuffer;
 import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.Dictionary;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.Collectors;
@@ -153,6 +158,9 @@
     private ApplicationId appId;
 
     static Map<String, DhcpAllocationInfo> allocationMap = Maps.newConcurrentMap();
+    private boolean modifyClientPktsSrcDstMac = false;
+    //Whether to use the uplink port of the OLTs to send/receive messages to the DHCP server
+    private boolean useOltUplink = false;
 
     @Activate
     protected void activate(ComponentContext context) {
@@ -213,7 +221,10 @@
      * @return true if all information we need have been initialized
      */
     private boolean configured() {
-        return dhcpServerConnectPoint.get() != null;
+        if (!useOltUplink) {
+            return dhcpServerConnectPoint.get() != null;
+        }
+        return true;
     }
 
     /**
@@ -254,29 +265,140 @@
         }
 
         dhcpConnectPoints = Sets.newConcurrentHashSet(cfg.getDhcpServerConnectPoint());
+        modifyClientPktsSrcDstMac = cfg.getModifySrcDstMacAddresses();
+        useOltUplink = cfg.getUseOltUplinkForServerPktInOut();
 
-        selectServerConnectPoint();
+        if (!useOltUplink) {
+            selectServerConnectPoint();
+        }
 
         log.info("dhcp server connect point: " + dhcpServerConnectPoint);
     }
 
     /**
+     * Returns all the uplink ports of OLTs configured in SADIS.
+     * Only ports visible in ONOS and for which this instance is master
+     * are returned
+     */
+    private List<ConnectPoint> getUplinkPortsOfOlts() {
+        List<ConnectPoint> cps = new ArrayList<>();
+
+        // find all the olt devices and if their uplink ports are visible
+        Iterable<Device> devices = deviceService.getDevices();
+        for (Device d : devices) {
+            // check if this device is provisioned in Sadis
+
+            log.debug("getUplinkPortsOfOlts: Checking mastership of {}", d);
+            // do only for devices for which we are the master
+            if (!mastershipService.isLocalMaster(d.id())) {
+                continue;
+            }
+
+            String devSerialNo = d.serialNumber();
+            SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
+            log.debug("getUplinkPortsOfOlts: Found device: {}", deviceInfo);
+            if (deviceInfo != null) {
+                // check if the uplink port with that number is available on the device
+                PortNumber pNum = PortNumber.portNumber(deviceInfo.uplinkPort());
+                Port port = deviceService.getPort(d.id(), pNum);
+                log.debug("getUplinkPortsOfOlts: Found port: {}", port);
+                if (port != null) {
+                    cps.add(new ConnectPoint(d.id(), pNum));
+                }
+            }
+        }
+        return cps;
+    }
+
+    /**
+     * Returns whether the passed port is the uplink port of the olt device.
+     */
+    private boolean isUplinkPortOfOlt(DeviceId dId, Port p) {
+        log.debug("isUplinkPortOfOlt: DeviceId: {} Port: {}", dId, p);
+        // do only for devices for which we are the master
+        if (!mastershipService.isLocalMaster(dId)) {
+            return false;
+        }
+
+        Device d = deviceService.getDevice(dId);
+        SubscriberAndDeviceInformation deviceInfo = subsService.get(d.serialNumber());
+
+        if (deviceInfo != null) {
+            return (deviceInfo.uplinkPort() == p.number().toLong());
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns the connectPoint which is the uplink port of the OLT.
+     */
+    private ConnectPoint getUplinkConnectPointOfOlt(DeviceId dId) {
+
+        Device d = deviceService.getDevice(dId);
+        SubscriberAndDeviceInformation deviceInfo = subsService.get(d.serialNumber());
+        log.debug("getUplinkConnectPointOfOlt DeviceId: {} devInfo: {}", dId, deviceInfo);
+        if (deviceInfo != null) {
+            PortNumber pNum = PortNumber.portNumber(deviceInfo.uplinkPort());
+            Port port = deviceService.getPort(d.id(), pNum);
+            if (port != null) {
+                return new ConnectPoint(d.id(), pNum);
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Request DHCP packet from particular connect point via PacketService.
+     */
+    private void requestDhcpPacketsFromConnectPoint(ConnectPoint cp) {
+        TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchInPort(cp.port())
+                .matchIPProtocol(IPv4.PROTOCOL_UDP)
+                .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
+        packetService.requestPackets(selectorServer.build(),
+                PacketPriority.CONTROL, appId, Optional.of(cp.deviceId()));
+    }
+
+    /**
+     * Cancel DHCP packet from particular connect point via PacketService.
+     */
+    private void cancelDhcpPacketsFromConnectPoint(ConnectPoint cp) {
+        TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchInPort(cp.port())
+                .matchIPProtocol(IPv4.PROTOCOL_UDP)
+                .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
+        packetService.cancelPackets(selectorServer.build(),
+                PacketPriority.CONTROL, appId, Optional.of(cp.deviceId()));
+    }
+
+    /**
      * Request DHCP packet in via PacketService.
      */
     private void requestDhcpPackets() {
-        TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
-                .matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPProtocol(IPv4.PROTOCOL_UDP)
-                .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
-        packetService.requestPackets(selectorServer.build(),
-                PacketPriority.CONTROL, appId);
+        if (!useOltUplink) {
+            TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
+                    .matchEthType(Ethernet.TYPE_IPV4)
+                    .matchIPProtocol(IPv4.PROTOCOL_UDP)
+                    .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)
-                .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
-        packetService.requestPackets(selectorClient.build(),
-                PacketPriority.CONTROL, appId);
+            TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
+                    .matchEthType(Ethernet.TYPE_IPV4)
+                    .matchIPProtocol(IPv4.PROTOCOL_UDP)
+                    .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
+            packetService.requestPackets(selectorClient.build(),
+                    PacketPriority.CONTROL, appId);
+        } else {
+            for (ConnectPoint cp: getUplinkPortsOfOlts()) {
+                log.debug("requestDhcpPackets: ConnectPoint: {}", cp);
+                requestDhcpPacketsFromConnectPoint(cp);
+            }
+        }
 
     }
 
@@ -284,19 +406,25 @@
      * Cancel requested DHCP packets in via packet service.
      */
     private void cancelDhcpPackets() {
-        TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
-                .matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPProtocol(IPv4.PROTOCOL_UDP)
-                .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
-        packetService.cancelPackets(selectorServer.build(),
-                PacketPriority.CONTROL, appId);
+        if (!useOltUplink) {
+            TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
+                    .matchEthType(Ethernet.TYPE_IPV4)
+                    .matchIPProtocol(IPv4.PROTOCOL_UDP)
+                    .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)
-                .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
-        packetService.cancelPackets(selectorClient.build(),
-                PacketPriority.CONTROL, appId);
+            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);
+        } else {
+            for (ConnectPoint cp: getUplinkPortsOfOlts()) {
+                cancelDhcpPacketsFromConnectPoint(cp);
+            }
+        }
     }
 
     public static Map<String, DhcpAllocationInfo> allocationMap() {
@@ -417,21 +545,29 @@
         }
 
         //forward the packet to ConnectPoint where the DHCP server is attached.
-        private void forwardPacket(Ethernet packet) {
+        private void forwardPacket(Ethernet packet, PacketContext context) {
+            ConnectPoint toSendTo = null;
 
-            if (dhcpServerConnectPoint.get() != null) {
+            if (!useOltUplink) {
+                toSendTo = dhcpServerConnectPoint.get();
+            } else {
+                toSendTo = getUplinkConnectPointOfOlt(context.inPacket().
+                                                      receivedFrom().deviceId());
+            }
+
+            if (toSendTo != null) {
                 TrafficTreatment t = DefaultTrafficTreatment.builder()
-                        .setOutput(dhcpServerConnectPoint.get().port()).build();
+                        .setOutput(toSendTo.port()).build();
                 OutboundPacket o = new DefaultOutboundPacket(
-                        dhcpServerConnectPoint.get().deviceId(), t,
+                        toSendTo.deviceId(), t,
                         ByteBuffer.wrap(packet.serialize()));
                 if (log.isTraceEnabled()) {
                     log.trace("Relaying packet to dhcp server {} at {}",
-                            packet, dhcpServerConnectPoint.get());
+                            packet, toSendTo);
                 }
                 packetService.emit(o);
             } else {
-                log.warn("No dhcp server connect point");
+                log.error("No connect point to send msg to DHCP Server");
             }
         }
 
@@ -464,7 +600,7 @@
                     Ethernet ethernetPacketDiscover =
                             processDhcpPacketFromClient(context, packet);
                     if (ethernetPacketDiscover != null) {
-                        forwardPacket(ethernetPacketDiscover);
+                        forwardPacket(ethernetPacketDiscover, context);
                     }
                     break;
                 case DHCPOFFER:
@@ -478,7 +614,7 @@
                     Ethernet ethernetPacketRequest =
                             processDhcpPacketFromClient(context, packet);
                     if (ethernetPacketRequest != null) {
-                        forwardPacket(ethernetPacketRequest);
+                        forwardPacket(ethernetPacketRequest, context);
                     }
                     break;
                 case DHCPACK:
@@ -537,8 +673,10 @@
 
             ipv4Packet.setPayload(udpPacket);
             etherReply.setPayload(ipv4Packet);
-            etherReply.setSourceMACAddress(relayAgentMac);
-            etherReply.setDestinationMACAddress(dhcpConnectMac);
+            if (modifyClientPktsSrcDstMac) {
+                etherReply.setSourceMACAddress(relayAgentMac);
+                etherReply.setDestinationMACAddress(dhcpConnectMac);
+            }
 
             etherReply.setPriorityCode(ethernetPacket.getPriorityCode());
             etherReply.setVlanID(cTag(context).toShort());
@@ -710,13 +848,15 @@
     private class InnerMastershipListener implements MastershipListener {
         @Override
         public void event(MastershipEvent event) {
-            if (dhcpServerConnectPoint.get() != null &&
-                    dhcpServerConnectPoint.get().deviceId().
-                            equals(event.subject())) {
-                log.trace("Mastership Event recevived for {}", event.subject());
-                // mastership of the device for our connect point has changed
-                // reselect
-                selectServerConnectPoint();
+            if (!useOltUplink) {
+                if (dhcpServerConnectPoint.get() != null &&
+                        dhcpServerConnectPoint.get().deviceId().
+                                equals(event.subject())) {
+                    log.trace("Mastership Event recevived for {}", event.subject());
+                    // mastership of the device for our connect point has changed
+                    // reselect
+                    selectServerConnectPoint();
+                }
             }
         }
     }
@@ -729,27 +869,40 @@
         @Override
         public void event(DeviceEvent event) {
             log.trace("Device Event recevived for {} event {}", event.subject(), event.type());
-            if (dhcpServerConnectPoint.get() == null) {
-                switch (event.type()) {
-                    case DEVICE_ADDED:
-                    case DEVICE_AVAILABILITY_CHANGED:
-                        // some device is available check if we can get one
-                        selectServerConnectPoint();
-                        break;
-                    default:
-                        break;
+            if (!useOltUplink) {
+                if (dhcpServerConnectPoint.get() == null) {
+                    switch (event.type()) {
+                        case DEVICE_ADDED:
+                        case DEVICE_AVAILABILITY_CHANGED:
+                            // some device is available check if we can get one
+                            selectServerConnectPoint();
+                            break;
+                        default:
+                            break;
+                    }
+                    return;
                 }
-                return;
-            }
-            if (dhcpServerConnectPoint.get().deviceId().
-                    equals(event.subject().id())) {
+                if (dhcpServerConnectPoint.get().deviceId().
+                        equals(event.subject().id())) {
+                    switch (event.type()) {
+                        case DEVICE_AVAILABILITY_CHANGED:
+                        case DEVICE_REMOVED:
+                        case DEVICE_SUSPENDED:
+                            // state of our device has changed, check if we need
+                            // to re-select
+                            selectServerConnectPoint();
+                            break;
+                        default:
+                            break;
+                    }
+                }
+            } else {
                 switch (event.type()) {
-                    case DEVICE_AVAILABILITY_CHANGED:
-                    case DEVICE_REMOVED:
-                    case DEVICE_SUSPENDED:
-                        // state of our device has changed, check if we need
-                        // to re-select
-                        selectServerConnectPoint();
+                    case PORT_ADDED:
+                        if (isUplinkPortOfOlt(event.subject().id(), event.port())) {
+                            requestDhcpPacketsFromConnectPoint(new ConnectPoint(event.subject().id(),
+                                    event.port().number()));
+                        }
                         break;
                     default:
                         break;