Fix: correct the logic of obtaining ext ip address from ext GW

Change-Id: I681a05eff40067c0b945022d16f2db9e514291d3
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
index ec7204e..7e65524 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
@@ -58,17 +58,14 @@
 import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
 import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
 import org.onosproject.openstacknetworking.api.PreCommitPortService;
-import org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil;
 import org.onosproject.openstacknode.api.OpenstackNode;
 import org.onosproject.openstacknode.api.OpenstackNodeEvent;
 import org.onosproject.openstacknode.api.OpenstackNodeListener;
 import org.onosproject.openstacknode.api.OpenstackNodeService;
-import org.openstack4j.model.network.IP;
 import org.openstack4j.model.network.NetFloatingIP;
 import org.openstack4j.model.network.Network;
 import org.openstack4j.model.network.Port;
 import org.openstack4j.model.network.Router;
-import org.openstack4j.model.network.RouterInterface;
 import org.osgi.service.component.ComponentContext;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
@@ -99,6 +96,7 @@
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.associatedFloatingIp;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.externalPeerRouterForNetwork;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.floatingIpByInstancePort;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getExternalIp;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByComputeDevId;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByInstancePort;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getPropertyValue;
@@ -530,65 +528,43 @@
     }
 
     private void setFakeGatewayArpRuleByRouter(Router router, boolean install) {
-        setFakeGatewayArpRuleByGateway(router.getId(), install);
-    }
-
-    private Set<IP> getExternalGatewaySnatIps(String routerId) {
-        if (routerId == null) {
-            return ImmutableSet.of();
-        }
-
-        Set<String> portIds = osRouterAdminService.routerInterfaces(routerId)
-                .stream()
-                .map(RouterInterface::getPortId)
-                .collect(Collectors.toSet());
-
-        return portIds.stream()
-                .map(pid -> osNetworkAdminService.port(pid))
-                .filter(p -> Objects.equals(p.getDeviceOwner(), DEVICE_OWNER_ROUTER_GW))
-                .flatMap(p -> p.getFixedIps().stream())
-                .collect(Collectors.toSet());
-    }
-
-    private void setFakeGatewayArpRuleByGateway(String routerId, boolean install) {
         if (ARP_BROADCAST_MODE.equals(getArpMode())) {
-            setFakeGatewayArpRuleByIps(getExternalGatewaySnatIps(routerId), install);
+            setFakeGatewayArpRuleByExternalIp(getExternalIp(router, osNetworkService), install);
         }
     }
 
-    private void setFakeGatewayArpRuleByIps(Set<IP> ips, boolean install) {
-        ips.forEach(ip -> {
-            TrafficSelector selector = DefaultTrafficSelector.builder()
-                    .matchEthType(EthType.EtherType.ARP.ethType().toShort())
-                    .matchArpOp(ARP.OP_REQUEST)
-                    .matchArpTpa(Ip4Address.valueOf(ip.getIpAddress()))
-                    .build();
+    private void setFakeGatewayArpRuleByExternalIp(IpAddress ipAddress, boolean install) {
 
-            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                    .setArpOp(ARP.OP_REPLY)
-                    .setArpSha(MacAddress.valueOf(gatewayMac))
-                    .setArpSpa(Ip4Address.valueOf(ip.getIpAddress()))
-                    .setOutput(PortNumber.IN_PORT)
-                    .build();
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(EthType.EtherType.ARP.ethType().toShort())
+                .matchArpOp(ARP.OP_REQUEST)
+                .matchArpTpa(ipAddress.getIp4Address())
+                .build();
 
-            osNodeService.completeNodes(GATEWAY).forEach(n ->
-                    osFlowRuleService.setRule(
-                            appId,
-                            n.intgBridge(),
-                            selector,
-                            treatment,
-                            PRIORITY_ARP_GATEWAY_RULE,
-                            GW_COMMON_TABLE,
-                            install
-                    )
-            );
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setArpOp(ARP.OP_REPLY)
+                .setArpSha(MacAddress.valueOf(gatewayMac))
+                .setArpSpa(ipAddress.getIp4Address())
+                .setOutput(PortNumber.IN_PORT)
+                .build();
 
-            if (install) {
-                log.info("Install ARP Rule for Gateway Snat {}", ip.getIpAddress());
-            } else {
-                log.info("Uninstall ARP Rule for Gateway Snat {}", ip.getIpAddress());
-            }
-        });
+        osNodeService.completeNodes(GATEWAY).forEach(n ->
+                osFlowRuleService.setRule(
+                        appId,
+                        n.intgBridge(),
+                        selector,
+                        treatment,
+                        PRIORITY_ARP_GATEWAY_RULE,
+                        GW_COMMON_TABLE,
+                        install
+                )
+        );
+
+        if (install) {
+            log.info("Install ARP Rule for Gateway Snat {}", ipAddress);
+        } else {
+            log.info("Uninstall ARP Rule for Gateway Snat {}", ipAddress);
+        }
     }
 
     /**
@@ -617,14 +593,18 @@
                 case OPENSTACK_PORT_CREATED:
                 case OPENSTACK_PORT_UPDATED:
                     eventExecutor.execute(() ->
-                        setFakeGatewayArpRuleByIps(
-                                (Set<IP>) event.port().getFixedIps(), true)
+                            setFakeGatewayArpRuleByExternalIp(
+                                    IpAddress.valueOf(event.port().getFixedIps()
+                                            .stream().findAny().get().getIpAddress()),
+                                    true)
                     );
                     break;
                 case OPENSTACK_PORT_REMOVED:
                     eventExecutor.execute(() ->
-                        setFakeGatewayArpRuleByIps(
-                                (Set<IP>) event.port().getFixedIps(), false)
+                            setFakeGatewayArpRuleByExternalIp(
+                                    IpAddress.valueOf(event.port().getFixedIps()
+                                            .stream().findAny().get().getIpAddress()),
+                                    false)
                     );
                     break;
                 default:
@@ -654,27 +634,20 @@
 
             switch (event.type()) {
                 case OPENSTACK_ROUTER_CREATED:
+                    // add a router with external gateway
+                case OPENSTACK_ROUTER_GATEWAY_ADDED:
+                    // add a gateway manually after adding a router
                     eventExecutor.execute(() ->
-                        // add a router with external gateway
-                        setFakeGatewayArpRuleByRouter(event.subject(), true)
+                            // add a router with external gateway
+                            setFakeGatewayArpRuleByRouter(event.subject(), true)
                     );
                     break;
                 case OPENSTACK_ROUTER_REMOVED:
-                    eventExecutor.execute(() ->
-                        // remove a router with external gateway
-                        setFakeGatewayArpRuleByRouter(event.subject(), false)
-                    );
-                    break;
-                case OPENSTACK_ROUTER_GATEWAY_ADDED:
-                    eventExecutor.execute(() ->
-                        // add a gateway manually after adding a router
-                        setFakeGatewayArpRuleByGateway(event.subject().getId(), true)
-                    );
-                    break;
+                    // remove a router with external gateway
                 case OPENSTACK_ROUTER_GATEWAY_REMOVED:
+                    // remove a gateway from an existing router
                     eventExecutor.execute(() ->
-                        // remove a gateway from an existing router
-                        setFakeGatewayArpRuleByGateway(event.subject().getId(), false)
+                            setFakeGatewayArpRuleByRouter(event.subject(), false)
                     );
                     break;
                 case OPENSTACK_FLOATING_IP_ASSOCIATED:
@@ -884,8 +857,7 @@
         private boolean isGwSelectedByComputeNode(Set<OpenstackNode> gws,
                                                   OpenstackNode computeNode,
                                                   OpenstackNode gwNode) {
-            return OpenstackNetworkingUtil
-                    .getGwByComputeDevId(gws, computeNode.intgBridge())
+            return getGwByComputeDevId(gws, computeNode.intgBridge())
                     .intgBridge().equals(gwNode.intgBridge());
         }
 
@@ -912,6 +884,7 @@
             if (getArpMode() == null) {
                 return;
             }
+            log.info("ARP mode is {}", getArpMode());
 
             switch (getArpMode()) {
                 case ARP_PROXY_MODE:
@@ -972,6 +945,7 @@
                     install
             );
 
+            log.info("calling setFakeGatewayArpRuleByRouter.. ");
             osRouterAdminService.routers().stream()
                     .filter(router -> router.getExternalGatewayInfo() != null)
                     .forEach(router -> setFakeGatewayArpRuleByRouter(router, install));
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandler.java
index 3e4b5ab..f4947f0 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandler.java
@@ -55,12 +55,10 @@
 import org.onosproject.store.service.ConsistentMap;
 import org.onosproject.store.service.Serializer;
 import org.onosproject.store.service.StorageService;
-import org.openstack4j.model.network.ExternalGateway;
 import org.openstack4j.model.network.Port;
 import org.openstack4j.model.network.Router;
 import org.openstack4j.model.network.RouterInterface;
 import org.openstack4j.model.network.Subnet;
-import org.openstack4j.openstack.networking.domain.NeutronIP;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Deactivate;
@@ -70,7 +68,6 @@
 
 import java.nio.ByteBuffer;
 import java.util.Objects;
-import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.stream.Collectors;
@@ -85,6 +82,8 @@
 import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
 import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
 import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_INTERNAL_ROUTING_RULE;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.externalIpFromSubnet;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.externalPeerRouterFromSubnet;
 import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -320,30 +319,19 @@
                 // this is a request to an external network
                 log.trace("Icmp request to external {} from {}", dstIp, srcIp);
 
-                RouterInterface routerInterface = routerInterface(srcSubnet);
-                if (routerInterface == null) {
-                    log.warn(ERR_REQ + "failed to get router interface");
-                    return false;
-                }
-
-                ExternalGateway externalGateway = externalGateway(routerInterface);
-                if (externalGateway == null) {
-                    log.warn(ERR_REQ + "failed to get external gateway");
-                    return false;
-                }
-
-                ExternalPeerRouter externalPeerRouter = osNetworkService.externalPeerRouter(externalGateway);
-                if (externalPeerRouter == null) {
-                    log.warn(ERR_REQ + "failed to get external peer router");
-                    return false;
-                }
-
-                IpAddress externalIp = getExternalIp(externalGateway, routerInterface);
+                IpAddress externalIp = externalIpFromSubnet(srcSubnet, osRouterService, osNetworkService);
                 if (externalIp == null) {
                     log.warn(ERR_REQ + "failed to get external ip");
                     return false;
                 }
 
+                ExternalPeerRouter externalPeerRouter =
+                        externalPeerRouterFromSubnet(srcSubnet, osRouterService, osNetworkService);
+                if (externalPeerRouter == null) {
+                    log.warn(ERR_REQ + "failed to get external peer router");
+                    return false;
+                }
+
                 String icmpInfoKey = icmpInfoKey(icmp,
                         externalIp.toString(),
                         IPv4.fromIPv4Address(ipPacket.getDestinationAddress()));
@@ -369,24 +357,6 @@
                     .concat(srcIp)
                     .concat(dstIp);
         }
-        private RouterInterface routerInterface(Subnet subnet) {
-            checkNotNull(subnet);
-            return osRouterService.routerInterfaces().stream()
-                    .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
-                    .findAny().orElse(null);
-        }
-
-        private ExternalGateway externalGateway(RouterInterface osRouterIface) {
-            checkNotNull(osRouterIface);
-            Router osRouter = osRouterService.router(osRouterIface.getId());
-            if (osRouter == null) {
-                return null;
-            }
-            if (osRouter.getExternalGatewayInfo() == null) {
-                return null;
-            }
-            return osRouter.getExternalGatewayInfo();
-        }
 
         private boolean handleEchoReply(IPv4 ipPacket, ICMP icmp) {
             String icmpInfoKey = icmpInfoKey(icmp,
@@ -432,38 +402,6 @@
             return routableGateways.contains(dstIp);
         }
 
-        private IpAddress getExternalIp(ExternalGateway externalGateway, RouterInterface osRouterIface) {
-            checkNotNull(externalGateway);
-            checkNotNull(osRouterIface);
-
-            Router osRouter = osRouterService.router(osRouterIface.getId());
-            if (osRouter == null) {
-                return null;
-            }
-
-            Port exGatewayPort = osNetworkService.ports(externalGateway.getNetworkId())
-                    .stream()
-                    .filter(port -> Objects.equals(port.getDeviceId(), osRouter.getId()))
-                    .findAny().orElse(null);
-            if (exGatewayPort == null) {
-                final String error = String.format(ERR_REQ +
-                                "no external gateway port for router (ID:%s, name:%s)",
-                        osRouter.getId(), osRouter.getName());
-                throw new IllegalStateException(error);
-            }
-            Optional<NeutronIP> externalIpAddress =
-                    (Optional<NeutronIP>) exGatewayPort.getFixedIps().stream().findFirst();
-            if (!externalIpAddress.isPresent() || externalIpAddress.get().getIpAddress() == null) {
-                final String error = String.format(ERR_REQ +
-                                "no external gateway IP address for router (ID:%s, name:%s)",
-                        osRouter.getId(), osRouter.getName());
-                log.warn(error);
-                return null;
-            }
-
-            return IpAddress.valueOf(externalIpAddress.get().getIpAddress());
-        }
-
         private void processRequestForGateway(IPv4 ipPacket, InstancePort instPort) {
             ICMP icmpReq = (ICMP) ipPacket.getPayload();
             icmpReq.setChecksum((short) 0);
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java
index 4695338..23333c4 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java
@@ -15,11 +15,6 @@
  */
 package org.onosproject.openstacknetworking.impl;
 
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Deactivate;
-import org.osgi.service.component.annotations.Reference;
-import org.osgi.service.component.annotations.ReferenceCardinality;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.IPv4;
 import org.onlab.packet.IpAddress;
@@ -56,18 +51,19 @@
 import org.onosproject.store.service.DistributedSet;
 import org.onosproject.store.service.Serializer;
 import org.onosproject.store.service.StorageService;
-import org.openstack4j.model.network.ExternalGateway;
 import org.openstack4j.model.network.IP;
 import org.openstack4j.model.network.Network;
 import org.openstack4j.model.network.NetworkType;
 import org.openstack4j.model.network.Port;
-import org.openstack4j.model.network.Router;
-import org.openstack4j.model.network.RouterInterface;
 import org.openstack4j.model.network.Subnet;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
 import org.slf4j.Logger;
 
 import java.nio.ByteBuffer;
-import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.stream.Collectors;
@@ -78,6 +74,8 @@
 import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
 import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
 import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_SNAT_RULE;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.externalIpFromSubnet;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.externalPeerRouterFromSubnet;
 import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -187,13 +185,15 @@
 
         IpAddress srcIp = IpAddress.valueOf(iPacket.getSourceAddress());
         Subnet srcSubnet = getSourceSubnet(srcInstPort, srcIp);
-        IpAddress externalGatewayIp = getExternalIp(srcSubnet);
+        IpAddress externalGatewayIp =
+                externalIpFromSubnet(srcSubnet, osRouterService, osNetworkService);
 
         if (externalGatewayIp == null) {
             return;
         }
 
-        ExternalPeerRouter externalPeerRouter = externalPeerRouter(srcSubnet);
+        ExternalPeerRouter externalPeerRouter =
+                externalPeerRouterFromSubnet(srcSubnet, osRouterService, osNetworkService);
         if (externalPeerRouter == null) {
             return;
         }
@@ -210,26 +210,6 @@
                 externalGatewayIp, externalPeerRouter);
     }
 
-    private ExternalPeerRouter externalPeerRouter(Subnet subnet) {
-        RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
-                .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
-                .findAny().orElse(null);
-        if (osRouterIface == null) {
-            return null;
-        }
-
-        Router osRouter = osRouterService.router(osRouterIface.getId());
-        if (osRouter == null) {
-            return null;
-        }
-        if (osRouter.getExternalGatewayInfo() == null) {
-            return null;
-        }
-
-        ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
-        return osNetworkService.externalPeerRouter(exGatewayInfo);
-    }
-
     private Subnet getSourceSubnet(InstancePort instance, IpAddress srcIp) {
         Port osPort = osNetworkService.port(instance.portId());
         IP fixedIp = osPort.getFixedIps().stream()
@@ -241,47 +221,6 @@
         return osNetworkService.subnet(fixedIp.getSubnetId());
     }
 
-    private IpAddress getExternalIp(Subnet srcSubnet) {
-        RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
-                .filter(i -> Objects.equals(i.getSubnetId(), srcSubnet.getId()))
-                .findAny().orElse(null);
-        if (osRouterIface == null) {
-            // this subnet is not connected to the router
-            log.trace(ERR_PACKETIN + "source subnet(ID:{}, CIDR:{}) has no router",
-                    srcSubnet.getId(), srcSubnet.getCidr());
-            return null;
-        }
-
-        Router osRouter = osRouterService.router(osRouterIface.getId());
-        if (osRouter.getExternalGatewayInfo() == null) {
-            // this router does not have external connectivity
-            log.trace(ERR_PACKETIN + "router({}) has no external gateway",
-                    osRouter.getName());
-            return null;
-        }
-
-        ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
-        if (!exGatewayInfo.isEnableSnat()) {
-            // SNAT is disabled in this router
-            log.trace(ERR_PACKETIN + "router({}) SNAT is disabled", osRouter.getName());
-            return null;
-        }
-
-        // TODO fix openstack4j for ExternalGateway provides external fixed IP list
-        Port exGatewayPort = osNetworkService.ports(exGatewayInfo.getNetworkId())
-                .stream()
-                .filter(port -> Objects.equals(port.getDeviceId(), osRouter.getId()))
-                .findAny().orElse(null);
-        if (exGatewayPort == null) {
-            log.trace(ERR_PACKETIN + "no external gateway port for router({})",
-                    osRouter.getName());
-            return null;
-        }
-
-        return IpAddress.valueOf(exGatewayPort.getFixedIps().stream()
-                .findFirst().get().getIpAddress());
-    }
-
     private void populateSnatFlowRules(InboundPacket packetIn, InstancePort srcInstPort,
                                        TpPort patPort, IpAddress externalIp, ExternalPeerRouter externalPeerRouter) {
         Network osNet = osNetworkService.network(srcInstPort.networkId());
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
index 25a7e41..27e122d 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
@@ -60,6 +60,7 @@
 import org.onosproject.openstacknetworking.api.InstancePort;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
 import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterService;
 import org.onosproject.openstacknetworking.impl.DefaultInstancePort;
 import org.onosproject.openstacknode.api.OpenstackAuth;
 import org.onosproject.openstacknode.api.OpenstackAuth.Perspective;
@@ -939,6 +940,98 @@
 
     }
 
+    /**
+     * Returns the external peer router with specified subnet information.
+     *
+     * @param subnet openstack subnet
+     * @param osRouterService openstack router service
+     * @param osNetworkService openstack network service
+     * @return external peer router
+     */
+    public static ExternalPeerRouter externalPeerRouterFromSubnet(Subnet subnet, OpenstackRouterService osRouterService,
+                                                                  OpenstackNetworkService osNetworkService) {
+        Router osRouter = getRouterFromSubnet(subnet, osRouterService);
+        if (osRouter == null) {
+            return null;
+        }
+        if (osRouter.getExternalGatewayInfo() == null) {
+            // this router does not have external connectivity
+            log.trace("router({}) has no external gateway",
+                    osRouter.getName());
+            return null;
+        }
+
+        return osNetworkService.externalPeerRouter(osRouter.getExternalGatewayInfo());
+    }
+
+    /**
+     * Returns the external ip address with specified router information.
+     *
+     * @param srcSubnet source subnet
+     * @param osRouterService openstack router service
+     * @param osNetworkService openstack network service
+     * @return external ip address
+     */
+    public static IpAddress externalIpFromSubnet(Subnet srcSubnet,
+                                                 OpenstackRouterService osRouterService,
+                                                 OpenstackNetworkService osNetworkService) {
+
+        Router osRouter = getRouterFromSubnet(srcSubnet, osRouterService);
+
+        if (osRouter.getExternalGatewayInfo() == null) {
+            // this router does not have external connectivity
+            log.trace("router({}) has no external gateway",
+                    osRouter.getName());
+            return null;
+        }
+
+        return getExternalIp(osRouter, osNetworkService);
+    }
+
+    /**
+     * Returns the external ip address with specified router information.
+     *
+     * @param router openstack router
+     * @param osNetworkService openstack network service
+     * @return external ip address
+     */
+    public static IpAddress getExternalIp(Router router, OpenstackNetworkService osNetworkService) {
+        if (router == null) {
+            return null;
+        }
+
+        ExternalGateway externalGateway = router.getExternalGatewayInfo();
+        if (externalGateway == null || !externalGateway.isEnableSnat()) {
+            log.trace("Failed to get externalIp for router {} because externalGateway is null or SNAT is disabled",
+                    router.getId());
+            return null;
+        }
+
+        // TODO fix openstack4j for ExternalGateway provides external fixed IP list
+        Port exGatewayPort = osNetworkService.ports(externalGateway.getNetworkId())
+                .stream()
+                .filter(port -> Objects.equals(port.getDeviceId(), router.getId()))
+                .findAny().orElse(null);
+
+        if (exGatewayPort == null) {
+            return null;
+        }
+
+        return IpAddress.valueOf(exGatewayPort.getFixedIps().stream()
+                .findAny().get().getIpAddress());
+    }
+
+    private static Router getRouterFromSubnet(Subnet subnet, OpenstackRouterService osRouterService) {
+        RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
+                .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
+                .findAny().orElse(null);
+        if (osRouterIface == null) {
+            return null;
+        }
+
+        return osRouterService.router(osRouterIface.getId());
+    }
+
     private static boolean isDirectPort(String portName) {
         return portNamePrefixMap().values().stream().anyMatch(p -> portName.startsWith(p));
     }