Implemented the bulk of the transit-route-pusher.py script in the BgpRoute module
diff --git a/src/main/java/net/floodlightcontroller/bgproute/BgpRoute.java b/src/main/java/net/floodlightcontroller/bgproute/BgpRoute.java
index d4ef11d..a91c96f 100644
--- a/src/main/java/net/floodlightcontroller/bgproute/BgpRoute.java
+++ b/src/main/java/net/floodlightcontroller/bgproute/BgpRoute.java
@@ -1,40 +1,107 @@
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.linkdiscovery.ILinkDiscovery.LDUpdate;
+import net.floodlightcontroller.devicemanager.IDeviceService;
+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;
+
+ //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")));
+ //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")));
+ //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")));
+ //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")));
+
+ }
@Override
public Collection<Class<? extends IFloodlightService>> getModuleServices() {
@@ -58,7 +125,10 @@
= 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);
+ //l.add(IBgpRouteService.class);
return l;
}
@@ -66,12 +136,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 +315,234 @@
}
node.rib = rib;
+
+ newPrefix(node);
}
}
+ private void newPrefix(PtreeNode node) {
+ //Add a flow to rewrite mac for this prefix to all border switches
+ GatewayRouter thisRouter = null;
+ for (GatewayRouter router : gatewayRouters){
+ log.debug("Matching router ip {} with RIB routerId {}",
+ router.getRouterIp().value(), InetAddresses.coerceToInteger(node.rib.routerId));
+
+ if (router.getRouterIp().value() == InetAddresses.coerceToInteger(node.rib.routerId)){
+ thisRouter = router;
+ break;
+ }
+ }
+
+ if (thisRouter == null){
+ log.error("Couldn't find advertising router in router list for prefix {}", node.rib);
+ 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)
+ .setLengthU(OFFlowMod.MINIMUM_LENGTH
+ + OFActionDataLayerDestination.MINIMUM_LENGTH
+ + OFActionOutput.MINIMUM_LENGTH);
+
+ OFMatch match = new OFMatch();
+ match.setDataLayerType(Ethernet.TYPE_IPv4);
+ //match.setNetworkDestination(InetAddresses.coerceToInteger(node.rib.getNextHop()));
+ //TODO set mask length
+
+ log.debug("finding prefix, {}", InetAddresses.fromInteger(node.keyBits).toString());
+ //match.setFromCIDR(node.keyBits, which)
+ //match.setw
+ //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)
+
+ 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);
+ }
+ }
+ }
+
+ private void prefixDelete(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 (Map.Entry<IOFSwitch, SwitchPort> src : gatewaySwitches.entrySet()){
+ for (GatewayRouter dstRouter : gatewayRouters){
+ SwitchPort routerAttachmentPoint = dstRouter.getAttachmentPoint();
+ for (Map.Entry<IOFSwitch, SwitchPort> src : gatewaySwitches.entrySet()) {
+ //List<IOFSwitch> switches = new ArrayList<IOFSwitch>(gatewaySwitches.keySet());
+
+ 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?
+ }
+
+ /*
+ List<FlowEntry> flowEntries = shortestPath.flowEntries();
+
+ for (FlowEntry e : shortestPath.flowEntries()){
+ log.debug("fish: {}" , e.dpid().toString());
+ }*/
+
+ //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 +550,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 +576,7 @@
if (change) {
//RestClient.get ("http://localhost:5000/topo_change");
}
+ */
+ calculateFullMesh();
}
}
diff --git a/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResource.java b/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResource.java
index fa9d577..f6d5616 100644
--- a/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResource.java
+++ b/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResource.java
@@ -28,8 +28,7 @@
//@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().
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..dcb5500
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/bgproute/GatewayRouter.java
@@ -0,0 +1,29 @@
+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;
+
+ public GatewayRouter(SwitchPort attachmentPoint, MACAddress routerMac, IPv4 routerIp) {
+ this.attachmentPoint = attachmentPoint;
+ this.routerMac = routerMac;
+ this.routerIp = routerIp;
+ }
+
+ public SwitchPort getAttachmentPoint() {
+ return attachmentPoint;
+ }
+
+ public MACAddress getRouterMac() {
+ return routerMac;
+ }
+
+ public IPv4 getRouterIp() {
+ return routerIp;
+ }
+}
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");
}