|  | #!/usr/bin/python | 
|  | # | 
|  | # dddos.py	DDOS dectection system. | 
|  | # | 
|  | # Written as a basic tracing example of using ePBF | 
|  | # to detect a potential DDOS attack against a system. | 
|  | # | 
|  | # Copyright (c) 2019 Jugurtha BELKALEM. | 
|  | # Licensed under the Apache License, Version 2.0 (the "License") | 
|  | # | 
|  | # 14-Jan-2019 Jugurtha BELKALEM Created this. | 
|  |  | 
|  | from bcc import BPF | 
|  | import ctypes as ct | 
|  | import datetime | 
|  | prog = """ | 
|  | #include <linux/skbuff.h> | 
|  | #include <uapi/linux/ip.h> | 
|  |  | 
|  | #define MAX_NB_PACKETS 1000 | 
|  | #define LEGAL_DIFF_TIMESTAMP_PACKETS 1000000 | 
|  |  | 
|  | BPF_HASH(rcv_packets); | 
|  |  | 
|  | struct detectionPackets { | 
|  | u64 nb_ddos_packets; | 
|  | }; | 
|  |  | 
|  | BPF_PERF_OUTPUT(events); | 
|  |  | 
|  | int detect_ddos(struct pt_regs *ctx, void *skb){ | 
|  | struct detectionPackets detectionPacket = {}; | 
|  |  | 
|  | // Used to count number of received packets | 
|  | u64 rcv_packets_nb_index = 0, rcv_packets_nb_inter=1, *rcv_packets_nb_ptr; | 
|  |  | 
|  | // Used to measure elapsed time between 2 successive received packets | 
|  | u64 rcv_packets_ts_index = 1, rcv_packets_ts_inter=0, *rcv_packets_ts_ptr; | 
|  |  | 
|  | /* The algorithm analyses packets received by ip_rcv function | 
|  | * and measures the difference in reception time between each packet. | 
|  | * DDOS flooders send millions of packets such that difference of | 
|  | * timestamp between 2 successive packets is so small | 
|  | * (which is not like regular applications behaviour). | 
|  | * This script looks for this difference in time and if it sees | 
|  | * more than MAX_NB_PACKETS successive packets with a difference | 
|  | * of timestamp between each one of them less than | 
|  | * LEGAL_DIFF_TIMESTAMP_PACKETS ns, | 
|  | * ------------------ It Triggers an ALERT ----------------- | 
|  | * Those settings must be adapted depending on regular network traffic | 
|  | * ------------------------------------------------------------------- | 
|  | * Important: this is a rudimentary intrusion detection system, one can | 
|  | * test a real case attack using hping3. However; if regular network | 
|  | * traffic increases above predefined detection settings, a false | 
|  | * positive alert will be triggered (an example would be the | 
|  | case of large file downloads). | 
|  | */ | 
|  | rcv_packets_nb_ptr = rcv_packets.lookup(&rcv_packets_nb_index); | 
|  | rcv_packets_ts_ptr = rcv_packets.lookup(&rcv_packets_ts_index); | 
|  | if(rcv_packets_nb_ptr != 0 && rcv_packets_ts_ptr != 0){ | 
|  | rcv_packets_nb_inter = *rcv_packets_nb_ptr; | 
|  | rcv_packets_ts_inter = bpf_ktime_get_ns() - *rcv_packets_ts_ptr; | 
|  | if(rcv_packets_ts_inter < LEGAL_DIFF_TIMESTAMP_PACKETS){ | 
|  | rcv_packets_nb_inter++; | 
|  | } else { | 
|  | rcv_packets_nb_inter = 0; | 
|  | } | 
|  | if(rcv_packets_nb_inter > MAX_NB_PACKETS){ | 
|  | detectionPacket.nb_ddos_packets = rcv_packets_nb_inter; | 
|  | events.perf_submit(ctx, &detectionPacket, sizeof(detectionPacket)); | 
|  | } | 
|  | } | 
|  | rcv_packets_ts_inter = bpf_ktime_get_ns(); | 
|  | rcv_packets.update(&rcv_packets_nb_index, &rcv_packets_nb_inter); | 
|  | rcv_packets.update(&rcv_packets_ts_index, &rcv_packets_ts_inter); | 
|  | return 0; | 
|  | } | 
|  | """ | 
|  |  | 
|  | # Loads eBPF program | 
|  | b = BPF(text=prog) | 
|  |  | 
|  | # Attach kprobe to kernel function and sets detect_ddos as kprobe handler | 
|  | b.attach_kprobe(event="ip_rcv", fn_name="detect_ddos") | 
|  |  | 
|  | class DetectionTimestamp(ct.Structure): | 
|  | _fields_ = [("nb_ddos_packets", ct.c_ulonglong)] | 
|  |  | 
|  | # Show message when ePBF stats | 
|  | print("DDOS detector started ... Hit Ctrl-C to end!") | 
|  |  | 
|  | print("%-26s %-10s" % ("TIME(s)", "MESSAGE")) | 
|  |  | 
|  | def trigger_alert_event(cpu, data, size): | 
|  | event = ct.cast(data, ct.POINTER(DetectionTimestamp)).contents | 
|  | print("%-26s %s %ld" % (datetime.datetime.now(), | 
|  | "DDOS Attack => nb of packets up to now : ", event.nb_ddos_packets)) | 
|  |  | 
|  | # loop with callback to trigger_alert_event | 
|  | b["events"].open_perf_buffer(trigger_alert_event) | 
|  | while 1: | 
|  | try: | 
|  | b.perf_buffer_poll() | 
|  | except KeyboardInterrupt: | 
|  | exit() |