├── Docs └── DDoS Detection and Mitigation System - Prez.pdf ├── INSTALL.MD ├── README.md ├── bin ├── countminsketch.py ├── ddos_processor.py ├── filter.c └── traffic_generator.py ├── images ├── Demo.png ├── architecture.png ├── cms_demo.png ├── cms_reference.png ├── entropy_detection.png ├── packet_flow.png └── xdp_chart.png ├── tests ├── cms_test.py ├── count_loader.py ├── count_loader_sip.py ├── filter.c ├── filter.py ├── handler_port_redirect.py ├── ip_loader.py ├── ip_reader.c ├── map_explorer.py └── xdp_count.c └── usertokerntests ├── hello_maps.py └── loader.py /Docs/DDoS Detection and Mitigation System - Prez.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notmorpheus/DDoS_Processor/ae4961a92e17e2de4537276001aabec785e8981b/Docs/DDoS Detection and Mitigation System - Prez.pdf -------------------------------------------------------------------------------- /INSTALL.MD: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | Install bcc using the instructions provided here: https://github.com/iovisor/bcc/blob/master/INSTALL.md#installing-bcc 4 | 5 | Amazon AWS has specific documentation on how to setup bcc on Amazon Linux instances https://aws.amazon.com/premiumsupport/knowledge-center/ec2-linux-tools-performance-bottlenecks/ 6 | 7 | Finally, run the tool using `python3 ddos_processor.py ` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DDoS_Processor 2 | A Distributed Denial of Service Detector and mitigator based on Extended Berkeley Packet Filters (eBPF) and Xpress Data Path (XDP) 3 | 4 | ## Workflow 5 | ![](images/architecture.png) 6 | - Heavy Hitters Estimation 7 | - Clean Pipe Approach 8 | 9 | ### Working 10 | #### Approximate Heavy Hitters and the Count-Min Sketch 11 | The program solves the problem of approximate heavy hitters by incorporating the use of probabilistic count. This is achieved through the use of Count Min Sketch. 12 | 13 | The problem of Heavy Hitters; 14 | 15 | An array A of length n, and also a parameter k. Think of n as very large (in the hundreds of millions, or billions), and k as modest (10, 100, or 1000). The goal is to compute the values that occur in the array at least n/k times. 16 | 17 | Solution? 18 | 19 | Use Count Min Sketch Data structure to provide 20 | a fixed, sublinear storage to process A and find 21 | Approx count of values. 22 | 23 | D = number of non cryptographic hash functions 24 | W = no. of digits of the output of the hash function 25 | 26 | ![](images/cms_reference.png) 27 | 28 | 29 | #### CountMinSketch 30 | ![](images/cms_demo.png) 31 | - Insert: 32 | - Structure initialized with zeros 33 | - Each stream input goes through the hash functions 34 | - Output of hash function determines the column that needs to be updated in the structure. 35 | - Increments of 1. 36 | 37 | - Count: 38 | - Pass value to hash functions 39 | - O/p of hash functions determines the locations 40 | - Obtain values at locations. Choose minimum. 41 | 42 | - Advantages: 43 | - Size of the structure independent of input 44 | - Lossy count with no under-counting. 45 | 46 | #### Express Data Path 47 | eXpress Data Path provides a high performance, programmable network data path in the Linux kernel. 48 | 49 | XDP provides bare metal packet processing at the lowest point in the software stack which makes it ideal for speed without compromising programmability. 50 | 51 | Can be implemented dynamically with the integrated fast path without kernel modification. 52 | 53 | eBPF can be used to write XDP programs. 54 | 55 | Trusted Technology - Used by Cloudflare, Facebook, Netronome, Prometheus, and many more. 56 | 57 | ### Performance Comparisons 58 | ![](images/xdp_chart.png) 59 | 60 | 61 | ### Packet Flow and use of eBPF 62 | ![](images/packet_flow.png) 63 | 64 | Ring Buffers: Ring buffers are shared buffers between the device driver and Network Interface Card (NIC). These buffers store incoming packets until the device driver can process them. Ring buffers exist on both the receive (rx) and transmit (tx) side of each interface. 65 | 66 | eBPF VM: Register-based Virtual Machine using a custom 64 bit RISC instruction set capable of running Just-in-Time native-compiled "BPF programs" inside the Linux kernel with access to a subset of kernel functions and memory. It is a full VM implementation, not to be confused with the Kernel-based VM (KVM) which is a module enabling Linux to act as hypervisor for other VMs. It is also part of the mainline kernel 67 | 68 | ### Screenshots 69 | ![](images/Demo.png) 70 | 71 | 72 | ### Future Work 73 | Time-bound sample of 1M packets. If Sample does not reach the set size in set amount of time, No DDoS. 74 | 75 | Feedback loop to clear old entries. 76 | 77 | Dynamically control threshold for heavy hitters. 78 | 79 | Feed Live stream to Count Min Sketch. 80 | 81 | Combine various DDoS Detection Algorithms - Entropy, Packet Size. 82 | -------------------------------------------------------------------------------- /bin/countminsketch.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import array 3 | 4 | 5 | class CountMinSketch(object): 6 | """ 7 | A class for counting hashable items using the Count-min Sketch strategy. 8 | It fulfills a similar purpose than `itertools.Counter`. 9 | 10 | The Count-min Sketch is a randomized data structure that uses a constant 11 | amount of memory and has constant insertion and lookup times at the cost 12 | of an arbitrarily small overestimation of the counts. 13 | 14 | It has two parameters: 15 | - `m` the size of the hash tables, larger implies smaller overestimation 16 | - `d` the amount of hash tables, larger implies lower probability of 17 | overestimation. 18 | 19 | An example usage: 20 | 21 | from countminsketch import CountMinSketch 22 | sketch = CountMinSketch(1000, 10) # m=1000, d=10 23 | sketch.update("oh yeah") 24 | sketch.update(tuple()) 25 | sketch.update(1, value=123) 26 | print sketch["oh yeah"] # prints 1 27 | print sketch[tuple()] # prints 1 28 | print sketch[1] # prints 123 29 | print sketch["non-existent"] # prints 0 30 | 31 | Note that this class can be used to count *any* hashable type, so it's 32 | possible to "count apples" and then "ask for oranges". Validation is up to 33 | the user. 34 | """ 35 | 36 | def __init__(self, m, d): 37 | """ `m` is the size of the hash tables, larger implies smaller 38 | overestimation. `d` the amount of hash tables, larger implies lower 39 | probability of overestimation. 40 | """ 41 | if not m or not d: 42 | raise ValueError("Table size (m) and amount of hash functions (d)" 43 | " must be non-zero") 44 | self.m = m 45 | self.d = d 46 | self.n = 0 47 | self.tables = [] 48 | self.salt = [] 49 | for _ in range(d): 50 | table = array.array("l", (0 for _ in range(m))) 51 | self.tables.append(table) 52 | self.salt.append(hash(str(id(table)))) 53 | 54 | def _hash(self, x): 55 | for s in self.salt: 56 | #print hash((x, s)) 57 | yield hash((x, s)) % self.m 58 | 59 | def add(self, x, value=1): 60 | """ 61 | Count element `x` as if had appeared `value` times. 62 | By default `value=1` so: 63 | 64 | sketch.add(x) 65 | 66 | Effectively counts `x` as occurring once. 67 | """ 68 | self.n += value 69 | xs = [] 70 | for table, i in zip(self.tables, self._hash(x)): 71 | table[i] += value 72 | xs.append(table[i]) 73 | return min(xs) # TODO: Add test 74 | 75 | def query(self, x): 76 | """ 77 | Return an estimation of the amount of times `x` has ocurred. 78 | The returned value always overestimates the real value. 79 | """ 80 | return min(table[i] for table, i in zip(self.tables, self._hash(x))) 81 | 82 | def __getitem__(self, x): 83 | """ 84 | A convenience method to call `query`. 85 | """ 86 | return self.query(x) 87 | 88 | def __len__(self): 89 | """ 90 | The amount of things counted. Takes into account that the `value` 91 | argument of `add` might be different from 1. 92 | """ 93 | return self.n 94 | -------------------------------------------------------------------------------- /bin/ddos_processor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from __future__ import division 3 | from bcc import BPF 4 | import time 5 | from ast import literal_eval 6 | import sys 7 | from countminsketch import CountMinSketch 8 | 9 | from ctypes import * 10 | import ctypes as ct 11 | import sys 12 | import socket 13 | import os 14 | import struct 15 | 16 | #Configuration 17 | device = sys.argv[1] #interface the program needs to deploy the XDP on 18 | OUTPUT_INTERVAL = 10 #read dropped packets after block 19 | sample_size = 1000 #no. of packets to collect to determine heavy hitters 20 | threshold_percent = 60 #criteria as to who are heavy hitters 21 | #end configuration 22 | 23 | count = 0 24 | sketch = CountMinSketch(1000, 10) 25 | event_list =[] 26 | 27 | 28 | #process each incoming source uninsigned int from BPF ring buffer 29 | def process_event(cpu, data, size): 30 | 31 | global count 32 | src = ct.cast(data, ct.POINTER(ct.c_uint64)).contents.value 33 | event_list.append(int(src)) 34 | count+=1 35 | 36 | #read the drop count from the map and return a decimal from long tuple 37 | def read_count(c_map): 38 | 39 | c_list = c_map.items() 40 | if (len(c_list)!=1): 41 | return 0 42 | else: 43 | return int(str(c_list[0][1])[7:-1]) 44 | 45 | 46 | #feed source ips into CMS to obtain lossy count and check for threshold 47 | def detect_ddos(event_list,threshold_percent): 48 | 49 | pkt_count = 0 50 | heavy_hitters ={} 51 | 52 | for ip in event_list: 53 | pkt_count = sketch.add(ip) 54 | hit_percent = (pkt_count/len(event_list)) * 100 55 | if (hit_percent>threshold_percent): 56 | heavy_hitters[ip]=pkt_count 57 | return heavy_hitters 58 | 59 | #send the detected source IPs back to kernel space for blocking traffic 60 | def mitigate_ddos(b_map,hitters): 61 | 62 | idx=0 63 | if(len(hitters)!=0): 64 | for ip in hitters.keys(): 65 | key = ip 66 | leaf=idx 67 | b_map[ct.c_uint(key)] = ct.c_uint(leaf) 68 | idx+=1 69 | print("IPs sent to XDP for mitigation!") 70 | else: 71 | print("No hitters identified") 72 | 73 | #convert u32 IP address to human IP notation 74 | def decimal_to_human(input_value): 75 | input_value = int(input_value) 76 | hex_value = hex(input_value)[2:] 77 | pt3 = literal_eval((str('0x'+str(hex_value[-2:])))) 78 | pt2 = literal_eval((str('0x'+str(hex_value[-4:-2])))) 79 | pt1 = literal_eval((str('0x'+str(hex_value[-6:-4])))) 80 | pt0 = literal_eval((str('0x'+str(hex_value[-8:-6])))) 81 | result = str(pt0)+'.'+str(pt1)+'.'+str(pt2)+'.'+str(pt3) 82 | return result 83 | 84 | #print IP addresses in human readable Notation 85 | def identify_hitters(hitters): 86 | if(len(hitters)!=0): 87 | for hitter in hitters.keys(): 88 | temp=decimal_to_human(hitter) 89 | print("Source IP: {} with total packets identified before confirm DoS: {}".format(temp,hitters[hitter])) 90 | 91 | #initialize the BPF program 92 | b = BPF(src_file="filter.c") 93 | 94 | fn = b.load_func("mitigator", BPF.XDP) 95 | b.attach_xdp(device, fn, 0) 96 | 97 | ip_map = b.get_table("ip_map") 98 | 99 | drop_count = b.get_table("drop_count") 100 | 101 | b["packet_event"].open_perf_buffer(process_event) 102 | 103 | 104 | delta_list=[] 105 | 106 | print("=========================DDOS PROCESSOR=============================\n") 107 | 108 | try: 109 | while True : 110 | b.perf_buffer_poll() 111 | print("Collecting a sample of packets, count currently at {} packets.".format(count)) 112 | cnt = read_count(drop_count) 113 | if (cnt>0): 114 | print("DDoS Mitigation in effect! {} packets dropped so far".format(cnt)) 115 | delta_list.extend([cnt,time.time()]) 116 | if(len(delta_list)==4): 117 | secs_passed = delta_list[3]-delta_list[1] 118 | packets_dropped = delta_list[2] - delta_list[0] 119 | pps = packets_dropped/secs_passed 120 | print ("Drop Rate: {} pkts/sec".format(pps)) 121 | delta_list=[] 122 | 123 | if (count >=sample_size): 124 | hitters = detect_ddos(event_list,threshold_percent) 125 | identify_hitters(hitters) 126 | 127 | print("performing DDOS mitigation...") 128 | mitigate_ddos(ip_map,hitters) 129 | 130 | #cleanup 131 | event_list=[] 132 | sketch = CountMinSketch(1000, 10) 133 | count = 0 134 | time.sleep(OUTPUT_INTERVAL) 135 | 136 | except KeyboardInterrupt: 137 | print("Removing filter") 138 | drop_count.clear() 139 | ip_map.clear() 140 | b.remove_xdp(device,0) 141 | 142 | 143 | -------------------------------------------------------------------------------- /bin/filter.c: -------------------------------------------------------------------------------- 1 | #define KBUILD_MODNAME "monitor" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | //initialize maps 12 | BPF_PERF_OUTPUT(packet_event); 13 | BPF_HASH(ip_map,u32,u32); 14 | BPF_HASH(drop_count,u64,long,256); 15 | 16 | //main method 17 | int mitigator(struct xdp_md *ctx) { 18 | u64 key=0; 19 | long* count = 0; 20 | long one = 1; 21 | u32 *ip; 22 | 23 | 24 | void *data_end = (void *)(long)ctx->data_end; 25 | void *data = (void *)(long)ctx->data; 26 | struct ethhdr *eth = data; 27 | 28 | // check packet size 29 | if (eth + 1 > data_end) { 30 | return XDP_PASS; 31 | } 32 | 33 | // get the source address of the packet 34 | struct iphdr *iph = data + sizeof(struct ethhdr); 35 | if (iph + 1 > data_end) { 36 | return XDP_PASS; 37 | } 38 | 39 | u32 ip_src = iph->saddr; 40 | 41 | ip = ip_map.lookup(&ip_src); 42 | 43 | count = drop_count.lookup(&key); 44 | 45 | if (ip_src) { 46 | packet_event.perf_submit(ctx, &ip_src, sizeof(ip_src)); 47 | 48 | } 49 | 50 | // If source IP not found in the map, forward the traffic. 51 | if (!ip){ 52 | return XDP_PASS; 53 | } 54 | 55 | //If source IP found in the map, drop the traffic. 56 | if (ip){ 57 | 58 | if (count) { 59 | *count+=1; 60 | } 61 | 62 | else { 63 | drop_count.update(&key, &one); 64 | } 65 | 66 | return XDP_DROP; 67 | } 68 | 69 | // drop the packet if the ip source address is equal to ip 70 | 71 | 72 | 73 | return XDP_PASS; 74 | } -------------------------------------------------------------------------------- /bin/traffic_generator.py: -------------------------------------------------------------------------------- 1 | import random 2 | import sys 3 | from scapy.all import * 4 | target_IP = sys.argv[1] 5 | actual_source = sys.argv[2] 6 | i = 1 7 | source_ip = actual_source 8 | while True: 9 | a = str(random.randint(1,254)) 10 | b = str(random.randint(1,254)) 11 | c = str(random.randint(1,254)) 12 | d = str(random.randint(1,254)) 13 | dot = "." 14 | source_ip_rand = a + dot + b + dot + c + dot + d 15 | payload = "yada yada yada" 16 | 17 | for source_port in range(1, 65535): 18 | IP1 = IP(src = source_ip, dst = target_IP) 19 | TCP1 = TCP(sport = source_port, dport = 22) 20 | pkt = IP1 / TCP1 / payload 21 | send(pkt,inter = .001) 22 | 23 | print ("packet sent ", i) 24 | i = i + 1 -------------------------------------------------------------------------------- /images/Demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notmorpheus/DDoS_Processor/ae4961a92e17e2de4537276001aabec785e8981b/images/Demo.png -------------------------------------------------------------------------------- /images/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notmorpheus/DDoS_Processor/ae4961a92e17e2de4537276001aabec785e8981b/images/architecture.png -------------------------------------------------------------------------------- /images/cms_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notmorpheus/DDoS_Processor/ae4961a92e17e2de4537276001aabec785e8981b/images/cms_demo.png -------------------------------------------------------------------------------- /images/cms_reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notmorpheus/DDoS_Processor/ae4961a92e17e2de4537276001aabec785e8981b/images/cms_reference.png -------------------------------------------------------------------------------- /images/entropy_detection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notmorpheus/DDoS_Processor/ae4961a92e17e2de4537276001aabec785e8981b/images/entropy_detection.png -------------------------------------------------------------------------------- /images/packet_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notmorpheus/DDoS_Processor/ae4961a92e17e2de4537276001aabec785e8981b/images/packet_flow.png -------------------------------------------------------------------------------- /images/xdp_chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notmorpheus/DDoS_Processor/ae4961a92e17e2de4537276001aabec785e8981b/images/xdp_chart.png -------------------------------------------------------------------------------- /tests/cms_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from __future__ import division 3 | from countminsketch import CountMinSketch 4 | 5 | sketch = CountMinSketch(1000, 10) 6 | sketch.add("a") 7 | #print sketch["a"] 8 | #sketch.add("122.150.82.37") 9 | file1 = open('ipdump.txt', 'r') 10 | ip_addresses = file1.readlines() 11 | count = 0.0 12 | heavy_hitters = {} 13 | hit_percent = (count/len(ip_addresses)) * 100 14 | threshold = 4 15 | 16 | for ip in ip_addresses: 17 | count = 0 18 | ip.replace(' ','') 19 | count = sketch.add(ip) 20 | hit_percent = (count/len(ip_addresses)) * 100 21 | if hit_percent>threshold: 22 | heavy_hitters[ip]=count 23 | 24 | print (heavy_hitters) 25 | #print sketch.query('122.150.82.37') -------------------------------------------------------------------------------- /tests/count_loader.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from bcc import BPF 3 | import time 4 | import sys 5 | 6 | device = "eth0" 7 | b = BPF(src_file="xdp_count.c") 8 | fn = b.load_func("myprogram", BPF.XDP) 9 | b.attach_xdp(device, fn, 0) 10 | packetcnt = b.get_table("packetcnt") 11 | 12 | prev = [0] * 256 13 | print("printing packet counts") 14 | while 1: 15 | try: 16 | for k in packetcnt.keys(): 17 | val = packetcnt.sum(k).value 18 | i = k.value 19 | if val: 20 | delta = val - prev[i] 21 | prev[i] = val 22 | print("{}: {} pkt/s".format(i, delta)) 23 | time.sleep(1) 24 | except KeyboardInterrupt: 25 | print("Removing filter") 26 | break 27 | b.remove_xdp(device,0) -------------------------------------------------------------------------------- /tests/count_loader_sip.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from bcc import BPF 3 | import time 4 | import sys 5 | 6 | device = "eth0" 7 | b = BPF(src_file="xdp_count.c") 8 | fn = b.load_func("myprogram", BPF.XDP) 9 | b.attach_xdp(device, fn, 0) 10 | packetcnt = b.get_table("packetcnt") 11 | 12 | prev = [0] * 256 13 | print("printing packet counts") 14 | while 1: 15 | try: 16 | for k in packetcnt.keys(): 17 | val = packetcnt.sum(k).value 18 | i = k.value 19 | if val: 20 | delta = val - prev[i] 21 | prev[i] = val 22 | print("{}: {} pkt/s".format(i, delta)) 23 | time.sleep(1) 24 | except KeyboardInterrupt: 25 | print("Removing filter") 26 | break 27 | b.remove_xdp(device,0) -------------------------------------------------------------------------------- /tests/filter.c: -------------------------------------------------------------------------------- 1 | #define KBUILD_MODNAME "filter" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int udpfilter(struct xdp_md *ctx) { 9 | bpf_trace_printk("got a packet\n"); 10 | 11 | void *data = (void *)(long)ctx->data; 12 | void *data_end = (void *)(long)ctx->data_end; 13 | 14 | struct ethhdr *eth = data; 15 | 16 | if ((void*)eth + sizeof(*eth) <= data_end) { 17 | struct iphdr *ip = data + sizeof(*eth); 18 | if ((void*)ip + sizeof(*ip) <= data_end) { 19 | if (ip->protocol == IPPROTO_UDP) { 20 | struct udphdr *udp = (void*)ip + sizeof(*ip); 21 | if ((void*)udp + sizeof(*udp) <= data_end) { 22 | if (udp->dest == ntohs(7999)) { 23 | bpf_trace_printk("udp port 7999\n"); 24 | udp->dest = ntohs(7998); 25 | } 26 | } 27 | } 28 | } 29 | } 30 | return XDP_PASS; 31 | } -------------------------------------------------------------------------------- /tests/filter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from bcc import BPF 3 | import time 4 | import ctypes as ct 5 | 6 | device = "ens33" 7 | b = BPF(src_file="filter.c") 8 | fn = b.load_func("mitigator", BPF.XDP) 9 | b.attach_xdp(device, fn, 0) 10 | ip_map = b.get_table("ip_map") 11 | 12 | try: 13 | test_ip = 122622124 14 | leaf = test_ip 15 | key=0 16 | ip_map[ct.c_uint(key)] = ct.c_uint(leaf) 17 | print("entry successful!") 18 | b.trace_print() 19 | except KeyboardInterrupt: 20 | print("Removing filter") 21 | b.remove_xdp(device,0) 22 | 23 | ''' 24 | ip is 2886749959 25 | xdp is reading 122622124 26 | ''' -------------------------------------------------------------------------------- /tests/handler_port_redirect.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | from bcc import BPF 4 | import time 5 | 6 | device = "lo" 7 | b = BPF(src_file="filter.c") 8 | fn = b.load_func("udpfilter", BPF.XDP) 9 | b.attach_xdp(device, fn, 0) 10 | 11 | try: 12 | b.trace_print() 13 | except KeyboardInterrupt: 14 | pass 15 | 16 | b.remove_xdp(device, 0) -------------------------------------------------------------------------------- /tests/ip_loader.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from __future__ import division 3 | from bcc import BPF 4 | import time 5 | from ast import literal_eval 6 | import sys 7 | from countminsketch import CountMinSketch 8 | 9 | def help(): 10 | print("execute: {0} ".format(sys.argv[0])) 11 | print("e.g.: {0} eno1\n".format(sys.argv[0])) 12 | exit(1) 13 | 14 | if len(sys.argv) != 2: 15 | help() 16 | elif len(sys.argv) == 2: 17 | INTERFACE = sys.argv[1] 18 | 19 | from ctypes import * 20 | import ctypes as ct 21 | import sys 22 | import socket 23 | import os 24 | import struct 25 | 26 | OUTPUT_INTERVAL = 1 27 | sample_size = 10 28 | count = 0.0 29 | heavy_hitters = {} 30 | threshold = 0 31 | 32 | sketch = CountMinSketch(1000, 10) 33 | bpf = BPF(src_file="ip_reader.c") 34 | 35 | function_skb_matching = bpf.load_func("packet_monitor", BPF.SOCKET_FILTER) 36 | 37 | BPF.attach_raw_socket(function_skb_matching, INTERFACE) 38 | 39 | # retreive packet_sample map 40 | packet_sample = bpf['packet_sample'] 41 | 42 | def decimal_to_human(input_value): 43 | input_value = int(input_value) 44 | hex_value = hex(input_value)[2:] 45 | pt3 = literal_eval((str('0x'+str(hex_value[-2:])))) 46 | pt2 = literal_eval((str('0x'+str(hex_value[-4:-2])))) 47 | pt1 = literal_eval((str('0x'+str(hex_value[-6:-4])))) 48 | pt0 = literal_eval((str('0x'+str(hex_value[-8:-6])))) 49 | result = str(pt0)+'.'+str(pt1)+'.'+str(pt2)+'.'+str(pt3) 50 | return result 51 | 52 | 53 | try: 54 | while True : 55 | time.sleep(OUTPUT_INTERVAL) 56 | packet_sample_output = packet_sample.items() 57 | output_len = len(packet_sample_output) 58 | 59 | if output_len < sample_size: 60 | print("waiting for sample size") 61 | continue 62 | 63 | for i in range(0,output_len): 64 | pkt_count = 0 65 | if (len(str(packet_sample_output[i][0]))) != 30: 66 | continue 67 | temp = int(str(packet_sample_output[i][0])[8:-2]) # initial output omitted from the kernel space program 68 | temp = int(str(bin(temp))[2:]) # raw file 69 | src = int(str(temp)[:32],2) # part1 70 | pkt_count = sketch.add(src) 71 | hit_percent = (pkt_count/output_len) * 100 72 | if (hit_percent>threshold): 73 | heavy_hitters[decimal_to_human(str(src))]=pkt_count 74 | monitor_result = 'source address : ' + decimal_to_human(str(src)) + ' ' + 'destination address : ' + \ 75 | decimal_to_human(str(dst)) + ' ' + pkt_num + ' ' + 'time : ' + str(time.localtime()[0])+\ 76 | ';'+str(time.localtime()[1]).zfill(2)+';'+str(time.localtime()[2]).zfill(2)+';'+\ 77 | str(time.localtime()[3]).zfill(2)+';'+str(time.localtime()[4]).zfill(2)+';'+\ 78 | str(time.localtime()[5]).zfill(2) 79 | #print(monitor_result) 80 | 81 | print(heavy_hitters) 82 | packet_sample.clear() 83 | 84 | # time.time() outputs time elapsed since 00:00 hours, 1st, Jan., 1970. 85 | # delete map entires after printing output. confiremd it deletes values and keys too 86 | 87 | except KeyboardInterrupt: 88 | sys.stdout.close() 89 | pass -------------------------------------------------------------------------------- /tests/ip_reader.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #define IP_TCP 6 6 | #define IP_UDP 17 7 | #define IP_ICMP 1 8 | #define ETH_HLEN 14 9 | BPF_PERF_OUTPUT(skb_events); 10 | BPF_HASH(packet_sample, u64, long, 1000000); 11 | 12 | int packet_monitor(struct __sk_buff *skb) { 13 | u8 *cursor = 0; 14 | u32 saddr; 15 | long one = 1; 16 | u64 pass_value = 0; 17 | 18 | struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); 19 | struct ip_t *ip = cursor_advance(cursor, sizeof(*ip)); 20 | if (ip->nextp != IP_TCP) 21 | { 22 | if (ip -> nextp != IP_UDP) 23 | { 24 | if (ip -> nextp != IP_ICMP) 25 | return 0; 26 | } 27 | } 28 | 29 | saddr = ip -> src; 30 | pass_value = saddr; 31 | pass_value = pass_value << 32; 32 | 33 | packet_sample.update(&pass_value,&one); 34 | 35 | return -1; 36 | } -------------------------------------------------------------------------------- /tests/map_explorer.py: -------------------------------------------------------------------------------- 1 | #This is what the hashmap table looks like-> 2 | # [(c_ulong(12398496642751287045L), c_long(1)), (c_ulong(6610540560804499205L), c_long(1)), (c_ulong(12398496658583542685L), c_long(1)), (c_ulong(12398496659931156225L), c_long(1))] 3 | 4 | a = ('c_ulong(12398496642751287045L)', 'c_long(1)') 5 | 6 | #this will give us the IP address in decimal which we can then convert to bit notation 7 | print(str(a[0])[8:-2]) 8 | 9 | ''' 10 | 00 -> 12398496642751287045 is 172.16.79.5 11 | 10 -> 6610540560804499205 is 172.16.79.5 12 | 20 -> 12398496658583542685 is 91.189.91.157 13 | 30 -> 12398496659931156225 is 172.16.79.1 14 | -------------------------------------------------------------------------------- /tests/xdp_count.c: -------------------------------------------------------------------------------- 1 | #define KBUILD_MODNAME "counter" 2 | #include 3 | #include 4 | #include 5 | 6 | BPF_TABLE("percpu_array", uint32_t, long, packetcnt, 256); 7 | 8 | int myprogram(struct xdp_md *ctx) { 9 | 10 | int ipsize = 0; 11 | void *data = (void *)(long)ctx->data; 12 | void *data_end = (void *)(long)ctx->data_end; 13 | struct ethhdr *eth = data; 14 | struct iphdr *ip; 15 | long *cnt; 16 | __u32 idx; 17 | 18 | ipsize = sizeof(*eth); 19 | ip = data + ipsize; 20 | ipsize += sizeof(struct iphdr); 21 | 22 | if (data + ipsize > data_end){ 23 | return XDP_DROP; 24 | } 25 | 26 | idx = ip->protocol; 27 | cnt = packetcnt.lookup(&idx); 28 | 29 | if (cnt) { 30 | *cnt+=1; 31 | } 32 | 33 | if (ip->protocol == IPPROTO_TCP) { 34 | return XDP_DROP; 35 | } 36 | 37 | return XDP_PASS; 38 | } -------------------------------------------------------------------------------- /usertokerntests/hello_maps.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Copyright (c) PLUMgrid, Inc. 3 | # Licensed under the Apache License, Version 2.0 (the "License") 4 | 5 | # run in project examples directory with: 6 | # sudo ./hello_world.py" 7 | # see trace_fields.py for a longer example 8 | 9 | from bcc import BPF 10 | import ctypes as ct 11 | 12 | 13 | # This may not work for 4.17 on x64, you need replace kprobe__sys_clone with kprobe____x64_sys_clone 14 | text =""" 15 | #include 16 | 17 | BPF_HASH(test, u32, u32, 128); 18 | 19 | 20 | int kprobe__sys_clone(void *ctx) 21 | { 22 | u32 key = 0; 23 | u32 *ip; 24 | ip = test.lookup(&key); 25 | 26 | if(ip) { 27 | bpf_trace_printk("%u\\n", *ip); 28 | return 0; 29 | } 30 | else { 31 | bpf_trace_printk("Hello, World!\\n"); 32 | return 0; 33 | } 34 | } 35 | 36 | """ 37 | 38 | b = BPF(text=text) 39 | ip_map = b.get_table("test") 40 | 41 | test_ip = 2886749959 42 | key=0 43 | leaf = test_ip 44 | 45 | ip_map[ct.c_uint(key)] = ct.c_uint(leaf) 46 | 47 | b.trace_print() 48 | -------------------------------------------------------------------------------- /usertokerntests/loader.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from __future__ import print_function 4 | from bcc import BPF 5 | from ctypes import * 6 | 7 | def encode_dns(name): 8 | if len(name) + 1 > 255: 9 | raise Exception("DNS Name too long.") 10 | b = bytearray() 11 | for element in name.split('.'): 12 | sublen = len(element) 13 | if sublen > 63: 14 | raise ValueError('DNS label %s is too long' % element) 15 | b.append(sublen) 16 | b.extend(element.encode('ascii')) 17 | b.append(0) # Add 0-len octet label for the root server 18 | return b 19 | 20 | 21 | def add_entry(table, value): 22 | key = table.Key() 23 | key_len = len(key.p) 24 | name_buffer = encode_dns(value) 25 | # Pad the buffer with null bytes if it is too short 26 | name_buffer.extend((0,) * (key_len - len(name_buffer))) 27 | key.p = (c_ubyte * key_len).from_buffer(name_buffer) 28 | leaf = table.Leaf() 29 | leaf.p = (c_ubyte * 4).from_buffer(bytearray(4)) 30 | table[key] = leaf 31 | 32 | text =""" 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | struct Key { 42 | unsigned char p[255]; 43 | }; 44 | 45 | struct Leaf { 46 | // Not really needed in this example 47 | unsigned char p[4]; 48 | }; 49 | 50 | struct dns_char_t 51 | { 52 | char c; 53 | } BPF_PACKET_HEADER; 54 | 55 | BPF_HASH(test, struct Key, struct Leaf, 128); 56 | 57 | int kprobe__sys_clone(void *ctx) { 58 | struct Key key = {}; 59 | u16 i = 0; 60 | u8 *cursor = 0; 61 | struct dns_char_t *c; 62 | 63 | struct Leaf * lookup_leaf = test.lookup(&key); 64 | 65 | if(lookup_leaf) { 66 | bpf_trace_printk("%s\\n", &lookup_leaf); 67 | return -1; 68 | } 69 | 70 | } 71 | """ 72 | 73 | dns_list=["foo.bar","abcd.com"] 74 | 75 | bpf = BPF(text = text, debug=0) 76 | 77 | test = bpf.get_table("test") 78 | 79 | for e in dns_list: 80 | print(">>>> Adding map entry: ", e) 81 | add_entry(test, e) 82 | print("entry added") 83 | bpf.trace_print() --------------------------------------------------------------------------------