blob: cdabd58e120bf71c125f753ee348bea3b309b8ec [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;
45import org.openflow.protocol.OFType;
46import org.openflow.protocol.action.OFAction;
47import org.openflow.protocol.action.OFActionDataLayerDestination;
48import org.openflow.protocol.action.OFActionOutput;
Jonathan Hart1236a9b2013-06-18 22:10:05 +120049import org.openflow.util.HexString;
pingping-lina2cbfad2013-03-07 08:39:21 +080050import org.slf4j.Logger;
51import org.slf4j.LoggerFactory;
52
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070053import com.google.common.net.InetAddresses;
54
Jonathan Hart1236a9b2013-06-18 22:10:05 +120055public class BgpRoute implements IFloodlightModule, IBgpRouteService,
56 ITopologyListener, IOFSwitchListener {
pingping-lina2cbfad2013-03-07 08:39:21 +080057
58 protected static Logger log = LoggerFactory.getLogger(BgpRoute.class);
59
60 protected IFloodlightProviderService floodlightProvider;
61 protected ITopologyService topology;
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070062 protected ITopoRouteService topoRouteService;
63 protected IDeviceService devices;
64 protected IRestApiService restApi;
65
pingping-lina2cbfad2013-03-07 08:39:21 +080066 protected static Ptree ptree;
Jonathan Hart61ba9372013-05-19 20:10:29 -070067 protected String bgpdRestIp;
68 protected String routerId;
Jonathan Hartd1f23252013-06-13 15:17:05 +120069 protected String gatewaysFilename = "gateways.json";
pingping-line2a09ca2013-03-23 09:33:58 +080070
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070071 protected Set<InetAddress> routerIpAddresses;
72
73 //We need to identify our flows somehow. But like it says in LearningSwitch.java,
74 //the controller/OS should hand out cookie IDs to prevent conflicts.
75 protected final long APP_COOKIE = 0xa0000000000000L;
76 //Cookie for flows that do L2 forwarding within SDN domain to egress routers
77 protected final long L2_FWD_COOKIE = APP_COOKIE + 1;
78 //Cookie for flows in ingress switches that rewrite the MAC address
79 protected final long MAC_RW_COOKIE = APP_COOKIE + 2;
Jonathan Hart50a8d1e2013-06-06 16:00:47 +120080 //Forwarding uses priority 0, and the mac rewrite entries in ingress switches
81 //need to be higher priority than this otherwise the rewrite may not get done
82 protected final short SDNIP_PRIORITY = 10;
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070083
Jonathan Hartd1f23252013-06-13 15:17:05 +120084 protected Map<String, GatewayRouter> gatewayRouters;
Jonathan Hart1236a9b2013-06-18 22:10:05 +120085 protected List<String> switches;
86
87 //True when all switches have connected
88 protected volatile boolean switchesConnected = false;
89 //True when we have a full mesh of shortest paths between gateways
90 protected volatile boolean topologyReady = false;
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070091
Jonathan Hartd1f23252013-06-13 15:17:05 +120092 private void readGatewaysConfiguration(String gatewaysFilename){
93 File gatewaysFile = new File(gatewaysFilename);
94 ObjectMapper mapper = new ObjectMapper();
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070095
Jonathan Hartd1f23252013-06-13 15:17:05 +120096 try {
Jonathan Hart1236a9b2013-06-18 22:10:05 +120097 Configuration config = mapper.readValue(gatewaysFile, Configuration.class);
98
99 gatewayRouters = config.getGateways();
100 switches = config.getSwitches();
101
102 for (String sw : switches){
103 log.debug("Switchjoin {}", sw);
104 }
105
Jonathan Hartd1f23252013-06-13 15:17:05 +1200106 } catch (JsonParseException e) {
107 log.error("Error in JSON file", e);
108 System.exit(1);
109 } catch (JsonMappingException e) {
110 log.error("Error in JSON file", e);
111 System.exit(1);
112 } catch (IOException e) {
113 log.error("Error reading JSON file", e);
114 System.exit(1);
115 }
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700116 }
pingping-lina2cbfad2013-03-07 08:39:21 +0800117
118 @Override
119 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Jonathan Hart61ba9372013-05-19 20:10:29 -0700120 Collection<Class<? extends IFloodlightService>> l
121 = new ArrayList<Class<? extends IFloodlightService>>();
pingping-lina2cbfad2013-03-07 08:39:21 +0800122 l.add(IBgpRouteService.class);
123 return l;
124 }
125
126 @Override
127 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
Jonathan Hart61ba9372013-05-19 20:10:29 -0700128 Map<Class<? extends IFloodlightService>, IFloodlightService> m
129 = new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
pingping-line2a09ca2013-03-23 09:33:58 +0800130 m.put(IBgpRouteService.class, this);
pingping-lina2cbfad2013-03-07 08:39:21 +0800131 return m;
132 }
133
pingping-lina2cbfad2013-03-07 08:39:21 +0800134 @Override
135 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Jonathan Hart61ba9372013-05-19 20:10:29 -0700136 Collection<Class<? extends IFloodlightService>> l
137 = new ArrayList<Class<? extends IFloodlightService>>();
pingping-lina2cbfad2013-03-07 08:39:21 +0800138 l.add(IFloodlightProviderService.class);
139 l.add(ITopologyService.class);
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700140 l.add(ITopoRouteService.class);
141 l.add(IDeviceService.class);
142 l.add(IRestApiService.class);
pingping-lina2cbfad2013-03-07 08:39:21 +0800143 return l;
144 }
145
146 @Override
147 public void init(FloodlightModuleContext context)
148 throws FloodlightModuleException {
149
150 ptree = new Ptree(32);
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700151
152 routerIpAddresses = new HashSet<InetAddress>();
pingping-lina2cbfad2013-03-07 08:39:21 +0800153
154 // Register floodlight provider and REST handler.
155 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
pingping-lina2cbfad2013-03-07 08:39:21 +0800156 topology = context.getServiceImpl(ITopologyService.class);
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700157 topoRouteService = context.getServiceImpl(ITopoRouteService.class);
158 devices = context.getServiceImpl(IDeviceService.class);
159 restApi = context.getServiceImpl(IRestApiService.class);
pingping-lina2cbfad2013-03-07 08:39:21 +0800160
Jonathan Hart61ba9372013-05-19 20:10:29 -0700161 //Read in config values
162 bgpdRestIp = context.getConfigParams(this).get("BgpdRestIp");
163 if (bgpdRestIp == null){
164 log.error("BgpdRestIp property not found in config file");
165 System.exit(1);
166 }
167 else {
168 log.info("BgpdRestIp set to {}", bgpdRestIp);
169 }
170
171 routerId = context.getConfigParams(this).get("RouterId");
172 if (routerId == null){
173 log.error("RouterId property not found in config file");
174 System.exit(1);
175 }
176 else {
177 log.info("RouterId set to {}", routerId);
178 }
Jonathan Hartd1f23252013-06-13 15:17:05 +1200179
180 readGatewaysConfiguration(gatewaysFilename);
pingping-lina2cbfad2013-03-07 08:39:21 +0800181 // Test.
182 //test();
183 }
184
185 public Ptree getPtree() {
186 return ptree;
187 }
Jonathan Hart61ba9372013-05-19 20:10:29 -0700188
189 public void clearPtree() {
190 //ptree = null;
191 ptree = new Ptree(32);
pingping-line2a09ca2013-03-23 09:33:58 +0800192 }
Jonathan Hart61ba9372013-05-19 20:10:29 -0700193
pingping-line2a09ca2013-03-23 09:33:58 +0800194 public String getBGPdRestIp() {
Jonathan Hart61ba9372013-05-19 20:10:29 -0700195 return bgpdRestIp;
pingping-line2a09ca2013-03-23 09:33:58 +0800196 }
Jonathan Hart61ba9372013-05-19 20:10:29 -0700197
pingping-line2a09ca2013-03-23 09:33:58 +0800198 public String getRouterId() {
Jonathan Hart61ba9372013-05-19 20:10:29 -0700199 return routerId;
pingping-line2a09ca2013-03-23 09:33:58 +0800200 }
pingping-lina2cbfad2013-03-07 08:39:21 +0800201
202 // Return nexthop address as byte array.
203 public Rib lookupRib(byte[] dest) {
204 if (ptree == null) {
205 log.debug("lookupRib: ptree null");
206 return null;
207 }
208
209 PtreeNode node = ptree.match(dest, 32);
210 if (node == null) {
211 log.debug("lookupRib: ptree node null");
212 return null;
213 }
Jonathan Hart61ba9372013-05-19 20:10:29 -0700214
pingping-lina2cbfad2013-03-07 08:39:21 +0800215 if (node.rib == null) {
216 log.debug("lookupRib: ptree rib null");
217 return null;
218 }
Jonathan Hart61ba9372013-05-19 20:10:29 -0700219
pingping-lina2cbfad2013-03-07 08:39:21 +0800220 ptree.delReference(node);
221
222 return node.rib;
223 }
224
Jonathan Hart61ba9372013-05-19 20:10:29 -0700225 //TODO looks like this should be a unit test
pingping-lina2cbfad2013-03-07 08:39:21 +0800226 @SuppressWarnings("unused")
Jonathan Hart61ba9372013-05-19 20:10:29 -0700227 private void test() throws UnknownHostException {
pingping-lina2cbfad2013-03-07 08:39:21 +0800228 System.out.println("Here it is");
229 Prefix p = new Prefix("128.0.0.0", 8);
230 Prefix q = new Prefix("8.0.0.0", 8);
231 Prefix r = new Prefix("10.0.0.0", 24);
232 Prefix a = new Prefix("10.0.0.1", 32);
233
234 ptree.acquire(p.getAddress(), p.masklen);
235 ptree.acquire(q.getAddress(), q.masklen);
236 ptree.acquire(r.getAddress(), r.masklen);
237
238 System.out.println("Traverse start");
239 for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
240 Prefix p_result = new Prefix(node.key, node.keyBits);
241 }
242
243 PtreeNode n = ptree.match(a.getAddress(), a.masklen);
244 if (n != null) {
245 System.out.println("Matched prefix for 10.0.0.1:");
246 Prefix x = new Prefix(n.key, n.keyBits);
247 ptree.delReference(n);
248 }
249
250 n = ptree.lookup(p.getAddress(), p.masklen);
251 if (n != null) {
252 ptree.delReference(n);
253 ptree.delReference(n);
254 }
255 System.out.println("Traverse start");
256 for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
257 Prefix p_result = new Prefix(node.key, node.keyBits);
258 }
259
260 n = ptree.lookup(q.getAddress(), q.masklen);
261 if (n != null) {
262 ptree.delReference(n);
263 ptree.delReference(n);
264 }
265 System.out.println("Traverse start");
266 for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
267 Prefix p_result = new Prefix(node.key, node.keyBits);
268 }
269
270 n = ptree.lookup(r.getAddress(), r.masklen);
271 if (n != null) {
272 ptree.delReference(n);
273 ptree.delReference(n);
274 }
275 System.out.println("Traverse start");
276 for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
277 Prefix p_result = new Prefix(node.key, node.keyBits);
278 }
279
280 }
281
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200282 private String getPrefixFromPtree(PtreeNode node){
283 InetAddress address = null;
284 try {
285 address = InetAddress.getByAddress(node.key);
286 } catch (UnknownHostException e1) {
287 //Should never happen is the reverse conversion has already been done
288 log.error("Malformed IP address");
289 return "";
290 }
291 return address.toString() + "/" + node.rib.masklen;
292 }
293
Jonathan Hart61ba9372013-05-19 20:10:29 -0700294 private void retrieveRib(){
295 String url = "http://" + bgpdRestIp + "/wm/bgp/" + routerId;
296 String response = RestClient.get(url);
297
298 if (response.equals("")){
299 return;
300 }
301
302 response = response.replaceAll("\"", "'");
303 JSONObject jsonObj = (JSONObject) JSONSerializer.toJSON(response);
304 JSONArray rib_json_array = jsonObj.getJSONArray("rib");
305 String router_id = jsonObj.getString("router-id");
306
307 int size = rib_json_array.size();
308
309 log.info("Retrived RIB of {} entries from BGPd", size);
310
311 for (int j = 0; j < size; j++) {
312 JSONObject second_json_object = rib_json_array.getJSONObject(j);
313 String prefix = second_json_object.getString("prefix");
314 String nexthop = second_json_object.getString("nexthop");
315
316 //insert each rib entry into the local rib;
317 String[] substring = prefix.split("/");
318 String prefix1 = substring[0];
319 String mask1 = substring[1];
320
321 Prefix p;
322 try {
323 p = new Prefix(prefix1, Integer.valueOf(mask1));
324 } catch (NumberFormatException e) {
325 log.warn("Wrong mask format in RIB JSON: {}", mask1);
326 continue;
327 } catch (UnknownHostException e1) {
328 log.warn("Wrong prefix format in RIB JSON: {}", prefix1);
329 continue;
330 }
331
332 PtreeNode node = ptree.acquire(p.getAddress(), p.masklen);
333 Rib rib = new Rib(router_id, nexthop, p.masklen);
334
335 if (node.rib != null) {
336 node.rib = null;
337 ptree.delReference(node);
338 }
339
340 node.rib = rib;
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700341
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200342 prefixAdded(node);
Jonathan Hart61ba9372013-05-19 20:10:29 -0700343 }
344 }
345
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200346 public void prefixAdded(PtreeNode node) {
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200347 if (!topologyReady){
348 return;
349 }
350
351 String prefix = getPrefixFromPtree(node);
352
353 log.debug("New prefix {} added, next hop {}",
354 prefix, node.rib.nextHop.toString());
355
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700356 //Add a flow to rewrite mac for this prefix to all border switches
Jonathan Hartd1f23252013-06-13 15:17:05 +1200357 GatewayRouter thisRouter = gatewayRouters
358 .get(InetAddresses.toAddrString(node.rib.nextHop));
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700359
360 if (thisRouter == null){
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200361 //TODO local router isn't in gateway list so this will get thrown
362 //Need to work out what to do about local prefixes with next hop 0.0.0.0.
363 log.error("Couldn't find next hop router in router {} in config"
364 , node.rib.nextHop.toString());
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700365 return; //just quit out here? This is probably a configuration error
366 }
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200367
Jonathan Hartd1f23252013-06-13 15:17:05 +1200368 for (GatewayRouter ingressRouter : gatewayRouters.values()){
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700369 if (ingressRouter == thisRouter) {
370 continue;
371 }
372
373 DataPath shortestPath = topoRouteService.getShortestPath(
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200374 ingressRouter.getAttachmentPoint(),
375 thisRouter.getAttachmentPoint());
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700376
377 if (shortestPath == null){
378 log.debug("Shortest path between {} and {} not found",
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200379 ingressRouter.getAttachmentPoint(),
380 thisRouter.getAttachmentPoint());
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700381 return; // just quit here?
382 }
383
384 //TODO check the shortest path against the cached version we
385 //calculated before. If they don't match up that's a problem
386
387 //Set up the flow mod
388 OFFlowMod fm =
389 (OFFlowMod) floodlightProvider.getOFMessageFactory()
390 .getMessage(OFType.FLOW_MOD);
391
392 fm.setIdleTimeout((short)0)
393 .setHardTimeout((short)0)
394 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
395 .setCookie(MAC_RW_COOKIE)
396 .setCommand(OFFlowMod.OFPFC_ADD)
397 //.setMatch(match)
398 //.setActions(actions)
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200399 .setPriority(SDNIP_PRIORITY)
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700400 .setLengthU(OFFlowMod.MINIMUM_LENGTH
401 + OFActionDataLayerDestination.MINIMUM_LENGTH
402 + OFActionOutput.MINIMUM_LENGTH);
403
404 OFMatch match = new OFMatch();
405 match.setDataLayerType(Ethernet.TYPE_IPv4);
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200406 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700407
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200408 match.setDataLayerSource(ingressRouter.getRouterMac().toBytes());
409 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
410
411 //match.setDataLayerDestination(ingressRouter.getSdnRouterMac().toBytes());
412 //match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
413
414 InetAddress address = null;
415 try {
416 address = InetAddress.getByAddress(node.key);
417 } catch (UnknownHostException e1) {
418 //Should never happen is the reverse conversion has already been done
419 log.error("Malformed IP address");
420 return;
421 }
422
423 match.setFromCIDR(address.getHostAddress() + "/" + node.rib.masklen, OFMatch.STR_NW_DST);
424 fm.setMatch(match);
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700425
426 //Set up MAC rewrite action
427 OFActionDataLayerDestination macRewriteAction = new OFActionDataLayerDestination();
428 macRewriteAction.setDataLayerAddress(thisRouter.getRouterMac().toBytes());
429
430 //Set up output action
431 OFActionOutput outputAction = new OFActionOutput();
432 outputAction.setMaxLength((short)0xffff); //TODO check what this is (and if needed for mac rewrite)
433
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200434 Port outputPort = shortestPath.flowEntries().get(0).outPort();
435 outputAction.setPort(outputPort.value());
436
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700437 List<OFAction> actions = new ArrayList<OFAction>();
438 actions.add(macRewriteAction);
439 actions.add(outputAction);
440 fm.setActions(actions);
441
442 //Write to switch
443 IOFSwitch sw = floodlightProvider.getSwitches()
444 .get(ingressRouter.getAttachmentPoint().dpid().value());
445
446 if (sw == null){
447 log.warn("Switch not found when pushing flow mod");
448 continue;
449 }
450
451 List<OFMessage> msglist = new ArrayList<OFMessage>();
452 msglist.add(fm);
453 try {
454 sw.write(msglist, null);
455 sw.flush();
456 } catch (IOException e) {
457 log.error("Failure writing flow mod", e);
458 }
459 }
460 }
461
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200462 public void prefixDeleted(PtreeNode node) {
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200463 if (!topologyReady) {
464 return;
465 }
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200466
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200467 String prefix = getPrefixFromPtree(node);
468
469 log.debug("Prefix {} deleted, next hop {}",
470 prefix, node.rib.nextHop.toString());
471
472 //Remove MAC rewriting flows from other border switches
473 GatewayRouter thisRouter = gatewayRouters
474 .get(InetAddresses.toAddrString(node.rib.nextHop));
475
476 for (GatewayRouter ingressRouter : gatewayRouters.values()){
477 if (ingressRouter == thisRouter) {
478 continue;
479 }
480
481 //Set up the flow mod
482 OFFlowMod fm =
483 (OFFlowMod) floodlightProvider.getOFMessageFactory()
484 .getMessage(OFType.FLOW_MOD);
485
486 fm.setIdleTimeout((short)0)
487 .setHardTimeout((short)0)
488 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
489 .setCookie(MAC_RW_COOKIE)
490 .setCommand(OFFlowMod.OFPFC_DELETE)
491 //.setMatch(match)
492 //.setActions(actions)
493 .setPriority(SDNIP_PRIORITY)
494 .setLengthU(OFFlowMod.MINIMUM_LENGTH);
495 //+ OFActionDataLayerDestination.MINIMUM_LENGTH
496 //+ OFActionOutput.MINIMUM_LENGTH);
497
498 OFMatch match = new OFMatch();
499 match.setDataLayerType(Ethernet.TYPE_IPv4);
500 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
501
502 match.setDataLayerSource(ingressRouter.getRouterMac().toBytes());
503 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
504
505 //match.setDataLayerDestination(ingressRouter.getSdnRouterMac().toBytes());
506 //match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
507
508 InetAddress address = null;
509 try {
510 address = InetAddress.getByAddress(node.key);
511 } catch (UnknownHostException e1) {
512 //Should never happen is the reverse conversion has already been done
513 log.error("Malformed IP address");
514 return;
515 }
516
517 match.setFromCIDR(address.getHostAddress() + "/" + node.rib.masklen, OFMatch.STR_NW_DST);
518 fm.setMatch(match);
519
520 //Write to switch
521 IOFSwitch sw = floodlightProvider.getSwitches()
522 .get(ingressRouter.getAttachmentPoint().dpid().value());
523
524 if (sw == null){
525 log.warn("Switch not found when pushing flow mod");
526 continue;
527 }
528
529 List<OFMessage> msglist = new ArrayList<OFMessage>();
530 msglist.add(fm);
531 try {
532 sw.write(msglist, null);
533 sw.flush();
534 } catch (IOException e) {
535 log.error("Failure writing flow mod", e);
536 }
537 }
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700538 }
539
540 /*
541 * On startup we need to calculate a full mesh of paths between all gateway
542 * switches
543 */
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200544 private void setupFullMesh(){
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700545 Map<IOFSwitch, SwitchPort> gatewaySwitches = new HashMap<IOFSwitch, SwitchPort>();
546
547 //have to account for switches not being there, paths not being found.
548
Jonathan Hartd1f23252013-06-13 15:17:05 +1200549 for (GatewayRouter router : gatewayRouters.values()){
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700550 SwitchPort switchPort = router.getAttachmentPoint();
551
552 IOFSwitch sw = floodlightProvider.getSwitches().get(switchPort.dpid().value());
553
554 if (sw == null){
555 log.debug("Gateway switch {} not here yet", switchPort.dpid().value());
556 return; // just quit here?
557 }
558
559 //Only need to know 1 external-facing port from each gateway switch
560 //which we can feed into shortest path calculation
561 if (!gatewaySwitches.containsKey(sw)){
562 gatewaySwitches.put(sw, switchPort);
563 }
564
565 }
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700566
567 //For each border router, calculate and install a path from every other
568 //border switch to said border router. However, don't install the entry
569 //in to the first hop switch, as we need to install an entry to rewrite
570 //for each prefix received. This will be done later when prefixes have
571 //actually been received.
572
Jonathan Hartd1f23252013-06-13 15:17:05 +1200573 for (GatewayRouter dstRouter : gatewayRouters.values()){
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700574 SwitchPort routerAttachmentPoint = dstRouter.getAttachmentPoint();
575 for (Map.Entry<IOFSwitch, SwitchPort> src : gatewaySwitches.entrySet()) {
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700576
577 if (routerAttachmentPoint.dpid().value() ==
578 src.getKey().getId()){
579 continue;
580 }
581
582 DataPath shortestPath = topoRouteService.getShortestPath(
583 src.getValue(), routerAttachmentPoint);
584
585 if (shortestPath == null){
586 log.debug("Shortest path between {} and {} not found",
587 src.getValue(), routerAttachmentPoint);
588 return; // just quit here?
589 }
590
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700591 //install flows
592 installPath(shortestPath.flowEntries(), dstRouter);
593 }
594 }
595 }
596
597 private void installPath(List<FlowEntry> flowEntries, GatewayRouter router){
598
599 //Set up the flow mod
600 OFFlowMod fm =
601 (OFFlowMod) floodlightProvider.getOFMessageFactory()
602 .getMessage(OFType.FLOW_MOD);
603
604 OFActionOutput action = new OFActionOutput();
605 action.setMaxLength((short)0xffff);
606 List<OFAction> actions = new ArrayList<OFAction>();
607 actions.add(action);
608
609 fm.setIdleTimeout((short)0)
610 .setHardTimeout((short)0)
611 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
612 .setCookie(L2_FWD_COOKIE)
613 .setCommand(OFFlowMod.OFPFC_ADD)
614 //.setMatch(match)
615 .setActions(actions)
616 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
617
618 //Don't push the first flow entry. We need to push entries in the
619 //first switch based on IP prefix which we don't know yet.
620 for (int i = 1; i < flowEntries.size(); i++){
621 FlowEntry flowEntry = flowEntries.get(i);
622
623 OFMatch match = new OFMatch();
624 match.setDataLayerDestination(router.getRouterMac().toBytes());
625 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
626 ((OFActionOutput) fm.getActions().get(0)).setPort(flowEntry.outPort().value());
627
628 fm.setMatch(match);
629
630 IOFSwitch sw = floodlightProvider.getSwitches().get(flowEntry.dpid().value());
631
632 if (sw == null){
633 log.warn("Switch not found when pushing flow mod");
634 continue;
635 }
636
637 List<OFMessage> msglist = new ArrayList<OFMessage>();
638 msglist.add(fm);
639 try {
640 sw.write(msglist, null);
641 sw.flush();
642 } catch (IOException e) {
643 log.error("Failure writing flow mod", e);
644 }
645
646 try {
647 fm = fm.clone();
648 } catch (CloneNotSupportedException e1) {
649 log.error("Failure cloning flow mod", e1);
650 }
651 }
652 }
653
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200654
655 private void beginRouting(){
656 log.debug("Topology is now ready, beginning routing function");
657
658 //traverse ptree and create flows for all routes
659 for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)){
660 if (node.rib != null){
661 prefixAdded(node);
662 }
663 }
664 }
665
666 private void checkSwitchesConnected(){
667 for (String dpid : switches){
668 if (floodlightProvider.getSwitches().get(HexString.toLong(dpid)) == null){
669 log.debug("Not all switches are here yet");
670 return;
671 }
672 }
673 switchesConnected = true;
674 }
675
676 private void checkTopologyReady(){
677 for (GatewayRouter dstRouter : gatewayRouters.values()){
678 SwitchPort dstAttachmentPoint = dstRouter.getAttachmentPoint();
679 for (GatewayRouter srcRouter : gatewayRouters.values()) {
680
681 if (dstRouter == srcRouter){
682 continue;
683 }
684
685 SwitchPort srcAttachmentPoint = srcRouter.getAttachmentPoint();
686
687 DataPath shortestPath = topoRouteService.getShortestPath(
688 srcAttachmentPoint, dstAttachmentPoint);
689
690 if (shortestPath == null){
691 log.debug("Shortest path between {} and {} not found",
692 srcAttachmentPoint, dstAttachmentPoint);
693 return;
694 }
695 }
696 }
697 topologyReady = true;
698 }
699
700 private void checkStatus(){
701 log.debug("In checkStatus, swC {}, toRe {}", switchesConnected, topologyReady);
702
703 if (!switchesConnected){
704 checkSwitchesConnected();
705 }
706 boolean oldTopologyReadyStatus = topologyReady;
707 if (switchesConnected && !topologyReady){
708 checkTopologyReady();
709 }
710 if (!oldTopologyReadyStatus && topologyReady){
711 beginRouting();
712 }
713 }
714
pingping-lina2cbfad2013-03-07 08:39:21 +0800715 @Override
716 public void startUp(FloodlightModuleContext context) {
pingping-line2a09ca2013-03-23 09:33:58 +0800717 restApi.addRestletRoutable(new BgpRouteWebRoutable());
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200718 floodlightProvider.addOFSwitchListener(this);
Jonathan Hart61ba9372013-05-19 20:10:29 -0700719 topology.addListener(this);
pingping-line2a09ca2013-03-23 09:33:58 +0800720
Jonathan Hart61ba9372013-05-19 20:10:29 -0700721 //Retrieve the RIB from BGPd during startup
722 retrieveRib();
pingping-lina2cbfad2013-03-07 08:39:21 +0800723 }
724
725 @Override
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200726 public void topologyChanged() {
727 //There seems to be more topology events than there should be. Lots of link
728 //updated, port up and switch updated on what should be a fairly static topology
pingping-lina2cbfad2013-03-07 08:39:21 +0800729
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200730 boolean refreshNeeded = false;
731 for (LDUpdate ldu : topology.getLastLinkUpdates()){
732 if (!ldu.getOperation().equals(ILinkDiscovery.UpdateOperation.LINK_UPDATED)){
733 //We don't need to recalculate anything for just link updates
734 //They happen way too frequently (may be a bug in our link discovery)
735 refreshNeeded = true;
736 }
737 log.debug("Topo change {}", ldu.getOperation());
738 }
739
740 if (refreshNeeded){
741 if (topologyReady){
742 setupFullMesh();
743 }
744 else{
745 checkStatus();
pingping-lina2cbfad2013-03-07 08:39:21 +0800746 }
747 }
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200748 }
pingping-lina2cbfad2013-03-07 08:39:21 +0800749
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200750 //TODO determine whether we need to listen for switch joins
751 @Override
752 public void addedSwitch(IOFSwitch sw) {
753 //checkStatus();
754 }
755
756 @Override
757 public void removedSwitch(IOFSwitch sw) {
758 // TODO Auto-generated method stub
759 }
760
761 @Override
762 public void switchPortChanged(Long switchId) {}
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200763
764 @Override
765 public String getName() {
766 return "BgpRoute";
pingping-lina2cbfad2013-03-07 08:39:21 +0800767 }
768}