[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/OpenstackIcmpHandler.java b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackIcmpHandler.java
index 3cc11f0..9d8bd71 100644
--- a/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackIcmpHandler.java
+++ b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackIcmpHandler.java
@@ -5,7 +5,7 @@
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -15,21 +15,260 @@
  */
 package org.onosproject.openstacknetworking.routing;
 
+import com.google.common.collect.Maps;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.ICMP;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
 import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.openstackinterface.OpenstackInterfaceService;
+import org.onosproject.openstackinterface.OpenstackPort;
+import org.onosproject.openstacknetworking.OpenstackPortInfo;
+import org.onosproject.openstacknetworking.OpenstackSwitchingService;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
 
 /**
- * Handle ICMP packet processing for Managing Flow Rules In Openstack Nodes.
+ * Handle ICMP packet sent from Openstack Gateway nodes.
  */
-public class OpenstackIcmpHandler implements Runnable {
+public class OpenstackIcmpHandler {
+    protected final Logger log = getLogger(getClass());
 
-    volatile PacketContext context;
-    private OpenstackRoutingRulePopulator rulePopulator;
-    OpenstackIcmpHandler(OpenstackRoutingRulePopulator rulePopulator, PacketContext context) {
-        this.context = context;
-        this.rulePopulator = rulePopulator;
+    private final PacketService packetService;
+    private final DeviceService deviceService;
+    private final Map<String, OpenstackPortInfo> icmpInfoMap = Maps.newHashMap();
+    private final OpenstackSwitchingService openstackSwitchingService;
+    private final OpenstackInterfaceService openstackService;
+    private final OpenstackRoutingConfig config;
+    private static final MacAddress GATEWAY_MAC = MacAddress.valueOf("1f:1f:1f:1f:1f:1f");
+    private static final String NETWORK_ROUTER_INTERFACE = "network:router_interface";
+    private static final String PORTNAME = "portName";
+
+    /**
+     * Default constructor.
+     *
+     * @param packetService packet service
+     * @param deviceService device service
+     * @param openstackService openstackInterface service
+     * @param config openstackRoutingConfig
+     * @param openstackSwitchingService openstackSwitching service
+     */
+    OpenstackIcmpHandler(PacketService packetService, DeviceService deviceService,
+                         OpenstackInterfaceService openstackService, OpenstackRoutingConfig config,
+                         OpenstackSwitchingService openstackSwitchingService) {
+        this.packetService = packetService;
+        this.deviceService = deviceService;
+        this.openstackService = checkNotNull(openstackService);
+        this.config = checkNotNull(config);
+        this.openstackSwitchingService = checkNotNull(openstackSwitchingService);
     }
 
-    @Override
-    public void run() {
+    /**
+     * Requests ICMP packet.
+     *
+     * @param appId Application Id
+     */
+    public void requestPacket(ApplicationId appId) {
+        TrafficSelector icmpSelector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPProtocol(IPv4.PROTOCOL_ICMP)
+                .build();
+
+        packetService.requestPackets(icmpSelector,
+                PacketPriority.CONTROL,
+                appId,
+                Optional.of(DeviceId.deviceId(config.gatewayBridgeId())));
     }
-}
\ No newline at end of file
+
+    /**
+     * Handles ICMP packet.
+     *
+     * @param context packet context
+     * @param ethernet ethernet
+     */
+    public void processIcmpPacket(PacketContext context, Ethernet ethernet) {
+        checkNotNull(context, "context can not be null");
+        checkNotNull(ethernet, "ethernet can not be null");
+
+        IPv4 ipPacket = (IPv4) ethernet.getPayload();
+
+        log.debug("icmpEvent called from ip {}, mac {}", Ip4Address.valueOf(ipPacket.getSourceAddress()).toString(),
+                ethernet.getSourceMAC().toString());
+
+        ICMP icmp = (ICMP) ipPacket.getPayload();
+        short icmpId = getIcmpId(icmp);
+
+        DeviceId deviceId = context.inPacket().receivedFrom().deviceId();
+        if (icmp.getIcmpType() == ICMP.TYPE_ECHO_REQUEST) {
+            //TODO: Considers icmp between internal subnets which are belonged to the same router.
+
+            OpenstackPortInfo openstackPortInfo =
+                    getOpenstackPortInfo(Ip4Address.valueOf(ipPacket.getSourceAddress()), ethernet.getSourceMAC());
+
+            checkNotNull(openstackPortInfo, "openstackPortInfo can not be null");
+
+            if (ipPacket.getDestinationAddress() == openstackPortInfo.gatewayIP().toInt()) {
+                processIcmpPacketSentToGateway(ipPacket, icmp, openstackPortInfo);
+            } else {
+                Ip4Address pNatIpAddress = pNatIpForPort(openstackPortInfo);
+                checkNotNull(pNatIpAddress, "pNatIpAddress can not be null");
+
+                sendRequestPacketToExt(ipPacket, icmp, deviceId, pNatIpAddress);
+
+                String icmpInfoKey = String.valueOf(icmpId)
+                        .concat(String.valueOf(pNatIpAddress.toInt()))
+                        .concat(String.valueOf(ipPacket.getDestinationAddress()));
+                icmpInfoMap.putIfAbsent(icmpInfoKey, openstackPortInfo);
+            }
+        } else if (icmp.getIcmpType() == ICMP.TYPE_ECHO_REPLY) {
+            String icmpInfoKey = String.valueOf(icmpId)
+                    .concat(String.valueOf(ipPacket.getDestinationAddress()))
+                    .concat(String.valueOf(ipPacket.getSourceAddress()));
+
+            processResponsePacketFromExternalToHost(ipPacket, icmp, icmpInfoMap.get(icmpInfoKey));
+
+            icmpInfoMap.remove(icmpInfoKey);
+        }
+    }
+
+    private void processIcmpPacketSentToGateway(IPv4 icmpRequestIpv4, ICMP icmpRequest,
+                                                OpenstackPortInfo openstackPortInfo) {
+        icmpRequest.setIcmpType(ICMP.TYPE_ECHO_REPLY)
+                .resetChecksum();
+
+        icmpRequestIpv4.setSourceAddress(icmpRequestIpv4.getDestinationAddress())
+                .setDestinationAddress(openstackPortInfo.ip().toInt())
+                .resetChecksum();
+
+        icmpRequestIpv4.setPayload(icmpRequest);
+
+        Ethernet icmpResponseEth = new Ethernet();
+
+        icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4)
+                .setSourceMACAddress(GATEWAY_MAC)
+                .setDestinationMACAddress(openstackPortInfo.mac())
+                .setPayload(icmpRequestIpv4);
+
+        sendResponsePacketToHost(icmpResponseEth, openstackPortInfo);
+    }
+
+    private void sendRequestPacketToExt(IPv4 icmpRequestIpv4, ICMP icmpRequest, DeviceId deviceId,
+                                        Ip4Address pNatIpAddress) {
+        icmpRequest.resetChecksum();
+        icmpRequestIpv4.setSourceAddress(pNatIpAddress.toInt())
+                .resetChecksum();
+        icmpRequestIpv4.setPayload(icmpRequest);
+
+        Ethernet icmpRequestEth = new Ethernet();
+
+        icmpRequestEth.setEtherType(Ethernet.TYPE_IPV4)
+                .setSourceMACAddress(MacAddress.valueOf(config.gatewayExternalInterfaceMac()))
+                .setDestinationMACAddress(MacAddress.valueOf(config.physicalRouterMac()))
+                .setPayload(icmpRequestIpv4);
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(getPortForAnnotationPortName(DeviceId.deviceId(config.gatewayBridgeId()),
+                        config.gatewayExternalInterfaceName()))
+                .build();
+
+        OutboundPacket packet = new DefaultOutboundPacket(deviceId,
+                treatment, ByteBuffer.wrap(icmpRequestEth.serialize()));
+
+        packetService.emit(packet);
+    }
+
+    private void processResponsePacketFromExternalToHost(IPv4 icmpResponseIpv4, ICMP icmpResponse,
+                                                         OpenstackPortInfo openstackPortInfo) {
+        icmpResponse.resetChecksum();
+
+        icmpResponseIpv4.setDestinationAddress(openstackPortInfo.ip().toInt())
+                .resetChecksum();
+        icmpResponseIpv4.setPayload(icmpResponse);
+
+        Ethernet icmpResponseEth = new Ethernet();
+
+        icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4)
+                .setSourceMACAddress(GATEWAY_MAC)
+                .setDestinationMACAddress(openstackPortInfo.mac())
+                .setPayload(icmpResponseIpv4);
+
+        sendResponsePacketToHost(icmpResponseEth, openstackPortInfo);
+    }
+
+    private void sendResponsePacketToHost(Ethernet icmpResponseEth, OpenstackPortInfo openstackPortInfo) {
+        Map.Entry<String, OpenstackPortInfo> entry = openstackSwitchingService.openstackPortInfo().entrySet().stream()
+                .filter(e -> e.getValue().mac().equals(openstackPortInfo.mac()))
+                .findAny().orElse(null);
+
+        if (entry == null) {
+            return;
+        }
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(getPortForAnnotationPortName(openstackPortInfo.deviceId(), entry.getKey()))
+                .build();
+
+        OutboundPacket packet = new DefaultOutboundPacket(openstackPortInfo.deviceId(),
+                treatment, ByteBuffer.wrap(icmpResponseEth.serialize()));
+
+        packetService.emit(packet);
+    }
+
+    private OpenstackPortInfo getOpenstackPortInfo(Ip4Address sourceIp, MacAddress sourceMac) {
+        checkNotNull(openstackSwitchingService.openstackPortInfo(), "openstackportinfo collection can not be null");
+
+        return openstackSwitchingService.openstackPortInfo().values()
+                .stream().filter(p -> p.ip().equals(sourceIp) && p.mac().equals(sourceMac))
+                .findAny().orElse(null);
+    }
+
+    private short getIcmpId(ICMP icmp) {
+        return ByteBuffer.wrap(icmp.serialize(), 4, 2).getShort();
+    }
+
+    private Ip4Address pNatIpForPort(OpenstackPortInfo openstackPortInfo) {
+
+        OpenstackPort openstackPort = openstackService.ports().stream()
+                .filter(p -> p.deviceOwner().equals(NETWORK_ROUTER_INTERFACE) &&
+                        p.networkId().equals(openstackPortInfo.networkId()))
+                .findAny().orElse(null);
+
+        checkNotNull(openstackPort, "openstackPort can not be null");
+
+        return openstackService.router(openstackPort.deviceId())
+                .gatewayExternalInfo().externalFixedIps().values()
+                .stream().findAny().orElse(null);
+    }
+
+    private PortNumber getPortForAnnotationPortName(DeviceId deviceId, String match) {
+        Port port = deviceService.getPorts(deviceId).stream()
+                .filter(p -> p.annotations().value(PORTNAME).equals(match))
+                .findAny().orElse(null);
+
+        checkNotNull(port, "port cannot be null");
+
+        return port.number();
+    }
+}
diff --git a/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingArpHandler.java b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingArpHandler.java
new file mode 100644
index 0000000..8ae7282
--- /dev/null
+++ b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingArpHandler.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openstacknetworking.routing;
+
+import org.onlab.packet.ARP;
+import org.onlab.packet.EthType;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.openstackinterface.OpenstackInterfaceService;
+import org.onosproject.openstackinterface.OpenstackPort;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Handle ARP packet sent from Openstack Gateway nodes.
+ */
+public class OpenstackRoutingArpHandler {
+    protected final Logger log = getLogger(getClass());
+
+    private final PacketService packetService;
+    private final OpenstackInterfaceService openstackService;
+    private final OpenstackRoutingConfig config;
+    private static final String NETWORK_ROUTER_GATEWAY = "network:router_gateway";
+
+    /**
+     * Default constructor.
+     *
+     * @param packetService packet service
+     * @param openstackService openstackInterface service
+     * @param config openstackRoutingConfig
+     */
+    OpenstackRoutingArpHandler(PacketService packetService, OpenstackInterfaceService openstackService,
+                               OpenstackRoutingConfig config) {
+        this.packetService = packetService;
+        this.openstackService = checkNotNull(openstackService);
+        this.config = checkNotNull(config);
+    }
+
+    /**
+     * Requests ARP packet to GatewayNode.
+     *
+     * @param appId application id
+     */
+    public void requestPacket(ApplicationId appId) {
+
+        TrafficSelector arpSelector = DefaultTrafficSelector.builder()
+                .matchEthType(EthType.EtherType.ARP.ethType().toShort())
+                .build();
+
+        packetService.requestPackets(arpSelector,
+                PacketPriority.CONTROL,
+                appId,
+                Optional.of(DeviceId.deviceId(config.gatewayBridgeId())));
+    }
+
+    /**
+     * Handles ARP packet.
+     *
+     * @param context packet context
+     * @param ethernet ethernet
+     */
+    public void processArpPacketFromRouter(PacketContext context, Ethernet ethernet) {
+        checkNotNull(context, "context can not be null");
+        checkNotNull(ethernet, "ethernet can not be null");
+
+        log.info("arpEvent called from {} to {}",
+                Ip4Address.valueOf(((IPv4) ethernet.getPayload()).getSourceAddress()).toString(),
+                Ip4Address.valueOf(((IPv4) ethernet.getPayload()).getDestinationAddress()).toString());
+        ARP arp = (ARP) ethernet.getPayload();
+
+        if (arp.getOpCode() != ARP.OP_REQUEST) {
+            return;
+        }
+
+        IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress());
+        MacAddress targetMac = getTargetMacForTargetIp(targetIp.getIp4Address());
+
+        if (targetMac == MacAddress.NONE) {
+            return;
+        }
+
+        Ethernet ethReply = ARP.buildArpReply(targetIp.getIp4Address(),
+                targetMac, ethernet);
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(context.inPacket().receivedFrom().port())
+                .build();
+
+        packetService.emit(new DefaultOutboundPacket(
+                context.inPacket().receivedFrom().deviceId(),
+                treatment,
+                ByteBuffer.wrap(ethReply.serialize())));
+    }
+
+    private MacAddress getTargetMacForTargetIp(Ip4Address targetIp) {
+        OpenstackPort port = openstackService.ports().stream()
+                .filter(p -> p.deviceOwner().equals(NETWORK_ROUTER_GATEWAY))
+                .filter(p -> p.fixedIps().containsValue(targetIp.getIp4Address()))
+                .findAny().orElse(null);
+
+        if (port == null) {
+            return MacAddress.NONE;
+        }
+        return port.macAddress();
+    }
+}
diff --git a/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingConfig.java b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingConfig.java
new file mode 100644
index 0000000..0656a94
--- /dev/null
+++ b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingConfig.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openstacknetworking.routing;
+
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.config.Config;
+import org.slf4j.Logger;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Configuration object for OpenstackRouting service.
+ */
+public class OpenstackRoutingConfig extends Config<ApplicationId> {
+    protected final Logger log = getLogger(getClass());
+
+    public static final String PHYSICAL_ROUTER_MAC = "physicalRouterMac";
+    public static final String GATEWAY_BRIDGE_ID = "gatewayBridgeId";
+    public static final String GATEWAY_EXTERNAL_INTERFACE_NAME = "gatewayExternalInterfaceName";
+    public static final String GATEWAY_EXTERNAL_INTERFACE_MAC = "gatewayExternalInterfaceMac";
+
+    /**
+     * Returns physical router mac.
+     *
+     * @return physical router mac
+     */
+    public String physicalRouterMac() {
+        return this.get("physicalRouterMac", "");
+    }
+
+    /**
+     * Returns gateway's bridge id.
+     *
+     * @return bridge id
+     */
+    public String gatewayBridgeId() {
+        return this.get("gatewayBridgeId", "");
+    }
+
+    /**
+     * Returns gateway's external interface name.
+     *
+     * @return external interface name
+     */
+    public String gatewayExternalInterfaceName() {
+        return this.get("gatewayExternalInterfaceName", "");
+    }
+
+    /**
+     * Returns gateway's external interface mac.
+     *
+     * @return external interface mac
+     */
+    public String gatewayExternalInterfaceMac() {
+        return this.get("gatewayExternalInterfaceMac", "");
+    }
+}
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;
+            }
+        }
+    }
 }
diff --git a/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingRulePopulator.java b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingRulePopulator.java
index b226001..ab1d360 100644
--- a/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingRulePopulator.java
+++ b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingRulePopulator.java
@@ -71,6 +71,7 @@
     private final OpenstackInterfaceService openstackService;
     private final DeviceService deviceService;
     private final DriverService driverService;
+    private final OpenstackRoutingConfig config;
 
     private static final String PORTNAME_PREFIX_VM = "tap";
     private static final String PORTNAME_PREFIX_ROUTER = "qr";
@@ -93,9 +94,6 @@
     private OpenstackRouter router;
     private OpenstackRouterInterface routerInterface;
 
-    // TODO: This will be replaced to get the information from openstackswitchingservice.
-    private static final String EXTERNAL_INTERFACE_NAME = "veth0";
-
     /**
      * The constructor of openstackRoutingRulePopulator.
      *
@@ -104,20 +102,23 @@
      * @param flowObjectiveService FlowObjectiveService
      * @param deviceService DeviceService
      * @param driverService DriverService
+     * @param config OpenstackRoutingConfig
      */
     public OpenstackRoutingRulePopulator(ApplicationId appId, OpenstackInterfaceService openstackService,
-                                         FlowObjectiveService flowObjectiveService,
-                                         DeviceService deviceService, DriverService driverService) {
+                                         FlowObjectiveService flowObjectiveService, DeviceService deviceService,
+                                         DriverService driverService, OpenstackRoutingConfig config) {
         this.appId = appId;
         this.flowObjectiveService = flowObjectiveService;
-        this.openstackService = openstackService;
+        this.openstackService = checkNotNull(openstackService);
         this.deviceService = deviceService;
         this.driverService = driverService;
+        this.config = config;
     }
 
     /**
      * Populates flow rules for Pnat configurations.
-     *  @param inboundPacket Packet-in event packet
+     *
+     * @param inboundPacket Packet-in event packet
      * @param openstackPort Target VM information
      * @param portNum Pnat port number
      * @param externalIp external ip address
@@ -168,6 +169,7 @@
                 tBuilder.setUdpSrc(TpPort.tpPort(portNum));
                 break;
             default:
+                log.debug("Unsupported IPv4 protocol {}");
                 break;
         }
 
@@ -188,7 +190,7 @@
 
     private Port getPortNumOfExternalInterface() {
         return deviceService.getPorts(inboundPacket.receivedFrom().deviceId()).stream()
-                .filter(p -> p.annotations().value(PORTNAME).equals(EXTERNAL_INTERFACE_NAME))
+                .filter(p -> p.annotations().value(PORTNAME).equals(config.gatewayExternalInterfaceName()))
                 .findAny().orElse(null);
     }
 
@@ -239,7 +241,15 @@
         flowObjectiveService.forward(inboundPacket.receivedFrom().deviceId(), fo);
     }
 
-    private ExtensionTreatment buildNiciraExtenstion(DeviceId id, Ip4Address hostIp) {
+    /**
+     * Returns NiciraExtension treatment.
+     *
+     * @param id device id
+     * @param hostIp host ip
+     * @return NiciraExtension treatment
+     */
+
+    public ExtensionTreatment buildNiciraExtenstion(DeviceId id, Ip4Address hostIp) {
         Driver driver = driverService.getDriver(id);
         DriverHandler driverHandler = new DefaultDriverHandler(new DefaultDriverData(driver, id));
         ExtensionTreatmentResolver resolver = driverHandler.behaviour(ExtensionTreatmentResolver.class);
@@ -257,7 +267,13 @@
         return extensionInstruction;
     }
 
-    private PortNumber getTunnelPort(DeviceId deviceId) {
+    /**
+     * Returns port number of vxlan tunnel.
+     *
+     * @param deviceId device id
+     * @return port number of vxlan tunnel
+     */
+    public PortNumber getTunnelPort(DeviceId deviceId) {
         Port port = deviceService.getPorts(deviceId).stream()
                 .filter(p -> p.annotations().value(PORTNAME).equals(PORTNAME_PREFIX_TUNNEL))
                 .findAny().orElse(null);
@@ -343,16 +359,11 @@
     }
 
     private Device getGatewayNode() {
-        return checkNotNull(StreamSupport.stream(deviceService.getAvailableDevices().spliterator(), false)
-                .filter(d -> checkGatewayNode(d.id()))
-                .findAny()
-                .orElse(null));
+        return checkNotNull(deviceService.getDevice(DeviceId.deviceId(config.gatewayBridgeId())));
     }
 
     private boolean checkGatewayNode(DeviceId deviceId) {
-        return !deviceService.getPorts(deviceId).stream().anyMatch(port ->
-                port.annotations().value(PORTNAME).startsWith(PORTNAME_PREFIX_ROUTER) ||
-                        port.annotations().value(PORTNAME).startsWith(PORTNAME_PREFIX_VM));
+        return deviceId.toString().equals(config.gatewayBridgeId());
     }
 
     private long getVni(OpenstackPort openstackPort) {