Linux as a network operating system describes the benefits of using standard Linux as a network operating system for hardware switches. A key benefit is that the behavior of the physical network can be efficiently emulated using standard Linux virtual machines and/or containers.
In this article, CONTAINERlab will be used to create a simple testbed that can be used to develop a real-time DDoS mitigation controller. This solution is highly scaleable. Each hardware switch can monitor and filter terabits per second of traffic and a single controller instance can monitor and control hundreds of switches.
Create test network
The following ddos.yml file specifies the testbed topology (shown in the screen shot at the top of this article):
name: ddos topology: nodes: router: kind: linux image: sflow/frr attacker: kind: linux image: sflow/hping3 victim: kind: linux image: alpine:latest links: - endpoints: ["router:swp1","attacker:eth1"] - endpoints: ["router:swp2","victim:eth1"]
Run the following command to run the emulation:
sudo containerlab deploy ddos.yml
Configure interfaces on router:
interface swp1 ip address 192.168.1.1/24 ! interface swp2 ip address 192.168.2.1/24 !
Configure attacker interface:
ip addr add 192.168.1.2/24 dev eth1 ip route add 192.168.2.0/24 via 192.168.1.1
Configure victim interface:
ip addr add 192.168.2.2/24 dev eth1 ip route add 192.168.1.0/24 via 192.168.2.1
Verify connectivity between the attacker and the victim:
sudo docker exec -it clab-ddos-attacker ping 192.168.2.2 PING 192.168.2.1 (192.168.2.1): 56 data bytes 64 bytes from 192.168.2.1: seq=0 ttl=64 time=0.069 ms
Install visibility and control applications on router
The advantage of using Linux as a network operating system is that you can develop and install software to tailor the network to address specific requirements. In this case, for DDoS mitigation, we need real-time visibility to detect DDoS attacks and real-time control to filter out the attack traffic.
Open a shell on router:
sudo docker exec -it clab-ddos-router sh
Install and configure Host sFlow agent:
apk --update add build-base linux-headers openssl-dev dbus-dev gcc git git clone https://github.com/sflow/host-sflow.git cd host-sflow make FEATURES="DENT" make install
Edit /etc/hsflowd.conf
sflow { agent = eth0 collector { ip=172.20.20.1 udpport=6343 } dent { sw=on switchport=swp.* } }
Note: On a hardware switch, set sw=off to offload packet sampling to hardware.
Start hsflowd:
hsflowd
Download and run tc_server Python script for adding and removing tc flower filters using a REST API:
wget https://raw.githubusercontent.com/sflow-rt/tc_server/master/tc_server nohup python3 tc_server > /dev/null &
The following command shows the Linux tc filters used in this example:
# tc filter show dev swp1 ingress filter protocol all pref 1 matchall chain 0 filter protocol all pref 1 matchall chain 0 handle 0x1 not_in_hw action order 1: sample rate 1/10000 group 1 trunc_size 128 continue index 3 ref 1 bind 1 filter protocol ip pref 14 flower chain 0 filter protocol ip pref 14 flower chain 0 handle 0x1 eth_type ipv4 ip_proto udp dst_ip 192.168.2.2 src_port 53 not_in_hw action order 1: gact action drop random type none pass val 0 index 1 ref 1 bind 1
The output shows the standard Linux tc-matchall and tc-flower filters used to monitor and drop traffic on the router. The Host sFlow agent automatically installs a matchall rule on each interface in order to sample packets. The tc_server script adds and removes a flower filters to drop unwanted traffic. On a hardware router, the filters are offloaded by the Linux switchdev driver to the router ASIC for line rate performance.
Test REST API
Add filter:
curl -X PUT -H "Content-Type: application/json" \ --data '{"ip_proto":"udp","dst_ip":"10.0.2.2","src_port":"53"}' \ http://clab-ddos-router:8081/swp1/10
Show filters:
curl http://clab-ddos-router:8081/swp1
Remove filter:
curl -X DELETE http://clab-ddos-router:8081/swp1/10
Build an automated DDoS mitigation controller
The following sFlow-RT ddos.js script automatically detects and drops UDP amplification attacks:
var block_minutes = 1; var thresh = 10000; setFlow('udp_target',{keys:'ipdestination,udpsourceport',value:'frames'}); setThreshold('attack',{metric:'udp_target', value:thresh, byFlow:true, timeout:2}); var id = 10; var controls = {}; setEventHandler(function(evt) { var key = evt.flowKey; if(controls[key]) return; var prt = ifName(evt.agent,evt.dataSource); if(!prt) return; var [dst_ip,src_port] = key.split(','); var filter = { // uncomment following line for hardware routers // 'skip_sw':'skip_sw', 'ip_proto':'udp', 'dst_ip':dst_ip, 'src_port':src_port }; var url = 'http://'+evt.agent+':8081/'+prt+'/'+id++; try { http(url,'put','application/json',JSON.stringify(filter)); } catch(e) { logWarning(url + ' put failed'); } controls[key] = {time:evt.timestamp, evt:evt, url:url}; logInfo('block ' + evt.flowKey); },['attack']); setIntervalHandler(function(now) { for(var key in controls) { var control = controls[key]; if(now - control.time < 1000 * 60 * block_minutes) continue; var evt = control.evt; if(thresholdTriggered(evt.thresholdID,evt.agent,evt.dataSource+'.'+evt.metric,evt.flowKey)) { // attack is ongoing - keep control continue; } try { http(control.url,'delete'); } catch(e) { logWarning(control.url + ' delete failed'); } delete controls[key]; logInfo('allow '+control.evt.flowKey); } });
See Writing Applications for more information on the script.
Run the controller script on the CONTAINERlab host using the sFlow-RT real-time analytics engine:
sudo docker run --network=host -v $PWD/ddos.js:/sflow-rt/ddos.js \ sflow/prometheus -Dscript.file=ddos.js
Verify that sFlow is being received by the checking the sFlow-RT status page, http://containerlab_ip:8008/
Test controller
Monitor for attack traffic on the victim:
sudo docker exec -it clab-ddos-victim sh apk --update add tcpdump tcpdump -n -i eth1 udp port 53
Start attack:
sudo docker exec -it clab-ddos-attacker \ hping3 --flood --udp -k -s 53 --rand-source 192.168.2.2
There should be a brief flurry of packets seen at the victim before the controller detects and blocks the attack. The entire period between launching the attack and the attack traffic being blocked is under a second.