add a virtual gateway for reactive routing

  There is no physical gateway in SDN network.
  However a host needs a gateway when it tries to communicate with a remote host.
  So we designed a virtual gateway for SDN network.
  The virtual gateway can have multiple IP addresses.
  Each IP address is used as the default gateway address of an IP prefix.
  We only configure one MAC address to the virtual gateway.
  You can choose any MAC address from the BGP speakers as the virtual gateway MAC address.
  We configure this MAC address staticly in the sdnip.json configuration file.

Change-Id: I2a72bef797fc55d25bb5473e8fca624ad659e1d1
diff --git a/apps/reactive-routing/pom.xml b/apps/reactive-routing/pom.xml
index d7bde79..a5059ca 100644
--- a/apps/reactive-routing/pom.xml
+++ b/apps/reactive-routing/pom.xml
@@ -41,6 +41,11 @@
       <artifactId>onos-app-routing</artifactId>
       <version>${project.version}</version>
     </dependency>
+
+    <dependency>
+      <groupId>org.onosproject</groupId>
+      <artifactId>onos-api</artifactId>
+    </dependency>
   </dependencies>
 
 </project>
\ No newline at end of file
diff --git a/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java b/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java
index a74387f..46f7fd9 100644
--- a/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java
+++ b/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java
@@ -16,13 +16,17 @@
 package org.onosproject.reactive.routing;
 import static org.slf4j.LoggerFactory.getLogger;
 
+import java.nio.ByteBuffer;
+
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.ARP;
 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;
@@ -40,6 +44,7 @@
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
 import org.onosproject.routing.RoutingService;
+import org.onosproject.routing.config.RoutingConfigurationService;
 import org.slf4j.Logger;
 
 /**
@@ -64,6 +69,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected RoutingService routingService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected RoutingConfigurationService config;
+
     private ApplicationId appId;
 
     private ReactiveRoutingProcessor processor =
@@ -80,6 +88,9 @@
         selector.matchEthType(Ethernet.TYPE_IPV4);
         packetService.requestPackets(selector.build(),
                                      PacketPriority.REACTIVE, appId);
+        selector.matchEthType(Ethernet.TYPE_ARP);
+        packetService.requestPackets(selector.build(),
+                                     PacketPriority.REACTIVE, appId);
 
         log.info("SDN-IP Reactive Routing Started");
     }
@@ -100,31 +111,56 @@
             if (ethPkt == null) {
                 return;
             }
-
-            // In theory, we do not need to check whether it is Ethernet
-            // TYPE_IPV4. However, due to the current implementation of the
-            // packetService, we will receive all packets from all subscribers.
-            // Hence, we have to check the Ethernet type again here.
-            if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) {
-                return;
-            }
-
-            // Parse packet
-            IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
-            IpAddress dstIp =
-                    IpAddress.valueOf(ipv4Packet.getDestinationAddress());
-            IpAddress srcIp =
-                    IpAddress.valueOf(ipv4Packet.getSourceAddress());
             ConnectPoint srcConnectPoint = pkt.receivedFrom();
-            MacAddress srcMac = ethPkt.getSourceMAC();
-            routingService.packetReactiveProcessor(dstIp, srcIp,
-                                                   srcConnectPoint, srcMac);
 
-            // TODO emit packet first or packetReactiveProcessor first
-            ConnectPoint egressConnectPoint = null;
-            egressConnectPoint = routingService.getEgressConnectPoint(dstIp);
-            if (egressConnectPoint != null) {
-                forwardPacketToDst(context, egressConnectPoint);
+            switch (ethPkt.getEtherType()) {
+            case Ethernet.TYPE_ARP:
+                ARP arpPacket = (ARP) ethPkt.getPayload();
+                Ip4Address targetIpAddress = Ip4Address
+                        .valueOf(arpPacket.getTargetProtocolAddress());
+                // Only when it is an ARP request packet and the target IP
+                // address is a virtual gateway IP address, then it will be
+                // processed.
+                if (arpPacket.getOpCode() == ARP.OP_REQUEST
+                        && config.isVirtualGatewayIpAddress(targetIpAddress)) {
+                    MacAddress gatewayMacAddress =
+                            config.getVirtualGatewayMacAddress();
+                    if (gatewayMacAddress == null) {
+                        break;
+                    }
+                    Ethernet eth = ARP.buildArpReply(targetIpAddress,
+                                                     gatewayMacAddress,
+                                                     ethPkt);
+
+                    TrafficTreatment.Builder builder =
+                            DefaultTrafficTreatment.builder();
+                    builder.setOutput(srcConnectPoint.port());
+                    packetService.emit(new DefaultOutboundPacket(
+                            srcConnectPoint.deviceId(),
+                            builder.build(),
+                            ByteBuffer.wrap(eth.serialize())));
+                }
+                break;
+            case Ethernet.TYPE_IPV4:
+                // Parse packet
+                IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
+                IpAddress dstIp =
+                        IpAddress.valueOf(ipv4Packet.getDestinationAddress());
+                IpAddress srcIp =
+                        IpAddress.valueOf(ipv4Packet.getSourceAddress());
+                MacAddress srcMac = ethPkt.getSourceMAC();
+                routingService.packetReactiveProcessor(dstIp, srcIp,
+                                                       srcConnectPoint, srcMac);
+
+                // TODO emit packet first or packetReactiveProcessor first
+                ConnectPoint egressConnectPoint = null;
+                egressConnectPoint = routingService.getEgressConnectPoint(dstIp);
+                if (egressConnectPoint != null) {
+                    forwardPacketToDst(context, egressConnectPoint);
+                }
+                break;
+            default:
+                break;
             }
         }
     }