add flowrule to specific ip for userdata

Change-Id: Ia38af7556cc0e04c173f3c337a8dd17f49a20eba
diff --git a/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/manager/impl/VtnManager.java b/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/manager/impl/VtnManager.java
index 17f43fd..be3d269 100644
--- a/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/manager/impl/VtnManager.java
+++ b/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/manager/impl/VtnManager.java
@@ -106,6 +106,7 @@
 import org.onosproject.vtnrsc.RouterInterface;
 import org.onosproject.vtnrsc.SecurityGroup;
 import org.onosproject.vtnrsc.SegmentationId;
+import org.onosproject.vtnrsc.Subnet;
 import org.onosproject.vtnrsc.SubnetId;
 import org.onosproject.vtnrsc.TenantId;
 import org.onosproject.vtnrsc.TenantNetwork;
@@ -205,6 +206,7 @@
     private static final String EX_PORT_OF_DEVICE = "exPortOfDevice";
     private static final String EX_PORT_MAP = "exPortMap";
     private static final String DEFAULT_IP = "0.0.0.0";
+    private static final String USERDATA_IP = "169.254.169.254";
     private static final int SUBNET_NUM = 2;
 
     private EventuallyConsistentMap<VirtualPortId, VirtualPort> vPortStore;
@@ -557,7 +559,27 @@
         for (PortNumber p : localTunnelPorts) {
             programGroupTable(deviceId, appId, p, devices, type);
         }
-
+        Subnet subnet = subnetService.getSubnet(subnetId);
+        String deviceOwner = virtualPort.deviceOwner();
+        if (deviceOwner != null) {
+            if (deviceOwner.equalsIgnoreCase("network:dhcp")) {
+                Sets.newHashSet(devices).stream()
+                        .filter(d -> d.type() == Device.Type.SWITCH)
+                        .forEach(d -> {
+                            if (subnet != null) {
+                                IpAddress dstIp = IpAddress
+                                        .valueOf(USERDATA_IP);
+                                classifierService
+                                        .programUserdataClassifierRules(d.id(),
+                                                                        subnet.cidr(),
+                                                                        dstIp,
+                                                                        mac,
+                                                                        segmentationId,
+                                                                        type);
+                            }
+                        });
+            }
+        }
         if (type == Objective.Operation.ADD) {
             vPortStore.put(virtualPortId, virtualPort);
             if (networkOflocalHostPorts == null) {
diff --git a/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/table/ClassifierService.java b/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/table/ClassifierService.java
index 7715b9d..fffa2fb 100644
--- a/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/table/ClassifierService.java
+++ b/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/table/ClassifierService.java
@@ -16,6 +16,7 @@
 package org.onosproject.vtn.table;
 
 import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.DeviceId;
@@ -102,4 +103,20 @@
                                    SegmentationId actionVni,
                                    Objective.Operation type);
 
+    /**
+     * Assemble the Userdata Classifier table rules.
+     * Match: subnet ip prefix and destination ip.
+     * Action: add flow rule to specific ip for userdata.
+     *
+     * @param deviceId Device Id
+     * @param ipPrefix source ip prefix
+     * @param dstIp userdata ip
+     * @param dstmac dst mac
+     * @param actionVni the vni of the source network (l2vni)
+     * @param type the operation type of the flow rules
+     */
+    void programUserdataClassifierRules(DeviceId deviceId, IpPrefix ipPrefix,
+                                        IpAddress dstIp, MacAddress dstmac,
+                                        SegmentationId actionVni,
+                                        Objective.Operation type);
 }
diff --git a/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/table/impl/ClassifierServiceImpl.java b/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/table/impl/ClassifierServiceImpl.java
index e60c217..030c783 100644
--- a/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/table/impl/ClassifierServiceImpl.java
+++ b/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/table/impl/ClassifierServiceImpl.java
@@ -56,7 +56,7 @@
     private static final int ARP_CLASSIFIER_PRIORITY = 60000;
     private static final int L3_CLASSIFIER_PRIORITY = 0xffff;
     private static final int L2_CLASSIFIER_PRIORITY = 50000;
-
+    private static final int USERDATA_CLASSIFIER_PRIORITY = 65535;
     private final FlowObjectiveService flowObjectiveService;
     private final ApplicationId appId;
 
@@ -193,4 +193,29 @@
         }
     }
 
+    @Override
+    public void programUserdataClassifierRules(DeviceId deviceId,
+                                               IpPrefix ipPrefix,
+                                               IpAddress dstIp,
+                                               MacAddress dstmac,
+                                               SegmentationId actionVni,
+                                               Objective.Operation type) {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4).matchIPSrc(ipPrefix)
+                .matchIPDst(IpPrefix.valueOf(dstIp, 32)).build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setTunnelId(Long.parseLong(actionVni.segmentationId()))
+                .setEthDst(dstmac).build();
+        ForwardingObjective.Builder objective = DefaultForwardingObjective
+                .builder().withTreatment(treatment).withSelector(selector)
+                .fromApp(appId).withFlag(Flag.SPECIFIC)
+                .withPriority(USERDATA_CLASSIFIER_PRIORITY);
+        if (type.equals(Objective.Operation.ADD)) {
+            log.debug("UserdataClassifierRules-->ADD");
+            flowObjectiveService.forward(deviceId, objective.add());
+        } else {
+            log.debug("UserdataClassifierRules-->REMOVE");
+            flowObjectiveService.forward(deviceId, objective.remove());
+        }
+    }
 }
diff --git a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/service/impl/VtnRscManager.java b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/service/impl/VtnRscManager.java
index 97fd122..b39e82c 100644
--- a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/service/impl/VtnRscManager.java
+++ b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/service/impl/VtnRscManager.java
@@ -51,6 +51,7 @@
 import org.onosproject.vtnrsc.PortPairGroup;
 import org.onosproject.vtnrsc.PortPairId;
 import org.onosproject.vtnrsc.Router;
+import org.onosproject.vtnrsc.RouterId;
 import org.onosproject.vtnrsc.RouterInterface;
 import org.onosproject.vtnrsc.SegmentationId;
 import org.onosproject.vtnrsc.Subnet;
@@ -167,7 +168,8 @@
 
         KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
                 .register(KryoNamespaces.API)
-                .register(TenantId.class, DeviceId.class, SegmentationId.class);
+                .register(TenantId.class, DeviceId.class, SegmentationId.class,
+                          TenantRouter.class, RouterId.class);
         l3vniTenantMap = storageService
                 .<TenantId, SegmentationId>eventuallyConsistentMapBuilder()
                 .withName(L3VNITENANTMAP).withSerializer(serializer)