blob: d3bb5981c1917c784a01b427998caedd597e1983 [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.List;
Jonathan Hart61ba9372013-05-19 20:10:29 -070011import java.util.Map;
pingping-lina2cbfad2013-03-07 08:39:21 +080012
Jonathan Hart61ba9372013-05-19 20:10:29 -070013import net.floodlightcontroller.core.IFloodlightProviderService;
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070014import net.floodlightcontroller.core.IOFSwitch;
Jonathan Hart1236a9b2013-06-18 22:10:05 +120015import net.floodlightcontroller.core.IOFSwitchListener;
pingping-lina2cbfad2013-03-07 08:39:21 +080016import net.floodlightcontroller.core.module.FloodlightModuleContext;
17import net.floodlightcontroller.core.module.FloodlightModuleException;
18import net.floodlightcontroller.core.module.IFloodlightModule;
19import net.floodlightcontroller.core.module.IFloodlightService;
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070020import net.floodlightcontroller.devicemanager.IDeviceService;
21import net.floodlightcontroller.packet.Ethernet;
Jonathan Hart832a7cb2013-06-24 11:25:35 +120022import net.floodlightcontroller.packet.IPv4;
pingping-lina2cbfad2013-03-07 08:39:21 +080023import net.floodlightcontroller.restserver.IRestApiService;
24import net.floodlightcontroller.topology.ITopologyListener;
25import net.floodlightcontroller.topology.ITopologyService;
HIGUCHI Yuta20514902013-06-12 11:24:16 -070026import net.onrc.onos.ofcontroller.core.INetMapTopologyService.ITopoRouteService;
HIGUCHI Yutaa56fbde2013-06-17 14:26:05 -070027import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscovery;
28import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscovery.LDUpdate;
HIGUCHI Yuta356086e2013-06-12 15:21:19 -070029import net.onrc.onos.ofcontroller.util.DataPath;
Jonathan Hart832a7cb2013-06-24 11:25:35 +120030import net.onrc.onos.ofcontroller.util.Dpid;
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 Hart832a7cb2013-06-24 11:25:35 +120069 protected String gatewaysFilename = "config.json";
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070070
71 //We need to identify our flows somehow. But like it says in LearningSwitch.java,
72 //the controller/OS should hand out cookie IDs to prevent conflicts.
73 protected final long APP_COOKIE = 0xa0000000000000L;
74 //Cookie for flows that do L2 forwarding within SDN domain to egress routers
75 protected final long L2_FWD_COOKIE = APP_COOKIE + 1;
76 //Cookie for flows in ingress switches that rewrite the MAC address
77 protected final long MAC_RW_COOKIE = APP_COOKIE + 2;
Jonathan Hart832a7cb2013-06-24 11:25:35 +120078 //Cookie for flows that setup BGP paths
79 protected final long BGP_COOKIE = APP_COOKIE + 3;
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 Hart832a7cb2013-06-24 11:25:35 +120084 protected final short BGP_PORT = 179;
85
86 //Configuration stuff
Jonathan Hartd1f23252013-06-13 15:17:05 +120087 protected Map<String, GatewayRouter> gatewayRouters;
Jonathan Hart1236a9b2013-06-18 22:10:05 +120088 protected List<String> switches;
Jonathan Hart832a7cb2013-06-24 11:25:35 +120089 protected Map<String, Interface> interfaces;
90 protected List<BgpPeer> bgpPeers;
91 //protected long bgpdAttachmentDpid;
92 //protected short bgpdAttachmentPort;
93 protected SwitchPort bgpdAttachmentPoint;
Jonathan Hart1236a9b2013-06-18 22:10:05 +120094
95 //True when all switches have connected
96 protected volatile boolean switchesConnected = false;
97 //True when we have a full mesh of shortest paths between gateways
98 protected volatile boolean topologyReady = false;
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070099
Jonathan Hartd1f23252013-06-13 15:17:05 +1200100 private void readGatewaysConfiguration(String gatewaysFilename){
101 File gatewaysFile = new File(gatewaysFilename);
102 ObjectMapper mapper = new ObjectMapper();
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700103
Jonathan Hartd1f23252013-06-13 15:17:05 +1200104 try {
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200105 Configuration config = mapper.readValue(gatewaysFile, Configuration.class);
106
107 gatewayRouters = config.getGateways();
108 switches = config.getSwitches();
Jonathan Hart832a7cb2013-06-24 11:25:35 +1200109 interfaces = config.getInterfaces();
110 bgpPeers = config.getPeers();
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200111
Jonathan Hart832a7cb2013-06-24 11:25:35 +1200112 bgpdAttachmentPoint = new SwitchPort(
113 new Dpid(config.getBgpdAttachmentDpid()),
114 new Port(config.getBgpdAttachmentPort()));
115 //bgpdAttachmentDpid = config.getBgpdAttachmentDpid();
116 //bgpdAttachmentPort = config.getBgpdAttachmentPort();
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200117
Jonathan Hartd1f23252013-06-13 15:17:05 +1200118 } catch (JsonParseException e) {
119 log.error("Error in JSON file", e);
120 System.exit(1);
121 } catch (JsonMappingException e) {
122 log.error("Error in JSON file", e);
123 System.exit(1);
124 } catch (IOException e) {
125 log.error("Error reading JSON file", e);
126 System.exit(1);
127 }
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700128 }
pingping-lina2cbfad2013-03-07 08:39:21 +0800129
130 @Override
131 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Jonathan Hart61ba9372013-05-19 20:10:29 -0700132 Collection<Class<? extends IFloodlightService>> l
133 = new ArrayList<Class<? extends IFloodlightService>>();
pingping-lina2cbfad2013-03-07 08:39:21 +0800134 l.add(IBgpRouteService.class);
135 return l;
136 }
137
138 @Override
139 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
Jonathan Hart61ba9372013-05-19 20:10:29 -0700140 Map<Class<? extends IFloodlightService>, IFloodlightService> m
141 = new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
pingping-line2a09ca2013-03-23 09:33:58 +0800142 m.put(IBgpRouteService.class, this);
pingping-lina2cbfad2013-03-07 08:39:21 +0800143 return m;
144 }
145
pingping-lina2cbfad2013-03-07 08:39:21 +0800146 @Override
147 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Jonathan Hart61ba9372013-05-19 20:10:29 -0700148 Collection<Class<? extends IFloodlightService>> l
149 = new ArrayList<Class<? extends IFloodlightService>>();
pingping-lina2cbfad2013-03-07 08:39:21 +0800150 l.add(IFloodlightProviderService.class);
151 l.add(ITopologyService.class);
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700152 l.add(ITopoRouteService.class);
153 l.add(IDeviceService.class);
154 l.add(IRestApiService.class);
pingping-lina2cbfad2013-03-07 08:39:21 +0800155 return l;
156 }
157
158 @Override
159 public void init(FloodlightModuleContext context)
160 throws FloodlightModuleException {
161
162 ptree = new Ptree(32);
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700163
Jonathan Hart832a7cb2013-06-24 11:25:35 +1200164 //routerIpAddresses = new HashSet<InetAddress>();
pingping-lina2cbfad2013-03-07 08:39:21 +0800165
166 // Register floodlight provider and REST handler.
167 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
pingping-lina2cbfad2013-03-07 08:39:21 +0800168 topology = context.getServiceImpl(ITopologyService.class);
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700169 topoRouteService = context.getServiceImpl(ITopoRouteService.class);
170 devices = context.getServiceImpl(IDeviceService.class);
171 restApi = context.getServiceImpl(IRestApiService.class);
pingping-lina2cbfad2013-03-07 08:39:21 +0800172
Jonathan Hart61ba9372013-05-19 20:10:29 -0700173 //Read in config values
174 bgpdRestIp = context.getConfigParams(this).get("BgpdRestIp");
175 if (bgpdRestIp == null){
176 log.error("BgpdRestIp property not found in config file");
177 System.exit(1);
178 }
179 else {
180 log.info("BgpdRestIp set to {}", bgpdRestIp);
181 }
182
183 routerId = context.getConfigParams(this).get("RouterId");
184 if (routerId == null){
185 log.error("RouterId property not found in config file");
186 System.exit(1);
187 }
188 else {
189 log.info("RouterId set to {}", routerId);
190 }
Jonathan Hartd1f23252013-06-13 15:17:05 +1200191
192 readGatewaysConfiguration(gatewaysFilename);
pingping-lina2cbfad2013-03-07 08:39:21 +0800193 // Test.
194 //test();
195 }
196
197 public Ptree getPtree() {
198 return ptree;
199 }
Jonathan Hart61ba9372013-05-19 20:10:29 -0700200
201 public void clearPtree() {
202 //ptree = null;
203 ptree = new Ptree(32);
pingping-line2a09ca2013-03-23 09:33:58 +0800204 }
Jonathan Hart61ba9372013-05-19 20:10:29 -0700205
pingping-line2a09ca2013-03-23 09:33:58 +0800206 public String getBGPdRestIp() {
Jonathan Hart61ba9372013-05-19 20:10:29 -0700207 return bgpdRestIp;
pingping-line2a09ca2013-03-23 09:33:58 +0800208 }
Jonathan Hart61ba9372013-05-19 20:10:29 -0700209
pingping-line2a09ca2013-03-23 09:33:58 +0800210 public String getRouterId() {
Jonathan Hart61ba9372013-05-19 20:10:29 -0700211 return routerId;
pingping-line2a09ca2013-03-23 09:33:58 +0800212 }
pingping-lina2cbfad2013-03-07 08:39:21 +0800213
214 // Return nexthop address as byte array.
215 public Rib lookupRib(byte[] dest) {
216 if (ptree == null) {
217 log.debug("lookupRib: ptree null");
218 return null;
219 }
220
221 PtreeNode node = ptree.match(dest, 32);
222 if (node == null) {
223 log.debug("lookupRib: ptree node null");
224 return null;
225 }
Jonathan Hart61ba9372013-05-19 20:10:29 -0700226
pingping-lina2cbfad2013-03-07 08:39:21 +0800227 if (node.rib == null) {
228 log.debug("lookupRib: ptree rib null");
229 return null;
230 }
Jonathan Hart61ba9372013-05-19 20:10:29 -0700231
pingping-lina2cbfad2013-03-07 08:39:21 +0800232 ptree.delReference(node);
233
234 return node.rib;
235 }
236
Jonathan Hart61ba9372013-05-19 20:10:29 -0700237 //TODO looks like this should be a unit test
pingping-lina2cbfad2013-03-07 08:39:21 +0800238 @SuppressWarnings("unused")
Jonathan Hart61ba9372013-05-19 20:10:29 -0700239 private void test() throws UnknownHostException {
pingping-lina2cbfad2013-03-07 08:39:21 +0800240 System.out.println("Here it is");
241 Prefix p = new Prefix("128.0.0.0", 8);
242 Prefix q = new Prefix("8.0.0.0", 8);
243 Prefix r = new Prefix("10.0.0.0", 24);
244 Prefix a = new Prefix("10.0.0.1", 32);
245
246 ptree.acquire(p.getAddress(), p.masklen);
247 ptree.acquire(q.getAddress(), q.masklen);
248 ptree.acquire(r.getAddress(), r.masklen);
249
250 System.out.println("Traverse start");
251 for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
252 Prefix p_result = new Prefix(node.key, node.keyBits);
253 }
254
255 PtreeNode n = ptree.match(a.getAddress(), a.masklen);
256 if (n != null) {
257 System.out.println("Matched prefix for 10.0.0.1:");
258 Prefix x = new Prefix(n.key, n.keyBits);
259 ptree.delReference(n);
260 }
261
262 n = ptree.lookup(p.getAddress(), p.masklen);
263 if (n != null) {
264 ptree.delReference(n);
265 ptree.delReference(n);
266 }
267 System.out.println("Traverse start");
268 for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
269 Prefix p_result = new Prefix(node.key, node.keyBits);
270 }
271
272 n = ptree.lookup(q.getAddress(), q.masklen);
273 if (n != null) {
274 ptree.delReference(n);
275 ptree.delReference(n);
276 }
277 System.out.println("Traverse start");
278 for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
279 Prefix p_result = new Prefix(node.key, node.keyBits);
280 }
281
282 n = ptree.lookup(r.getAddress(), r.masklen);
283 if (n != null) {
284 ptree.delReference(n);
285 ptree.delReference(n);
286 }
287 System.out.println("Traverse start");
288 for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
289 Prefix p_result = new Prefix(node.key, node.keyBits);
290 }
291
292 }
293
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200294 private String getPrefixFromPtree(PtreeNode node){
295 InetAddress address = null;
296 try {
297 address = InetAddress.getByAddress(node.key);
298 } catch (UnknownHostException e1) {
299 //Should never happen is the reverse conversion has already been done
300 log.error("Malformed IP address");
301 return "";
302 }
303 return address.toString() + "/" + node.rib.masklen;
304 }
305
Jonathan Hart61ba9372013-05-19 20:10:29 -0700306 private void retrieveRib(){
307 String url = "http://" + bgpdRestIp + "/wm/bgp/" + routerId;
308 String response = RestClient.get(url);
309
310 if (response.equals("")){
311 return;
312 }
313
314 response = response.replaceAll("\"", "'");
315 JSONObject jsonObj = (JSONObject) JSONSerializer.toJSON(response);
316 JSONArray rib_json_array = jsonObj.getJSONArray("rib");
317 String router_id = jsonObj.getString("router-id");
318
319 int size = rib_json_array.size();
320
321 log.info("Retrived RIB of {} entries from BGPd", size);
322
323 for (int j = 0; j < size; j++) {
324 JSONObject second_json_object = rib_json_array.getJSONObject(j);
325 String prefix = second_json_object.getString("prefix");
326 String nexthop = second_json_object.getString("nexthop");
327
328 //insert each rib entry into the local rib;
329 String[] substring = prefix.split("/");
330 String prefix1 = substring[0];
331 String mask1 = substring[1];
332
333 Prefix p;
334 try {
335 p = new Prefix(prefix1, Integer.valueOf(mask1));
336 } catch (NumberFormatException e) {
337 log.warn("Wrong mask format in RIB JSON: {}", mask1);
338 continue;
339 } catch (UnknownHostException e1) {
340 log.warn("Wrong prefix format in RIB JSON: {}", prefix1);
341 continue;
342 }
343
344 PtreeNode node = ptree.acquire(p.getAddress(), p.masklen);
345 Rib rib = new Rib(router_id, nexthop, p.masklen);
346
347 if (node.rib != null) {
348 node.rib = null;
349 ptree.delReference(node);
350 }
351
352 node.rib = rib;
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700353
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200354 prefixAdded(node);
Jonathan Hart61ba9372013-05-19 20:10:29 -0700355 }
356 }
357
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200358 public void prefixAdded(PtreeNode node) {
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200359 if (!topologyReady){
360 return;
361 }
362
363 String prefix = getPrefixFromPtree(node);
364
365 log.debug("New prefix {} added, next hop {}",
366 prefix, node.rib.nextHop.toString());
367
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700368 //Add a flow to rewrite mac for this prefix to all border switches
Jonathan Hartd1f23252013-06-13 15:17:05 +1200369 GatewayRouter thisRouter = gatewayRouters
370 .get(InetAddresses.toAddrString(node.rib.nextHop));
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700371
372 if (thisRouter == null){
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200373 //TODO local router isn't in gateway list so this will get thrown
374 //Need to work out what to do about local prefixes with next hop 0.0.0.0.
375 log.error("Couldn't find next hop router in router {} in config"
376 , node.rib.nextHop.toString());
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700377 return; //just quit out here? This is probably a configuration error
378 }
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200379
Jonathan Hartd1f23252013-06-13 15:17:05 +1200380 for (GatewayRouter ingressRouter : gatewayRouters.values()){
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700381 if (ingressRouter == thisRouter) {
382 continue;
383 }
384
385 DataPath shortestPath = topoRouteService.getShortestPath(
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200386 ingressRouter.getAttachmentPoint(),
387 thisRouter.getAttachmentPoint());
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700388
389 if (shortestPath == null){
390 log.debug("Shortest path between {} and {} not found",
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200391 ingressRouter.getAttachmentPoint(),
392 thisRouter.getAttachmentPoint());
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700393 return; // just quit here?
394 }
395
396 //TODO check the shortest path against the cached version we
397 //calculated before. If they don't match up that's a problem
398
399 //Set up the flow mod
400 OFFlowMod fm =
401 (OFFlowMod) floodlightProvider.getOFMessageFactory()
402 .getMessage(OFType.FLOW_MOD);
403
404 fm.setIdleTimeout((short)0)
405 .setHardTimeout((short)0)
406 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
407 .setCookie(MAC_RW_COOKIE)
408 .setCommand(OFFlowMod.OFPFC_ADD)
409 //.setMatch(match)
410 //.setActions(actions)
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200411 .setPriority(SDNIP_PRIORITY)
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700412 .setLengthU(OFFlowMod.MINIMUM_LENGTH
413 + OFActionDataLayerDestination.MINIMUM_LENGTH
414 + OFActionOutput.MINIMUM_LENGTH);
415
416 OFMatch match = new OFMatch();
417 match.setDataLayerType(Ethernet.TYPE_IPv4);
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200418 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700419
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200420 match.setDataLayerSource(ingressRouter.getRouterMac().toBytes());
421 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200422
423 InetAddress address = null;
424 try {
425 address = InetAddress.getByAddress(node.key);
426 } catch (UnknownHostException e1) {
427 //Should never happen is the reverse conversion has already been done
428 log.error("Malformed IP address");
429 return;
430 }
431
432 match.setFromCIDR(address.getHostAddress() + "/" + node.rib.masklen, OFMatch.STR_NW_DST);
433 fm.setMatch(match);
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700434
435 //Set up MAC rewrite action
436 OFActionDataLayerDestination macRewriteAction = new OFActionDataLayerDestination();
437 macRewriteAction.setDataLayerAddress(thisRouter.getRouterMac().toBytes());
438
439 //Set up output action
440 OFActionOutput outputAction = new OFActionOutput();
Jonathan Hart832a7cb2013-06-24 11:25:35 +1200441 outputAction.setMaxLength((short)0xffff);
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700442
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200443 Port outputPort = shortestPath.flowEntries().get(0).outPort();
444 outputAction.setPort(outputPort.value());
445
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700446 List<OFAction> actions = new ArrayList<OFAction>();
447 actions.add(macRewriteAction);
448 actions.add(outputAction);
449 fm.setActions(actions);
450
451 //Write to switch
452 IOFSwitch sw = floodlightProvider.getSwitches()
453 .get(ingressRouter.getAttachmentPoint().dpid().value());
454
455 if (sw == null){
456 log.warn("Switch not found when pushing flow mod");
457 continue;
458 }
459
460 List<OFMessage> msglist = new ArrayList<OFMessage>();
461 msglist.add(fm);
462 try {
463 sw.write(msglist, null);
464 sw.flush();
465 } catch (IOException e) {
466 log.error("Failure writing flow mod", e);
467 }
468 }
469 }
470
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200471 public void prefixDeleted(PtreeNode node) {
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200472 if (!topologyReady) {
473 return;
474 }
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200475
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200476 String prefix = getPrefixFromPtree(node);
477
478 log.debug("Prefix {} deleted, next hop {}",
479 prefix, node.rib.nextHop.toString());
480
481 //Remove MAC rewriting flows from other border switches
482 GatewayRouter thisRouter = gatewayRouters
483 .get(InetAddresses.toAddrString(node.rib.nextHop));
484
485 for (GatewayRouter ingressRouter : gatewayRouters.values()){
486 if (ingressRouter == thisRouter) {
487 continue;
488 }
489
490 //Set up the flow mod
491 OFFlowMod fm =
492 (OFFlowMod) floodlightProvider.getOFMessageFactory()
493 .getMessage(OFType.FLOW_MOD);
494
495 fm.setIdleTimeout((short)0)
496 .setHardTimeout((short)0)
497 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
498 .setCookie(MAC_RW_COOKIE)
499 .setCommand(OFFlowMod.OFPFC_DELETE)
500 //.setMatch(match)
501 //.setActions(actions)
502 .setPriority(SDNIP_PRIORITY)
503 .setLengthU(OFFlowMod.MINIMUM_LENGTH);
504 //+ OFActionDataLayerDestination.MINIMUM_LENGTH
505 //+ OFActionOutput.MINIMUM_LENGTH);
506
507 OFMatch match = new OFMatch();
508 match.setDataLayerType(Ethernet.TYPE_IPv4);
509 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
510
511 match.setDataLayerSource(ingressRouter.getRouterMac().toBytes());
512 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
513
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200514 InetAddress address = null;
515 try {
516 address = InetAddress.getByAddress(node.key);
517 } catch (UnknownHostException e1) {
518 //Should never happen is the reverse conversion has already been done
519 log.error("Malformed IP address");
520 return;
521 }
522
523 match.setFromCIDR(address.getHostAddress() + "/" + node.rib.masklen, OFMatch.STR_NW_DST);
524 fm.setMatch(match);
525
526 //Write to switch
527 IOFSwitch sw = floodlightProvider.getSwitches()
528 .get(ingressRouter.getAttachmentPoint().dpid().value());
529
530 if (sw == null){
531 log.warn("Switch not found when pushing flow mod");
532 continue;
533 }
534
535 List<OFMessage> msglist = new ArrayList<OFMessage>();
536 msglist.add(fm);
537 try {
538 sw.write(msglist, null);
539 sw.flush();
540 } catch (IOException e) {
541 log.error("Failure writing flow mod", e);
542 }
543 }
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700544 }
545
546 /*
547 * On startup we need to calculate a full mesh of paths between all gateway
548 * switches
549 */
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200550 private void setupFullMesh(){
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700551 Map<IOFSwitch, SwitchPort> gatewaySwitches = new HashMap<IOFSwitch, SwitchPort>();
552
553 //have to account for switches not being there, paths not being found.
554
Jonathan Hartd1f23252013-06-13 15:17:05 +1200555 for (GatewayRouter router : gatewayRouters.values()){
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700556 SwitchPort switchPort = router.getAttachmentPoint();
557
558 IOFSwitch sw = floodlightProvider.getSwitches().get(switchPort.dpid().value());
559
560 if (sw == null){
561 log.debug("Gateway switch {} not here yet", switchPort.dpid().value());
562 return; // just quit here?
563 }
564
565 //Only need to know 1 external-facing port from each gateway switch
566 //which we can feed into shortest path calculation
567 if (!gatewaySwitches.containsKey(sw)){
568 gatewaySwitches.put(sw, switchPort);
569 }
570
571 }
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700572
573 //For each border router, calculate and install a path from every other
574 //border switch to said border router. However, don't install the entry
575 //in to the first hop switch, as we need to install an entry to rewrite
576 //for each prefix received. This will be done later when prefixes have
577 //actually been received.
578
Jonathan Hartd1f23252013-06-13 15:17:05 +1200579 for (GatewayRouter dstRouter : gatewayRouters.values()){
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700580 SwitchPort routerAttachmentPoint = dstRouter.getAttachmentPoint();
581 for (Map.Entry<IOFSwitch, SwitchPort> src : gatewaySwitches.entrySet()) {
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700582
583 if (routerAttachmentPoint.dpid().value() ==
584 src.getKey().getId()){
585 continue;
586 }
587
588 DataPath shortestPath = topoRouteService.getShortestPath(
589 src.getValue(), routerAttachmentPoint);
590
591 if (shortestPath == null){
592 log.debug("Shortest path between {} and {} not found",
593 src.getValue(), routerAttachmentPoint);
594 return; // just quit here?
595 }
596
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700597 //install flows
598 installPath(shortestPath.flowEntries(), dstRouter);
599 }
600 }
601 }
602
603 private void installPath(List<FlowEntry> flowEntries, GatewayRouter router){
604
605 //Set up the flow mod
606 OFFlowMod fm =
607 (OFFlowMod) floodlightProvider.getOFMessageFactory()
608 .getMessage(OFType.FLOW_MOD);
609
610 OFActionOutput action = new OFActionOutput();
611 action.setMaxLength((short)0xffff);
612 List<OFAction> actions = new ArrayList<OFAction>();
613 actions.add(action);
614
615 fm.setIdleTimeout((short)0)
616 .setHardTimeout((short)0)
617 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
618 .setCookie(L2_FWD_COOKIE)
619 .setCommand(OFFlowMod.OFPFC_ADD)
620 //.setMatch(match)
621 .setActions(actions)
622 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
623
624 //Don't push the first flow entry. We need to push entries in the
625 //first switch based on IP prefix which we don't know yet.
626 for (int i = 1; i < flowEntries.size(); i++){
627 FlowEntry flowEntry = flowEntries.get(i);
628
629 OFMatch match = new OFMatch();
630 match.setDataLayerDestination(router.getRouterMac().toBytes());
631 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
632 ((OFActionOutput) fm.getActions().get(0)).setPort(flowEntry.outPort().value());
633
634 fm.setMatch(match);
635
636 IOFSwitch sw = floodlightProvider.getSwitches().get(flowEntry.dpid().value());
637
638 if (sw == null){
639 log.warn("Switch not found when pushing flow mod");
640 continue;
641 }
642
643 List<OFMessage> msglist = new ArrayList<OFMessage>();
644 msglist.add(fm);
645 try {
646 sw.write(msglist, null);
647 sw.flush();
648 } catch (IOException e) {
649 log.error("Failure writing flow mod", e);
650 }
651
652 try {
653 fm = fm.clone();
654 } catch (CloneNotSupportedException e1) {
655 log.error("Failure cloning flow mod", e1);
656 }
657 }
658 }
659
Jonathan Hart832a7cb2013-06-24 11:25:35 +1200660 private void setupBgpPaths(){
661 for (BgpPeer bgpPeer : bgpPeers){
662 Interface peerInterface = interfaces.get(bgpPeer.getInterfaceName());
663
664 DataPath path = topoRouteService.getShortestPath(
665 peerInterface.getSwitchPort(), bgpdAttachmentPoint);
666
667 if (path == null){
668 log.debug("Unable to compute path for BGP traffic for {}",
669 bgpPeer.getIpAddress());
670 continue;
671 }
672
673 //Set up the flow mod
674 OFFlowMod fm =
675 (OFFlowMod) floodlightProvider.getOFMessageFactory()
676 .getMessage(OFType.FLOW_MOD);
677
678 OFActionOutput action = new OFActionOutput();
679 action.setMaxLength((short)0xffff);
680 List<OFAction> actions = new ArrayList<OFAction>();
681 actions.add(action);
682
683 fm.setIdleTimeout((short)0)
684 .setHardTimeout((short)0)
685 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
686 .setCookie(BGP_COOKIE)
687 .setCommand(OFFlowMod.OFPFC_ADD)
688 .setPriority(SDNIP_PRIORITY)
689 .setActions(actions)
690 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
691
692 //Forward = gateway -> bgpd, reverse = bgpd -> gateway
693 OFMatch forwardMatchSrc = new OFMatch();
694
695
696 String interfaceCidrAddress = peerInterface.getIpAddress().getHostAddress()
697 + "/32";
698 String peerCidrAddress = bgpPeer.getIpAddress().getHostAddress()
699 + "/32";
700
701 //Common match fields
702 forwardMatchSrc.setDataLayerType(Ethernet.TYPE_IPv4);
703 //forwardMatch.setWildcards(forwardMatch.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
704 forwardMatchSrc.setNetworkProtocol(IPv4.PROTOCOL_TCP);
705 forwardMatchSrc.setTransportDestination(BGP_PORT);
706 forwardMatchSrc.setWildcards(forwardMatchSrc.getWildcards() & ~OFMatch.OFPFW_IN_PORT
707 & ~OFMatch.OFPFW_DL_TYPE & ~OFMatch.OFPFW_NW_PROTO);
708
709
710 OFMatch reverseMatchSrc = forwardMatchSrc.clone();
711
712 forwardMatchSrc.setFromCIDR(peerCidrAddress, OFMatch.STR_NW_SRC);
713 forwardMatchSrc.setFromCIDR(interfaceCidrAddress, OFMatch.STR_NW_DST);
714
715 OFMatch forwardMatchDst = forwardMatchSrc.clone();
716
717 forwardMatchSrc.setTransportSource(BGP_PORT);
718 forwardMatchSrc.setWildcards(forwardMatchSrc.getWildcards() & ~OFMatch.OFPFW_TP_SRC);
719 forwardMatchDst.setTransportDestination(BGP_PORT);
720 forwardMatchDst.setWildcards(forwardMatchDst.getWildcards() & ~OFMatch.OFPFW_TP_DST);
721
722 reverseMatchSrc.setFromCIDR(interfaceCidrAddress, OFMatch.STR_NW_SRC);
723 reverseMatchSrc.setFromCIDR(peerCidrAddress, OFMatch.STR_NW_DST);
724
725 OFMatch reverseMatchDst = reverseMatchSrc.clone();
726
727 reverseMatchSrc.setTransportSource(BGP_PORT);
728 reverseMatchSrc.setWildcards(forwardMatchSrc.getWildcards() & ~OFMatch.OFPFW_TP_SRC);
729 reverseMatchDst.setTransportDestination(BGP_PORT);
730 reverseMatchDst.setWildcards(forwardMatchDst.getWildcards() & ~OFMatch.OFPFW_TP_DST);
731
732 fm.setMatch(forwardMatchSrc);
733
734 for (FlowEntry flowEntry : path.flowEntries()){
735 OFFlowMod forwardFlowModSrc, forwardFlowModDst;
736 OFFlowMod reverseFlowModSrc, reverseFlowModDst;
737 try {
738 forwardFlowModSrc = fm.clone();
739 forwardFlowModDst = fm.clone();
740 reverseFlowModSrc = fm.clone();
741 reverseFlowModDst = fm.clone();
742 } catch (CloneNotSupportedException e) {
743 log.warn("Clone failed", e);
744 continue;
745 }
746
747 forwardMatchSrc.setInputPort(flowEntry.inPort().value());
748 forwardFlowModSrc.setMatch(forwardMatchSrc);
749 ((OFActionOutput)forwardFlowModSrc.getActions().get(0))
750 .setPort(flowEntry.outPort().value());
751
752 forwardMatchDst.setInputPort(flowEntry.inPort().value());
753 forwardFlowModDst.setMatch(forwardMatchDst);
754 ((OFActionOutput)forwardFlowModDst.getActions().get(0))
755 .setPort(flowEntry.outPort().value());
756
757 reverseMatchSrc.setInputPort(flowEntry.outPort().value());
758 reverseFlowModSrc.setMatch(reverseMatchSrc);
759 ((OFActionOutput)reverseFlowModSrc.getActions().get(0))
760 .setPort(flowEntry.inPort().value());
761
762 reverseMatchDst.setInputPort(flowEntry.outPort().value());
763 reverseFlowModDst.setMatch(reverseMatchDst);
764 ((OFActionOutput)reverseFlowModDst.getActions().get(0))
765 .setPort(flowEntry.inPort().value());
766
767 IOFSwitch sw = floodlightProvider.getSwitches().get(flowEntry.dpid().value());
768
769 //Hopefully the switch is there
770 List<OFMessage> msgList = new ArrayList<OFMessage>(2);
771 msgList.add(forwardFlowModSrc);
772 msgList.add(forwardFlowModDst);
773 msgList.add(reverseFlowModSrc);
774 msgList.add(reverseFlowModDst);
775
776 try {
777 sw.write(msgList, null);
778 sw.flush();
779 } catch (IOException e) {
780 log.error("Failure writing flow mod", e);
781 }
782 }
783 }
784 }
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200785
786 private void beginRouting(){
787 log.debug("Topology is now ready, beginning routing function");
Jonathan Hart832a7cb2013-06-24 11:25:35 +1200788 setupBgpPaths();
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200789
Jonathan Hart832a7cb2013-06-24 11:25:35 +1200790 //Traverse ptree and create flows for all routes
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200791 for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)){
792 if (node.rib != null){
793 prefixAdded(node);
794 }
795 }
796 }
797
798 private void checkSwitchesConnected(){
799 for (String dpid : switches){
800 if (floodlightProvider.getSwitches().get(HexString.toLong(dpid)) == null){
801 log.debug("Not all switches are here yet");
802 return;
803 }
804 }
805 switchesConnected = true;
806 }
807
808 private void checkTopologyReady(){
809 for (GatewayRouter dstRouter : gatewayRouters.values()){
810 SwitchPort dstAttachmentPoint = dstRouter.getAttachmentPoint();
811 for (GatewayRouter srcRouter : gatewayRouters.values()) {
812
813 if (dstRouter == srcRouter){
814 continue;
815 }
816
817 SwitchPort srcAttachmentPoint = srcRouter.getAttachmentPoint();
818
819 DataPath shortestPath = topoRouteService.getShortestPath(
820 srcAttachmentPoint, dstAttachmentPoint);
821
822 if (shortestPath == null){
823 log.debug("Shortest path between {} and {} not found",
824 srcAttachmentPoint, dstAttachmentPoint);
825 return;
826 }
827 }
828 }
829 topologyReady = true;
830 }
831
832 private void checkStatus(){
833 log.debug("In checkStatus, swC {}, toRe {}", switchesConnected, topologyReady);
834
835 if (!switchesConnected){
836 checkSwitchesConnected();
837 }
838 boolean oldTopologyReadyStatus = topologyReady;
839 if (switchesConnected && !topologyReady){
840 checkTopologyReady();
841 }
842 if (!oldTopologyReadyStatus && topologyReady){
843 beginRouting();
844 }
845 }
846
pingping-lina2cbfad2013-03-07 08:39:21 +0800847 @Override
848 public void startUp(FloodlightModuleContext context) {
pingping-line2a09ca2013-03-23 09:33:58 +0800849 restApi.addRestletRoutable(new BgpRouteWebRoutable());
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200850 floodlightProvider.addOFSwitchListener(this);
Jonathan Hart61ba9372013-05-19 20:10:29 -0700851 topology.addListener(this);
pingping-line2a09ca2013-03-23 09:33:58 +0800852
Jonathan Hart61ba9372013-05-19 20:10:29 -0700853 //Retrieve the RIB from BGPd during startup
854 retrieveRib();
pingping-lina2cbfad2013-03-07 08:39:21 +0800855 }
856
857 @Override
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200858 public void topologyChanged() {
859 //There seems to be more topology events than there should be. Lots of link
860 //updated, port up and switch updated on what should be a fairly static topology
pingping-lina2cbfad2013-03-07 08:39:21 +0800861
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200862 boolean refreshNeeded = false;
863 for (LDUpdate ldu : topology.getLastLinkUpdates()){
864 if (!ldu.getOperation().equals(ILinkDiscovery.UpdateOperation.LINK_UPDATED)){
865 //We don't need to recalculate anything for just link updates
866 //They happen way too frequently (may be a bug in our link discovery)
867 refreshNeeded = true;
868 }
869 log.debug("Topo change {}", ldu.getOperation());
870 }
871
872 if (refreshNeeded){
873 if (topologyReady){
874 setupFullMesh();
875 }
876 else{
877 checkStatus();
pingping-lina2cbfad2013-03-07 08:39:21 +0800878 }
879 }
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200880 }
pingping-lina2cbfad2013-03-07 08:39:21 +0800881
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200882 //TODO determine whether we need to listen for switch joins
883 @Override
884 public void addedSwitch(IOFSwitch sw) {
885 //checkStatus();
886 }
887
888 @Override
889 public void removedSwitch(IOFSwitch sw) {
890 // TODO Auto-generated method stub
891 }
892
893 @Override
894 public void switchPortChanged(Long switchId) {}
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200895
896 @Override
897 public String getName() {
898 return "BgpRoute";
pingping-lina2cbfad2013-03-07 08:39:21 +0800899 }
900}