[ONOS-7444] Optimize SONA gw doesn't use vrouter app and quagga anymore
- Done: Deriving MAC address from external peer router, SNAT, Floating IP-based routing, SNAT with VLAN
- Todo: Floating IP-based routing with VLAN, GW loadbalancing

Change-Id: I718b71eaf64a40049fc86687f10432446bb1b5bf
diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkService.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkService.java
index 2974c47..7ff07bf 100644
--- a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkService.java
+++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkService.java
@@ -106,8 +106,9 @@
      *
      * @param externalGateway external gateway information
      * @param router router which owns externalGateway
+     * @param vlanId vlan id of external network
      */
-    void deriveExternalPeerRouterMac(ExternalGateway externalGateway, Router router);
+    void deriveExternalPeerRouterMac(ExternalGateway externalGateway, Router router, VlanId vlanId);
 
     /**
      * Deletes external router with supplied external gateway.
@@ -165,6 +166,14 @@
     ExternalPeerRouter externalPeerRouter(IpAddress ipAddress);
 
     /**
+     * Returns external router with supplied external gateway.
+     *
+     * @param externalGateway external gateway information
+     * @return external router
+     */
+    ExternalPeerRouter externalPeerRouter(ExternalGateway externalGateway);
+
+    /**
      * Returns external peer router list.
      *
      * @return external peer router list
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/UpdateExternalPeerRouterVlanCommand.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/UpdateExternalPeerRouterVlanCommand.java
index eed62e7..e0aae53 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/UpdateExternalPeerRouterVlanCommand.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/UpdateExternalPeerRouterVlanCommand.java
@@ -24,6 +24,10 @@
 import org.onosproject.cli.AbstractShellCommand;
 import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterService;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.Subnet;
 
 import java.util.List;
 
@@ -47,37 +51,62 @@
 
     @Override
     protected void execute() {
-        OpenstackNetworkService service = AbstractShellCommand.get(OpenstackNetworkService.class);
+        OpenstackNetworkService osNetService = AbstractShellCommand.get(OpenstackNetworkService.class);
+        OpenstackRouterService osRouterService = AbstractShellCommand.get(OpenstackRouterService.class);
 
         IpAddress externalPeerIpAddress = IpAddress.valueOf(
                 IpAddress.Version.INET, Ip4Address.valueOf(ipAddress).toOctets());
 
-        if (service.externalPeerRouters().isEmpty()) {
+        if (osNetService.externalPeerRouters().isEmpty()) {
             print(NO_ELEMENT);
             return;
-        } else if (service.externalPeerRouters().stream()
+        } else if (osNetService.externalPeerRouters().stream()
                 .noneMatch(router -> router.externalPeerRouterIp().toString().equals(ipAddress))) {
             print(NO_ELEMENT);
             return;
         }
 
+        Subnet subnet = osNetService.subnets().stream()
+                .filter(s -> s.getGateway().equals(ipAddress))
+                .findAny().orElse(null);
+        if (subnet == null) {
+            return;
+        }
+
+        Network network = osNetService.network(subnet.getNetworkId());
+        if (network == null) {
+            return;
+        }
+
+        Router router = osRouterService.routers().stream()
+                .filter(r -> r.getExternalGatewayInfo().getNetworkId().equals(network.getId()))
+                .findAny().orElse(null);
+
+        if (router == null) {
+            return;
+        }
+
         try {
             if (vlanId.equals(NONE)) {
-                service.updateExternalPeerRouterVlan(externalPeerIpAddress, VlanId.NONE);
+                osNetService.updateExternalPeerRouterVlan(externalPeerIpAddress, VlanId.NONE);
+                osNetService.deriveExternalPeerRouterMac(router.getExternalGatewayInfo(), router, VlanId.NONE);
             } else {
-                service.updateExternalPeerRouterVlan(externalPeerIpAddress, VlanId.vlanId(vlanId));
+                osNetService.updateExternalPeerRouterVlan(externalPeerIpAddress, VlanId.vlanId(vlanId));
+                osNetService.deriveExternalPeerRouterMac(
+                        router.getExternalGatewayInfo(), router, VlanId.vlanId(vlanId));
+
             }
         } catch (IllegalArgumentException e) {
             log.error("Exception occurred because of {}", e.toString());
         }
 
         print(FORMAT, "Router IP", "Mac Address", "VLAN ID");
-        List<ExternalPeerRouter> routers = Lists.newArrayList(service.externalPeerRouters());
+        List<ExternalPeerRouter> routers = Lists.newArrayList(osNetService.externalPeerRouters());
 
-        for (ExternalPeerRouter router: routers) {
-            print(FORMAT, router.externalPeerRouterIp(),
-                    router.externalPeerRouterMac().toString(),
-                    router.externalPeerRouterVlanId());
+        for (ExternalPeerRouter r: routers) {
+            print(FORMAT, r.externalPeerRouterIp(),
+                    r.externalPeerRouterMac().toString(),
+                    r.externalPeerRouterVlanId());
         }
     }
 }
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java
index ac1e796..e67270c 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java
@@ -124,7 +124,6 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected OpenstackNodeService osNodeService;
 
-
     private final OpenstackNetworkStoreDelegate delegate = new InternalNetworkStoreDelegate();
 
     private ConsistentMap<String, ExternalPeerRouter> externalPeerRouterMap;
@@ -342,7 +341,22 @@
     }
 
     @Override
-    public void deriveExternalPeerRouterMac(ExternalGateway externalGateway, Router router) {
+    public ExternalPeerRouter externalPeerRouter(ExternalGateway externalGateway) {
+        IpAddress ipAddress = getExternalPeerRouterIp(externalGateway);
+
+        if (ipAddress == null) {
+            return null;
+        }
+
+        if (externalPeerRouterMap.containsKey(ipAddress.toString())) {
+            return externalPeerRouterMap.get(ipAddress.toString()).value();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void deriveExternalPeerRouterMac(ExternalGateway externalGateway, Router router, VlanId vlanId) {
         log.info("deriveExternalPeerRouterMac called");
 
         IpAddress sourceIp = getExternalGatewaySourceIp(externalGateway, router);
@@ -364,7 +378,7 @@
         Ethernet ethRequest = ARP.buildArpRequest(sourceMac.toBytes(),
                 sourceIp.toOctets(),
                 targetIp.toOctets(),
-                VlanId.NO_VID);
+                vlanId.id());
 
         if (osNodeService.completeNodes(OpenstackNode.NodeType.GATEWAY).isEmpty()) {
             log.warn("There's no complete gateway");
@@ -400,7 +414,7 @@
                 ByteBuffer.wrap(ethRequest.serialize())));
 
         externalPeerRouterMap.put(
-                targetIp.toString(), new DefaultExternalPeerRouter(targetIp, MacAddress.NONE, VlanId.NONE));
+                targetIp.toString(), new DefaultExternalPeerRouter(targetIp, MacAddress.NONE, vlanId));
 
         log.info("Initializes external peer router map with peer router IP {}", targetIp.toString());
     }
@@ -428,32 +442,6 @@
         }
 
     }
-    private IpAddress getExternalGatewaySourceIp(ExternalGateway externalGateway, Router router) {
-        Port exGatewayPort = ports(externalGateway.getNetworkId())
-                .stream()
-                .filter(port -> Objects.equals(port.getDeviceId(), router.getId()))
-                .findAny().orElse(null);
-        if (exGatewayPort == null) {
-            log.warn("no external gateway port for router({})", router.getName());
-            return null;
-        }
-
-        IP ipAddress = exGatewayPort.getFixedIps().stream().findFirst().orElse(null);
-
-        return ipAddress == null ? null : IpAddress.valueOf(ipAddress.getIpAddress());
-    }
-
-    private IpAddress getExternalPeerRouterIp(ExternalGateway externalGateway) {
-        Optional<Subnet> externalSubnet = subnets(externalGateway.getNetworkId())
-                .stream()
-                .findFirst();
-
-        if (externalSubnet.isPresent()) {
-            return IpAddress.valueOf(externalSubnet.get().getGateway());
-        } else {
-            return null;
-        }
-    }
 
     @Override
     public void updateExternalPeerRouterMac(IpAddress ipAddress, MacAddress macAddress) {
@@ -497,9 +485,9 @@
     public void updateExternalPeerRouterVlan(IpAddress ipAddress, VlanId vlanId) {
 
         try {
-            externalPeerRouterMap.computeIfPresent(ipAddress.toString(), (id, existing) -> {
-                return new DefaultExternalPeerRouter(ipAddress, existing.externalPeerRouterMac(), vlanId);
-            });
+            externalPeerRouterMap.computeIfPresent(ipAddress.toString(), (id, existing) ->
+                    new DefaultExternalPeerRouter(ipAddress, existing.externalPeerRouterMac(), vlanId));
+
         } catch (Exception e) {
             log.error("Exception occurred because of {}", e.toString());
         }
@@ -534,4 +522,27 @@
             }
         }
     }
+
+    private IpAddress getExternalGatewaySourceIp(ExternalGateway externalGateway, Router router) {
+        Port exGatewayPort = ports(externalGateway.getNetworkId())
+                .stream()
+                .filter(port -> Objects.equals(port.getDeviceId(), router.getId()))
+                .findAny().orElse(null);
+        if (exGatewayPort == null) {
+            log.warn("no external gateway port for router({})", router.getName());
+            return null;
+        }
+
+        IP ipAddress = exGatewayPort.getFixedIps().stream().findFirst().orElse(null);
+
+        return ipAddress == null ? null : IpAddress.valueOf(ipAddress.getIpAddress());
+    }
+
+    private IpAddress getExternalPeerRouterIp(ExternalGateway externalGateway) {
+        Optional<Subnet> externalSubnet = subnets(externalGateway.getNetworkId())
+                .stream()
+                .findFirst();
+
+        return externalSubnet.map(subnet -> IpAddress.valueOf(subnet.getGateway())).orElse(null);
+    }
 }
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
index 5dee134..657d7a0 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
@@ -34,8 +34,8 @@
 import org.onosproject.net.packet.PacketContext;
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
-import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
 import org.onosproject.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
 import org.onosproject.openstacknetworking.api.OpenstackRouterService;
 import org.onosproject.openstacknode.api.OpenstackNode;
 import org.onosproject.openstacknode.api.OpenstackNodeService;
@@ -112,6 +112,7 @@
                     .filter(ip -> ip.getFloatingIpAddress().equals(targetIp.toString()))
                     .findAny().orElse(null);
 
+            //In case target ip is for associated floating ip, sets target mac to vm's.
             if (floatingIP != null && floatingIP.getPortId() != null) {
                 targetMac = MacAddress.valueOf(osNetworkService.port(floatingIP.getPortId()).getMacAddress());
             }
@@ -128,9 +129,9 @@
             Ethernet ethReply = ARP.buildArpReply(targetIp.getIp4Address(),
                     targetMac, ethernet);
 
+
             TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                    .setOutput(context.inPacket().receivedFrom().port())
-                    .build();
+                    .setOutput(context.inPacket().receivedFrom().port()).build();
 
             packetService.emit(new DefaultOutboundPacket(
                     context.inPacket().receivedFrom().deviceId(),
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
index 6d2f70b..42a3da4 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
@@ -49,6 +49,7 @@
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.instructions.ExtensionTreatment;
 import org.onosproject.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
 import org.onosproject.openstacknetworking.api.InstancePort;
 import org.onosproject.openstacknetworking.api.InstancePortEvent;
 import org.onosproject.openstacknetworking.api.InstancePortListener;
@@ -211,16 +212,17 @@
             setRouterAdminRules(network.getProviderSegID(), network.getNetworkType(), !osRouter.isAdminStateUp());
         });
 
+
+        ExternalPeerRouter externalPeerRouter = osNetworkService.externalPeerRouter(exGateway);
+        VlanId vlanId = externalPeerRouter == null ? VlanId.NONE : externalPeerRouter.externalPeerRouterVlanId();
+
         if (exGateway == null) {
             osNetworkService.deleteExternalPeerRouter(exGateway);
-            osRouterService.routerInterfaces(osRouter.getId()).forEach(iface -> {
-                setSourceNat(osRouter, iface, false);
-            });
+            osRouterService.routerInterfaces(osRouter.getId()).forEach(iface -> setSourceNat(iface, false));
         } else {
-            osNetworkService.deriveExternalPeerRouterMac(exGateway, osRouter);
-            osRouterService.routerInterfaces(osRouter.getId()).forEach(iface -> {
-                setSourceNat(osRouter, iface, exGateway.isEnableSnat());
-            });
+            osNetworkService.deriveExternalPeerRouterMac(exGateway, osRouter, vlanId);
+            osRouterService.routerInterfaces(osRouter.getId()).forEach(iface ->
+                    setSourceNat(iface, exGateway.isEnableSnat()));
         }
     }
 
@@ -251,7 +253,7 @@
         setGatewayIcmp(osSubnet, true);
         ExternalGateway exGateway = osRouter.getExternalGatewayInfo();
         if (exGateway != null && exGateway.isEnableSnat()) {
-            setSourceNat(osRouter, osRouterIface, true);
+            setSourceNat(osRouterIface, true);
         }
         log.info("Connected subnet({}) to {}", osSubnet.getCidr(), osRouter.getName());
     }
@@ -275,12 +277,12 @@
         setGatewayIcmp(osSubnet, false);
         ExternalGateway exGateway = osRouter.getExternalGatewayInfo();
         if (exGateway != null && exGateway.isEnableSnat()) {
-            setSourceNat(osRouter, osRouterIface, false);
+            setSourceNat(osRouterIface, false);
         }
         log.info("Disconnected subnet({}) from {}", osSubnet.getCidr(), osRouter.getName());
     }
 
-    private void setSourceNat(Router osRouter, RouterInterface routerIface, boolean install) {
+    private void setSourceNat(RouterInterface routerIface, boolean install) {
         Subnet osSubnet = osNetworkService.subnet(routerIface.getSubnetId());
         Network osNet = osNetworkService.network(osSubnet.getNetworkId());
 
@@ -320,8 +322,8 @@
 
         osNodeService.completeNodes(OpenstackNode.NodeType.GATEWAY)
                 .forEach(gwNode -> {
-                        instancePortService.instancePorts(netId).stream()
-                            .forEach(port -> setRulesForSnatIngressRule(gwNode.intgBridge(),
+                        instancePortService.instancePorts(netId)
+                                .forEach(port -> setRulesForSnatIngressRule(gwNode.intgBridge(),
                                     Long.parseLong(osNet.getProviderSegID()),
                                     IpPrefix.valueOf(port.ipAddress(), 32),
                                     port.deviceId(),
@@ -427,13 +429,11 @@
         }
 
         IpAddress gatewayIp = IpAddress.valueOf(osSubnet.getGateway());
-        osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
+        osNodeService.completeNodes(GATEWAY).forEach(gNode ->
             setGatewayIcmpRule(
                     gatewayIp,
                     gNode.intgBridge(),
-                    install
-            );
-        });
+                    install));
 
         final String updateStr = install ? MSG_ENABLED : MSG_DISABLED;
         log.debug(updateStr + "ICMP to {}", osSubnet.getGateway());
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandler.java
index 13c9e18..f82b1c5 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandler.java
@@ -26,6 +26,7 @@
 import org.onlab.packet.IPv4;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
 import org.onlab.util.KryoNamespace;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
@@ -39,6 +40,7 @@
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
 import org.onosproject.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
 import org.onosproject.openstacknetworking.api.InstancePort;
 import org.onosproject.openstacknetworking.api.InstancePortService;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
@@ -177,25 +179,25 @@
                                    ICMP icmp) {
         InstancePort instPort = instancePortService.instancePort(srcMac);
         if (instPort == null) {
-            log.trace(ERR_REQ + "unknown source host(MAC:{})", srcMac);
+            log.info(ERR_REQ + "unknown source host(MAC:{})", srcMac);
             return;
         }
 
         IpAddress srcIp = IpAddress.valueOf(ipPacket.getSourceAddress());
         Subnet srcSubnet = getSourceSubnet(instPort, srcIp);
         if (srcSubnet == null) {
-            log.trace(ERR_REQ + "unknown source subnet(IP:{})", srcIp);
+            log.info(ERR_REQ + "unknown source subnet(IP:{})", srcIp);
             return;
         }
         if (Strings.isNullOrEmpty(srcSubnet.getGateway())) {
-            log.trace(ERR_REQ + "source subnet(ID:{}, CIDR:{}) has no gateway",
+            log.info(ERR_REQ + "source subnet(ID:{}, CIDR:{}) has no gateway",
                     srcSubnet.getId(), srcSubnet.getCidr());
             return;
         }
 
-        MacAddress externalPeerRouterMac = externalPeerRouterMac(srcSubnet);
-        if (externalPeerRouterMac == null) {
-            log.trace(ERR_REQ + "failed to get external peer router mac");
+        ExternalPeerRouter externalPeerRouter = externalPeerRouter(srcSubnet);
+        if (externalPeerRouter == null) {
+            log.info(ERR_REQ + "failed to get external peer router");
             return;
         }
 
@@ -210,7 +212,7 @@
                 return;
             }
 
-            sendRequestForExternal(ipPacket, srcDevice, externalIp, externalPeerRouterMac(srcSubnet));
+            sendRequestForExternal(ipPacket, srcDevice, externalIp, externalPeerRouter);
             String icmpInfoKey = String.valueOf(getIcmpId(icmp))
                     .concat(String.valueOf(externalIp.getIp4Address().toInt()))
                     .concat(String.valueOf(ipPacket.getDestinationAddress()));
@@ -226,7 +228,7 @@
         }
     }
 
-    private MacAddress externalPeerRouterMac(Subnet subnet) {
+    private ExternalPeerRouter externalPeerRouter(Subnet subnet) {
         RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
                 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
                 .findAny().orElse(null);
@@ -244,7 +246,7 @@
 
         ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
 
-        return osNetworkService.externalPeerRouterMac(exGatewayInfo);
+        return osNetworkService.externalPeerRouter(exGatewayInfo);
     }
 
     private void handleEchoReply(IPv4 ipPacket, ICMP icmp) {
@@ -355,7 +357,7 @@
     }
 
     private void sendRequestForExternal(IPv4 ipPacket, DeviceId srcDevice,
-                                        IpAddress srcNatIp, MacAddress externalRouterMac) {
+                                        IpAddress srcNatIp, ExternalPeerRouter externalPeerRouter) {
         ICMP icmpReq = (ICMP) ipPacket.getPayload();
         icmpReq.resetChecksum();
         ipPacket.setSourceAddress(srcNatIp.getIp4Address().toInt()).resetChecksum();
@@ -364,8 +366,13 @@
         Ethernet icmpRequestEth = new Ethernet();
         icmpRequestEth.setEtherType(Ethernet.TYPE_IPV4)
                 .setSourceMACAddress(DEFAULT_GATEWAY_MAC)
-                .setDestinationMACAddress(externalRouterMac)
-                .setPayload(ipPacket);
+                .setDestinationMACAddress(externalPeerRouter.externalPeerRouterMac());
+
+        if (!externalPeerRouter.externalPeerRouterVlanId().equals(VlanId.NONE)) {
+            icmpRequestEth.setVlanID(externalPeerRouter.externalPeerRouterVlanId().toShort());
+        }
+
+        icmpRequestEth.setPayload(ipPacket);
 
         OpenstackNode osNode = osNodeService.node(srcDevice);
         if (osNode == null) {
@@ -386,7 +393,13 @@
     }
 
     private void processReplyFromExternal(IPv4 ipPacket, InstancePort instPort) {
+
+        if (instPort.networkId() == null) {
+            return;
+        }
+
         ICMP icmpReply = (ICMP) ipPacket.getPayload();
+
         icmpReply.resetChecksum();
 
         ipPacket.setDestinationAddress(instPort.ipAddress().getIp4Address().toInt())
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java
index 9f792bd..d4db5a3 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java
@@ -24,7 +24,6 @@
 import org.onlab.packet.IPv4;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
 import org.onlab.packet.TCP;
 import org.onlab.packet.TpPort;
 import org.onlab.packet.UDP;
@@ -43,6 +42,7 @@
 import org.onosproject.net.packet.PacketContext;
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
+import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
 import org.onosproject.openstacknetworking.api.InstancePort;
 import org.onosproject.openstacknetworking.api.InstancePortService;
 import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
@@ -192,23 +192,24 @@
             return;
         }
 
-        MacAddress externalPeerRouterMac = externalPeerRouterMac(srcSubnet);
-        if (externalPeerRouterMac == null) {
+        ExternalPeerRouter externalPeerRouter = externalPeerRouter(srcSubnet);
+        if (externalPeerRouter == null) {
             return;
         }
 
         populateSnatFlowRules(context.inPacket(),
                 srcInstPort,
                 TpPort.tpPort(patPort),
-                externalGatewayIp, externalPeerRouterMac);
+                externalGatewayIp, externalPeerRouter);
+
 
         packetOut(eth.duplicate(),
                 packetIn.receivedFrom().deviceId(),
                 patPort,
-                externalGatewayIp, externalPeerRouterMac);
+                externalGatewayIp, externalPeerRouter);
     }
 
-    private MacAddress externalPeerRouterMac(Subnet subnet) {
+    private ExternalPeerRouter externalPeerRouter(Subnet subnet) {
         RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
                 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
                 .findAny().orElse(null);
@@ -225,8 +226,7 @@
         }
 
         ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
-
-        return osNetworkService.externalPeerRouterMac(exGatewayInfo);
+        return osNetworkService.externalPeerRouter(exGatewayInfo);
     }
 
     private Subnet getSourceSubnet(InstancePort instance, IpAddress srcIp) {
@@ -282,7 +282,7 @@
     }
 
     private void populateSnatFlowRules(InboundPacket packetIn, InstancePort srcInstPort,
-                                       TpPort patPort, IpAddress externalIp, MacAddress externalPeerRouterMac) {
+                                       TpPort patPort, IpAddress externalIp, ExternalPeerRouter externalPeerRouter) {
         Network osNet = osNetworkService.network(srcInstPort.networkId());
         if (osNet == null) {
             final String error = String.format("%s network %s not found",
@@ -294,13 +294,14 @@
                 osNet.getProviderSegID(),
                 osNet.getNetworkType(),
                 externalIp,
+                externalPeerRouter,
                 patPort,
                 packetIn);
 
         setUpstreamRules(osNet.getProviderSegID(),
                 osNet.getNetworkType(),
                 externalIp,
-                externalPeerRouterMac,
+                externalPeerRouter,
                 patPort,
                 packetIn);
     }
@@ -308,6 +309,7 @@
     private void setDownstreamRules(InstancePort srcInstPort, String segmentId,
                                     NetworkType networkType,
                                     IpAddress externalIp,
+                                    ExternalPeerRouter externalPeerRouter,
                                     TpPort patPort,
                                     InboundPacket packetIn) {
         IPv4 iPacket = (IPv4) packetIn.parsed().getPayload();
@@ -323,6 +325,11 @@
                 .setEthDst(packetIn.parsed().getSourceMAC())
                 .setIpDst(internalIp);
 
+        if (!externalPeerRouter.externalPeerRouterVlanId().equals(VlanId.NONE)) {
+            sBuilder.matchVlanId(externalPeerRouter.externalPeerRouterVlanId());
+            tBuilder.popVlan();
+        }
+
         switch (networkType) {
             case VXLAN:
                 tBuilder.setTunnelId(Long.parseLong(segmentId));
@@ -389,7 +396,7 @@
     }
 
     private void setUpstreamRules(String segmentId, NetworkType networkType,
-                                  IpAddress externalIp, MacAddress externalPeerRouterMac,
+                                  IpAddress externalIp, ExternalPeerRouter externalPeerRouter,
                                   TpPort patPort,
                                   InboundPacket packetIn) {
         IPv4 iPacket = (IPv4) packetIn.parsed().getPayload();
@@ -422,20 +429,24 @@
                 sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
                         .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
                 tBuilder.setTcpSrc(patPort)
-                        .setEthDst(externalPeerRouterMac);
+                        .setEthDst(externalPeerRouter.externalPeerRouterMac());
                 break;
             case IPv4.PROTOCOL_UDP:
                 UDP udpPacket = (UDP) iPacket.getPayload();
                 sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
                         .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
                 tBuilder.setUdpSrc(patPort)
-                        .setEthDst(externalPeerRouterMac);
+                        .setEthDst(externalPeerRouter.externalPeerRouterMac());
                 break;
             default:
                 log.debug("Unsupported IPv4 protocol {}");
                 break;
         }
 
+        if (!externalPeerRouter.externalPeerRouterVlanId().equals(VlanId.NONE)) {
+            tBuilder.pushVlan().setVlanId(externalPeerRouter.externalPeerRouterVlanId());
+        }
+
         tBuilder.setIpSrc(externalIp);
         osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
             TrafficTreatment.Builder tmpBuilder =
@@ -454,7 +465,7 @@
     }
 
     private void packetOut(Ethernet ethPacketIn, DeviceId srcDevice, int patPort,
-                           IpAddress externalIp, MacAddress externalPeerRouterMac) {
+                           IpAddress externalIp, ExternalPeerRouter externalPeerRouter) {
         IPv4 iPacket = (IPv4) ethPacketIn.getPayload();
         switch (iPacket.getProtocol()) {
             case IPv4.PROTOCOL_TCP:
@@ -479,8 +490,14 @@
         iPacket.setSourceAddress(externalIp.toString());
         iPacket.resetChecksum();
         iPacket.setParent(ethPacketIn);
-        ethPacketIn.setDestinationMACAddress(externalPeerRouterMac);
+        ethPacketIn.setSourceMACAddress(DEFAULT_GATEWAY_MAC);
+        ethPacketIn.setDestinationMACAddress(externalPeerRouter.externalPeerRouterMac());
         ethPacketIn.setPayload(iPacket);
+
+        if (!externalPeerRouter.externalPeerRouterVlanId().equals(VlanId.NONE)) {
+            ethPacketIn.setVlanID(externalPeerRouter.externalPeerRouterVlanId().toShort());
+        }
+
         ethPacketIn.resetChecksum();
 
         OpenstackNode srcNode = osNodeService.node(srcDevice);
@@ -490,11 +507,11 @@
             throw new IllegalStateException(error);
         }
 
-        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                .setOutput(srcNode.uplinkPortNum()).build();
+        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+
         packetService.emit(new DefaultOutboundPacket(
                 srcDevice,
-                treatment,
+                tBuilder.setOutput(srcNode.uplinkPortNum()).build(),
                 ByteBuffer.wrap(ethPacketIn.serialize())));
     }