blob: 1f3939a726bfe61ed6ee2d7d256ae21661d0cc1f [file] [log] [blame]
HIGUCHI Yutaea60e5f2013-06-12 11:10:21 -07001package net.onrc.onos.ofcontroller.bgproute;
pingping-lina2cbfad2013-03-07 08:39:21 +08002
Jonathan Hartd1f23252013-06-13 15:17:05 +12003import java.io.File;
Jonathan Harte7e1c6e2013-06-04 20:50:23 -07004import java.io.IOException;
5import java.net.InetAddress;
Jonathan Hart61ba9372013-05-19 20:10:29 -07006import java.net.UnknownHostException;
pingping-lina2cbfad2013-03-07 08:39:21 +08007import java.util.ArrayList;
Jonathan Hart61ba9372013-05-19 20:10:29 -07008import java.util.Collection;
pingping-lina2cbfad2013-03-07 08:39:21 +08009import java.util.HashMap;
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070010import java.util.HashSet;
11import java.util.List;
Jonathan Hart61ba9372013-05-19 20:10:29 -070012import java.util.Map;
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070013import java.util.Set;
pingping-lina2cbfad2013-03-07 08:39:21 +080014
Jonathan Hart61ba9372013-05-19 20:10:29 -070015import net.floodlightcontroller.core.IFloodlightProviderService;
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070016import net.floodlightcontroller.core.IOFSwitch;
Jonathan Hart1236a9b2013-06-18 22:10:05 +120017import net.floodlightcontroller.core.IOFSwitchListener;
pingping-lina2cbfad2013-03-07 08:39:21 +080018import net.floodlightcontroller.core.module.FloodlightModuleContext;
19import net.floodlightcontroller.core.module.FloodlightModuleException;
20import net.floodlightcontroller.core.module.IFloodlightModule;
21import net.floodlightcontroller.core.module.IFloodlightService;
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070022import net.floodlightcontroller.devicemanager.IDeviceService;
Jonathan Hart1236a9b2013-06-18 22:10:05 +120023import net.floodlightcontroller.linkdiscovery.ILinkDiscovery;
Jonathan Hart50a8d1e2013-06-06 16:00:47 +120024import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.LDUpdate;
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070025import net.floodlightcontroller.packet.Ethernet;
pingping-lina2cbfad2013-03-07 08:39:21 +080026import net.floodlightcontroller.restserver.IRestApiService;
27import net.floodlightcontroller.topology.ITopologyListener;
28import net.floodlightcontroller.topology.ITopologyService;
HIGUCHI Yuta20514902013-06-12 11:24:16 -070029import net.onrc.onos.ofcontroller.core.INetMapTopologyService.ITopoRouteService;
HIGUCHI Yuta356086e2013-06-12 15:21:19 -070030import net.onrc.onos.ofcontroller.util.DataPath;
HIGUCHI Yuta356086e2013-06-12 15:21:19 -070031import net.onrc.onos.ofcontroller.util.FlowEntry;
HIGUCHI Yuta356086e2013-06-12 15:21:19 -070032import net.onrc.onos.ofcontroller.util.Port;
33import net.onrc.onos.ofcontroller.util.SwitchPort;
pingping-line2a09ca2013-03-23 09:33:58 +080034import net.sf.json.JSONArray;
35import net.sf.json.JSONObject;
36import net.sf.json.JSONSerializer;
pingping-lina2cbfad2013-03-07 08:39:21 +080037
Jonathan Hartd1f23252013-06-13 15:17:05 +120038import org.codehaus.jackson.JsonParseException;
39import org.codehaus.jackson.map.JsonMappingException;
40import org.codehaus.jackson.map.ObjectMapper;
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070041import org.openflow.protocol.OFFlowMod;
42import org.openflow.protocol.OFMatch;
43import org.openflow.protocol.OFMessage;
44import org.openflow.protocol.OFPacketOut;
Jonathan Hart1236a9b2013-06-18 22:10:05 +120045import org.openflow.protocol.OFPhysicalPort;
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070046import org.openflow.protocol.OFType;
47import org.openflow.protocol.action.OFAction;
48import org.openflow.protocol.action.OFActionDataLayerDestination;
49import org.openflow.protocol.action.OFActionOutput;
Jonathan Hart1236a9b2013-06-18 22:10:05 +120050import org.openflow.util.HexString;
pingping-lina2cbfad2013-03-07 08:39:21 +080051import org.slf4j.Logger;
52import org.slf4j.LoggerFactory;
53
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070054import com.google.common.net.InetAddresses;
55
Jonathan Hart1236a9b2013-06-18 22:10:05 +120056public class BgpRoute implements IFloodlightModule, IBgpRouteService,
57 ITopologyListener, IOFSwitchListener {
pingping-lina2cbfad2013-03-07 08:39:21 +080058
59 protected static Logger log = LoggerFactory.getLogger(BgpRoute.class);
60
61 protected IFloodlightProviderService floodlightProvider;
62 protected ITopologyService topology;
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070063 protected ITopoRouteService topoRouteService;
64 protected IDeviceService devices;
65 protected IRestApiService restApi;
66
pingping-lina2cbfad2013-03-07 08:39:21 +080067 protected static Ptree ptree;
Jonathan Hart61ba9372013-05-19 20:10:29 -070068 protected String bgpdRestIp;
69 protected String routerId;
Jonathan Hartd1f23252013-06-13 15:17:05 +120070 protected String gatewaysFilename = "gateways.json";
pingping-line2a09ca2013-03-23 09:33:58 +080071
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070072 protected Set<InetAddress> routerIpAddresses;
73
74 //We need to identify our flows somehow. But like it says in LearningSwitch.java,
75 //the controller/OS should hand out cookie IDs to prevent conflicts.
76 protected final long APP_COOKIE = 0xa0000000000000L;
77 //Cookie for flows that do L2 forwarding within SDN domain to egress routers
78 protected final long L2_FWD_COOKIE = APP_COOKIE + 1;
79 //Cookie for flows in ingress switches that rewrite the MAC address
80 protected final long MAC_RW_COOKIE = APP_COOKIE + 2;
Jonathan Hart50a8d1e2013-06-06 16:00:47 +120081 //Forwarding uses priority 0, and the mac rewrite entries in ingress switches
82 //need to be higher priority than this otherwise the rewrite may not get done
83 protected final short SDNIP_PRIORITY = 10;
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070084
Jonathan Hartd1f23252013-06-13 15:17:05 +120085 protected Map<String, GatewayRouter> gatewayRouters;
Jonathan Hart1236a9b2013-06-18 22:10:05 +120086 protected List<String> switches;
87
88 //True when all switches have connected
89 protected volatile boolean switchesConnected = false;
90 //True when we have a full mesh of shortest paths between gateways
91 protected volatile boolean topologyReady = false;
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070092
Jonathan Hartd1f23252013-06-13 15:17:05 +120093 private void readGatewaysConfiguration(String gatewaysFilename){
94 File gatewaysFile = new File(gatewaysFilename);
95 ObjectMapper mapper = new ObjectMapper();
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070096
Jonathan Hartd1f23252013-06-13 15:17:05 +120097 try {
Jonathan Hart1236a9b2013-06-18 22:10:05 +120098 Configuration config = mapper.readValue(gatewaysFile, Configuration.class);
99
100 gatewayRouters = config.getGateways();
101 switches = config.getSwitches();
102
103 for (String sw : switches){
104 log.debug("Switchjoin {}", sw);
105 }
106
Jonathan Hartd1f23252013-06-13 15:17:05 +1200107 } catch (JsonParseException e) {
108 log.error("Error in JSON file", e);
109 System.exit(1);
110 } catch (JsonMappingException e) {
111 log.error("Error in JSON file", e);
112 System.exit(1);
113 } catch (IOException e) {
114 log.error("Error reading JSON file", e);
115 System.exit(1);
116 }
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700117 }
pingping-lina2cbfad2013-03-07 08:39:21 +0800118
119 @Override
120 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Jonathan Hart61ba9372013-05-19 20:10:29 -0700121 Collection<Class<? extends IFloodlightService>> l
122 = new ArrayList<Class<? extends IFloodlightService>>();
pingping-lina2cbfad2013-03-07 08:39:21 +0800123 l.add(IBgpRouteService.class);
124 return l;
125 }
126
127 @Override
128 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
Jonathan Hart61ba9372013-05-19 20:10:29 -0700129 Map<Class<? extends IFloodlightService>, IFloodlightService> m
130 = new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
pingping-line2a09ca2013-03-23 09:33:58 +0800131 m.put(IBgpRouteService.class, this);
pingping-lina2cbfad2013-03-07 08:39:21 +0800132 return m;
133 }
134
pingping-lina2cbfad2013-03-07 08:39:21 +0800135 @Override
136 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Jonathan Hart61ba9372013-05-19 20:10:29 -0700137 Collection<Class<? extends IFloodlightService>> l
138 = new ArrayList<Class<? extends IFloodlightService>>();
pingping-lina2cbfad2013-03-07 08:39:21 +0800139 l.add(IFloodlightProviderService.class);
140 l.add(ITopologyService.class);
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700141 l.add(ITopoRouteService.class);
142 l.add(IDeviceService.class);
143 l.add(IRestApiService.class);
pingping-lina2cbfad2013-03-07 08:39:21 +0800144 return l;
145 }
146
147 @Override
148 public void init(FloodlightModuleContext context)
149 throws FloodlightModuleException {
150
151 ptree = new Ptree(32);
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700152
153 routerIpAddresses = new HashSet<InetAddress>();
pingping-lina2cbfad2013-03-07 08:39:21 +0800154
155 // Register floodlight provider and REST handler.
156 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
pingping-lina2cbfad2013-03-07 08:39:21 +0800157 topology = context.getServiceImpl(ITopologyService.class);
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700158 topoRouteService = context.getServiceImpl(ITopoRouteService.class);
159 devices = context.getServiceImpl(IDeviceService.class);
160 restApi = context.getServiceImpl(IRestApiService.class);
pingping-lina2cbfad2013-03-07 08:39:21 +0800161
Jonathan Hart61ba9372013-05-19 20:10:29 -0700162 //Read in config values
163 bgpdRestIp = context.getConfigParams(this).get("BgpdRestIp");
164 if (bgpdRestIp == null){
165 log.error("BgpdRestIp property not found in config file");
166 System.exit(1);
167 }
168 else {
169 log.info("BgpdRestIp set to {}", bgpdRestIp);
170 }
171
172 routerId = context.getConfigParams(this).get("RouterId");
173 if (routerId == null){
174 log.error("RouterId property not found in config file");
175 System.exit(1);
176 }
177 else {
178 log.info("RouterId set to {}", routerId);
179 }
Jonathan Hartd1f23252013-06-13 15:17:05 +1200180
181 readGatewaysConfiguration(gatewaysFilename);
pingping-lina2cbfad2013-03-07 08:39:21 +0800182 // Test.
183 //test();
184 }
185
186 public Ptree getPtree() {
187 return ptree;
188 }
Jonathan Hart61ba9372013-05-19 20:10:29 -0700189
190 public void clearPtree() {
191 //ptree = null;
192 ptree = new Ptree(32);
pingping-line2a09ca2013-03-23 09:33:58 +0800193 }
Jonathan Hart61ba9372013-05-19 20:10:29 -0700194
pingping-line2a09ca2013-03-23 09:33:58 +0800195 public String getBGPdRestIp() {
Jonathan Hart61ba9372013-05-19 20:10:29 -0700196 return bgpdRestIp;
pingping-line2a09ca2013-03-23 09:33:58 +0800197 }
Jonathan Hart61ba9372013-05-19 20:10:29 -0700198
pingping-line2a09ca2013-03-23 09:33:58 +0800199 public String getRouterId() {
Jonathan Hart61ba9372013-05-19 20:10:29 -0700200 return routerId;
pingping-line2a09ca2013-03-23 09:33:58 +0800201 }
pingping-lina2cbfad2013-03-07 08:39:21 +0800202
203 // Return nexthop address as byte array.
204 public Rib lookupRib(byte[] dest) {
205 if (ptree == null) {
206 log.debug("lookupRib: ptree null");
207 return null;
208 }
209
210 PtreeNode node = ptree.match(dest, 32);
211 if (node == null) {
212 log.debug("lookupRib: ptree node null");
213 return null;
214 }
Jonathan Hart61ba9372013-05-19 20:10:29 -0700215
pingping-lina2cbfad2013-03-07 08:39:21 +0800216 if (node.rib == null) {
217 log.debug("lookupRib: ptree rib null");
218 return null;
219 }
Jonathan Hart61ba9372013-05-19 20:10:29 -0700220
pingping-lina2cbfad2013-03-07 08:39:21 +0800221 ptree.delReference(node);
222
223 return node.rib;
224 }
225
Jonathan Hart61ba9372013-05-19 20:10:29 -0700226 //TODO looks like this should be a unit test
pingping-lina2cbfad2013-03-07 08:39:21 +0800227 @SuppressWarnings("unused")
Jonathan Hart61ba9372013-05-19 20:10:29 -0700228 private void test() throws UnknownHostException {
pingping-lina2cbfad2013-03-07 08:39:21 +0800229 System.out.println("Here it is");
230 Prefix p = new Prefix("128.0.0.0", 8);
231 Prefix q = new Prefix("8.0.0.0", 8);
232 Prefix r = new Prefix("10.0.0.0", 24);
233 Prefix a = new Prefix("10.0.0.1", 32);
234
235 ptree.acquire(p.getAddress(), p.masklen);
236 ptree.acquire(q.getAddress(), q.masklen);
237 ptree.acquire(r.getAddress(), r.masklen);
238
239 System.out.println("Traverse start");
240 for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
241 Prefix p_result = new Prefix(node.key, node.keyBits);
242 }
243
244 PtreeNode n = ptree.match(a.getAddress(), a.masklen);
245 if (n != null) {
246 System.out.println("Matched prefix for 10.0.0.1:");
247 Prefix x = new Prefix(n.key, n.keyBits);
248 ptree.delReference(n);
249 }
250
251 n = ptree.lookup(p.getAddress(), p.masklen);
252 if (n != null) {
253 ptree.delReference(n);
254 ptree.delReference(n);
255 }
256 System.out.println("Traverse start");
257 for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
258 Prefix p_result = new Prefix(node.key, node.keyBits);
259 }
260
261 n = ptree.lookup(q.getAddress(), q.masklen);
262 if (n != null) {
263 ptree.delReference(n);
264 ptree.delReference(n);
265 }
266 System.out.println("Traverse start");
267 for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
268 Prefix p_result = new Prefix(node.key, node.keyBits);
269 }
270
271 n = ptree.lookup(r.getAddress(), r.masklen);
272 if (n != null) {
273 ptree.delReference(n);
274 ptree.delReference(n);
275 }
276 System.out.println("Traverse start");
277 for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
278 Prefix p_result = new Prefix(node.key, node.keyBits);
279 }
280
281 }
282
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200283 private String getPrefixFromPtree(PtreeNode node){
284 InetAddress address = null;
285 try {
286 address = InetAddress.getByAddress(node.key);
287 } catch (UnknownHostException e1) {
288 //Should never happen is the reverse conversion has already been done
289 log.error("Malformed IP address");
290 return "";
291 }
292 return address.toString() + "/" + node.rib.masklen;
293 }
294
Jonathan Hart61ba9372013-05-19 20:10:29 -0700295 private void retrieveRib(){
296 String url = "http://" + bgpdRestIp + "/wm/bgp/" + routerId;
297 String response = RestClient.get(url);
298
299 if (response.equals("")){
300 return;
301 }
302
303 response = response.replaceAll("\"", "'");
304 JSONObject jsonObj = (JSONObject) JSONSerializer.toJSON(response);
305 JSONArray rib_json_array = jsonObj.getJSONArray("rib");
306 String router_id = jsonObj.getString("router-id");
307
308 int size = rib_json_array.size();
309
310 log.info("Retrived RIB of {} entries from BGPd", size);
311
312 for (int j = 0; j < size; j++) {
313 JSONObject second_json_object = rib_json_array.getJSONObject(j);
314 String prefix = second_json_object.getString("prefix");
315 String nexthop = second_json_object.getString("nexthop");
316
317 //insert each rib entry into the local rib;
318 String[] substring = prefix.split("/");
319 String prefix1 = substring[0];
320 String mask1 = substring[1];
321
322 Prefix p;
323 try {
324 p = new Prefix(prefix1, Integer.valueOf(mask1));
325 } catch (NumberFormatException e) {
326 log.warn("Wrong mask format in RIB JSON: {}", mask1);
327 continue;
328 } catch (UnknownHostException e1) {
329 log.warn("Wrong prefix format in RIB JSON: {}", prefix1);
330 continue;
331 }
332
333 PtreeNode node = ptree.acquire(p.getAddress(), p.masklen);
334 Rib rib = new Rib(router_id, nexthop, p.masklen);
335
336 if (node.rib != null) {
337 node.rib = null;
338 ptree.delReference(node);
339 }
340
341 node.rib = rib;
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700342
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200343 prefixAdded(node);
Jonathan Hart61ba9372013-05-19 20:10:29 -0700344 }
345 }
346
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200347 public void prefixAdded(PtreeNode node) {
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200348 if (!topologyReady){
349 return;
350 }
351
352 String prefix = getPrefixFromPtree(node);
353
354 log.debug("New prefix {} added, next hop {}",
355 prefix, node.rib.nextHop.toString());
356
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700357 //Add a flow to rewrite mac for this prefix to all border switches
Jonathan Hartd1f23252013-06-13 15:17:05 +1200358 GatewayRouter thisRouter = gatewayRouters
359 .get(InetAddresses.toAddrString(node.rib.nextHop));
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700360
361 if (thisRouter == null){
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200362 //TODO local router isn't in gateway list so this will get thrown
363 //Need to work out what to do about local prefixes with next hop 0.0.0.0.
364 log.error("Couldn't find next hop router in router {} in config"
365 , node.rib.nextHop.toString());
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700366 return; //just quit out here? This is probably a configuration error
367 }
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200368
Jonathan Hartd1f23252013-06-13 15:17:05 +1200369 for (GatewayRouter ingressRouter : gatewayRouters.values()){
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700370 if (ingressRouter == thisRouter) {
371 continue;
372 }
373
374 DataPath shortestPath = topoRouteService.getShortestPath(
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200375 ingressRouter.getAttachmentPoint(),
376 thisRouter.getAttachmentPoint());
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700377
378 if (shortestPath == null){
379 log.debug("Shortest path between {} and {} not found",
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200380 ingressRouter.getAttachmentPoint(),
381 thisRouter.getAttachmentPoint());
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700382 return; // just quit here?
383 }
384
385 //TODO check the shortest path against the cached version we
386 //calculated before. If they don't match up that's a problem
387
388 //Set up the flow mod
389 OFFlowMod fm =
390 (OFFlowMod) floodlightProvider.getOFMessageFactory()
391 .getMessage(OFType.FLOW_MOD);
392
393 fm.setIdleTimeout((short)0)
394 .setHardTimeout((short)0)
395 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
396 .setCookie(MAC_RW_COOKIE)
397 .setCommand(OFFlowMod.OFPFC_ADD)
398 //.setMatch(match)
399 //.setActions(actions)
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200400 .setPriority(SDNIP_PRIORITY)
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700401 .setLengthU(OFFlowMod.MINIMUM_LENGTH
402 + OFActionDataLayerDestination.MINIMUM_LENGTH
403 + OFActionOutput.MINIMUM_LENGTH);
404
405 OFMatch match = new OFMatch();
406 match.setDataLayerType(Ethernet.TYPE_IPv4);
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200407 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700408
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200409 match.setDataLayerSource(ingressRouter.getRouterMac().toBytes());
410 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
411
412 //match.setDataLayerDestination(ingressRouter.getSdnRouterMac().toBytes());
413 //match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
414
415 InetAddress address = null;
416 try {
417 address = InetAddress.getByAddress(node.key);
418 } catch (UnknownHostException e1) {
419 //Should never happen is the reverse conversion has already been done
420 log.error("Malformed IP address");
421 return;
422 }
423
424 match.setFromCIDR(address.getHostAddress() + "/" + node.rib.masklen, OFMatch.STR_NW_DST);
425 fm.setMatch(match);
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700426
427 //Set up MAC rewrite action
428 OFActionDataLayerDestination macRewriteAction = new OFActionDataLayerDestination();
429 macRewriteAction.setDataLayerAddress(thisRouter.getRouterMac().toBytes());
430
431 //Set up output action
432 OFActionOutput outputAction = new OFActionOutput();
433 outputAction.setMaxLength((short)0xffff); //TODO check what this is (and if needed for mac rewrite)
434
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200435 Port outputPort = shortestPath.flowEntries().get(0).outPort();
436 outputAction.setPort(outputPort.value());
437
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700438 List<OFAction> actions = new ArrayList<OFAction>();
439 actions.add(macRewriteAction);
440 actions.add(outputAction);
441 fm.setActions(actions);
442
443 //Write to switch
444 IOFSwitch sw = floodlightProvider.getSwitches()
445 .get(ingressRouter.getAttachmentPoint().dpid().value());
446
447 if (sw == null){
448 log.warn("Switch not found when pushing flow mod");
449 continue;
450 }
451
452 List<OFMessage> msglist = new ArrayList<OFMessage>();
453 msglist.add(fm);
454 try {
455 sw.write(msglist, null);
456 sw.flush();
457 } catch (IOException e) {
458 log.error("Failure writing flow mod", e);
459 }
460 }
461 }
462
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200463 public void prefixDeleted(PtreeNode node) {
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200464 if (!topologyReady) {
465 return;
466 }
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200467
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200468 String prefix = getPrefixFromPtree(node);
469
470 log.debug("Prefix {} deleted, next hop {}",
471 prefix, node.rib.nextHop.toString());
472
473 //Remove MAC rewriting flows from other border switches
474 GatewayRouter thisRouter = gatewayRouters
475 .get(InetAddresses.toAddrString(node.rib.nextHop));
476
477 for (GatewayRouter ingressRouter : gatewayRouters.values()){
478 if (ingressRouter == thisRouter) {
479 continue;
480 }
481
482 //Set up the flow mod
483 OFFlowMod fm =
484 (OFFlowMod) floodlightProvider.getOFMessageFactory()
485 .getMessage(OFType.FLOW_MOD);
486
487 fm.setIdleTimeout((short)0)
488 .setHardTimeout((short)0)
489 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
490 .setCookie(MAC_RW_COOKIE)
491 .setCommand(OFFlowMod.OFPFC_DELETE)
492 //.setMatch(match)
493 //.setActions(actions)
494 .setPriority(SDNIP_PRIORITY)
495 .setLengthU(OFFlowMod.MINIMUM_LENGTH);
496 //+ OFActionDataLayerDestination.MINIMUM_LENGTH
497 //+ OFActionOutput.MINIMUM_LENGTH);
498
499 OFMatch match = new OFMatch();
500 match.setDataLayerType(Ethernet.TYPE_IPv4);
501 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
502
503 match.setDataLayerSource(ingressRouter.getRouterMac().toBytes());
504 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
505
506 //match.setDataLayerDestination(ingressRouter.getSdnRouterMac().toBytes());
507 //match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
508
509 InetAddress address = null;
510 try {
511 address = InetAddress.getByAddress(node.key);
512 } catch (UnknownHostException e1) {
513 //Should never happen is the reverse conversion has already been done
514 log.error("Malformed IP address");
515 return;
516 }
517
518 match.setFromCIDR(address.getHostAddress() + "/" + node.rib.masklen, OFMatch.STR_NW_DST);
519 fm.setMatch(match);
520
521 //Write to switch
522 IOFSwitch sw = floodlightProvider.getSwitches()
523 .get(ingressRouter.getAttachmentPoint().dpid().value());
524
525 if (sw == null){
526 log.warn("Switch not found when pushing flow mod");
527 continue;
528 }
529
530 List<OFMessage> msglist = new ArrayList<OFMessage>();
531 msglist.add(fm);
532 try {
533 sw.write(msglist, null);
534 sw.flush();
535 } catch (IOException e) {
536 log.error("Failure writing flow mod", e);
537 }
538 }
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700539 }
540
541 /*
542 * On startup we need to calculate a full mesh of paths between all gateway
543 * switches
544 */
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200545 private void setupFullMesh(){
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700546 Map<IOFSwitch, SwitchPort> gatewaySwitches = new HashMap<IOFSwitch, SwitchPort>();
547
548 //have to account for switches not being there, paths not being found.
549
Jonathan Hartd1f23252013-06-13 15:17:05 +1200550 for (GatewayRouter router : gatewayRouters.values()){
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700551 SwitchPort switchPort = router.getAttachmentPoint();
552
553 IOFSwitch sw = floodlightProvider.getSwitches().get(switchPort.dpid().value());
554
555 if (sw == null){
556 log.debug("Gateway switch {} not here yet", switchPort.dpid().value());
557 return; // just quit here?
558 }
559
560 //Only need to know 1 external-facing port from each gateway switch
561 //which we can feed into shortest path calculation
562 if (!gatewaySwitches.containsKey(sw)){
563 gatewaySwitches.put(sw, switchPort);
564 }
565
566 }
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700567
568 //For each border router, calculate and install a path from every other
569 //border switch to said border router. However, don't install the entry
570 //in to the first hop switch, as we need to install an entry to rewrite
571 //for each prefix received. This will be done later when prefixes have
572 //actually been received.
573
Jonathan Hartd1f23252013-06-13 15:17:05 +1200574 for (GatewayRouter dstRouter : gatewayRouters.values()){
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700575 SwitchPort routerAttachmentPoint = dstRouter.getAttachmentPoint();
576 for (Map.Entry<IOFSwitch, SwitchPort> src : gatewaySwitches.entrySet()) {
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700577
578 if (routerAttachmentPoint.dpid().value() ==
579 src.getKey().getId()){
580 continue;
581 }
582
583 DataPath shortestPath = topoRouteService.getShortestPath(
584 src.getValue(), routerAttachmentPoint);
585
586 if (shortestPath == null){
587 log.debug("Shortest path between {} and {} not found",
588 src.getValue(), routerAttachmentPoint);
589 return; // just quit here?
590 }
591
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700592 //install flows
593 installPath(shortestPath.flowEntries(), dstRouter);
594 }
595 }
596 }
597
598 private void installPath(List<FlowEntry> flowEntries, GatewayRouter router){
599
600 //Set up the flow mod
601 OFFlowMod fm =
602 (OFFlowMod) floodlightProvider.getOFMessageFactory()
603 .getMessage(OFType.FLOW_MOD);
604
605 OFActionOutput action = new OFActionOutput();
606 action.setMaxLength((short)0xffff);
607 List<OFAction> actions = new ArrayList<OFAction>();
608 actions.add(action);
609
610 fm.setIdleTimeout((short)0)
611 .setHardTimeout((short)0)
612 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
613 .setCookie(L2_FWD_COOKIE)
614 .setCommand(OFFlowMod.OFPFC_ADD)
615 //.setMatch(match)
616 .setActions(actions)
617 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
618
619 //Don't push the first flow entry. We need to push entries in the
620 //first switch based on IP prefix which we don't know yet.
621 for (int i = 1; i < flowEntries.size(); i++){
622 FlowEntry flowEntry = flowEntries.get(i);
623
624 OFMatch match = new OFMatch();
625 match.setDataLayerDestination(router.getRouterMac().toBytes());
626 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
627 ((OFActionOutput) fm.getActions().get(0)).setPort(flowEntry.outPort().value());
628
629 fm.setMatch(match);
630
631 IOFSwitch sw = floodlightProvider.getSwitches().get(flowEntry.dpid().value());
632
633 if (sw == null){
634 log.warn("Switch not found when pushing flow mod");
635 continue;
636 }
637
638 List<OFMessage> msglist = new ArrayList<OFMessage>();
639 msglist.add(fm);
640 try {
641 sw.write(msglist, null);
642 sw.flush();
643 } catch (IOException e) {
644 log.error("Failure writing flow mod", e);
645 }
646
647 try {
648 fm = fm.clone();
649 } catch (CloneNotSupportedException e1) {
650 log.error("Failure cloning flow mod", e1);
651 }
652 }
653 }
654
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200655
656 private void beginRouting(){
657 log.debug("Topology is now ready, beginning routing function");
658
659 //traverse ptree and create flows for all routes
660 for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)){
661 if (node.rib != null){
662 prefixAdded(node);
663 }
664 }
665 }
666
667 private void checkSwitchesConnected(){
668 for (String dpid : switches){
669 if (floodlightProvider.getSwitches().get(HexString.toLong(dpid)) == null){
670 log.debug("Not all switches are here yet");
671 return;
672 }
673 }
674 switchesConnected = true;
675 }
676
677 private void checkTopologyReady(){
678 for (GatewayRouter dstRouter : gatewayRouters.values()){
679 SwitchPort dstAttachmentPoint = dstRouter.getAttachmentPoint();
680 for (GatewayRouter srcRouter : gatewayRouters.values()) {
681
682 if (dstRouter == srcRouter){
683 continue;
684 }
685
686 SwitchPort srcAttachmentPoint = srcRouter.getAttachmentPoint();
687
688 DataPath shortestPath = topoRouteService.getShortestPath(
689 srcAttachmentPoint, dstAttachmentPoint);
690
691 if (shortestPath == null){
692 log.debug("Shortest path between {} and {} not found",
693 srcAttachmentPoint, dstAttachmentPoint);
694 return;
695 }
696 }
697 }
698 topologyReady = true;
699 }
700
701 private void checkStatus(){
702 log.debug("In checkStatus, swC {}, toRe {}", switchesConnected, topologyReady);
703
704 if (!switchesConnected){
705 checkSwitchesConnected();
706 }
707 boolean oldTopologyReadyStatus = topologyReady;
708 if (switchesConnected && !topologyReady){
709 checkTopologyReady();
710 }
711 if (!oldTopologyReadyStatus && topologyReady){
712 beginRouting();
713 }
714 }
715
pingping-lina2cbfad2013-03-07 08:39:21 +0800716 @Override
717 public void startUp(FloodlightModuleContext context) {
pingping-line2a09ca2013-03-23 09:33:58 +0800718 restApi.addRestletRoutable(new BgpRouteWebRoutable());
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200719 floodlightProvider.addOFSwitchListener(this);
Jonathan Hart61ba9372013-05-19 20:10:29 -0700720 topology.addListener(this);
pingping-line2a09ca2013-03-23 09:33:58 +0800721
Jonathan Hart61ba9372013-05-19 20:10:29 -0700722 //Retrieve the RIB from BGPd during startup
723 retrieveRib();
pingping-lina2cbfad2013-03-07 08:39:21 +0800724 }
725
726 @Override
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200727 public void topologyChanged() {
728 //There seems to be more topology events than there should be. Lots of link
729 //updated, port up and switch updated on what should be a fairly static topology
pingping-lina2cbfad2013-03-07 08:39:21 +0800730
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200731 boolean refreshNeeded = false;
732 for (LDUpdate ldu : topology.getLastLinkUpdates()){
733 if (!ldu.getOperation().equals(ILinkDiscovery.UpdateOperation.LINK_UPDATED)){
734 //We don't need to recalculate anything for just link updates
735 //They happen way too frequently (may be a bug in our link discovery)
736 refreshNeeded = true;
737 }
738 log.debug("Topo change {}", ldu.getOperation());
739 }
740
741 if (refreshNeeded){
742 if (topologyReady){
743 setupFullMesh();
744 }
745 else{
746 checkStatus();
pingping-lina2cbfad2013-03-07 08:39:21 +0800747 }
748 }
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200749 }
pingping-lina2cbfad2013-03-07 08:39:21 +0800750
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200751 //TODO determine whether we need to listen for switch joins
752 @Override
753 public void addedSwitch(IOFSwitch sw) {
754 //checkStatus();
755 }
756
757 @Override
758 public void removedSwitch(IOFSwitch sw) {
759 // TODO Auto-generated method stub
760 }
761
762 @Override
763 public void switchPortChanged(Long switchId) {}
764 @Override
765 public void switchPortAdded(Long switchId, OFPhysicalPort port) {}
766 @Override
767 public void switchPortRemoved(Long switchId, OFPhysicalPort port) {}
768
769 @Override
770 public String getName() {
771 return "BgpRoute";
pingping-lina2cbfad2013-03-07 08:39:21 +0800772 }
773}