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;
             }
         }