Monday, January 6, 2014

OpenDaylight

This article looks takes the DDoS example and repeats it using the OpenDaylight controller.

First install Open Daylight in the Mininet testbed.
$ wget https://jenkins.opendaylight.org/controller/job/controller-merge/lastSuccessfulBuild/artifact/opendaylight/distribution/opendaylight/target/distribution.opendaylight-osgipackage.zip
unzip distribution.opendaylight-osgipackage.zip
Next start Mininet.
sudo mn --topo single,3 --controller=remote,ip=127.0.0.1
Enable sFlow on the switch:
sudo ovs-vsctl -- --id=@sflow create sflow agent=eth0  target=\"127.0.0.1:6343\" sampling=10 polling=20 -- -- set bridge s1 sflow=@sflow
Start OpenDaylight.
cd opendaylight
./run.sh
Confirm that the controller is running and has discovered the switch by connecting a browser to port 8080 on the testbed - the screen shot at the start of the article shows the OpenDaylight Devices tab with the switch 00:00:00:00:00:00:00:01 shown in the Nodes Learned list and in the map (the default credentials to log into the OpenDaylight interface are User:admin, Password:admin).

The following sFlow-RT script modified the original to use the OpenDaylight Flow Programmer REST API to push OpenFlow rules to the switch.
include('extras/json2.js');

var flowkeys = 'ipsource';
var value = 'frames';
var filter = 'outputifindex!=discard&direction=ingress&sourcegroup=external';
var threshold = 1000;
var groups = {'external':['0.0.0.0/0'],'internal':['10.0.0.2/32']};

var metricName = 'ddos';
var controls = {};
var enabled = true;
var blockSeconds = 20;
var ruleid = 0;

var flowprogrammer = 'http://127.0.0.1:8080/controller/nb/v2/flowprogrammer/default/node/OF/';
var user = 'admin';
var password = 'admin';
var bridge = '00:00:00:00:00:00:00:01';

function setOpenFlow(bridge,name,spec) {
  http(flowprogrammer+bridge+'/staticFlow/'+name,'put','application/json',
       JSON.stringify(spec),user,password);
}

function deleteOpenFlow(bridge,name) {
  http(flowprogrammer+bridge+'/staticFlow/'+name,'delete','application/json',
       null,user,password);
}

function block(address) {
  if(!controls[address]) {
     var name = 'block' + ruleid++;
     setOpenFlow(bridge,name,{installInHw:true,name:name, 
                 node:{id:bridge, type:'OF'},
                 priority:'11', etherType:'0x0800', 
                 nwSrc: address, actions:['DROP']});
     controls[address] = { name: name, action:'block', 
                           time: (new Date()).getTime() };
  }
}

function allow(address) {
  if(controls[address]) {
     deleteOpenFlow(bridge,controls[address].name);
     delete controls[address];
  }
}

setEventHandler(function(evt) {
  if(!enabled) return;

  var addr = evt.flowKey;
  block(addr);  
},[metricName]);

setIntervalHandler(function() {
  // remove stale controls
  var stale = [];
  var now = (new Date()).getTime();
  var threshMs = 1000 * blockSeconds;
  for(var addr in controls) {
    if((now - controls[addr].time) > threshMs) stale.push(addr);
  }
  for(var i = 0; i < stale.length; i++) allow(stale[i]);
},10);

setHttpHandler(function(request) {
  var result = {};
  try {
    var action = '' + request.query.action;
    switch(action) {
    case 'block':
       var address = request.query.address[0];
       if(address) block(address);
        break;
    case 'allow':
       var address = request.query.address[0];
       if(address) allow(address);
       break;
    case 'enable':
      enabled = true;
      break;
    case 'disable':
      enabled = false;
      break;
    }
  }
  catch(e) { result.error = e.message }
  result.controls = controls;
  result.enabled = enabled;
  return JSON.stringify(result);
});

setGroups(groups);
setFlow(metricName,{keys:flowkeys,value:value,filter:filter});
setThreshold(metricName,{metric:metricName,value:threshold,byFlow:true,timeout:5});
The following command line argument loads the script on startup:
-D file.script=odl.js
Repeating the simulated denial of service attack without the controller active and with the controller active shows the same results demonstrated in the previous article:
When the controller is disabled, the attack traffic exceeds 6,000 packets per second and persists until the attacker stops sending. When the controller is enabled, traffic is stopped the instant it hits the 1,000 packet per second threshold in the application. The control is removed 20 seconds later and re-triggers if the attacker is still sending traffic.

DDoS mitigation is only one use case for large flow control, others described on this blog include: ECMP / LAG load balancing, traffic marking and packet capture. This script can be modified to address these different use cases. The Mininet test bed provides a useful way to test OpenFlow control schemes before moving them into production using physical switches.

10 comments:

  1. Hello, i want to run the same example with Helium, what are the changes in the above script, in order to be compatible with Helium?

    e.g. --> 1) var flowprogrammer = 'http://127.0.0.1:8181/controller/nb/v2/flowprogrammer/default/node/OF/'; ???

    2) var bridge = 'openflow:1'; ???

    thanks in advance!

    ReplyDelete
  2. Hello,

    I'd like to use sFlow to monitor my mininet network (custom topology). I use the OpenDaylight controller. I tried to run the command to enable sFlow in the mininet CLI, but I get the following error:

    ovs-vsctl: no row "127.0.0.1" in table Bridge

    Do you know why I'm getting this message?

    Thanks!
    Toni.

    ReplyDelete
    Replies
    1. Try running the command in a different terminal (not in the mininet CLI). Also, take a look at the sFlow-RT extras/leafandspine.py script. There is configSFlow function that you could modify and incorporate in the script that builds your custom topology.

      Delete
  3. Hello peter,

    i want build a virtual data centre with openstack, can i implement this method to openstack? Then how to deploy or install or enable sflow in openstack environment?

    thanks peter


    regret
    ryanda

    ReplyDelete
    Replies
    1. You should be able to implement this technique as part of an OpenStack deployment, provided that you were using Open vSwitch, but I haven't tried it, so I can't help with the implementation details.

      Delete
  4. Hello Peter,

    when i run the sflow-rt, i get this message :

    2015-09-10T20:49:48-0400 INFO: Listening, sFlow port 6343
    2015-09-10T20:49:48-0400 INFO: Starting the Jetty [HTTP/1.1] server on port 8008
    2015-09-10T20:49:48-0400 INFO: Starting com.sflow.rt.rest.SFlowApplication application
    2015-09-10T20:49:48-0400 INFO: Listening, http://localhost:8008
    2015-09-10T20:49:48-0400 INFO: odl.js started
    2015-09-10T20:49:48-0400 WARNING: bad group name [object Object]
    Is that message is fine?
    and when i move to web UI i cannot see DDOS in metric? why it happened?

    Thanks Peter

    Regards,
    Ryanda

    ReplyDelete
    Replies
    1. The syntax for defining groups and including them in flow definitions has changed:
      var filter = 'outputifindex!=discard&direction=ingress&group:ipsource:ddos=external';
      ...
      setGroups('ddos',groups);

      Delete
    2. Hi Peters,

      why the flow not shown in the opendaylight gui?

      Thanks Peter


      Regards,
      Ryanda

      Delete
  5. Hi Peter,

    Is there a version of the Javascript that works on newer versions of OpenDayLight running RESTCONF Northbound APIs?

    Many thanks,
    Silvio

    ReplyDelete
    Replies
    1. I don't know if anyone has updated this script to use the latest OpenDaylight RESTCONF APIs. You might try asking the questions on the sFlow-RT mailing list at sflow-rt.com.

      Delete