CORD-512 Support vSG <-> vRouter default route

- Support multiple subnets per port. getIpPort() will only return the first non-/32 and non-/0 subnet
    /32 is used as vSG subnet
    /0 is used as default gateway
- Support multiple L3 unicast group on a single port
    Change the way to generate the group ID and group key
- Special case for 0.0.0.0 host. Push a /0 to IP table instead of /32
- Implement vRouterConfig
    Put VR MAC to TMAC table of all leaves when config added
        When processEthDst see PortNumber.ANY in key, match ETH_DST only
- For OFDPA, wipe existing instruction before sending to controller
    So packet that misses L3 unicast table won't be sent to controller twice
- For SpringOpenTTP, pop VLAN before sending to controller
- Move several constant definitions to SegmentRoutingService
- Add minimum priority for IP rules such that /0 won't collide with zero priority default rules
- Update the config sample
    Use VLAN=-1 for hosts
    Add example for default route

Change-Id: Id751697ce36a7e5c13b3859350ff21b585c38525
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
index 3d2d337..db4bc63 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
@@ -15,9 +15,12 @@
  */
 package org.onosproject.segmentrouting.config;
 
+import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.SetMultimap;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onosproject.incubator.net.config.basics.ConfigException;
@@ -37,6 +40,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -59,13 +63,13 @@
         Ip4Address ip;
         MacAddress mac;
         boolean isEdge;
-        HashMap<PortNumber, Ip4Address> gatewayIps;
-        HashMap<PortNumber, Ip4Prefix> subnets;
+        Map<PortNumber, Ip4Address> gatewayIps;
+        SetMultimap<PortNumber, Ip4Prefix> subnets;
         Map<Integer, Set<Integer>> adjacencySids;
 
         public SegmentRouterInfo() {
             gatewayIps = new HashMap<>();
-            subnets = new HashMap<>();
+            subnets = HashMultimap.create();
         }
     }
 
@@ -78,10 +82,10 @@
     public DeviceConfiguration(NetworkConfigRegistry cfgService) {
         // Read config from device subject, excluding gatewayIps and subnets.
         Set<DeviceId> deviceSubjects =
-                cfgService.getSubjects(DeviceId.class, SegmentRoutingConfig.class);
+                cfgService.getSubjects(DeviceId.class, SegmentRoutingDeviceConfig.class);
         deviceSubjects.forEach(subject -> {
-            SegmentRoutingConfig config =
-                cfgService.getConfig(subject, SegmentRoutingConfig.class);
+            SegmentRoutingDeviceConfig config =
+                cfgService.getConfig(subject, SegmentRoutingDeviceConfig.class);
             SegmentRouterInfo info = new SegmentRouterInfo();
             info.deviceId = subject;
             info.nodeSid = config.nodeSid();
@@ -119,7 +123,11 @@
                     // Extract subnet information
                     Set<InterfaceIpAddress> interfaceAddresses = networkInterface.ipAddresses();
                     interfaceAddresses.forEach(interfaceAddress -> {
-                        info.gatewayIps.put(port, interfaceAddress.ipAddress().getIp4Address());
+                        // Do not add /0 and /32 to gateway IP list
+                        int prefixLength = interfaceAddress.subnetAddress().prefixLength();
+                        if (prefixLength != 0 && prefixLength != IpPrefix.MAX_INET_MASK_LENGTH) {
+                            info.gatewayIps.put(port, interfaceAddress.ipAddress().getIp4Address());
+                        }
                         info.subnets.put(port, interfaceAddress.subnetAddress().getIp4Prefix());
                     });
 
@@ -247,9 +255,13 @@
         Map<Ip4Prefix, List<PortNumber>> subnetPortMap = new HashMap<>();
 
         // Construct subnet-port mapping from port-subnet mapping
-        Map<PortNumber, Ip4Prefix> portSubnetMap =
+        SetMultimap<PortNumber, Ip4Prefix> portSubnetMap =
                 this.deviceConfigMap.get(deviceId).subnets;
-        portSubnetMap.forEach((port, subnet) -> {
+
+        portSubnetMap.entries().forEach(entry -> {
+            PortNumber port = entry.getKey();
+            Ip4Prefix subnet = entry.getValue();
+
             if (subnetPortMap.containsKey(subnet)) {
                 subnetPortMap.get(subnet).add(port);
             } else {
@@ -258,7 +270,6 @@
                 subnetPortMap.put(subnet, ports);
             }
         });
-
         return subnetPortMap;
     }
 
@@ -322,21 +333,6 @@
     }
 
     /**
-     * Returns the configured IP addresses per port
-     * for a segment router.
-     *
-     * @param deviceId device identifier
-     * @return map of port to gateway IP addresses or null if not found
-     */
-    public Map<PortNumber, Ip4Address> getPortIPMap(DeviceId deviceId) {
-        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
-        if (srinfo != null) {
-            return srinfo.gatewayIps;
-        }
-        return null;
-    }
-
-    /**
      * Returns the configured subnet prefixes for a segment router.
      *
      * @param deviceId device identifier
@@ -353,8 +349,8 @@
     }
 
     /**
-     *  Returns the configured subnet on the given port, or null if no
-     *  subnet has been configured on the port.
+     *  Returns the configured non-/32 and non-/0 subnet on the given port,
+     *  or null if no subnet has been configured on the port.
      *
      *  @param deviceId device identifier
      *  @param pnum  port identifier
@@ -363,7 +359,12 @@
     public Ip4Prefix getPortSubnet(DeviceId deviceId, PortNumber pnum) {
         SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
         if (srinfo != null) {
-            return srinfo.subnets.get(pnum);
+            Optional<Ip4Prefix> result = srinfo.subnets.get(pnum).stream()
+                    .filter(subnet ->
+                            subnet.getIp4Prefix().prefixLength() != IpPrefix.MAX_INET_MASK_LENGTH &&
+                            subnet.getIp4Prefix().prefixLength() != 0)
+                    .findFirst();
+            return (result.isPresent()) ? result.get() : null;
         }
         return null;
     }
@@ -378,7 +379,7 @@
     public Ip4Address getRouterIpAddressForASubnetHost(Ip4Address destIpAddress) {
         for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
                     deviceConfigMap.entrySet()) {
-            for (Ip4Prefix prefix:entry.getValue().subnets.values()) {
+            for (Ip4Prefix prefix : entry.getValue().subnets.values()) {
                 if (prefix.contains(destIpAddress)) {
                     return entry.getValue().ip;
                 }
@@ -428,7 +429,8 @@
         }
 
         for (Ip4Prefix subnet: subnets) {
-            if (subnet.contains(hostIp)) {
+            // Exclude /0 since it is a special case used for default route
+            if (subnet.prefixLength() != 0 && subnet.contains(hostIp)) {
                 return true;
             }
         }
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfig.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfig.java
new file mode 100644
index 0000000..e39fb18
--- /dev/null
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfig.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2016 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.segmentrouting.config;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.google.common.collect.ImmutableSet;
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.config.Config;
+import java.util.Set;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * App configuration object for Segment Routing.
+ */
+public class SegmentRoutingAppConfig extends Config<ApplicationId> {
+    private static final String VROUTER_MACS = "vRouterMacs";
+
+    @Override
+    public boolean isValid() {
+        return hasOnlyFields(VROUTER_MACS) && vRouterMacs() != null;
+    }
+
+    /**
+     * Gets vRouters from the config.
+     *
+     * @return a set of vRouter MAC addresses
+     */
+    public Set<MacAddress> vRouterMacs() {
+        if (!object.has(VROUTER_MACS)) {
+            return null;
+        }
+
+        ImmutableSet.Builder<MacAddress> builder = ImmutableSet.builder();
+        ArrayNode arrayNode = (ArrayNode) object.path(VROUTER_MACS);
+        for (JsonNode jsonNode : arrayNode) {
+            MacAddress mac;
+
+            String macStr = jsonNode.asText(null);
+            if (macStr == null) {
+                return null;
+            }
+            try {
+                mac = MacAddress.valueOf(macStr);
+            } catch (IllegalArgumentException e) {
+                return null;
+            }
+
+            builder.add(mac);
+        }
+        return builder.build();
+    }
+
+    /**
+     * Sets vRouters to the config.
+     *
+     * @param vRouterMacs a set of vRouter MAC addresses
+     * @return this {@link SegmentRoutingAppConfig}
+     */
+    public SegmentRoutingAppConfig setVRouterMacs(Set<MacAddress> vRouterMacs) {
+        if (vRouterMacs == null) {
+            object.remove(VROUTER_MACS);
+        } else {
+            ArrayNode arrayNode = mapper.createArrayNode();
+
+            vRouterMacs.forEach(mac -> {
+                arrayNode.add(mac.toString());
+            });
+
+            object.set(VROUTER_MACS, arrayNode);
+        }
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("vRouterMacs", vRouterMacs())
+                .toString();
+    }
+}
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingDeviceConfig.java
similarity index 87%
rename from apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java
rename to apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingDeviceConfig.java
index 64486c6..10c69ca 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingDeviceConfig.java
@@ -34,7 +34,7 @@
 /**
  * Configuration object for Segment Routing Application.
  */
-public class SegmentRoutingConfig extends Config<DeviceId> {
+public class SegmentRoutingDeviceConfig extends Config<DeviceId> {
     private static final String NAME = "name";
     private static final String IP = "routerIp";
     private static final String MAC = "routerMac";
@@ -71,8 +71,8 @@
      * @param name name of the router.
      * @return the config of the router.
      */
-    public SegmentRoutingConfig setName(String name) {
-        return (SegmentRoutingConfig) setOrClear(NAME, name);
+    public SegmentRoutingDeviceConfig setName(String name) {
+        return (SegmentRoutingDeviceConfig) setOrClear(NAME, name);
     }
 
     /**
@@ -91,8 +91,8 @@
      * @param ip IP address of the router.
      * @return the config of the router.
      */
-    public SegmentRoutingConfig setRouterIp(String ip) {
-        return (SegmentRoutingConfig) setOrClear(IP, ip);
+    public SegmentRoutingDeviceConfig setRouterIp(String ip) {
+        return (SegmentRoutingDeviceConfig) setOrClear(IP, ip);
     }
 
     /**
@@ -111,8 +111,8 @@
      * @param mac MAC address of the router.
      * @return the config of the router.
      */
-    public SegmentRoutingConfig setRouterMac(String mac) {
-        return (SegmentRoutingConfig) setOrClear(MAC, mac);
+    public SegmentRoutingDeviceConfig setRouterMac(String mac) {
+        return (SegmentRoutingDeviceConfig) setOrClear(MAC, mac);
     }
 
     /**
@@ -130,8 +130,8 @@
      * @param sid node SID of the router.
      * @return the config of the router.
      */
-    public SegmentRoutingConfig setNodeSid(int sid) {
-        return (SegmentRoutingConfig) setOrClear(SID, sid);
+    public SegmentRoutingDeviceConfig setNodeSid(int sid) {
+        return (SegmentRoutingDeviceConfig) setOrClear(SID, sid);
     }
 
     /**
@@ -154,8 +154,8 @@
      * @param isEdgeRouter true if the router is an edge router.
      * @return the config of the router.
      */
-    public SegmentRoutingConfig setIsEdgeRouter(boolean isEdgeRouter) {
-        return (SegmentRoutingConfig) setOrClear(EDGE, isEdgeRouter);
+    public SegmentRoutingDeviceConfig setIsEdgeRouter(boolean isEdgeRouter) {
+        return (SegmentRoutingDeviceConfig) setOrClear(EDGE, isEdgeRouter);
     }
 
     /**
@@ -197,7 +197,7 @@
      * @param adjacencySids adjacency SIDs of the router.
      * @return the config of the router.
      */
-    public SegmentRoutingConfig setAdjacencySids(Map<Integer, Set<Integer>> adjacencySids) {
+    public SegmentRoutingDeviceConfig setAdjacencySids(Map<Integer, Set<Integer>> adjacencySids) {
         if (adjacencySids == null) {
             object.remove(ADJSIDS);
         } else {