sdn-ip reactive routing

   This module can handle 3 cases:
   (1) one host wants to talk to another host, both two hosts are in SDN network.
   (2) one host in SDN network wants to talk to another host in Internet.
   (3) one host from Internet wants to talk to another host in SDN network.
   In all cases, we use MultiPointToSinglePointIntent.

Change-Id: I80dd954bd608e52b45b993f3c27e67636a7105d9
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 11de4ae..9a4ecba 100644
--- a/apps/bgprouter/src/main/java/org/onosproject/bgprouter/BgpRouter.java
+++ b/apps/bgprouter/src/main/java/org/onosproject/bgprouter/BgpRouter.java
@@ -172,7 +172,8 @@
 
         icmpHandler = new IcmpHandler(configService, packetService);
 
-        routingService.start(new InternalFibListener());
+        routingService.addFibListener(new InternalFibListener());
+        routingService.start();
 
         connectivityManager.start();
 
diff --git a/apps/pom.xml b/apps/pom.xml
index 05f9b59..93f65b5 100644
--- a/apps/pom.xml
+++ b/apps/pom.xml
@@ -42,6 +42,7 @@
         <module>oecfg</module>
         <module>routing</module>
         <module>routing-api</module>
+        <module>reactive-routing</module>
         <module>bgprouter</module>
         <module>test</module>
     </modules>
diff --git a/apps/reactive-routing/app.xml b/apps/reactive-routing/app.xml
new file mode 100644
index 0000000..2c45141
--- /dev/null
+++ b/apps/reactive-routing/app.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<app name="org.onosproject.reactive.routing" origin="ON.Lab" version="1.2.0"
+        features="onos-app-reactive-routing">
+    <description> ONOS SDN/IP reactive routing </description>
+</app>
\ No newline at end of file
diff --git a/apps/reactive-routing/pom.xml b/apps/reactive-routing/pom.xml
new file mode 100644
index 0000000..d7bde79
--- /dev/null
+++ b/apps/reactive-routing/pom.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>onos-app-reactive-routing</artifactId>
+  <packaging>bundle</packaging>
+  <description> SDN-IP reactive routing </description>
+
+  <parent>
+    <groupId>org.onosproject</groupId>
+    <artifactId>onos-apps</artifactId>
+    <version>1.2.0-SNAPSHOT</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.onosproject</groupId>
+      <artifactId>onos-app-routing-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.onosproject</groupId>
+      <artifactId>onos-app-routing</artifactId>
+      <version>${project.version}</version>
+    </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
new file mode 100644
index 0000000..a74387f
--- /dev/null
+++ b/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java
@@ -0,0 +1,151 @@
+/*
+ * 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.reactive.routing;
+import static org.slf4j.LoggerFactory.getLogger;
+
+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.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.ConnectPoint;
+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.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.routing.RoutingService;
+import org.slf4j.Logger;
+
+/**
+ * This is reactive routing to handle 3 cases:
+ * (1) one host wants to talk to another host, both two hosts are in
+ * SDN network.
+ * (2) one host in SDN network wants to talk to another host in Internet.
+ * (3) one host from Internet wants to talk to another host in SDN network.
+ */
+@Component(immediate = true)
+public class SdnIpReactiveRouting {
+
+    private static final String APP_NAME = "org.onosproject.reactive.routing";
+    private final Logger log = getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected PacketService packetService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected RoutingService routingService;
+
+    private ApplicationId appId;
+
+    private ReactiveRoutingProcessor processor =
+            new ReactiveRoutingProcessor();
+
+    @Activate
+    public void activate() {
+        appId = coreService.registerApplication(APP_NAME);
+        packetService.addProcessor(processor,
+                                   PacketProcessor.ADVISOR_MAX + 2);
+
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        // TODO: to support IPv6 later
+        selector.matchEthType(Ethernet.TYPE_IPV4);
+        packetService.requestPackets(selector.build(),
+                                     PacketPriority.REACTIVE, appId);
+
+        log.info("SDN-IP Reactive Routing Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        packetService.removeProcessor(processor);
+        processor = null;
+        log.info("SDN-IP Reactive Routing Stopped");
+    }
+
+    private class ReactiveRoutingProcessor implements PacketProcessor {
+        @Override
+        public void process(PacketContext context) {
+
+            InboundPacket pkt = context.inPacket();
+            Ethernet ethPkt = pkt.parsed();
+            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);
+            }
+        }
+    }
+
+    /**
+     * Emits the specified packet onto the network.
+     *
+     * @param context the packet context
+     * @param connectPoint the connect point where the packet should be
+     *        sent out
+     */
+    private void forwardPacketToDst(PacketContext context,
+                                    ConnectPoint connectPoint) {
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(connectPoint.port()).build();
+        OutboundPacket packet =
+                new DefaultOutboundPacket(connectPoint.deviceId(), treatment,
+                                          context.inPacket().unparsed());
+        packetService.emit(packet);
+        log.trace("sending packet: {}", packet);
+    }
+
+}
+
diff --git a/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/package-info.java b/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/package-info.java
new file mode 100644
index 0000000..87c8fc8
--- /dev/null
+++ b/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Application to enable (1) hosts in local SDN network to talk to other hosts
+ * in Internet, and (2) hosts in local SDN network to talk to each other.
+ */
+package org.onosproject.reactive.routing;
diff --git a/apps/routing-api/src/main/java/org/onosproject/routing/IntentRequestListener.java b/apps/routing-api/src/main/java/org/onosproject/routing/IntentRequestListener.java
new file mode 100644
index 0000000..2d1bb31
--- /dev/null
+++ b/apps/routing-api/src/main/java/org/onosproject/routing/IntentRequestListener.java
@@ -0,0 +1,76 @@
+/*
+ * 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.routing;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.ConnectPoint;
+
+/**
+ * An interface to process intent requests.
+ */
+public interface IntentRequestListener {
+
+    /**
+     * Sets up connectivity for packet from Internet to a host in local
+     * SDN network.
+     *
+     * @param dstIpAddress IP address of destination host in local SDN network
+     */
+    void setUpConnectivityInternetToHost(IpAddress dstIpAddress);
+
+    /**
+     * Sets up the connectivity for two hosts in local SDN network.
+     *
+     * @param dstIpAddress the destination IP address
+     * @param srcIpAddress the source IP address
+     * @param srcMacAddress the source MAC address
+     * @param srcConnectPoint the connectPoint of the source host
+     */
+    void setUpConnectivityHostToHost(IpAddress dstIpAddress,
+                                     IpAddress srcIpAddress,
+                                     MacAddress srcMacAddress,
+                                     ConnectPoint srcConnectPoint);
+
+    /**
+     * Adds one new ingress connect point into ingress points of an existing
+     * intent and resubmits the new intent.
+     * <p>
+     * If there is already an intent for an IP prefix in the system, we do not
+     * need to create a new one, we only need to update this existing intent by
+     * adding more ingress points.
+     * </p>
+     *
+     * @param ipPrefix the IP prefix used to search the existing
+     *        MultiPointToSinglePointIntent
+     * @param ingressConnectPoint the ingress connect point to be added into
+     *        the exiting intent
+     */
+    void updateExistingMp2pIntent(IpPrefix ipPrefix,
+                                  ConnectPoint ingressConnectPoint);
+
+    /**
+     * Checks whether there is a MultiPointToSinglePointIntent in memory for a
+     * given IP prefix.
+     *
+     * @param ipPrefix the IP prefix used to search the existing
+     *        MultiPointToSinglePointIntent
+     * @return true if there is a MultiPointToSinglePointIntent, otherwise false
+     */
+    boolean mp2pIntentExists(IpPrefix ipPrefix);
+
+}
diff --git a/apps/routing-api/src/main/java/org/onosproject/routing/RoutingService.java b/apps/routing-api/src/main/java/org/onosproject/routing/RoutingService.java
index fdd4f2c..4356984 100644
--- a/apps/routing-api/src/main/java/org/onosproject/routing/RoutingService.java
+++ b/apps/routing-api/src/main/java/org/onosproject/routing/RoutingService.java
@@ -17,17 +17,92 @@
 
 import java.util.Collection;
 
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.ConnectPoint;
+
 /**
  * Provides a way of interacting with the RIB management component.
  */
 public interface RoutingService {
 
     /**
-     * Starts the routing service.
-     *
-     * @param listener listener to send FIB updates to
+     * Specifies the type of an IP address or an IP prefix location.
      */
-    public void start(FibListener listener);
+    public static enum LocationType {
+        /**
+         * The location of an IP address or an IP prefix is in local SDN network.
+         */
+        LOCAL,
+        /**
+         * The location of an IP address or an IP prefix is outside local SDN network.
+         */
+        INTERNET,
+        /**
+         * There is no route for this IP address or IP prefix.
+         */
+        NO_ROUTE
+    }
+
+    /**
+     * Specifies the type of traffic.
+     * <p>
+     * We classify traffic by the first packet of each traffic.
+     * </p>
+     */
+    public enum TrafficType {
+        /**
+         * Traffic from a host located in local SDN network wants to
+         * communicate with destination host located in Internet (outside
+         * local SDN network).
+         */
+        HOST_TO_INTERNET,
+        /**
+         * Traffic from Internet wants to communicate with a host located
+         * in local SDN network.
+         */
+        INTERNET_TO_HOST,
+        /**
+         * Both the source host and destination host of a traffic are in
+         * local SDN network.
+         */
+        HOST_TO_HOST,
+        /**
+         * Traffic from Internet wants to traverse local SDN network.
+         */
+        INTERNET_TO_INTERNET,
+        /**
+         * Any traffic wants to communicate with a destination which has
+         * no route, or traffic from Internet wants to access a local private
+         * IP address.
+         */
+        DROP,
+        /**
+         * Traffic does not belong to the types above.
+         */
+        UNKNOWN
+    }
+
+    /**
+     * Starts the routing service.
+     */
+    public void start();
+
+    /**
+     * Adds FIB listener.
+     *
+     * @param fibListener listener to send FIB updates to
+     */
+    public void addFibListener(FibListener fibListener);
+
+    /**
+     * Adds intent creation and submission listener.
+     *
+     * @param intentRequestListener listener to send intent creation and
+     *        submission request to
+     */
+    public void addIntentRequestListener(IntentRequestListener
+                                         intentRequestListener);
 
     /**
      * Stops the routing service.
@@ -47,4 +122,43 @@
      * @return the SDN-IP IPv6 routes
      */
     public Collection<RouteEntry> getRoutes6();
+
+    /**
+     * Evaluates the location of an IP address and returns the location type.
+     *
+     * @param ipAddress the IP address to evaluate
+     * @return the IP address location type
+     */
+    public LocationType getLocationType(IpAddress ipAddress);
+
+    /**
+     * Finds out the route entry which has the longest matchable IP prefix.
+     *
+     * @param ipAddress IP address used to find out longest matchable IP prefix
+     * @return a route entry which has the longest matchable IP prefix if
+     * found, otherwise null
+     */
+    public RouteEntry getLongestMatchableRouteEntry(IpAddress ipAddress);
+
+    /**
+     * Finds out the egress connect point where to emit the first packet
+     * based on destination IP address.
+     *
+     * @param dstIpAddress the destination IP address
+     * @return the egress connect point if found, otherwise null
+     */
+    public ConnectPoint getEgressConnectPoint(IpAddress dstIpAddress);
+
+    /**
+     * Routes packet reactively.
+     *
+     * @param dstIpAddress the destination IP address of a packet
+     * @param srcIpAddress the source IP address of a packet
+     * @param srcConnectPoint the connect point where a packet comes from
+     * @param srcMacAddress the source MAC address of a packet
+     */
+    public void packetReactiveProcessor(IpAddress dstIpAddress,
+                                        IpAddress srcIpAddress,
+                                        ConnectPoint srcConnectPoint,
+                                        MacAddress srcMacAddress);
 }
diff --git a/apps/routing-api/src/main/java/org/onosproject/routing/config/LocalIpPrefixEntry.java b/apps/routing-api/src/main/java/org/onosproject/routing/config/LocalIpPrefixEntry.java
new file mode 100644
index 0000000..a2f3c8c
--- /dev/null
+++ b/apps/routing-api/src/main/java/org/onosproject/routing/config/LocalIpPrefixEntry.java
@@ -0,0 +1,126 @@
+/*
+ * 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.routing.config;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.MoreObjects;
+
+import java.util.Objects;
+
+import org.onlab.packet.IpPrefix;
+
+/**
+ * Configuration details for an IP prefix entry.
+ */
+public class LocalIpPrefixEntry {
+    private final IpPrefix ipPrefix;
+    private final IpPrefixType type;
+
+    /**
+     * Specifies the type of local IP prefix.
+     */
+    public enum IpPrefixType {
+        /**
+         * Public IP prefixes should be exchanged by eBGP.
+         */
+        PUBLIC,
+        /**
+         * Private IP prefixes should be used only locally and not exchanged
+         * by eBGP.
+         */
+        PRIVATE,
+        /**
+         * For IP prefixes in blacklist.
+         */
+        BLACK_LIST
+    }
+
+    /**
+     * Creates a new IP prefix entry.
+     *
+     * @param ipPrefix an IP prefix as a String
+     * @param type an IP prefix type as an IpPrefixType
+     */
+    public LocalIpPrefixEntry(@JsonProperty("ipPrefix") String ipPrefix,
+                              @JsonProperty("type") IpPrefixType type) {
+        this.ipPrefix = IpPrefix.valueOf(ipPrefix);
+        this.type = type;
+    }
+
+    /**
+     * Gets the IP prefix of the IP prefix entry.
+     *
+     * @return the IP prefix
+     */
+    public IpPrefix ipPrefix() {
+        return ipPrefix;
+    }
+
+    /**
+     * Gets the IP prefix type of the IP prefix entry.
+     *
+     * @return the IP prefix type
+     */
+    public IpPrefixType ipPrefixType() {
+        return type;
+    }
+
+    /**
+     * Tests whether the IP version of this entry is IPv4.
+     *
+     * @return true if the IP version of this entry is IPv4, otherwise false.
+     */
+    public boolean isIp4() {
+        return ipPrefix.isIp4();
+    }
+
+    /**
+     * Tests whether the IP version of this entry is IPv6.
+     *
+     * @return true if the IP version of this entry is IPv6, otherwise false.
+     */
+    public boolean isIp6() {
+        return ipPrefix.isIp6();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(ipPrefix, type);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+
+        if (!(obj instanceof LocalIpPrefixEntry)) {
+            return false;
+        }
+
+        LocalIpPrefixEntry that = (LocalIpPrefixEntry) obj;
+        return Objects.equals(this.ipPrefix, that.ipPrefix)
+                && Objects.equals(this.type, that.type);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("ipPrefix", ipPrefix)
+                .add("ipPrefixType", type)
+                .toString();
+    }
+}
diff --git a/apps/routing-api/src/main/java/org/onosproject/routing/config/RoutingConfigurationService.java b/apps/routing-api/src/main/java/org/onosproject/routing/config/RoutingConfigurationService.java
index 113daa7..a306328 100644
--- a/apps/routing-api/src/main/java/org/onosproject/routing/config/RoutingConfigurationService.java
+++ b/apps/routing-api/src/main/java/org/onosproject/routing/config/RoutingConfigurationService.java
@@ -16,6 +16,7 @@
 package org.onosproject.routing.config;
 
 import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
 import org.onosproject.net.ConnectPoint;
 
 import java.util.Map;
@@ -41,6 +42,22 @@
     public Map<IpAddress, BgpPeer> getBgpPeers();
 
     /**
+     * Evaluates whether an IP address belongs to local SDN network.
+     *
+     * @param ipAddress the IP address to evaluate
+     * @return true if the IP address belongs to local SDN network, otherwise false
+     */
+    public boolean isIpAddressLocal(IpAddress ipAddress);
+
+    /**
+     * Evaluates whether an IP prefix belongs to local SDN network.
+     *
+     * @param ipPrefix the IP prefix to evaluate
+     * @return true if the IP prefix belongs to local SDN network, otherwise false
+     */
+    public boolean isIpPrefixLocal(IpPrefix ipPrefix);
+
+    /**
      * Retrieves the entire set of interfaces in the network.
      *
      * @return the set of interfaces
diff --git a/apps/routing/src/main/java/org/onosproject/routing/config/impl/Configuration.java b/apps/routing/src/main/java/org/onosproject/routing/config/impl/Configuration.java
index bb57302..76f1df0 100644
--- a/apps/routing/src/main/java/org/onosproject/routing/config/impl/Configuration.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/config/impl/Configuration.java
@@ -16,8 +16,10 @@
 package org.onosproject.routing.config.impl;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
+
 import org.onosproject.routing.config.BgpPeer;
 import org.onosproject.routing.config.BgpSpeaker;
+import org.onosproject.routing.config.LocalIpPrefixEntry;
 
 import java.util.Collections;
 import java.util.List;
@@ -32,6 +34,12 @@
     private List<BgpSpeaker> bgpSpeakers;
     private List<BgpPeer> peers;
 
+    // All IP prefixes from the configuration are local
+    private List<LocalIpPrefixEntry> localIp4PrefixEntries =
+            Collections.emptyList();
+    private List<LocalIpPrefixEntry> localIp6PrefixEntries =
+            Collections.emptyList();
+
     /**
      * Default constructor.
      */
@@ -78,4 +86,51 @@
         this.peers = peers;
     }
 
+    /**
+     * Gets a list of local IPv4 prefix entries configured for local
+     * SDN network.
+     * <p>
+     * IP prefix entries are represented by {@link LocalIpPrefixEntry}
+     * objects.
+     * </p>
+     *
+     * @return the list of local IPv4 prefix entries
+     */
+    public List<LocalIpPrefixEntry> getLocalIp4PrefixEntries() {
+        return Collections.unmodifiableList(localIp4PrefixEntries);
+    }
+
+    /**
+     * Sets a list of IPv4 prefix entries configured for local SDN network.
+     *
+     * @param ip4PrefixEntries the list of Ipv4 prefix entries
+     */
+    @JsonProperty("ip4LocalPrefixes")
+    public void setLocalIp4PrefixEntries(List<LocalIpPrefixEntry> ip4PrefixEntries) {
+        this.localIp4PrefixEntries = ip4PrefixEntries;
+    }
+
+    /**
+     * Gets a list of IPv6 prefix entries configured for local SDN network.
+     * <p>
+     * IP prefix entries are represented by {@link LocalIpPrefixEntry}
+     * objects.
+     * </p>
+     *
+     * @return the list of IPv6 prefix entries
+     */
+    public List<LocalIpPrefixEntry> getLocalIp6PrefixEntries() {
+        return Collections.unmodifiableList(localIp6PrefixEntries);
+    }
+
+    /**
+     * Sets a list of IPv6 prefix entries configured for local SDN network.
+     *
+     * @param ip6PrefixEntries the list of Ipv6 prefix entries
+     */
+    @JsonProperty("ip6LocalPrefixes")
+    public void setLocalIp6PrefixEntries(List<LocalIpPrefixEntry> ip6PrefixEntries) {
+        this.localIp6PrefixEntries = ip6PrefixEntries;
+    }
+
 }
diff --git a/apps/routing/src/main/java/org/onosproject/routing/config/impl/RoutingConfigurationImpl.java b/apps/routing/src/main/java/org/onosproject/routing/config/impl/RoutingConfigurationImpl.java
index ac20475..abb6a13 100644
--- a/apps/routing/src/main/java/org/onosproject/routing/config/impl/RoutingConfigurationImpl.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/config/impl/RoutingConfigurationImpl.java
@@ -16,17 +16,25 @@
 package org.onosproject.routing.config.impl;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
+import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
+import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
+
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip6Address;
 import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.host.HostService;
 import org.onosproject.routing.config.BgpPeer;
 import org.onosproject.routing.config.BgpSpeaker;
 import org.onosproject.routing.config.Interface;
+import org.onosproject.routing.config.LocalIpPrefixEntry;
 import org.onosproject.routing.config.RoutingConfigurationService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -39,6 +47,8 @@
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
+import static org.onosproject.routing.RouteEntry.createBinaryString;
+
 /**
  * Implementation of RoutingConfigurationService which reads routing
  * configuration from a file.
@@ -59,6 +69,13 @@
     private Map<String, BgpSpeaker> bgpSpeakers = new ConcurrentHashMap<>();
     private Map<IpAddress, BgpPeer> bgpPeers = new ConcurrentHashMap<>();
 
+    private InvertedRadixTree<LocalIpPrefixEntry>
+            localPrefixTable4 = new ConcurrentInvertedRadixTree<>(
+                    new DefaultByteArrayNodeFactory());
+    private InvertedRadixTree<LocalIpPrefixEntry>
+            localPrefixTable6 = new ConcurrentInvertedRadixTree<>(
+                    new DefaultByteArrayNodeFactory());
+
     private HostToInterfaceAdaptor hostAdaptor;
 
     @Activate
@@ -88,6 +105,16 @@
             for (BgpPeer peer : config.getPeers()) {
                 bgpPeers.put(peer.ipAddress(), peer);
             }
+
+            for (LocalIpPrefixEntry entry : config.getLocalIp4PrefixEntries()) {
+                localPrefixTable4.put(createBinaryString(entry.ipPrefix()),
+                                      entry);
+            }
+            for (LocalIpPrefixEntry entry : config.getLocalIp6PrefixEntries()) {
+                localPrefixTable6.put(createBinaryString(entry.ipPrefix()),
+                                      entry);
+            }
+
         } catch (FileNotFoundException e) {
             log.warn("Configuration file not found: {}", configFileName);
         } catch (IOException e) {
@@ -96,7 +123,8 @@
     }
 
     /**
-     * Instructs the configuration reader to read the configuration from the file.
+     * Instructs the configuration reader to read the configuration from the
+     * file.
      */
     public void readConfiguration() {
         readConfiguration(configFileName);
@@ -127,4 +155,27 @@
         return hostAdaptor.getMatchingInterface(ipAddress);
     }
 
+    @Override
+    public boolean isIpAddressLocal(IpAddress ipAddress) {
+        if (ipAddress.isIp4()) {
+            return localPrefixTable4.getValuesForKeysPrefixing(
+                    createBinaryString(
+                    IpPrefix.valueOf(ipAddress, Ip4Address.BIT_LENGTH)))
+                    .iterator().hasNext();
+        } else {
+            return localPrefixTable6.getValuesForKeysPrefixing(
+                    createBinaryString(
+                    IpPrefix.valueOf(ipAddress, Ip6Address.BIT_LENGTH)))
+                    .iterator().hasNext();
+        }
+    }
+
+    @Override
+    public boolean isIpPrefixLocal(IpPrefix ipPrefix) {
+        return (localPrefixTable4.getValueForExactKey(
+                createBinaryString(ipPrefix)) != null ||
+                localPrefixTable6.getValueForExactKey(
+                createBinaryString(ipPrefix)) != null);
+    }
+
 }
diff --git a/apps/routing/src/main/java/org/onosproject/routing/impl/Router.java b/apps/routing/src/main/java/org/onosproject/routing/impl/Router.java
index 71990a1..cf0b428 100644
--- a/apps/routing/src/main/java/org/onosproject/routing/impl/Router.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/impl/Router.java
@@ -36,9 +36,12 @@
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip6Address;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
+import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.Host;
 import org.onosproject.net.host.HostEvent;
 import org.onosproject.net.host.HostListener;
@@ -47,10 +50,13 @@
 import org.onosproject.routing.FibEntry;
 import org.onosproject.routing.FibListener;
 import org.onosproject.routing.FibUpdate;
+import org.onosproject.routing.IntentRequestListener;
 import org.onosproject.routing.RouteEntry;
 import org.onosproject.routing.RouteListener;
 import org.onosproject.routing.RouteUpdate;
 import org.onosproject.routing.RoutingService;
+import org.onosproject.routing.config.Interface;
+import org.onosproject.routing.config.RoutingConfigurationService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -63,6 +69,8 @@
 import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
 import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
 
+import static org.onosproject.routing.RouteEntry.createBinaryString;
+
 /**
  * This class processes route updates and maintains a Routing Information Base
  * (RIB). After route updates have been processed and next hops have been
@@ -80,16 +88,18 @@
     private InvertedRadixTree<RouteEntry> ribTable6;
 
     // Stores all incoming route updates in a queue.
-    private final BlockingQueue<Collection<RouteUpdate>> routeUpdatesQueue
-            = new LinkedBlockingQueue<>();
+    private final BlockingQueue<Collection<RouteUpdate>> routeUpdatesQueue =
+            new LinkedBlockingQueue<>();
 
-    // Next-hop IP address to route entry mapping for next hops pending MAC resolution
+    // Next-hop IP address to route entry mapping for next hops pending MAC
+    // resolution
     private SetMultimap<IpAddress, RouteEntry> routesWaitingOnArp;
 
     // The IPv4 address to MAC address mapping
     private final Map<IpAddress, MacAddress> ip2Mac = new ConcurrentHashMap<>();
 
     private FibListener fibComponent;
+    private IntentRequestListener intentRequestListener;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected HostService hostService;
@@ -97,6 +107,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected BgpService bgpService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected RoutingConfigurationService routingConfigurationService;
+
     private ExecutorService bgpUpdatesExecutor;
     private final HostListener hostListener = new InternalHostListener();
 
@@ -106,6 +119,7 @@
                 new DefaultByteArrayNodeFactory());
         ribTable6 = new ConcurrentInvertedRadixTree<>(
                 new DefaultByteArrayNodeFactory());
+
         routesWaitingOnArp = Multimaps.synchronizedSetMultimap(
                 HashMultimap.<IpAddress, RouteEntry>create());
 
@@ -120,8 +134,18 @@
     }
 
     @Override
-    public void start(FibListener listener) {
-        this.fibComponent = checkNotNull(listener);
+    public void addFibListener(FibListener fibListener) {
+        this.fibComponent = checkNotNull(fibListener);
+
+    }
+
+    @Override
+    public void addIntentRequestListener(IntentRequestListener intentRequestListener) {
+        this.intentRequestListener = checkNotNull(intentRequestListener);
+    }
+
+    @Override
+    public void start() {
         this.hostService.addListener(hostListener);
 
         bgpService.start(new InternalRouteListener());
@@ -146,9 +170,9 @@
         synchronized (this) {
             // Cleanup all local state
             ribTable4 = new ConcurrentInvertedRadixTree<>(
-                new DefaultByteArrayNodeFactory());
+                    new DefaultByteArrayNodeFactory());
             ribTable6 = new ConcurrentInvertedRadixTree<>(
-                new DefaultByteArrayNodeFactory());
+                    new DefaultByteArrayNodeFactory());
             routeUpdatesQueue.clear();
             routesWaitingOnArp.clear();
             ip2Mac.clear();
@@ -178,7 +202,7 @@
             while (!interrupted) {
                 try {
                     Collection<RouteUpdate> routeUpdates =
-                        routeUpdatesQueue.take();
+                            routeUpdatesQueue.take();
                     processRouteUpdates(routeUpdates);
                 } catch (InterruptedException e) {
                     log.error("Interrupted while taking from updates queue", e);
@@ -202,7 +226,7 @@
     @Override
     public Collection<RouteEntry> getRoutes4() {
         Iterator<KeyValuePair<RouteEntry>> it =
-            ribTable4.getKeyValuePairsForKeysStartingWith("").iterator();
+                ribTable4.getKeyValuePairsForKeysStartingWith("").iterator();
 
         List<RouteEntry> routes = new LinkedList<>();
 
@@ -222,7 +246,7 @@
     @Override
     public Collection<RouteEntry> getRoutes6() {
         Iterator<KeyValuePair<RouteEntry>> it =
-            ribTable6.getKeyValuePairsForKeysStartingWith("").iterator();
+                ribTable6.getKeyValuePairsForKeysStartingWith("").iterator();
 
         List<RouteEntry> routes = new LinkedList<>();
 
@@ -242,7 +266,7 @@
      * @return the route if found, otherwise null
      */
     RouteEntry findRibRoute(IpPrefix prefix) {
-        String binaryString = RouteEntry.createBinaryString(prefix);
+        String binaryString = createBinaryString(prefix);
         if (prefix.isIp4()) {
             // IPv4
             return ribTable4.getValueForExactKey(binaryString);
@@ -259,12 +283,12 @@
     void addRibRoute(RouteEntry routeEntry) {
         if (routeEntry.isIp4()) {
             // IPv4
-            ribTable4.put(RouteEntry.createBinaryString(routeEntry.prefix()),
-                          routeEntry);
+            ribTable4.put(createBinaryString(routeEntry.prefix()),
+                    routeEntry);
         } else {
             // IPv6
-            ribTable6.put(RouteEntry.createBinaryString(routeEntry.prefix()),
-                          routeEntry);
+            ribTable6.put(createBinaryString(routeEntry.prefix()),
+                    routeEntry);
         }
     }
 
@@ -278,10 +302,10 @@
     boolean removeRibRoute(IpPrefix prefix) {
         if (prefix.isIp4()) {
             // IPv4
-            return ribTable4.remove(RouteEntry.createBinaryString(prefix));
+            return ribTable4.remove(createBinaryString(prefix));
         }
         // IPv6
-        return ribTable6.remove(RouteEntry.createBinaryString(prefix));
+        return ribTable6.remove(createBinaryString(prefix));
     }
 
     /**
@@ -298,8 +322,9 @@
             for (RouteUpdate update : routeUpdates) {
                 switch (update.type()) {
                 case UPDATE:
+
                     FibEntry fib = processRouteAdd(update.routeEntry(),
-                                                    withdrawPrefixes);
+                            withdrawPrefixes);
                     if (fib != null) {
                         fibUpdates.add(new FibUpdate(FibUpdate.Type.UPDATE, fib));
                     }
@@ -341,9 +366,8 @@
      * intents will be withdrawn
      * @return the corresponding FIB entry change, or null
      */
-    private FibEntry processRouteAdd(
-                RouteEntry routeEntry,
-                Collection<IpPrefix> withdrawPrefixes) {
+    private FibEntry processRouteAdd(RouteEntry routeEntry,
+                                     Collection<IpPrefix> withdrawPrefixes) {
         log.debug("Processing route add: {}", routeEntry);
 
         // Find the old next-hop if we are updating an old route entry
@@ -368,11 +392,12 @@
             withdrawPrefixes.add(routeEntry.prefix());
         }
 
-        if (routeEntry.nextHop().isZero()) {
-            // Route originated by SDN domain
-            // We don't handle these at the moment
+        if (routingConfigurationService.isIpPrefixLocal(routeEntry.prefix())) {
+            // Route originated by local SDN domain
+            // We don't handle these here, reactive routing APP will handle
+            // these
             log.debug("Own route {} to {}",
-                      routeEntry.prefix(), routeEntry.nextHop());
+                    routeEntry.prefix(), routeEntry.nextHop());
             return null;
         }
 
@@ -402,7 +427,7 @@
             return null;
         }
         return new FibEntry(routeEntry.prefix(), routeEntry.nextHop(),
-                             nextHopMacAddress);
+                nextHopMacAddress);
     }
 
     /**
@@ -446,7 +471,7 @@
      */
     private void updateMac(IpAddress ipAddress, MacAddress macAddress) {
         log.debug("Received updated MAC info: {} => {}", ipAddress,
-                  macAddress);
+                macAddress);
 
         //
         // We synchronize on "this" to prevent changes to the Radix tree
@@ -457,23 +482,23 @@
             Collection<FibUpdate> submitFibEntries = new LinkedList<>();
 
             Set<RouteEntry> routesToPush =
-                routesWaitingOnArp.removeAll(ipAddress);
+                    routesWaitingOnArp.removeAll(ipAddress);
 
             for (RouteEntry routeEntry : routesToPush) {
                 // These will always be adds
                 RouteEntry foundRouteEntry = findRibRoute(routeEntry.prefix());
                 if (foundRouteEntry != null &&
-                    foundRouteEntry.nextHop().equals(routeEntry.nextHop())) {
+                        foundRouteEntry.nextHop().equals(routeEntry.nextHop())) {
                     // We only push FIB updates if the prefix is still in the
                     // radix tree and the next hop is the same as our entry.
                     // The prefix could have been removed while we were waiting
                     // for the ARP, or the next hop could have changed.
                     submitFibEntries.add(new FibUpdate(FibUpdate.Type.UPDATE,
-                                                       new FibEntry(routeEntry.prefix(),
-                                                       ipAddress, macAddress)));
+                            new FibEntry(routeEntry.prefix(),
+                                    ipAddress, macAddress)));
                 } else {
                     log.debug("{} has been revoked before the MAC was resolved",
-                              routeEntry);
+                            routeEntry);
                 }
             }
 
@@ -522,4 +547,179 @@
             Router.this.update(routeUpdates);
         }
     }
+
+    @Override
+    public LocationType getLocationType(IpAddress ipAddress) {
+        if (routingConfigurationService.isIpAddressLocal(ipAddress)) {
+            return LocationType.LOCAL;
+        } else if (getLongestMatchableRouteEntry(ipAddress) != null) {
+            return LocationType.INTERNET;
+        } else {
+            return LocationType.NO_ROUTE;
+        }
+    }
+
+    @Override
+    public RouteEntry getLongestMatchableRouteEntry(IpAddress ipAddress) {
+        RouteEntry routeEntry = null;
+        Iterable<RouteEntry> routeEntries;
+
+        if (ipAddress.isIp4()) {
+            routeEntries = ribTable4.getValuesForKeysPrefixing(
+                    createBinaryString(
+                    IpPrefix.valueOf(ipAddress, Ip4Address.BIT_LENGTH)));
+        } else {
+            routeEntries = ribTable6.getValuesForKeysPrefixing(
+                    createBinaryString(
+                    IpPrefix.valueOf(ipAddress, Ip6Address.BIT_LENGTH)));
+        }
+        if (routeEntries == null) {
+            return null;
+        }
+        Iterator<RouteEntry> it = routeEntries.iterator();
+        while (it.hasNext()) {
+            routeEntry = it.next();
+        }
+        return routeEntry;
+    }
+
+    @Override
+    public ConnectPoint getEgressConnectPoint(IpAddress dstIpAddress) {
+        LocationType type = getLocationType(dstIpAddress);
+        if (type == LocationType.LOCAL) {
+            Set<Host> hosts = hostService.getHostsByIp(dstIpAddress);
+            if (!hosts.isEmpty()) {
+                return hosts.iterator().next().location();
+            } else {
+                hostService.startMonitoringIp(dstIpAddress);
+                return null;
+            }
+        } else if (type == LocationType.INTERNET) {
+            IpAddress nextHopIpAddress = null;
+            RouteEntry routeEntry = getLongestMatchableRouteEntry(dstIpAddress);
+            if (routeEntry != null) {
+                nextHopIpAddress = routeEntry.nextHop();
+                Interface it = routingConfigurationService
+                        .getMatchingInterface(nextHopIpAddress);
+                if (it != null) {
+                    return it.connectPoint();
+                } else {
+                    return null;
+                }
+            } else {
+                return null;
+            }
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void packetReactiveProcessor(IpAddress dstIpAddress,
+                                        IpAddress srcIpAddress,
+                                        ConnectPoint srcConnectPoint,
+                                        MacAddress srcMacAddress) {
+        checkNotNull(dstIpAddress);
+        checkNotNull(srcIpAddress);
+        checkNotNull(srcConnectPoint);
+        checkNotNull(srcMacAddress);
+
+        //
+        // Step1: Try to update the existing intent first if it exists.
+        //
+        IpPrefix ipPrefix = null;
+        if (routingConfigurationService.isIpAddressLocal(dstIpAddress)) {
+            if (dstIpAddress.isIp4()) {
+                ipPrefix = IpPrefix.valueOf(dstIpAddress,
+                        Ip4Address.BIT_LENGTH);
+            } else {
+                ipPrefix = IpPrefix.valueOf(dstIpAddress,
+                        Ip6Address.BIT_LENGTH);
+            }
+        } else {
+            // Get IP prefix from BGP route table
+            RouteEntry routeEntry = getLongestMatchableRouteEntry(dstIpAddress);
+            if (routeEntry != null) {
+                ipPrefix = routeEntry.prefix();
+            }
+        }
+        if (ipPrefix != null
+                && intentRequestListener.mp2pIntentExists(ipPrefix)) {
+            intentRequestListener.updateExistingMp2pIntent(ipPrefix,
+                    srcConnectPoint);
+            return;
+        }
+
+        //
+        // Step2: There is no existing intent for the destination IP address.
+        // Check whether it is necessary to create a new one. If necessary then
+        // create a new one.
+        //
+        TrafficType trafficType =
+                trafficTypeClassifier(srcConnectPoint, dstIpAddress);
+
+        switch (trafficType) {
+        case HOST_TO_INTERNET:
+            // If the destination IP address is outside the local SDN network.
+            // The Step 1 has already handled it. We do not need to do anything here.
+            break;
+        case INTERNET_TO_HOST:
+            intentRequestListener.setUpConnectivityInternetToHost(dstIpAddress);
+            break;
+        case HOST_TO_HOST:
+            intentRequestListener.setUpConnectivityHostToHost(dstIpAddress,
+                    srcIpAddress, srcMacAddress, srcConnectPoint);
+            break;
+        case INTERNET_TO_INTERNET:
+            log.trace("This is transit traffic, "
+                    + "the intent should be preinstalled already");
+            break;
+        case DROP:
+            // TODO here should setUpDropPaccketIntent(...);
+            // We need a new type of intent here.
+            break;
+        case UNKNOWN:
+            log.trace("This is unknown traffic, so we do nothing");
+            break;
+        default:
+            break;
+        }
+    }
+
+    /**
+     * Classifies the traffic and return the traffic type.
+     *
+     * @param srcConnectPoint the connect point where the packet comes from
+     * @param dstIp the destination IP address in packet
+     * @return the traffic type which this packet belongs to
+     */
+    private TrafficType trafficTypeClassifier(ConnectPoint srcConnectPoint,
+                                              IpAddress dstIp) {
+        LocationType dstIpLocationType = getLocationType(dstIp);
+        Interface srcInterface =
+                routingConfigurationService.getInterface(srcConnectPoint);
+
+        switch (dstIpLocationType) {
+        case INTERNET:
+            if (srcInterface == null) {
+                return TrafficType.HOST_TO_INTERNET;
+            } else {
+                return TrafficType.INTERNET_TO_INTERNET;
+            }
+        case LOCAL:
+            if (srcInterface == null) {
+                return TrafficType.HOST_TO_HOST;
+            } else {
+                // TODO Currently we only consider local public prefixes.
+                // In the future, we will consider the local private prefixes.
+                // If dstIpLocationType is a local private, we should return
+                // TrafficType.DROP.
+                return TrafficType.INTERNET_TO_HOST;
+            }
+        case NO_ROUTE:
+            return TrafficType.DROP;
+        default:
+            return TrafficType.UNKNOWN;
+        }
+    }
 }
diff --git a/apps/routing/src/test/java/org/onosproject/routing/impl/RouterAsyncArpTest.java b/apps/routing/src/test/java/org/onosproject/routing/impl/RouterAsyncArpTest.java
index 8ef48b2..dace7a6 100644
--- a/apps/routing/src/test/java/org/onosproject/routing/impl/RouterAsyncArpTest.java
+++ b/apps/routing/src/test/java/org/onosproject/routing/impl/RouterAsyncArpTest.java
@@ -16,6 +16,7 @@
 package org.onosproject.routing.impl;
 
 import com.google.common.collect.Sets;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -36,6 +37,7 @@
 import org.onosproject.net.host.HostListener;
 import org.onosproject.net.host.HostService;
 import org.onosproject.net.provider.ProviderId;
+import org.onosproject.routing.config.RoutingConfigurationService;
 import org.onosproject.routing.impl.Router.InternalHostListener;
 import org.onosproject.routing.BgpService;
 import org.onosproject.routing.FibEntry;
@@ -57,6 +59,7 @@
 
     private HostService hostService;
     private FibListener fibListener;
+    private RoutingConfigurationService routingConfigurationService;
 
     private static final ConnectPoint SW1_ETH1 = new ConnectPoint(
             DeviceId.deviceId("of:0000000000000001"),
@@ -76,6 +79,8 @@
     @Before
     public void setUp() throws Exception {
         hostService = createMock(HostService.class);
+        routingConfigurationService =
+                createMock(RoutingConfigurationService.class);
 
         BgpService bgpService = createMock(BgpService.class);
         bgpService.start(anyObject(RouteListener.class));
@@ -86,10 +91,12 @@
 
         router = new Router();
         router.hostService = hostService;
+        router.routingConfigurationService = routingConfigurationService;
         router.bgpService = bgpService;
         router.activate();
 
-        router.start(fibListener);
+        router.addFibListener(fibListener);
+        router.start();
 
         internalHostListener = router.new InternalHostListener();
     }
@@ -121,6 +128,10 @@
         hostService.startMonitoringIp(IpAddress.valueOf("192.168.10.1"));
         replay(hostService);
 
+        reset(routingConfigurationService);
+        expect(routingConfigurationService.isIpPrefixLocal(
+                anyObject(IpPrefix.class))).andReturn(false);
+        replay(routingConfigurationService);
 
         // Initially when we add the route, no FIB update will be sent
         replay(fibListener);
@@ -175,6 +186,10 @@
         hostService.startMonitoringIp(IpAddress.valueOf("192.168.20.1"));
         replay(hostService);
 
+        reset(routingConfigurationService);
+        expect(routingConfigurationService.isIpPrefixLocal(
+                anyObject(IpPrefix.class))).andReturn(false);
+        replay(routingConfigurationService);
 
         // Initially when we add the route, the DELETE FIB update will be sent
         // but the UPDATE FIB update will come later when the MAC is resolved
diff --git a/apps/routing/src/test/java/org/onosproject/routing/impl/RouterTest.java b/apps/routing/src/test/java/org/onosproject/routing/impl/RouterTest.java
index d669a0b..288cf99 100644
--- a/apps/routing/src/test/java/org/onosproject/routing/impl/RouterTest.java
+++ b/apps/routing/src/test/java/org/onosproject/routing/impl/RouterTest.java
@@ -16,6 +16,7 @@
 package org.onosproject.routing.impl;
 
 import com.google.common.collect.Sets;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -42,6 +43,7 @@
 import org.onosproject.routing.RouteEntry;
 import org.onosproject.routing.RouteListener;
 import org.onosproject.routing.RouteUpdate;
+import org.onosproject.routing.config.RoutingConfigurationService;
 
 import java.util.Collections;
 
@@ -58,6 +60,7 @@
 public class RouterTest {
 
     private HostService hostService;
+    private RoutingConfigurationService routingConfigurationService;
 
     private FibListener fibListener;
 
@@ -82,6 +85,8 @@
     @Before
     public void setUp() throws Exception {
         setUpHostService();
+        routingConfigurationService =
+                createMock(RoutingConfigurationService.class);
 
         BgpService bgpService = createMock(BgpService.class);
         bgpService.start(anyObject(RouteListener.class));
@@ -92,10 +97,12 @@
 
         router = new Router();
         router.hostService = hostService;
+        router.routingConfigurationService = routingConfigurationService;
         router.bgpService = bgpService;
         router.activate();
 
-        router.start(fibListener);
+        router.addFibListener(fibListener);
+        router.start();
     }
 
     @After
@@ -207,9 +214,13 @@
                                     FibUpdate.Type.UPDATE, updateFibEntry)),
                            Collections.singletonList(new FibUpdate(
                                     FibUpdate.Type.DELETE, withdrawFibEntry)));
-
         replay(fibListener);
 
+        reset(routingConfigurationService);
+        expect(routingConfigurationService.isIpPrefixLocal(
+                anyObject(IpPrefix.class))).andReturn(false);
+        replay(routingConfigurationService);
+
         router.processRouteUpdates(Collections.singletonList(new RouteUpdate(
                 RouteUpdate.Type.UPDATE, routeEntryUpdate)));
 
@@ -256,6 +267,11 @@
         // No methods on the FIB listener should be called
         replay(fibListener);
 
+        reset(routingConfigurationService);
+        expect(routingConfigurationService.isIpPrefixLocal(
+                anyObject(IpPrefix.class))).andReturn(true);
+        replay(routingConfigurationService);
+
         // Call the processRouteUpdates() method in Router class
         RouteUpdate routeUpdate = new RouteUpdate(RouteUpdate.Type.UPDATE,
                                                   routeEntry);
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java
index f08e513..126d052 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java
@@ -35,12 +35,14 @@
 import org.onlab.packet.VlanId;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Host;
 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.flow.criteria.Criteria.IPCriterion;
 import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.host.HostService;
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentService;
 import org.onosproject.net.intent.IntentState;
@@ -49,6 +51,7 @@
 import org.onosproject.net.intent.PointToPointIntent;
 import org.onosproject.routing.FibListener;
 import org.onosproject.routing.FibUpdate;
+import org.onosproject.routing.IntentRequestListener;
 import org.onosproject.routing.config.BgpPeer;
 import org.onosproject.routing.config.Interface;
 import org.onosproject.routing.config.RoutingConfigurationService;
@@ -58,12 +61,13 @@
 import com.google.common.util.concurrent.ThreadFactoryBuilder;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
  * Synchronizes intents between the in-memory intent store and the
  * IntentService.
  */
-public class IntentSynchronizer implements FibListener {
+public class IntentSynchronizer implements FibListener, IntentRequestListener {
     private static final int PRIORITY_OFFSET = 100;
     private static final int PRIORITY_MULTIPLIER = 5;
 
@@ -72,6 +76,7 @@
 
     private final ApplicationId appId;
     private final IntentService intentService;
+    private final HostService hostService;
     private final Map<IntentKey, PointToPointIntent> peerIntents;
     private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents;
 
@@ -90,12 +95,15 @@
      *
      * @param appId the Application ID
      * @param intentService the intent service
+     * @param hostService the host service
      * @param configService the SDN-IP configuration service
      */
     IntentSynchronizer(ApplicationId appId, IntentService intentService,
+                       HostService hostService,
                        RoutingConfigurationService configService) {
         this.appId = appId;
         this.intentService = intentService;
+        this.hostService = hostService;
         peerIntents = new ConcurrentHashMap<>();
         routeIntents = new ConcurrentHashMap<>();
 
@@ -264,6 +272,25 @@
     }
 
     /**
+     * Submits a MultiPointToSinglePointIntent for reactive routing.
+     *
+     * @param ipPrefix the IP prefix to match in a MultiPointToSinglePointIntent
+     * @param intent the intent to submit
+     */
+    void submitReactiveIntent(IpPrefix ipPrefix, MultiPointToSinglePointIntent intent) {
+        synchronized (this) {
+            // Store the intent in memory
+            routeIntents.put(ipPrefix, intent);
+
+            // Push the intent
+            if (isElectedLeader && isActivatedLeader) {
+                log.trace("SDN-IP submitting reactive routing intent: {}", intent);
+                intentService.submit(intent);
+            }
+        }
+    }
+
+    /**
      * Generates a route intent for a prefix, the next hop IP address, and
      * the next hop MAC address.
      * <p/>
@@ -352,6 +379,62 @@
     }
 
     @Override
+    public void setUpConnectivityInternetToHost(IpAddress hostIpAddress) {
+        checkNotNull(hostIpAddress);
+        Set<ConnectPoint> ingressPoints = new HashSet<ConnectPoint>();
+        for (Interface intf : configService.getInterfaces()) {
+            ConnectPoint srcPoint = intf.connectPoint();
+            ingressPoints.add(srcPoint);
+        }
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+
+        if (hostIpAddress.isIp4()) {
+            selector.matchEthType(Ethernet.TYPE_IPV4);
+        } else {
+            selector.matchEthType(Ethernet.TYPE_IPV6);
+        }
+
+        // Match the destination IP prefix at the first hop
+        IpPrefix ipPrefix = hostIpAddress.toIpPrefix();
+        selector.matchIPDst(ipPrefix);
+
+        // Rewrite the destination MAC address
+        MacAddress hostMac = null;
+        ConnectPoint egressPoint = null;
+        for (Host host : hostService.getHostsByIp(hostIpAddress)) {
+            if (host.mac() != null) {
+                hostMac = host.mac();
+                egressPoint = host.location();
+                break;
+            }
+        }
+        if (hostMac == null) {
+            hostService.startMonitoringIp(hostIpAddress);
+            return;
+        }
+
+        TrafficTreatment.Builder treatment =
+                DefaultTrafficTreatment.builder().setEthDst(hostMac);
+        Key key = Key.of(ipPrefix.toString(), appId);
+        int priority = ipPrefix.prefixLength() * PRIORITY_MULTIPLIER
+                + PRIORITY_OFFSET;
+        MultiPointToSinglePointIntent intent =
+                MultiPointToSinglePointIntent.builder()
+                .appId(appId)
+                .key(key)
+                .selector(selector.build())
+                .treatment(treatment.build())
+                .ingressPoints(ingressPoints)
+                .egressPoint(egressPoint)
+                .priority(priority)
+                .build();
+
+        log.trace("Generates ConnectivityInternetToHost intent {}", intent);
+        submitReactiveIntent(ipPrefix, intent);
+    }
+
+
+    @Override
     public void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) {
         //
         // NOTE: Semantically, we MUST withdraw existing intents before
@@ -738,4 +821,167 @@
             return false;
         }
     }
+
+    @Override
+    public void setUpConnectivityHostToHost(IpAddress dstIpAddress,
+                                            IpAddress srcIpAddress,
+                                            MacAddress srcMacAddress,
+                                            ConnectPoint srcConnectPoint) {
+        checkNotNull(dstIpAddress);
+        checkNotNull(srcIpAddress);
+        checkNotNull(srcMacAddress);
+        checkNotNull(srcConnectPoint);
+
+        IpPrefix srcIpPrefix = srcIpAddress.toIpPrefix();
+        IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix();
+        ConnectPoint dstConnectPoint = null;
+        MacAddress dstMacAddress = null;
+
+        for (Host host : hostService.getHostsByIp(dstIpAddress)) {
+            if (host.mac() != null) {
+                dstMacAddress = host.mac();
+                dstConnectPoint = host.location();
+                break;
+            }
+        }
+        if (dstMacAddress == null) {
+            hostService.startMonitoringIp(dstIpAddress);
+            return;
+        }
+
+        //
+        // Handle intent from source host to destination host
+        //
+        MultiPointToSinglePointIntent srcToDstIntent =
+                hostToHostIntentGenerator(dstIpAddress, dstConnectPoint,
+                                    dstMacAddress, srcConnectPoint);
+        submitReactiveIntent(dstIpPrefix, srcToDstIntent);
+
+        //
+        // Handle intent from destination host to source host
+        //
+
+        // Since we proactively handle the intent from destination host to
+        // source host, we should check whether there is an exiting intent
+        // first.
+        if (mp2pIntentExists(srcIpPrefix)) {
+            updateExistingMp2pIntent(srcIpPrefix, dstConnectPoint);
+            return;
+        } else {
+            // There is no existing intent, create a new one.
+            MultiPointToSinglePointIntent dstToSrcIntent =
+                    hostToHostIntentGenerator(srcIpAddress, srcConnectPoint,
+                                        srcMacAddress, dstConnectPoint);
+            submitReactiveIntent(srcIpPrefix, dstToSrcIntent);
+        }
+    }
+
+    /**
+     * Generates MultiPointToSinglePointIntent for both source host and
+     * destination host located in local SDN network.
+     *
+     * @param dstIpAddress the destination IP address
+     * @param dstConnectPoint the destination host connect point
+     * @param dstMacAddress the MAC address of destination host
+     * @param srcConnectPoint the connect point where packet-in from
+     * @return the generated MultiPointToSinglePointIntent
+     */
+    private MultiPointToSinglePointIntent hostToHostIntentGenerator(
+                                       IpAddress dstIpAddress,
+                                       ConnectPoint dstConnectPoint,
+                                       MacAddress dstMacAddress,
+                                       ConnectPoint srcConnectPoint) {
+        checkNotNull(dstIpAddress);
+        checkNotNull(dstConnectPoint);
+        checkNotNull(dstMacAddress);
+        checkNotNull(srcConnectPoint);
+
+        Set<ConnectPoint> ingressPoints = new HashSet<ConnectPoint>();
+        ingressPoints.add(srcConnectPoint);
+        IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix();
+
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        if (dstIpAddress.isIp4()) {
+            selector.matchEthType(Ethernet.TYPE_IPV4);
+            selector.matchIPDst(dstIpPrefix);
+        } else {
+            selector.matchEthType(Ethernet.TYPE_IPV6);
+            selector.matchIPv6Dst(dstIpPrefix);
+        }
+
+        // Rewrite the destination MAC address
+        TrafficTreatment.Builder treatment =
+                DefaultTrafficTreatment.builder().setEthDst(dstMacAddress);
+
+        Key key = Key.of(dstIpPrefix.toString(), appId);
+        int priority = dstIpPrefix.prefixLength() * PRIORITY_MULTIPLIER
+                + PRIORITY_OFFSET;
+        MultiPointToSinglePointIntent intent =
+                MultiPointToSinglePointIntent.builder()
+                .appId(appId)
+                .key(key)
+                .selector(selector.build())
+                .treatment(treatment.build())
+                .ingressPoints(ingressPoints)
+                .egressPoint(dstConnectPoint)
+                .priority(priority)
+                .build();
+
+        log.trace("Generates ConnectivityHostToHost = {} ", intent);
+        return intent;
+    }
+
+    @Override
+    public void updateExistingMp2pIntent(IpPrefix ipPrefix,
+                                         ConnectPoint ingressConnectPoint) {
+        checkNotNull(ipPrefix);
+        checkNotNull(ingressConnectPoint);
+
+        MultiPointToSinglePointIntent existingIntent =
+                getExistingMp2pIntent(ipPrefix);
+        if (existingIntent != null) {
+            Set<ConnectPoint> ingressPoints = existingIntent.ingressPoints();
+            // Add host connect point into ingressPoints of the existing intent
+            if (ingressPoints.add(ingressConnectPoint)) {
+                MultiPointToSinglePointIntent updatedMp2pIntent =
+                        MultiPointToSinglePointIntent.builder()
+                        .appId(appId)
+                        .key(existingIntent.key())
+                        .selector(existingIntent.selector())
+                        .treatment(existingIntent.treatment())
+                        .ingressPoints(ingressPoints)
+                        .egressPoint(existingIntent.egressPoint())
+                        .priority(existingIntent.priority())
+                        .build();
+
+                log.trace("Update an existing MultiPointToSinglePointIntent "
+                        + "to new intent = {} ", updatedMp2pIntent);
+                submitReactiveIntent(ipPrefix, updatedMp2pIntent);
+            }
+            // If adding ingressConnectPoint to ingressPoints failed, it
+            // because between the time interval from checking existing intent
+            // to generating new intent, onos updated this intent due to other
+            // packet-in and the new intent also includes the
+            // ingressConnectPoint. This will not affect reactive routing.
+        }
+    }
+
+    @Override
+    public boolean mp2pIntentExists(IpPrefix ipPrefix) {
+        checkNotNull(ipPrefix);
+        return routeIntents.get(ipPrefix) == null ? false : true;
+    }
+
+    /**
+     * Gets the existing MultiPointToSinglePointIntent from memory for a given
+     * IP prefix.
+     *
+     * @param ipPrefix the IP prefix used to find MultiPointToSinglePointIntent
+     * @return the MultiPointToSinglePointIntent if found, otherwise null
+     */
+    private MultiPointToSinglePointIntent getExistingMp2pIntent(IpPrefix
+                                                                ipPrefix) {
+        checkNotNull(ipPrefix);
+        return routeIntents.get(ipPrefix);
+    }
 }
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java
index 641559d..fba0fcf 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java
@@ -29,6 +29,7 @@
 import org.onosproject.config.NetworkConfigService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
+import org.onosproject.net.host.HostService;
 import org.onosproject.net.intent.IntentService;
 import org.onosproject.routing.RoutingService;
 import org.onosproject.routing.config.RoutingConfigurationService;
@@ -53,6 +54,9 @@
     protected IntentService intentService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostService hostService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected ClusterService clusterService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -89,6 +93,7 @@
         localControllerNode = clusterService.getLocalNode();
 
         intentSynchronizer = new IntentSynchronizer(appId, intentService,
+                                                    hostService,
                                                     config);
         intentSynchronizer.start();
 
@@ -97,7 +102,9 @@
                                                        config);
         peerConnectivity.start();
 
-        routingService.start(intentSynchronizer);
+        routingService.addFibListener(intentSynchronizer);
+        routingService.addIntentRequestListener(intentSynchronizer);
+        routingService.start();
 
         leadershipService.addListener(leadershipEventListener);
         leadershipService.runForLeadership(appId.name());
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpService.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpService.java
index dbb0d88..89d2651 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpService.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpService.java
@@ -27,4 +27,5 @@
      * @param isPrimary true if the instance is primary, false if it is not
      */
     public void modifyPrimary(boolean isPrimary);
+
 }
diff --git a/apps/sdnip/src/main/resources/config-examples/sdnip.json b/apps/sdnip/src/main/resources/config-examples/sdnip.json
index 13f4db8..913687e 100644
--- a/apps/sdnip/src/main/resources/config-examples/sdnip.json
+++ b/apps/sdnip/src/main/resources/config-examples/sdnip.json
@@ -62,5 +62,21 @@
             ]
             
         }
+    ],
+    "ip4LocalPrefixes" : [
+        {
+                "ipPrefix" : "100.0.0.0/24",
+                "type" : "PUBLIC"
+        },
+        {
+                "ipPrefix" : "200.0.0.0/8",
+                "type" : "PUBLIC"
+        },
+        {
+                "ipPrefix" : "192.0.0.0/24",
+                "type" : "PRIVATE"
+        }
+    ],
+    "ip6LocalPrefixes" : [
     ]
 }
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java
index 7625af6..91315c9 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java
+++ b/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java
@@ -118,7 +118,7 @@
         intentService = createMock(IntentService.class);
 
         intentSynchronizer = new IntentSynchronizer(APPID, intentService,
-                                                    routingConfig);
+                                                    null, routingConfig);
     }
 
     /**
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java
index f16c8de..f3ea2aa 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java
+++ b/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java
@@ -555,7 +555,7 @@
         replay(intentService);
 
         intentSynchronizer = new IntentSynchronizer(APPID, intentService,
-                                                    routingConfig);
+                                                    null, routingConfig);
         intentSynchronizer.leaderChanged(true);
         TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
 
diff --git a/features/features.xml b/features/features.xml
index fb76afd..7e4ebbd 100644
--- a/features/features.xml
+++ b/features/features.xml
@@ -188,6 +188,12 @@
         <bundle>mvn:org.onosproject/onos-app-optical/@ONOS-VERSION</bundle>
     </feature>
 
+    <feature name="onos-app-reactive-routing" version="@FEATURE-VERSION"
+             description="ONOS SDN/IP reactive routing">
+        <feature>onos-app-sdnip</feature>
+        <bundle>mvn:org.onosproject/onos-app-reactive-routing/@ONOS-VERSION</bundle>
+    </feature>
+
     <feature name="onos-app-sdnip" version="@FEATURE-VERSION"
              description="SDN-IP peering application">
         <feature>onos-api</feature>
diff --git a/utils/misc/src/main/java/org/onlab/packet/IpAddress.java b/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
index a783fa0..93a550c 100644
--- a/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
+++ b/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
@@ -371,6 +371,22 @@
     }
 
     /**
+     * Generates an IP prefix.
+     *
+     * @return the IP prefix of the IP address
+     */
+    public IpPrefix toIpPrefix() {
+
+        if (isIp4()) {
+            return IpPrefix.valueOf(new IpAddress(Version.INET, octets),
+                                    Ip4Address.BIT_LENGTH);
+        } else {
+            return IpPrefix.valueOf(new IpAddress(Version.INET6, octets),
+                                    Ip6Address.BIT_LENGTH);
+        }
+    }
+
+    /**
      * Gets the IP address name for the IP address version.
      *
      * @param version the IP address version