Merge branch 'master' of https://github.com/OPENNETWORKINGLAB/ONOS
diff --git a/src/main/java/net/floodlightcontroller/bgproute/BgpRoute.java b/src/main/java/net/floodlightcontroller/bgproute/BgpRoute.java
index d4ef11d..517126c 100644
--- a/src/main/java/net/floodlightcontroller/bgproute/BgpRoute.java
+++ b/src/main/java/net/floodlightcontroller/bgproute/BgpRoute.java
@@ -1,40 +1,118 @@
 package net.floodlightcontroller.bgproute;
 
+import java.io.IOException;
+import java.net.InetAddress;
 import java.net.UnknownHostException;
 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 net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.INetMapTopologyService.ITopoRouteService;
+import net.floodlightcontroller.core.IOFSwitch;
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.core.module.FloodlightModuleException;
 import net.floodlightcontroller.core.module.IFloodlightModule;
 import net.floodlightcontroller.core.module.IFloodlightService;
-import net.floodlightcontroller.linkdiscovery.ILinkDiscovery;
+import net.floodlightcontroller.devicemanager.IDeviceService;
 import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.LDUpdate;
+import net.floodlightcontroller.packet.Ethernet;
 import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.topology.ITopologyListener;
 import net.floodlightcontroller.topology.ITopologyService;
+import net.floodlightcontroller.util.DataPath;
+import net.floodlightcontroller.util.Dpid;
+import net.floodlightcontroller.util.FlowEntry;
+import net.floodlightcontroller.util.IPv4;
+import net.floodlightcontroller.util.MACAddress;
+import net.floodlightcontroller.util.Port;
+import net.floodlightcontroller.util.SwitchPort;
 import net.sf.json.JSONArray;
 import net.sf.json.JSONObject;
 import net.sf.json.JSONSerializer;
 
+import org.openflow.protocol.OFFlowMod;
+import org.openflow.protocol.OFMatch;
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFPacketOut;
+import org.openflow.protocol.OFType;
+import org.openflow.protocol.action.OFAction;
+import org.openflow.protocol.action.OFActionDataLayerDestination;
+import org.openflow.protocol.action.OFActionOutput;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.net.InetAddresses;
+
 public class BgpRoute implements IFloodlightModule, IBgpRouteService, ITopologyListener {
 	
 	protected static Logger log = LoggerFactory.getLogger(BgpRoute.class);
 
 	protected IFloodlightProviderService floodlightProvider;
-	protected IRestApiService restApi;
 	protected ITopologyService topology;
+	protected ITopoRouteService topoRouteService;
+	protected IDeviceService devices;
+	protected IRestApiService restApi;
 	
 	protected static Ptree ptree;
 	protected String bgpdRestIp;
 	protected String routerId;
 	
+	protected Set<InetAddress> routerIpAddresses;
+	
+	//We need to identify our flows somehow. But like it says in LearningSwitch.java,
+	//the controller/OS should hand out cookie IDs to prevent conflicts.
+	protected final long APP_COOKIE = 0xa0000000000000L;
+	//Cookie for flows that do L2 forwarding within SDN domain to egress routers
+	protected final long L2_FWD_COOKIE = APP_COOKIE + 1;
+	//Cookie for flows in ingress switches that rewrite the MAC address
+	protected final long MAC_RW_COOKIE = APP_COOKIE + 2;
+	//Forwarding uses priority 0, and the mac rewrite entries in ingress switches
+	//need to be higher priority than this otherwise the rewrite may not get done
+	protected final short SDNIP_PRIORITY = 10;
+	
+	//TODO temporary
+	protected List<GatewayRouter> gatewayRouters;
+	
+	private void initGateways(){
+		gatewayRouters = new ArrayList<GatewayRouter>();
+		//00:00:00:00:00:00:0s0:a3 port 1
+		gatewayRouters.add(
+				new GatewayRouter(new SwitchPort(new Dpid(163L), new Port((short)1)),
+				new MACAddress(new byte[] {0x00, 0x00, 0x00, 0x00, 0x02, 0x01}),
+				new IPv4("192.168.10.1"),
+				new MACAddress(new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}),
+				new IPv4("192.168.10.101")));
+		//00:00:00:00:00:00:00:a5 port 1
+		//gatewayRouters.add(new SwitchPort(new Dpid(165L), new Port((short)1)));
+		gatewayRouters.add(
+				new GatewayRouter(new SwitchPort(new Dpid(165L), new Port((short)1)),
+				new MACAddress(new byte[] {0x00, 0x00, 0x00, 0x00, 0x02, 0x02}),
+				new IPv4("192.168.20.1"),
+				new MACAddress(new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}),
+				new IPv4("192.168.20.101")));
+		//00:00:00:00:00:00:00:a2 port 1
+		//gatewayRouters.add(new SwitchPort(new Dpid(162L), new Port((short)1)));
+		gatewayRouters.add(
+				new GatewayRouter(new SwitchPort(new Dpid(162L), new Port((short)1)),
+				new MACAddress(new byte[] {0x00, 0x00, 0x00, 0x00, 0x03, 0x01}),
+				new IPv4("192.168.30.1"),
+				new MACAddress(new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}),
+				new IPv4("192.168.30.101")));
+		//00:00:00:00:00:00:00:a6
+		//gatewayRouters.add(new SwitchPort(new Dpid(166L), new Port((short)1)));
+		gatewayRouters.add(
+				new GatewayRouter(new SwitchPort(new Dpid(166L), new Port((short)1)),
+				new MACAddress(new byte[] {0x00, 0x00, 0x00, 0x00, 0x04, 0x01}),
+				new IPv4("192.168.40.1"),
+				new MACAddress(new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}),
+				new IPv4("192.168.40.101")));
+		
+	}
 	
 	@Override
 	public Collection<Class<? extends IFloodlightService>> getModuleServices() {
@@ -58,7 +136,9 @@
 			= new ArrayList<Class<? extends IFloodlightService>>();
 		l.add(IFloodlightProviderService.class);
 		l.add(ITopologyService.class);
-		l.add(IBgpRouteService.class);
+		l.add(ITopoRouteService.class);
+		l.add(IDeviceService.class);
+		l.add(IRestApiService.class);
 		return l;
 	}
 	
@@ -66,12 +146,18 @@
 	public void init(FloodlightModuleContext context)
 			throws FloodlightModuleException {
 	    
+		initGateways();
+		
 	    ptree = new Ptree(32);
+	    
+	    routerIpAddresses = new HashSet<InetAddress>();
 		
 		// Register floodlight provider and REST handler.
 		floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
-		restApi = context.getServiceImpl(IRestApiService.class);
 		topology = context.getServiceImpl(ITopologyService.class);
+		topoRouteService = context.getServiceImpl(ITopoRouteService.class);
+		devices = context.getServiceImpl(IDeviceService.class);
+		restApi = context.getServiceImpl(IRestApiService.class);		
 		
 		//Read in config values
 		bgpdRestIp = context.getConfigParams(this).get("BgpdRestIp");
@@ -239,9 +325,245 @@
 			}
 			
 			node.rib = rib;
+			
+			prefixAdded(node);
 		} 
 	}
 	
+	public void prefixAdded(PtreeNode node) {
+		//Add a flow to rewrite mac for this prefix to all border switches
+		GatewayRouter thisRouter = null;
+		for (GatewayRouter router : gatewayRouters){	
+			if (router.getRouterIp().value() == 
+					InetAddresses.coerceToInteger(node.rib.nextHop)){
+				thisRouter = router;
+				break;
+			}
+		}
+		
+		if (thisRouter == null){
+			//TODO local router isn't in gateway list so this will get thrown
+			//Need to work out what to do about local prefixes with next hop 0.0.0.0.
+			log.error("Couldn't find next hop router in router {} in config"
+					, node.rib.nextHop.toString());
+			return; //just quit out here? This is probably a configuration error
+		}
+
+		for (GatewayRouter ingressRouter : gatewayRouters){
+			if (ingressRouter == thisRouter) {
+				continue;
+			}
+			
+			DataPath shortestPath = topoRouteService.getShortestPath(
+					ingressRouter.getAttachmentPoint(), 
+					thisRouter.getAttachmentPoint());
+			
+			if (shortestPath == null){
+				log.debug("Shortest path between {} and {} not found",
+						ingressRouter.getAttachmentPoint(), 
+						thisRouter.getAttachmentPoint());
+				return; // just quit here?
+			}
+			
+			//TODO check the shortest path against the cached version we
+			//calculated before. If they don't match up that's a problem
+			
+			//Set up the flow mod
+			OFFlowMod fm =
+	                (OFFlowMod) floodlightProvider.getOFMessageFactory()
+	                                              .getMessage(OFType.FLOW_MOD);
+			
+	        fm.setIdleTimeout((short)0)
+	        .setHardTimeout((short)0)
+	        .setBufferId(OFPacketOut.BUFFER_ID_NONE)
+	        .setCookie(MAC_RW_COOKIE)
+	        .setCommand(OFFlowMod.OFPFC_ADD)
+	        //.setMatch(match)
+	        //.setActions(actions)
+	        .setPriority(SDNIP_PRIORITY)
+	        .setLengthU(OFFlowMod.MINIMUM_LENGTH
+	        		+ OFActionDataLayerDestination.MINIMUM_LENGTH
+	        		+ OFActionOutput.MINIMUM_LENGTH);
+	        
+	        OFMatch match = new OFMatch();
+	        match.setDataLayerType(Ethernet.TYPE_IPv4);
+	        match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
+	        
+	        match.setDataLayerSource(ingressRouter.getRouterMac().toBytes());
+	        match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
+	        
+	        //match.setDataLayerDestination(ingressRouter.getSdnRouterMac().toBytes());
+	        //match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
+
+	        InetAddress address = null;
+	        try {
+				address = InetAddress.getByAddress(node.key);
+			} catch (UnknownHostException e1) {
+				//Should never happen is the reverse conversion has already been done
+				log.error("Malformed IP address");
+				return;
+			}
+	        
+	        match.setFromCIDR(address.getHostAddress() + "/" + node.rib.masklen, OFMatch.STR_NW_DST);
+	        fm.setMatch(match);
+	        
+	        //Set up MAC rewrite action
+	        OFActionDataLayerDestination macRewriteAction = new OFActionDataLayerDestination();
+	        macRewriteAction.setDataLayerAddress(thisRouter.getRouterMac().toBytes());
+	        
+	        //Set up output action
+	        OFActionOutput outputAction = new OFActionOutput();
+	        outputAction.setMaxLength((short)0xffff); //TODO check what this is (and if needed for mac rewrite)
+	        
+	        Port outputPort = shortestPath.flowEntries().get(0).outPort();
+	        outputAction.setPort(outputPort.value());
+	        
+	        List<OFAction> actions = new ArrayList<OFAction>();
+	        actions.add(macRewriteAction);
+	        actions.add(outputAction);
+	        fm.setActions(actions);
+	        
+	        //Write to switch
+	        IOFSwitch sw = floodlightProvider.getSwitches()
+	        		.get(ingressRouter.getAttachmentPoint().dpid().value());
+	        
+            if (sw == null){
+            	log.warn("Switch not found when pushing flow mod");
+            	continue;
+            }
+            
+            List<OFMessage> msglist = new ArrayList<OFMessage>();
+            msglist.add(fm);
+            try {
+				sw.write(msglist, null);
+				sw.flush();
+			} catch (IOException e) {
+				log.error("Failure writing flow mod", e);
+			}
+		}
+	}
+	
+	public void prefixDeleted(PtreeNode node) {
+		//Remove MAC rewriting flows from other border switches
+		
+	}
+	
+	/*
+	 * On startup we need to calculate a full mesh of paths between all gateway
+	 * switches
+	 */
+	private void calculateFullMesh(){
+		Map<IOFSwitch, SwitchPort> gatewaySwitches = new HashMap<IOFSwitch, SwitchPort>();
+		
+		//have to account for switches not being there, paths not being found.
+		
+		//for (SwitchPort switchPort : gatewayRouters){
+		for (GatewayRouter router : gatewayRouters){
+			SwitchPort switchPort = router.getAttachmentPoint();
+			
+			IOFSwitch sw = floodlightProvider.getSwitches().get(switchPort.dpid().value());
+			
+			if (sw == null){
+				log.debug("Gateway switch {} not here yet", switchPort.dpid().value());
+				return; // just quit here?
+			}
+			
+			//Only need to know 1 external-facing port from each gateway switch
+			//which we can feed into shortest path calculation
+			if (!gatewaySwitches.containsKey(sw)){
+				gatewaySwitches.put(sw, switchPort);
+			}
+			
+		}
+		log.debug("size {}", gatewaySwitches.size());
+		
+		//For each border router, calculate and install a path from every other
+		//border switch to said border router. However, don't install the entry
+		//in to the first hop switch, as we need to install an entry to rewrite
+		//for each prefix received. This will be done later when prefixes have 
+		//actually been received.
+		
+		for (GatewayRouter dstRouter : gatewayRouters){
+			SwitchPort routerAttachmentPoint = dstRouter.getAttachmentPoint();
+			for (Map.Entry<IOFSwitch, SwitchPort> src : gatewaySwitches.entrySet()) {
+		
+				if (routerAttachmentPoint.dpid().value() == 
+						src.getKey().getId()){
+					continue;
+				}
+				
+				DataPath shortestPath = topoRouteService.getShortestPath(
+						src.getValue(), routerAttachmentPoint);
+				
+				if (shortestPath == null){
+					log.debug("Shortest path between {} and {} not found",
+							src.getValue(), routerAttachmentPoint);
+					return; // just quit here?
+				}
+				
+				//install flows
+				installPath(shortestPath.flowEntries(), dstRouter);
+			}
+		}
+	}
+	
+	private void installPath(List<FlowEntry> flowEntries, GatewayRouter router){
+
+		//Set up the flow mod
+		OFFlowMod fm =
+                (OFFlowMod) floodlightProvider.getOFMessageFactory()
+                                              .getMessage(OFType.FLOW_MOD);
+		
+        OFActionOutput action = new OFActionOutput();
+        action.setMaxLength((short)0xffff);
+        List<OFAction> actions = new ArrayList<OFAction>();
+        actions.add(action);
+        
+        fm.setIdleTimeout((short)0)
+        .setHardTimeout((short)0)
+        .setBufferId(OFPacketOut.BUFFER_ID_NONE)
+        .setCookie(L2_FWD_COOKIE)
+        .setCommand(OFFlowMod.OFPFC_ADD)
+        //.setMatch(match)
+        .setActions(actions)
+        .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
+        
+        //Don't push the first flow entry. We need to push entries in the
+		//first switch based on IP prefix which we don't know yet.
+        for (int i = 1; i < flowEntries.size(); i++){        	
+        	FlowEntry flowEntry = flowEntries.get(i);
+           
+            OFMatch match = new OFMatch();
+            match.setDataLayerDestination(router.getRouterMac().toBytes());
+            match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
+            ((OFActionOutput) fm.getActions().get(0)).setPort(flowEntry.outPort().value());
+            
+            fm.setMatch(match);
+            
+            IOFSwitch sw = floodlightProvider.getSwitches().get(flowEntry.dpid().value());
+            
+            if (sw == null){
+            	log.warn("Switch not found when pushing flow mod");
+            	continue;
+            }
+            
+            List<OFMessage> msglist = new ArrayList<OFMessage>();
+            msglist.add(fm);
+            try {
+				sw.write(msglist, null);
+				sw.flush();
+			} catch (IOException e) {
+				log.error("Failure writing flow mod", e);
+			}
+            
+            try {
+                fm = fm.clone();
+            } catch (CloneNotSupportedException e1) {
+                log.error("Failure cloning flow mod", e1);
+            }
+		}
+	}
+	
 	@Override
 	public void startUp(FloodlightModuleContext context) {
 		restApi.addRestletRoutable(new BgpRouteWebRoutable());
@@ -249,10 +571,15 @@
 		
 		//Retrieve the RIB from BGPd during startup
 		retrieveRib();
+		
+		//Don't have to do this as we'll never have switches connected here
+		//calculateFullMesh();
 	}
 
 	@Override
 	public void topologyChanged() {
+		//Probably need to look at all changes, not just port changes
+		/*
 		boolean change = false;
 		String changelog = "";
 		
@@ -270,5 +597,13 @@
 		if (change) {
 			//RestClient.get ("http://localhost:5000/topo_change");
 		}
+		*/
+		
+		for (LDUpdate update : topology.getLastLinkUpdates()){
+			log.debug("{} event causing internal L2 path recalculation",
+					update.getOperation().toString());
+			
+		}
+		calculateFullMesh();
 	}
 }
diff --git a/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResource.java b/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResource.java
index fa9d577..4ad1e95 100644
--- a/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResource.java
+++ b/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResource.java
@@ -28,16 +28,14 @@
 
 	//@SuppressWarnings("unused")
 	@Get
-	public String get(String fmJson) {		
-		//String linpp=fmJson;
+	public String get(String fmJson) {
 		String dest = (String) getRequestAttributes().get("dest");
 		String output = "";
 		IBgpRouteService bgpRoute = (IBgpRouteService)getContext().getAttributes().
 				get(IBgpRouteService.class.getCanonicalName());
 
 		if (dest != null) {
-			//TODO this code path looks like its never used (except maybe for debugging)
-			//Needs to be changed to use the new RestClient.get().
+			//TODO Needs to be changed to use the new RestClient.get().
 			
 			
 			//Prefix p;
@@ -136,6 +134,8 @@
 			}
 			node.rib = rib;
 
+			bgpRoute.prefixAdded(node);
+			
 			reply = "[POST: " + prefix + "/" + mask + ":" + nexthop + "]";
 			log.info(reply);
 		}
@@ -194,6 +194,8 @@
 				}
 			}
 
+			bgpRoute.prefixDeleted(node);
+			
 			reply =reply + "[DELE: " + prefix + "/" + mask + ":" + nextHop + "]";
 		}
 		else {
diff --git a/src/main/java/net/floodlightcontroller/bgproute/GatewayRouter.java b/src/main/java/net/floodlightcontroller/bgproute/GatewayRouter.java
new file mode 100644
index 0000000..56d5243
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/bgproute/GatewayRouter.java
@@ -0,0 +1,46 @@
+package net.floodlightcontroller.bgproute;
+
+import net.floodlightcontroller.util.IPv4;
+import net.floodlightcontroller.util.MACAddress;
+import net.floodlightcontroller.util.SwitchPort;
+
+public class GatewayRouter {
+	private SwitchPort attachmentPoint;
+	private MACAddress routerMac;
+	private IPv4 routerIp;
+	
+	//For now, put in the IP and MAC of the SDN domain's router that this 
+	//gateway will be communicating with
+	private MACAddress sdnRouterMac;
+	private IPv4 sdnRouterIp;
+	
+	public GatewayRouter(SwitchPort attachmentPoint, MACAddress routerMac, 
+			IPv4 routerIp, MACAddress sdnRouterMac, IPv4 sdnRouterIp) {
+		this.attachmentPoint = attachmentPoint;
+		this.routerMac = routerMac;
+		this.routerIp = routerIp;
+		this.sdnRouterIp = sdnRouterIp;
+		this.sdnRouterMac = sdnRouterMac;
+	}
+
+	public SwitchPort getAttachmentPoint() {
+		return attachmentPoint;
+	}
+
+	public MACAddress getRouterMac() {
+		return routerMac;
+	}
+
+	public IPv4 getRouterIp() {
+		return routerIp;
+	}
+	
+	//TODO delete if not needed
+	public MACAddress getSdnRouterMac() {
+		return sdnRouterMac;
+	}
+	
+	public IPv4 getSdnRouterIp() {
+		return sdnRouterIp;
+	}
+}
diff --git a/src/main/java/net/floodlightcontroller/bgproute/IBgpRouteService.java b/src/main/java/net/floodlightcontroller/bgproute/IBgpRouteService.java
index 35bc3d4..0e95d9e 100644
--- a/src/main/java/net/floodlightcontroller/bgproute/IBgpRouteService.java
+++ b/src/main/java/net/floodlightcontroller/bgproute/IBgpRouteService.java
@@ -12,5 +12,9 @@
 
 	public String getRouterId();
 
-	public void  clearPtree() ;
+	public void clearPtree();
+	
+	//TODO This functionality should be provided by some sort of Ptree listener framework
+	public void prefixAdded(PtreeNode node);
+	public void prefixDeleted(PtreeNode node);
 }
diff --git a/src/main/java/net/floodlightcontroller/bgproute/RestClient.java b/src/main/java/net/floodlightcontroller/bgproute/RestClient.java
index faf7d6e..ef5e38b 100644
--- a/src/main/java/net/floodlightcontroller/bgproute/RestClient.java
+++ b/src/main/java/net/floodlightcontroller/bgproute/RestClient.java
@@ -7,6 +7,7 @@
 import java.net.MalformedURLException;
 import java.net.URL;
 
+import org.apache.commons.httpclient.ConnectTimeoutException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -21,6 +22,7 @@
 
 			URL url = new URL(str);
 			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+			conn.setConnectTimeout(2 * 1000); //2 seconds
 			conn.setRequestMethod("GET");
 			conn.setRequestProperty("Accept", "application/json");
 
@@ -44,6 +46,8 @@
 			
 		} catch (MalformedURLException e) {
 			log.error("Malformed URL for GET request", e);
+		} catch (ConnectTimeoutException e) {
+			log.warn("Couldn't connect remote REST server");
 		} catch (IOException e) {
 			log.warn("Couldn't connect remote REST server");
 		}