[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/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())));
     }