Bug 3378 - ovsdb netvirt needs help in getting mac for a given ip in br-ex 53/24253/1
authorAnil Vishnoi <vishnoianil@gmail.com>
Tue, 7 Jul 2015 23:50:43 +0000 (16:50 -0700)
committerSam Hague <shague@redhat.com>
Fri, 17 Jul 2015 18:53:20 +0000 (18:53 +0000)
This patch, does not do periodic refresh of gateway mac. Will push that in next gerrit.

Change-Id: Ie1a5a81fb7f196874f87bfbe0095ea595756a0a4
Signed-off-by: Anil Vishnoi <vishnoianil@gmail.com>
(cherry picked from commit 65a8de37c522f8f09caf6be73997906b98fd0b89)

18 files changed:
openstack/net-virt-providers/pom.xml
openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/ConfigActivator.java
openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/NetvirtProvidersProvider.java
openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/AbstractServiceInstance.java
openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/Service.java
openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/Arp.java [new file with mode: 0644]
openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/ArpFlowFactory.java [new file with mode: 0644]
openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/ArpMessageAddress.java [new file with mode: 0644]
openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/ArpOperation.java [new file with mode: 0644]
openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/ArpResolverMetadata.java [new file with mode: 0644]
openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/ArpResolverUtils.java [new file with mode: 0644]
openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/ArpSender.java [new file with mode: 0644]
openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/ArpUtils.java [new file with mode: 0644]
openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/GatewayMacResolverService.java [new file with mode: 0644]
openstack/net-virt/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/ConfigActivator.java
openstack/net-virt/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/SouthboundHandler.java
openstack/net-virt/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/api/GatewayMacResolver.java [new file with mode: 0644]
openstack/net-virt/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/impl/NeutronL3Adapter.java

index c8c50bcbbc663943ad0f07bf9df5b3eff360a7cb..4ea6b6606d8b4702a160b96f06036961287efbce 100644 (file)
@@ -52,6 +52,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
     <ovsdb.utils.servicehelper.version>1.2.0-SNAPSHOT</ovsdb.utils.servicehelper.version>
     <powermock.version>1.5.2</powermock.version>
     <sonar-jacoco-listeners.version>2.4</sonar-jacoco-listeners.version>
+    <liblldp.version>0.9.1-SNAPSHOT</liblldp.version>
     <root.directory>${env.PWD}</root.directory>
     <sonar.jacoco.itReportPath>${root.directory}/target/code-coverage/jacoco-it.exec</sonar.jacoco.itReportPath>
   </properties>
@@ -151,6 +152,11 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
     </dependency>
+    <dependency>
+       <groupId>org.opendaylight.controller</groupId>
+       <artifactId>liblldp</artifactId>
+       <version>${liblldp.version}</version>
+     </dependency>
     <dependency>
       <groupId>org.mockito</groupId>
       <artifactId>mockito-core</artifactId>
index 8d56e89d9e98975d1b2eae9c33b7272b5c676ff3..2b98a0f79df0925a64c99a971afb9a2d49d4ee99 100644 (file)
@@ -4,7 +4,9 @@ import java.util.ArrayList;
 import java.util.Dictionary;
 import java.util.Hashtable;
 import java.util.List;
+
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
 import org.opendaylight.ovsdb.openstack.netvirt.api.*;
 import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.AbstractServiceInstance;
 import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.OF13Provider;
@@ -12,6 +14,7 @@ import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.PipelineOrc
 import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.PipelineOrchestratorImpl;
 import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.Service;
 import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services.*;
+import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services.arp.GatewayMacResolverService;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
@@ -91,6 +94,12 @@ public class ConfigActivator implements BundleActivator {
         registerService(context, OutboundNatProvider.class.getName(),
                 outboundNatService, Service.OUTBOUND_NAT);
 
+        GatewayMacResolverService gatewayMacResolverService = new GatewayMacResolverService();
+        registerService(context, GatewayMacResolver.class.getName(),
+                gatewayMacResolverService, Service.GATEWAY_RESOLVER);
+        getNotificationProviderService().registerNotificationListener(gatewayMacResolverService);
+
+
         pipelineOrchestrator.setDependencies(context, null);
         outboundNatService.setDependencies(context, null);
         egressAclService.setDependencies(context, null);
@@ -104,6 +113,7 @@ public class ConfigActivator implements BundleActivator {
         arpResponderService.setDependencies(context, null);
         classifierService.setDependencies(context, null);
         of13Provider.setDependencies(context, null);
+        gatewayMacResolverService.setDependencies(context, null);
 
         @SuppressWarnings("unchecked")
         ServiceTracker NetworkingProviderManagerTracker = new ServiceTracker(context,
@@ -153,4 +163,9 @@ public class ConfigActivator implements BundleActivator {
                 new String[] {AbstractServiceInstance.class.getName(), interfaceClassName},
                 properties, impl);
     }
+
+    private NotificationProviderService getNotificationProviderService(){
+        return this.providerContext.<NotificationProviderService>getSALService(
+                NotificationProviderService.class);
+    }
 }
index b94b268fc47bc95ef2980cae50974da7e036e9a9..0dc866a52ef61173837bca94c1c1fdb5f4bfc1c5 100644 (file)
@@ -15,6 +15,7 @@ public class NetvirtProvidersProvider implements BindingAwareProvider, AutoClose
     private BundleContext bundleContext = null;
     private static DataBroker dataBroker = null;
     private ConfigActivator activator;
+    private static ProviderContext providerContext = null;
 
     public NetvirtProvidersProvider(BundleContext bundleContext) {
         LOG.info("NetvirtProvidersProvider: bundleContext: {}", bundleContext);
@@ -25,16 +26,21 @@ public class NetvirtProvidersProvider implements BindingAwareProvider, AutoClose
         return dataBroker;
     }
 
+    public static ProviderContext getProviderContext() {
+        return providerContext;
+    }
+
     @Override
     public void close() throws Exception {
         activator.stop(bundleContext);
     }
 
     @Override
-    public void onSessionInitiated(ProviderContext providerContext) {
-        dataBroker = providerContext.getSALService(DataBroker.class);
+    public void onSessionInitiated(ProviderContext providerContextRef) {
+        dataBroker = providerContextRef.getSALService(DataBroker.class);
+        providerContext = providerContextRef;
         LOG.info("NetvirtProvidersProvider: onSessionInitiated dataBroker: {}", dataBroker);
-        this.activator = new ConfigActivator(providerContext);
+        this.activator = new ConfigActivator(providerContextRef);
         try {
             activator.start(bundleContext);
         } catch (Exception e) {
index 62c3464867f05766a9813d43869bce967e6479ea..5b9573fd4c094f9cab6262fba7c8e66fc3b251d8 100644 (file)
@@ -41,8 +41,10 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import com.google.common.base.Optional;
 import com.google.common.collect.Lists;
 import com.google.common.util.concurrent.CheckedFuture;
+
 import java.util.List;
 import java.util.concurrent.ExecutionException;
+
 import org.osgi.framework.ServiceReference;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -190,6 +192,24 @@ public abstract class AbstractServiceInstance {
         return null;
     }
 
+    public org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node
+    getOpenFlowNode(String nodeId) {
+
+        ReadOnlyTransaction readTx = dataBroker.newReadOnlyTransaction();
+        try {
+            Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node> data =
+                    readTx.read(LogicalDatastoreType.OPERATIONAL, createNodePath(createNodeBuilder(nodeId))).get();
+            if (data.isPresent()) {
+                return data.get();
+            }
+        } catch (InterruptedException|ExecutionException e) {
+            logger.error(e.getMessage(), e);
+        }
+
+        logger.debug("Cannot find data for Node " + nodeId);
+        return null;
+    }
+
     private Long getDpid(Node node) {
         Long dpid = 0L;
         dpid = southbound.getDataPathId(node);
index 88d465cda8de12d5a6069ab306c4e22e1f46c8e6..4af71d304362d76c7c4fff0e12ce67d3f5583960 100644 (file)
@@ -13,6 +13,7 @@ package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13;
 public enum Service {
 
     CLASSIFIER ((short) 0, "Classifier"),
+    GATEWAY_RESOLVER((short) 0, "External Network Gateway Resolver"),
     DIRECTOR ((short) 10, "Director"),
     ARP_RESPONDER ((short) 20, "Distributed ARP Responder"),
     INBOUND_NAT ((short) 30, "DNAT for inbound floating-ip traffic"),
diff --git a/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/Arp.java b/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/Arp.java
new file mode 100644 (file)
index 0000000..717473e
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services.arp;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import io.netty.buffer.Unpooled;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
+import org.opendaylight.controller.liblldp.EtherTypes;
+import org.opendaylight.controller.liblldp.NetUtils;
+import org.opendaylight.controller.liblldp.Packet;
+import org.opendaylight.controller.liblldp.PacketException;
+
+/**
+ * Represents ARP packet. Contains methods ({@link #setSHAFieldCoordinate(Pair)}
+ * {@link #setSPAFieldCoordinate(Pair)} {@link #setTHAFieldCoordinate(Pair)}
+ * {@link #setTPAFieldCoordinate(Pair)}) for customization of ARP.
+ * Arp by default contain fields for IPv4 as protocol address and MAC as hardware address.
+ */
+public class Arp extends Packet {
+
+    private static final String HTYPE = "htype";
+    private static final String PTYPE = "ptype";
+    private static final String HLEN = "hlen";
+    private static final String PLEN = "plen";
+    private static final String OPERATION = "operation";
+    private static final String SHA = "sha";
+    private static final String SPA = "spa";
+    private static final String THA = "tha";
+    private static final String TPA = "tpa";
+
+    private static final int ARP_FIELDS_COUNT = 9;
+    private static final int ETHERNET_HW_TYPE = 1;
+    private final Map<String, Pair<Integer, Integer>> ARP_FIELD_COORDINATES = new LinkedHashMap<String, Pair<Integer, Integer>>() {
+
+        private static final long serialVersionUID = 1L;
+
+        {
+            put(HTYPE, ImmutablePair.of(0, 16));
+            put(PTYPE, ImmutablePair.of(16, 16));
+            put(HLEN, ImmutablePair.of(32, 8));
+            put(PLEN, ImmutablePair.of(40, 8));
+            put(OPERATION, ImmutablePair.of(48, 16));
+            put(SHA, ImmutablePair.of(64, 48));
+            put(SPA, ImmutablePair.of(112, 32));
+            put(THA, ImmutablePair.of(144, 48));
+            put(TPA, ImmutablePair.of(192, 32));
+        }
+    };
+
+    public Arp() {
+        payload = null;
+        hdrFieldsMap = new HashMap<String, byte[]>(ARP_FIELDS_COUNT);
+        setHardwareLength((short) 6); // MAC address length
+        setProtocolLength((short) 4); // IPv4 address length
+        setHardwareType(ETHERNET_HW_TYPE);
+        setProtocolType(EtherTypes.IPv4.intValue());
+        hdrFieldCoordMap = ARP_FIELD_COORDINATES;
+    }
+
+    public Pair<Integer, Integer> setSHAFieldCoordinate(Pair<Integer, Integer> bitOffsetAndBitLength) {
+        checkNotNullPair(bitOffsetAndBitLength);
+        return ARP_FIELD_COORDINATES.put(SHA, bitOffsetAndBitLength);
+    }
+
+    public Pair<Integer, Integer> setSPAFieldCoordinate(Pair<Integer, Integer> bitOffsetAndBitLength) {
+        checkNotNullPair(bitOffsetAndBitLength);
+        return ARP_FIELD_COORDINATES.put(SPA, bitOffsetAndBitLength);
+    }
+
+    public Pair<Integer, Integer> setTHAFieldCoordinate(Pair<Integer, Integer> bitOffsetAndBitLength) {
+        checkNotNullPair(bitOffsetAndBitLength);
+        return ARP_FIELD_COORDINATES.put(THA, bitOffsetAndBitLength);
+    }
+
+    public Pair<Integer, Integer> setTPAFieldCoordinate(Pair<Integer, Integer> bitOffsetAndBitLength) {
+        checkNotNullPair(bitOffsetAndBitLength);
+        return ARP_FIELD_COORDINATES.put(TPA, bitOffsetAndBitLength);
+    }
+
+    private void checkNotNullPair(Pair<Integer, Integer> pair) {
+        checkNotNull(pair);
+        checkNotNull(pair.getLeft());
+        checkNotNull(pair.getRight());
+    }
+
+    @Override
+    public Packet deserialize(byte[] data, int bitOffset, int size) throws PacketException {
+        return super.deserialize(data, bitOffset, size);
+    }
+
+    @Override
+    public byte[] serialize() throws PacketException {
+        return super.serialize();
+    }
+
+    @Override
+    public int getfieldnumBits(String fieldName) {
+        if (fieldName.equals(SHA) || fieldName.equals(THA)) {
+            return getHardwareLength() * NetUtils.NumBitsInAByte;
+        } else if (fieldName.equals(SPA) || fieldName.equals(TPA)) {
+            return getProtocolLength() * NetUtils.NumBitsInAByte;
+        }
+        return hdrFieldCoordMap.get(fieldName).getRight();
+    }
+
+    public Arp setHardwareType(int value) {
+        hdrFieldsMap.put(HTYPE, Unpooled.copyShort(value).array());
+        return this;
+    }
+
+    public Arp setProtocolType(int value) {
+        hdrFieldsMap.put(PTYPE, Unpooled.copyShort(value).array());
+        return this;
+    }
+
+    /**
+     * @param value hardware length in Bytes
+     */
+    public Arp setHardwareLength(short value) {
+        hdrFieldsMap.put(HLEN, Unpooled.buffer(1).writeByte(value).array());
+        return this;
+    }
+
+    /**
+     * @param value protocol length in Bytes
+     */
+    public Arp setProtocolLength(short value) {
+        hdrFieldsMap.put(PLEN, Unpooled.buffer(1).writeByte(value).array());
+        return this;
+    }
+
+    public Arp setOperation(int value) {
+        hdrFieldsMap.put(OPERATION, Unpooled.copyShort(value).array());
+        return this;
+    }
+
+    public Arp setSenderHardwareAddress(byte[] value) {
+        hdrFieldsMap.put(SHA, value);
+        return this;
+    }
+
+    public Arp setSenderProtocolAddress(byte[] value) {
+        hdrFieldsMap.put(SPA, value);
+        return this;
+    }
+
+    public Arp setTargetHardwareAddress(byte[] value) {
+        hdrFieldsMap.put(THA, value);
+        return this;
+    }
+
+    public Arp setTargetProtocolAddress(byte[] value) {
+        hdrFieldsMap.put(TPA, value);
+        return this;
+    }
+
+    public int getHardwareType() {
+        byte[] htype = hdrFieldsMap.get(HTYPE);
+        return Unpooled.wrappedBuffer(htype).readUnsignedShort();
+    }
+
+    public int getProtocolType() {
+        byte[] ptype = hdrFieldsMap.get(PTYPE);
+        return Unpooled.wrappedBuffer(ptype).readUnsignedShort();
+    }
+
+    public short getHardwareLength() {
+        byte[] hlen = hdrFieldsMap.get(HLEN);
+        return Unpooled.wrappedBuffer(hlen).readUnsignedByte();
+    }
+
+    public short getProtocolLength() {
+        byte[] plen = hdrFieldsMap.get(PLEN);
+        return Unpooled.wrappedBuffer(plen).readUnsignedByte();
+    }
+
+    public int getOperation() {
+        byte[] operation = hdrFieldsMap.get(OPERATION);
+        return Unpooled.wrappedBuffer(operation).readUnsignedShort();
+    }
+
+    public byte[] getSenderHardwareAddress() {
+        return hdrFieldsMap.get(SHA);
+    }
+
+    public byte[] getSenderProtocolAddress() {
+        return hdrFieldsMap.get(SPA);
+    }
+
+    public byte[] getTargetHardwareAddress() {
+        return hdrFieldsMap.get(THA);
+    }
+
+    public byte[] getTargetProtocolAddress() {
+        return hdrFieldsMap.get(TPA);
+    }
+
+}
diff --git a/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/ArpFlowFactory.java b/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/ArpFlowFactory.java
new file mode 100644 (file)
index 0000000..9699a51
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services.arp;
+
+import org.opendaylight.controller.liblldp.EtherTypes;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.output.action._case.OutputActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.OutputPortValues;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.EtherType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.arp.match.fields.ArpTargetHardwareAddressBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetDestinationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetTypeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatchBuilder;
+
+/**
+ * Contains methods creating flow part for ARP flow.
+ */
+public class ArpFlowFactory {
+
+    private static final String HOST_MASK = "/32";
+
+    /**
+     * Creates {@link EthernetMatch} containing ARP ether-type and the given destination MAC address
+     */
+    public static EthernetMatch createEthernetMatch(MacAddress destinationMacAddress) {
+        return new EthernetMatchBuilder().setEthernetType(
+                new EthernetTypeBuilder().setType(new EtherType(Long.valueOf(EtherTypes.ARP.intValue()))).build())
+            .setEthernetDestination(new EthernetDestinationBuilder().setAddress(destinationMacAddress).build())
+            .build();
+    }
+
+    /**
+     * Creates {@link ArpMatch} containing Reply ARP operation, THA and TPA for the given target
+     * address and SPA for the given sender protocol address
+     */
+    public static ArpMatch createArpMatch(ArpMessageAddress targetAddress, Ipv4Address senderProtocolAddress) {
+        return new ArpMatchBuilder().setArpOp(ArpOperation.REPLY.intValue())
+            .setArpTargetHardwareAddress(
+                    new ArpTargetHardwareAddressBuilder().setAddress(targetAddress.getHardwareAddress()).build())
+            .setArpTargetTransportAddress(new Ipv4Prefix(targetAddress.getProtocolAddress().getValue() + HOST_MASK))
+            .setArpSourceTransportAddress(new Ipv4Prefix(senderProtocolAddress.getValue() + HOST_MASK))
+            .build();
+    }
+
+    /**
+     * Creates {@link Action} representing output to the controller
+     *
+     * @param order the order for the action
+     */
+    public static Action createSendToControllerAction(int order) {
+        return new ActionBuilder().setOrder(order)
+            .setKey(new ActionKey(order))
+            .setAction(
+                    new OutputActionCaseBuilder().setOutputAction(
+                            new OutputActionBuilder().setMaxLength(0xffff)
+                                .setOutputNodeConnector(new Uri(OutputPortValues.CONTROLLER.toString()))
+                                .build()).build())
+            .build();
+    }
+
+}
diff --git a/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/ArpMessageAddress.java b/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/ArpMessageAddress.java
new file mode 100644 (file)
index 0000000..e054862
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services.arp;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.annotation.concurrent.Immutable;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+
+/**
+ * Represents ARP fields where protocol address is IPv4 address and hardware address is MAC address.
+ */
+@Immutable
+public class ArpMessageAddress {
+
+    private final MacAddress hwAddress;
+    private final Ipv4Address protocolAddress;
+
+    public ArpMessageAddress(MacAddress hwAddress, Ipv4Address protocolAddress) {
+        this.hwAddress = checkNotNull(hwAddress);
+        this.protocolAddress = checkNotNull(protocolAddress);
+    }
+
+    public MacAddress getHardwareAddress() {
+        return hwAddress;
+    }
+
+    public Ipv4Address getProtocolAddress() {
+        return protocolAddress;
+    }
+
+}
diff --git a/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/ArpOperation.java b/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/ArpOperation.java
new file mode 100644 (file)
index 0000000..b65e5a2
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services.arp;
+
+import javax.annotation.Nullable;
+
+public enum ArpOperation {
+
+    REQUEST(1), REPLY(2);
+
+    private final int intOperation;
+
+    private ArpOperation(int operationNumber) {
+        this.intOperation = operationNumber;
+    }
+
+    public int intValue() {
+        return intOperation;
+    }
+
+    public static @Nullable ArpOperation loadFromInt(int intOperation) {
+        for (ArpOperation operation : ArpOperation.values()) {
+            if (operation.intOperation == intOperation) {
+                return operation;
+            }
+        }
+        return null;
+    }
+}
diff --git a/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/ArpResolverMetadata.java b/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/ArpResolverMetadata.java
new file mode 100644 (file)
index 0000000..8947cdf
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2015 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services.arp;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInput;
+
+/**
+*
+* @author Anil Vishnoi (avishnoi@Brocade.com)
+*
+*/
+
+public final class ArpResolverMetadata {
+
+    private RemoveFlowInput flowToRemove;
+    private Ipv4Address gatewayIpAddress;
+    private MacAddress gatewayMacAddress;
+    private boolean periodicRefresh;
+
+    public ArpResolverMetadata(){
+
+    }
+    public ArpResolverMetadata(RemoveFlowInput flowToRemove, Ipv4Address gatewayIpAddress, boolean periodicRefresh){
+        this.flowToRemove = flowToRemove;
+        this.gatewayIpAddress = gatewayIpAddress;
+        this.periodicRefresh = periodicRefresh;
+    }
+
+    public ArpResolverMetadata(RemoveFlowInput flowToRemove, Ipv4Address gatewayIpAddress, MacAddress gatewayMacAddress, boolean periodicRefresh){
+        this.flowToRemove = flowToRemove;
+        this.gatewayIpAddress = gatewayIpAddress;
+        this.gatewayMacAddress = gatewayMacAddress;
+        this.periodicRefresh = periodicRefresh;
+    }
+
+    public RemoveFlowInput getFlowToRemove() {
+        return flowToRemove;
+    }
+    public boolean isPeriodicRefresh() {
+        return periodicRefresh;
+    }
+    public void setPeriodicRefresh(boolean periodicRefresh) {
+        this.periodicRefresh = periodicRefresh;
+    }
+    public void setFlowToRemove(RemoveFlowInput flowToRemove) {
+        this.flowToRemove = flowToRemove;
+    }
+    public Ipv4Address getGatewayIpAddress() {
+        return gatewayIpAddress;
+    }
+    public void setGatewayIpAddress(Ipv4Address gatewayIpAddress) {
+        this.gatewayIpAddress = gatewayIpAddress;
+    }
+    public MacAddress getGatewayMacAddress() {
+        return gatewayMacAddress;
+    }
+    public void setGatewayMacAddress(MacAddress gatewayMacAddress) {
+        this.gatewayMacAddress = gatewayMacAddress;
+    }
+
+}
diff --git a/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/ArpResolverUtils.java b/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/ArpResolverUtils.java
new file mode 100644 (file)
index 0000000..3f404f6
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services.arp;
+
+import org.opendaylight.controller.liblldp.EtherTypes;
+import org.opendaylight.controller.liblldp.Ethernet;
+import org.opendaylight.controller.liblldp.NetUtils;
+import org.opendaylight.controller.liblldp.PacketException;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
+
+public class ArpResolverUtils {
+
+    static {
+        Ethernet.etherTypeClassMap.put(EtherTypes.ARP.shortValue(), Arp.class);
+    }
+
+    /**
+     * Tries to deserialize received packet as ARP packet with IPv4 protocol address and MAC
+     * hardware address.
+     *
+     * @param potentialArp the packet for deserialization
+     * @return ARP packet if received packet is ARP and deserialization was successful
+     * @throws PacketException if packet is not ARP or deserialization was not successful
+     */
+    public static Arp getArpFrom(PacketReceived potentialArp) throws PacketException {
+        byte[] payload = potentialArp.getPayload();
+        Ethernet ethPkt = new Ethernet();
+        ethPkt.deserialize(payload, 0, payload.length * NetUtils.NumBitsInAByte);
+        if (ethPkt.getPayload() instanceof Arp) {
+            return (Arp) ethPkt.getPayload();
+        }
+        throw new PacketException("Packet is not ARP: " + potentialArp);
+    }
+}
diff --git a/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/ArpSender.java b/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/ArpSender.java
new file mode 100644 (file)
index 0000000..8f72018
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services.arp;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.liblldp.EtherTypes;
+import org.opendaylight.controller.liblldp.Ethernet;
+import org.opendaylight.controller.liblldp.NetUtils;
+import org.opendaylight.controller.liblldp.PacketException;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.JdkFutureAdapters;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Uses packet-out for sending ARP Requests.
+ */
+public class ArpSender {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ArpSender.class);
+
+    private static final String OFPP_ALL = "0xfffffffc";
+    private final PacketProcessingService packetProcessingService;
+
+    public ArpSender(PacketProcessingService packetProcessingService) {
+        this.packetProcessingService = checkNotNull(packetProcessingService);
+    }
+
+    /**
+     * Sends ARP Request as packet-out from all openflow physical ports on the given node.
+     *
+     * @param senderAddress the addresses used in sender part of ARP packet
+     * @param tpa the target protocol address, in this case IPv4 address for which MAC should be
+     *        discovered
+     * @param nodeIid the path to node from where the ARP packet will be flooded
+     * @return future result about success of packet-out
+     */
+    public ListenableFuture<RpcResult<Void>> floodArp(ArpMessageAddress senderAddress, Ipv4Address tpa,
+            InstanceIdentifier<Node> nodeIid) {
+        checkNotNull(senderAddress);
+        checkNotNull(tpa);
+        checkNotNull(nodeIid);
+        // node connector representing all physical ports on node
+        NodeConnectorKey nodeConnectorKey = new NodeConnectorKey(createNodeConnectorId(OFPP_ALL,
+                nodeIid.firstKeyOf(Node.class, NodeKey.class).getId()));
+        InstanceIdentifier<NodeConnector> egressNc = nodeIid.child(NodeConnector.class, nodeConnectorKey);
+        return sendArp(senderAddress, tpa, egressNc);
+    }
+
+    private NodeConnectorId createNodeConnectorId(String connectorId, NodeId nodeId) {
+        StringBuilder stringId = new StringBuilder(nodeId.getValue()).append(":").append(connectorId);
+        return new NodeConnectorId(stringId.toString());
+    }
+
+    /**
+     * Sends ARP Request as packet-out from the given port (node connector).
+     *
+     * @param senderAddress the addresses used in sender part of ARP packet
+     * @param tpa the target protocol address, in this case IPv4 address for which MAC should be
+     *        discovered
+     * @param egressNc the path to node connector from where the ARP packet will be sent
+     * @return future result about success of packet-out
+     */
+    public ListenableFuture<RpcResult<Void>> sendArp(ArpMessageAddress senderAddress, Ipv4Address tpa,
+            InstanceIdentifier<NodeConnector> egressNc) {
+        checkNotNull(senderAddress);
+        checkNotNull(tpa);
+        checkNotNull(egressNc);
+        final Ethernet arpFrame = createArpFrame(senderAddress, tpa);
+        byte[] arpFrameAsBytes;
+        try {
+            arpFrameAsBytes = arpFrame.serialize();
+        } catch (PacketException e) {
+            LOG.warn("Serializition of ARP packet is not successful.", e);
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("ARP packet: {}", ArpUtils.getArpFrameToStringFormat(arpFrame));
+            }
+            return Futures.immediateFailedFuture(e);
+        }
+        // Generate packet with destination switch and port
+        TransmitPacketInput packet = new TransmitPacketInputBuilder().setEgress(new NodeConnectorRef(egressNc))
+            .setNode(new NodeRef(egressNc.firstIdentifierOf(Node.class)))
+            .setPayload(arpFrameAsBytes)
+            .build();
+        if (LOG.isTraceEnabled()) {
+            LOG.trace("Sending ARP REQUEST \n{}", ArpUtils.getArpFrameToStringFormat(arpFrame));
+        }
+        Future<RpcResult<Void>> futureTransmitPacketResult = packetProcessingService.transmitPacket(packet);
+        return JdkFutureAdapters.listenInPoolThread(futureTransmitPacketResult);
+    }
+
+    private Ethernet createArpFrame(ArpMessageAddress senderAddress, Ipv4Address tpa) {
+        byte[] senderMac = ArpUtils.macToBytes(senderAddress.getHardwareAddress());
+        byte[] senderIp = ArpUtils.ipToBytes(senderAddress.getProtocolAddress());
+        byte[] targetMac = NetUtils.getBroadcastMACAddr();
+        byte[] targetIp = ArpUtils.ipToBytes(tpa);
+        Ethernet arpFrame = new Ethernet().setSourceMACAddress(senderMac)
+            .setDestinationMACAddress(targetMac)
+            .setEtherType(EtherTypes.ARP.shortValue());
+        Arp arp = new Arp().setOperation(ArpOperation.REQUEST.intValue())
+            .setSenderHardwareAddress(senderMac)
+            .setSenderProtocolAddress(senderIp)
+            .setTargetHardwareAddress(targetMac)
+            .setTargetProtocolAddress(targetIp);
+        arpFrame.setPayload(arp);
+        return arpFrame;
+    }
+
+}
diff --git a/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/ArpUtils.java b/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/ArpUtils.java
new file mode 100644 (file)
index 0000000..6e2aace
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services.arp;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import javax.annotation.Nullable;
+
+import org.opendaylight.controller.liblldp.EtherTypes;
+import org.opendaylight.controller.liblldp.Ethernet;
+import org.opendaylight.controller.liblldp.HexEncode;
+import org.opendaylight.controller.liblldp.Packet;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+
+import com.google.common.net.InetAddresses;
+
+public class ArpUtils {
+
+    private ArpUtils() {
+        throw new UnsupportedOperationException("Cannot create an instance.");
+    }
+
+    /**
+     * Returns Ethernet and ARP in readable string format
+     */
+    public static String getArpFrameToStringFormat(Ethernet eth) {
+        String ethernetString = "Ethernet [getEtherType()="
+                + EtherTypes.loadFromString(String.valueOf(eth.getEtherType())) + ", getSourceMACAddress()="
+                + HexEncode.bytesToHexStringFormat(eth.getSourceMACAddress()) + ", getDestinationMACAddress()="
+                + HexEncode.bytesToHexStringFormat(eth.getDestinationMACAddress()) + "]\n";
+        Packet potentialArp = eth.getPayload();
+        String arpString = null;
+        if (potentialArp instanceof Arp) {
+            Arp arp = (Arp) potentialArp;
+            arpString = ArpUtils.getArpToStringFormat(arp);
+        } else {
+            arpString = "ARP was not found in Ethernet frame.";
+        }
+        return ethernetString.concat(arpString);
+    }
+
+    /**
+     * Returns ARP in readable string format
+     */
+    public static String getArpToStringFormat(Arp arp) {
+        try {
+            return "Arp [getHardwareType()=" + arp.getHardwareType() + ", getProtocolType()=" + arp.getProtocolType()
+                    + ", getHardwareLength()=" + arp.getHardwareLength() + ", getProtocolLength()="
+                    + arp.getProtocolLength() + ", getOperation()=" + ArpOperation.loadFromInt(arp.getOperation())
+                    + ", getSenderHardwareAddress()="
+                    + HexEncode.bytesToHexStringFormat(arp.getSenderHardwareAddress())
+                    + ", getSenderProtocolAddress()="
+                    + InetAddress.getByAddress(arp.getSenderProtocolAddress()).getHostAddress()
+                    + ", getTargetHardwareAddress()="
+                    + HexEncode.bytesToHexStringFormat(arp.getTargetHardwareAddress())
+                    + ", getTargetProtocolAddress()="
+                    + InetAddress.getByAddress(arp.getTargetProtocolAddress()).getHostAddress() + "]\n";
+        } catch (UnknownHostException e1) {
+            return "Error during parsing Arp " + arp;
+        }
+    }
+
+    public static byte[] macToBytes(MacAddress mac) {
+        return HexEncode.bytesFromHexString(mac.getValue());
+    }
+
+    public static @Nullable MacAddress bytesToMac(byte[] macBytes) {
+        String mac = HexEncode.bytesToHexStringFormat(macBytes);
+        if (!"null".equals(mac)) {
+            return new MacAddress(mac);
+        }
+        return null;
+    }
+
+    public static byte[] ipToBytes(Ipv4Address ip) {
+        return InetAddresses.forString(ip.getValue()).getAddress();
+    }
+
+    public static @Nullable Ipv4Address bytesToIp(byte[] ipv4AsBytes) {
+        try {
+            return new Ipv4Address(InetAddress.getByAddress(ipv4AsBytes).getHostAddress());
+        } catch (UnknownHostException e) {
+            return null;
+        }
+    }
+
+}
diff --git a/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/GatewayMacResolverService.java b/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/arp/GatewayMacResolverService.java
new file mode 100644 (file)
index 0000000..095b287
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 2015 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services.arp;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.math.BigInteger;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.annotation.Nullable;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
+import org.opendaylight.openflowplugin.api.OFConstants;
+import org.opendaylight.ovsdb.openstack.netvirt.api.GatewayMacResolver;
+import org.opendaylight.ovsdb.openstack.netvirt.providers.ConfigInterface;
+import org.opendaylight.ovsdb.openstack.netvirt.providers.NetvirtProvidersProvider;
+import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.AbstractServiceInstance;
+import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.Service;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActions;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.JdkFutureAdapters;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+/**
+ *
+ * @author Anil Vishnoi (avishnoi@Brocade.com)
+ *
+ */
+public class GatewayMacResolverService extends AbstractServiceInstance
+                                        implements ConfigInterface, GatewayMacResolver,PacketProcessingListener {
+
+    private static final Logger LOG = LoggerFactory.getLogger(GatewayMacResolverService.class);
+    private static final short TABEL_FOR_ARP_FLOW = 0;
+    private static final String ARP_REPLY_TO_CONTROLLER_FLOW_NAME = "GatewayArpReplyRouter";
+    private static final int ARP_REPLY_TO_CONTROLLER_FLOW_PRIORITY = 10000;
+    private static final Instruction SEND_TO_CONTROLLER_INSTRUCTION;
+    private ArpSender arpSender;
+    private SalFlowService flowService;
+    private final AtomicLong flowCookie = new AtomicLong();
+    private final ConcurrentHashMap<Ipv4Address, ArpResolverMetadata> arpRemoveFlowInputAndL3EpKeyById =
+            new ConcurrentHashMap<Ipv4Address, ArpResolverMetadata>();
+    private final int ARP_WATCH_BROTHERS = 10;
+    private final int WAIT_CYCLES = 3;
+    private final int PER_CYCLE_WAIT_DURATION = 1000;
+    private final ListeningExecutorService arpWatcherWall = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(ARP_WATCH_BROTHERS));
+    private AtomicBoolean initializationDone = new AtomicBoolean(false);
+
+    static {
+        ApplyActions applyActions = new ApplyActionsBuilder().setAction(
+                ImmutableList.of(ArpFlowFactory.createSendToControllerAction(0))).build();
+        SEND_TO_CONTROLLER_INSTRUCTION = new InstructionBuilder().setOrder(0)
+            .setInstruction(new ApplyActionsCaseBuilder().setApplyActions(applyActions).build())
+            .build();
+    }
+
+    public GatewayMacResolverService(){
+        super(Service.GATEWAY_RESOLVER);
+    }
+
+    public GatewayMacResolverService(Service service){
+        super(service);
+    }
+
+    private void init(){
+        if(!initializationDone.get()){
+            initializationDone.set(true);
+            ProviderContext providerContext = NetvirtProvidersProvider.getProviderContext();
+            checkNotNull(providerContext);
+            PacketProcessingService packetProcessingService = providerContext.getRpcService(PacketProcessingService.class);
+            if (packetProcessingService != null) {
+                LOG.debug("{} was found.", PacketProcessingService.class.getSimpleName());
+                this.arpSender = new ArpSender(packetProcessingService);
+            } else {
+                LOG.error("Missing service {}", PacketProcessingService.class.getSimpleName());
+                this.arpSender = null;
+            }
+            flowService = providerContext.getRpcService(SalFlowService.class);
+        }
+    }
+    /**
+     * Method do following actions:
+     * 1. Install flow to direct ARP response packet to controller
+     * 2. Send ARP request packet out on all port of the given External network bridge.
+     * 3. Cache the flow that need to be removed once ARP resolution is done.
+     * 4. Return listenable future so that user can add callback to get the MacAddress
+     * @param externalNetworkBridgeDpid Broadcast ARP request packet on this bridge
+     * @param gatewayIp IP address for which MAC need to be resolved
+     * @param sourceIpAddress Source Ip address for the ARP request packet
+     * @param sourceMacAddress Source Mac address for the ARP request packet
+     * @param periodicRefresh Enable/Disable periodic refresh of the Gateway Mac address
+     * NOTE:Periodic refresh is not supported yet.
+     * @param gatewayIp  Resolve MAC address of this Gateway Ip
+     * @return Future<MacAddress> Future object
+     */
+    @Override
+    public ListenableFuture<MacAddress> resolveMacAddress( final Long externalNetworkBridgeDpid, final Ipv4Address gatewayIp,
+            final Ipv4Address sourceIpAddress, final MacAddress sourceMacAddress, final Boolean periodicRefresh){
+        Preconditions.checkNotNull(sourceIpAddress);
+        Preconditions.checkNotNull(sourceMacAddress);
+        Preconditions.checkNotNull(gatewayIp);
+
+        LOG.info("Trigger Mac resolution for gateway {}, using source ip {} and mac {}",
+                gatewayIp.getValue(),sourceIpAddress.getValue(),sourceMacAddress.getValue());
+
+        init();
+        if(arpRemoveFlowInputAndL3EpKeyById.containsKey(gatewayIp)){
+            if(arpRemoveFlowInputAndL3EpKeyById.get(gatewayIp).getGatewayMacAddress() != null){
+                return arpWatcherWall.submit(new Callable<MacAddress>(){
+
+                    @Override
+                    public MacAddress call() throws Exception {
+                        return arpRemoveFlowInputAndL3EpKeyById.get(gatewayIp).getGatewayMacAddress();
+                    }
+                });
+            }
+        }else{
+            arpRemoveFlowInputAndL3EpKeyById.put(gatewayIp,
+                    new ArpResolverMetadata(null, gatewayIp,periodicRefresh));
+        }
+
+        final ArpMessageAddress senderAddress = new ArpMessageAddress(sourceMacAddress,sourceIpAddress);
+
+        final String nodeName = OPENFLOW + externalNetworkBridgeDpid;
+
+        final Node externalNetworkBridge = getOpenFlowNode(nodeName);
+        if(externalNetworkBridge == null){
+            LOG.error("MAC address for gateway {} can not be resolved, because external bridge {} "
+                    + "is not connected to controller.",gatewayIp.getValue(),externalNetworkBridgeDpid );
+            return null;
+        }
+        //Build arp reply router flow
+        final Flow arpReplyToControllerFlow = createArpReplyToControllerFlow(senderAddress, gatewayIp);
+
+
+        final InstanceIdentifier<Node> nodeIid = InstanceIdentifier.builder(Nodes.class)
+                .child(Node.class, externalNetworkBridge.getKey())
+                .build();
+        final InstanceIdentifier<Flow> flowIid = createFlowIid(arpReplyToControllerFlow, nodeIid);
+        final NodeRef nodeRef = new NodeRef(nodeIid);
+
+        //Install flow
+        Future<RpcResult<AddFlowOutput>> addFlowResult = flowService.addFlow(new AddFlowInputBuilder(
+                arpReplyToControllerFlow).setFlowRef(new FlowRef(flowIid)).setNode(nodeRef).build());
+        //wait for flow installation
+        Futures.addCallback(JdkFutureAdapters.listenInPoolThread(addFlowResult),
+                new FutureCallback<RpcResult<AddFlowOutput>>() {
+
+            @Override
+            public void onSuccess(RpcResult<AddFlowOutput> result) {
+                if (!result.isSuccessful()) {
+                    LOG.warn("Flow to route ARP Reply to Controller is not installed successfully : {} \nErrors: {}", flowIid,result.getErrors());
+                    return;
+                }
+                LOG.debug("Flow to route ARP Reply to Controller installed successfully : {}", flowIid);
+
+                //cache metadata
+                arpRemoveFlowInputAndL3EpKeyById.get(gatewayIp).setFlowToRemove(
+                        new RemoveFlowInputBuilder(arpReplyToControllerFlow).setNode(nodeRef).build());
+
+                //Broadcast ARP request packets
+                for (NodeConnector egressNc : externalNetworkBridge.getNodeConnector()) {
+                    KeyedInstanceIdentifier<NodeConnector, NodeConnectorKey> egressNcIid = nodeIid.child(
+                            NodeConnector.class, new NodeConnectorKey(egressNc.getId()));
+                    ListenableFuture<RpcResult<Void>> futureSendArpResult = arpSender.sendArp(
+                            senderAddress, gatewayIp, egressNcIid);
+                    Futures.addCallback(futureSendArpResult, logResult(gatewayIp, egressNcIid));
+                }
+            }
+
+            @Override
+            public void onFailure(Throwable t) {
+                LOG.warn("ARP Reply to Controller flow was not created: {}", flowIid, t);
+            }
+            }
+        );
+        //Wait for MacAddress population in cache
+        return waitForMacAddress(gatewayIp);
+    }
+
+    private ListenableFuture<MacAddress> waitForMacAddress(final Ipv4Address gatewayIp){
+
+        return arpWatcherWall.submit(new Callable<MacAddress>(){
+
+            @Override
+            public MacAddress call() throws Exception {
+                for(int cycle = 0;cycle < WAIT_CYCLES;cycle++){
+                    //Sleep before checking mac address, so meanwhile ARP request packets
+                    // will be broadcasted on the bridge.
+                    Thread.sleep(PER_CYCLE_WAIT_DURATION);
+                    ArpResolverMetadata arpResolverMetadata = arpRemoveFlowInputAndL3EpKeyById.get(gatewayIp);
+                    if(arpResolverMetadata != null && arpResolverMetadata.getGatewayMacAddress() != null){
+                        if(!arpResolverMetadata.isPeriodicRefresh()){
+                            return arpRemoveFlowInputAndL3EpKeyById.remove(gatewayIp).getGatewayMacAddress();
+                        }
+                        return arpResolverMetadata.getGatewayMacAddress();
+                    }
+                }
+                return null;
+            }
+        });
+    }
+    private static @Nullable Ipv4Address getIPv4Addresses(IpAddress ipAddress) {
+        if (ipAddress.getIpv4Address() == null) {
+            return null;
+        }
+        return ipAddress.getIpv4Address();
+    }
+
+    private Flow createArpReplyToControllerFlow(final ArpMessageAddress senderAddress, final Ipv4Address ipForRequestedMac) {
+        checkNotNull(senderAddress);
+        checkNotNull(ipForRequestedMac);
+        FlowBuilder arpFlow = new FlowBuilder().setTableId(TABEL_FOR_ARP_FLOW)
+            .setFlowName(ARP_REPLY_TO_CONTROLLER_FLOW_NAME)
+            .setPriority(ARP_REPLY_TO_CONTROLLER_FLOW_PRIORITY)
+            .setBufferId(OFConstants.OFP_NO_BUFFER)
+            .setIdleTimeout(0)
+            .setHardTimeout(0)
+            .setCookie(new FlowCookie(BigInteger.valueOf(flowCookie.incrementAndGet())))
+            .setFlags(new FlowModFlags(false, false, false, false, false));
+
+        EthernetMatch ethernetMatch = ArpFlowFactory.createEthernetMatch(senderAddress.getHardwareAddress());
+        ArpMatch arpMatch = ArpFlowFactory.createArpMatch(senderAddress, ipForRequestedMac);
+        Match match = new MatchBuilder().setEthernetMatch(ethernetMatch).setLayer3Match(arpMatch).build();
+        arpFlow.setMatch(match);
+        arpFlow.setInstructions(new InstructionsBuilder().setInstruction(
+                ImmutableList.of(SEND_TO_CONTROLLER_INSTRUCTION)).build());
+        arpFlow.setId(createFlowId(senderAddress, ipForRequestedMac));
+        return arpFlow.build();
+    }
+
+    private FlowId createFlowId(ArpMessageAddress senderAddress, Ipv4Address ipForRequestedMac) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(ARP_REPLY_TO_CONTROLLER_FLOW_NAME);
+        sb.append("|").append(ipForRequestedMac.getValue());
+        return new FlowId(sb.toString());
+    }
+
+    private static InstanceIdentifier<Flow> createFlowIid(Flow flow, InstanceIdentifier<Node> nodeIid) {
+        return nodeIid.builder()
+            .augmentation(FlowCapableNode.class)
+            .child(Table.class, new TableKey(flow.getTableId()))
+            .child(Flow.class, new FlowKey(flow.getId()))
+            .build();
+    }
+
+    private FutureCallback<RpcResult<Void>> logResult(final Ipv4Address tpa,
+            final KeyedInstanceIdentifier<NodeConnector, NodeConnectorKey> egressNcIid) {
+        return new FutureCallback<RpcResult<Void>>() {
+
+            @Override
+            public void onSuccess(RpcResult<Void> result) {
+                LOG.debug("ARP Request for IP {} was sent from {}.", tpa.getValue(), egressNcIid);
+            }
+
+            @Override
+            public void onFailure(Throwable t) {
+                LOG.warn("ARP Request for IP {} was NOT sent from {}.", tpa.getValue(), egressNcIid);
+            }
+        };
+    }
+
+    @Override
+    public void onPacketReceived(PacketReceived potentialArp) {
+        Arp arp = null;
+        try {
+            arp = ArpResolverUtils.getArpFrom(potentialArp);
+        } catch (Exception e) {
+            LOG.trace(
+                    "Failed to decode potential ARP packet. This could occur when other than ARP packet was received.",
+                    e);
+            return;
+        }
+        if (arp.getOperation() != ArpOperation.REPLY.intValue()) {
+            LOG.trace("Packet is not ARP REPLY packet.");
+            return;
+        }
+        if (LOG.isTraceEnabled()) {
+            LOG.trace("ARP REPLY received - {}", ArpUtils.getArpToStringFormat(arp));
+        }
+        NodeKey nodeKey = potentialArp.getIngress().getValue().firstKeyOf(Node.class, NodeKey.class);
+        if (nodeKey == null) {
+            LOG.info("Unknown source node of ARP packet: {}", potentialArp);
+            return;
+        }
+        Ipv4Address gatewayIpAddress = ArpUtils.bytesToIp(arp.getSenderProtocolAddress());
+        MacAddress gatewayMacAddress = ArpUtils.bytesToMac(arp.getSenderHardwareAddress());
+        ArpResolverMetadata removeFlowInputAndL3EpKey = arpRemoveFlowInputAndL3EpKeyById.get(gatewayIpAddress);
+        if(removeFlowInputAndL3EpKey != null){
+            removeFlowInputAndL3EpKey.setGatewayMacAddress(gatewayMacAddress);
+            flowService.removeFlow(removeFlowInputAndL3EpKey.getFlowToRemove());
+        }
+    }
+
+    @Override
+    public void setDependencies(BundleContext bundleContext,
+            ServiceReference serviceReference) {
+        super.setDependencies(bundleContext.getServiceReference(GatewayMacResolver.class.getName()), this);
+
+    }
+
+    @Override
+    public void setDependencies(Object impl) {}
+
+    @Override
+    public void stopPeriodicReferesh(Ipv4Address gatewayIp) {
+        init();
+        arpRemoveFlowInputAndL3EpKeyById.remove(gatewayIp);
+    }
+
+}
index bf3d965a72482f0d3b0612d45f3d14f6ccd0daa2..53dbd7c9e688cdc8c241c48bfdecaff172e6ac8a 100644 (file)
@@ -32,6 +32,7 @@ public class ConfigActivator implements BundleActivator {
     private ServiceTracker outboundNatProviderTracker;
     private ServiceTracker routingProviderTracker;
     private ServiceTracker l3ForwardingProviderTracker;
+    private ServiceTracker gatewayMacResolverProviderTracker;
 
     public ConfigActivator(ProviderContext providerContext) {
         this.providerContext = providerContext;
@@ -403,8 +404,26 @@ public class ConfigActivator implements BundleActivator {
         };
         l3ForwardingProviderTracker.open();
         this.l3ForwardingProviderTracker = l3ForwardingProviderTracker;
+
+        @SuppressWarnings("unchecked")
+        ServiceTracker gatewayMacResolverProviderTracker = new ServiceTracker(context,
+                GatewayMacResolver.class, null) {
+            @Override
+            public Object addingService(ServiceReference reference) {
+                LOG.info("addingService GatwayMacResolverProvider");
+                GatewayMacResolver service =
+                        (GatewayMacResolver) context.getService(reference);
+                if (service != null) {
+                    neutronL3Adapter.setDependencies(service);
+                }
+                return service;
+            }
+        };
+        gatewayMacResolverProviderTracker.open();
+        this.gatewayMacResolverProviderTracker = gatewayMacResolverProviderTracker;
     }
 
+
     @Override
     public void stop(BundleContext context) throws Exception {
         LOG.info("ConfigActivator stop");
index 3d89b43dc468720399509f45103ee61d56e4181c..61f6d7cf75a9116a0f9b8fea4afcdae42b4730de 100644 (file)
@@ -295,8 +295,10 @@ public class SouthboundHandler extends AbstractHandler
     private void processPortUpdate(Node node, OvsdbTerminationPointAugmentation port) {
         LOGGER.debug("processPortUpdate <{}> <{}>", node, port);
         NeutronNetwork network = tenantNetworkManager.getTenantNetwork(port);
-        if (network != null && !network.getRouterExternal()) {
-            this.handleInterfaceUpdate(node, port);
+        if (network != null ){
+            if(!network.getRouterExternal()){
+                this.handleInterfaceUpdate(node, port);
+            }
         }
     }
 
diff --git a/openstack/net-virt/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/api/GatewayMacResolver.java b/openstack/net-virt/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/api/GatewayMacResolver.java
new file mode 100644 (file)
index 0000000..28e6fc1
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2015 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.ovsdb.openstack.netvirt.api;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+*
+* @author Anil Vishnoi (avishnoi@Brocade.com)
+*
+*/
+
+public interface GatewayMacResolver {
+
+    /**
+     * Method will trigger the mac resolution for gateway IP. If user set periodicRefresh to true,
+     * it will periodically trigger the gateway resolution after a specific time interval. If
+     * periodicRefresh is false, it will just do one time gateway resolution.
+     * @param externalNetworkBridgeDpid
+     * @param gatewayIp
+     * @param periodicReferesh
+     * @return
+     */
+    public ListenableFuture<MacAddress> resolveMacAddress( final Long externalNetworkBridgeDpid, final Ipv4Address gatewayIp,
+            final Ipv4Address sourceIpAddress, final MacAddress sourceMacAddress, final Boolean periodicRefresh);
+
+    /**
+     * Method will stop the periodic refresh of the given gateway ip address.
+     * @param gatewayIp
+     */
+    public void stopPeriodicReferesh(final Ipv4Address gatewayIp);
+}
index d0fb583268d6346a360e173bab260be1d88cfd82..1c58e6ca6062aeb7bbd1bedc9f44306926e76e82 100644 (file)
@@ -24,11 +24,17 @@ import org.opendaylight.neutron.spi.Neutron_IPs;
 import org.opendaylight.ovsdb.openstack.netvirt.ConfigInterface;
 import org.opendaylight.ovsdb.openstack.netvirt.api.*;
 import org.opendaylight.ovsdb.utils.servicehelper.ServiceHelper;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
 
 import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
 import org.slf4j.Logger;
@@ -42,6 +48,8 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 
 /**
  * Neutron L3 Adapter implements a hub-like adapter for the various Neutron events. Based on
@@ -63,6 +71,7 @@ public class NeutronL3Adapter implements ConfigInterface {
     private volatile OutboundNatProvider outboundNatProvider;
     private volatile ArpProvider arpProvider;
     private volatile RoutingProvider routingProvider;
+    private volatile GatewayMacResolver gatewayMacResolver;
 
     private class FloatIpData {
         private final Long dpid;          // br-int of node where floating ip is associated with tenant port
@@ -100,6 +109,7 @@ public class NeutronL3Adapter implements ConfigInterface {
     private Boolean enabled = false;
     private Boolean flgDistributedARPEnabled = true;
     private Southbound southbound;
+    private final ExecutorService gatewayMacResolverPool = Executors.newFixedThreadPool(5);
 
     private static final String OWNER_ROUTER_INTERFACE = "network:router_interface";
     private static final String OWNER_ROUTER_INTERFACE_DISTRIBUTED = "network:router_interface_distributed";
@@ -193,11 +203,36 @@ public class NeutronL3Adapter implements ConfigInterface {
 
         final boolean isDelete = action == Action.DELETE;
 
+        if (neutronPort.getDeviceOwner().equalsIgnoreCase(OWNER_ROUTER_GATEWAY)){
+            if(!isDelete){
+                Node externalBridgeNode = getExternalBridgeNode();
+                if(externalBridgeNode != null){
+                    LOGGER.info("Port {} is network router gateway interface, "
+                            + "triggering gateway resolution for the attached external network on node {}",neutronPort,externalBridgeNode);
+                    this.triggerGatewayMacResolver(externalBridgeNode, neutronPort);
+                }else{
+                    LOGGER.error("Did not find Node that has external bridge (br-ex), Gateway resolution failed");
+                }
+            }else{
+                NeutronNetwork externalNetwork = neutronNetworkCache.getNetwork(neutronPort.getNetworkUUID());
+
+                if(externalNetwork != null){
+                    if(externalNetwork.isRouterExternal()){
+                        final NeutronSubnet externalSubnet = getExternalNetworkSubnet(neutronPort);
+                        if(externalSubnet != null){
+                            gatewayMacResolver.stopPeriodicReferesh(new Ipv4Address(externalSubnet.getGatewayIP()));
+                        }
+                    }
+                }
+            }
+        }
+
         // Treat the port event as a router interface event if the port belongs to router. This is a
         // helper for handling cases when handleNeutronRouterInterfaceEvent is not available
         //
         if (neutronPort.getDeviceOwner().equalsIgnoreCase(OWNER_ROUTER_INTERFACE) ||
             neutronPort.getDeviceOwner().equalsIgnoreCase(OWNER_ROUTER_INTERFACE_DISTRIBUTED)) {
+
             for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
                 NeutronRouter_Interface neutronRouterInterface =
                         new NeutronRouter_Interface(neutronIP.getSubnetUUID(), neutronPort.getPortUUID());
@@ -623,7 +658,7 @@ public class NeutronL3Adapter implements ConfigInterface {
                                            Action actionForNode) {
         // Based on the local cache, figure out whether programming needs to occur. To do this, we
         // will look at desired action for node.
-        //
+
         final String cacheKey = node.getNodeId().getValue() + ":" + providerSegmentationId + ":" + ipStr;
         final Boolean isProgrammed = l3ForwardingCache.contains(cacheKey);
 
@@ -1205,6 +1240,88 @@ public class NeutronL3Adapter implements ConfigInterface {
         return null;
     }
 
+    private Long getDpidForExternalBridge(Node node) {
+        // Check if node is integration bridge; and only then return its dpid
+        if (southbound.getBridge(node, configurationService.getExternalBridgeName()) != null) {
+            return southbound.getDataPathId(node);
+        }
+        return null;
+    }
+
+    private Node getExternalBridgeNode(){
+        //Pickup the first node that has external bridge (br-ex).
+        //NOTE: We are assuming that all the br-ex are serving one external network and gateway ip of
+        //the external network is reachable from every br-ex
+        // TODO: Consider other deployment scenario, and thing of better solution.
+        List<Node> allBridges = nodeCacheManager.getBridgeNodes();
+        for(Node node : allBridges){
+            if (southbound.getBridge(node, configurationService.getExternalBridgeName()) != null) {
+                return node;
+            }
+        }
+        return null;
+    }
+
+    private NeutronSubnet getExternalNetworkSubnet(NeutronPort gatewayPort){
+        NeutronSubnet extSubnet = null;
+        for (NeutronSubnet subnet : neutronSubnetCache.getAllSubnets()){
+            if(subnet.getPortsInSubnet().contains(gatewayPort)){
+                extSubnet = subnet;
+                break;
+            }
+        }
+        return extSubnet;
+    }
+
+    public void triggerGatewayMacResolver(final Node node, final NeutronPort gatewayPort ){
+
+        Preconditions.checkNotNull(node);
+        Preconditions.checkNotNull(gatewayPort);
+        NeutronNetwork externalNetwork = neutronNetworkCache.getNetwork(gatewayPort.getNetworkUUID());
+
+        if(externalNetwork != null){
+            if(externalNetwork.isRouterExternal()){
+                final NeutronSubnet externalSubnet = getExternalNetworkSubnet(gatewayPort);
+                if(externalSubnet != null){
+                    if(externalSubnet.getGatewayIP() != null){
+                        LOGGER.info("Trigger MAC resolution for gateway ip {} on Node {}",externalSubnet.getGatewayIP(),node.getNodeId());
+
+                        ListenableFuture<MacAddress> gatewayMacAddress =
+                                gatewayMacResolver.resolveMacAddress(getDpidForExternalBridge(node),
+                                        new Ipv4Address(externalSubnet.getGatewayIP()),
+                                        new Ipv4Address(gatewayPort.getFixedIPs().get(0).getIpAddress()),
+                                        new MacAddress(gatewayPort.getMacAddress()),
+                                        false);
+                        if(gatewayMacAddress != null){
+                            Futures.addCallback(gatewayMacAddress, new FutureCallback<MacAddress>(){
+                                @Override
+                                public void onSuccess(MacAddress result) {
+                                    if(result != null){
+                                        updateExternalRouterMac(result.getValue());
+                                        LOGGER.info("Resolved MAC address for gateway IP {} is {}", externalSubnet.getGatewayIP(),result.getValue());
+                                    }else{
+                                        LOGGER.warn("MAC address resolution failed for gateway IP {}",externalSubnet.getGatewayIP());
+                                    }
+                                }
+
+                                @Override
+                                public void onFailure(Throwable t) {
+                                    LOGGER.warn("MAC address resolution failed for gateway IP {}",externalSubnet.getGatewayIP());
+                                }
+                            }, gatewayMacResolverPool);
+                        }
+                    }else{
+                        LOGGER.warn("No gateway IP address found for external subnet {}",externalSubnet);
+                    }
+                }else{
+                    LOGGER.warn("Neutron subnet not found for external network {}",externalNetwork);
+                }
+            }
+        }else{
+            LOGGER.warn("Neutron network not found for router interface {}",gatewayPort);
+        }
+    }
+
     /**
      * Return String that represents OF port with marker explicitly provided (reverse of MatchUtils:parseExplicitOFPort)
      *
@@ -1235,7 +1352,8 @@ public class NeutronL3Adapter implements ConfigInterface {
                 (NodeCacheManager) ServiceHelper.getGlobalInstance(NodeCacheManager.class, this);
         southbound =
                 (Southbound) ServiceHelper.getGlobalInstance(Southbound.class, this);
-
+        gatewayMacResolver =
+                (GatewayMacResolver) ServiceHelper.getGlobalInstance(GatewayMacResolver.class, this);
         initL3AdapterMembers();
     }
 
@@ -1257,6 +1375,8 @@ public class NeutronL3Adapter implements ConfigInterface {
             routingProvider = (RoutingProvider)impl;
         } else if (impl instanceof L3ForwardingProvider) {
             l3ForwardingProvider = (L3ForwardingProvider)impl;
+        }else if (impl instanceof GatewayMacResolver) {
+            gatewayMacResolver = (GatewayMacResolver)impl;
         }
     }
 }