fix ARP (single onos instance & multiple onos instances)

1. make sure that ARP requests coming from host in a LAN
   are not broadcasted to all other edge ports (other ASes)
2. make sure the ARP requests from BGPd host can be singlecast
   to other ASes
3. add a 'get external network switch ports' function
4. make the arp work for both single onos instance & multiple
   onos instances
5. add a switch ports blacklist. Arp requests are not to be sent
   to those switch ports on this list.

Change-Id: I4ffdd73e2ad202517c54690a86109cfa1fbe400f
diff --git a/src/main/java/net/onrc/onos/api/packet/IPacketService.java b/src/main/java/net/onrc/onos/api/packet/IPacketService.java
index d182c57..8ed7737 100644
--- a/src/main/java/net/onrc/onos/api/packet/IPacketService.java
+++ b/src/main/java/net/onrc/onos/api/packet/IPacketService.java
@@ -47,11 +47,13 @@
      * Broadcast the packet out all edge ports in the network. An edge port is
      * defined as any port that doesn't have a link to another switch.
      * <p/>
+     * By default, this function does not broadcast to external networks.
+     * <p/>
      * NOTE: currently unimplemented.
      *
      * @param eth the packet to broadcast
      */
-    public void broadcastPacketOutEdge(Ethernet eth);
+    public void broadcastPacketOutInternalEdge(Ethernet eth);
 
     /**
      * Broadcast the packet out all edge ports in the network, except for the
@@ -61,10 +63,12 @@
      * This is useful for packets that are received from a host in the
      * dataplane, where we want to broadcast the packet to everyone apart from
      * the host that sent it.
+     * <p/>
+     * By default, this function does not broadcast to external networks.
      *
      * @param eth the packet to broadcast
      * @param inSwitchPort the exception port that the packet is not
      * broadcast out
      */
-    public void broadcastPacketOutEdge(Ethernet eth, SwitchPort inSwitchPort);
+    public void broadcastPacketOutInternalEdge(Ethernet eth, SwitchPort inSwitchPort);
 }
diff --git a/src/main/java/net/onrc/onos/apps/forwarding/Forwarding.java b/src/main/java/net/onrc/onos/apps/forwarding/Forwarding.java
index 6758b7a..3e4e1c4 100644
--- a/src/main/java/net/onrc/onos/apps/forwarding/Forwarding.java
+++ b/src/main/java/net/onrc/onos/apps/forwarding/Forwarding.java
@@ -249,7 +249,7 @@
             log.trace("Sending broadcast packet to other ONOS instances");
         }
 
-        packetService.broadcastPacketOutEdge(eth,
+        packetService.broadcastPacketOutInternalEdge(eth,
                 new SwitchPort(sw.getDpid(), inPort.getNumber()));
     }
 
diff --git a/src/main/java/net/onrc/onos/apps/proxyarp/ProxyArpManager.java b/src/main/java/net/onrc/onos/apps/proxyarp/ProxyArpManager.java
index cfdd9c1..197b40d 100644
--- a/src/main/java/net/onrc/onos/apps/proxyarp/ProxyArpManager.java
+++ b/src/main/java/net/onrc/onos/apps/proxyarp/ProxyArpManager.java
@@ -404,13 +404,17 @@
         }
 
         InetAddress target;
+        InetAddress sender;
         try {
             target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
+            sender = InetAddress.getByAddress(arp.getSenderProtocolAddress());
+
         } catch (UnknownHostException e) {
             log.debug("Invalid address in ARP request", e);
             return;
         }
 
+        // Handle ARP from external network
         if (configService.fromExternalNetwork(dpid, inPort)) {
             // If the request came from outside our network, we only care if
             // it was a request for one of our interfaces.
@@ -420,6 +424,7 @@
                         target.getHostAddress(),
                         configService.getRouterMacAddress());
 
+                //TODO: learn MAC address dynamically rather than from configuration
                 sendArpReply(arp, dpid, inPort,
                         configService.getRouterMacAddress());
             }
@@ -427,6 +432,17 @@
             return;
         }
 
+        // Handle ARP to external network
+        if (configService.inConnectedNetwork(target)
+                && configService.isInterfaceAddress(sender)) {
+            SwitchPort switchPort =
+                    configService.getOutgoingInterface(target).getSwitchPort();
+            arpRequests.put(target, new ArpRequest(
+                    new HostArpRequester(arp, dpid, inPort), false));
+            packetService.sendPacket(eth, switchPort);
+            return;
+        }
+
         //MACAddress mac = arpCache.lookup(target);
 
         arpRequests.put(target, new ArpRequest(
@@ -444,7 +460,7 @@
             }
 
             // We don't know the device so broadcast the request out
-            packetService.broadcastPacketOutEdge(eth,
+            packetService.broadcastPacketOutInternalEdge(eth,
                     new SwitchPort(dpid, inPort));
         } else {
             // Even if the device exists in our database, we do not reply to
@@ -468,7 +484,7 @@
                             " - broadcasting", macAddress);
                 }
 
-                packetService.broadcastPacketOutEdge(eth,
+                packetService.broadcastPacketOutInternalEdge(eth,
                         new SwitchPort(dpid, inPort));
             } else {
                 for (Port portObject : outPorts) {
diff --git a/src/main/java/net/onrc/onos/apps/sdnip/SdnIp.java b/src/main/java/net/onrc/onos/apps/sdnip/SdnIp.java
index cd1c2de..ed7a8ee 100644
--- a/src/main/java/net/onrc/onos/apps/sdnip/SdnIp.java
+++ b/src/main/java/net/onrc/onos/apps/sdnip/SdnIp.java
@@ -5,6 +5,7 @@
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -108,6 +109,7 @@
     private SwitchPort bgpdAttachmentPoint;
     private MACAddress bgpdMacAddress;
     private short vlan;
+    private Set<SwitchPort> externalNetworkSwitchPorts;
 
     private SetMultimap<InetAddress, RibUpdate> prefixesWaitingOnArp;
 
@@ -132,6 +134,8 @@
             interfaces = new HashMap<>();
             for (Interface intf : config.getInterfaces()) {
                 interfaces.put(intf.getName(), intf);
+                externalNetworkSwitchPorts.add(new SwitchPort(intf.getDpid(),
+                        intf.getPort()));
             }
             bgpPeers = new HashMap<>();
             for (BgpPeer peer : config.getPeers()) {
@@ -193,6 +197,7 @@
                 new DefaultByteArrayNodeFactory());
         interfaceRoutes = new ConcurrentInvertedRadixTree<>(
                 new DefaultByteArrayNodeFactory());
+        externalNetworkSwitchPorts = new HashSet<SwitchPort>();
 
         ribUpdates = new LinkedBlockingQueue<>();
 
@@ -906,4 +911,9 @@
         return vlan;
     }
 
+    @Override
+    public Set<SwitchPort> getExternalSwitchPorts() {
+        return Collections.unmodifiableSet(externalNetworkSwitchPorts);
+    }
+
 }
diff --git a/src/main/java/net/onrc/onos/core/main/config/DefaultConfiguration.java b/src/main/java/net/onrc/onos/core/main/config/DefaultConfiguration.java
index 219cdf3..de33253 100644
--- a/src/main/java/net/onrc/onos/core/main/config/DefaultConfiguration.java
+++ b/src/main/java/net/onrc/onos/core/main/config/DefaultConfiguration.java
@@ -5,6 +5,7 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
 
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.core.module.FloodlightModuleException;
@@ -12,6 +13,7 @@
 import net.floodlightcontroller.core.module.IFloodlightService;
 import net.floodlightcontroller.util.MACAddress;
 import net.onrc.onos.apps.sdnip.Interface;
+import net.onrc.onos.core.util.SwitchPort;
 
 import org.projectfloodlight.openflow.util.HexString;
 
@@ -84,4 +86,9 @@
         // no-op
     }
 
+    @Override
+    public Set<SwitchPort> getExternalSwitchPorts() {
+        return null;
+    }
+
 }
diff --git a/src/main/java/net/onrc/onos/core/main/config/IConfigInfoService.java b/src/main/java/net/onrc/onos/core/main/config/IConfigInfoService.java
index 000a6a1..513777e 100644
--- a/src/main/java/net/onrc/onos/core/main/config/IConfigInfoService.java
+++ b/src/main/java/net/onrc/onos/core/main/config/IConfigInfoService.java
@@ -1,10 +1,12 @@
 package net.onrc.onos.core.main.config;
 
 import java.net.InetAddress;
+import java.util.Set;
 
 import net.floodlightcontroller.core.module.IFloodlightService;
 import net.floodlightcontroller.util.MACAddress;
 import net.onrc.onos.apps.sdnip.Interface;
+import net.onrc.onos.core.util.SwitchPort;
 
 /**
  * Provides information about the layer 3 properties of the network.
@@ -48,5 +50,16 @@
      */
     public short getVlan();
 
+    /**
+     * Gets the external-facing switch ports in the network.
+     * <p/>
+     * We treat the switch ports (in SDN network) connected to other networks
+     * as external networks switch ports.
+     *
+     * @return all the switch ports connected to external networks
+     */
+    public Set<SwitchPort> getExternalSwitchPorts();
+
+
 
 }
diff --git a/src/main/java/net/onrc/onos/core/packetservice/BroadcastPacketOutNotification.java b/src/main/java/net/onrc/onos/core/packetservice/BroadcastPacketOutNotification.java
index f7629d8..01f79a6 100644
--- a/src/main/java/net/onrc/onos/core/packetservice/BroadcastPacketOutNotification.java
+++ b/src/main/java/net/onrc/onos/core/packetservice/BroadcastPacketOutNotification.java
@@ -1,11 +1,15 @@
 package net.onrc.onos.core.packetservice;
 
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 import net.onrc.onos.core.topology.MutableTopology;
 import net.onrc.onos.core.topology.Port;
 import net.onrc.onos.core.util.Dpid;
 import net.onrc.onos.core.util.PortNumber;
+import net.onrc.onos.core.util.SwitchPort;
 
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.Multimap;
@@ -21,11 +25,8 @@
  */
 public class BroadcastPacketOutNotification extends PacketOutNotification {
 
-    private static final long serialVersionUID = 1L;
-
     private final int address;
-    private final long inSwitch;
-    private final short inPort;
+    private final Set<SwitchPort> blacklistSwitchPorts;
 
     /**
      * Default constructor, used for deserialization.
@@ -33,8 +34,7 @@
     protected BroadcastPacketOutNotification() {
         super();
         this.address = 0;
-        this.inSwitch = 0;
-        this.inPort = 0;
+        this.blacklistSwitchPorts = null;
     }
 
     /**
@@ -42,34 +42,39 @@
      *
      * @param packet   packet data to send in the packet-out
      * @param address  target IP address if the packet is an ARP packet
-     * @param inSwitch dpid of the switch the packet was received on
-     * @param inPort   port number of the receiving port
+     * @param inSwitchPort switch port the packet was received on
      */
     public BroadcastPacketOutNotification(byte[] packet, int address,
-                                          long inSwitch, short inPort) {
+            SwitchPort inSwitchPort) {
         super(packet);
 
         this.address = address;
-        this.inSwitch = inSwitch;
-        this.inPort = inPort;
+        this.blacklistSwitchPorts =  new HashSet<SwitchPort>();
+        blacklistSwitchPorts.add(inSwitchPort);
     }
 
     /**
-     * Get the dpid of the switch the packet was received on.
+     * Class constructor.
      *
-     * @return receiving switch dpid
+     * @param packet   packet data to send in the packet-out
+     * @param address  target IP address if the packet is an ARP packet
+     * @param blacklistSwitchPorts switch ports will not be broadcasted to
      */
-    public long getInSwitch() {
-        return inSwitch;
+    public BroadcastPacketOutNotification(byte[] packet, int address,
+            Set<SwitchPort> blacklistSwitchPorts) {
+        super(packet);
+
+        this.address = address;
+        this.blacklistSwitchPorts = new HashSet<SwitchPort>(blacklistSwitchPorts);
     }
 
     /**
-     * Get the port number of the port the packet was received on.
+     * Get the blacklist SwitchPorts.
      *
-     * @return receiving port number
+     * @return blacklist SwitchPorts
      */
-    public short getInPort() {
-        return inPort;
+    public Set<SwitchPort> getInSwitchPort() {
+        return Collections.unmodifiableSet(blacklistSwitchPorts);
     }
 
     /**
@@ -96,9 +101,11 @@
             } finally {
                 mutableTopology.releaseReadLock();
             }
+            SwitchPort switchPort =  new SwitchPort(entry.getKey(), entry.getValue());
 
-            if ((!entry.getKey().equals(inSwitch) ||
-                    !entry.getValue().equals(inPort)) &&
+
+            if ((blacklistSwitchPorts == null || !blacklistSwitchPorts
+                    .contains(switchPort)) &&
                     globalPort != null &&
                     globalPort.getOutgoingLink() == null) {
 
@@ -108,4 +115,5 @@
 
         return outPorts;
     }
+
 }
diff --git a/src/main/java/net/onrc/onos/core/packetservice/PacketModule.java b/src/main/java/net/onrc/onos/core/packetservice/PacketModule.java
index f872e5c..9cf618a 100644
--- a/src/main/java/net/onrc/onos/core/packetservice/PacketModule.java
+++ b/src/main/java/net/onrc/onos/core/packetservice/PacketModule.java
@@ -3,8 +3,10 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 import net.floodlightcontroller.core.FloodlightContext;
@@ -21,6 +23,7 @@
 import net.onrc.onos.core.datagrid.IEventChannel;
 import net.onrc.onos.core.datagrid.IEventChannelListener;
 import net.onrc.onos.core.flowprogrammer.IFlowPusherService;
+import net.onrc.onos.core.main.config.IConfigInfoService;
 import net.onrc.onos.core.packet.Ethernet;
 import net.onrc.onos.core.topology.ITopologyService;
 import net.onrc.onos.core.topology.Port;
@@ -54,6 +57,7 @@
     private MutableTopology mutableTopology;
     private IDatagridService datagrid;
     private IFlowPusherService flowPusher;
+    private IConfigInfoService configService;
 
     private IEventChannel<Long, PacketOutNotification> packetOutEventChannel;
 
@@ -69,12 +73,14 @@
         @Override
         public void entryAdded(PacketOutNotification value) {
             Multimap<Long, Short> localPorts = HashMultimap.create();
+
             for (IOFSwitch sw : floodlightProvider.getSwitches().values()) {
                 for (OFPortDesc port : sw.getEnabledPorts()) {
                     // XXX S fix this to int
                     localPorts.put(sw.getId(), port.getPortNo().getShortPortNumber());
                 }
             }
+
             Multimap<Long, Short> outPorts = value.calculateOutPorts(
                     localPorts, mutableTopology);
             sendPacketToSwitches(outPorts, value.getPacketData());
@@ -118,16 +124,21 @@
     }
 
     @Override
-    public void broadcastPacketOutEdge(Ethernet eth) {
+    public void broadcastPacketOutInternalEdge(Ethernet eth) {
         // TODO Auto-generated method stub
         throw new UnsupportedOperationException("Not yet implemented");
     }
 
     @Override
-    public void broadcastPacketOutEdge(Ethernet eth, SwitchPort inSwitchPort) {
+    public void broadcastPacketOutInternalEdge(Ethernet eth, SwitchPort inSwitchPort) {
+
+        Set<SwitchPort> blacklistSwitchPorts = new HashSet<SwitchPort>(configService
+                .getExternalSwitchPorts());
+        blacklistSwitchPorts.add(inSwitchPort);
+
         BroadcastPacketOutNotification notification =
                 new BroadcastPacketOutNotification(eth.serialize(), 0,
-                        inSwitchPort.getDpid().value(), inSwitchPort.getPortNumber().shortValue());
+                        blacklistSwitchPorts);
 
         long dstMac = eth.getDestinationMAC().toLong();
         packetOutEventChannel.addTransientEntry(dstMac, notification);
@@ -209,6 +220,7 @@
         dependencies.add(ITopologyService.class);
         dependencies.add(IDatagridService.class);
         dependencies.add(IFlowPusherService.class);
+        dependencies.add(IConfigInfoService.class);
         return dependencies;
     }
 
@@ -221,6 +233,7 @@
                 .getTopology();
         datagrid = context.getServiceImpl(IDatagridService.class);
         flowPusher = context.getServiceImpl(IFlowPusherService.class);
+        configService = context.getServiceImpl(IConfigInfoService.class);
     }
 
     @Override
diff --git a/src/main/java/net/onrc/onos/core/packetservice/PacketOutNotification.java b/src/main/java/net/onrc/onos/core/packetservice/PacketOutNotification.java
index 2a577f3..11a1015 100644
--- a/src/main/java/net/onrc/onos/core/packetservice/PacketOutNotification.java
+++ b/src/main/java/net/onrc/onos/core/packetservice/PacketOutNotification.java
@@ -1,6 +1,5 @@
 package net.onrc.onos.core.packetservice;
 
-import java.io.Serializable;
 import java.util.Arrays;
 
 import net.onrc.onos.core.topology.MutableTopology;
@@ -13,9 +12,7 @@
  * abstract base class that will be subclassed by specific types of
  * notifications.
  */
-public abstract class PacketOutNotification implements Serializable {
-
-    private static final long serialVersionUID = 1L;
+public abstract class PacketOutNotification {
 
     private final byte[] packet;
 
diff --git a/src/main/java/net/onrc/onos/core/packetservice/SinglePacketOutNotification.java b/src/main/java/net/onrc/onos/core/packetservice/SinglePacketOutNotification.java
index e9bb7a4..85896e7 100644
--- a/src/main/java/net/onrc/onos/core/packetservice/SinglePacketOutNotification.java
+++ b/src/main/java/net/onrc/onos/core/packetservice/SinglePacketOutNotification.java
@@ -10,8 +10,6 @@
  */
 public class SinglePacketOutNotification extends PacketOutNotification {
 
-    private static final long serialVersionUID = 1L;
-
     private final int address;
     private final long outSwitch;
     private final short outPort;