[ONOS-4920] Use subnet not network when setting routing rules for routers

Change-Id: I91bdab845071436dda16ba05f2dd7eeca23a111b
diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/Constants.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/Constants.java
index dbcb521..1af9dc6 100644
--- a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/Constants.java
+++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/Constants.java
@@ -58,7 +58,7 @@
     public static final int SWITCHING_RULE_PRIORITY = 30000;
     public static final int TUNNELTAG_RULE_PRIORITY = 30000;
     public static final int ACL_RULE_PRIORITY = 30000;
-
+    public static final int EW_ROUTING_RULE_PRIORITY = 28000;
     public static final int ROUTING_RULE_PRIORITY = 25000;
     public static final int FLOATING_RULE_PRIORITY = 42000;
     public static final int PNAT_RULE_PRIORITY = 26000;
diff --git a/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingManager.java b/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingManager.java
index 9b12402..add2634 100644
--- a/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingManager.java
+++ b/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingManager.java
@@ -24,6 +24,7 @@
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.util.Tools;
 import org.onosproject.core.ApplicationId;
@@ -58,6 +59,8 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
@@ -121,7 +124,10 @@
             openstackService.ports().stream()
                     .filter(osPort -> osPort.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE) &&
                             osPort.deviceId().equals(osRouter.id()))
-                    .forEach(osPort -> setExternalConnection(osRouter, osPort.networkId()));
+                    .forEach(osPort -> {
+                        String subnetId = osPort.fixedIps().keySet().stream().findFirst().get();
+                        setExternalConnection(osRouter, subnetId);
+                    });
 
             log.info("Connected external gateway {} to router {}",
                      osRouter.gatewayExternalInfo().externalFixedIps(),
@@ -153,7 +159,8 @@
 
         setRoutes(osRouter, Optional.empty());
         if (osRouter.gatewayExternalInfo().externalFixedIps().size() > 0) {
-            setExternalConnection(osRouter, osPort.networkId());
+            String subnetId = osPort.fixedIps().keySet().stream().findFirst().get();
+            setExternalConnection(osRouter, subnetId);
         }
         log.info("Connected {} to router {}", osPort.fixedIps(), osRouter.name());
     }
@@ -169,22 +176,22 @@
         OpenstackSubnet osSubnet = openstackService.subnet(routerIface.subnetId());
         OpenstackNetwork osNet = openstackService.network(osSubnet.networkId());
 
-        unsetRoutes(osRouter, osNet);
+        unsetRoutes(osRouter, osSubnet);
         if (osRouter.gatewayExternalInfo().externalFixedIps().size() > 0) {
             unsetExternalConnection(osRouter, osNet.id());
         }
         log.info("Disconnected {} from router {}", osSubnet.cidr(), osRouter.name());
     }
 
-    private void setExternalConnection(OpenstackRouter osRouter, String osNetId) {
+    private void setExternalConnection(OpenstackRouter osRouter, String osSubNetId) {
         if (!osRouter.gatewayExternalInfo().isEnablePnat()) {
             log.debug("Source NAT is disabled");
             return;
         }
 
-        // FIXME router interface is subnet specific, not network
-        OpenstackNetwork osNet = openstackService.network(osNetId);
-        populateExternalRules(osNet);
+        OpenstackSubnet osSubNet = openstackService.subnet(osSubNetId);
+        OpenstackNetwork osNet = openstackService.network(osSubNet.networkId());
+        populateExternalRules(osNet, osSubNet);
     }
 
     private void unsetExternalConnection(OpenstackRouter osRouter, String osNetId) {
@@ -199,40 +206,39 @@
     }
 
     private void setRoutes(OpenstackRouter osRouter, Optional<Host> host) {
-        Set<OpenstackNetwork> routableNets = routableNetworks(osRouter.id());
-        if (routableNets.size() < 2) {
+        Set<OpenstackSubnet> routableSubNets = routableSubNets(osRouter.id());
+        if (routableSubNets.size() < 2) {
             // no other subnet interface is connected to this router, do nothing
             return;
         }
 
-        // FIXME router interface is subnet specific, not network
-        Set<String> routableNetIds = routableNets.stream()
-                .map(OpenstackNetwork::id)
+        Set<String> routableSubNetIds = routableSubNets.stream()
+                .map(OpenstackSubnet::id)
                 .collect(Collectors.toSet());
 
+
         Set<Host> hosts = host.isPresent() ? ImmutableSet.of(host.get()) :
                 Tools.stream(hostService.getHosts())
-                        .filter(h -> routableNetIds.contains(h.annotations().value(NETWORK_ID)))
+                        .filter(h -> routableSubNetIds.contains(h.annotations().value(SUBNET_ID)))
                         .collect(Collectors.toSet());
 
-        hosts.forEach(h -> populateRoutingRules(h, routableNets));
+        hosts.forEach(h -> populateRoutingRules(h, routableSubNets));
     }
 
-    private void unsetRoutes(OpenstackRouter osRouter, OpenstackNetwork osNet) {
-        // FIXME router interface is subnet specific, not network
-        Set<OpenstackNetwork> routableNets = routableNetworks(osRouter.id());
+    private void unsetRoutes(OpenstackRouter osRouter, OpenstackSubnet osSubNet) {
+        Set<OpenstackSubnet> routableSubNets = routableSubNets(osRouter.id());
         Tools.stream(hostService.getHosts())
                 .filter(h -> Objects.equals(
-                        h.annotations().value(NETWORK_ID), osNet.id()))
-                .forEach(h -> removeRoutingRules(h, routableNets));
+                        h.annotations().value(NETWORK_ID), osSubNet.id()))
+                .forEach(h -> removeRoutingRules(h, routableSubNets));
 
-        routableNets.forEach(n -> {
+        routableSubNets.forEach(n -> {
             Tools.stream(hostService.getHosts())
                     .filter(h -> Objects.equals(
-                            h.annotations().value(NETWORK_ID),
+                            h.annotations().value(SUBNET_ID),
                             n.id()))
-                    .forEach(h -> removeRoutingRules(h, ImmutableSet.of(osNet)));
-            log.debug("Removed between {} to {}", n.name(), osNet.name());
+                    .forEach(h -> removeRoutingRules(h, ImmutableSet.of(osSubNet)));
+            log.debug("Removed between {} to {}", n.name(), osSubNet.name());
         });
     }
 
@@ -249,18 +255,17 @@
                 .findAny();
     }
 
-    private Set<OpenstackNetwork> routableNetworks(String osRouterId) {
-        // FIXME router interface is subnet specific, not network
+    private Set<OpenstackSubnet> routableSubNets(String osRouterId) {
         return openstackService.ports().stream()
                 .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE) &&
                         p.deviceId().equals(osRouterId))
-                .map(p -> openstackService.network(p.networkId()))
+                .map(p -> openstackService.subnet(p.fixedIps().keySet().stream().findFirst().get()))
                 .collect(Collectors.toSet());
     }
 
-    private void populateExternalRules(OpenstackNetwork osNet) {
-        populateCnodeToGateway(Long.valueOf(osNet.segmentId()));
-        populateGatewayToController(Long.valueOf(osNet.segmentId()));
+    private void populateExternalRules(OpenstackNetwork osNet, OpenstackSubnet osSubNet) {
+        populateCnodeToGateway(Long.valueOf(osNet.segmentId()), osSubNet.cidr());
+        populateGatewayToController(Long.valueOf(osNet.segmentId()), osSubNet.cidr());
     }
 
     private void removeExternalRules(OpenstackNetwork osNet) {
@@ -284,9 +289,9 @@
         });
     }
 
-    private void populateRoutingRules(Host host, Set<OpenstackNetwork> osNets) {
-        String osNetId = host.annotations().value(NETWORK_ID);
-        if (osNetId == null) {
+    private void populateRoutingRules(Host host, Set<OpenstackSubnet> osSubNets) {
+        String osSubNetId = host.annotations().value(SUBNET_ID);
+        if (osSubNetId == null) {
             return;
         }
 
@@ -297,37 +302,46 @@
             return;
         }
 
+        Map<String, String> vniMap = new HashMap<>();
+        openstackService.networks().stream().forEach(n -> vniMap.put(n.id(), n.segmentId()));
+
         // TODO improve pipeline, do we have to install access rules between networks
         // for every single VMs?
-        osNets.stream().filter(osNet -> !osNet.id().equals(osNetId)).forEach(osNet -> {
+        osSubNets.stream().filter(osSubNet -> !osSubNet.id().equals(osSubNetId)).forEach(osSubNet -> {
             populateRoutingRulestoSameNode(
                     host.ipAddresses().stream().findFirst().get().getIp4Address(),
                     host.mac(),
                     localPort, localDevice,
-                    Long.valueOf(osNet.segmentId()));
+                    Long.valueOf(vniMap.get(osSubNet.networkId())),
+                    osSubNet.cidr());
 
             nodeService.completeNodes().stream()
                     .filter(node -> node.type().equals(COMPUTE))
                     .filter(node -> !node.intBridge().equals(localDevice))
                     .forEach(node -> populateRoutingRulestoDifferentNode(
                             host.ipAddresses().stream().findFirst().get().getIp4Address(),
-                            Long.valueOf(osNet.segmentId()),
+                            Long.valueOf(vniMap.get(osSubNet.networkId())),
                             node.intBridge(),
-                            nodeService.dataIp(localDevice).get().getIp4Address()));
+                            nodeService.dataIp(localDevice).get().getIp4Address(),
+                            osSubNet.cidr()));
         });
     }
 
-    private void removeRoutingRules(Host host, Set<OpenstackNetwork> osNets) {
-        String osNetId = host.annotations().value(NETWORK_ID);
-        if (osNetId == null) {
+    private void removeRoutingRules(Host host, Set<OpenstackSubnet> osSubNets) {
+        String osSubNetId = host.annotations().value(SUBNET_ID);
+        if (osSubNetId == null) {
             return;
         }
 
-        osNets.stream().filter(osNet -> !osNet.id().equals(osNetId)).forEach(osNet -> {
+        Map<String, String> vniMap = new HashMap<>();
+        openstackService.networks().stream().forEach(n -> vniMap.put(n.id(), n.segmentId()));
+
+        osSubNets.stream().filter(osSubNet -> !osSubNet.id().equals(osSubNetId)).forEach(osSubNet -> {
             TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
             sBuilder.matchEthType(Ethernet.TYPE_IPV4)
                     .matchIPDst(host.ipAddresses().stream().findFirst().get().toIpPrefix())
-                    .matchTunnelId(Long.valueOf(osNet.segmentId()));
+                    .matchIPSrc(IpPrefix.valueOf(osSubNet.cidr()))
+                    .matchTunnelId(Long.valueOf(vniMap.get(osSubNet.networkId())));
 
             nodeService.completeNodes().stream()
                     .filter(node -> node.type().equals(COMPUTE))
@@ -339,15 +353,16 @@
                             ForwardingObjective.Flag.SPECIFIC,
                             ROUTING_RULE_PRIORITY));
         });
-        log.debug("Removed routing rule from {} to {}", host, osNets);
+        log.debug("Removed routing rule from {} to {}", host, osSubNets);
     }
 
-    private void populateGatewayToController(long vni) {
+    private void populateGatewayToController(long vni, String subNetCidr) {
         TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
         TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
 
         sBuilder.matchEthType(Ethernet.TYPE_IPV4)
                 .matchTunnelId(vni)
+                .matchIPSrc(IpPrefix.valueOf(subNetCidr))
                 .matchEthDst(Constants.DEFAULT_GATEWAY_MAC);
         tBuilder.setOutput(PortNumber.CONTROLLER);
 
@@ -362,21 +377,22 @@
         gatewayService.getGatewayDeviceIds().forEach(deviceId -> flowObjectiveService.forward(deviceId, fo));
     }
 
-    private void populateCnodeToGateway(long vni) {
+    private void populateCnodeToGateway(long vni, String subnetCidr) {
         nodeService.completeNodes().stream()
                 .filter(node -> node.type().equals(COMPUTE))
                 .forEach(node -> populateRuleToGateway(
                         node.intBridge(),
                         gatewayService.getGatewayGroupId(node.intBridge()),
-                        vni));
+                        vni, subnetCidr));
     }
 
-    private void populateRuleToGateway(DeviceId deviceId, GroupId groupId, long vni) {
+    private void populateRuleToGateway(DeviceId deviceId, GroupId groupId, long vni, String cidr) {
         TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
         TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
 
         sBuilder.matchEthType(Ethernet.TYPE_IPV4)
                 .matchTunnelId(vni)
+                .matchIPSrc(IpPrefix.valueOf(cidr))
                 .matchEthDst(Constants.DEFAULT_GATEWAY_MAC);
 
         tBuilder.group(groupId);
@@ -392,12 +408,14 @@
     }
 
     private void populateRoutingRulestoDifferentNode(Ip4Address vmIp, long vni,
-                                                     DeviceId deviceId, Ip4Address hostIp) {
+                                                     DeviceId deviceId, Ip4Address hostIp,
+                                                     String cidr) {
         TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
         TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
 
         sBuilder.matchEthType(Ethernet.TYPE_IPV4)
                 .matchTunnelId(vni)
+                .matchIPSrc(IpPrefix.valueOf(cidr))
                 .matchIPDst(vmIp.toIpPrefix());
         tBuilder.extension(buildExtension(deviceService, deviceId, hostIp), deviceId)
                 .setOutput(nodeService.tunnelPort(deviceId).get());
@@ -405,7 +423,7 @@
         ForwardingObjective fo = DefaultForwardingObjective.builder()
                 .withSelector(sBuilder.build())
                 .withTreatment(tBuilder.build())
-                .withPriority(ROUTING_RULE_PRIORITY)
+                .withPriority(EW_ROUTING_RULE_PRIORITY)
                 .withFlag(ForwardingObjective.Flag.SPECIFIC)
                 .fromApp(appId)
                 .add();
@@ -414,12 +432,15 @@
     }
 
     private void populateRoutingRulestoSameNode(Ip4Address vmIp, MacAddress vmMac,
-                                                PortNumber port, DeviceId deviceId, long vni) {
+                                                PortNumber port, DeviceId deviceId, long vni,
+                                                String cidr) {
         TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
         TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
 
+        // FIXME: we need to check the VNI of the dest IP also just in case...
         sBuilder.matchEthType(Ethernet.TYPE_IPV4)
                 .matchIPDst(vmIp.toIpPrefix())
+                .matchIPSrc(IpPrefix.valueOf(cidr))
                 .matchTunnelId(vni);
 
         tBuilder.setEthDst(vmMac)
@@ -428,7 +449,7 @@
         ForwardingObjective fo = DefaultForwardingObjective.builder()
                 .withSelector(sBuilder.build())
                 .withTreatment(tBuilder.build())
-                .withPriority(ROUTING_RULE_PRIORITY)
+                .withPriority(EW_ROUTING_RULE_PRIORITY)
                 .withFlag(ForwardingObjective.Flag.SPECIFIC)
                 .fromApp(appId)
                 .add();
@@ -443,7 +464,8 @@
                     OpenstackRouter osRouter = openstackRouter(osPort.deviceId());
                     setRoutes(osRouter, Optional.empty());
                     if (osRouter.gatewayExternalInfo().externalFixedIps().size() > 0) {
-                        setExternalConnection(osRouter, osPort.networkId());
+                        String subnetId = osPort.fixedIps().keySet().stream().findFirst().get();
+                        setExternalConnection(osRouter, subnetId);
                     }
                 }));
     }
@@ -467,8 +489,8 @@
         if (!routerIface.isPresent()) {
             return;
         }
-        Set<OpenstackNetwork> routableNets = routableNetworks(routerIface.get().deviceId());
-        eventExecutor.execute(() -> removeRoutingRules(host, routableNets));
+        Set<OpenstackSubnet> routableSubNets = routableSubNets(routerIface.get().deviceId());
+        eventExecutor.execute(() -> removeRoutingRules(host, routableSubNets));
     }
 
     private class InternalNodeListener implements OpenstackNodeListener {