[ONOS-3946] Implement IcmpHandler for OpenstackRoutingService
- Process Icmp packet sent from Host to external network for OpenstackGateway Node
- Process Arp packet sent from physical  router to gateway

Change-Id: Ifcde71a9ca10180682811c9e1bcf58f991b36443
diff --git a/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingManager.java b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingManager.java
index b1c0a74..2ab19ac 100644
--- a/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingManager.java
+++ b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingManager.java
@@ -32,6 +32,12 @@
 import org.onosproject.core.CoreService;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Port;
+import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.basics.SubjectFactories;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.driver.DriverService;
 import org.onosproject.net.flowobjective.FlowObjectiveService;
@@ -45,6 +51,7 @@
 import org.onosproject.openstackinterface.OpenstackRouter;
 import org.onosproject.openstackinterface.OpenstackRouterInterface;
 import org.onosproject.openstacknetworking.OpenstackRoutingService;
+import org.onosproject.openstacknetworking.OpenstackSwitchingService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -80,24 +87,44 @@
     protected OpenstackInterfaceService openstackService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackSwitchingService openstackSwitchingService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected FlowObjectiveService flowObjectiveService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected DriverService driverService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigService configService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigRegistry configRegistry;
+
+
+
     private ApplicationId appId;
     private Map<String, OpenstackRouterInterface> routerInterfaceMap = Maps.newHashMap();
     private Map<Integer, String> portNumMap = initPortNumMap();
     private static final String APP_ID = "org.onosproject.openstackrouting";
     private static final String PORT_NAME = "portName";
     private static final String DEVICE_OWNER_ROUTER_INTERFACE = "network:router_interface";
+    private final ConfigFactory configFactory =
+            new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY, OpenstackRoutingConfig.class, "openstackrouting") {
+                @Override
+                public OpenstackRoutingConfig createConfig() {
+                    return new OpenstackRoutingConfig();
+                }
+            };
+    private final NetworkConfigListener configListener = new InternalConfigListener();
 
-    // TODO: This will be replaced to get the information from openstackswitchingservice.
-    private static final String EXTERNAL_INTERFACE_NAME = "veth0";
+    private OpenstackRoutingConfig config;
+    private static final int PNAT_PORT_NUM_START = 1024;
+    private static final int PNAT_PORT_NUM_END = 65535;
 
     private Map<Integer, String> initPortNumMap() {
         Map<Integer, String> map = Maps.newHashMap();
-        for (int i = 1024; i < 65535; i++) {
+        for (int i = PNAT_PORT_NUM_START; i < PNAT_PORT_NUM_END; i++) {
             map.put(i, "");
         }
         return map;
@@ -108,17 +135,30 @@
             Executors.newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "L3-event"));
     private ExecutorService icmpEventExecutorService =
             Executors.newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "icmp-event"));
+    private ExecutorService arpEventExecutorService =
+            Executors.newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "arp-event"));
+    private OpenstackIcmpHandler openstackIcmpHandler;
+    private OpenstackRoutingArpHandler openstackArpHandler;
 
     @Activate
     protected void activate() {
         appId = coreService.registerApplication(APP_ID);
         packetService.addProcessor(internalPacketProcessor, PacketProcessor.director(1));
+        configRegistry.registerConfigFactory(configFactory);
+        configService.addListener(configListener);
+
+        readConfiguration();
+
         log.info("onos-openstackrouting started");
     }
 
     @Deactivate
     protected void deactivate() {
         packetService.removeProcessor(internalPacketProcessor);
+        l3EventExecutorService.shutdown();
+        icmpEventExecutorService.shutdown();
+        arpEventExecutorService.shutdown();
+
         log.info("onos-openstackrouting stopped");
     }
 
@@ -155,16 +195,16 @@
 
     @Override
     public void updateRouterInterface(OpenstackRouterInterface routerInterface) {
-        routerInterfaceMap.putIfAbsent(routerInterface.portId(), routerInterface);
+        routerInterfaceMap.putIfAbsent(routerInterface.id(), routerInterface);
         List<OpenstackRouterInterface> routerInterfaces = Lists.newArrayList();
         routerInterfaces.add(routerInterface);
-        checkExternalConnection(getOpenstackRouter(routerInterface.portId()), routerInterfaces);
+        checkExternalConnection(getOpenstackRouter(routerInterface.id()), routerInterfaces);
     }
 
     @Override
     public void removeRouterInterface(OpenstackRouterInterface routerInterface) {
         OpenstackRoutingRulePopulator rulePopulator = new OpenstackRoutingRulePopulator(appId,
-                openstackService, flowObjectiveService, deviceService, driverService);
+                openstackService, flowObjectiveService, deviceService, driverService, config);
         rulePopulator.removeExternalRules(routerInterface);
         routerInterfaceMap.remove(routerInterface.portId());
     }
@@ -181,10 +221,10 @@
 
     private OpenstackRouterInterface portToRouterInterface(OpenstackPort p) {
         OpenstackRouterInterface.Builder osBuilder = new OpenstackRouterInterface.Builder()
-                .id(checkNotNull(p.id()))
+                .id(checkNotNull(p.deviceId()))
                 .tenantId(checkNotNull(openstackService.network(p.networkId()).tenantId()))
                 .subnetId(checkNotNull(p.fixedIps().keySet().stream().findFirst().orElse(null)).toString())
-                .portId(checkNotNull(p.deviceId()));
+                .portId(checkNotNull(p.id()));
 
         return osBuilder.build();
     }
@@ -196,19 +236,26 @@
 
             if (context.isHandled()) {
                 return;
+            } else if (!context.inPacket().receivedFrom().deviceId().toString()
+                    .equals(config.gatewayBridgeId())) {
+                return;
             }
 
             InboundPacket pkt = context.inPacket();
             Ethernet ethernet = pkt.parsed();
 
-            if (ethernet != null && ethernet.getEtherType() == Ethernet.TYPE_IPV4) {
+            //TODO: Considers IPv6 later.
+            if (ethernet == null) {
+                return;
+            } else if (ethernet.getEtherType() == Ethernet.TYPE_IPV4) {
                 IPv4 iPacket = (IPv4) ethernet.getPayload();
                 OpenstackRoutingRulePopulator rulePopulator = new OpenstackRoutingRulePopulator(appId,
-                        openstackService, flowObjectiveService, deviceService,
-                        driverService);
+                        openstackService, flowObjectiveService, deviceService, driverService, config);
                 switch (iPacket.getProtocol()) {
                     case IPv4.PROTOCOL_ICMP:
-                        icmpEventExecutorService.execute(new OpenstackIcmpHandler(rulePopulator, context));
+
+                        icmpEventExecutorService.submit(() ->
+                                openstackIcmpHandler.processIcmpPacket(context, ethernet));
                         break;
                     case IPv4.PROTOCOL_UDP:
                         // don't process DHCP
@@ -219,7 +266,8 @@
                         }
                     default:
                         int portNum = getPortNum(ethernet.getSourceMAC(), iPacket.getDestinationAddress());
-                        Port port = getExternalPort(pkt.receivedFrom().deviceId(), EXTERNAL_INTERFACE_NAME);
+                        Port port =
+                                getExternalPort(pkt.receivedFrom().deviceId(), config.gatewayExternalInterfaceName());
                         if (port == null) {
                             log.warn("There`s no external interface");
                             break;
@@ -230,7 +278,9 @@
                                 portNum, openstackPort, port));
                         break;
                 }
-
+            } else if (ethernet.getEtherType() == Ethernet.TYPE_ARP) {
+                arpEventExecutorService.submit(() ->
+                        openstackArpHandler.processArpPacketFromRouter(context, ethernet));
             }
         }
 
@@ -265,9 +315,9 @@
 
     private void initiateL3Rule(OpenstackRouter router, OpenstackRouterInterface routerInterface) {
         long vni = Long.parseLong(openstackService.network(openstackService
-                .port(routerInterface.id()).networkId()).segmentId());
+                .port(routerInterface.portId()).networkId()).segmentId());
         OpenstackRoutingRulePopulator rulePopulator = new OpenstackRoutingRulePopulator(appId,
-                openstackService, flowObjectiveService, deviceService, driverService);
+                openstackService, flowObjectiveService, deviceService, driverService, config);
         rulePopulator.populateExternalRules(vni, router, routerInterface);
     }
 
@@ -288,4 +338,50 @@
                 .equals(ip4Address) ? openstackPort : null;
     }
 
+    private void readConfiguration() {
+        config = configService.getConfig(appId, OpenstackRoutingConfig.class);
+        if (config == null) {
+            log.error("No configuration found");
+            return;
+        }
+
+        checkNotNull(config.physicalRouterMac());
+        checkNotNull(config.gatewayBridgeId());
+        checkNotNull(config.gatewayExternalInterfaceMac());
+        checkNotNull(config.gatewayExternalInterfaceName());
+
+        log.debug("Configured info: {}, {}, {}, {}", config.physicalRouterMac(), config.gatewayBridgeId(),
+                config.gatewayExternalInterfaceMac(), config.gatewayExternalInterfaceName());
+
+        reloadInitL3Rules();
+
+        openstackIcmpHandler = new OpenstackIcmpHandler(packetService, deviceService,
+                openstackService, config, openstackSwitchingService);
+        openstackArpHandler = new OpenstackRoutingArpHandler(packetService, openstackService, config);
+
+        openstackIcmpHandler.requestPacket(appId);
+        openstackArpHandler.requestPacket(appId);
+
+        log.info("OpenstackRouting configured");
+    }
+
+    private class InternalConfigListener implements NetworkConfigListener {
+
+        @Override
+        public void event(NetworkConfigEvent event) {
+            if (!event.configClass().equals(OpenstackRoutingConfig.class)) {
+                return;
+            }
+
+            switch (event.type()) {
+                case CONFIG_ADDED:
+                case CONFIG_UPDATED:
+                    l3EventExecutorService.execute(OpenstackRoutingManager.this::readConfiguration);
+                    break;
+                default:
+                    log.debug("Unsupported event type {}", event.type().toString());
+                    break;
+            }
+        }
+    }
 }