blob: 244c53309922977d23e4939e79a22affd0f21c70 [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;
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120029import net.onrc.onos.ofcontroller.proxyarp.ProxyArpManager;
HIGUCHI Yuta356086e2013-06-12 15:21:19 -070030import net.onrc.onos.ofcontroller.util.DataPath;
Jonathan Hart832a7cb2013-06-24 11:25:35 +120031import net.onrc.onos.ofcontroller.util.Dpid;
HIGUCHI Yuta356086e2013-06-12 15:21:19 -070032import net.onrc.onos.ofcontroller.util.FlowEntry;
HIGUCHI Yuta356086e2013-06-12 15:21:19 -070033import net.onrc.onos.ofcontroller.util.Port;
34import net.onrc.onos.ofcontroller.util.SwitchPort;
pingping-line2a09ca2013-03-23 09:33:58 +080035import net.sf.json.JSONArray;
36import net.sf.json.JSONObject;
37import net.sf.json.JSONSerializer;
pingping-lina2cbfad2013-03-07 08:39:21 +080038
Jonathan Hartd1f23252013-06-13 15:17:05 +120039import org.codehaus.jackson.JsonParseException;
40import org.codehaus.jackson.map.JsonMappingException;
41import org.codehaus.jackson.map.ObjectMapper;
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070042import org.openflow.protocol.OFFlowMod;
43import org.openflow.protocol.OFMatch;
44import org.openflow.protocol.OFMessage;
45import org.openflow.protocol.OFPacketOut;
46import 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
Jonathan Hartc7ca35d2013-06-25 20:54:25 +120067 protected ProxyArpManager proxyArp;
68
pingping-lina2cbfad2013-03-07 08:39:21 +080069 protected static Ptree ptree;
Jonathan Hart61ba9372013-05-19 20:10:29 -070070 protected String bgpdRestIp;
71 protected String routerId;
Jonathan Hart832a7cb2013-06-24 11:25:35 +120072 protected String gatewaysFilename = "config.json";
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070073
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 Hart832a7cb2013-06-24 11:25:35 +120081 //Cookie for flows that setup BGP paths
82 protected final long BGP_COOKIE = APP_COOKIE + 3;
Jonathan Hart50a8d1e2013-06-06 16:00:47 +120083 //Forwarding uses priority 0, and the mac rewrite entries in ingress switches
84 //need to be higher priority than this otherwise the rewrite may not get done
85 protected final short SDNIP_PRIORITY = 10;
Jonathan Harte7e1c6e2013-06-04 20:50:23 -070086
Jonathan Hart832a7cb2013-06-24 11:25:35 +120087 protected final short BGP_PORT = 179;
88
89 //Configuration stuff
Jonathan Hartd1f23252013-06-13 15:17:05 +120090 protected Map<String, GatewayRouter> gatewayRouters;
Jonathan Hart1236a9b2013-06-18 22:10:05 +120091 protected List<String> switches;
Jonathan Hart832a7cb2013-06-24 11:25:35 +120092 protected Map<String, Interface> interfaces;
93 protected List<BgpPeer> bgpPeers;
94 //protected long bgpdAttachmentDpid;
95 //protected short bgpdAttachmentPort;
96 protected SwitchPort bgpdAttachmentPoint;
Jonathan Hart1236a9b2013-06-18 22:10:05 +120097
98 //True when all switches have connected
99 protected volatile boolean switchesConnected = false;
100 //True when we have a full mesh of shortest paths between gateways
101 protected volatile boolean topologyReady = false;
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700102
Jonathan Hartd1f23252013-06-13 15:17:05 +1200103 private void readGatewaysConfiguration(String gatewaysFilename){
104 File gatewaysFile = new File(gatewaysFilename);
105 ObjectMapper mapper = new ObjectMapper();
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700106
Jonathan Hartd1f23252013-06-13 15:17:05 +1200107 try {
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200108 Configuration config = mapper.readValue(gatewaysFile, Configuration.class);
109
110 gatewayRouters = config.getGateways();
111 switches = config.getSwitches();
Jonathan Hart832a7cb2013-06-24 11:25:35 +1200112 interfaces = config.getInterfaces();
113 bgpPeers = config.getPeers();
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200114
Jonathan Hart832a7cb2013-06-24 11:25:35 +1200115 bgpdAttachmentPoint = new SwitchPort(
116 new Dpid(config.getBgpdAttachmentDpid()),
117 new Port(config.getBgpdAttachmentPort()));
118 //bgpdAttachmentDpid = config.getBgpdAttachmentDpid();
119 //bgpdAttachmentPort = config.getBgpdAttachmentPort();
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200120
Jonathan Hartd1f23252013-06-13 15:17:05 +1200121 } catch (JsonParseException e) {
122 log.error("Error in JSON file", e);
123 System.exit(1);
124 } catch (JsonMappingException e) {
125 log.error("Error in JSON file", e);
126 System.exit(1);
127 } catch (IOException e) {
128 log.error("Error reading JSON file", e);
129 System.exit(1);
130 }
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700131 }
pingping-lina2cbfad2013-03-07 08:39:21 +0800132
133 @Override
134 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Jonathan Hart61ba9372013-05-19 20:10:29 -0700135 Collection<Class<? extends IFloodlightService>> l
136 = new ArrayList<Class<? extends IFloodlightService>>();
pingping-lina2cbfad2013-03-07 08:39:21 +0800137 l.add(IBgpRouteService.class);
138 return l;
139 }
140
141 @Override
142 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
Jonathan Hart61ba9372013-05-19 20:10:29 -0700143 Map<Class<? extends IFloodlightService>, IFloodlightService> m
144 = new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
pingping-line2a09ca2013-03-23 09:33:58 +0800145 m.put(IBgpRouteService.class, this);
pingping-lina2cbfad2013-03-07 08:39:21 +0800146 return m;
147 }
148
pingping-lina2cbfad2013-03-07 08:39:21 +0800149 @Override
150 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Jonathan Hart61ba9372013-05-19 20:10:29 -0700151 Collection<Class<? extends IFloodlightService>> l
152 = new ArrayList<Class<? extends IFloodlightService>>();
pingping-lina2cbfad2013-03-07 08:39:21 +0800153 l.add(IFloodlightProviderService.class);
154 l.add(ITopologyService.class);
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700155 l.add(ITopoRouteService.class);
156 l.add(IDeviceService.class);
157 l.add(IRestApiService.class);
pingping-lina2cbfad2013-03-07 08:39:21 +0800158 return l;
159 }
160
161 @Override
162 public void init(FloodlightModuleContext context)
163 throws FloodlightModuleException {
164
165 ptree = new Ptree(32);
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700166
Jonathan Hart832a7cb2013-06-24 11:25:35 +1200167 //routerIpAddresses = new HashSet<InetAddress>();
pingping-lina2cbfad2013-03-07 08:39:21 +0800168
169 // Register floodlight provider and REST handler.
170 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
pingping-lina2cbfad2013-03-07 08:39:21 +0800171 topology = context.getServiceImpl(ITopologyService.class);
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700172 topoRouteService = context.getServiceImpl(ITopoRouteService.class);
173 devices = context.getServiceImpl(IDeviceService.class);
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200174 restApi = context.getServiceImpl(IRestApiService.class);
175
176 //TODO We'll initialise this here for now, but it should really be done as
177 //part of the controller core
178 proxyArp = new ProxyArpManager(floodlightProvider, topology);
pingping-lina2cbfad2013-03-07 08:39:21 +0800179
Jonathan Hart61ba9372013-05-19 20:10:29 -0700180 //Read in config values
181 bgpdRestIp = context.getConfigParams(this).get("BgpdRestIp");
182 if (bgpdRestIp == null){
183 log.error("BgpdRestIp property not found in config file");
184 System.exit(1);
185 }
186 else {
187 log.info("BgpdRestIp set to {}", bgpdRestIp);
188 }
189
190 routerId = context.getConfigParams(this).get("RouterId");
191 if (routerId == null){
192 log.error("RouterId property not found in config file");
193 System.exit(1);
194 }
195 else {
196 log.info("RouterId set to {}", routerId);
197 }
Jonathan Hartd1f23252013-06-13 15:17:05 +1200198
199 readGatewaysConfiguration(gatewaysFilename);
pingping-lina2cbfad2013-03-07 08:39:21 +0800200 // Test.
201 //test();
202 }
203
204 public Ptree getPtree() {
205 return ptree;
206 }
Jonathan Hart61ba9372013-05-19 20:10:29 -0700207
208 public void clearPtree() {
209 //ptree = null;
210 ptree = new Ptree(32);
pingping-line2a09ca2013-03-23 09:33:58 +0800211 }
Jonathan Hart61ba9372013-05-19 20:10:29 -0700212
pingping-line2a09ca2013-03-23 09:33:58 +0800213 public String getBGPdRestIp() {
Jonathan Hart61ba9372013-05-19 20:10:29 -0700214 return bgpdRestIp;
pingping-line2a09ca2013-03-23 09:33:58 +0800215 }
Jonathan Hart61ba9372013-05-19 20:10:29 -0700216
pingping-line2a09ca2013-03-23 09:33:58 +0800217 public String getRouterId() {
Jonathan Hart61ba9372013-05-19 20:10:29 -0700218 return routerId;
pingping-line2a09ca2013-03-23 09:33:58 +0800219 }
pingping-lina2cbfad2013-03-07 08:39:21 +0800220
221 // Return nexthop address as byte array.
222 public Rib lookupRib(byte[] dest) {
223 if (ptree == null) {
224 log.debug("lookupRib: ptree null");
225 return null;
226 }
227
228 PtreeNode node = ptree.match(dest, 32);
229 if (node == null) {
230 log.debug("lookupRib: ptree node null");
231 return null;
232 }
Jonathan Hart61ba9372013-05-19 20:10:29 -0700233
pingping-lina2cbfad2013-03-07 08:39:21 +0800234 if (node.rib == null) {
235 log.debug("lookupRib: ptree rib null");
236 return null;
237 }
Jonathan Hart61ba9372013-05-19 20:10:29 -0700238
pingping-lina2cbfad2013-03-07 08:39:21 +0800239 ptree.delReference(node);
240
241 return node.rib;
242 }
243
Jonathan Hart61ba9372013-05-19 20:10:29 -0700244 //TODO looks like this should be a unit test
pingping-lina2cbfad2013-03-07 08:39:21 +0800245 @SuppressWarnings("unused")
Jonathan Hart61ba9372013-05-19 20:10:29 -0700246 private void test() throws UnknownHostException {
pingping-lina2cbfad2013-03-07 08:39:21 +0800247 System.out.println("Here it is");
248 Prefix p = new Prefix("128.0.0.0", 8);
249 Prefix q = new Prefix("8.0.0.0", 8);
250 Prefix r = new Prefix("10.0.0.0", 24);
251 Prefix a = new Prefix("10.0.0.1", 32);
252
253 ptree.acquire(p.getAddress(), p.masklen);
254 ptree.acquire(q.getAddress(), q.masklen);
255 ptree.acquire(r.getAddress(), r.masklen);
256
257 System.out.println("Traverse start");
258 for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
259 Prefix p_result = new Prefix(node.key, node.keyBits);
260 }
261
262 PtreeNode n = ptree.match(a.getAddress(), a.masklen);
263 if (n != null) {
264 System.out.println("Matched prefix for 10.0.0.1:");
265 Prefix x = new Prefix(n.key, n.keyBits);
266 ptree.delReference(n);
267 }
268
269 n = ptree.lookup(p.getAddress(), p.masklen);
270 if (n != null) {
271 ptree.delReference(n);
272 ptree.delReference(n);
273 }
274 System.out.println("Traverse start");
275 for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
276 Prefix p_result = new Prefix(node.key, node.keyBits);
277 }
278
279 n = ptree.lookup(q.getAddress(), q.masklen);
280 if (n != null) {
281 ptree.delReference(n);
282 ptree.delReference(n);
283 }
284 System.out.println("Traverse start");
285 for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
286 Prefix p_result = new Prefix(node.key, node.keyBits);
287 }
288
289 n = ptree.lookup(r.getAddress(), r.masklen);
290 if (n != null) {
291 ptree.delReference(n);
292 ptree.delReference(n);
293 }
294 System.out.println("Traverse start");
295 for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
296 Prefix p_result = new Prefix(node.key, node.keyBits);
297 }
298
299 }
300
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200301 private String getPrefixFromPtree(PtreeNode node){
302 InetAddress address = null;
303 try {
304 address = InetAddress.getByAddress(node.key);
305 } catch (UnknownHostException e1) {
306 //Should never happen is the reverse conversion has already been done
307 log.error("Malformed IP address");
308 return "";
309 }
310 return address.toString() + "/" + node.rib.masklen;
311 }
312
Jonathan Hart61ba9372013-05-19 20:10:29 -0700313 private void retrieveRib(){
314 String url = "http://" + bgpdRestIp + "/wm/bgp/" + routerId;
315 String response = RestClient.get(url);
316
317 if (response.equals("")){
318 return;
319 }
320
321 response = response.replaceAll("\"", "'");
322 JSONObject jsonObj = (JSONObject) JSONSerializer.toJSON(response);
323 JSONArray rib_json_array = jsonObj.getJSONArray("rib");
324 String router_id = jsonObj.getString("router-id");
325
326 int size = rib_json_array.size();
327
328 log.info("Retrived RIB of {} entries from BGPd", size);
329
330 for (int j = 0; j < size; j++) {
331 JSONObject second_json_object = rib_json_array.getJSONObject(j);
332 String prefix = second_json_object.getString("prefix");
333 String nexthop = second_json_object.getString("nexthop");
334
335 //insert each rib entry into the local rib;
336 String[] substring = prefix.split("/");
337 String prefix1 = substring[0];
338 String mask1 = substring[1];
339
340 Prefix p;
341 try {
342 p = new Prefix(prefix1, Integer.valueOf(mask1));
343 } catch (NumberFormatException e) {
344 log.warn("Wrong mask format in RIB JSON: {}", mask1);
345 continue;
346 } catch (UnknownHostException e1) {
347 log.warn("Wrong prefix format in RIB JSON: {}", prefix1);
348 continue;
349 }
350
351 PtreeNode node = ptree.acquire(p.getAddress(), p.masklen);
352 Rib rib = new Rib(router_id, nexthop, p.masklen);
353
354 if (node.rib != null) {
355 node.rib = null;
356 ptree.delReference(node);
357 }
358
359 node.rib = rib;
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700360
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200361 prefixAdded(node);
Jonathan Hart61ba9372013-05-19 20:10:29 -0700362 }
363 }
364
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200365 public void prefixAdded(PtreeNode node) {
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200366 if (!topologyReady){
367 return;
368 }
369
370 String prefix = getPrefixFromPtree(node);
371
372 log.debug("New prefix {} added, next hop {}",
373 prefix, node.rib.nextHop.toString());
374
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700375 //Add a flow to rewrite mac for this prefix to all border switches
Jonathan Hartd1f23252013-06-13 15:17:05 +1200376 GatewayRouter thisRouter = gatewayRouters
377 .get(InetAddresses.toAddrString(node.rib.nextHop));
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700378
379 if (thisRouter == null){
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200380 //TODO local router isn't in gateway list so this will get thrown
381 //Need to work out what to do about local prefixes with next hop 0.0.0.0.
382 log.error("Couldn't find next hop router in router {} in config"
383 , node.rib.nextHop.toString());
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700384 return; //just quit out here? This is probably a configuration error
385 }
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200386
Jonathan Hartd1f23252013-06-13 15:17:05 +1200387 for (GatewayRouter ingressRouter : gatewayRouters.values()){
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700388 if (ingressRouter == thisRouter) {
389 continue;
390 }
391
392 DataPath shortestPath = topoRouteService.getShortestPath(
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200393 ingressRouter.getAttachmentPoint(),
394 thisRouter.getAttachmentPoint());
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700395
396 if (shortestPath == null){
397 log.debug("Shortest path between {} and {} not found",
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200398 ingressRouter.getAttachmentPoint(),
399 thisRouter.getAttachmentPoint());
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700400 return; // just quit here?
401 }
402
403 //TODO check the shortest path against the cached version we
404 //calculated before. If they don't match up that's a problem
405
406 //Set up the flow mod
407 OFFlowMod fm =
408 (OFFlowMod) floodlightProvider.getOFMessageFactory()
409 .getMessage(OFType.FLOW_MOD);
410
411 fm.setIdleTimeout((short)0)
412 .setHardTimeout((short)0)
413 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
414 .setCookie(MAC_RW_COOKIE)
415 .setCommand(OFFlowMod.OFPFC_ADD)
416 //.setMatch(match)
417 //.setActions(actions)
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200418 .setPriority(SDNIP_PRIORITY)
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700419 .setLengthU(OFFlowMod.MINIMUM_LENGTH
420 + OFActionDataLayerDestination.MINIMUM_LENGTH
421 + OFActionOutput.MINIMUM_LENGTH);
422
423 OFMatch match = new OFMatch();
424 match.setDataLayerType(Ethernet.TYPE_IPv4);
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200425 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700426
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200427 match.setDataLayerSource(ingressRouter.getRouterMac().toBytes());
428 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200429
430 InetAddress address = null;
431 try {
432 address = InetAddress.getByAddress(node.key);
433 } catch (UnknownHostException e1) {
434 //Should never happen is the reverse conversion has already been done
435 log.error("Malformed IP address");
436 return;
437 }
438
439 match.setFromCIDR(address.getHostAddress() + "/" + node.rib.masklen, OFMatch.STR_NW_DST);
440 fm.setMatch(match);
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700441
442 //Set up MAC rewrite action
443 OFActionDataLayerDestination macRewriteAction = new OFActionDataLayerDestination();
444 macRewriteAction.setDataLayerAddress(thisRouter.getRouterMac().toBytes());
445
446 //Set up output action
447 OFActionOutput outputAction = new OFActionOutput();
Jonathan Hart832a7cb2013-06-24 11:25:35 +1200448 outputAction.setMaxLength((short)0xffff);
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700449
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200450 Port outputPort = shortestPath.flowEntries().get(0).outPort();
451 outputAction.setPort(outputPort.value());
452
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700453 List<OFAction> actions = new ArrayList<OFAction>();
454 actions.add(macRewriteAction);
455 actions.add(outputAction);
456 fm.setActions(actions);
457
458 //Write to switch
459 IOFSwitch sw = floodlightProvider.getSwitches()
460 .get(ingressRouter.getAttachmentPoint().dpid().value());
461
462 if (sw == null){
463 log.warn("Switch not found when pushing flow mod");
464 continue;
465 }
466
467 List<OFMessage> msglist = new ArrayList<OFMessage>();
468 msglist.add(fm);
469 try {
470 sw.write(msglist, null);
471 sw.flush();
472 } catch (IOException e) {
473 log.error("Failure writing flow mod", e);
474 }
475 }
476 }
477
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200478 public void prefixDeleted(PtreeNode node) {
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200479 if (!topologyReady) {
480 return;
481 }
Jonathan Hart50a8d1e2013-06-06 16:00:47 +1200482
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200483 String prefix = getPrefixFromPtree(node);
484
485 log.debug("Prefix {} deleted, next hop {}",
486 prefix, node.rib.nextHop.toString());
487
488 //Remove MAC rewriting flows from other border switches
489 GatewayRouter thisRouter = gatewayRouters
490 .get(InetAddresses.toAddrString(node.rib.nextHop));
491
492 for (GatewayRouter ingressRouter : gatewayRouters.values()){
493 if (ingressRouter == thisRouter) {
494 continue;
495 }
496
497 //Set up the flow mod
498 OFFlowMod fm =
499 (OFFlowMod) floodlightProvider.getOFMessageFactory()
500 .getMessage(OFType.FLOW_MOD);
501
502 fm.setIdleTimeout((short)0)
503 .setHardTimeout((short)0)
504 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
505 .setCookie(MAC_RW_COOKIE)
506 .setCommand(OFFlowMod.OFPFC_DELETE)
507 //.setMatch(match)
508 //.setActions(actions)
509 .setPriority(SDNIP_PRIORITY)
510 .setLengthU(OFFlowMod.MINIMUM_LENGTH);
511 //+ OFActionDataLayerDestination.MINIMUM_LENGTH
512 //+ OFActionOutput.MINIMUM_LENGTH);
513
514 OFMatch match = new OFMatch();
515 match.setDataLayerType(Ethernet.TYPE_IPv4);
516 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
517
518 match.setDataLayerSource(ingressRouter.getRouterMac().toBytes());
519 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
520
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200521 InetAddress address = null;
522 try {
523 address = InetAddress.getByAddress(node.key);
524 } catch (UnknownHostException e1) {
525 //Should never happen is the reverse conversion has already been done
526 log.error("Malformed IP address");
527 return;
528 }
529
530 match.setFromCIDR(address.getHostAddress() + "/" + node.rib.masklen, OFMatch.STR_NW_DST);
531 fm.setMatch(match);
532
533 //Write to switch
534 IOFSwitch sw = floodlightProvider.getSwitches()
535 .get(ingressRouter.getAttachmentPoint().dpid().value());
536
537 if (sw == null){
538 log.warn("Switch not found when pushing flow mod");
539 continue;
540 }
541
542 List<OFMessage> msglist = new ArrayList<OFMessage>();
543 msglist.add(fm);
544 try {
545 sw.write(msglist, null);
546 sw.flush();
547 } catch (IOException e) {
548 log.error("Failure writing flow mod", e);
549 }
550 }
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700551 }
552
553 /*
554 * On startup we need to calculate a full mesh of paths between all gateway
555 * switches
556 */
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200557 private void setupFullMesh(){
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700558 Map<IOFSwitch, SwitchPort> gatewaySwitches = new HashMap<IOFSwitch, SwitchPort>();
559
560 //have to account for switches not being there, paths not being found.
561
Jonathan Hartd1f23252013-06-13 15:17:05 +1200562 for (GatewayRouter router : gatewayRouters.values()){
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700563 SwitchPort switchPort = router.getAttachmentPoint();
564
565 IOFSwitch sw = floodlightProvider.getSwitches().get(switchPort.dpid().value());
566
567 if (sw == null){
568 log.debug("Gateway switch {} not here yet", switchPort.dpid().value());
569 return; // just quit here?
570 }
571
572 //Only need to know 1 external-facing port from each gateway switch
573 //which we can feed into shortest path calculation
574 if (!gatewaySwitches.containsKey(sw)){
575 gatewaySwitches.put(sw, switchPort);
576 }
577
578 }
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700579
580 //For each border router, calculate and install a path from every other
581 //border switch to said border router. However, don't install the entry
582 //in to the first hop switch, as we need to install an entry to rewrite
583 //for each prefix received. This will be done later when prefixes have
584 //actually been received.
585
Jonathan Hartd1f23252013-06-13 15:17:05 +1200586 for (GatewayRouter dstRouter : gatewayRouters.values()){
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700587 SwitchPort routerAttachmentPoint = dstRouter.getAttachmentPoint();
588 for (Map.Entry<IOFSwitch, SwitchPort> src : gatewaySwitches.entrySet()) {
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700589
590 if (routerAttachmentPoint.dpid().value() ==
591 src.getKey().getId()){
592 continue;
593 }
594
595 DataPath shortestPath = topoRouteService.getShortestPath(
596 src.getValue(), routerAttachmentPoint);
597
598 if (shortestPath == null){
599 log.debug("Shortest path between {} and {} not found",
600 src.getValue(), routerAttachmentPoint);
601 return; // just quit here?
602 }
603
Jonathan Harte7e1c6e2013-06-04 20:50:23 -0700604 //install flows
605 installPath(shortestPath.flowEntries(), dstRouter);
606 }
607 }
608 }
609
610 private void installPath(List<FlowEntry> flowEntries, GatewayRouter router){
611
612 //Set up the flow mod
613 OFFlowMod fm =
614 (OFFlowMod) floodlightProvider.getOFMessageFactory()
615 .getMessage(OFType.FLOW_MOD);
616
617 OFActionOutput action = new OFActionOutput();
618 action.setMaxLength((short)0xffff);
619 List<OFAction> actions = new ArrayList<OFAction>();
620 actions.add(action);
621
622 fm.setIdleTimeout((short)0)
623 .setHardTimeout((short)0)
624 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
625 .setCookie(L2_FWD_COOKIE)
626 .setCommand(OFFlowMod.OFPFC_ADD)
627 //.setMatch(match)
628 .setActions(actions)
629 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
630
631 //Don't push the first flow entry. We need to push entries in the
632 //first switch based on IP prefix which we don't know yet.
633 for (int i = 1; i < flowEntries.size(); i++){
634 FlowEntry flowEntry = flowEntries.get(i);
635
636 OFMatch match = new OFMatch();
637 match.setDataLayerDestination(router.getRouterMac().toBytes());
638 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
639 ((OFActionOutput) fm.getActions().get(0)).setPort(flowEntry.outPort().value());
640
641 fm.setMatch(match);
642
643 IOFSwitch sw = floodlightProvider.getSwitches().get(flowEntry.dpid().value());
644
645 if (sw == null){
646 log.warn("Switch not found when pushing flow mod");
647 continue;
648 }
649
650 List<OFMessage> msglist = new ArrayList<OFMessage>();
651 msglist.add(fm);
652 try {
653 sw.write(msglist, null);
654 sw.flush();
655 } catch (IOException e) {
656 log.error("Failure writing flow mod", e);
657 }
658
659 try {
660 fm = fm.clone();
661 } catch (CloneNotSupportedException e1) {
662 log.error("Failure cloning flow mod", e1);
663 }
664 }
665 }
666
Jonathan Hart832a7cb2013-06-24 11:25:35 +1200667 private void setupBgpPaths(){
668 for (BgpPeer bgpPeer : bgpPeers){
669 Interface peerInterface = interfaces.get(bgpPeer.getInterfaceName());
670
671 DataPath path = topoRouteService.getShortestPath(
672 peerInterface.getSwitchPort(), bgpdAttachmentPoint);
673
674 if (path == null){
675 log.debug("Unable to compute path for BGP traffic for {}",
676 bgpPeer.getIpAddress());
677 continue;
678 }
679
680 //Set up the flow mod
681 OFFlowMod fm =
682 (OFFlowMod) floodlightProvider.getOFMessageFactory()
683 .getMessage(OFType.FLOW_MOD);
684
685 OFActionOutput action = new OFActionOutput();
686 action.setMaxLength((short)0xffff);
687 List<OFAction> actions = new ArrayList<OFAction>();
688 actions.add(action);
689
690 fm.setIdleTimeout((short)0)
691 .setHardTimeout((short)0)
692 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
693 .setCookie(BGP_COOKIE)
694 .setCommand(OFFlowMod.OFPFC_ADD)
695 .setPriority(SDNIP_PRIORITY)
696 .setActions(actions)
697 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
698
699 //Forward = gateway -> bgpd, reverse = bgpd -> gateway
700 OFMatch forwardMatchSrc = new OFMatch();
701
702
703 String interfaceCidrAddress = peerInterface.getIpAddress().getHostAddress()
704 + "/32";
705 String peerCidrAddress = bgpPeer.getIpAddress().getHostAddress()
706 + "/32";
707
708 //Common match fields
709 forwardMatchSrc.setDataLayerType(Ethernet.TYPE_IPv4);
710 //forwardMatch.setWildcards(forwardMatch.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
711 forwardMatchSrc.setNetworkProtocol(IPv4.PROTOCOL_TCP);
712 forwardMatchSrc.setTransportDestination(BGP_PORT);
713 forwardMatchSrc.setWildcards(forwardMatchSrc.getWildcards() & ~OFMatch.OFPFW_IN_PORT
714 & ~OFMatch.OFPFW_DL_TYPE & ~OFMatch.OFPFW_NW_PROTO);
715
716
717 OFMatch reverseMatchSrc = forwardMatchSrc.clone();
718
719 forwardMatchSrc.setFromCIDR(peerCidrAddress, OFMatch.STR_NW_SRC);
720 forwardMatchSrc.setFromCIDR(interfaceCidrAddress, OFMatch.STR_NW_DST);
721
722 OFMatch forwardMatchDst = forwardMatchSrc.clone();
723
724 forwardMatchSrc.setTransportSource(BGP_PORT);
725 forwardMatchSrc.setWildcards(forwardMatchSrc.getWildcards() & ~OFMatch.OFPFW_TP_SRC);
726 forwardMatchDst.setTransportDestination(BGP_PORT);
727 forwardMatchDst.setWildcards(forwardMatchDst.getWildcards() & ~OFMatch.OFPFW_TP_DST);
728
729 reverseMatchSrc.setFromCIDR(interfaceCidrAddress, OFMatch.STR_NW_SRC);
730 reverseMatchSrc.setFromCIDR(peerCidrAddress, OFMatch.STR_NW_DST);
731
732 OFMatch reverseMatchDst = reverseMatchSrc.clone();
733
734 reverseMatchSrc.setTransportSource(BGP_PORT);
735 reverseMatchSrc.setWildcards(forwardMatchSrc.getWildcards() & ~OFMatch.OFPFW_TP_SRC);
736 reverseMatchDst.setTransportDestination(BGP_PORT);
737 reverseMatchDst.setWildcards(forwardMatchDst.getWildcards() & ~OFMatch.OFPFW_TP_DST);
738
739 fm.setMatch(forwardMatchSrc);
740
741 for (FlowEntry flowEntry : path.flowEntries()){
742 OFFlowMod forwardFlowModSrc, forwardFlowModDst;
743 OFFlowMod reverseFlowModSrc, reverseFlowModDst;
744 try {
745 forwardFlowModSrc = fm.clone();
746 forwardFlowModDst = fm.clone();
747 reverseFlowModSrc = fm.clone();
748 reverseFlowModDst = fm.clone();
749 } catch (CloneNotSupportedException e) {
750 log.warn("Clone failed", e);
751 continue;
752 }
753
754 forwardMatchSrc.setInputPort(flowEntry.inPort().value());
755 forwardFlowModSrc.setMatch(forwardMatchSrc);
756 ((OFActionOutput)forwardFlowModSrc.getActions().get(0))
757 .setPort(flowEntry.outPort().value());
758
759 forwardMatchDst.setInputPort(flowEntry.inPort().value());
760 forwardFlowModDst.setMatch(forwardMatchDst);
761 ((OFActionOutput)forwardFlowModDst.getActions().get(0))
762 .setPort(flowEntry.outPort().value());
763
764 reverseMatchSrc.setInputPort(flowEntry.outPort().value());
765 reverseFlowModSrc.setMatch(reverseMatchSrc);
766 ((OFActionOutput)reverseFlowModSrc.getActions().get(0))
767 .setPort(flowEntry.inPort().value());
768
769 reverseMatchDst.setInputPort(flowEntry.outPort().value());
770 reverseFlowModDst.setMatch(reverseMatchDst);
771 ((OFActionOutput)reverseFlowModDst.getActions().get(0))
772 .setPort(flowEntry.inPort().value());
773
774 IOFSwitch sw = floodlightProvider.getSwitches().get(flowEntry.dpid().value());
775
776 //Hopefully the switch is there
777 List<OFMessage> msgList = new ArrayList<OFMessage>(2);
778 msgList.add(forwardFlowModSrc);
779 msgList.add(forwardFlowModDst);
780 msgList.add(reverseFlowModSrc);
781 msgList.add(reverseFlowModDst);
782
783 try {
784 sw.write(msgList, null);
785 sw.flush();
786 } catch (IOException e) {
787 log.error("Failure writing flow mod", e);
788 }
789 }
790 }
791 }
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200792
793 private void beginRouting(){
794 log.debug("Topology is now ready, beginning routing function");
Jonathan Hart832a7cb2013-06-24 11:25:35 +1200795 setupBgpPaths();
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200796
Jonathan Hart832a7cb2013-06-24 11:25:35 +1200797 //Traverse ptree and create flows for all routes
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200798 for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)){
799 if (node.rib != null){
800 prefixAdded(node);
801 }
802 }
803 }
804
805 private void checkSwitchesConnected(){
806 for (String dpid : switches){
807 if (floodlightProvider.getSwitches().get(HexString.toLong(dpid)) == null){
808 log.debug("Not all switches are here yet");
809 return;
810 }
811 }
812 switchesConnected = true;
813 }
814
815 private void checkTopologyReady(){
816 for (GatewayRouter dstRouter : gatewayRouters.values()){
817 SwitchPort dstAttachmentPoint = dstRouter.getAttachmentPoint();
818 for (GatewayRouter srcRouter : gatewayRouters.values()) {
819
820 if (dstRouter == srcRouter){
821 continue;
822 }
823
824 SwitchPort srcAttachmentPoint = srcRouter.getAttachmentPoint();
825
826 DataPath shortestPath = topoRouteService.getShortestPath(
827 srcAttachmentPoint, dstAttachmentPoint);
828
829 if (shortestPath == null){
830 log.debug("Shortest path between {} and {} not found",
831 srcAttachmentPoint, dstAttachmentPoint);
832 return;
833 }
834 }
835 }
836 topologyReady = true;
837 }
838
839 private void checkStatus(){
840 log.debug("In checkStatus, swC {}, toRe {}", switchesConnected, topologyReady);
841
842 if (!switchesConnected){
843 checkSwitchesConnected();
844 }
845 boolean oldTopologyReadyStatus = topologyReady;
846 if (switchesConnected && !topologyReady){
847 checkTopologyReady();
848 }
849 if (!oldTopologyReadyStatus && topologyReady){
850 beginRouting();
851 }
852 }
853
pingping-lina2cbfad2013-03-07 08:39:21 +0800854 @Override
855 public void startUp(FloodlightModuleContext context) {
pingping-line2a09ca2013-03-23 09:33:58 +0800856 restApi.addRestletRoutable(new BgpRouteWebRoutable());
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200857 floodlightProvider.addOFSwitchListener(this);
Jonathan Hart61ba9372013-05-19 20:10:29 -0700858 topology.addListener(this);
pingping-line2a09ca2013-03-23 09:33:58 +0800859
Jonathan Hartc7ca35d2013-06-25 20:54:25 +1200860 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, proxyArp);
861
Jonathan Hart61ba9372013-05-19 20:10:29 -0700862 //Retrieve the RIB from BGPd during startup
863 retrieveRib();
pingping-lina2cbfad2013-03-07 08:39:21 +0800864 }
865
866 @Override
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200867 public void topologyChanged() {
868 //There seems to be more topology events than there should be. Lots of link
869 //updated, port up and switch updated on what should be a fairly static topology
pingping-lina2cbfad2013-03-07 08:39:21 +0800870
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200871 boolean refreshNeeded = false;
872 for (LDUpdate ldu : topology.getLastLinkUpdates()){
873 if (!ldu.getOperation().equals(ILinkDiscovery.UpdateOperation.LINK_UPDATED)){
874 //We don't need to recalculate anything for just link updates
875 //They happen way too frequently (may be a bug in our link discovery)
876 refreshNeeded = true;
877 }
878 log.debug("Topo change {}", ldu.getOperation());
879 }
880
881 if (refreshNeeded){
882 if (topologyReady){
883 setupFullMesh();
884 }
885 else{
886 checkStatus();
pingping-lina2cbfad2013-03-07 08:39:21 +0800887 }
888 }
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200889 }
pingping-lina2cbfad2013-03-07 08:39:21 +0800890
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200891 //TODO determine whether we need to listen for switch joins
892 @Override
893 public void addedSwitch(IOFSwitch sw) {
894 //checkStatus();
895 }
896
897 @Override
898 public void removedSwitch(IOFSwitch sw) {
899 // TODO Auto-generated method stub
900 }
901
902 @Override
903 public void switchPortChanged(Long switchId) {}
Jonathan Hart1236a9b2013-06-18 22:10:05 +1200904
905 @Override
906 public String getName() {
907 return "BgpRoute";
pingping-lina2cbfad2013-03-07 08:39:21 +0800908 }
909}