Add ICMP handler in bgp router app

Change-Id: I22a1dcdf5285e08c691767eb1ca41437e7ce1874
diff --git a/apps/bgprouter/src/main/java/org/onosproject/bgprouter/BgpRouter.java b/apps/bgprouter/src/main/java/org/onosproject/bgprouter/BgpRouter.java
index f461269..b6c921e 100644
--- a/apps/bgprouter/src/main/java/org/onosproject/bgprouter/BgpRouter.java
+++ b/apps/bgprouter/src/main/java/org/onosproject/bgprouter/BgpRouter.java
@@ -138,6 +138,8 @@
 
     private TunnellingConnectivityManager connectivityManager;
 
+    private IcmpHandler icmpHandler;
+
     private InternalTableHandler provisionStaticTables = new InternalTableHandler();
 
     @Activate
@@ -154,10 +156,14 @@
                                                                 packetService,
                                                                 flowService);
 
+        icmpHandler = new IcmpHandler(configService, packetService);
+
         routingService.start(new InternalFibListener());
 
         connectivityManager.start();
 
+        icmpHandler.start();
+
         log.info("BgpRouter started");
     }
 
@@ -165,6 +171,7 @@
     protected void deactivate() {
         routingService.stop();
         connectivityManager.stop();
+        icmpHandler.stop();
         provisionStaticTables.provision(false, configService.getInterfaces());
 
         groupService.removeListener(groupListener);
@@ -186,6 +193,7 @@
             deviceId = s.interfaceAddresses().get(0).connectPoint().deviceId();
             break;
         }
+
         log.info("Router dpid: {}", deviceId);
         log.info("Control Plane OVS dpid: {}", ctrlDeviceId);
     }
diff --git a/apps/bgprouter/src/main/java/org/onosproject/bgprouter/IcmpHandler.java b/apps/bgprouter/src/main/java/org/onosproject/bgprouter/IcmpHandler.java
new file mode 100644
index 0000000..1af3fd9
--- /dev/null
+++ b/apps/bgprouter/src/main/java/org/onosproject/bgprouter/IcmpHandler.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2015 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.bgprouter;
+
+import java.nio.ByteBuffer;
+
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.ICMP;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.IpAddress;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.host.InterfaceIpAddress;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.routing.config.Interface;
+import org.onosproject.routing.config.RoutingConfigurationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class IcmpHandler {
+
+    private static final Logger log = LoggerFactory.getLogger(IcmpHandler.class);
+
+    private final PacketService packetService;
+    private final RoutingConfigurationService configService;
+
+    private final IcmpProcessor processor = new IcmpProcessor();
+
+
+    public IcmpHandler(RoutingConfigurationService configService,
+                       PacketService packetService) {
+        this.configService = configService;
+        this.packetService = packetService;
+    }
+
+    public void start() {
+        packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 4);
+    }
+
+    public void stop() {
+        packetService.removeProcessor(processor);
+    }
+
+    private void processPacketIn(InboundPacket pkt) {
+
+        boolean ipMatches = false;
+        Ethernet ethernet = pkt.parsed();
+        IPv4 ipv4 = (IPv4) ethernet.getPayload();
+        ConnectPoint connectPoint = pkt.receivedFrom();
+        IpAddress destIpAddress = IpAddress.valueOf(ipv4.getDestinationAddress());
+        Interface targetInterface = configService.getMatchingInterface(destIpAddress);
+
+        if (targetInterface == null) {
+            log.trace("No matching interface for {}", destIpAddress);
+            return;
+        }
+
+        for (InterfaceIpAddress interfaceIpAddress: targetInterface.ipAddresses()) {
+            if (interfaceIpAddress.ipAddress().equals(destIpAddress)) {
+                ipMatches = true;
+                break;
+            }
+        }
+
+        if (((ICMP) ipv4.getPayload()).getIcmpType() == ICMP.TYPE_ECHO_REQUEST &&
+                ipMatches) {
+            sendICMPResponse(ethernet, connectPoint);
+        }
+    }
+
+    private void sendICMPResponse(Ethernet icmpRequest, ConnectPoint outport) {
+
+        Ethernet icmpReplyEth = new Ethernet();
+
+        IPv4 icmpRequestIpv4 = (IPv4) icmpRequest.getPayload();
+        IPv4 icmpReplyIpv4 = new IPv4();
+
+        int destAddress = icmpRequestIpv4.getDestinationAddress();
+        icmpReplyIpv4.setDestinationAddress(icmpRequestIpv4.getSourceAddress());
+        icmpReplyIpv4.setSourceAddress(destAddress);
+        icmpReplyIpv4.setTtl((byte) 64);
+        icmpReplyIpv4.setChecksum((short) 0);
+
+        ICMP icmpReply = (ICMP) icmpRequestIpv4.getPayload().clone();
+        icmpReply.setIcmpType(ICMP.TYPE_ECHO_REPLY);
+        icmpReply.setIcmpCode(ICMP.SUBTYPE_ECHO_REPLY);
+        icmpReply.setChecksum((short) 0);
+
+        icmpReplyIpv4.setPayload(icmpReply);
+
+        icmpReplyEth.setPayload(icmpReplyIpv4);
+        icmpReplyEth.setEtherType(Ethernet.TYPE_IPV4);
+        icmpReplyEth.setDestinationMACAddress(icmpRequest.getSourceMACAddress());
+        icmpReplyEth.setSourceMACAddress(icmpRequest.getDestinationMACAddress());
+        icmpReplyEth.setVlanID(icmpRequest.getVlanID());
+
+        sendPacketOut(outport, icmpReplyEth);
+
+    }
+
+    private void sendPacketOut(ConnectPoint outport, Ethernet payload) {
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder().
+                setOutput(outport.port()).build();
+        OutboundPacket packet = new DefaultOutboundPacket(outport.deviceId(),
+                treatment, ByteBuffer.wrap(payload.serialize()));
+        packetService.emit(packet);
+    }
+
+    /**
+     * Packet processor responsible receiving and filtering ICMP packets.
+     */
+    private class IcmpProcessor implements PacketProcessor {
+
+        @Override
+        public void process(PacketContext context) {
+            // Stop processing if the packet has been handled, since we
+            // can't do any more to it.
+
+            if (context.isHandled()) {
+                return;
+            }
+
+            Ethernet packet = context.inPacket().parsed();
+
+            if (packet == null) {
+                return;
+            }
+
+            if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
+                IPv4 ipv4Packet = (IPv4) packet.getPayload();
+                if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_ICMP) {
+                    processPacketIn(context.inPacket());
+                }
+            }
+        }
+    }
+
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/ICMP.java b/utils/misc/src/main/java/org/onlab/packet/ICMP.java
index 6e6dd5f..e832517 100644
--- a/utils/misc/src/main/java/org/onlab/packet/ICMP.java
+++ b/utils/misc/src/main/java/org/onlab/packet/ICMP.java
@@ -29,6 +29,10 @@
     protected byte icmpCode;
     protected short checksum;
 
+    public static final byte TYPE_ECHO_REQUEST = 0x08;
+    public static final byte TYPE_ECHO_REPLY = 0x00;
+    public static final byte SUBTYPE_ECHO_REPLY = 0x00;
+
     /**
      * @return the icmpType
      */